summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
commit0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch)
tree3f3789daa2f6db22da6e55e92bee0062a7d613fe /pigeonhole/src/lib-sieve/plugins
parentInitial commit. (diff)
downloaddovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.tar.xz
dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.zip
Adding upstream version 1:2.3.21+dfsg1.upstream/1%2.3.21+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pigeonhole/src/lib-sieve/plugins')
-rw-r--r--pigeonhole/src/lib-sieve/plugins/Makefile.am32
-rw-r--r--pigeonhole/src/lib-sieve/plugins/Makefile.in719
-rw-r--r--pigeonhole/src/lib-sieve/plugins/body/Makefile.am16
-rw-r--r--pigeonhole/src/lib-sieve/plugins/body/Makefile.in692
-rw-r--r--pigeonhole/src/lib-sieve/plugins/body/ext-body-common.c102
-rw-r--r--pigeonhole/src/lib-sieve/plugins/body/ext-body-common.h40
-rw-r--r--pigeonhole/src/lib-sieve/plugins/body/ext-body.c54
-rw-r--r--pigeonhole/src/lib-sieve/plugins/body/tst-body.c385
-rw-r--r--pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.am8
-rw-r--r--pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.in674
-rw-r--r--pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c160
-rw-r--r--pigeonhole/src/lib-sieve/plugins/copy/Makefile.am18
-rw-r--r--pigeonhole/src/lib-sieve/plugins/copy/Makefile.in735
-rw-r--r--pigeonhole/src/lib-sieve/plugins/copy/ext-copy.c183
-rw-r--r--pigeonhole/src/lib-sieve/plugins/copy/sieve-ext-copy.h21
-rw-r--r--pigeonhole/src/lib-sieve/plugins/date/Makefile.am16
-rw-r--r--pigeonhole/src/lib-sieve/plugins/date/Makefile.in692
-rw-r--r--pigeonhole/src/lib-sieve/plugins/date/ext-date-common.c593
-rw-r--r--pigeonhole/src/lib-sieve/plugins/date/ext-date-common.h80
-rw-r--r--pigeonhole/src/lib-sieve/plugins/date/ext-date.c62
-rw-r--r--pigeonhole/src/lib-sieve/plugins/date/tst-date.c496
-rw-r--r--pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.am20
-rw-r--r--pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.in697
-rw-r--r--pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.c298
-rw-r--r--pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.h41
-rw-r--r--pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate.c107
-rw-r--r--pigeonhole/src/lib-sieve/plugins/duplicate/tst-duplicate.c449
-rw-r--r--pigeonhole/src/lib-sieve/plugins/editheader/Makefile.am19
-rw-r--r--pigeonhole/src/lib-sieve/plugins/editheader/Makefile.in701
-rw-r--r--pigeonhole/src/lib-sieve/plugins/editheader/cmd-addheader.c337
-rw-r--r--pigeonhole/src/lib-sieve/plugins/editheader/cmd-deleteheader.c551
-rw-r--r--pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.c211
-rw-r--r--pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.h48
-rw-r--r--pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-limits.h7
-rw-r--r--pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader.c66
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/Makefile.am44
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/Makefile.in904
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/cmd-notify.c621
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.c718
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.h122
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-limits.h6
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify.c103
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.am16
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.in687
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c794
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c683
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h49
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h197
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c233
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c144
-rw-r--r--pigeonhole/src/lib-sieve/plugins/enotify/vmodf-encodeurl.c119
-rw-r--r--pigeonhole/src/lib-sieve/plugins/environment/Makefile.am24
-rw-r--r--pigeonhole/src/lib-sieve/plugins/environment/Makefile.in753
-rw-r--r--pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.c339
-rw-r--r--pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.h53
-rw-r--r--pigeonhole/src/lib-sieve/plugins/environment/ext-environment.c59
-rw-r--r--pigeonhole/src/lib-sieve/plugins/environment/sieve-ext-environment.h54
-rw-r--r--pigeonhole/src/lib-sieve/plugins/environment/tst-environment.c215
-rw-r--r--pigeonhole/src/lib-sieve/plugins/ihave/Makefile.am22
-rw-r--r--pigeonhole/src/lib-sieve/plugins/ihave/Makefile.in707
-rw-r--r--pigeonhole/src/lib-sieve/plugins/ihave/cmd-error.c131
-rw-r--r--pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.c249
-rw-r--r--pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.h33
-rw-r--r--pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.c52
-rw-r--r--pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.h52
-rw-r--r--pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave.c70
-rw-r--r--pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c294
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.am33
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.in776
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/cmd-flag.c251
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c733
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.h97
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c96
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imapflags.c213
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/sieve-ext-imap4flags.h74
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/tag-flags.c402
-rw-r--r--pigeonhole/src/lib-sieve/plugins/imap4flags/tst-hasflag.c248
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/Makefile.am24
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/Makefile.in718
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/cmd-global.c329
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/cmd-include.c406
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/cmd-return.c71
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c492
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.h64
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/ext-include-common.c892
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/ext-include-common.h170
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/ext-include-limits.h9
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.c254
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.h41
-rw-r--r--pigeonhole/src/lib-sieve/plugins/include/ext-include.c121
-rw-r--r--pigeonhole/src/lib-sieve/plugins/index/Makefile.am13
-rw-r--r--pigeonhole/src/lib-sieve/plugins/index/Makefile.in688
-rw-r--r--pigeonhole/src/lib-sieve/plugins/index/ext-index-common.c15
-rw-r--r--pigeonhole/src/lib-sieve/plugins/index/ext-index-common.h29
-rw-r--r--pigeonhole/src/lib-sieve/plugins/index/ext-index.c69
-rw-r--r--pigeonhole/src/lib-sieve/plugins/index/tag-index.c278
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.am26
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.in757
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox-common.h39
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox.c72
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mailbox/sieve-ext-mailbox.h21
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c186
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c284
-rw-r--r--pigeonhole/src/lib-sieve/plugins/metadata/Makefile.am24
-rw-r--r--pigeonhole/src/lib-sieve/plugins/metadata/Makefile.in705
-rw-r--r--pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata-common.h40
-rw-r--r--pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata.c83
-rw-r--r--pigeonhole/src/lib-sieve/plugins/metadata/tst-metadata.c433
-rw-r--r--pigeonhole/src/lib-sieve/plugins/metadata/tst-metadataexists.c431
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/Makefile.am30
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/Makefile.in727
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/cmd-break.c273
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/cmd-extracttext.c370
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/cmd-foreverypart.c377
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/ext-extracttext.c130
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/ext-foreverypart.c62
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.c27
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.h85
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/ext-mime.c77
-rw-r--r--pigeonhole/src/lib-sieve/plugins/mime/tag-mime.c757
-rw-r--r--pigeonhole/src/lib-sieve/plugins/notify/Makefile.am20
-rw-r--r--pigeonhole/src/lib-sieve/plugins/notify/Makefile.in699
-rw-r--r--pigeonhole/src/lib-sieve/plugins/notify/cmd-denotify.c389
-rw-r--r--pigeonhole/src/lib-sieve/plugins/notify/cmd-notify.c900
-rw-r--r--pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.c361
-rw-r--r--pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.h66
-rw-r--r--pigeonhole/src/lib-sieve/plugins/notify/ext-notify-limits.h7
-rw-r--r--pigeonhole/src/lib-sieve/plugins/notify/ext-notify.c108
-rw-r--r--pigeonhole/src/lib-sieve/plugins/regex/Makefile.am13
-rw-r--r--pigeonhole/src/lib-sieve/plugins/regex/Makefile.in688
-rw-r--r--pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.c22
-rw-r--r--pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.h24
-rw-r--r--pigeonhole/src/lib-sieve/plugins/regex/ext-regex.c65
-rw-r--r--pigeonhole/src/lib-sieve/plugins/regex/mcht-regex.c385
-rw-r--r--pigeonhole/src/lib-sieve/plugins/relational/Makefile.am14
-rw-r--r--pigeonhole/src/lib-sieve/plugins/relational/Makefile.in694
-rw-r--r--pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.c171
-rw-r--r--pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.h86
-rw-r--r--pigeonhole/src/lib-sieve/plugins/relational/ext-relational.c53
-rw-r--r--pigeonhole/src/lib-sieve/plugins/relational/mcht-count.c119
-rw-r--r--pigeonhole/src/lib-sieve/plugins/relational/mcht-value.c80
-rw-r--r--pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.am16
-rw-r--r--pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.in694
-rw-r--r--pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c674
-rw-r--r--pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.h35
-rw-r--r--pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c146
-rw-r--r--pigeonhole/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c304
-rw-r--r--pigeonhole/src/lib-sieve/plugins/special-use/Makefile.am22
-rw-r--r--pigeonhole/src/lib-sieve/plugins/special-use/Makefile.in703
-rw-r--r--pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.c31
-rw-r--r--pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.h43
-rw-r--r--pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use.c57
-rw-r--r--pigeonhole/src/lib-sieve/plugins/special-use/tag-specialuse.c316
-rw-r--r--pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c525
-rw-r--r--pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.am8
-rw-r--r--pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.in673
-rw-r--r--pigeonhole/src/lib-sieve/plugins/subaddress/ext-subaddress.c191
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vacation/Makefile.am19
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vacation/Makefile.in700
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vacation/cmd-vacation.c1578
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.c114
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.h60
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c66
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation.c131
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/Makefile.am41
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/Makefile.in801
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/cmd-set.c235
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c420
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.h24
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c950
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.h102
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.c137
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.h22
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-limits.h35
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c578
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.h66
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.c110
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.h43
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.c236
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.h43
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.c279
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.h37
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables.c84
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/sieve-ext-variables.h364
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/tst-string.c271
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.am2
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.in692
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.am15
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.in687
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/cmd-debug-log.c130
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug-common.h22
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c70
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.am16
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.in692
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-common.h37
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c95
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-variables.c206
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment.c112
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.am17
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.in695
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c692
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c51
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h40
-rw-r--r--pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c52
204 files changed, 52770 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/plugins/Makefile.am b/pigeonhole/src/lib-sieve/plugins/Makefile.am
new file mode 100644
index 0000000..2a1c054
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/Makefile.am
@@ -0,0 +1,32 @@
+if BUILD_UNFINISHED
+UNFINISHED =
+endif
+
+SUBDIRS = \
+ vacation \
+ subaddress \
+ comparator-i-ascii-numeric \
+ relational \
+ regex \
+ imap4flags \
+ copy \
+ include \
+ body \
+ variables \
+ enotify \
+ notify \
+ environment \
+ mailbox \
+ date \
+ spamvirustest \
+ ihave \
+ editheader \
+ duplicate \
+ index \
+ metadata \
+ mime \
+ special-use \
+ vnd.dovecot \
+ $(UNFINISHED)
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/Makefile.in b/pigeonhole/src/lib-sieve/plugins/Makefile.in
new file mode 100644
index 0000000..8443114
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/Makefile.in
@@ -0,0 +1,719 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@BUILD_UNFINISHED_TRUE@UNFINISHED =
+SUBDIRS = \
+ vacation \
+ subaddress \
+ comparator-i-ascii-numeric \
+ relational \
+ regex \
+ imap4flags \
+ copy \
+ include \
+ body \
+ variables \
+ enotify \
+ notify \
+ environment \
+ mailbox \
+ date \
+ spamvirustest \
+ ihave \
+ editheader \
+ duplicate \
+ index \
+ metadata \
+ mime \
+ special-use \
+ vnd.dovecot \
+ $(UNFINISHED)
+
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am clean clean-generic clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/body/Makefile.am b/pigeonhole/src/lib-sieve/plugins/body/Makefile.am
new file mode 100644
index 0000000..251b8d6
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/body/Makefile.am
@@ -0,0 +1,16 @@
+noinst_LTLIBRARIES = libsieve_ext_body.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tsts = \
+ tst-body.c
+
+libsieve_ext_body_la_SOURCES = \
+ ext-body-common.c \
+ $(tsts) \
+ ext-body.c
+
+noinst_HEADERS = \
+ ext-body-common.h
diff --git a/pigeonhole/src/lib-sieve/plugins/body/Makefile.in b/pigeonhole/src/lib-sieve/plugins/body/Makefile.in
new file mode 100644
index 0000000..5d58bbd
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/body/Makefile.in
@@ -0,0 +1,692 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/body
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_body_la_LIBADD =
+am__objects_1 = tst-body.lo
+am_libsieve_ext_body_la_OBJECTS = ext-body-common.lo $(am__objects_1) \
+ ext-body.lo
+libsieve_ext_body_la_OBJECTS = $(am_libsieve_ext_body_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-body-common.Plo \
+ ./$(DEPDIR)/ext-body.Plo ./$(DEPDIR)/tst-body.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_body_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_body_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_body.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tsts = \
+ tst-body.c
+
+libsieve_ext_body_la_SOURCES = \
+ ext-body-common.c \
+ $(tsts) \
+ ext-body.c
+
+noinst_HEADERS = \
+ ext-body-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/body/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/body/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_body.la: $(libsieve_ext_body_la_OBJECTS) $(libsieve_ext_body_la_DEPENDENCIES) $(EXTRA_libsieve_ext_body_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_body_la_OBJECTS) $(libsieve_ext_body_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-body-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-body.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-body.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-body-common.Plo
+ -rm -f ./$(DEPDIR)/ext-body.Plo
+ -rm -f ./$(DEPDIR)/tst-body.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-body-common.Plo
+ -rm -f ./$(DEPDIR)/ext-body.Plo
+ -rm -f ./$(DEPDIR)/tst-body.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.c b/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.c
new file mode 100644
index 0000000..c19940e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.c
@@ -0,0 +1,102 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "mempool.h"
+#include "buffer.h"
+#include "array.h"
+#include "str.h"
+#include "istream.h"
+#include "mail-storage.h"
+
+#include "sieve-common.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-message.h"
+#include "sieve-interpreter.h"
+
+#include "ext-body-common.h"
+
+
+/*
+ * Body part stringlist
+ */
+
+static int ext_body_stringlist_next_item
+ (struct sieve_stringlist *_strlist, string_t **str_r);
+static void ext_body_stringlist_reset
+ (struct sieve_stringlist *_strlist);
+
+struct ext_body_stringlist {
+ struct sieve_stringlist strlist;
+
+ struct sieve_message_part_data *body_parts;
+ struct sieve_message_part_data *body_parts_iter;
+};
+
+int ext_body_get_part_list
+(const struct sieve_runtime_env *renv, enum tst_body_transform transform,
+ const char * const *content_types, struct sieve_stringlist **strlist_r)
+{
+ static const char * const _no_content_types[] = { "", NULL };
+ struct ext_body_stringlist *strlist;
+ struct sieve_message_part_data *body_parts = NULL;
+ int ret;
+
+ *strlist_r = NULL;
+
+ if ( content_types == NULL ) content_types = _no_content_types;
+
+ switch ( transform ) {
+ case TST_BODY_TRANSFORM_RAW:
+ if ( (ret=sieve_message_body_get_raw(renv, &body_parts)) <= 0 )
+ return ret;
+ break;
+ case TST_BODY_TRANSFORM_CONTENT:
+ if ( (ret=sieve_message_body_get_content
+ (renv, content_types, &body_parts)) <= 0 )
+ return ret;
+ break;
+ case TST_BODY_TRANSFORM_TEXT:
+ if ( (ret=sieve_message_body_get_text(renv, &body_parts)) <= 0 )
+ return ret;
+ break;
+ default:
+ i_unreached();
+ }
+
+ strlist = t_new(struct ext_body_stringlist, 1);
+ strlist->strlist.runenv = renv;
+ strlist->strlist.next_item = ext_body_stringlist_next_item;
+ strlist->strlist.reset = ext_body_stringlist_reset;
+ strlist->body_parts = body_parts;
+ strlist->body_parts_iter = body_parts;
+
+ *strlist_r = &strlist->strlist;
+ return SIEVE_EXEC_OK;
+}
+
+static int ext_body_stringlist_next_item
+(struct sieve_stringlist *_strlist, string_t **str_r)
+{
+ struct ext_body_stringlist *strlist =
+ (struct ext_body_stringlist *)_strlist;
+
+ *str_r = NULL;
+
+ if ( strlist->body_parts_iter->content == NULL ) return 0;
+
+ *str_r = t_str_new_const
+ (strlist->body_parts_iter->content, strlist->body_parts_iter->size);
+ strlist->body_parts_iter++;
+ return 1;
+}
+
+static void ext_body_stringlist_reset
+(struct sieve_stringlist *_strlist)
+{
+ struct ext_body_stringlist *strlist =
+ (struct ext_body_stringlist *)_strlist;
+
+ strlist->body_parts_iter = strlist->body_parts;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.h b/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.h
new file mode 100644
index 0000000..290ca13
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/body/ext-body-common.h
@@ -0,0 +1,40 @@
+#ifndef EXT_BODY_COMMON_H
+#define EXT_BODY_COMMON_H
+
+/*
+ * Types
+ */
+
+enum tst_body_transform {
+ TST_BODY_TRANSFORM_RAW,
+ TST_BODY_TRANSFORM_CONTENT,
+ TST_BODY_TRANSFORM_TEXT
+};
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def body_extension;
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def body_test;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def body_operation;
+
+/*
+ * Message body part extraction
+ */
+
+int ext_body_get_part_list
+ (const struct sieve_runtime_env *renv, enum tst_body_transform transform,
+ const char * const *content_types, struct sieve_stringlist **strlist_r);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/body/ext-body.c b/pigeonhole/src/lib-sieve/plugins/body/ext-body.c
new file mode 100644
index 0000000..27218bd
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/body/ext-body.c
@@ -0,0 +1,54 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension body
+ * ------------------
+ *
+ * Authors: Stephan Bosch
+ * Original CMUSieve implementation by Timo Sirainen
+ * Specification: RFC 5173
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-body-common.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_body_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def body_extension = {
+ .name = "body",
+ .validator_load = ext_body_validator_load,
+ SIEVE_EXT_DEFINE_OPERATION(body_operation)
+};
+
+static bool ext_body_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register new test */
+ sieve_validator_register_command(valdtr, ext, &body_test);
+
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/body/tst-body.c b/pigeonhole/src/lib-sieve/plugins/body/tst-body.c
new file mode 100644
index 0000000..f08dd54
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/body/tst-body.c
@@ -0,0 +1,385 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-body-common.h"
+
+/*
+ * Body test
+ *
+ * Syntax
+ * body [COMPARATOR] [MATCH-TYPE] [BODY-TRANSFORM]
+ * <key-list: string-list>
+ */
+
+static bool tst_body_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool tst_body_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool tst_body_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def body_test = {
+ .identifier = "body",
+ .type = SCT_TEST,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_body_registered,
+ .validate = tst_body_validate,
+ .generate = tst_body_generate
+};
+
+/*
+ * Body operation
+ */
+
+static bool ext_body_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int ext_body_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def body_operation = {
+ .mnemonic = "body",
+ .ext_def = &body_extension,
+ .dump = ext_body_operation_dump,
+ .execute = ext_body_operation_execute
+};
+
+/*
+ * Optional operands
+ */
+
+enum tst_body_optional {
+ OPT_BODY_TRANSFORM = SIEVE_MATCH_OPT_LAST
+};
+
+/*
+ * Tagged arguments
+ */
+
+/* Forward declarations */
+
+static bool tag_body_transform_validate
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool tag_body_transform_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *cmd);
+
+/* Argument objects */
+
+static const struct sieve_argument_def body_raw_tag = {
+ .identifier = "raw",
+ .validate = tag_body_transform_validate,
+ .generate = tag_body_transform_generate
+};
+
+static const struct sieve_argument_def body_content_tag = {
+ .identifier = "content",
+ .validate = tag_body_transform_validate,
+ .generate = tag_body_transform_generate
+};
+
+static const struct sieve_argument_def body_text_tag = {
+ .identifier = "text",
+ .validate = tag_body_transform_validate,
+ .generate = tag_body_transform_generate
+};
+
+/* Argument implementation */
+
+static bool tag_body_transform_validate
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ enum tst_body_transform transform;
+ struct sieve_ast_argument *tag = *arg;
+
+ /* BODY-TRANSFORM:
+ * :raw
+ * / :content <content-types: string-list>
+ * / :text
+ */
+ if ( (bool) cmd->data ) {
+ sieve_argument_validate_error(valdtr, *arg,
+ "the :raw, :content and :text arguments for the body test are mutually "
+ "exclusive, but more than one was specified");
+ return FALSE;
+ }
+
+ /* Skip tag */
+ *arg = sieve_ast_argument_next(*arg);
+
+ /* :content tag has a string-list argument */
+ if ( sieve_argument_is(tag, body_raw_tag) )
+ transform = TST_BODY_TRANSFORM_RAW;
+
+ else if ( sieve_argument_is(tag, body_text_tag) )
+ transform = TST_BODY_TRANSFORM_TEXT;
+
+ else if ( sieve_argument_is(tag, body_content_tag) ) {
+ /* Check syntax:
+ * :content <content-types: string-list>
+ */
+ if ( !sieve_validate_tag_parameter
+ (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING_LIST, FALSE) ) {
+ return FALSE;
+ }
+
+ /* Assign tag parameters */
+ tag->parameters = *arg;
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ transform = TST_BODY_TRANSFORM_CONTENT;
+ } else
+ return FALSE;
+
+ /* Signal the presence of this tag */
+ cmd->data = (void *) TRUE;
+
+ /* Assign context data */
+ tag->argument->data = (void *) transform;
+
+ return TRUE;
+}
+
+/*
+ * Command Registration
+ */
+
+static bool tst_body_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ /* The order of these is not significant */
+ sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+ sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &body_raw_tag, OPT_BODY_TRANSFORM);
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &body_content_tag, OPT_BODY_TRANSFORM);
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &body_text_tag, OPT_BODY_TRANSFORM);
+
+ return TRUE;
+}
+
+/*
+ * Validation
+ */
+
+static bool tst_body_validate
+(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ const struct sieve_match_type mcht_default =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ const struct sieve_comparator cmp_default =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "key list", 1, SAAT_STRING_LIST) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ /* Validate the key argument to a specified match type */
+ return sieve_match_type_validate
+ (valdtr, tst, arg, &mcht_default, &cmp_default);
+}
+
+/*
+ * Code generation
+ */
+
+static bool tst_body_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &body_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+static bool tag_body_transform_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *cmd ATTR_UNUSED)
+{
+ enum tst_body_transform transform =
+ POINTER_CAST_TO(arg->argument->data, enum tst_body_transform);
+
+ sieve_binary_emit_byte(cgenv->sblock, transform);
+ sieve_generate_argument_parameters(cgenv, cmd, arg);
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool ext_body_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ unsigned int transform;
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "BODY");
+ sieve_code_descend(denv);
+
+ /* Handle any optional arguments */
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code))
+ < 0 )
+ return FALSE;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_BODY_TRANSFORM:
+ if ( !sieve_binary_read_byte(denv->sblock, address, &transform) )
+ return FALSE;
+
+ switch ( transform ) {
+ case TST_BODY_TRANSFORM_RAW:
+ sieve_code_dumpf(denv, "BODY-TRANSFORM: RAW");
+ break;
+ case TST_BODY_TRANSFORM_TEXT:
+ sieve_code_dumpf(denv, "BODY-TRANSFORM: TEXT");
+ break;
+ case TST_BODY_TRANSFORM_CONTENT:
+ sieve_code_dumpf(denv, "BODY-TRANSFORM: CONTENT");
+
+ sieve_code_descend(denv);
+ if ( !sieve_opr_stringlist_dump(denv, address, "content types") )
+ return FALSE;
+ sieve_code_ascend(denv);
+ break;
+ default:
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ };
+
+ return sieve_opr_stringlist_dump(denv, address, "key list");
+}
+
+/*
+ * Interpretation
+ */
+
+static int ext_body_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ int opt_code = 0;
+ struct sieve_comparator cmp =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+ struct sieve_match_type mcht =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ unsigned int transform = TST_BODY_TRANSFORM_TEXT;
+ struct sieve_stringlist *ctype_list, *value_list, *key_list;
+ bool mvalues_active;
+ const char * const *content_types = NULL;
+ int match, ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Optional operands */
+
+ ctype_list = NULL;
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_match_opr_optional_read
+ (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 )
+ return ret;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_BODY_TRANSFORM:
+ if ( !sieve_binary_read_byte(renv->sblock, address, &transform) ||
+ transform > TST_BODY_TRANSFORM_TEXT ) {
+ sieve_runtime_trace_error(renv, "invalid body transform type");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( transform == TST_BODY_TRANSFORM_CONTENT &&
+ (ret=sieve_opr_stringlist_read
+ (renv, address, "content-type-list", &ctype_list)) <= 0 )
+ return ret;
+
+ break;
+
+ default:
+ sieve_runtime_trace_error(renv, "unknown optional operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ }
+
+ /* Read key-list */
+
+ if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list))
+ <= 0 )
+ return ret;
+
+ if ( ctype_list != NULL && sieve_stringlist_read_all
+ (ctype_list, pool_datastack_create(), &content_types) < 0 ) {
+ sieve_runtime_trace_error(renv, "failed to read content-type-list operand");
+ return ctype_list->exec_status;
+ }
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "body test");
+
+ /* Extract requested parts */
+ if ( (ret=ext_body_get_part_list(renv,
+ (enum tst_body_transform) transform, content_types,&value_list)) <= 0 )
+ return ret;
+
+ /* Disable match values processing as required by RFC */
+ mvalues_active = sieve_match_values_set_enabled(renv, FALSE);
+
+ /* Perform match */
+ match = sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret);
+
+ /* Restore match values processing */
+ (void)sieve_match_values_set_enabled(renv, mvalues_active);
+
+ if ( match < 0 )
+ return ret;
+
+ /* Set test result for subsequent conditional jump */
+ sieve_interpreter_set_test_result(renv->interp, match > 0);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.am b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.am
new file mode 100644
index 0000000..8e385c5
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.am
@@ -0,0 +1,8 @@
+noinst_LTLIBRARIES = libsieve_ext_comparator-i-ascii-numeric.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_comparator_i_ascii_numeric_la_SOURCES = \
+ ext-cmp-i-ascii-numeric.c
diff --git a/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.in b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.in
new file mode 100644
index 0000000..35ef51f
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile.in
@@ -0,0 +1,674 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/comparator-i-ascii-numeric
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_comparator_i_ascii_numeric_la_LIBADD =
+am_libsieve_ext_comparator_i_ascii_numeric_la_OBJECTS = \
+ ext-cmp-i-ascii-numeric.lo
+libsieve_ext_comparator_i_ascii_numeric_la_OBJECTS = \
+ $(am_libsieve_ext_comparator_i_ascii_numeric_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-cmp-i-ascii-numeric.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_comparator_i_ascii_numeric_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_comparator_i_ascii_numeric_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_comparator-i-ascii-numeric.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_comparator_i_ascii_numeric_la_SOURCES = \
+ ext-cmp-i-ascii-numeric.c
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/comparator-i-ascii-numeric/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_comparator-i-ascii-numeric.la: $(libsieve_ext_comparator_i_ascii_numeric_la_OBJECTS) $(libsieve_ext_comparator_i_ascii_numeric_la_DEPENDENCIES) $(EXTRA_libsieve_ext_comparator_i_ascii_numeric_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_comparator_i_ascii_numeric_la_OBJECTS) $(libsieve_ext_comparator_i_ascii_numeric_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-cmp-i-ascii-numeric.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-cmp-i-ascii-numeric.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-cmp-i-ascii-numeric.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c
new file mode 100644
index 0000000..20ec38b
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/comparator-i-ascii-numeric/ext-cmp-i-ascii-numeric.c
@@ -0,0 +1,160 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension comparator-i;ascii-numeric
+ * ------------------------------------
+ *
+ * Author: Stephan Bosch
+ * Specification: RFC 2244
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-comparators.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include <ctype.h>
+
+/*
+ * Forward declarations
+ */
+
+static const struct sieve_operand_def my_comparator_operand;
+
+const struct sieve_comparator_def i_ascii_numeric_comparator;
+
+/*
+ * Extension
+ */
+
+static bool ext_cmp_i_ascii_numeric_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *validator);
+
+const struct sieve_extension_def comparator_i_ascii_numeric_extension = {
+ .name = "comparator-i;ascii-numeric",
+ .validator_load = ext_cmp_i_ascii_numeric_validator_load,
+ SIEVE_EXT_DEFINE_OPERAND(my_comparator_operand)
+};
+
+static bool ext_cmp_i_ascii_numeric_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *validator)
+{
+ sieve_comparator_register(validator, ext, &i_ascii_numeric_comparator);
+ return TRUE;
+}
+
+/*
+ * Operand
+ */
+
+static const struct sieve_extension_objects ext_comparators =
+ SIEVE_EXT_DEFINE_COMPARATOR(i_ascii_numeric_comparator);
+
+static const struct sieve_operand_def my_comparator_operand = {
+ .name = "comparator-i;ascii-numeric",
+ .ext_def = &comparator_i_ascii_numeric_extension,
+ .class = &sieve_comparator_operand_class,
+ .interface = &ext_comparators
+};
+
+/*
+ * Comparator
+ */
+
+/* Forward declarations */
+
+static int cmp_i_ascii_numeric_compare
+ (const struct sieve_comparator *cmp,
+ const char *val1, size_t val1_size, const char *val2, size_t val2_size);
+
+/* Comparator object */
+
+const struct sieve_comparator_def i_ascii_numeric_comparator = {
+ SIEVE_OBJECT("i;ascii-numeric",
+ &my_comparator_operand, 0),
+ .flags =
+ SIEVE_COMPARATOR_FLAG_ORDERING |
+ SIEVE_COMPARATOR_FLAG_EQUALITY,
+ .compare = cmp_i_ascii_numeric_compare
+};
+
+/* Comparator implementation */
+
+static int cmp_i_ascii_numeric_compare
+ (const struct sieve_comparator *cmp ATTR_UNUSED,
+ const char *val, size_t val_size, const char *key, size_t key_size)
+{
+ const char *vend = val + val_size;
+ const char *kend = key + key_size;
+ const char *vp = val;
+ const char *kp = key;
+ int digits, i;
+
+ /* RFC 4790: All input is valid; strings that do not start with a digit
+ * represent positive infinity.
+ */
+ if ( !i_isdigit(*vp) ) {
+ if ( i_isdigit(*kp) ) {
+ /* Value is greater */
+ return 1;
+ }
+ } else {
+ if ( !i_isdigit(*kp) ) {
+ /* Value is less */
+ return -1;
+ }
+ }
+
+ /* Ignore leading zeros */
+
+ while ( *vp == '0' && vp < vend )
+ vp++;
+
+ while ( *kp == '0' && kp < kend )
+ kp++;
+
+ /* Check whether both numbers are equally long in terms of digits */
+
+ digits = 0;
+ while ( vp < vend && kp < kend && i_isdigit(*vp) && i_isdigit(*kp) ) {
+ vp++;
+ kp++;
+ digits++;
+ }
+
+ if ( vp == vend || !i_isdigit(*vp) ) {
+ if ( kp != kend && i_isdigit(*kp) ) {
+ /* Value is less */
+ return -1;
+ }
+ } else {
+ /* Value is greater */
+ return 1;
+ }
+
+ /* Equally long: compare digits */
+
+ vp -= digits;
+ kp -= digits;
+ i = 0;
+ while ( i < digits ) {
+ if ( *vp > *kp )
+ return 1;
+ else if ( *vp < *kp )
+ return -1;
+
+ kp++;
+ vp++;
+ i++;
+ }
+
+ return 0;
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/copy/Makefile.am b/pigeonhole/src/lib-sieve/plugins/copy/Makefile.am
new file mode 100644
index 0000000..483032a
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/copy/Makefile.am
@@ -0,0 +1,18 @@
+noinst_LTLIBRARIES = libsieve_ext_copy.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_copy_la_SOURCES = \
+ ext-copy.c
+
+public_headers = \
+ sieve-ext-copy.h
+
+headers =
+
+pkginc_libdir=$(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
+
diff --git a/pigeonhole/src/lib-sieve/plugins/copy/Makefile.in b/pigeonhole/src/lib-sieve/plugins/copy/Makefile.in
new file mode 100644
index 0000000..9824089
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/copy/Makefile.in
@@ -0,0 +1,735 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/copy
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(pkginc_lib_HEADERS) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_copy_la_LIBADD =
+am_libsieve_ext_copy_la_OBJECTS = ext-copy.lo
+libsieve_ext_copy_la_OBJECTS = $(am_libsieve_ext_copy_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-copy.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_copy_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_copy_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(pkginc_libdir)"
+HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_copy.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_copy_la_SOURCES = \
+ ext-copy.c
+
+public_headers = \
+ sieve-ext-copy.h
+
+headers =
+pkginc_libdir = $(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/copy/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/copy/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_copy.la: $(libsieve_ext_copy_la_OBJECTS) $(libsieve_ext_copy_la_DEPENDENCIES) $(EXTRA_libsieve_ext_copy_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_copy_la_OBJECTS) $(libsieve_ext_copy_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-copy.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkginc_libHEADERS: $(pkginc_lib_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \
+ done
+
+uninstall-pkginc_libHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(pkginc_libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-copy.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pkginc_libHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-copy.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pkginc_libHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-pkginc_libHEADERS install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-pkginc_libHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/copy/ext-copy.c b/pigeonhole/src/lib-sieve/plugins/copy/ext-copy.c
new file mode 100644
index 0000000..e7db9dd
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/copy/ext-copy.c
@@ -0,0 +1,183 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension copy
+ * --------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 3894
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "sieve-ext-copy.h"
+
+/*
+ * Forward declarations
+ */
+
+static const struct sieve_argument_def copy_tag;
+static const struct sieve_operand_def copy_side_effect_operand;
+
+/*
+ * Extension
+ */
+
+static bool
+ext_copy_validator_load(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr);
+
+const struct sieve_extension_def copy_extension = {
+ .name = "copy",
+ .validator_load = ext_copy_validator_load,
+ SIEVE_EXT_DEFINE_OPERAND(copy_side_effect_operand),
+};
+
+static bool
+ext_copy_validator_load(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr)
+{
+ /* Register copy tag with redirect and fileinto commands and we don't
+ care whether these commands are registered or even whether they will
+ be registered at all. The validator handles either situation
+ gracefully.
+ */
+ sieve_validator_register_external_tag(valdtr, "redirect", ext,
+ &copy_tag, SIEVE_OPT_SIDE_EFFECT);
+ sieve_validator_register_external_tag(valdtr, "fileinto", ext,
+ &copy_tag, SIEVE_OPT_SIDE_EFFECT);
+ return TRUE;
+}
+
+/*
+ * Side effect
+ */
+
+static void
+seff_copy_print(const struct sieve_side_effect *seffect,
+ const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv, bool *keep);
+static int
+seff_copy_post_execute(const struct sieve_side_effect *seffect ATTR_UNUSED,
+ const struct sieve_action_exec_env *aenv ATTR_UNUSED,
+ void *tr_context ATTR_UNUSED,
+ void *se_tr_context ATTR_UNUSED, bool *keep);
+
+const struct sieve_side_effect_def copy_side_effect = {
+ SIEVE_OBJECT("copy", &copy_side_effect_operand, 0),
+ .to_action = &act_store,
+ .print = seff_copy_print,
+ .post_execute = seff_copy_post_execute,
+};
+
+/*
+ * Tagged argument
+ */
+
+static bool
+tag_copy_validate(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg, struct sieve_command *cmd);
+static bool
+tag_copy_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd);
+
+static const struct sieve_argument_def copy_tag = {
+ .identifier = "copy",
+ .validate = tag_copy_validate,
+ .generate = tag_copy_generate,
+};
+
+/*
+ * Operand
+ */
+
+static const struct sieve_extension_objects ext_side_effects =
+ SIEVE_EXT_DEFINE_SIDE_EFFECT(copy_side_effect);
+
+static const struct sieve_operand_def copy_side_effect_operand = {
+ .name = "copy operand",
+ .ext_def = &copy_extension,
+ .class = &sieve_side_effect_operand_class,
+ .interface = &ext_side_effects,
+};
+
+/*
+ * Tag registration
+ */
+
+void sieve_ext_copy_register_tag(struct sieve_validator *valdtr,
+ const struct sieve_extension *copy_ext,
+ const char *command)
+{
+ if (sieve_validator_extension_loaded(valdtr, copy_ext)) {
+ sieve_validator_register_external_tag(
+ valdtr, command, copy_ext, &copy_tag,
+ SIEVE_OPT_SIDE_EFFECT);
+ }
+}
+
+/*
+ * Tag validation
+ */
+
+static bool
+tag_copy_validate(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_ast_argument **arg ATTR_UNUSED,
+ struct sieve_command *cmd ATTR_UNUSED)
+{
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+tag_copy_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg,
+ struct sieve_command *cmd ATTR_UNUSED)
+{
+ if (sieve_ast_argument_type(arg) != SAAT_TAG)
+ return FALSE;
+
+ sieve_opr_side_effect_emit(cgenv->sblock, sieve_argument_ext(arg),
+ &copy_side_effect);
+ return TRUE;
+}
+
+/*
+ * Side effect implementation
+ */
+
+static void
+seff_copy_print(const struct sieve_side_effect *seffect ATTR_UNUSED,
+ const struct sieve_action *action ATTR_UNUSED,
+ const struct sieve_result_print_env *rpenv, bool *keep)
+{
+ sieve_result_seffect_printf(rpenv, "preserve implicit keep");
+ *keep = TRUE;
+}
+
+static int
+seff_copy_post_execute(const struct sieve_side_effect *seffect ATTR_UNUSED,
+ const struct sieve_action_exec_env *aenv ATTR_UNUSED,
+ void *tr_context ATTR_UNUSED,
+ void *se_tr_context ATTR_UNUSED, bool *keep)
+{
+ *keep = TRUE;
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/copy/sieve-ext-copy.h b/pigeonhole/src/lib-sieve/plugins/copy/sieve-ext-copy.h
new file mode 100644
index 0000000..faf19dc
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/copy/sieve-ext-copy.h
@@ -0,0 +1,21 @@
+#ifndef SIEVE_EXT_COPY_H
+#define SIEVE_EXT_COPY_H
+
+/* sieve_ext_copy_get_extension():
+ * Get the extension struct for the copy extension.
+ */
+static inline const struct sieve_extension *sieve_ext_copy_get_extension
+(struct sieve_instance *svinst)
+{
+ return sieve_extension_get_by_name(svinst, "copy");
+}
+
+/* sieve_ext_copy_register_tag():
+ * Register the :copy tagged argument for a command other than fileinto and
+ * redirect.
+ */
+void sieve_ext_copy_register_tag
+ (struct sieve_validator *valdtr, const struct sieve_extension *copy_ext,
+ const char *command);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/date/Makefile.am b/pigeonhole/src/lib-sieve/plugins/date/Makefile.am
new file mode 100644
index 0000000..bf257ac
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/date/Makefile.am
@@ -0,0 +1,16 @@
+noinst_LTLIBRARIES = libsieve_ext_date.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tests = \
+ tst-date.c
+
+libsieve_ext_date_la_SOURCES = \
+ $(tests) \
+ ext-date-common.c \
+ ext-date.c
+
+noinst_HEADERS = \
+ ext-date-common.h
diff --git a/pigeonhole/src/lib-sieve/plugins/date/Makefile.in b/pigeonhole/src/lib-sieve/plugins/date/Makefile.in
new file mode 100644
index 0000000..0bc3ec0
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/date/Makefile.in
@@ -0,0 +1,692 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/date
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_date_la_LIBADD =
+am__objects_1 = tst-date.lo
+am_libsieve_ext_date_la_OBJECTS = $(am__objects_1) ext-date-common.lo \
+ ext-date.lo
+libsieve_ext_date_la_OBJECTS = $(am_libsieve_ext_date_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-date-common.Plo \
+ ./$(DEPDIR)/ext-date.Plo ./$(DEPDIR)/tst-date.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_date_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_date_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_date.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tests = \
+ tst-date.c
+
+libsieve_ext_date_la_SOURCES = \
+ $(tests) \
+ ext-date-common.c \
+ ext-date.c
+
+noinst_HEADERS = \
+ ext-date-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/date/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/date/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_date.la: $(libsieve_ext_date_la_OBJECTS) $(libsieve_ext_date_la_DEPENDENCIES) $(EXTRA_libsieve_ext_date_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_date_la_OBJECTS) $(libsieve_ext_date_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-date-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-date.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-date.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-date-common.Plo
+ -rm -f ./$(DEPDIR)/ext-date.Plo
+ -rm -f ./$(DEPDIR)/tst-date.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-date-common.Plo
+ -rm -f ./$(DEPDIR)/ext-date.Plo
+ -rm -f ./$(DEPDIR)/tst-date.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.c b/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.c
new file mode 100644
index 0000000..7493b87
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.c
@@ -0,0 +1,593 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "utc-offset.h"
+#include "str.h"
+#include "iso8601-date.h"
+#include "message-date.h"
+
+#include "sieve-common.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-interpreter.h"
+#include "sieve-message.h"
+
+#include "ext-date-common.h"
+
+#include <time.h>
+#include <ctype.h>
+
+struct ext_date_context {
+ time_t current_date;
+ int zone_offset;
+};
+
+/*
+ * Runtime initialization
+ */
+
+static int ext_date_runtime_init
+(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ void *context ATTR_UNUSED, bool deferred ATTR_UNUSED)
+{
+ struct ext_date_context *dctx;
+ pool_t pool;
+ struct timeval msg_time;
+ time_t current_date;
+ struct tm *tm;
+ int zone_offset;
+
+ /* Get current time at instance main script is started */
+ sieve_message_context_time(renv->msgctx, &msg_time);
+ current_date = msg_time.tv_sec;
+
+ tm = localtime(&current_date);
+ zone_offset = utc_offset(tm, current_date);
+
+ /* Create context */
+ pool = sieve_message_context_pool(renv->msgctx);
+ dctx = p_new(pool, struct ext_date_context, 1);
+ dctx->current_date = current_date;
+ dctx->zone_offset = zone_offset;
+
+ sieve_message_context_extension_set
+ (renv->msgctx, ext, (void *) dctx);
+ return SIEVE_EXEC_OK;
+}
+
+static struct sieve_interpreter_extension
+date_interpreter_extension = {
+ .ext_def = &date_extension,
+ .run = ext_date_runtime_init
+};
+
+bool ext_date_interpreter_load
+(const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ /* Register runtime hook to obtain stript start timestamp */
+ if ( renv->msgctx == NULL ||
+ sieve_message_context_extension_get(renv->msgctx, ext) == NULL ) {
+ sieve_interpreter_extension_register
+ (renv->interp, ext, &date_interpreter_extension, NULL);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Zone string
+ */
+
+bool ext_date_parse_timezone
+(const char *zone, int *zone_offset_r)
+{
+ const unsigned char *str = (const unsigned char *) zone;
+ size_t len = strlen(zone);
+
+ if (len == 5 && (*str == '+' || *str == '-')) {
+ int offset;
+
+ if (!i_isdigit(str[1]) || !i_isdigit(str[2]) ||
+ !i_isdigit(str[3]) || !i_isdigit(str[4]))
+ return FALSE;
+
+ offset = ((str[1]-'0') * 10 + (str[2]-'0')) * 60 +
+ (str[3]-'0') * 10 + (str[4]-'0');
+
+ if ( zone_offset_r != NULL )
+ *zone_offset_r = *str == '+' ? offset : -offset;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Current date
+ */
+
+time_t ext_date_get_current_date
+(const struct sieve_runtime_env *renv, int *zone_offset_r)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct ext_date_context *dctx = (struct ext_date_context *)
+ sieve_message_context_extension_get(renv->msgctx, this_ext);
+
+ if ( dctx == NULL ) {
+ ext_date_runtime_init(this_ext, renv, NULL, FALSE);
+ dctx = (struct ext_date_context *)
+ sieve_message_context_extension_get(renv->msgctx, this_ext);
+
+ i_assert(dctx != NULL);
+ }
+
+ /* Read script start timestamp from message context */
+
+ if ( zone_offset_r != NULL )
+ *zone_offset_r = dctx->zone_offset;
+
+ return dctx->current_date;
+}
+
+/*
+ * Date parts
+ */
+
+/* "year" => the year, "0000" .. "9999".
+ */
+
+static const char *ext_date_year_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part year_date_part = {
+ "year",
+ ext_date_year_part_get
+};
+
+/* "month" => the month, "01" .. "12".
+ */
+
+static const char *ext_date_month_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part month_date_part = {
+ "month",
+ ext_date_month_part_get
+};
+
+/* "day" => the day, "01" .. "31".
+ */
+
+static const char *ext_date_day_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part day_date_part = {
+ "day",
+ ext_date_day_part_get
+};
+
+/* "date" => the date in "yyyy-mm-dd" format.
+ */
+
+static const char *ext_date_date_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part date_date_part = {
+ "date",
+ ext_date_date_part_get
+};
+
+/* "julian" => the Modified Julian Day, that is, the date
+ * expressed as an integer number of days since
+ * 00:00 UTC on November 17, 1858 (using the Gregorian
+ * calendar). This corresponds to the regular
+ * Julian Day minus 2400000.5. Sample routines to
+ * convert to and from modified Julian dates are
+ * given in Appendix A.
+ */
+
+static const char *ext_date_julian_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part julian_date_part = {
+ "julian",
+ ext_date_julian_part_get
+};
+
+/* "hour" => the hour, "00" .. "23".
+ */
+static const char *ext_date_hour_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part hour_date_part = {
+ "hour",
+ ext_date_hour_part_get
+};
+
+/* "minute" => the minute, "00" .. "59".
+ */
+static const char *ext_date_minute_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part minute_date_part = {
+ "minute",
+ ext_date_minute_part_get
+};
+
+/* "second" => the second, "00" .. "60".
+ */
+static const char *ext_date_second_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part second_date_part = {
+ "second",
+ ext_date_second_part_get
+};
+
+/* "time" => the time in "hh:mm:ss" format.
+ */
+static const char *ext_date_time_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part time_date_part = {
+ "time",
+ ext_date_time_part_get
+};
+
+/* "iso8601" => the date and time in restricted ISO 8601 format.
+ */
+static const char *ext_date_iso8601_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part iso8601_date_part = {
+ "iso8601",
+ ext_date_iso8601_part_get
+};
+
+/* "std11" => the date and time in a format appropriate
+ * for use in a Date: header field [RFC2822].
+ */
+static const char *ext_date_std11_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part std11_date_part = {
+ "std11",
+ ext_date_std11_part_get
+};
+
+/* "zone" => the time zone in use. If the user specified a
+ * time zone with ":zone", "zone" will
+ * contain that value. If :originalzone is specified
+ * this value will be the original zone specified
+ * in the date-time value. If neither argument is
+ * specified the value will be the server's default
+ * time zone in offset format "+hhmm" or "-hhmm". An
+ * offset of 0 (Zulu) always has a positive sign.
+ */
+static const char *ext_date_zone_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part zone_date_part = {
+ "zone",
+ ext_date_zone_part_get
+};
+
+/* "weekday" => the day of the week expressed as an integer between
+ * "0" and "6". "0" is Sunday, "1" is Monday, etc.
+ */
+static const char *ext_date_weekday_part_get(struct tm *tm, int zone_offset);
+
+static const struct ext_date_part weekday_date_part = {
+ "weekday",
+ ext_date_weekday_part_get
+};
+
+/*
+ * Date part extraction
+ */
+
+static const struct ext_date_part *date_parts[] = {
+ &year_date_part, &month_date_part, &day_date_part, &date_date_part,
+ &julian_date_part, &hour_date_part, &minute_date_part, &second_date_part,
+ &time_date_part, &iso8601_date_part, &std11_date_part, &zone_date_part,
+ &weekday_date_part
+};
+
+unsigned int date_parts_count = N_ELEMENTS(date_parts);
+
+const struct ext_date_part *ext_date_part_find(const char *part)
+{
+ unsigned int i;
+
+ for ( i = 0; i < date_parts_count; i++ ) {
+ if ( strcasecmp(date_parts[i]->identifier, part) == 0 ) {
+ return date_parts[i];
+ }
+ }
+
+ return NULL;
+}
+
+const char *ext_date_part_extract
+(const struct ext_date_part *dpart, struct tm *tm, int zone_offset)
+{
+ if ( dpart == NULL || dpart->get_string == NULL )
+ return NULL;
+
+ return dpart->get_string(tm, zone_offset);
+}
+
+/*
+ * Date part implementations
+ */
+
+static const char *month_names[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static const char *weekday_names[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static const char *ext_date_year_part_get
+(struct tm *tm, int zone_offset ATTR_UNUSED)
+{
+ return t_strdup_printf("%04d", tm->tm_year + 1900);
+}
+
+static const char *ext_date_month_part_get
+(struct tm *tm, int zone_offset ATTR_UNUSED)
+{
+ return t_strdup_printf("%02d", tm->tm_mon + 1);
+}
+
+static const char *ext_date_day_part_get
+(struct tm *tm, int zone_offset ATTR_UNUSED)
+{
+ return t_strdup_printf("%02d", tm->tm_mday);
+}
+
+static const char *ext_date_date_part_get
+(struct tm *tm, int zone_offset ATTR_UNUSED)
+{
+ return t_strdup_printf("%04d-%02d-%02d",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
+}
+
+static const char *ext_date_julian_part_get
+(struct tm *tm, int zone_offset ATTR_UNUSED)
+{
+ int year = tm->tm_year+1900;
+ int month = tm->tm_mon+1;
+ int day = tm->tm_mday;
+ int c, ya, jd;
+
+ /* Modified from RFC 5260 Appendix A (refer to Errata) */
+
+ if ( month > 2 )
+ month -= 3;
+ else {
+ month += 9;
+ year--;
+ }
+
+ c = year / 100;
+ ya = year - c * 100;
+
+ jd = c * 146097 / 4 + ya * 1461 / 4 + (month * 153 + 2) / 5 + day + 1721119;
+
+ return t_strdup_printf("%d", jd - 2400001);
+}
+
+static const char *ext_date_hour_part_get
+(struct tm *tm, int zone_offset ATTR_UNUSED)
+{
+ return t_strdup_printf("%02d", tm->tm_hour);
+}
+
+static const char *ext_date_minute_part_get
+(struct tm *tm, int zone_offset ATTR_UNUSED)
+{
+ return t_strdup_printf("%02d", tm->tm_min);
+}
+
+static const char *ext_date_second_part_get
+(struct tm *tm, int zone_offset ATTR_UNUSED)
+{
+ return t_strdup_printf("%02d", tm->tm_sec);
+}
+
+static const char *ext_date_time_part_get
+(struct tm *tm, int zone_offset ATTR_UNUSED)
+{
+ return t_strdup_printf("%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+static const char *ext_date_iso8601_part_get
+(struct tm *tm, int zone_offset)
+{
+ /* From RFC: `The restricted ISO 8601 format is specified by the date-time
+ * ABNF production given in [RFC3339], Section 5.6, with the added
+ * restrictions that the letters "T" and "Z" MUST be in upper case, and
+ * a time zone offset of zero MUST be represented by "Z" and not "+00:00".
+ */
+ if ( zone_offset == 0 )
+ zone_offset = INT_MAX;
+
+ return iso8601_date_create_tm(tm, zone_offset);
+}
+
+
+static const char *ext_date_std11_part_get
+(struct tm *tm, int zone_offset)
+{
+ return t_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d %s",
+ weekday_names[tm->tm_wday],
+ tm->tm_mday,
+ month_names[tm->tm_mon],
+ tm->tm_year+1900,
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ ext_date_zone_part_get(tm, zone_offset));
+}
+
+static const char *ext_date_zone_part_get
+(struct tm *tm ATTR_UNUSED, int zone_offset)
+{
+ bool negative;
+ int offset = zone_offset;
+
+ if (zone_offset >= 0)
+ negative = FALSE;
+ else {
+ negative = TRUE;
+ offset = -offset;
+ }
+
+ return t_strdup_printf
+ ("%c%02d%02d", negative ? '-' : '+', offset / 60, offset % 60);
+}
+
+static const char *ext_date_weekday_part_get
+(struct tm *tm, int zone_offset ATTR_UNUSED)
+{
+ return t_strdup_printf("%d", tm->tm_wday);
+}
+
+/*
+ * Date stringlist
+ */
+
+/* Forward declarations */
+
+static int ext_date_stringlist_next_item
+ (struct sieve_stringlist *_strlist, string_t **str_r);
+static void ext_date_stringlist_reset
+ (struct sieve_stringlist *_strlist);
+
+/* Stringlist object */
+
+struct ext_date_stringlist {
+ struct sieve_stringlist strlist;
+
+ struct sieve_stringlist *field_values;
+ int time_zone;
+ const struct ext_date_part *date_part;
+
+ time_t local_time;
+ int local_zone;
+
+ bool read:1;
+};
+
+struct sieve_stringlist *ext_date_stringlist_create
+(const struct sieve_runtime_env *renv, struct sieve_stringlist *field_values,
+ int time_zone, const struct ext_date_part *dpart)
+{
+ struct ext_date_stringlist *strlist;
+
+ strlist = t_new(struct ext_date_stringlist, 1);
+ strlist->strlist.runenv = renv;
+ strlist->strlist.exec_status = SIEVE_EXEC_OK;
+ strlist->strlist.next_item = ext_date_stringlist_next_item;
+ strlist->strlist.reset = ext_date_stringlist_reset;
+ strlist->field_values = field_values;
+ strlist->time_zone = time_zone;
+ strlist->date_part = dpart;
+
+ strlist->local_time = ext_date_get_current_date(renv, &strlist->local_zone);
+
+ return &strlist->strlist;
+}
+
+/* Stringlist implementation */
+
+static int ext_date_stringlist_next_item
+(struct sieve_stringlist *_strlist, string_t **str_r)
+{
+ struct ext_date_stringlist *strlist =
+ (struct ext_date_stringlist *) _strlist;
+ bool got_date = FALSE;
+ time_t date_value;
+ const char *part_value = NULL;
+ int original_zone;
+
+ /* Check whether the item was already read */
+ if ( strlist->read ) return 0;
+
+ if ( strlist->field_values != NULL ) {
+ string_t *hdr_item;
+ const char *header_value, *date_string;
+ int ret;
+
+ /* Use header field value */
+
+ /* Read first */
+ if ( (ret=sieve_stringlist_next_item(strlist->field_values, &hdr_item))
+ <= 0 )
+ return ret;
+
+ /* Extract the date string value */
+
+ header_value = str_c(hdr_item);
+ date_string = strrchr(header_value, ';');
+
+ if ( date_string == NULL ) {
+ /* Direct header value */
+ date_string = header_value;
+ } else {
+ /* Delimited by ';', e.g. a Received: header */
+ date_string++;
+ }
+
+ /* Parse the date value */
+ if ( message_date_parse((const unsigned char *) date_string,
+ strlen(date_string), &date_value, &original_zone) ) {
+ got_date = TRUE;
+ }
+ } else {
+ /* Use time stamp recorded at the time the script first started */
+ date_value = strlist->local_time;
+ original_zone = strlist->local_zone;
+ got_date = TRUE;
+ }
+
+ if ( got_date ) {
+ int wanted_zone;
+ struct tm *date_tm;
+
+ /* Apply wanted timezone */
+
+ switch ( strlist->time_zone ) {
+ case EXT_DATE_TIMEZONE_LOCAL:
+ wanted_zone = strlist->local_zone;
+ break;
+ case EXT_DATE_TIMEZONE_ORIGINAL:
+ wanted_zone = original_zone;
+ break;
+ default:
+ wanted_zone = strlist->time_zone;
+ }
+
+ date_value += wanted_zone * 60;
+
+ /* Convert timestamp to struct tm */
+
+ if ( (date_tm=gmtime(&date_value)) != NULL ) {
+ /* Extract the date part */
+ part_value = ext_date_part_extract
+ (strlist->date_part, date_tm, wanted_zone);
+ }
+ }
+
+ strlist->read = TRUE;
+
+ if ( part_value == NULL )
+ return 0;
+
+ *str_r = t_str_new_const(part_value, strlen(part_value));
+ return 1;
+}
+
+static void ext_date_stringlist_reset
+(struct sieve_stringlist *_strlist)
+{
+ struct ext_date_stringlist *strlist =
+ (struct ext_date_stringlist *) _strlist;
+
+ if ( strlist->field_values != NULL )
+ sieve_stringlist_reset(strlist->field_values);
+ strlist->read = FALSE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.h b/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.h
new file mode 100644
index 0000000..116af3e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/date/ext-date-common.h
@@ -0,0 +1,80 @@
+#ifndef EXT_DATE_COMMON_H
+#define EXT_DATE_COMMON_H
+
+#include "sieve-common.h"
+
+#include <time.h>
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def date_extension;
+
+bool ext_date_interpreter_load
+ (const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED);
+
+/*
+ * Tests
+ */
+
+extern const struct sieve_command_def date_test;
+extern const struct sieve_command_def currentdate_test;
+
+/*
+ * Operations
+ */
+
+enum ext_date_opcode {
+ EXT_DATE_OPERATION_DATE,
+ EXT_DATE_OPERATION_CURRENTDATE
+};
+
+extern const struct sieve_operation_def date_operation;
+extern const struct sieve_operation_def currentdate_operation;
+
+/*
+ * Zone string
+ */
+
+bool ext_date_parse_timezone(const char *zone, int *zone_offset_r);
+
+/*
+ * Current date
+ */
+
+time_t ext_date_get_current_date
+ (const struct sieve_runtime_env *renv, int *zone_offset_r);
+
+/*
+ * Date part
+ */
+
+struct ext_date_part {
+ const char *identifier;
+
+ const char *(*get_string)(struct tm *tm, int zone_offset);
+};
+
+const struct ext_date_part *ext_date_part_find(const char *part);
+
+const char *ext_date_part_extract
+ (const struct ext_date_part *dpart, struct tm *tm, int zone_offset);
+
+/*
+ * Date stringlist
+ */
+
+enum ext_date_timezone_special {
+ EXT_DATE_TIMEZONE_LOCAL = 100,
+ EXT_DATE_TIMEZONE_ORIGINAL = 101
+};
+
+struct sieve_stringlist *ext_date_stringlist_create
+(const struct sieve_runtime_env *renv, struct sieve_stringlist *field_values,
+ int time_zone, const struct ext_date_part *dpart);
+
+
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/date/ext-date.c b/pigeonhole/src/lib-sieve/plugins/date/ext-date.c
new file mode 100644
index 0000000..3880d82
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/date/ext-date.c
@@ -0,0 +1,62 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension date
+ * ------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5260
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-common.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-date-common.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_date_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *validator);
+
+const struct sieve_operation_def *ext_date_operations[] = {
+ &date_operation,
+ &currentdate_operation
+};
+
+const struct sieve_extension_def date_extension = {
+ .name = "date",
+ .validator_load = ext_date_validator_load,
+ .interpreter_load = ext_date_interpreter_load,
+ SIEVE_EXT_DEFINE_OPERATIONS(ext_date_operations)
+};
+
+static bool ext_date_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register new test */
+ sieve_validator_register_command(valdtr, ext, &date_test);
+ sieve_validator_register_command(valdtr, ext, &currentdate_test);
+
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/date/tst-date.c b/pigeonhole/src/lib-sieve/plugins/date/tst-date.c
new file mode 100644
index 0000000..60ee92c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/date/tst-date.c
@@ -0,0 +1,496 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+#include "sieve-message.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-date-common.h"
+
+#include <time.h>
+
+/*
+ * Tests
+ */
+
+static bool tst_date_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool tst_date_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+/* Date test
+ *
+ * Syntax:
+ * date [<":zone" <time-zone: string>> / ":originalzone"]
+ * [COMPARATOR] [MATCH-TYPE] <header-name: string>
+ * <date-part: string> <key-list: string-list>
+ */
+
+static bool tst_date_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+
+const struct sieve_command_def date_test = {
+ .identifier = "date",
+ .type = SCT_TEST,
+ .positional_args = 3,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_date_registered,
+ .validate = tst_date_validate,
+ .generate = tst_date_generate
+};
+
+/* Currentdate test
+ *
+ * Syntax:
+ * currentdate [":zone" <time-zone: string>]
+ * [COMPARATOR] [MATCH-TYPE]
+ * <date-part: string> <key-list: string-list>
+ */
+
+static bool tst_currentdate_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+
+const struct sieve_command_def currentdate_test = {
+ .identifier = "currentdate",
+ .type = SCT_TEST,
+ .positional_args = 2,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_currentdate_registered,
+ .validate = tst_date_validate,
+ .generate = tst_date_generate
+};
+
+/*
+ * Tagged arguments
+ */
+
+/* Forward declarations */
+
+static bool tag_zone_validate
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool tag_zone_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *cmd);
+
+/* Argument objects */
+
+static const struct sieve_argument_def date_zone_tag = {
+ .identifier = "zone",
+ .validate = tag_zone_validate,
+ .generate = tag_zone_generate
+};
+
+static const struct sieve_argument_def date_originalzone_tag = {
+ .identifier = "originalzone",
+ .validate = tag_zone_validate,
+ .generate = tag_zone_generate
+};
+
+/*
+ * Date operation
+ */
+
+static bool tst_date_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_date_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def date_operation = {
+ .mnemonic = "DATE",
+ .ext_def = &date_extension,
+ .code = EXT_DATE_OPERATION_DATE,
+ .dump = tst_date_operation_dump,
+ .execute = tst_date_operation_execute
+};
+
+const struct sieve_operation_def currentdate_operation = {
+ .mnemonic = "CURRENTDATE",
+ .ext_def = &date_extension,
+ .code = EXT_DATE_OPERATION_CURRENTDATE,
+ .dump = tst_date_operation_dump,
+ .execute = tst_date_operation_execute
+};
+
+/*
+ * Optional operands
+ */
+
+enum tst_date_optional {
+ OPT_DATE_ZONE = SIEVE_AM_OPT_LAST,
+ OPT_DATE_LAST
+};
+
+/*
+ * Tag implementation
+ */
+
+static bool tag_zone_validate
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+
+ if ( (bool) cmd->data ) {
+ if ( sieve_command_is(cmd, date_test) ) {
+ sieve_argument_validate_error(valdtr, *arg,
+ "multiple :zone or :originalzone arguments specified for "
+ "the currentdate test");
+ } else {
+ sieve_argument_validate_error(valdtr, *arg,
+ "multiple :zone arguments specified for the currentdate test");
+ }
+ return FALSE;
+ }
+
+ /* Skip tag */
+ *arg = sieve_ast_argument_next(*arg);
+
+ /* :content tag has a string-list argument */
+ if ( sieve_argument_is(tag, date_zone_tag) ) {
+
+ /* Check syntax:
+ * :zone <time-zone: string>
+ */
+ if ( !sieve_validate_tag_parameter
+ (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, FALSE) ) {
+ return FALSE;
+ }
+
+ /* Check it */
+ if ( sieve_argument_is_string_literal(*arg) ) {
+ const char *zone = sieve_ast_argument_strc(*arg);
+
+ if ( !ext_date_parse_timezone(zone, NULL) ) {
+ sieve_argument_validate_warning(valdtr, *arg,
+ "specified :zone argument '%s' is not a valid timezone",
+ str_sanitize(zone, 40));
+ }
+ }
+
+ /* Assign tag parameters */
+ tag->parameters = *arg;
+ *arg = sieve_ast_arguments_detach(*arg,1);
+ }
+
+ cmd->data = (void *) TRUE;
+
+ return TRUE;
+}
+
+/*
+ * Test registration
+ */
+
+static bool tst_date_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+ sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &date_zone_tag, OPT_DATE_ZONE);
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &date_originalzone_tag, OPT_DATE_ZONE);
+
+ return TRUE;
+}
+
+static bool tst_currentdate_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+ sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &date_zone_tag, OPT_DATE_ZONE);
+
+ return TRUE;
+}
+
+/*
+ * Validation
+ */
+
+static bool tst_date_validate
+(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ unsigned int arg_offset = 0 ;
+ const struct sieve_match_type mcht_default =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ const struct sieve_comparator cmp_default =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+
+ /* Check header name */
+
+ if ( sieve_command_is(tst, date_test) ) {
+ arg_offset = 1;
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "header name", 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ if ( !sieve_command_verify_headers_argument(valdtr, arg) )
+ return FALSE;
+
+ arg = sieve_ast_argument_next(arg);
+ }
+
+ /* Check date part */
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "date part", arg_offset + 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ if ( sieve_argument_is_string_literal(arg) ) {
+ const char * part = sieve_ast_argument_strc(arg);
+
+ if ( ext_date_part_find(part) == NULL ) {
+ sieve_argument_validate_warning
+ (valdtr, arg, "specified date part `%s' is not known",
+ str_sanitize(part, 80));
+ }
+ }
+
+ arg = sieve_ast_argument_next(arg);
+
+ /* Check key list */
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "key list", arg_offset + 2, SAAT_STRING_LIST) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ /* Validate the key argument to a specified match type */
+ return sieve_match_type_validate
+ (valdtr, tst, arg, &mcht_default, &cmp_default);
+}
+
+/*
+ * Code generation
+ */
+
+static bool tst_date_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *tst)
+{
+ if ( sieve_command_is(tst, date_test) )
+ sieve_operation_emit(cgenv->sblock, tst->ext, &date_operation);
+ else if ( sieve_command_is(tst, currentdate_test) )
+ sieve_operation_emit(cgenv->sblock, tst->ext, &currentdate_operation);
+ else
+ i_unreached();
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, tst, NULL);
+}
+
+static bool tag_zone_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *cmd)
+{
+ if ( arg->parameters == NULL ) {
+ sieve_opr_omitted_emit(cgenv->sblock);
+ return TRUE;
+ }
+
+ return sieve_generate_argument_parameters(cgenv, cmd, arg);
+}
+
+/*
+ * Code dump
+ */
+
+static bool tst_date_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ int opt_code = 0;
+ const struct sieve_operation *op = denv->oprtn;
+
+ sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op));
+ sieve_code_descend(denv);
+
+ /* Handle any optional arguments */
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_message_opr_optional_dump(denv, address, &opt_code)) < 0 )
+ return FALSE;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_DATE_ZONE:
+ if ( !sieve_opr_string_dump_ex(denv, address, "zone", "ORIGINAL") )
+ return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+ }
+
+ if ( sieve_operation_is(op, date_operation) &&
+ !sieve_opr_string_dump(denv, address, "header name") )
+ return FALSE;
+
+ return
+ sieve_opr_string_dump(denv, address, "date part") &&
+ sieve_opr_stringlist_dump(denv, address, "key list");
+}
+
+
+/*
+ * Code execution
+ */
+
+static int tst_date_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ const struct sieve_operation *op = renv->oprtn;
+ int opt_code = 0;
+ struct sieve_match_type mcht =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ struct sieve_comparator cmp =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+ ARRAY_TYPE(sieve_message_override) svmos;
+ string_t *date_part = NULL, *zone = NULL;
+ struct sieve_stringlist *hdr_list = NULL, *hdr_value_list;
+ struct sieve_stringlist *value_list, *key_list;
+ bool zone_specified = FALSE, zone_literal = TRUE;
+ const struct ext_date_part *dpart;
+ int time_zone;
+ int match, ret;
+
+ /* Read optional operands */
+ for (;;) {
+ int opt;
+
+ /* Optional operands */
+ i_zero(&svmos);
+ if ( (opt=sieve_message_opr_optional_read
+ (renv, address, &opt_code, &ret, NULL, &mcht, &cmp, &svmos)) < 0 )
+ return ret;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_DATE_ZONE:
+ if ( (ret=sieve_opr_string_read_ex
+ (renv, address, "zone", TRUE, &zone, &zone_literal)) <= 0 )
+ return ret;
+
+ zone_specified = TRUE;
+ break;
+ default:
+ sieve_runtime_trace_error(renv, "unknown optional operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ }
+
+ if ( sieve_operation_is(op, date_operation) ) {
+ /* Read header name as stringlist */
+ if ( (ret=sieve_opr_stringlist_read
+ (renv, address, "header-name", &hdr_list)) <= 0 )
+ return ret;
+ }
+
+ /* Read date part */
+ if ( (ret=sieve_opr_string_read(renv, address, "date-part", &date_part))
+ <= 0 )
+ return ret;
+
+ /* Read key-list */
+ if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list))
+ <= 0 )
+ return ret;
+
+ /* Determine what time zone to use in the result */
+ if ( !zone_specified ) {
+ time_zone = EXT_DATE_TIMEZONE_LOCAL;
+ } else if ( zone == NULL ) {
+ time_zone = EXT_DATE_TIMEZONE_ORIGINAL;
+ } else if ( !ext_date_parse_timezone(str_c(zone), &time_zone) ) {
+ if ( !zone_literal )
+ sieve_runtime_warning(renv, NULL,
+ "specified :zone argument `%s' is not a valid timezone "
+ "(using local zone)", str_sanitize(str_c(zone), 40));
+ time_zone = EXT_DATE_TIMEZONE_LOCAL;
+ }
+
+ if ( (dpart=ext_date_part_find(str_c(date_part))) == NULL ) {
+ sieve_runtime_warning(renv, NULL,
+ "specified date part argument `%s' is not known",
+ str_sanitize(str_c(date_part), 40));
+ sieve_interpreter_set_test_result(renv->interp, FALSE);
+ return SIEVE_EXEC_OK;
+ }
+
+ /*
+ * Perform test
+ */
+
+ if ( sieve_operation_is(op, date_operation) ) {
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "date test");
+
+ /* Get header */
+ sieve_runtime_trace_descend(renv);
+ if ( (ret=sieve_message_get_header_fields
+ (renv, hdr_list, &svmos, FALSE, &hdr_value_list)) <= 0 )
+ return ret;
+ sieve_runtime_trace_ascend(renv);
+
+ /* Create value stringlist */
+ value_list = ext_date_stringlist_create
+ (renv, hdr_value_list, time_zone, dpart);
+
+ } else if ( sieve_operation_is(op, currentdate_operation) ) {
+ /* Use time stamp recorded at the time the script first started */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "currentdatedate test");
+
+ /* Create value stringlist */
+ value_list = ext_date_stringlist_create(renv, NULL, time_zone, dpart);
+ } else {
+ i_unreached();
+ }
+
+ /* Perform match */
+ if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 )
+ return ret;
+
+ /* Set test result for subsequent conditional jump */
+ sieve_interpreter_set_test_result(renv->interp, match > 0);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.am b/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.am
new file mode 100644
index 0000000..7469b98
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.am
@@ -0,0 +1,20 @@
+noinst_LTLIBRARIES = libsieve_ext_duplicate.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tests = \
+ tst-duplicate.c
+
+extensions = \
+ ext-duplicate.c
+
+libsieve_ext_duplicate_la_SOURCES = \
+ $(tests) \
+ $(extensions) \
+ ext-duplicate-common.c
+
+noinst_HEADERS = \
+ ext-duplicate-common.h
+
diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.in b/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.in
new file mode 100644
index 0000000..d29f834
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/duplicate/Makefile.in
@@ -0,0 +1,697 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/duplicate
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_duplicate_la_LIBADD =
+am__objects_1 = tst-duplicate.lo
+am__objects_2 = ext-duplicate.lo
+am_libsieve_ext_duplicate_la_OBJECTS = $(am__objects_1) \
+ $(am__objects_2) ext-duplicate-common.lo
+libsieve_ext_duplicate_la_OBJECTS = \
+ $(am_libsieve_ext_duplicate_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-duplicate-common.Plo \
+ ./$(DEPDIR)/ext-duplicate.Plo ./$(DEPDIR)/tst-duplicate.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_duplicate_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_duplicate_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_duplicate.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tests = \
+ tst-duplicate.c
+
+extensions = \
+ ext-duplicate.c
+
+libsieve_ext_duplicate_la_SOURCES = \
+ $(tests) \
+ $(extensions) \
+ ext-duplicate-common.c
+
+noinst_HEADERS = \
+ ext-duplicate-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/duplicate/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/duplicate/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_duplicate.la: $(libsieve_ext_duplicate_la_OBJECTS) $(libsieve_ext_duplicate_la_DEPENDENCIES) $(EXTRA_libsieve_ext_duplicate_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_duplicate_la_OBJECTS) $(libsieve_ext_duplicate_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-duplicate-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-duplicate.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-duplicate.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-duplicate-common.Plo
+ -rm -f ./$(DEPDIR)/ext-duplicate.Plo
+ -rm -f ./$(DEPDIR)/tst-duplicate.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-duplicate-common.Plo
+ -rm -f ./$(DEPDIR)/ext-duplicate.Plo
+ -rm -f ./$(DEPDIR)/tst-duplicate.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.c b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.c
new file mode 100644
index 0000000..5af7e04
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.c
@@ -0,0 +1,298 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "md5.h"
+#include "ioloop.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-settings.h"
+#include "sieve-error.h"
+#include "sieve-extensions.h"
+#include "sieve-message.h"
+#include "sieve-code.h"
+#include "sieve-runtime.h"
+#include "sieve-interpreter.h"
+#include "sieve-actions.h"
+#include "sieve-result.h"
+
+#include "ext-duplicate-common.h"
+
+/*
+ * Extension configuration
+ */
+
+#define EXT_DUPLICATE_DEFAULT_PERIOD (12*60*60)
+#define EXT_DUPLICATE_DEFAULT_MAX_PERIOD (2*24*60*60)
+
+bool ext_duplicate_load(const struct sieve_extension *ext, void **context)
+{
+ struct sieve_instance *svinst = ext->svinst;
+ struct ext_duplicate_config *config;
+ sieve_number_t default_period, max_period;
+
+ if (*context != NULL)
+ ext_duplicate_unload(ext);
+
+ if (!sieve_setting_get_duration_value(
+ svinst, "sieve_duplicate_default_period", &default_period))
+ default_period = EXT_DUPLICATE_DEFAULT_PERIOD;
+ if (!sieve_setting_get_duration_value(
+ svinst, "sieve_duplicate_max_period", &max_period)) {
+ max_period = EXT_DUPLICATE_DEFAULT_MAX_PERIOD;
+ }
+
+ config = i_new(struct ext_duplicate_config, 1);
+ config->default_period = default_period;
+ config->max_period = max_period;
+
+ *context = (void *) config;
+ return TRUE;
+}
+
+void ext_duplicate_unload(const struct sieve_extension *ext)
+{
+ struct ext_duplicate_config *config =
+ (struct ext_duplicate_config *)ext->context;
+
+ i_free(config);
+}
+
+/*
+ * Duplicate_mark action
+ */
+
+struct act_duplicate_mark_data {
+ const char *handle;
+ unsigned int period;
+ unsigned char hash[MD5_RESULTLEN];
+ bool last:1;
+};
+
+static void
+act_duplicate_mark_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep);
+static void
+act_duplicate_mark_finish(const struct sieve_action_exec_env *aenv,
+ void *tr_context, int status);
+
+static const struct sieve_action_def act_duplicate_mark = {
+ .name = "duplicate_mark",
+ .print = act_duplicate_mark_print,
+ .finish = act_duplicate_mark_finish
+};
+
+static void
+act_duplicate_mark_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep ATTR_UNUSED)
+{
+ struct act_duplicate_mark_data *data =
+ (struct act_duplicate_mark_data *)action->context;
+ const char *last = (data->last ? " last" : "");
+
+ if (data->handle != NULL) {
+ sieve_result_action_printf(
+ rpenv, "track%s duplicate with handle: %s",
+ last, str_sanitize(data->handle, 128));
+ } else {
+ sieve_result_action_printf(rpenv, "track%s duplicate", last);
+ }
+}
+
+static void
+act_duplicate_mark_finish(const struct sieve_action_exec_env *aenv,
+ void *tr_context ATTR_UNUSED, int status)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct act_duplicate_mark_data *data =
+ (struct act_duplicate_mark_data *)aenv->action->context;
+
+ if (status != SIEVE_EXEC_OK) {
+ e_debug(aenv->event, "Not marking duplicate (status=%s)",
+ sieve_execution_exitcode_to_str(status));
+ return;
+ }
+
+ e_debug(aenv->event, "Marking duplicate");
+
+ /* Message was handled successfully, so track duplicate for this
+ * message.
+ */
+ eenv->exec_status->significant_action_executed = TRUE;
+ sieve_action_duplicate_mark(aenv, data->hash, sizeof(data->hash),
+ ioloop_time + data->period);
+}
+
+/*
+ * Duplicate checking
+ */
+
+struct ext_duplicate_handle {
+ const char *handle;
+ bool last:1;
+ bool duplicate:1;
+};
+
+struct ext_duplicate_hash {
+ unsigned char hash[MD5_RESULTLEN];
+ ARRAY(struct ext_duplicate_handle) handles;
+};
+
+struct ext_duplicate_context {
+ ARRAY(struct ext_duplicate_hash) hashes;
+};
+
+static void
+ext_duplicate_hash(string_t *handle, const char *value, size_t value_len,
+ bool last, unsigned char hash_r[])
+{
+ static const char *id = "sieve duplicate";
+ struct md5_context md5ctx;
+
+ md5_init(&md5ctx);
+ md5_update(&md5ctx, id, strlen(id));
+ if (last)
+ md5_update(&md5ctx, "0", 1);
+ else
+ md5_update(&md5ctx, "+", 1);
+ if (handle != NULL) {
+ md5_update(&md5ctx, "h-", 2);
+ md5_update(&md5ctx, str_c(handle), str_len(handle));
+ } else {
+ md5_update(&md5ctx, "default", 7);
+ }
+ md5_update(&md5ctx, value, value_len);
+ md5_final(&md5ctx, hash_r);
+}
+
+int ext_duplicate_check(const struct sieve_runtime_env *renv, string_t *handle,
+ const char *value, size_t value_len,
+ sieve_number_t period, bool last, bool *duplicate_r)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct ext_duplicate_context *rctx;
+ bool duplicate = FALSE;
+ pool_t msg_pool = NULL, result_pool = NULL;
+ unsigned char hash[MD5_RESULTLEN];
+ struct ext_duplicate_hash *hash_record = NULL;
+ struct ext_duplicate_handle *handle_record = NULL;
+ struct act_duplicate_mark_data *act;
+ int ret;
+
+ *duplicate_r = FALSE;
+
+ if (!sieve_execute_duplicate_check_available(eenv)) {
+ sieve_runtime_warning(
+ renv, NULL, "duplicate test: "
+ "duplicate checking not available in this context");
+ return SIEVE_EXEC_OK;
+ }
+
+ if (value == NULL)
+ return SIEVE_EXEC_OK;
+
+ /* Create hash */
+ ext_duplicate_hash(handle, value, value_len, last, hash);
+
+ /* Get context; find out whether duplicate was checked earlier */
+ rctx = (struct ext_duplicate_context *)
+ sieve_message_context_extension_get(renv->msgctx, this_ext);
+
+ if (rctx == NULL) {
+ /* Create context */
+ msg_pool = sieve_message_context_pool(renv->msgctx);
+ rctx = p_new(msg_pool, struct ext_duplicate_context, 1);
+ sieve_message_context_extension_set(renv->msgctx, this_ext,
+ (void *)rctx);
+ } else if (array_is_created(&rctx->hashes)) {
+ struct ext_duplicate_hash *record;
+
+ array_foreach_modifiable(&rctx->hashes, record) {
+ if (memcmp(record->hash, hash, MD5_RESULTLEN) == 0) {
+ hash_record = record;
+ break;
+ }
+ }
+ }
+ if (hash_record != NULL) {
+ const struct ext_duplicate_handle *rhandle;
+ array_foreach(&hash_record->handles, rhandle) {
+ const char *handle_str =
+ (handle == NULL ? NULL : str_c(handle));
+ if (null_strcmp(rhandle->handle, handle_str) == 0 &&
+ rhandle->last == last)
+ return (rhandle->duplicate ?
+ SIEVE_DUPLICATE_CHECK_RESULT_EXISTS :
+ SIEVE_DUPLICATE_CHECK_RESULT_NOT_FOUND);
+ }
+ }
+
+ result_pool = sieve_result_pool(renv->result);
+ act = p_new(result_pool, struct act_duplicate_mark_data, 1);
+ if (handle != NULL)
+ act->handle = p_strdup(result_pool, str_c(handle));
+ act->period = period;
+ memcpy(act->hash, hash, MD5_RESULTLEN);
+ act->last = last;
+
+ /* Check duplicate */
+ ret = sieve_execute_duplicate_check(eenv, hash, sizeof(hash),
+ &duplicate);
+ if (ret >= SIEVE_EXEC_OK && !duplicate && last) {
+ unsigned char no_last_hash[MD5_RESULTLEN];
+
+ /* Check for entry without :last */
+ ext_duplicate_hash(handle, value, value_len,
+ FALSE, no_last_hash);
+ ret = sieve_execute_duplicate_check(
+ eenv, no_last_hash, sizeof(no_last_hash),
+ &duplicate);
+ }
+ if (ret < SIEVE_EXEC_OK) {
+ sieve_runtime_critical(
+ renv, NULL, "failed to check for duplicate",
+ "failed to check for duplicate%s",
+ (ret == SIEVE_EXEC_TEMP_FAILURE ?
+ " (temporary failure)" : ""));
+ return ret;
+ }
+
+ /* We may only mark the message as duplicate when Sieve script executes
+ * successfully; therefore defer this operation until successful result
+ * execution.
+ */
+ if (!duplicate || last) {
+ if (sieve_result_add_action(renv, NULL, NULL,
+ &act_duplicate_mark,
+ NULL, (void *) act, 0, FALSE) < 0)
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ /* Cache result */
+ if (msg_pool == NULL)
+ msg_pool = sieve_message_context_pool(renv->msgctx);
+ if (hash_record == NULL) {
+ if (!array_is_created(&rctx->hashes))
+ p_array_init(&rctx->hashes, msg_pool, 64);
+ hash_record = array_append_space(&rctx->hashes);
+ memcpy(hash_record->hash, hash, MD5_RESULTLEN);
+ p_array_init(&hash_record->handles, msg_pool, 64);
+ }
+
+ handle_record = array_append_space(&hash_record->handles);
+ if (handle != NULL)
+ handle_record->handle = p_strdup(msg_pool, str_c(handle));
+ handle_record->last = last;
+ handle_record->duplicate = duplicate;
+
+ *duplicate_r = duplicate;
+
+ return SIEVE_EXEC_OK;
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.h b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.h
new file mode 100644
index 0000000..c802b08
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate-common.h
@@ -0,0 +1,41 @@
+#ifndef EXT_DUPLICATE_COMMON_H
+#define EXT_DUPLICATE_COMMON_H
+
+#include "sieve-common.h"
+
+/*
+ * Extension
+ */
+
+struct ext_duplicate_config {
+ unsigned int default_period;
+ unsigned int max_period;
+};
+
+bool ext_duplicate_load(const struct sieve_extension *ext, void **context);
+void ext_duplicate_unload(const struct sieve_extension *ext);
+
+extern const struct sieve_extension_def duplicate_extension;
+extern const struct sieve_extension_def vnd_duplicate_extension;
+
+/*
+ * Tests
+ */
+
+extern const struct sieve_command_def tst_duplicate;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def tst_duplicate_operation;
+
+/*
+ * Duplicate checking
+ */
+
+int ext_duplicate_check(const struct sieve_runtime_env *renv, string_t *handle,
+ const char *value, size_t value_len,
+ sieve_number_t period, bool last, bool *duplicate_r);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate.c b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate.c
new file mode 100644
index 0000000..54de5e5
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/duplicate/ext-duplicate.c
@@ -0,0 +1,107 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension duplicate
+ * -------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: vendor-defined; spec-bosch-sieve-duplicate
+ * Implementation: full
+ * Status: experimental
+ *
+ */
+
+/* Extension vnd.dovecot.duplicate
+ * -------------------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: vendor-defined; spec-bosch-sieve-duplicate
+ * Implementation: full, but deprecated; provided for backwards compatibility
+ * Status: experimental
+ *
+ */
+
+#include "lib.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-binary.h"
+
+#include "sieve-validator.h"
+
+#include "ext-duplicate-common.h"
+
+/*
+ * Extensions
+ */
+
+static bool ext_duplicate_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def duplicate_extension = {
+ .name = "duplicate",
+ .load = ext_duplicate_load,
+ .unload = ext_duplicate_unload,
+ .validator_load = ext_duplicate_validator_load,
+ SIEVE_EXT_DEFINE_OPERATION(tst_duplicate_operation)
+};
+
+const struct sieve_extension_def vnd_duplicate_extension = {
+ .name = "vnd.dovecot.duplicate",
+ .load = ext_duplicate_load,
+ .unload = ext_duplicate_unload,
+ .validator_load = ext_duplicate_validator_load,
+ SIEVE_EXT_DEFINE_OPERATION(tst_duplicate_operation)
+};
+
+/*
+ * Validation
+ */
+
+static bool ext_duplicate_validator_check_conflict
+ (const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context,
+ struct sieve_ast_argument *require_arg,
+ const struct sieve_extension *ext_other,
+ bool required);
+
+const struct sieve_validator_extension
+duplicate_validator_extension = {
+ .ext = &vnd_duplicate_extension,
+ .check_conflict = ext_duplicate_validator_check_conflict
+};
+
+static bool ext_duplicate_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register validator extension to check for conflict between
+ vnd.dovecot.duplicate and duplicate extensions */
+ if ( sieve_extension_is(ext, vnd_duplicate_extension) ) {
+ sieve_validator_extension_register
+ (valdtr, ext, &duplicate_validator_extension, NULL);
+ }
+
+ /* Register duplicate test */
+ sieve_validator_register_command(valdtr, ext, &tst_duplicate);
+
+ return TRUE;
+}
+
+static bool ext_duplicate_validator_check_conflict
+(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_validator *valdtr, void *context ATTR_UNUSED,
+ struct sieve_ast_argument *require_arg,
+ const struct sieve_extension *ext_other,
+ bool required ATTR_UNUSED)
+{
+ /* Check for conflict with duplicate extension */
+ if ( sieve_extension_name_is(ext_other, "duplicate") ) {
+ sieve_argument_validate_error(valdtr, require_arg,
+ "the (deprecated) vnd.dovecot.duplicate extension "
+ "cannot be used together with the duplicate extension");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/duplicate/tst-duplicate.c b/pigeonhole/src/lib-sieve/plugins/duplicate/tst-duplicate.c
new file mode 100644
index 0000000..d211e04
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/duplicate/tst-duplicate.c
@@ -0,0 +1,449 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "mail-storage.h"
+
+#include "sieve-common.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-duplicate-common.h"
+
+/* Duplicate test
+ *
+ * Syntax:
+ * Usage: "duplicate" [":handle" <handle: string>]
+ * [":header" <header-name: string> /
+ * ":uniqueid" <value: string>]
+ * [":seconds" <timeout: number>] [":last"]
+ */
+
+static bool
+tst_duplicate_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool
+tst_duplicate_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+const struct sieve_command_def tst_duplicate = {
+ .identifier = "duplicate",
+ .type = SCT_TEST,
+ .positional_args = 0,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_duplicate_registered,
+ .generate = tst_duplicate_generate
+};
+
+/*
+ * Duplicate test tags
+ */
+
+static bool
+tst_duplicate_validate_number_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool
+tst_duplicate_validate_string_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+static const struct sieve_argument_def duplicate_seconds_tag = {
+ .identifier = "seconds",
+ .validate = tst_duplicate_validate_number_tag
+};
+
+static const struct sieve_argument_def duplicate_header_tag = {
+ .identifier = "header",
+ .validate = tst_duplicate_validate_string_tag
+};
+
+static const struct sieve_argument_def duplicate_uniqueid_tag = {
+ .identifier = "uniqueid",
+ .validate = tst_duplicate_validate_string_tag
+};
+
+static const struct sieve_argument_def duplicate_value_tag = {
+ .identifier = "value", /* vnd.dovecot.duplicate (deprecated) */
+ .validate = tst_duplicate_validate_string_tag
+};
+
+static const struct sieve_argument_def duplicate_handle_tag = {
+ .identifier = "handle",
+ .validate = tst_duplicate_validate_string_tag
+};
+
+static const struct sieve_argument_def duplicate_last_tag = {
+ .identifier = "last"
+};
+
+/* Codes for optional arguments */
+
+enum tst_duplicate_optional {
+ OPT_END,
+ OPT_SECONDS,
+ OPT_HEADER,
+ OPT_UNIQUEID,
+ OPT_LAST,
+ OPT_HANDLE
+};
+
+/*
+ * Duplicate operation
+ */
+
+static bool
+tst_duplicate_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+tst_duplicate_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def tst_duplicate_operation = {
+ .mnemonic = "DUPLICATE",
+ .ext_def = &duplicate_extension,
+ .dump = tst_duplicate_operation_dump,
+ .execute = tst_duplicate_operation_execute
+};
+
+/*
+ * Tag validation
+ */
+
+static bool
+tst_duplicate_validate_number_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ const struct sieve_extension *ext = sieve_argument_ext(*arg);
+ const struct ext_duplicate_config *config =
+ (const struct ext_duplicate_config *)ext->context;
+ struct sieve_ast_argument *tag = *arg;
+ sieve_number_t seconds;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :seconds number
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_NUMBER, FALSE))
+ return FALSE;
+
+ seconds = sieve_ast_argument_number(*arg);
+ /* Enforce :days <= max_period */
+ if (config->max_period > 0 && seconds > config->max_period) {
+ seconds = config->max_period;
+
+ sieve_argument_validate_warning(
+ valdtr, *arg,
+ "specified :seconds value '%llu' is over the maximum",
+ (unsigned long long)seconds);
+ }
+
+ sieve_ast_argument_number_set(*arg, seconds);
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+ return TRUE;
+}
+
+static bool
+tst_duplicate_validate_string_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ const struct sieve_extension *ext = cmd->ext;
+ struct sieve_ast_argument *tag = *arg;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :header <header-name: string>
+ * :value <value: string>
+ * :handle <handle: string>
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_STRING, FALSE))
+ return FALSE;
+
+ if (!sieve_argument_is(tag, duplicate_handle_tag) && (bool)cmd->data) {
+ sieve_argument_validate_error(
+ valdtr, *arg,
+ "conflicting :header and %s arguments specified "
+ "for the duplicate test",
+ (sieve_extension_is(ext, duplicate_extension) ?
+ ":uniqueid" : ":value"));
+ return FALSE;
+ }
+
+ /* :header <header-name: string> */
+ if (sieve_argument_is(tag, duplicate_header_tag)) {
+ if (!sieve_command_verify_headers_argument(valdtr, *arg))
+ return FALSE;
+ cmd->data = (void *)TRUE;
+ /* :handle <handle: string> */
+ } else if (sieve_argument_is(tag, duplicate_handle_tag)) {
+ /* nothing to be done */
+ } else if (sieve_argument_is(tag, duplicate_uniqueid_tag)) {
+ i_assert(sieve_extension_is(ext, duplicate_extension));
+ cmd->data = (void *)TRUE;
+ /* :value <value: string> (vnd.dovecot.duplicate) */
+ } else if (sieve_argument_is(tag, duplicate_value_tag)) {
+ i_assert(sieve_extension_is(ext, vnd_duplicate_extension));
+ cmd->data = (void *)TRUE;
+ } else {
+ i_unreached();
+ }
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+ return TRUE;
+}
+
+/*
+ * Command registration
+ */
+
+static bool
+tst_duplicate_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &duplicate_seconds_tag, OPT_SECONDS);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &duplicate_last_tag, OPT_LAST);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &duplicate_header_tag, OPT_HEADER);
+ if (sieve_extension_is(ext, duplicate_extension)) {
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &duplicate_uniqueid_tag,
+ OPT_UNIQUEID);
+ } else {
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &duplicate_value_tag,
+ OPT_UNIQUEID);
+ }
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &duplicate_handle_tag, OPT_HANDLE);
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+tst_duplicate_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &tst_duplicate_operation);
+
+ if (!sieve_generate_arguments(cgenv, cmd, NULL))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+tst_duplicate_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ const struct sieve_extension *ext = denv->oprtn->ext;
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "DUPLICATE");
+ sieve_code_descend(denv);
+
+ /* Dump optional operands */
+
+ for (;;) {
+ int opt;
+ bool opok = TRUE;
+
+ if ((opt = sieve_opr_optional_dump(denv, address,
+ &opt_code)) < 0)
+ return FALSE;
+
+ if (opt == 0)
+ break;
+
+ switch (opt_code) {
+ case OPT_SECONDS:
+ opok = sieve_opr_number_dump(denv, address, "seconds");
+ break;
+ case OPT_LAST:
+ sieve_code_dumpf(denv, "last");
+ break;
+ case OPT_HEADER:
+ opok = sieve_opr_string_dump(denv, address, "header");
+ break;
+ case OPT_UNIQUEID:
+ if (sieve_extension_is(ext, duplicate_extension)) {
+ opok = sieve_opr_string_dump(denv, address,
+ "uniqueid");
+ } else {
+ opok = sieve_opr_string_dump(denv, address,
+ "value");
+ }
+ break;
+ case OPT_HANDLE:
+ opok = sieve_opr_string_dump(denv, address, "handle");
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (!opok)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code execution
+ */
+
+static int
+tst_duplicate_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ const struct sieve_extension *ext = renv->oprtn->ext;
+ const struct ext_duplicate_config *config =
+ (const struct ext_duplicate_config *)ext->context;
+ struct mail *mail = eenv->msgdata->mail;
+ int opt_code = 0;
+ string_t *handle = NULL, *header = NULL, *uniqueid = NULL;
+ const char *val = NULL;
+ size_t val_len = 0;
+ sieve_number_t seconds = config->default_period;
+ bool last = FALSE, duplicate = FALSE;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Optional operands */
+
+ for (;;) {
+ int opt;
+
+ if ((opt = sieve_opr_optional_read(renv, address,
+ &opt_code)) < 0)
+ return SIEVE_EXEC_BIN_CORRUPT;
+
+ if (opt == 0)
+ break;
+
+ switch (opt_code) {
+ case OPT_SECONDS:
+ ret = sieve_opr_number_read(renv, address, "seconds",
+ &seconds);
+ break;
+ case OPT_LAST:
+ last = TRUE;
+ ret = SIEVE_EXEC_OK;
+ break;
+ case OPT_HEADER:
+ ret = sieve_opr_string_read(renv, address, "header",
+ &header);
+ break;
+ case OPT_UNIQUEID:
+ if (sieve_extension_is(ext, duplicate_extension)) {
+ ret = sieve_opr_string_read(renv, address,
+ "uniqueid",
+ &uniqueid);
+ } else {
+ ret = sieve_opr_string_read(renv, address,
+ "value", &uniqueid);
+ }
+ break;
+ case OPT_HANDLE:
+ ret = sieve_opr_string_read(renv, address,
+ "handle", &handle);
+ break;
+ default:
+ sieve_runtime_trace_error(
+ renv, "unknown optional operand");
+ ret = SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if (ret <= 0)
+ return ret;
+ }
+
+ /*
+ * Perform operation
+ */
+
+ /* Trace */
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "duplicate test");
+ sieve_runtime_trace_descend(renv);
+
+ /* Get value */
+ if (uniqueid != NULL) {
+ val = str_c(uniqueid);
+ val_len = str_len(uniqueid);
+ } else {
+ if (header == NULL) {
+ ret = mail_get_message_id(mail, &val);
+ if (ret < 0) {
+ return sieve_runtime_mail_error(
+ renv, mail, "duplicate test: "
+ "failed to read header field `message-id'");
+ }
+ } else {
+ ret = mail_get_first_header_utf8(mail, str_c(header),
+ &val);
+ if (ret < 0) {
+ return sieve_runtime_mail_error(
+ renv, mail, "duplicate test: "
+ "failed to read header field `%s'",
+ str_c(header));
+ }
+ }
+
+ if (ret > 0)
+ val_len = strlen(val);
+ }
+
+ /* Check duplicate */
+ if (val == NULL) {
+ duplicate = FALSE;
+ } else {
+ ret = ext_duplicate_check(renv, handle, val, val_len,
+ seconds, last, &duplicate);
+ if (ret < SIEVE_EXEC_OK)
+ return ret;
+ }
+
+ /* Trace */
+ if (duplicate) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "message is a duplicate");
+ } else {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "message is not a duplicate");
+ }
+
+ /* Set test result for subsequent conditional jump */
+ sieve_interpreter_set_test_result(renv->interp, duplicate);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.am b/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.am
new file mode 100644
index 0000000..6824ec0
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.am
@@ -0,0 +1,19 @@
+noinst_LTLIBRARIES = libsieve_ext_editheader.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../util \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-addheader.c \
+ cmd-deleteheader.c
+
+libsieve_ext_editheader_la_SOURCES = \
+ $(commands) \
+ ext-editheader.c \
+ ext-editheader-common.c
+
+noinst_HEADERS = \
+ ext-editheader-limits.h \
+ ext-editheader-common.h
diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.in b/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.in
new file mode 100644
index 0000000..75d9571
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/editheader/Makefile.in
@@ -0,0 +1,701 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/editheader
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_editheader_la_LIBADD =
+am__objects_1 = cmd-addheader.lo cmd-deleteheader.lo
+am_libsieve_ext_editheader_la_OBJECTS = $(am__objects_1) \
+ ext-editheader.lo ext-editheader-common.lo
+libsieve_ext_editheader_la_OBJECTS = \
+ $(am_libsieve_ext_editheader_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-addheader.Plo \
+ ./$(DEPDIR)/cmd-deleteheader.Plo \
+ ./$(DEPDIR)/ext-editheader-common.Plo \
+ ./$(DEPDIR)/ext-editheader.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_editheader_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_editheader_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_editheader.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../util \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-addheader.c \
+ cmd-deleteheader.c
+
+libsieve_ext_editheader_la_SOURCES = \
+ $(commands) \
+ ext-editheader.c \
+ ext-editheader-common.c
+
+noinst_HEADERS = \
+ ext-editheader-limits.h \
+ ext-editheader-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/editheader/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/editheader/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_editheader.la: $(libsieve_ext_editheader_la_OBJECTS) $(libsieve_ext_editheader_la_DEPENDENCIES) $(EXTRA_libsieve_ext_editheader_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_editheader_la_OBJECTS) $(libsieve_ext_editheader_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-addheader.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-deleteheader.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-editheader-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-editheader.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmd-addheader.Plo
+ -rm -f ./$(DEPDIR)/cmd-deleteheader.Plo
+ -rm -f ./$(DEPDIR)/ext-editheader-common.Plo
+ -rm -f ./$(DEPDIR)/ext-editheader.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmd-addheader.Plo
+ -rm -f ./$(DEPDIR)/cmd-deleteheader.Plo
+ -rm -f ./$(DEPDIR)/ext-editheader-common.Plo
+ -rm -f ./$(DEPDIR)/ext-editheader.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/cmd-addheader.c b/pigeonhole/src/lib-sieve/plugins/editheader/cmd-addheader.c
new file mode 100644
index 0000000..f39cbb7
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/editheader/cmd-addheader.c
@@ -0,0 +1,337 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "mail-storage.h"
+
+#include "rfc2822.h"
+#include "edit-mail.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-message.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-editheader-common.h"
+
+/*
+ * Addheader command
+ *
+ * Syntax
+ * "addheader" [":last"] <field-name: string> <value: string>
+ */
+
+static bool cmd_addheader_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool cmd_addheader_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool cmd_addheader_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def addheader_command = {
+ .identifier = "addheader",
+ .type = SCT_COMMAND,
+ .positional_args = 2,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_addheader_registered,
+ .validate = cmd_addheader_validate,
+ .generate = cmd_addheader_generate
+};
+
+/*
+ * Addheader command tags
+ */
+
+/* Argument objects */
+
+static const struct sieve_argument_def addheader_last_tag = {
+ .identifier = "last"
+};
+
+/* Codes for optional arguments */
+
+enum cmd_addheader_optional {
+ OPT_END,
+ OPT_LAST
+};
+
+/*
+ * Addheader operation
+ */
+
+static bool cmd_addheader_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_addheader_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def addheader_operation = {
+ .mnemonic = "ADDHEADER",
+ .ext_def = &editheader_extension,
+ .code = EXT_EDITHEADER_OPERATION_ADDHEADER,
+ .dump = cmd_addheader_operation_dump,
+ .execute = cmd_addheader_operation_execute
+};
+
+/*
+ * Utility
+ */
+
+static bool _str_contains_nul(const string_t *str)
+{
+ const unsigned char *p, *pend;
+
+ p = str_data(str);
+ pend = p + str_len(str);
+ while (p < pend) {
+ if (*p == '\0')
+ return TRUE;
+ p++;
+ }
+ return FALSE;
+}
+
+/*
+ * Validation
+ */
+
+static bool cmd_addheader_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg = cmd->first_positional;
+
+ /* Check field-name syntax */
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, cmd, arg, "field-name", 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) )
+ return FALSE;
+
+ if ( sieve_argument_is_string_literal(arg) ) {
+ string_t *fname = sieve_ast_argument_str(arg);
+
+ if ( !rfc2822_header_field_name_verify(str_c(fname), str_len(fname)) ) {
+ sieve_argument_validate_error
+ (valdtr, arg, "addheader command: specified field name `%s' is invalid",
+ str_sanitize(str_c(fname), 80));
+ return FALSE;
+ }
+
+ if ( !ext_editheader_header_allow_add
+ (cmd->ext, str_c(fname)) ) {
+ sieve_argument_validate_warning
+ (valdtr, arg, "addheader command: "
+ "adding specified header field `%s' is forbidden; "
+ "modification will be denied",
+ str_sanitize(str_c(fname), 80));
+ }
+ }
+
+ /* Check value syntax */
+
+ arg = sieve_ast_argument_next(arg);
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, cmd, arg, "value", 2, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) )
+ return FALSE;
+
+ if ( sieve_argument_is_string_literal(arg) ) {
+ string_t *fvalue = sieve_ast_argument_str(arg);
+
+ if ( _str_contains_nul(fvalue) ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "addheader command: specified value `%s' is invalid "
+ "(contains NUL character)", str_sanitize(str_c(fvalue), 80));
+ return FALSE;
+ }
+
+ if ( !rfc2822_header_field_body_verify
+ (str_c(fvalue), str_len(fvalue), TRUE, TRUE) ) {
+ sieve_argument_validate_warning(valdtr, arg,
+ "addheader command: specified value `%s' is invalid",
+ str_sanitize(str_c(fvalue), 80));
+ }
+
+ if ( ext_editheader_header_too_large(cmd->ext, str_len(fvalue)) ) {
+ sieve_argument_validate_error(valdtr, arg, "addheader command: "
+ "specified header value `%s' is too large (%zu bytes)",
+ str_sanitize(str_c(fvalue), 80), str_len(fvalue));
+ return SIEVE_EXEC_FAILURE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * Command registration
+ */
+
+static bool cmd_addheader_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &addheader_last_tag, OPT_LAST);
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_addheader_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &addheader_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool cmd_addheader_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "addheader");
+ sieve_code_descend(denv);
+
+ /* Dump optional operands */
+
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 )
+ return FALSE;
+
+ if ( opt == 0 ) break;
+
+ if ( opt_code == OPT_LAST ) {
+ sieve_code_dumpf(denv, "last");
+ } else {
+ return FALSE;
+ }
+ }
+
+ return
+ sieve_opr_string_dump(denv, address, "field-name") &&
+ sieve_opr_string_dump(denv, address, "value");
+}
+
+/*
+ * Interpretation
+ */
+
+static int cmd_addheader_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ string_t *field_name;
+ string_t *value;
+ struct edit_mail *edmail;
+ bool last = FALSE;
+ int opt_code = 0;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Optional operands */
+
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_opr_optional_read(renv, address, &opt_code)) < 0 )
+ return SIEVE_EXEC_BIN_CORRUPT;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_LAST:
+ last = TRUE;
+ break;
+ default:
+ sieve_runtime_trace_error(renv, "unknown optional operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ }
+
+ /* Read message */
+
+ if ( (ret=sieve_opr_string_read
+ (renv, address, "field-name", &field_name)) <= 0 )
+ return ret;
+
+ if ( (ret=sieve_opr_string_read
+ (renv, address, "value", &value)) <= 0 )
+ return ret;
+
+ /*
+ * Verify arguments
+ */
+
+ if ( !rfc2822_header_field_name_verify
+ (str_c(field_name), str_len(field_name)) ) {
+ sieve_runtime_error(renv, NULL, "addheader action: "
+ "specified field name `%s' is invalid",
+ str_sanitize(str_c(field_name), 80));
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ if ( !ext_editheader_header_allow_add
+ (this_ext, str_c(field_name)) ) {
+ sieve_runtime_warning(renv, NULL, "addheader action: "
+ "adding specified header field `%s' is forbidden; "
+ "modification denied", str_sanitize(str_c(field_name), 80));
+ return SIEVE_EXEC_OK;
+ }
+
+ if ( _str_contains_nul(value) ) {
+ sieve_runtime_error(renv, NULL, "addheader action: "
+ "specified value `%s' is invalid (contains NUL character)",
+ str_sanitize(str_c(value), 80));
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ if ( ext_editheader_header_too_large(this_ext, str_len(value)) ) {
+ sieve_runtime_error(renv, NULL, "addheader action: "
+ "specified header value `%s' is too large (%zu bytes)",
+ str_sanitize(str_c(value), 80), str_len(value));
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "addheader \"%s: %s\"",
+ str_sanitize(str_c(field_name), 80), str_sanitize(str_c(value), 80));
+
+ edmail = sieve_message_edit(renv->msgctx);
+ edit_mail_header_add(edmail,
+ rfc2822_header_field_name_sanitize(str_c(field_name)),
+ str_c(value), last);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/cmd-deleteheader.c b/pigeonhole/src/lib-sieve/plugins/editheader/cmd-deleteheader.c
new file mode 100644
index 0000000..a6964b7
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/editheader/cmd-deleteheader.c
@@ -0,0 +1,551 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "mail-storage.h"
+
+#include "rfc2822.h"
+#include "edit-mail.h"
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-message.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-editheader-common.h"
+
+/*
+ * Deleteheader command
+ *
+ * Syntax:
+ * deleteheader [":index" <fieldno: number> [":last"]]
+ * [COMPARATOR] [MATCH-TYPE]
+ * <field-name: string> [<value-patterns: string-list>]
+ */
+
+static bool cmd_deleteheader_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool cmd_deleteheader_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_deleteheader_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd);
+
+const struct sieve_command_def deleteheader_command = {
+ .identifier = "deleteheader",
+ .type = SCT_COMMAND,
+ .positional_args = -1, /* We check positional arguments ourselves */
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_deleteheader_registered,
+ .validate = cmd_deleteheader_validate,
+ .generate = cmd_deleteheader_generate
+};
+
+/*
+ * Deleteheader command tags
+ */
+
+/* Forward declarations */
+
+static bool cmd_deleteheader_validate_index_tag
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool cmd_deleteheader_validate_last_tag
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+/* Argument objects */
+
+static const struct sieve_argument_def deleteheader_index_tag = {
+ .identifier = "index",
+ .validate = cmd_deleteheader_validate_index_tag
+};
+
+static const struct sieve_argument_def deleteheader_last_tag = {
+ .identifier = "last",
+ .validate = cmd_deleteheader_validate_last_tag
+};
+
+/* Codes for optional arguments */
+
+enum cmd_deleteheader_optional {
+ OPT_INDEX = SIEVE_MATCH_OPT_LAST,
+ OPT_LAST
+};
+
+/*
+ * Deleteheader operation
+ */
+
+static bool cmd_deleteheader_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_deleteheader_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def deleteheader_operation = {
+ .mnemonic = "DELETEHEADER",
+ .ext_def = &editheader_extension,
+ .code = EXT_EDITHEADER_OPERATION_DELETEHEADER,
+ .dump = cmd_deleteheader_operation_dump,
+ .execute = cmd_deleteheader_operation_execute
+};
+
+/*
+ * Command registration
+ */
+
+static bool cmd_deleteheader_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_command_registration *cmd_reg)
+{
+ /* The order of these is not significant */
+ sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+ sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &deleteheader_index_tag, OPT_INDEX);
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &deleteheader_last_tag, OPT_LAST);
+
+ return TRUE;
+}
+
+/*
+ * Command validation context
+ */
+
+struct cmd_deleteheader_context_data {
+ struct sieve_ast_argument *arg_index;
+ struct sieve_ast_argument *arg_last;
+};
+
+/*
+ * Tag validation
+ */
+
+static struct cmd_deleteheader_context_data *
+cmd_deleteheader_get_context
+(struct sieve_command *cmd)
+{
+ struct cmd_deleteheader_context_data *ctx_data =
+ (struct cmd_deleteheader_context_data *)cmd->data;
+
+ if ( ctx_data != NULL ) return ctx_data;
+
+ ctx_data = p_new
+ (sieve_command_pool(cmd), struct cmd_deleteheader_context_data, 1);
+ cmd->data = (void *)ctx_data;
+
+ return ctx_data;
+}
+
+static bool cmd_deleteheader_validate_index_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+ struct cmd_deleteheader_context_data *ctx_data;
+ sieve_number_t index;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :index number
+ */
+ if ( !sieve_validate_tag_parameter
+ (valdtr, cmd, tag, *arg, NULL, 0, SAAT_NUMBER, FALSE) ) {
+ return FALSE;
+ }
+
+ index = sieve_ast_argument_number(*arg);
+ if ( index > INT_MAX ) {
+ sieve_argument_validate_warning(valdtr, *arg,
+ "the :%s tag for the %s %s has a parameter value '%llu' "
+ "exceeding the maximum (%d)",
+ sieve_argument_identifier(tag), sieve_command_identifier(cmd),
+ sieve_command_type_name(cmd), (unsigned long long) index,
+ INT_MAX);
+ return FALSE;
+ }
+
+ ctx_data = cmd_deleteheader_get_context(cmd);
+ ctx_data->arg_index = *arg;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+static bool cmd_deleteheader_validate_last_tag
+(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct cmd_deleteheader_context_data *ctx_data;
+
+ ctx_data = cmd_deleteheader_get_context(cmd);
+ ctx_data->arg_last = *arg;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+/*
+ * Validation
+ */
+
+static bool cmd_deleteheader_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg = cmd->first_positional;
+ struct cmd_deleteheader_context_data *ctx_data =
+ (struct cmd_deleteheader_context_data *)cmd->data;
+ struct sieve_comparator cmp_default =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+ struct sieve_match_type mcht_default =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+
+ if ( ctx_data != NULL ) {
+ if ( ctx_data->arg_last != NULL && ctx_data->arg_index == NULL ) {
+ sieve_argument_validate_error(valdtr, ctx_data->arg_last,
+ "the :last tag for the %s %s cannot be specified "
+ "without the :index tag",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd));
+ }
+ }
+
+ /* Field name argument */
+
+ if ( arg == NULL ) {
+ sieve_command_validate_error(valdtr, cmd,
+ "the %s %s expects at least one positional argument, but none was found",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd));
+ return FALSE;
+ }
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, cmd, arg, "field name", 1, SAAT_STRING_LIST) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) )
+ return FALSE;
+
+ if ( sieve_argument_is_string_literal(arg) ) {
+ string_t *fname = sieve_ast_argument_str(arg);
+
+ if ( !rfc2822_header_field_name_verify(str_c(fname), str_len(fname)) ) {
+ sieve_argument_validate_error(valdtr, arg, "deleteheader command:"
+ "specified field name `%s' is invalid",
+ str_sanitize(str_c(fname), 80));
+ return FALSE;
+ }
+
+ if ( !ext_editheader_header_allow_delete
+ (cmd->ext, str_c(fname)) ) {
+ sieve_argument_validate_warning
+ (valdtr, arg, "deleteheader command: "
+ "deleting specified header field `%s' is forbidden; "
+ "modification will be denied",
+ str_sanitize(str_c(fname), 80));
+ }
+ }
+
+ /* Value patterns argument */
+
+ arg = sieve_ast_argument_next(arg);
+ if ( arg == NULL ) {
+ /* There is none; let's not generate code for useless match arguments */
+ sieve_match_type_arguments_remove(valdtr, cmd);
+
+ return TRUE;
+ }
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, cmd, arg, "value patterns", 2, SAAT_STRING_LIST) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) )
+ return FALSE;
+
+ /* Validate the value patterns to a specified match type */
+ return sieve_match_type_validate
+ (valdtr, cmd, arg, &mcht_default, &cmp_default);
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_deleteheader_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &deleteheader_operation);
+
+ /* Generate arguments */
+ if ( !sieve_generate_arguments(cgenv, cmd, NULL) )
+ return FALSE;
+
+ /* Emit a placeholder when the value-patterns argument is missing */
+ if ( sieve_ast_argument_next(cmd->first_positional) == NULL )
+ sieve_opr_omitted_emit(cgenv->sblock);
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool cmd_deleteheader_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "DELETEHEADER");
+ sieve_code_descend(denv);
+
+ /* Optional operands */
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code)) < 0 )
+ return FALSE;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_INDEX:
+ if ( !sieve_opr_number_dump(denv, address, "index") )
+ return FALSE;
+ break;
+ case OPT_LAST:
+ sieve_code_dumpf(denv, "last");
+ break;
+ default:
+ return FALSE;
+ }
+ };
+
+ if ( !sieve_opr_string_dump(denv, address, "field name") )
+ return FALSE;
+
+ return sieve_opr_stringlist_dump_ex(denv, address, "value patterns", "");
+}
+
+/*
+ * Code execution
+ */
+
+static int cmd_deleteheader_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ int opt_code = 0;
+ struct sieve_comparator cmp =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+ struct sieve_match_type mcht =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ string_t *field_name;
+ struct sieve_stringlist *vpattern_list = NULL;
+ struct edit_mail *edmail;
+ sieve_number_t index_offset = 0;
+ bool index_last = FALSE;
+ bool trace = FALSE;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_match_opr_optional_read
+ (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 )
+ return ret;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_INDEX:
+ if ( (ret=sieve_opr_number_read(renv, address, "index", &index_offset))
+ <= 0 )
+ return ret;
+
+ if ( index_offset > INT_MAX ) {
+ sieve_runtime_trace_error(renv, "index is > %d", INT_MAX);
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ break;
+ case OPT_LAST:
+ index_last = TRUE;
+ break;
+ default:
+ sieve_runtime_trace_error(renv, "unknown optional operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ }
+
+ /* Read field-name */
+ if ( (ret=sieve_opr_string_read(renv, address, "field-name", &field_name))
+ <= 0 )
+ return ret;
+
+ /* Read value-patterns */
+ if ( (ret=sieve_opr_stringlist_read_ex
+ (renv, address, "value-patterns", TRUE, &vpattern_list)) <= 0 )
+ return ret;
+
+ /*
+ * Verify arguments
+ */
+
+ if ( !rfc2822_header_field_name_verify
+ (str_c(field_name), str_len(field_name)) ) {
+ sieve_runtime_error(renv, NULL, "deleteheader action: "
+ "specified field name `%s' is invalid",
+ str_sanitize(str_c(field_name), 80));
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ if ( !ext_editheader_header_allow_delete
+ (this_ext, str_c(field_name)) ) {
+ sieve_runtime_warning(renv, NULL, "deleteheader action: "
+ "deleting specified header field `%s' is forbidden; "
+ "modification denied",
+ str_sanitize(str_c(field_name), 80));
+ return SIEVE_EXEC_OK;
+ }
+
+ /*
+ * Execute command
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "deleteheader command");
+
+ /* Start editing the mail */
+ edmail = sieve_message_edit(renv->msgctx);
+
+ trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS);
+
+ /* Either do string matching or just kill all/indexed notify action(s) */
+ if ( vpattern_list != NULL ) {
+ struct edit_mail_header_iter *edhiter;
+ struct sieve_match_context *mctx;
+
+ if ( trace ) {
+ sieve_runtime_trace_descend(renv);
+ if ( index_offset != 0 ) {
+ sieve_runtime_trace(renv, 0,
+ "deleting matching occurrences of header `%s' at index %llu%s",
+ str_c(field_name), (unsigned long long)index_offset,
+ ( index_last ? " from last": ""));
+ } else {
+ sieve_runtime_trace(renv, 0,
+ "deleting matching occurrences of header `%s'", str_c(field_name));
+ }
+ }
+
+ /* Iterate through all headers and delete those that match */
+ if ( (ret=edit_mail_headers_iterate_init
+ (edmail, str_c(field_name), index_last, &edhiter)) > 0 )
+ {
+ int mret = 0;
+ sieve_number_t pos = 0;
+
+ /* Initialize match */
+ mctx = sieve_match_begin(renv, &mcht, &cmp);
+
+ /* Match */
+ for (;;) {
+ pos++;
+
+ /* Check index if any */
+ if ( index_offset == 0 || pos == index_offset ) {
+ const char *value;
+ int match;
+
+ /* Match value against all value patterns */
+ edit_mail_headers_iterate_get(edhiter, &value);
+ if ( (match=sieve_match_value
+ (mctx, value, strlen(value), vpattern_list)) < 0 )
+ break;
+
+ if ( match > 0 ) {
+ /* Remove it and iterate to next */
+ sieve_runtime_trace(renv, 0, "deleting header with value `%s'",
+ value);
+
+ if ( !edit_mail_headers_iterate_remove(edhiter) ) break;
+ continue;
+ }
+ }
+
+ if ( !edit_mail_headers_iterate_next(edhiter) )
+ break;
+ }
+
+ /* Finish match */
+ mret = sieve_match_end(&mctx, &ret);
+
+ edit_mail_headers_iterate_deinit(&edhiter);
+
+ if ( mret < 0 )
+ return ret;
+ }
+
+ if ( ret == 0 ) {
+ sieve_runtime_trace(renv, 0, "header `%s' not found", str_c(field_name));
+ } else if ( ret < 0 ) {
+ sieve_runtime_warning(renv, NULL, "deleteheader action: "
+ "failed to delete occurrences of header `%s' (this should not happen!)",
+ str_c(field_name));
+ }
+
+ } else {
+ int index = ( index_last ? -((int)index_offset) : ((int)index_offset) );
+
+ if ( trace ) {
+ sieve_runtime_trace_descend(renv);
+ if ( index_offset != 0 ) {
+ sieve_runtime_trace(renv, 0, "deleting header `%s' at index %llu%s",
+ str_c(field_name), (unsigned long long)index_offset,
+ ( index_last ? " from last": ""));
+ } else {
+ sieve_runtime_trace(renv, 0, "deleting header `%s'", str_c(field_name));
+ }
+ }
+
+ /* Delete all occurrences of header */
+ ret = edit_mail_header_delete(edmail, str_c(field_name), index);
+
+ if ( ret < 0 ) {
+ sieve_runtime_warning(renv, NULL, "deleteheader action: "
+ "failed to delete occurrences of header `%s' (this should not happen!)",
+ str_c(field_name));
+ } else if ( trace ) {
+ sieve_runtime_trace(renv, 0, "deleted %d occurrences of header `%s'",
+ ret, str_c(field_name));
+ }
+
+ }
+
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.c b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.c
new file mode 100644
index 0000000..89fb180
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.c
@@ -0,0 +1,211 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "mempool.h"
+#include "array.h"
+
+#include "rfc2822.h"
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+#include "sieve-settings.h"
+#include "sieve-extensions.h"
+
+#include "ext-editheader-limits.h"
+#include "ext-editheader-common.h"
+
+/*
+ * Extension configuration
+ */
+
+struct ext_editheader_header {
+ const char *name;
+
+ bool forbid_add:1;
+ bool forbid_delete:1;
+};
+
+struct ext_editheader_config {
+ pool_t pool;
+
+ ARRAY(struct ext_editheader_header) headers;
+
+ size_t max_header_size;
+};
+
+static struct ext_editheader_header *
+ext_editheader_config_header_find(struct ext_editheader_config *ext_config,
+ const char *hname)
+{
+ struct ext_editheader_header *headers;
+ unsigned int count, i;
+
+ headers = array_get_modifiable(&ext_config->headers, &count);
+ for (i = 0; i < count; i++) {
+ if (strcasecmp(hname, headers[i].name) == 0)
+ return &headers[i];
+ }
+ return NULL;
+}
+
+static void
+ext_editheader_config_headers(struct sieve_instance *svinst,
+ struct ext_editheader_config *ext_config,
+ const char *setting, bool forbid_add,
+ bool forbid_delete)
+{
+ const char *setval;
+
+ setval = sieve_setting_get(svinst, setting);
+ if (setval != NULL) {
+ const char **headers = t_strsplit_spaces(setval, " \t");
+
+ while (*headers != NULL) {
+ struct ext_editheader_header *header;
+
+ if (!rfc2822_header_field_name_verify(
+ *headers, strlen(*headers))) {
+ e_warning(svinst->event, "editheader: "
+ "setting %s contains invalid header field name "
+ "`%s' (ignored)",
+ setting, *headers);
+ headers++;
+ continue;
+ }
+
+ header = ext_editheader_config_header_find(
+ ext_config, *headers);
+ if (header == NULL) {
+ header = array_append_space(
+ &ext_config->headers);
+ header->name = p_strdup(ext_config->pool,
+ *headers);
+ }
+
+ if (forbid_add)
+ header->forbid_add = TRUE;
+ if (forbid_delete)
+ header->forbid_delete = TRUE;
+
+ headers++;
+ }
+ }
+}
+
+bool ext_editheader_load(const struct sieve_extension *ext, void **context)
+{
+ struct ext_editheader_config *ext_config;
+ struct sieve_instance *svinst = ext->svinst;
+ size_t max_header_size;
+ pool_t pool;
+
+ if (*context != NULL) {
+ ext_editheader_unload(ext);
+ *context = NULL;
+ }
+
+ T_BEGIN {
+ pool = pool_alloconly_create("editheader_config", 1024);
+ ext_config = p_new(pool, struct ext_editheader_config, 1);
+ ext_config->pool = pool;
+ ext_config->max_header_size =
+ EXT_EDITHEADER_DEFAULT_MAX_HEADER_SIZE;
+
+ p_array_init(&ext_config->headers, pool, 16);
+
+ ext_editheader_config_headers(
+ svinst, ext_config,
+ "sieve_editheader_protected", TRUE, TRUE);
+ ext_editheader_config_headers(
+ svinst, ext_config,
+ "sieve_editheader_forbid_add", TRUE, FALSE);
+ ext_editheader_config_headers(
+ svinst, ext_config,
+ "sieve_editheader_forbid_delete", FALSE, TRUE);
+
+ if (sieve_setting_get_size_value(
+ svinst, "sieve_editheader_max_header_size",
+ &max_header_size)) {
+ if (max_header_size < EXT_EDITHEADER_MINIMUM_MAX_HEADER_SIZE) {
+ e_warning(svinst->event, "editheader: "
+ "value of sieve_editheader_max_header_size setting "
+ "(=%zu) is less than the minimum (=%zu) "
+ "(ignored)", max_header_size,
+ (size_t)EXT_EDITHEADER_MINIMUM_MAX_HEADER_SIZE);
+ } else {
+ ext_config->max_header_size = max_header_size;
+ }
+ }
+ } T_END;
+
+ *context = (void *)ext_config;
+ return TRUE;
+}
+
+void ext_editheader_unload(const struct sieve_extension *ext)
+{
+ struct ext_editheader_config *ext_config =
+ (struct ext_editheader_config *)ext->context;
+
+ if (ext_config != NULL)
+ pool_unref(&ext_config->pool);
+}
+
+/*
+ * Protected headers
+ */
+
+bool ext_editheader_header_allow_add(const struct sieve_extension *ext,
+ const char *hname)
+{
+ struct ext_editheader_config *ext_config =
+ (struct ext_editheader_config *)ext->context;
+ const struct ext_editheader_header *header;
+
+ if (strcasecmp(hname, "subject") == 0)
+ return TRUE;
+ if (strcasecmp(hname, "x-sieve-redirected-from") == 0)
+ return FALSE;
+
+ header = ext_editheader_config_header_find(ext_config, hname);
+ if (header == NULL)
+ return TRUE;
+
+ return !header->forbid_add;
+}
+
+bool ext_editheader_header_allow_delete(const struct sieve_extension *ext,
+ const char *hname)
+{
+ struct ext_editheader_config *ext_config =
+ (struct ext_editheader_config *)ext->context;
+ const struct ext_editheader_header *header;
+
+ if (strcasecmp(hname, "received") == 0 ||
+ strcasecmp(hname, "auto-submitted") == 0)
+ return FALSE;
+ if (strcasecmp(hname, "x-sieve-redirected-from") == 0)
+ return FALSE;
+ if (strcasecmp(hname, "subject") == 0)
+ return TRUE;
+
+ header = ext_editheader_config_header_find(ext_config, hname);
+ if (header == NULL)
+ return TRUE;
+
+ return !header->forbid_delete;
+}
+
+/*
+ * Limits
+ */
+
+bool ext_editheader_header_too_large(const struct sieve_extension *ext,
+ size_t size)
+{
+ struct ext_editheader_config *ext_config =
+ (struct ext_editheader_config *)ext->context;
+
+ return size > ext_config->max_header_size;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.h b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.h
new file mode 100644
index 0000000..8a6cc36
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-common.h
@@ -0,0 +1,48 @@
+#ifndef EXT_EDITHEADER_COMMON_H
+#define EXT_EDITHEADER_COMMON_H
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def addheader_command;
+extern const struct sieve_command_def deleteheader_command;
+
+/*
+ * Operations
+ */
+
+enum ext_imap4flags_opcode {
+ EXT_EDITHEADER_OPERATION_ADDHEADER,
+ EXT_EDITHEADER_OPERATION_DELETEHEADER,
+};
+
+extern const struct sieve_operation_def addheader_operation;
+extern const struct sieve_operation_def deleteheader_operation;
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def editheader_extension;
+
+bool ext_editheader_load(const struct sieve_extension *ext, void **context);
+void ext_editheader_unload(const struct sieve_extension *ext);
+
+/*
+ * Protected headers
+ */
+
+bool ext_editheader_header_allow_add(const struct sieve_extension *ext,
+ const char *hname);
+bool ext_editheader_header_allow_delete(const struct sieve_extension *ext,
+ const char *hname);
+
+/*
+ * Limits
+ */
+
+bool ext_editheader_header_too_large(const struct sieve_extension *ext,
+ size_t size);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-limits.h b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-limits.h
new file mode 100644
index 0000000..a8e834b
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader-limits.h
@@ -0,0 +1,7 @@
+#ifndef EXT_EDITHEADER_LIMITS_H
+#define EXT_EDITHEADER_LIMITS_H
+
+#define EXT_EDITHEADER_MINIMUM_MAX_HEADER_SIZE 1024
+#define EXT_EDITHEADER_DEFAULT_MAX_HEADER_SIZE 2048
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader.c b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader.c
new file mode 100644
index 0000000..dc8eb12
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/editheader/ext-editheader.c
@@ -0,0 +1,66 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension debug
+ * ---------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5293
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-editheader-common.h"
+
+/*
+ * Operations
+ */
+
+const struct sieve_operation_def *editheader_operations[] = {
+ &addheader_operation,
+ &deleteheader_operation
+};
+
+/*
+ * Extension
+ */
+
+static bool ext_editheader_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *validator);
+
+const struct sieve_extension_def editheader_extension = {
+ .name = "editheader",
+ .load = ext_editheader_load,
+ .unload = ext_editheader_unload,
+ .validator_load = ext_editheader_validator_load,
+ SIEVE_EXT_DEFINE_OPERATIONS(editheader_operations)
+};
+
+static bool ext_editheader_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *validator)
+{
+ /* Register new commands */
+ sieve_validator_register_command(validator, ext, &addheader_command);
+ sieve_validator_register_command(validator, ext, &deleteheader_command);
+
+ return TRUE;
+}
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.am b/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.am
new file mode 100644
index 0000000..4b65630
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.am
@@ -0,0 +1,44 @@
+SUBDIRS = mailto
+
+noinst_LTLIBRARIES = libsieve_ext_enotify.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../variables \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-notify.c
+
+tests = \
+ tst-valid-notify-method.c \
+ tst-notify-method-capability.c
+
+var_modifiers = \
+ vmodf-encodeurl.c
+
+notify_methods = \
+ ./mailto/libsieve_ext_enotify_mailto.la
+
+libsieve_ext_enotify_la_DEPENDENCIES = \
+ $(notify_methods)
+libsieve_ext_enotify_la_LIBADD = \
+ $(notify_methods)
+
+libsieve_ext_enotify_la_SOURCES = \
+ ext-enotify.c \
+ ext-enotify-common.c \
+ $(commands) \
+ $(tests) \
+ $(var_modifiers)
+
+public_headers = \
+ sieve-ext-enotify.h
+
+headers = \
+ ext-enotify-limits.h \
+ ext-enotify-common.h
+
+pkginc_libdir=$(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.in b/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.in
new file mode 100644
index 0000000..fea2423
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/Makefile.in
@@ -0,0 +1,904 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/enotify
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(pkginc_lib_HEADERS) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__objects_1 = cmd-notify.lo
+am__objects_2 = tst-valid-notify-method.lo \
+ tst-notify-method-capability.lo
+am__objects_3 = vmodf-encodeurl.lo
+am_libsieve_ext_enotify_la_OBJECTS = ext-enotify.lo \
+ ext-enotify-common.lo $(am__objects_1) $(am__objects_2) \
+ $(am__objects_3)
+libsieve_ext_enotify_la_OBJECTS = \
+ $(am_libsieve_ext_enotify_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-notify.Plo \
+ ./$(DEPDIR)/ext-enotify-common.Plo ./$(DEPDIR)/ext-enotify.Plo \
+ ./$(DEPDIR)/tst-notify-method-capability.Plo \
+ ./$(DEPDIR)/tst-valid-notify-method.Plo \
+ ./$(DEPDIR)/vmodf-encodeurl.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_enotify_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_enotify_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(pkginc_libdir)"
+HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = mailto
+noinst_LTLIBRARIES = libsieve_ext_enotify.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../variables \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-notify.c
+
+tests = \
+ tst-valid-notify-method.c \
+ tst-notify-method-capability.c
+
+var_modifiers = \
+ vmodf-encodeurl.c
+
+notify_methods = \
+ ./mailto/libsieve_ext_enotify_mailto.la
+
+libsieve_ext_enotify_la_DEPENDENCIES = \
+ $(notify_methods)
+
+libsieve_ext_enotify_la_LIBADD = \
+ $(notify_methods)
+
+libsieve_ext_enotify_la_SOURCES = \
+ ext-enotify.c \
+ ext-enotify-common.c \
+ $(commands) \
+ $(tests) \
+ $(var_modifiers)
+
+public_headers = \
+ sieve-ext-enotify.h
+
+headers = \
+ ext-enotify-limits.h \
+ ext-enotify-common.h
+
+pkginc_libdir = $(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/enotify/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/enotify/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_enotify.la: $(libsieve_ext_enotify_la_OBJECTS) $(libsieve_ext_enotify_la_DEPENDENCIES) $(EXTRA_libsieve_ext_enotify_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_enotify_la_OBJECTS) $(libsieve_ext_enotify_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-notify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-enotify-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-enotify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-notify-method-capability.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-valid-notify-method.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vmodf-encodeurl.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkginc_libHEADERS: $(pkginc_lib_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \
+ done
+
+uninstall-pkginc_libHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir)
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(pkginc_libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/cmd-notify.Plo
+ -rm -f ./$(DEPDIR)/ext-enotify-common.Plo
+ -rm -f ./$(DEPDIR)/ext-enotify.Plo
+ -rm -f ./$(DEPDIR)/tst-notify-method-capability.Plo
+ -rm -f ./$(DEPDIR)/tst-valid-notify-method.Plo
+ -rm -f ./$(DEPDIR)/vmodf-encodeurl.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-pkginc_libHEADERS
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/cmd-notify.Plo
+ -rm -f ./$(DEPDIR)/ext-enotify-common.Plo
+ -rm -f ./$(DEPDIR)/ext-enotify.Plo
+ -rm -f ./$(DEPDIR)/tst-notify-method-capability.Plo
+ -rm -f ./$(DEPDIR)/tst-valid-notify-method.Plo
+ -rm -f ./$(DEPDIR)/vmodf-encodeurl.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-pkginc_libHEADERS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic clean-libtool \
+ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-pkginc_libHEADERS \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am \
+ uninstall-pkginc_libHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/cmd-notify.c b/pigeonhole/src/lib-sieve/plugins/enotify/cmd-notify.c
new file mode 100644
index 0000000..4fe121a
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/cmd-notify.c
@@ -0,0 +1,621 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-result.h"
+
+#include "ext-enotify-common.h"
+
+/*
+ * Forward declarations
+ */
+
+static const struct sieve_argument_def notify_importance_tag;
+static const struct sieve_argument_def notify_from_tag;
+static const struct sieve_argument_def notify_options_tag;
+static const struct sieve_argument_def notify_message_tag;
+
+/*
+ * Notify command
+ *
+ * Syntax:
+ * notify [":from" string]
+ * [":importance" <"1" / "2" / "3">]
+ * [":options" string-list]
+ * [":message" string]
+ * <method: string>
+ */
+
+static bool
+cmd_notify_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool
+cmd_notify_pre_validate(struct sieve_validator *validator,
+ struct sieve_command *cmd);
+static bool
+cmd_notify_validate(struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool
+cmd_notify_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+const struct sieve_command_def notify_command = {
+ .identifier = "notify",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_notify_registered,
+ .pre_validate = cmd_notify_pre_validate,
+ .validate = cmd_notify_validate,
+ .generate = cmd_notify_generate,
+};
+
+/*
+ * Notify command tags
+ */
+
+/* Forward declarations */
+
+static bool
+cmd_notify_validate_string_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool
+cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool
+cmd_notify_validate_importance_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+/* Argument objects */
+
+static const struct sieve_argument_def notify_from_tag = {
+ .identifier = "from",
+ .validate = cmd_notify_validate_string_tag
+};
+
+static const struct sieve_argument_def notify_options_tag = {
+ .identifier = "options",
+ .validate = cmd_notify_validate_stringlist_tag
+};
+
+static const struct sieve_argument_def notify_message_tag = {
+ .identifier = "message",
+ .validate = cmd_notify_validate_string_tag
+};
+
+static const struct sieve_argument_def notify_importance_tag = {
+ .identifier = "importance",
+ .validate = cmd_notify_validate_importance_tag
+};
+
+/*
+ * Notify operation
+ */
+
+static bool
+cmd_notify_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+cmd_notify_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def notify_operation = {
+ .mnemonic = "NOTIFY",
+ .ext_def = &enotify_extension,
+ .code = EXT_ENOTIFY_OPERATION_NOTIFY,
+ .dump = cmd_notify_operation_dump,
+ .execute = cmd_notify_operation_execute
+};
+
+/*
+ * Notify action
+ */
+
+/* Forward declarations */
+
+static int
+act_notify_check_duplicate(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other);
+static void
+act_notify_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep);
+static int
+act_notify_commit(const struct sieve_action_exec_env *aenv, void *tr_context);
+
+/* Action object */
+
+const struct sieve_action_def act_notify = {
+ .name = "notify",
+ .check_duplicate =act_notify_check_duplicate,
+ .print = act_notify_print,
+ .commit = act_notify_commit,
+};
+
+/*
+ * Command validation context
+ */
+
+struct cmd_notify_context_data {
+ struct sieve_ast_argument *from;
+ struct sieve_ast_argument *message;
+ struct sieve_ast_argument *options;
+};
+
+/*
+ * Tag validation
+ */
+
+static bool
+cmd_notify_validate_string_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+ struct cmd_notify_context_data *ctx_data =
+ (struct cmd_notify_context_data *)cmd->data;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :from <string>
+ * :message <string>
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_STRING, FALSE))
+ return FALSE;
+
+ if (sieve_argument_is(tag, notify_from_tag)) {
+ ctx_data->from = *arg;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+ } else if (sieve_argument_is(tag, notify_message_tag)) {
+ ctx_data->message = *arg;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+ }
+ return TRUE;
+}
+
+static bool
+cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+ struct cmd_notify_context_data *ctx_data =
+ (struct cmd_notify_context_data *)cmd->data;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :options string-list
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_STRING_LIST, FALSE))
+ return FALSE;
+
+ /* Assign context */
+ ctx_data->options = *arg;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+static bool
+cmd_notify_validate_importance_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd ATTR_UNUSED)
+{
+ const struct sieve_ast_argument *tag = *arg;
+ const char *impstr;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :importance <"1" / "2" / "3">
+ */
+ if (sieve_ast_argument_type(*arg) != SAAT_STRING) {
+ /* Not a string */
+ sieve_argument_validate_error(
+ valdtr, *arg,
+ "the :importance tag for the notify command requires a string parameter, "
+ "but %s was found", sieve_ast_argument_name(*arg));
+ return FALSE;
+ }
+
+ impstr = sieve_ast_argument_strc(*arg);
+ if (impstr[0] < '1' || impstr[0] > '3' || impstr[1] != '\0') {
+ /* Invalid importance */
+ sieve_argument_validate_error(
+ valdtr, *arg,
+ "invalid :importance value for notify command: %s",
+ impstr);
+ return FALSE;
+ }
+
+ sieve_ast_argument_number_substitute(*arg, impstr[0] - '0');
+ (*arg)->argument = sieve_argument_create((*arg)->ast, &number_argument,
+ tag->argument->ext,
+ tag->argument->id_code);
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+/*
+ * Command registration
+ */
+
+static bool
+cmd_notify_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &notify_importance_tag,
+ CMD_NOTIFY_OPT_IMPORTANCE);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &notify_from_tag, CMD_NOTIFY_OPT_FROM);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &notify_options_tag,
+ CMD_NOTIFY_OPT_OPTIONS);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &notify_message_tag,
+ CMD_NOTIFY_OPT_MESSAGE);
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool
+cmd_notify_pre_validate(struct sieve_validator *validator ATTR_UNUSED,
+ struct sieve_command *cmd)
+{
+ struct cmd_notify_context_data *ctx_data;
+
+ /* Assign context */
+ ctx_data = p_new(sieve_command_pool(cmd),
+ struct cmd_notify_context_data, 1);
+ cmd->data = ctx_data;
+
+ return TRUE;
+}
+
+static bool
+cmd_notify_validate(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg = cmd->first_positional;
+ struct cmd_notify_context_data *ctx_data =
+ (struct cmd_notify_context_data *)cmd->data;
+
+ if (!sieve_validate_positional_argument(valdtr, cmd, arg, "method", 1,
+ SAAT_STRING))
+ return FALSE;
+
+ if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE))
+ return FALSE;
+
+ return ext_enotify_compile_check_arguments(
+ valdtr, cmd, arg, ctx_data->message, ctx_data->from,
+ ctx_data->options);
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+cmd_notify_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &notify_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+cmd_notify_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "NOTIFY");
+ sieve_code_descend(denv);
+
+ /* Dump optional operands */
+
+ for (;;) {
+ int opt;
+ bool opok = TRUE;
+
+ if ((opt = sieve_opr_optional_dump(denv, address,
+ &opt_code)) < 0)
+ return FALSE;
+
+ if (opt == 0)
+ break;
+
+ switch (opt_code) {
+ case CMD_NOTIFY_OPT_IMPORTANCE:
+ opok = sieve_opr_number_dump(denv, address,
+ "importance");
+ break;
+ case CMD_NOTIFY_OPT_FROM:
+ opok = sieve_opr_string_dump(denv, address, "from");
+ break;
+ case CMD_NOTIFY_OPT_OPTIONS:
+ opok = sieve_opr_stringlist_dump(denv, address,
+ "options");
+ break;
+ case CMD_NOTIFY_OPT_MESSAGE:
+ opok = sieve_opr_string_dump(denv, address, "message");
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (!opok)
+ return FALSE;
+ }
+
+ /* Dump method operand */
+ return sieve_opr_string_dump(denv, address, "method");
+}
+
+/*
+ * Code execution
+ */
+
+static int
+cmd_notify_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct sieve_side_effects_list *slist = NULL;
+ struct sieve_enotify_action *act;
+ void *method_context;
+ pool_t pool;
+ int opt_code = 0;
+ sieve_number_t importance = 2;
+ struct sieve_stringlist *options = NULL;
+ const struct sieve_enotify_method *method;
+ string_t *method_uri, *message = NULL, *from = NULL;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Optional operands */
+
+ for (;;) {
+ int opt;
+
+ if ((opt = sieve_opr_optional_read(renv, address,
+ &opt_code)) < 0)
+ return SIEVE_EXEC_BIN_CORRUPT;
+
+ if (opt == 0) break;
+
+ switch (opt_code) {
+ case CMD_NOTIFY_OPT_IMPORTANCE:
+ ret = sieve_opr_number_read(renv, address, "importance",
+ &importance);
+ break;
+ case CMD_NOTIFY_OPT_FROM:
+ ret = sieve_opr_string_read(renv, address, "from",
+ &from);
+ break;
+ case CMD_NOTIFY_OPT_MESSAGE:
+ ret = sieve_opr_string_read(renv, address, "message",
+ &message);
+ break;
+ case CMD_NOTIFY_OPT_OPTIONS:
+ ret = sieve_opr_stringlist_read(renv, address,
+ "options", &options);
+ break;
+ default:
+ sieve_runtime_trace_error(
+ renv, "unknown optional operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if (ret <= 0)
+ return ret;
+ }
+
+ /* Method operand */
+
+ if ((ret = sieve_opr_string_read(renv, address, "method",
+ &method_uri)) <= 0)
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ /* Enforce 0 < importance < 4 (just to be sure) */
+
+ if (importance < 1)
+ importance = 1;
+ else if (importance > 3)
+ importance = 3;
+
+ /* Trace */
+
+ if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) {
+ sieve_runtime_trace(renv, 0, "notify action");
+ sieve_runtime_trace_descend(renv);
+ sieve_runtime_trace(renv, 0, "notify with uri `%s'",
+ str_sanitize(str_c(method_uri), 80));
+ }
+
+ /* Check operands */
+
+ if ((ret = ext_enotify_runtime_check_operands(renv, method_uri, message,
+ from, options, &method,
+ &method_context)) > 0)
+ {
+ /* Add notify action to the result */
+ pool = sieve_result_pool(renv->result);
+ act = p_new(pool, struct sieve_enotify_action, 1);
+ act->method = method;
+ act->method_context = method_context;
+ act->importance = importance;
+ if (message != NULL)
+ act->message = p_strdup(pool, str_c(message));
+ if (from != NULL)
+ act->from = p_strdup(pool, str_c(from));
+
+ if (sieve_result_add_action(renv, this_ext, "notify",
+ &act_notify, slist,
+ (void *)act, 0, FALSE) < 0)
+ return SIEVE_EXEC_FAILURE;
+
+ return SIEVE_EXEC_OK;
+ }
+ return ret;
+}
+
+/*
+ * Action
+ */
+
+/* Runtime verification */
+
+static int
+act_notify_check_duplicate(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ const struct sieve_enotify_action *nact, *nact_other;
+ const struct sieve_enotify_method_def *nmth_def;
+ struct sieve_enotify_env nenv;
+ int result;
+
+ if (act->context == NULL || act_other->context == NULL)
+ return 0;
+
+ nact = (const struct sieve_enotify_action *)act->context;
+ nact_other = (const struct sieve_enotify_action *)act_other->context;
+
+ if (nact->method == NULL || nact->method->def == NULL)
+ return 0;
+
+ nmth_def = nact->method->def;
+ if (nmth_def->action_check_duplicates == NULL)
+ return 0;
+
+ i_zero(&nenv);
+ nenv.svinst = eenv->svinst;
+ nenv.method = nact->method;
+ nenv.ehandler = renv->ehandler;
+ nenv.location = act->location;
+ nenv.event = event_create(nenv.svinst->event);
+ event_set_append_log_prefix(nenv.event, "notify: ");
+
+ result = nmth_def->action_check_duplicates(&nenv, nact, nact_other);
+
+ event_unref(&nenv.event);
+
+ return result;
+}
+
+/* Result printing */
+
+static void
+act_notify_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep ATTR_UNUSED)
+{
+ const struct sieve_enotify_action *act =
+ (const struct sieve_enotify_action *)action->context;
+ const struct sieve_enotify_method *method;
+
+ method = act->method;
+
+ if (method->def != NULL) {
+ sieve_result_action_printf(
+ rpenv, "send notification with method '%s:':",
+ method->def->identifier);
+
+ if (method->def->action_print != NULL) {
+ struct sieve_enotify_print_env penv;
+
+ i_zero(&penv);
+ penv.result_penv = rpenv;
+
+ method->def->action_print(&penv, act);
+ }
+ }
+}
+
+/* Result execution */
+
+static int
+act_notify_commit(const struct sieve_action_exec_env *aenv,
+ void *tr_context ATTR_UNUSED)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ const struct sieve_enotify_action *act =
+ (const struct sieve_enotify_action *)aenv->action->context;
+ const struct sieve_enotify_method *method = act->method;
+ struct sieve_enotify_exec_env nenv;
+ int ret = 0;
+
+ if (method->def != NULL && method->def->action_execute != NULL) {
+ /* Compose log structure */
+ i_zero(&nenv);
+ nenv.svinst = eenv->svinst;
+ nenv.flags = eenv->flags;
+ nenv.method = method;
+ nenv.scriptenv = eenv->scriptenv;
+ nenv.msgdata = eenv->msgdata;
+ nenv.msgctx = aenv->msgctx;
+
+ nenv.ehandler = aenv->ehandler;
+ nenv.event = aenv->event;
+
+ ret = method->def->action_execute(&nenv, act);
+ if (ret >= 0)
+ eenv->exec_status->significant_action_executed = TRUE;
+ }
+
+ return (ret >= 0 ? SIEVE_EXEC_OK : SIEVE_EXEC_TEMP_FAILURE);
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.c b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.c
new file mode 100644
index 0000000..e0539f0
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.c
@@ -0,0 +1,718 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-ast.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "ext-enotify-limits.h"
+#include "ext-enotify-common.h"
+
+#include <ctype.h>
+
+/* FIXME: (from draft RFC)
+
+ Header/envelope tests [Sieve] together with Sieve variables can be used to
+ extract the list of users to receive notifications from the incoming email
+ message or its envelope. This is potentially quite dangerous, as this can be
+ used for Deny Of Service attacks on recipients controlled by the message
+ sender. For this reason implementations SHOULD NOT allow use of variables
+ containing values extracted from the email message in the method parameter to
+ the notify action. Note that violation of this SHOULD NOT may result in* the
+ creation of an open relay, i.e. any sender would be able to create specially
+ crafted email messages that would result in notifications delivered to
+ recipients under the control of the sender. In worst case this might result
+ in financial loss by user controlling the Sieve script and/or by recipients
+ of notifications (e.g. if a notification is an SMS message).
+
+ --> This is currently not possible to check.
+ */
+
+/*
+ * Notify capability
+ */
+
+static const char *
+ext_notify_get_methods_string(const struct sieve_extension *ntfy_ext);
+
+const struct sieve_extension_capabilities notify_capabilities = {
+ "notify",
+ ext_notify_get_methods_string
+};
+
+/*
+ * Core notification methods
+ */
+
+extern const struct sieve_enotify_method_def mailto_notify;
+
+/*
+ * Notify method registry
+ */
+
+static const struct sieve_enotify_method *
+ext_enotify_method_register(struct sieve_instance *svinst,
+ struct ext_enotify_context *ectx,
+ const struct sieve_enotify_method_def *nmth_def)
+{
+ struct sieve_enotify_method *nmth;
+ int nmth_id = (int)array_count(&ectx->notify_methods);
+
+ nmth = array_append_space(&ectx->notify_methods);
+ nmth->def = nmth_def;
+ nmth->id = nmth_id;
+ nmth->svinst = svinst;
+
+ if (nmth_def->load != NULL)
+ nmth_def->load(nmth, &nmth->context);
+
+ return nmth;
+}
+
+void ext_enotify_methods_init(struct sieve_instance *svinst,
+ struct ext_enotify_context *ectx)
+{
+ p_array_init(&ectx->notify_methods, default_pool, 4);
+
+ ext_enotify_method_register(svinst, ectx, &mailto_notify);
+}
+
+void ext_enotify_methods_deinit(struct ext_enotify_context *ectx)
+{
+ const struct sieve_enotify_method *methods;
+ unsigned int meth_count, i;
+
+ methods = array_get(&ectx->notify_methods, &meth_count);
+ for (i = 0; i < meth_count; i++) {
+ if (methods[i].def != NULL && methods[i].def->unload != NULL)
+ methods[i].def->unload(&methods[i]);
+ }
+
+ array_free(&ectx->notify_methods);
+}
+
+const struct sieve_enotify_method *
+sieve_enotify_method_register(struct sieve_instance *svinst,
+ const struct sieve_enotify_method_def *nmth_def)
+{
+ const struct sieve_extension *ntfy_ext =
+ sieve_extension_get_by_name(svinst, "enotify");
+
+ if (ntfy_ext != NULL) {
+ struct ext_enotify_context *ectx =
+ (struct ext_enotify_context *) ntfy_ext->context;
+
+ return ext_enotify_method_register(svinst, ectx, nmth_def);
+ }
+ return NULL;
+}
+
+void sieve_enotify_method_unregister(const struct sieve_enotify_method *nmth)
+{
+ struct sieve_instance *svinst = nmth->svinst;
+ const struct sieve_extension *ntfy_ext =
+ sieve_extension_get_by_name(svinst, "enotify");
+
+ if (ntfy_ext != NULL) {
+ struct ext_enotify_context *ectx =
+ (struct ext_enotify_context *) ntfy_ext->context;
+ int nmth_id = nmth->id;
+
+ if (nmth_id >= 0 &&
+ nmth_id < (int)array_count(&ectx->notify_methods)) {
+ struct sieve_enotify_method *nmth_mod =
+ array_idx_modifiable(&ectx->notify_methods,
+ nmth_id);
+
+ nmth_mod->def = NULL;
+ }
+ }
+}
+
+const struct sieve_enotify_method *
+ext_enotify_method_find(const struct sieve_extension *ntfy_ext,
+ const char *identifier)
+{
+ struct ext_enotify_context *ectx =
+ (struct ext_enotify_context *)ntfy_ext->context;
+ unsigned int meth_count, i;
+ const struct sieve_enotify_method *methods;
+
+ methods = array_get(&ectx->notify_methods, &meth_count);
+ for (i = 0; i < meth_count; i++) {
+ if (methods[i].def == NULL)
+ continue;
+
+ if (strcasecmp(methods[i].def->identifier, identifier) == 0)
+ return &methods[i];
+ }
+ return NULL;
+}
+
+static const char *
+ext_notify_get_methods_string(const struct sieve_extension *ntfy_ext)
+{
+ struct ext_enotify_context *ectx =
+ (struct ext_enotify_context *) ntfy_ext->context;
+ unsigned int meth_count, i;
+ const struct sieve_enotify_method *methods;
+ string_t *result = t_str_new(128);
+
+ methods = array_get(&ectx->notify_methods, &meth_count);
+ if (meth_count > 0) {
+ for (i = 0; i < meth_count; i++) {
+ if (str_len(result) > 0)
+ str_append_c(result, ' ');
+ if (methods[i].def != NULL)
+ str_append(result, methods[i].def->identifier);
+ }
+ return str_c(result);
+ }
+ return NULL;
+}
+
+/*
+ * Compile-time argument validation
+ */
+
+static const char *ext_enotify_uri_scheme_parse(const char **uri_p)
+{
+ string_t *scheme = t_str_new(EXT_ENOTIFY_MAX_SCHEME_LEN);
+ const char *p = *uri_p;
+ unsigned int len = 0;
+
+ /* RFC 3968:
+
+ scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+
+ FIXME: we do not allow '%' in schemes. Is this correct?
+ */
+
+ if (!i_isalpha(*p))
+ return NULL;
+
+ str_append_c(scheme, *p);
+ p++;
+
+ while (*p != '\0' && len < EXT_ENOTIFY_MAX_SCHEME_LEN) {
+ if (!i_isalnum(*p) && *p != '+' && *p != '-' && *p != '.')
+ break;
+
+ str_append_c(scheme, *p);
+ p++;
+ len++;
+ }
+
+ if (*p != ':')
+ return NULL;
+ p++;
+
+ *uri_p = p;
+ return str_c(scheme);
+}
+
+static bool
+ext_enotify_option_parse(struct sieve_enotify_env *nenv,
+ const char *option, bool name_only,
+ const char **opt_name_r, const char **opt_value_r)
+{
+ const char *p = option;
+
+ /* "<optionname>=<value>".
+
+ l-d = ALPHA / DIGIT
+ l-d-p = l-d / "." / "-" / "_"
+ optionname = l-d *l-d-p
+ value = *(%x01-09 / %x0B-0C / %x0E-FF)
+ */
+
+ /*
+ * Parse option name
+ */
+
+ /* optionname = l-d *l-d-p
+ */
+
+ /* Explicitly report empty option as such */
+ if (*p == '\0') {
+ sieve_enotify_error(nenv, "empty option specified");
+ return FALSE;
+ }
+
+ /* l-d = ALPHA / DIGIT */
+ if (i_isalnum(*p)) {
+ p++;
+
+ /* l-d-p = l-d / "." / "-" / "_" */
+ while (i_isalnum(*p) || *p == '.' || *p == '-' || *p == '_')
+ p++;
+ }
+
+ /* Parsing must end at '=' and we must parse at least one character */
+ if (*p != '=' || p == option) {
+ sieve_enotify_error(
+ nenv, "invalid option name specified in option '%s'",
+ str_sanitize(option, 80));
+ return FALSE;
+ }
+
+ /* Assign option name */
+ if (opt_name_r != NULL)
+ *opt_name_r = t_strdup_until(option, p);
+
+ /* Skip '=' */
+ p++;
+
+ /* Exit now if only the option name is of interest */
+ if (name_only)
+ return TRUE;
+
+ /*
+ * Parse option value
+ */
+
+ /* value = *(%x01-09 / %x0B-0C / %x0E-FF) */
+ while (*p != '\0' && *p != 0x0A && *p != 0x0D)
+ p++;
+
+ /* Parse must end at end of string */
+ if (*p != '\0') {
+ sieve_enotify_error(
+ nenv, "notify command: "
+ "invalid option value specified in option '%s'",
+ str_sanitize(option, 80));
+ return FALSE;
+ }
+
+ /* Assign option value */
+ if (opt_value_r != NULL)
+ *opt_value_r = p;
+
+ return TRUE;
+}
+
+struct _ext_enotify_option_check_context {
+ struct sieve_instance *svinst;
+ struct sieve_validator *valdtr;
+ const struct sieve_enotify_method *method;
+};
+
+static int
+_ext_enotify_option_check(void *context, struct sieve_ast_argument *arg)
+{
+ struct _ext_enotify_option_check_context *optn_context =
+ (struct _ext_enotify_option_check_context *) context;
+ struct sieve_validator *valdtr = optn_context->valdtr;
+ const struct sieve_enotify_method *method = optn_context->method;
+ struct sieve_enotify_env nenv;
+ const char *option = sieve_ast_argument_strc(arg);
+ const char *opt_name = NULL, *opt_value = NULL;
+ bool check = TRUE;
+ int result = 1;
+
+ /* Compose log structure */
+ i_zero(&nenv);
+ nenv.svinst = optn_context->svinst;
+ nenv.method = method;
+ nenv.ehandler = sieve_validator_error_handler(valdtr);
+ nenv.location = sieve_error_script_location(
+ sieve_validator_script(valdtr), arg->source_line);
+ nenv.event = event_create(nenv.svinst->event);
+ event_set_append_log_prefix(nenv.event, "notify command: ");
+
+ /* Parse option */
+ if (!sieve_argument_is_string_literal(arg)) {
+ /* Variable string: partial option parse
+
+ If the string item is not a string literal, it cannot be
+ validated fully at compile time. We can however check whether
+ the '=' is in the string specification and whether the part
+ before the '=' is a valid option name. In that case, the
+ method option check function is called with the value
+ parameter equal to NULL, meaning that it should only check
+ the validity of the option itself and not the assigned value.
+ */
+ if (!ext_enotify_option_parse(NULL, option, TRUE,
+ &opt_name, &opt_value))
+ check = FALSE;
+ } else {
+ /* Literal string: full option parse */
+ if (!ext_enotify_option_parse(&nenv, option, FALSE,
+ &opt_name, &opt_value))
+ result = -1;
+ }
+
+ /* Call method's option check function */
+ if (result > 0 && check && method->def != NULL &&
+ method->def->compile_check_option != NULL) {
+ result = (method->def->compile_check_option(&nenv, opt_name,
+ opt_value) ?
+ 1 : -1 );
+ }
+
+ event_unref(&nenv.event);
+ return result;
+}
+
+bool ext_enotify_compile_check_arguments(struct sieve_validator *valdtr,
+ struct sieve_command *cmd,
+ struct sieve_ast_argument *uri_arg,
+ struct sieve_ast_argument *msg_arg,
+ struct sieve_ast_argument *from_arg,
+ struct sieve_ast_argument *options_arg)
+{
+ const struct sieve_extension *this_ext = cmd->ext;
+ struct sieve_instance *svinst = this_ext->svinst;
+ const char *uri = sieve_ast_argument_strc(uri_arg);
+ const char *scheme;
+ const struct sieve_enotify_method *method;
+ struct sieve_enotify_env nenv;
+ bool result = TRUE;
+
+ /* If the uri string is not a constant literal, we cannot determine
+ which method is used, so we bail out successfully and defer checking
+ to runtime.
+ */
+ if (!sieve_argument_is_string_literal(uri_arg))
+ return TRUE;
+
+ /* Parse scheme part of URI */
+ if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) {
+ sieve_argument_validate_error(
+ valdtr, uri_arg, "notify command: "
+ "invalid scheme part for method URI '%s'",
+ str_sanitize(sieve_ast_argument_strc(uri_arg), 80));
+ return FALSE;
+ }
+
+ /* Find used method with the parsed scheme identifier */
+ if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) {
+ sieve_argument_validate_error(
+ valdtr, uri_arg, "notify command: "
+ "invalid method '%s'", scheme);
+ return FALSE;
+ }
+
+ if (method->def == NULL)
+ return TRUE;
+
+ /* Compose log structure */
+ i_zero(&nenv);
+ nenv.svinst = svinst;
+ nenv.method = method;
+
+ /* Check URI itself */
+ if (result && method->def->compile_check_uri != NULL) {
+ /* Set log location to location of URI argument */
+ nenv.ehandler = sieve_validator_error_handler(valdtr);
+ nenv.location = sieve_error_script_location(
+ sieve_validator_script(valdtr), uri_arg->source_line);
+ nenv.event = event_create(nenv.svinst->event);
+ event_set_append_log_prefix(nenv.event, "notify command: ");
+
+ /* Execute method check function */
+ result = method->def->compile_check_uri(
+ &nenv, sieve_ast_argument_strc(uri_arg), uri);
+ }
+
+ /* Check :message argument */
+ if (result && msg_arg != NULL &&
+ sieve_argument_is_string_literal(msg_arg) &&
+ method->def->compile_check_message != NULL ) {
+ /* Set log location to location of :message argument */
+ event_unref(&nenv.event);
+ nenv.ehandler = sieve_validator_error_handler(valdtr);
+ nenv.location = sieve_error_script_location(
+ sieve_validator_script(valdtr), msg_arg->source_line);
+ nenv.event = event_create(nenv.svinst->event);
+ event_set_append_log_prefix(nenv.event, "notify command: ");
+
+ /* Execute method check function */
+ result = method->def->compile_check_message(
+ &nenv, sieve_ast_argument_str(msg_arg));
+ }
+
+ /* Check :from argument */
+ if (result && from_arg != NULL &&
+ sieve_argument_is_string_literal(from_arg) &&
+ method->def->compile_check_from != NULL ) {
+ /* Set log location to location of :from argument */
+ event_unref(&nenv.event);
+ nenv.ehandler = sieve_validator_error_handler(valdtr);
+ nenv.location = sieve_error_script_location(
+ sieve_validator_script(valdtr), from_arg->source_line);
+ nenv.event = event_create(nenv.svinst->event);
+ event_set_append_log_prefix(nenv.event, "notify command: ");
+
+ /* Execute method check function */
+ result = method->def->compile_check_from(
+ &nenv, sieve_ast_argument_str(from_arg));
+ }
+
+ event_unref(&nenv.event);
+
+ /* Check :options argument */
+ if (result && options_arg != NULL) {
+ struct sieve_ast_argument *option = options_arg;
+ struct _ext_enotify_option_check_context optn_context = {
+ svinst, valdtr, method };
+
+ /* Parse and check options */
+ result = (sieve_ast_stringlist_map(
+ &option, (void *) &optn_context,
+ _ext_enotify_option_check) > 0);
+
+ /* Discard argument if options are not accepted by method */
+ if (result && method->def->compile_check_option == NULL) {
+ sieve_argument_validate_warning(
+ valdtr, options_arg, "notify command: "
+ "method '%s' accepts no options", scheme);
+ (void)sieve_ast_arguments_detach(options_arg, 1);
+ }
+ }
+ return result;
+}
+
+/*
+ * Runtime operand checking
+ */
+
+bool ext_enotify_runtime_method_validate(const struct sieve_runtime_env *renv,
+ string_t *method_uri)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ const struct sieve_enotify_method *method;
+ const char *uri = str_c(method_uri);
+ const char *scheme;
+ bool result = TRUE;
+
+ /* Get the method */
+
+ if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL)
+ return FALSE;
+ if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL)
+ return FALSE;
+
+ /* Validate the provided URI */
+
+ if (method->def != NULL && method->def->runtime_check_uri != NULL) {
+ struct sieve_enotify_env nenv;
+
+ i_zero(&nenv);
+ nenv.svinst = eenv->svinst;
+ nenv.method = method;
+ nenv.ehandler = renv->ehandler;
+ nenv.location = sieve_runtime_get_full_command_location(renv),
+ nenv.event = event_create(nenv.svinst->event);
+ event_set_append_log_prefix(nenv.event,
+ "valid_notify_method test: ");
+
+ /* Use the method check function to validate the URI */
+ result = method->def->runtime_check_uri(
+ &nenv, str_c(method_uri), uri);
+
+ event_unref(&nenv.event);
+ }
+
+ return result;
+}
+
+static const struct
+sieve_enotify_method *ext_enotify_get_method(
+ const struct sieve_runtime_env *renv, string_t *method_uri,
+ const char **uri_body_r)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ const struct sieve_enotify_method *method;
+ const char *uri = str_c(method_uri);
+ const char *scheme;
+
+ /* Parse part before ':' of the uri (the scheme) and use it to identify
+ notify method.
+ */
+ if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) {
+ sieve_runtime_error(
+ renv, NULL, "invalid scheme part for method URI '%s'",
+ str_sanitize(str_c(method_uri), 80));
+ return NULL;
+ }
+
+ /* Find the notify method */
+ if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) {
+ sieve_runtime_error(renv, NULL, "invalid notify method '%s'",
+ scheme);
+ return NULL;
+ }
+
+ /* Return the parse pointer and the found method */
+ *uri_body_r = uri;
+ return method;
+}
+
+const char *
+ext_enotify_runtime_get_method_capability(const struct sieve_runtime_env *renv,
+ string_t *method_uri,
+ const char *capability)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ const struct sieve_enotify_method *method;
+ const char *uri_body;
+ const char *result = NULL;
+
+ /* Get method */
+ method = ext_enotify_get_method(renv, method_uri, &uri_body);
+ if ( method == NULL ) return NULL;
+
+ /* Get requested capability */
+ if (method->def != NULL &&
+ method->def->runtime_get_method_capability != NULL) {
+ struct sieve_enotify_env nenv;
+
+ i_zero(&nenv);
+ nenv.svinst = eenv->svinst;
+ nenv.method = method;
+ nenv.ehandler = renv->ehandler;
+ nenv.location = sieve_runtime_get_full_command_location(renv),
+ nenv.event = event_create(nenv.svinst->event);
+ event_set_append_log_prefix(nenv.event,
+ "notify_method_capability test: ");
+
+ /* Execute method function to acquire capability value */
+ result = method->def->runtime_get_method_capability(
+ &nenv, str_c(method_uri), uri_body, capability);
+
+ event_unref(&nenv.event);
+ }
+
+ return result;
+}
+
+int ext_enotify_runtime_check_operands(
+ const struct sieve_runtime_env *renv, string_t *method_uri,
+ string_t *message, string_t *from, struct sieve_stringlist *options,
+ const struct sieve_enotify_method **method_r, void **method_context)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ const struct sieve_enotify_method *method;
+ const char *uri_body;
+
+ /* Get method */
+ method = ext_enotify_get_method(renv, method_uri, &uri_body);
+ if (method == NULL)
+ return SIEVE_EXEC_FAILURE;
+
+ /* Check provided operands */
+ if (method->def != NULL &&
+ method->def->runtime_check_operands != NULL) {
+ struct sieve_enotify_env nenv;
+ int result = SIEVE_EXEC_OK;
+
+ i_zero(&nenv);
+ nenv.svinst = eenv->svinst;
+ nenv.method = method;
+ nenv.ehandler = renv->ehandler;
+ nenv.location = sieve_runtime_get_full_command_location(renv),
+ nenv.event = event_create(nenv.svinst->event);
+ event_set_append_log_prefix(nenv.event, "notify_action: ");
+
+ /* Execute check function */
+ if (method->def->runtime_check_operands(
+ &nenv, str_c(method_uri), uri_body, message, from,
+ sieve_result_pool(renv->result), method_context)) {
+
+ /* Check any provided options */
+ if (options != NULL) {
+ string_t *option = NULL;
+ int ret;
+
+ /* Iterate through all provided options */
+ while ((ret = sieve_stringlist_next_item(
+ options, &option)) > 0) {
+ const char *opt_name = NULL;
+ const char *opt_value = NULL;
+
+ /* Parse option into <optionname> and
+ <value> */
+ if (ext_enotify_option_parse(
+ &nenv, str_c(option), FALSE,
+ &opt_name, &opt_value)) {
+
+ /* Set option */
+ if (method->def->runtime_set_option != NULL) {
+ (void)method->def->runtime_set_option(
+ &nenv, *method_context,
+ opt_name, opt_value);
+ }
+ }
+ }
+
+ /* Check for binary corruptions encountered
+ during string list iteration */
+ if (ret >= 0) {
+ *method_r = method;
+ } else {
+ /* Binary corrupt */
+ sieve_runtime_trace_error(
+ renv, "invalid item in options string list");
+ result = SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ } else {
+ /* No options */
+ *method_r = method;
+ }
+
+ } else {
+ /* Operand check failed */
+ result = SIEVE_EXEC_FAILURE;
+ }
+
+ event_unref(&nenv.event);
+ return result;
+ }
+
+ /* No check function defined: a most unlikely situation */
+ *method_context = NULL;
+ *method_r = method;
+ return SIEVE_EXEC_OK;
+}
+
+/*
+ * Notify method printing
+ */
+
+void sieve_enotify_method_printf(const struct sieve_enotify_print_env *penv,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_result_vprintf(penv->result_penv, fmt, args);
+ va_end(args);
+}
+
+/*
+ * Action execution
+ */
+
+struct event_passthrough *
+sieve_enotify_create_finish_event(const struct sieve_enotify_exec_env *nenv)
+{
+ struct event_passthrough *e =
+ event_create_passthrough(nenv->event)->
+ set_name("sieve_action_finished");
+
+ return e;
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.h b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.h
new file mode 100644
index 0000000..ec43202
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.h
@@ -0,0 +1,122 @@
+#ifndef EXT_ENOTIFY_COMMON_H
+#define EXT_ENOTIFY_COMMON_H
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-common.h"
+
+#include "sieve-ext-variables.h"
+
+#include "sieve-ext-enotify.h"
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def enotify_extension;
+extern const struct sieve_extension_capabilities notify_capabilities;
+
+struct ext_enotify_context {
+ const struct sieve_extension *var_ext;
+ ARRAY(struct sieve_enotify_method) notify_methods;
+};
+
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def notify_command;
+
+/* Codes for optional arguments */
+
+enum cmd_notify_optional {
+ CMD_NOTIFY_OPT_END,
+ CMD_NOTIFY_OPT_FROM,
+ CMD_NOTIFY_OPT_OPTIONS,
+ CMD_NOTIFY_OPT_MESSAGE,
+ CMD_NOTIFY_OPT_IMPORTANCE
+};
+
+/*
+ * Tests
+ */
+
+extern const struct sieve_command_def valid_notify_method_test;
+extern const struct sieve_command_def notify_method_capability_test;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def notify_operation;
+extern const struct sieve_operation_def valid_notify_method_operation;
+extern const struct sieve_operation_def notify_method_capability_operation;
+
+enum ext_variables_opcode {
+ EXT_ENOTIFY_OPERATION_NOTIFY,
+ EXT_ENOTIFY_OPERATION_VALID_NOTIFY_METHOD,
+ EXT_ENOTIFY_OPERATION_NOTIFY_METHOD_CAPABILITY
+};
+
+/*
+ * Operands
+ */
+
+extern const struct sieve_operand_def encodeurl_operand;
+
+/*
+ * Modifiers
+ */
+
+extern const struct sieve_variables_modifier_def encodeurl_modifier;
+
+/*
+ * Notify methods
+ */
+
+void ext_enotify_methods_init(struct sieve_instance *svinst,
+ struct ext_enotify_context *ectx);
+void ext_enotify_methods_deinit(struct ext_enotify_context *ectx);
+
+const struct sieve_enotify_method *
+ext_enotify_method_find(const struct sieve_extension *ntfy_ext,
+ const char *identifier);
+
+/*
+ * Validation
+ */
+
+bool ext_enotify_compile_check_arguments(
+ struct sieve_validator *valdtr, struct sieve_command *cmd,
+ struct sieve_ast_argument *uri_arg, struct sieve_ast_argument *msg_arg,
+ struct sieve_ast_argument *from_arg,
+ struct sieve_ast_argument *options_arg);
+
+/*
+ * Runtime
+ */
+
+bool ext_enotify_runtime_method_validate(const struct sieve_runtime_env *renv,
+ string_t *method_uri);
+
+const char *
+ext_enotify_runtime_get_method_capability(const struct sieve_runtime_env *renv,
+ string_t *method_uri,
+ const char *capability);
+
+int ext_enotify_runtime_check_operands(
+ const struct sieve_runtime_env *renv, string_t *method_uri,
+ string_t *message, string_t *from, struct sieve_stringlist *options,
+ const struct sieve_enotify_method **method_r, void **method_context);
+
+/*
+ * Method printing
+ */
+
+struct sieve_enotify_print_env {
+ const struct sieve_result_print_env *result_penv;
+};
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-limits.h b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-limits.h
new file mode 100644
index 0000000..aac48df
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-limits.h
@@ -0,0 +1,6 @@
+#ifndef EXT_ENOTIFY_LIMITS_H
+#define EXT_ENOTIFY_LIMITS_H
+
+#define EXT_ENOTIFY_MAX_SCHEME_LEN 32
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify.c b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify.c
new file mode 100644
index 0000000..df479b3
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify.c
@@ -0,0 +1,103 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension enotify
+ * ------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5435
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-enotify-common.h"
+
+/*
+ * Operations
+ */
+
+const struct sieve_operation_def *ext_enotify_operations[] = {
+ &notify_operation,
+ &valid_notify_method_operation,
+ &notify_method_capability_operation
+};
+
+/*
+ * Extension
+ */
+
+static bool ext_enotify_load(const struct sieve_extension *ext, void **context);
+static void ext_enotify_unload(const struct sieve_extension *ext);
+static bool ext_enotify_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def enotify_extension = {
+ .name = "enotify",
+ .load = ext_enotify_load,
+ .unload = ext_enotify_unload,
+ .validator_load = ext_enotify_validator_load,
+ SIEVE_EXT_DEFINE_OPERATIONS(ext_enotify_operations),
+ SIEVE_EXT_DEFINE_OPERAND(encodeurl_operand)
+};
+
+static bool ext_enotify_load(const struct sieve_extension *ext, void **context)
+{
+ struct ext_enotify_context *ectx;
+
+ if ( *context != NULL ) {
+ ext_enotify_unload(ext);
+ }
+
+ ectx = i_new(struct ext_enotify_context, 1);
+ ectx->var_ext = sieve_ext_variables_get_extension(ext->svinst);
+ *context = (void *) ectx;
+
+ ext_enotify_methods_init(ext->svinst, ectx);
+
+ sieve_extension_capabilities_register(ext, &notify_capabilities);
+
+ return TRUE;
+}
+
+static void ext_enotify_unload(const struct sieve_extension *ext)
+{
+ struct ext_enotify_context *ectx =
+ (struct ext_enotify_context *) ext->context;
+
+ ext_enotify_methods_deinit(ectx);
+
+ i_free(ectx);
+}
+
+static bool ext_enotify_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ struct ext_enotify_context *ectx =
+ (struct ext_enotify_context *) ext->context;
+
+ /* Register new commands */
+ sieve_validator_register_command(valdtr, ext, &notify_command);
+ sieve_validator_register_command(valdtr, ext, &valid_notify_method_test);
+ sieve_validator_register_command(valdtr, ext, &notify_method_capability_test);
+
+ /* Register new set modifier for variables extension */
+ sieve_variables_modifier_register
+ (ectx->var_ext, valdtr, ext, &encodeurl_modifier);
+
+ return TRUE;
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.am b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.am
new file mode 100644
index 0000000..83f129c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.am
@@ -0,0 +1,16 @@
+noinst_LTLIBRARIES = libsieve_ext_enotify_mailto.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/.. \
+ -I$(srcdir)/../../.. \
+ -I$(srcdir)/../../../util \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_enotify_mailto_la_SOURCES = \
+ uri-mailto.c \
+ ntfy-mailto.c
+
+noinst_HEADERS = \
+ uri-mailto.h
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.in b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.in
new file mode 100644
index 0000000..49aa41a
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/Makefile.in
@@ -0,0 +1,687 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/enotify/mailto
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_enotify_mailto_la_LIBADD =
+am_libsieve_ext_enotify_mailto_la_OBJECTS = uri-mailto.lo \
+ ntfy-mailto.lo
+libsieve_ext_enotify_mailto_la_OBJECTS = \
+ $(am_libsieve_ext_enotify_mailto_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ntfy-mailto.Plo \
+ ./$(DEPDIR)/uri-mailto.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_enotify_mailto_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_enotify_mailto_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_enotify_mailto.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/.. \
+ -I$(srcdir)/../../.. \
+ -I$(srcdir)/../../../util \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_enotify_mailto_la_SOURCES = \
+ uri-mailto.c \
+ ntfy-mailto.c
+
+noinst_HEADERS = \
+ uri-mailto.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/enotify/mailto/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/enotify/mailto/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_enotify_mailto.la: $(libsieve_ext_enotify_mailto_la_OBJECTS) $(libsieve_ext_enotify_mailto_la_DEPENDENCIES) $(EXTRA_libsieve_ext_enotify_mailto_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_enotify_mailto_la_OBJECTS) $(libsieve_ext_enotify_mailto_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfy-mailto.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uri-mailto.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ntfy-mailto.Plo
+ -rm -f ./$(DEPDIR)/uri-mailto.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ntfy-mailto.Plo
+ -rm -f ./$(DEPDIR)/uri-mailto.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c
new file mode 100644
index 0000000..4e104d1
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c
@@ -0,0 +1,794 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Notify method mailto
+ * --------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5436
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and
+ * draft-duerst-mailto-bis-05.txt. Should fully migrate to new specification
+ * when it matures. This requires modifications to the address parser (no
+ * whitespace allowed within the address itself) and UTF-8 support will be
+ * required in the URL.
+ */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "ioloop.h"
+#include "str-sanitize.h"
+#include "ostream.h"
+#include "message-date.h"
+#include "mail-storage.h"
+
+#include "sieve-common.h"
+#include "sieve-address.h"
+#include "sieve-address-source.h"
+#include "sieve-message.h"
+#include "sieve-smtp.h"
+#include "sieve-settings.h"
+
+#include "sieve-ext-enotify.h"
+
+#include "rfc2822.h"
+
+#include "uri-mailto.h"
+
+/*
+ * Configuration
+ */
+
+#define NTFY_MAILTO_MAX_RECIPIENTS 8
+#define NTFY_MAILTO_MAX_HEADERS 16
+#define NTFY_MAILTO_MAX_SUBJECT 256
+
+/*
+ * Mailto notification configuration
+ */
+
+struct ntfy_mailto_config {
+ pool_t pool;
+ struct sieve_address_source envelope_from;
+};
+
+/*
+ * Mailto notification method
+ */
+
+static bool ntfy_mailto_load
+ (const struct sieve_enotify_method *nmth, void **context);
+static void ntfy_mailto_unload
+ (const struct sieve_enotify_method *nmth);
+
+static bool ntfy_mailto_compile_check_uri
+ (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body);
+static bool ntfy_mailto_compile_check_from
+ (const struct sieve_enotify_env *nenv, string_t *from);
+
+static const char *ntfy_mailto_runtime_get_notify_capability
+ (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body,
+ const char *capability);
+static bool ntfy_mailto_runtime_check_uri
+ (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body);
+static bool ntfy_mailto_runtime_check_operands
+ (const struct sieve_enotify_env *nenv, const char *uri,const char *uri_body,
+ string_t *message, string_t *from, pool_t context_pool,
+ void **method_context);
+
+static int ntfy_mailto_action_check_duplicates
+ (const struct sieve_enotify_env *nenv,
+ const struct sieve_enotify_action *nact,
+ const struct sieve_enotify_action *nact_other);
+
+static void ntfy_mailto_action_print
+ (const struct sieve_enotify_print_env *penv,
+ const struct sieve_enotify_action *nact);
+
+static int ntfy_mailto_action_execute
+ (const struct sieve_enotify_exec_env *nenv,
+ const struct sieve_enotify_action *nact);
+
+const struct sieve_enotify_method_def mailto_notify = {
+ "mailto",
+ ntfy_mailto_load,
+ ntfy_mailto_unload,
+ ntfy_mailto_compile_check_uri,
+ NULL,
+ ntfy_mailto_compile_check_from,
+ NULL,
+ ntfy_mailto_runtime_check_uri,
+ ntfy_mailto_runtime_get_notify_capability,
+ ntfy_mailto_runtime_check_operands,
+ NULL,
+ ntfy_mailto_action_check_duplicates,
+ ntfy_mailto_action_print,
+ ntfy_mailto_action_execute
+};
+
+/*
+ * Reserved and unique headers
+ */
+
+static const char *_reserved_headers[] = {
+ "auto-submitted",
+ "received",
+ "message-id",
+ "data",
+ "bcc",
+ "in-reply-to",
+ "references",
+ "resent-date",
+ "resent-from",
+ "resent-sender",
+ "resent-to",
+ "resent-cc",
+ "resent-bcc",
+ "resent-msg-id",
+ "from",
+ "sender",
+ NULL
+};
+
+static const char *_unique_headers[] = {
+ "reply-to",
+ NULL
+};
+
+/*
+ * Method context data
+ */
+
+struct ntfy_mailto_context {
+ struct uri_mailto *uri;
+ const struct smtp_address *from_address;
+};
+
+/*
+ * Method registration
+ */
+
+static bool ntfy_mailto_load
+(const struct sieve_enotify_method *nmth, void **context)
+{
+ struct sieve_instance *svinst = nmth->svinst;
+ struct ntfy_mailto_config *config;
+ pool_t pool;
+
+ if ( *context != NULL ) {
+ ntfy_mailto_unload(nmth);
+ }
+
+ pool = pool_alloconly_create("ntfy_mailto_config", 256);
+ config = p_new(pool, struct ntfy_mailto_config, 1);
+ config->pool = pool;
+
+ (void)sieve_address_source_parse_from_setting (svinst,
+ config->pool, "sieve_notify_mailto_envelope_from",
+ &config->envelope_from);
+
+ *context = (void *) config;
+
+ return TRUE;
+}
+
+static void ntfy_mailto_unload
+(const struct sieve_enotify_method *nmth)
+{
+ struct ntfy_mailto_config *config =
+ (struct ntfy_mailto_config *) nmth->context;
+
+ pool_unref(&config->pool);
+}
+
+/*
+ * URI parsing
+ */
+
+struct ntfy_mailto_uri_env {
+ const struct sieve_enotify_env *nenv;
+
+ struct event *event;
+
+ struct uri_mailto_log uri_log;
+};
+
+static void ATTR_FORMAT(5, 0)
+ntfy_mailto_uri_logv(void *context, enum log_type log_type,
+ const char *csrc_filename, unsigned int csrc_linenum,
+ const char *fmt, va_list args)
+{
+ struct ntfy_mailto_uri_env *nmuenv = context;
+ const struct sieve_enotify_env *nenv = nmuenv->nenv;
+
+ sieve_event_logv(nenv->svinst, nenv->ehandler, nmuenv->event,
+ log_type, csrc_filename, csrc_linenum,
+ nenv->location, 0, fmt, args);
+}
+
+static void
+ntfy_mailto_uri_env_init(struct ntfy_mailto_uri_env *nmuenv,
+ const struct sieve_enotify_env *nenv)
+{
+ i_zero(nmuenv);
+ nmuenv->nenv = nenv;
+ nmuenv->event = event_create(nenv->event);
+ event_set_append_log_prefix(nmuenv->event, "mailto URI: ");
+
+ nmuenv->uri_log.context = nmuenv;
+ nmuenv->uri_log.logv = ntfy_mailto_uri_logv;
+}
+
+static void
+ntfy_mailto_uri_env_deinit(struct ntfy_mailto_uri_env *nmuenv)
+{
+ event_unref(&nmuenv->event);
+}
+
+/*
+ * Validation
+ */
+
+
+static bool ntfy_mailto_compile_check_uri
+(const struct sieve_enotify_env *nenv, const char *uri ATTR_UNUSED,
+ const char *uri_body)
+{
+ struct ntfy_mailto_uri_env nmuenv;
+ bool result;
+
+ ntfy_mailto_uri_env_init(&nmuenv, nenv);
+ result = uri_mailto_validate(
+ uri_body, _reserved_headers, _unique_headers,
+ NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS,
+ &nmuenv.uri_log);
+ ntfy_mailto_uri_env_deinit(&nmuenv);
+
+ return result;
+}
+
+static bool ntfy_mailto_compile_check_from
+(const struct sieve_enotify_env *nenv, string_t *from)
+{
+ const char *error;
+ bool result = FALSE;
+
+ T_BEGIN {
+ result = sieve_address_validate_str(from, &error);
+ if ( !result ) {
+ sieve_enotify_error(nenv,
+ "specified :from address '%s' is invalid for "
+ "the mailto method: %s",
+ str_sanitize(str_c(from), 128), error);
+ }
+ } T_END;
+
+ return result;
+}
+
+/*
+ * Runtime
+ */
+
+struct ntfy_mailto_runtime_env {
+ const struct sieve_enotify_env *nenv;
+
+ struct event *event;
+};
+
+static const char *ntfy_mailto_runtime_get_notify_capability
+(const struct sieve_enotify_env *nenv ATTR_UNUSED, const char *uri ATTR_UNUSED,
+ const char *uri_body, const char *capability)
+{
+ if ( !uri_mailto_validate(uri_body, _reserved_headers, _unique_headers,
+ NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, NULL) ) {
+ return NULL;
+ }
+
+ if ( strcasecmp(capability, "online") == 0 )
+ return "maybe";
+
+ return NULL;
+}
+
+static bool ntfy_mailto_runtime_check_uri
+(const struct sieve_enotify_env *nenv ATTR_UNUSED, const char *uri ATTR_UNUSED,
+ const char *uri_body)
+{
+ return uri_mailto_validate
+ (uri_body, _reserved_headers, _unique_headers,
+ NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, NULL);
+}
+
+static bool ntfy_mailto_runtime_check_operands
+(const struct sieve_enotify_env *nenv, const char *uri ATTR_UNUSED,
+ const char *uri_body, string_t *message ATTR_UNUSED, string_t *from,
+ pool_t context_pool, void **method_context)
+{
+ struct ntfy_mailto_context *mtctx;
+ struct uri_mailto *parsed_uri;
+ const struct smtp_address *address;
+ struct ntfy_mailto_uri_env nmuenv;
+ const char *error;
+
+ /* Need to create context before validation to have arrays present */
+ mtctx = p_new(context_pool, struct ntfy_mailto_context, 1);
+
+ /* Validate :from */
+ if ( from != NULL ) {
+ T_BEGIN {
+ address = sieve_address_parse_str(from, &error);
+ if ( address == NULL ) {
+ sieve_enotify_error(nenv,
+ "specified :from address '%s' is invalid for "
+ "the mailto method: %s",
+ str_sanitize(str_c(from), 128), error);
+ } else
+ mtctx->from_address =
+ smtp_address_clone(context_pool, address);
+ } T_END;
+
+ if ( address == NULL ) return FALSE;
+ }
+
+ ntfy_mailto_uri_env_init(&nmuenv, nenv);
+ parsed_uri = uri_mailto_parse(uri_body, context_pool,
+ _reserved_headers, _unique_headers,
+ NTFY_MAILTO_MAX_RECIPIENTS,
+ NTFY_MAILTO_MAX_HEADERS,
+ &nmuenv.uri_log);
+ ntfy_mailto_uri_env_deinit(&nmuenv);
+
+ if (parsed_uri == NULL)
+ return FALSE;
+
+ mtctx->uri = parsed_uri;
+ *method_context = (void *) mtctx;
+ return TRUE;
+}
+
+/*
+ * Action duplicates
+ */
+
+static int ntfy_mailto_action_check_duplicates
+(const struct sieve_enotify_env *nenv ATTR_UNUSED,
+ const struct sieve_enotify_action *nact,
+ const struct sieve_enotify_action *nact_other)
+{
+ struct ntfy_mailto_context *mtctx =
+ (struct ntfy_mailto_context *) nact->method_context;
+ struct ntfy_mailto_context *mtctx_other =
+ (struct ntfy_mailto_context *) nact_other->method_context;
+ const struct uri_mailto_recipient *new_rcpts, *old_rcpts;
+ unsigned int new_count, old_count, i, j;
+ unsigned int del_start = 0, del_len = 0;
+
+ new_rcpts = array_get(&mtctx->uri->recipients, &new_count);
+ old_rcpts = array_get(&mtctx_other->uri->recipients, &old_count);
+
+ for ( i = 0; i < new_count; i++ ) {
+ for ( j = 0; j < old_count; j++ ) {
+ if ( smtp_address_equals
+ (new_rcpts[i].address, old_rcpts[j].address) )
+ break;
+ }
+
+ if ( j == old_count ) {
+ /* Not duplicate */
+ if ( del_len > 0 ) {
+ /* Perform pending deletion */
+ array_delete(&mtctx->uri->recipients, del_start, del_len);
+
+ /* Make sure the loop integrity is maintained */
+ i -= del_len;
+ new_rcpts = array_get(&mtctx->uri->recipients, &new_count);
+ }
+ del_len = 0;
+ } else {
+ /* Mark deletion */
+ if ( del_len == 0 )
+ del_start = i;
+ del_len++;
+ }
+ }
+
+ /* Perform pending deletion */
+ if ( del_len > 0 ) {
+ array_delete(&mtctx->uri->recipients, del_start, del_len);
+ }
+
+ return ( array_count(&mtctx->uri->recipients) > 0 ? 0 : 1 );
+}
+
+/*
+ * Action printing
+ */
+
+static void ntfy_mailto_action_print
+(const struct sieve_enotify_print_env *penv,
+ const struct sieve_enotify_action *nact)
+{
+ unsigned int count, i;
+ const struct uri_mailto_recipient *recipients;
+ const struct uri_mailto_header_field *headers;
+ struct ntfy_mailto_context *mtctx =
+ (struct ntfy_mailto_context *) nact->method_context;
+
+ /* Print main method parameters */
+
+ sieve_enotify_method_printf
+ (penv, " => importance : %llu\n",
+ (unsigned long long)nact->importance);
+
+ if ( nact->message != NULL )
+ sieve_enotify_method_printf
+ (penv, " => subject : %s\n", nact->message);
+ else if ( mtctx->uri->subject != NULL )
+ sieve_enotify_method_printf
+ (penv, " => subject : %s\n", mtctx->uri->subject);
+
+ if ( nact->from != NULL )
+ sieve_enotify_method_printf
+ (penv, " => from : %s\n", nact->from);
+
+ /* Print mailto: recipients */
+
+ sieve_enotify_method_printf(penv, " => recipients :\n" );
+
+ recipients = array_get(&mtctx->uri->recipients, &count);
+ if ( count == 0 ) {
+ sieve_enotify_method_printf(penv, " NONE, action has no effect\n");
+ } else {
+ for ( i = 0; i < count; i++ ) {
+ if ( recipients[i].carbon_copy )
+ sieve_enotify_method_printf
+ (penv, " + Cc: %s\n", recipients[i].full);
+ else
+ sieve_enotify_method_printf
+ (penv, " + To: %s\n", recipients[i].full);
+ }
+ }
+
+ /* Print accepted headers for notification message */
+
+ headers = array_get(&mtctx->uri->headers, &count);
+ if ( count > 0 ) {
+ sieve_enotify_method_printf(penv, " => headers :\n" );
+ for ( i = 0; i < count; i++ ) {
+ sieve_enotify_method_printf(penv, " + %s: %s\n",
+ headers[i].name, headers[i].body);
+ }
+ }
+
+ /* Print body for notification message */
+
+ if ( mtctx->uri->body != NULL )
+ sieve_enotify_method_printf
+ (penv, " => body : \n--\n%s\n--\n", mtctx->uri->body);
+
+ /* Finish output with an empty line */
+
+ sieve_enotify_method_printf(penv, "\n");
+}
+
+/*
+ * Action execution
+ */
+
+static bool _contains_8bit(const char *msg)
+{
+ const unsigned char *s = (const unsigned char *)msg;
+
+ for (; *s != '\0'; s++) {
+ if ((*s & 0x80) != 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int ntfy_mailto_send
+(const struct sieve_enotify_exec_env *nenv,
+ const struct sieve_enotify_action *nact,
+ const struct smtp_address *owner_email)
+{
+ struct sieve_instance *svinst = nenv->svinst;
+ const struct sieve_message_data *msgdata = nenv->msgdata;
+ const struct sieve_script_env *senv = nenv->scriptenv;
+ struct ntfy_mailto_context *mtctx =
+ (struct ntfy_mailto_context *) nact->method_context;
+ struct ntfy_mailto_config *mth_config =
+ (struct ntfy_mailto_config *)nenv->method->context;
+ struct sieve_address_source env_from =
+ mth_config->envelope_from;
+ const char *from = NULL;
+ const struct smtp_address *from_smtp = NULL;
+ const char *subject = mtctx->uri->subject;
+ const char *body = mtctx->uri->body;
+ string_t *to, *cc, *all;
+ const struct uri_mailto_recipient *recipients;
+ const struct uri_mailto_header_field *headers;
+ struct sieve_smtp_context *sctx;
+ struct ostream *output;
+ string_t *msg;
+ unsigned int count, i, hcount, h;
+ const char *outmsgid, *error;
+ int ret;
+
+ /* Get recipients */
+ recipients = array_get(&mtctx->uri->recipients, &count);
+ if ( count == 0 ) {
+ sieve_enotify_warning(nenv,
+ "notify mailto uri specifies no recipients; action has no effect");
+ return 0;
+ }
+
+ /* Just to be sure */
+ if ( !sieve_smtp_available(senv) ) {
+ sieve_enotify_global_warning(nenv,
+ "notify mailto method has no means to send mail");
+ return 0;
+ }
+
+ /* Determine which sender to use
+
+ From RFC 5436, Section 2.3:
+
+ The ":from" tag overrides the default sender of the notification
+ message. "Sender", here, refers to the value used in the [RFC5322]
+ "From" header. Implementations MAY also use this value in the
+ [RFC5321] "MAIL FROM" command (the "envelope sender"), or they may
+ prefer to establish a mailbox that receives bounces from notification
+ messages.
+ */
+ if ( (nenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 ) {
+ from_smtp = sieve_message_get_sender(nenv->msgctx);
+ if ( from_smtp == NULL ) {
+ /* "<>" */
+ i_zero(&env_from);
+ env_from.type = SIEVE_ADDRESS_SOURCE_EXPLICIT;
+ }
+ }
+ from = nact->from;
+ if ( (ret=sieve_address_source_get_address(&env_from, svinst,
+ senv, nenv->msgctx, nenv->flags, &from_smtp)) < 0 ) {
+ from_smtp = NULL;
+ } else if ( ret == 0 ) {
+ if ( mtctx->from_address != NULL )
+ from_smtp = mtctx->from_address;
+ else if ( svinst->user_email != NULL )
+ from_smtp = svinst->user_email;
+ else {
+ from_smtp = sieve_get_postmaster_smtp(senv);
+ if (from == NULL)
+ from = sieve_get_postmaster_address(senv);
+ }
+ }
+
+ /* Determine message from address */
+ if ( from == NULL ) {
+ if ( from_smtp == NULL )
+ from = sieve_get_postmaster_address(senv);
+ else {
+ from = t_strdup_printf("<%s>",
+ smtp_address_encode(from_smtp));
+ }
+ }
+
+ /* Determine subject */
+ if ( nact->message != NULL ) {
+ /* FIXME: handle UTF-8 */
+ subject = str_sanitize(nact->message, NTFY_MAILTO_MAX_SUBJECT);
+ } else if ( subject == NULL ) {
+ const char *const *hsubject;
+
+ /* Fetch subject from original message */
+ if ( mail_get_headers_utf8
+ (msgdata->mail, "subject", &hsubject) > 0 )
+ subject = str_sanitize(t_strdup_printf("Notification: %s", hsubject[0]),
+ NTFY_MAILTO_MAX_SUBJECT);
+ else
+ subject = "Notification: (no subject)";
+ }
+
+ /* Compose To and Cc headers */
+ to = NULL;
+ cc = NULL;
+ all = t_str_new(256);
+ for ( i = 0; i < count; i++ ) {
+ if ( recipients[i].carbon_copy ) {
+ if ( cc == NULL ) {
+ cc = t_str_new(256);
+ str_append(cc, recipients[i].full);
+ } else {
+ str_append(cc, ", ");
+ str_append(cc, recipients[i].full);
+ }
+ } else {
+ if ( to == NULL ) {
+ to = t_str_new(256);
+ str_append(to, recipients[i].full);
+ } else {
+ str_append(to, ", ");
+ str_append(to, recipients[i].full);
+ }
+ }
+ if ( i < 3) {
+ if ( i > 0 )
+ str_append(all, ", ");
+ str_append(all,
+ smtp_address_encode_path(recipients[i].address));
+ } else if (i == 3) {
+ str_printfa(all, ", ... (%u total)", count);
+ }
+ }
+
+ msg = t_str_new(512);
+ outmsgid = sieve_message_get_new_id(svinst);
+
+ rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION);
+ rfc2822_header_write(msg, "Message-ID", outmsgid);
+ rfc2822_header_write(msg, "Date", message_date_create(ioloop_time));
+ rfc2822_header_utf8_printf(msg, "Subject", "%s", subject);
+
+ rfc2822_header_write_address(msg, "From", from);
+
+ if ( to != NULL )
+ rfc2822_header_write_address(msg, "To", str_c(to));
+ if ( cc != NULL )
+ rfc2822_header_write_address(msg, "Cc", str_c(cc));
+
+ rfc2822_header_printf(msg, "Auto-Submitted",
+ "auto-notified; owner-email=\"%s\"",
+ smtp_address_encode(owner_email));
+ rfc2822_header_write(msg, "Precedence", "bulk");
+
+ /* Set importance */
+ switch ( nact->importance ) {
+ case 1:
+ rfc2822_header_write(msg, "X-Priority", "1 (Highest)");
+ rfc2822_header_write(msg, "Importance", "High");
+ break;
+ case 3:
+ rfc2822_header_write(msg, "X-Priority", "5 (Lowest)");
+ rfc2822_header_write(msg, "Importance", "Low");
+ break;
+ case 2:
+ default:
+ rfc2822_header_write(msg, "X-Priority", "3 (Normal)");
+ rfc2822_header_write(msg, "Importance", "Normal");
+ break;
+ }
+
+ /* Add custom headers */
+
+ headers = array_get(&mtctx->uri->headers, &hcount);
+ for ( h = 0; h < hcount; h++ ) {
+ const char *name = rfc2822_header_field_name_sanitize(headers[h].name);
+
+ rfc2822_header_write(msg, name, headers[h].body);
+ }
+
+ /* Generate message body */
+
+ rfc2822_header_write(msg, "MIME-Version", "1.0");
+ if ( body != NULL ) {
+ if (_contains_8bit(body)) {
+ rfc2822_header_write
+ (msg, "Content-Type", "text/plain; charset=utf-8");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit");
+ } else {
+ rfc2822_header_write
+ (msg, "Content-Type", "text/plain; charset=us-ascii");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit");
+ }
+ str_printfa(msg, "\r\n%s\r\n", body);
+
+ } else {
+ rfc2822_header_write
+ (msg, "Content-Type", "text/plain; charset=US-ASCII");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit");
+
+ str_append(msg, "\r\nNotification of new message.\r\n");
+ }
+
+ sctx = sieve_smtp_start(senv, from_smtp);
+
+ /* Send message to all recipients */
+ for ( i = 0; i < count; i++ )
+ sieve_smtp_add_rcpt(sctx, recipients[i].address);
+
+ output = sieve_smtp_send(sctx);
+ o_stream_nsend(output, str_data(msg), str_len(msg));
+
+ if ( (ret=sieve_smtp_finish(sctx, &error)) <= 0 ) {
+ if (ret < 0) {
+ sieve_enotify_global_error(nenv,
+ "failed to send mail notification to %s: %s (temporary failure)",
+ str_c(all), str_sanitize(error, 512));
+ } else {
+ sieve_enotify_global_log_error(nenv,
+ "failed to send mail notification to %s: %s (permanent failure)",
+ str_c(all), str_sanitize(error, 512));
+ }
+ } else {
+ struct event_passthrough *e =
+ sieve_enotify_create_finish_event(nenv)->
+ add_str("notify_target", str_c(all));
+
+ sieve_enotify_event_log(nenv, e->event(),
+ "sent mail notification to %s",
+ str_c(all));
+ }
+
+ return 0;
+}
+
+static int ntfy_mailto_action_execute
+(const struct sieve_enotify_exec_env *nenv,
+ const struct sieve_enotify_action *nact)
+{
+ struct sieve_instance *svinst = nenv->svinst;
+ const struct sieve_script_env *senv = nenv->scriptenv;
+ struct mail *mail = nenv->msgdata->mail;
+ const struct smtp_address *owner_email;
+ const char *const *hdsp;
+ int ret;
+
+ owner_email = svinst->user_email;
+ if ( owner_email == NULL &&
+ (nenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 )
+ owner_email = sieve_message_get_final_recipient(nenv->msgctx);
+ if ( owner_email == NULL ) {
+ owner_email = sieve_get_postmaster_smtp(senv);
+ }
+ i_assert( owner_email != NULL );
+
+ /* Is the message an automatic reply ? */
+ if ( (ret=mail_get_headers(mail, "auto-submitted", &hdsp)) < 0 ) {
+ sieve_enotify_critical(nenv,
+ "mailto notification: "
+ "failed to read `auto-submitted' header field",
+ "mailto notification: "
+ "failed to read `auto-submitted' header field: %s",
+ mailbox_get_last_internal_error(mail->box, NULL));
+ return -1;
+ }
+
+ /* Theoretically multiple headers could exist, so lets make sure */
+ if ( ret > 0 ) {
+ while ( *hdsp != NULL ) {
+ if ( strcasecmp(*hdsp, "no") != 0 ) {
+ const struct smtp_address *sender = NULL;
+ const char *from;
+
+ if ( (nenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 )
+ sender = sieve_message_get_sender(nenv->msgctx);
+ from = (sender == NULL ? "" : t_strdup_printf
+ (" from <%s>", smtp_address_encode(sender)));
+
+ sieve_enotify_global_info(nenv,
+ "not sending notification "
+ "for auto-submitted message%s", from);
+ return 0;
+ }
+ hdsp++;
+ }
+ }
+
+ T_BEGIN {
+ ret = ntfy_mailto_send(nenv, nact, owner_email);
+ } T_END;
+
+ return ret;
+}
+
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c
new file mode 100644
index 0000000..c5a0953
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c
@@ -0,0 +1,683 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and
+ draft-duerst-mailto-bis-05.txt. Should fully migrate to new
+ specification when it matures. This requires modifications to the
+ address parser (no whitespace allowed within the address itself) and
+ UTF-8 support will be required in the URL.
+ */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "str-sanitize.h"
+
+#include "rfc2822.h"
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+#include "sieve-address.h"
+#include "sieve-message.h"
+
+#include "uri-mailto.h"
+
+/* Parser object */
+
+struct uri_mailto_parser {
+ pool_t pool;
+ const struct uri_mailto_log *log;
+
+ struct uri_mailto *uri;
+
+ const char **reserved_headers;
+ const char **unique_headers;
+
+ int max_recipients;
+ int max_headers;
+};
+
+/* Error handling */
+
+#define uri_mailto_error(PARSER, ...) \
+ uri_mailto_log((PARSER), LOG_TYPE_ERROR, \
+ __FILE__, __LINE__, __VA_ARGS__)
+#define uri_mailto_warning(PARSER, ...) \
+ uri_mailto_log((PARSER), LOG_TYPE_WARNING, \
+ __FILE__, __LINE__, __VA_ARGS__)
+
+static inline void ATTR_FORMAT(5, 6)
+uri_mailto_log(struct uri_mailto_parser *parser, enum log_type log_type,
+ const char *csrc_filename, unsigned int csrc_linenum,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ if (parser->log == NULL || parser->log->logv == NULL)
+ return;
+
+ va_start(args, fmt);
+ parser->log->logv(parser->log->context, log_type,
+ csrc_filename, csrc_linenum, fmt, args);
+ va_end(args);
+}
+
+
+/*
+ * Error handling
+ */
+
+/*
+ * Reserved and unique headers
+ */
+
+static inline bool
+uri_mailto_header_is_reserved(struct uri_mailto_parser *parser,
+ const char *field_name)
+{
+ const char **hdr = parser->reserved_headers;
+
+ if (hdr == NULL)
+ return FALSE;
+
+ /* Check whether it is reserved */
+ while (*hdr != NULL) {
+ if (strcasecmp(field_name, *hdr) == 0)
+ return TRUE;
+ hdr++;
+ }
+ return FALSE;
+}
+
+static inline bool
+uri_mailto_header_is_unique(struct uri_mailto_parser *parser,
+ const char *field_name)
+{
+ const char **hdr = parser->unique_headers;
+
+ if (hdr == NULL)
+ return FALSE;
+
+ /* Check whether it is supposed to be unique */
+ while (*hdr != NULL) {
+ if (strcasecmp(field_name, *hdr) == 0)
+ return TRUE;
+ hdr++;
+ }
+ return FALSE;
+}
+
+/*
+ * Low-level URI parsing.
+ *
+ * FIXME: much of this implementation will be common to other URI schemes. This
+ * should be merged into a common implementation.
+ */
+
+static const char _qchar_lookup[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
+ 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 20
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 30
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 70
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F0
+};
+
+static inline bool _is_qchar(char c)
+{
+ return ((_qchar_lookup[(unsigned char)c] & 0x01) != 0);
+}
+
+static inline int _decode_hex_digit(unsigned char digit)
+{
+ switch (digit) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return (int) digit - '0';
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ return (int) digit - 'a' + 0x0a;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ return (int) digit - 'A' + 0x0A;
+ }
+ return -1;
+}
+
+static bool _parse_hex_value(const char **in, char *out)
+{
+ int value, digit;
+
+ if ((digit = _decode_hex_digit((unsigned char)**in)) < 0)
+ return FALSE;
+
+ value = digit << 4;
+ (*in)++;
+
+ if ((digit = _decode_hex_digit((unsigned char)**in)) < 0)
+ return FALSE;
+
+ value |= digit;
+ (*in)++;
+
+ if (value == 0)
+ return FALSE;
+
+ *out = (char)value;
+ return TRUE;
+}
+
+/*
+ * URI recipient parsing
+ */
+
+static bool
+uri_mailto_add_valid_recipient(struct uri_mailto_parser *parser,
+ string_t *recipient, bool cc)
+{
+ struct uri_mailto *uri = parser->uri;
+ struct uri_mailto_recipient *new_recipient;
+ struct uri_mailto_recipient *rcpts;
+ unsigned int count, i;
+ const char *error;
+ const struct smtp_address *address;
+
+ /* Verify recipient */
+ if ((address = sieve_address_parse_str(recipient, &error)) == NULL) {
+ uri_mailto_error(parser, "invalid recipient '%s': %s",
+ str_sanitize(str_c(recipient), 80), error);
+ return FALSE;
+ }
+
+ /* Add recipient to the uri */
+ if (uri != NULL) {
+ /* Get current recipients */
+ rcpts = array_get_modifiable(&uri->recipients, &count);
+
+ /* Enforce limits */
+ if (parser->max_recipients > 0 &&
+ (int)count >= parser->max_recipients) {
+ if ((int)count == parser->max_recipients) {
+ uri_mailto_warning(
+ parser,
+ "more than the maximum %u recipients specified; "
+ "rest is discarded",
+ parser->max_recipients);
+ }
+ return TRUE;
+ }
+
+ /* Check for duplicate first */
+ for (i = 0; i < count; i++) {
+ if (smtp_address_equals(rcpts[i].address, address)) {
+ /* Upgrade existing Cc: recipient to a To:
+ recipient if possible */
+ rcpts[i].carbon_copy =
+ (rcpts[i].carbon_copy && cc);
+
+ uri_mailto_warning(
+ parser,
+ "ignored duplicate recipient '%s'",
+ str_sanitize(str_c(recipient), 80));
+ return TRUE;
+ }
+ }
+
+ /* Add */
+ new_recipient = array_append_space(&uri->recipients);
+ new_recipient->carbon_copy = cc;
+ new_recipient->full = p_strdup(parser->pool, str_c(recipient));
+ new_recipient->address =
+ smtp_address_clone(parser->pool, address);
+ }
+
+ return TRUE;
+}
+
+static bool
+uri_mailto_parse_recipients(struct uri_mailto_parser *parser,
+ const char **uri_p)
+{
+ string_t *to = t_str_new(128);
+ const char *p = *uri_p;
+
+ if (*p == '\0' || *p == '?')
+ return TRUE;
+
+ while (*p != '\0' && *p != '?') {
+ if (*p == '%') {
+ /* % encoded character */
+ char ch;
+
+ p++;
+
+ /* Parse 2-digit hex value */
+ if (!_parse_hex_value(&p, &ch)) {
+ uri_mailto_error(parser, "invalid %% encoding");
+ return FALSE;
+ }
+
+ /* Check for delimiter */
+ if (ch == ',') {
+ /* Verify and add recipient */
+ if (!uri_mailto_add_valid_recipient(
+ parser, to, FALSE))
+ return FALSE;
+
+ /* Reset for next recipient */
+ str_truncate(to, 0);
+ } else {
+ /* Content character */
+ str_append_c(to, ch);
+ }
+ } else {
+ if (*p == ':' || *p == ';' || *p == ',' ||
+ !_is_qchar(*p)) {
+ uri_mailto_error(
+ parser,
+ "invalid character '%c' in 'to' part", *p);
+ return FALSE;
+ }
+
+ /* Content character */
+ str_append_c(to, *p);
+ p++;
+ }
+ }
+
+ i_assert(*p == '\0' || *p == '?');
+
+ /* Verify and add recipient */
+ if (!uri_mailto_add_valid_recipient(parser, to, FALSE))
+ return FALSE;
+
+ *uri_p = p;
+ return TRUE;
+}
+
+static bool
+uri_mailto_parse_header_recipients(struct uri_mailto_parser *parser,
+ string_t *rcpt_header, bool cc)
+{
+ string_t *to = t_str_new(128);
+ const char *p = (const char *)str_data(rcpt_header);
+ const char *pend = p + str_len(rcpt_header);
+
+ while (p < pend) {
+ if (*p == ',') {
+ /* Verify and add recipient */
+ if (!uri_mailto_add_valid_recipient(parser, to, cc))
+ return FALSE;
+
+ /* Reset for next recipient */
+ str_truncate(to, 0);
+ } else {
+ /* Content character */
+ str_append_c(to, *p);
+ }
+ p++;
+ }
+
+ /* Verify and add recipient */
+ if (!uri_mailto_add_valid_recipient(parser, to, cc))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* URI header parsing */
+
+static bool
+uri_mailto_header_is_duplicate(struct uri_mailto_parser *parser,
+ const char *field_name)
+{
+ struct uri_mailto *uri = parser->uri;
+
+ if (uri == NULL)
+ return FALSE;
+
+ if (uri_mailto_header_is_unique(parser, field_name)) {
+ const struct uri_mailto_header_field *hdrs;
+ unsigned int count, i;
+
+ hdrs = array_get(&uri->headers, &count);
+ for (i = 0; i < count; i++) {
+ if (strcasecmp(hdrs[i].name, field_name) == 0)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static bool
+uri_mailto_parse_headers(struct uri_mailto_parser *parser, const char **uri_p)
+{
+ struct uri_mailto *uri = parser->uri;
+ unsigned int header_count = 0;
+ string_t *field = t_str_new(128);
+ const char *p = *uri_p;
+
+ while (*p != '\0') {
+ enum {
+ _HNAME_IGNORED,
+ _HNAME_GENERIC,
+ _HNAME_TO,
+ _HNAME_CC,
+ _HNAME_SUBJECT,
+ _HNAME_BODY,
+ } hname_type = _HNAME_GENERIC;
+ struct uri_mailto_header_field *hdrf = NULL;
+ const char *field_name;
+
+ /* Parse field name */
+ while (*p != '\0' && *p != '=') {
+ char ch = *p;
+ p++;
+
+ if (ch == '%') {
+ /* Encoded, parse 2-digit hex value */
+ if (!_parse_hex_value(&p, &ch)) {
+ uri_mailto_error(parser,
+ "invalid %% encoding");
+ return FALSE;
+ }
+ } else if (ch != '=' && !_is_qchar(ch)) {
+ uri_mailto_error(
+ parser,
+ "invalid character '%c' in header field name part",
+ ch);
+ return FALSE;
+ }
+
+ str_append_c(field, ch);
+ }
+ if (*p != '\0')
+ p++;
+
+ /* Verify field name */
+ if (!rfc2822_header_field_name_verify(str_c(field),
+ str_len(field))) {
+ uri_mailto_error(parser, "invalid header field name");
+ return FALSE;
+ }
+
+ if (parser->max_headers > -1 &&
+ (int)header_count >= parser->max_headers) {
+ /* Refuse to accept more headers than allowed by policy
+ */
+ if ((int)header_count == parser->max_headers) {
+ uri_mailto_warning(
+ parser,
+ "more than the maximum %u headers specified; "
+ "rest is discarded",
+ parser->max_headers);
+ }
+
+ hname_type = _HNAME_IGNORED;
+ } else {
+ /* Add new header field to array and assign its name */
+
+ field_name = str_c(field);
+ if (strcasecmp(field_name, "to") == 0)
+ hname_type = _HNAME_TO;
+ else if (strcasecmp(field_name, "cc") == 0)
+ hname_type = _HNAME_CC;
+ else if (strcasecmp(field_name, "subject") == 0)
+ hname_type = _HNAME_SUBJECT;
+ else if (strcasecmp(field_name, "body") == 0)
+ hname_type = _HNAME_BODY;
+ else if (!uri_mailto_header_is_reserved(parser, field_name)) {
+ if (uri != NULL) {
+ if (!uri_mailto_header_is_duplicate(parser, field_name)) {
+ hdrf = array_append_space(&uri->headers);
+ hdrf->name = p_strdup(parser->pool, field_name);
+ } else {
+ uri_mailto_warning(
+ parser,
+ "ignored duplicate for unique header field '%s'",
+ str_sanitize(field_name, 32));
+ hname_type = _HNAME_IGNORED;
+ }
+ } else {
+ hname_type = _HNAME_IGNORED;
+ }
+ } else {
+ uri_mailto_warning(
+ parser,
+ "ignored reserved header field '%s'",
+ str_sanitize(field_name, 32));
+ hname_type = _HNAME_IGNORED;
+ }
+ }
+
+ header_count++;
+
+ /* Reset for field body */
+ str_truncate(field, 0);
+
+ /* Parse field body */
+ while (*p != '\0' && *p != '&') {
+ char ch = *p;
+ p++;
+
+ if (ch == '%') {
+ /* Encoded, parse 2-digit hex value */
+ if (!_parse_hex_value(&p, &ch)) {
+ uri_mailto_error(parser,
+ "invalid %% encoding");
+ return FALSE;
+ }
+ } else if (ch != '=' && !_is_qchar(ch)) {
+ uri_mailto_error(
+ parser,
+ "invalid character '%c' in header field value part",
+ ch);
+ return FALSE;
+ }
+ str_append_c(field, ch);
+ }
+ if (*p != '\0')
+ p++;
+
+ /* Verify field body */
+ if (hname_type == _HNAME_BODY) {
+ // FIXME: verify body ...
+ } else {
+ if (!rfc2822_header_field_body_verify(
+ str_c(field), str_len(field), FALSE, FALSE)) {
+ uri_mailto_error(parser,
+ "invalid header field body");
+ return FALSE;
+ }
+ }
+
+ /* Assign field body */
+
+ switch (hname_type) {
+ case _HNAME_IGNORED:
+ break;
+ case _HNAME_TO:
+ /* Gracefully allow duplicate To fields */
+ if (!uri_mailto_parse_header_recipients(
+ parser, field, FALSE))
+ return FALSE;
+ break;
+ case _HNAME_CC:
+ /* Gracefully allow duplicate Cc fields */
+ if (!uri_mailto_parse_header_recipients(
+ parser, field, TRUE))
+ return FALSE;
+ break;
+ case _HNAME_SUBJECT:
+ /* Ignore duplicate subject field */
+ if (uri != NULL) {
+ if (uri->subject == NULL) {
+ uri->subject =
+ p_strdup(parser->pool, str_c(field));
+ } else {
+ uri_mailto_warning(
+ parser,
+ "ignored duplicate subject field");
+ }
+ }
+ break;
+ case _HNAME_BODY:
+ /* Ignore duplicate body field */
+ if (uri != NULL) {
+ if (uri->body == NULL) {
+ uri->body = p_strdup(
+ parser->pool, str_c(field));
+ } else {
+ uri_mailto_warning(
+ parser, "ignored duplicate body field");
+ }
+ }
+ break;
+ case _HNAME_GENERIC:
+ if (uri != NULL && hdrf != NULL)
+ hdrf->body = p_strdup(parser->pool, str_c(field));
+ break;
+ }
+
+ /* Reset for next name */
+ str_truncate(field, 0);
+ }
+
+ /* Skip '&' */
+ if (*p != '\0')
+ p++;
+
+ *uri_p = p;
+ return TRUE;
+}
+
+static bool
+uri_mailto_parse_uri(struct uri_mailto_parser *parser, const char *uri_body)
+{
+ const char *p = uri_body;
+
+ /*
+ * mailtoURI = "mailto:" [ to ] [ hfields ]
+ * to = [ addr-spec *("%2C" addr-spec ) ]
+ * hfields = "?" hfield *( "&" hfield )
+ * hfield = hfname "=" hfvalue
+ * hfname = *qchar
+ * hfvalue = *qchar
+ * addr-spec = local-part "@" domain
+ * local-part = dot-atom / quoted-string
+ * qchar = unreserved / pct-encoded / some-delims
+ * some-delims = "!" / "$" / "'" / "(" / ")" / "*"
+ * / "+" / "," / ";" / ":" / "@"
+ *
+ * to ~= *tqchar
+ * tqchar ~= <qchar> without ";" and ":"
+ *
+ * Scheme 'mailto:' already parsed, starting parse after colon
+ */
+
+ /* First extract to-part by searching for '?' and decoding % items
+ */
+
+ if (!uri_mailto_parse_recipients(parser, &p))
+ return FALSE;
+
+ if (*p == '\0')
+ return TRUE;
+ i_assert(*p == '?');
+ p++;
+
+ /* Extract hfield items */
+
+ while (*p != '\0') {
+ /* Extract hfield item by searching for '&' and decoding '%'
+ items */
+ if (!uri_mailto_parse_headers(parser, &p))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Validation
+ */
+
+bool uri_mailto_validate(const char *uri_body,
+ const char **reserved_headers,
+ const char **unique_headers, int max_recipients,
+ int max_headers, const struct uri_mailto_log *log)
+{
+ struct uri_mailto_parser parser;
+
+ i_zero(&parser);
+ parser.log = log;
+ parser.max_recipients = max_recipients;
+ parser.max_headers = max_headers;
+ parser.reserved_headers = reserved_headers;
+ parser.unique_headers = unique_headers;
+
+ /* If no errors are reported, we don't need to record any data */
+ if (log != NULL) {
+ parser.pool = pool_datastack_create();
+
+ parser.uri = p_new(parser.pool, struct uri_mailto, 1);
+ p_array_init(&parser.uri->recipients, parser.pool, max_recipients);
+ p_array_init(&parser.uri->headers, parser.pool, max_headers);
+ }
+
+ if (!uri_mailto_parse_uri(&parser, uri_body))
+ return FALSE;
+
+ if (log != NULL) {
+ if (array_count(&parser.uri->recipients) == 0) {
+ uri_mailto_warning(
+ &parser,
+ "notification URI specifies no recipients");
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * Parsing
+ */
+
+struct uri_mailto *
+uri_mailto_parse(const char *uri_body, pool_t pool,
+ const char **reserved_headers, const char **unique_headers,
+ int max_recipients, int max_headers,
+ const struct uri_mailto_log *log)
+{
+ struct uri_mailto_parser parser;
+
+ parser.pool = pool;
+ parser.log = log;
+ parser.max_recipients = max_recipients;
+ parser.max_headers = max_headers;
+ parser.reserved_headers = reserved_headers;
+ parser.unique_headers = unique_headers;
+
+ parser.uri = p_new(pool, struct uri_mailto, 1);
+ p_array_init(&parser.uri->recipients, pool, max_recipients);
+ p_array_init(&parser.uri->headers, pool, max_headers);
+
+ if (!uri_mailto_parse_uri(&parser, uri_body))
+ return NULL;
+
+ if (log != NULL) {
+ if (array_count(&parser.uri->recipients) == 0) {
+ uri_mailto_warning(
+ &parser,
+ "notification URI specifies no recipients");
+ }
+ }
+ return parser.uri;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h
new file mode 100644
index 0000000..8e9e057
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h
@@ -0,0 +1,49 @@
+#ifndef URI_MAILTO_H
+#define URI_MAILTO_H
+
+/*
+ * Types
+ */
+
+struct uri_mailto_header_field {
+ const char *name;
+ const char *body;
+};
+
+struct uri_mailto_recipient {
+ const char *full;
+ const struct smtp_address *address;
+ bool carbon_copy;
+};
+
+ARRAY_DEFINE_TYPE(recipients, struct uri_mailto_recipient);
+ARRAY_DEFINE_TYPE(headers, struct uri_mailto_header_field);
+
+struct uri_mailto_log {
+ void *context;
+
+ void (*logv)(void *context, enum log_type log_type,
+ const char *csrc_filename, unsigned int csrc_linenum,
+ const char *fmt, va_list args) ATTR_FORMAT(5, 0);
+};
+
+struct uri_mailto {
+ ARRAY_TYPE(recipients) recipients;
+ ARRAY_TYPE(headers) headers;
+ const char *subject;
+ const char *body;
+};
+
+bool uri_mailto_validate
+ (const char *uri_body, const char **reserved_headers,
+ const char **unique_headers, int max_recipients, int max_headers,
+ const struct uri_mailto_log *log);
+
+struct uri_mailto *uri_mailto_parse
+(const char *uri_body, pool_t pool, const char **reserved_headers,
+ const char **unique_headers, int max_recipients, int max_headers,
+ const struct uri_mailto_log *log);
+
+#endif
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h b/pigeonhole/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h
new file mode 100644
index 0000000..fd8b574
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h
@@ -0,0 +1,197 @@
+#ifndef SIEVE_EXT_ENOTIFY_H
+#define SIEVE_EXT_ENOTIFY_H
+
+#include "lib.h"
+#include "compat.h"
+#include <stdarg.h>
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+
+/*
+ * Forward declarations
+ */
+
+struct sieve_enotify_method;
+struct sieve_enotify_env;
+struct sieve_enotify_action;
+struct sieve_enotify_print_env;
+struct sieve_enotify_exec_env;
+
+/*
+ * Notify method definition
+ */
+
+struct sieve_enotify_method_def {
+ const char *identifier;
+
+ /* Registration */
+ bool (*load)
+ (const struct sieve_enotify_method *nmth, void **context);
+ void (*unload)
+ (const struct sieve_enotify_method *nmth);
+
+ /* Validation */
+ bool (*compile_check_uri)
+ (const struct sieve_enotify_env *nenv, const char *uri,
+ const char *uri_body);
+ bool (*compile_check_message)
+ (const struct sieve_enotify_env *nenv, string_t *message);
+ bool (*compile_check_from)
+ (const struct sieve_enotify_env *nenv, string_t *from);
+ bool (*compile_check_option)
+ (const struct sieve_enotify_env *nenv, const char *option,
+ const char *value);
+
+ /* Runtime */
+ bool (*runtime_check_uri)
+ (const struct sieve_enotify_env *nenv, const char *uri,
+ const char *uri_body);
+ const char *(*runtime_get_method_capability)
+ (const struct sieve_enotify_env *nenv, const char *uri,
+ const char *uri_body, const char *capability);
+ bool (*runtime_check_operands)
+ (const struct sieve_enotify_env *nenv, const char *uri,
+ const char *uri_body, string_t *message, string_t *from,
+ pool_t context_pool, void **method_context);
+ bool (*runtime_set_option)
+ (const struct sieve_enotify_env *nenv, void *method_context,
+ const char *option, const char *value);
+
+ /* Action duplicates */
+ int (*action_check_duplicates)
+ (const struct sieve_enotify_env *nenv,
+ const struct sieve_enotify_action *nact,
+ const struct sieve_enotify_action *nact_other);
+
+ /* Action print */
+ void (*action_print)
+ (const struct sieve_enotify_print_env *penv,
+ const struct sieve_enotify_action *nact);
+
+ /* Action execution
+ (returns 0 if all is ok and -1 for temporary error)
+ */
+ int (*action_execute)
+ (const struct sieve_enotify_exec_env *nenv,
+ const struct sieve_enotify_action *nact);
+};
+
+/*
+ * Notify method instance
+ */
+
+struct sieve_enotify_method {
+ const struct sieve_enotify_method_def *def;
+ int id;
+
+ struct sieve_instance *svinst;
+ void *context;
+};
+
+const struct sieve_enotify_method *sieve_enotify_method_register
+ (struct sieve_instance *svinst,
+ const struct sieve_enotify_method_def *nmth_def);
+void sieve_enotify_method_unregister
+ (const struct sieve_enotify_method *nmth);
+
+/*
+ * Notify method environment
+ */
+
+struct sieve_enotify_env {
+ struct sieve_instance *svinst;
+
+ const struct sieve_enotify_method *method;
+
+ struct sieve_error_handler *ehandler;
+ const char *location;
+ struct event *event;
+};
+
+/*
+ * Notify method printing
+ */
+
+void sieve_enotify_method_printf
+ (const struct sieve_enotify_print_env *penv, const char *fmt, ...)
+ ATTR_FORMAT(2, 3);
+
+/*
+ * Notify execution environment
+ */
+
+struct sieve_enotify_exec_env {
+ struct sieve_instance *svinst;
+ enum sieve_execute_flags flags;
+
+ const struct sieve_enotify_method *method;
+
+ const struct sieve_script_env *scriptenv;
+ const struct sieve_message_data *msgdata;
+ struct sieve_message_context *msgctx;
+
+ struct sieve_error_handler *ehandler;
+ const char *location;
+ struct event *event;
+};
+
+struct event_passthrough *
+sieve_enotify_create_finish_event(const struct sieve_enotify_exec_env *nenv);
+
+/*
+ * Notify action
+ */
+
+struct sieve_enotify_action {
+ const struct sieve_enotify_method *method;
+ void *method_context;
+
+ sieve_number_t importance;
+ const char *message;
+ const char *from;
+};
+
+/*
+ * Error handling
+ */
+
+#define sieve_enotify_error(ENV, ...) \
+ sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \
+ LOG_TYPE_ERROR, (ENV)->location, 0, __VA_ARGS__ )
+#define sieve_enotify_warning(ENV, ...) \
+ sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \
+ LOG_TYPE_WARNING, \
+ (ENV)->location, 0, __VA_ARGS__ )
+#define sieve_enotify_info(ENV, ...) \
+ sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \
+ LOG_TYPE_INFO, \
+ (ENV)->location, 0, __VA_ARGS__ )
+#define sieve_enotify_critical(ENV, ...) \
+ sieve_critical((ENV)->svinst, (ENV)->ehandler, NULL, __VA_ARGS__ )
+
+#define sieve_enotify_global_error(ENV, ...) \
+ sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \
+ LOG_TYPE_ERROR, (ENV)->location, \
+ SIEVE_ERROR_FLAG_GLOBAL, __VA_ARGS__ )
+#define sieve_enotify_global_warning(ENV, ...) \
+ sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \
+ LOG_TYPE_WARNING, (ENV)->location, \
+ SIEVE_ERROR_FLAG_GLOBAL, __VA_ARGS__ )
+#define sieve_enotify_global_info(ENV, ...) \
+ sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \
+ LOG_TYPE_INFO, (ENV)->location, \
+ SIEVE_ERROR_FLAG_GLOBAL, __VA_ARGS__ )
+
+#define sieve_enotify_event_log(ENV, EVENT, ...) \
+ sieve_event_log((ENV)->svinst, (ENV)->ehandler, (EVENT), \
+ LOG_TYPE_INFO, (ENV)->location, \
+ SIEVE_ERROR_FLAG_GLOBAL, __VA_ARGS__ )
+
+#define sieve_enotify_global_log_error(ENV, ...) \
+ sieve_event_log((ENV)->svinst, (ENV)->ehandler, (ENV)->event, \
+ LOG_TYPE_ERROR, (ENV)->location, \
+ (SIEVE_ERROR_FLAG_GLOBAL | \
+ SIEVE_ERROR_FLAG_GLOBAL_MAX_INFO), __VA_ARGS__ )
+#endif
+
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c b/pigeonhole/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c
new file mode 100644
index 0000000..8a6c071
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/tst-notify-method-capability.c
@@ -0,0 +1,233 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-enotify-common.h"
+
+/*
+ * String test
+ *
+ * Syntax:
+ * notify_method_capability [COMPARATOR] [MATCH-TYPE]
+ * <notification-uri: string>
+ * <notification-capability: string>
+ * <key-list: string-list>
+ */
+
+static bool tst_notifymc_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool tst_notifymc_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool tst_notifymc_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def notify_method_capability_test = {
+ .identifier = "notify_method_capability",
+ .type = SCT_TEST,
+ .positional_args = 3,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_notifymc_registered,
+ .validate = tst_notifymc_validate,
+ .generate = tst_notifymc_generate
+};
+
+/*
+ * String operation
+ */
+
+static bool tst_notifymc_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_notifymc_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def notify_method_capability_operation = {
+ .mnemonic = "NOTIFY_METHOD_CAPABILITY",
+ .ext_def = &enotify_extension,
+ .code = EXT_ENOTIFY_OPERATION_NOTIFY_METHOD_CAPABILITY,
+ .dump = tst_notifymc_operation_dump,
+ .execute = tst_notifymc_operation_execute
+};
+
+/*
+ * Optional arguments
+ */
+
+enum tst_notifymc_optional {
+ OPT_END,
+ OPT_COMPARATOR,
+ OPT_MATCH_TYPE
+};
+
+/*
+ * Test registration
+ */
+
+static bool tst_notifymc_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_command_registration *cmd_reg)
+{
+ /* The order of these is not significant */
+ sieve_comparators_link_tag(valdtr, cmd_reg, OPT_COMPARATOR);
+ sieve_match_types_link_tags(valdtr, cmd_reg, OPT_MATCH_TYPE);
+
+ return TRUE;
+}
+
+/*
+ * Test validation
+ */
+
+static bool tst_notifymc_validate
+(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ const struct sieve_match_type mcht_default =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ const struct sieve_comparator cmp_default =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "notification-uri", 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ arg = sieve_ast_argument_next(arg);
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "notification-capability", 2, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ arg = sieve_ast_argument_next(arg);
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "key-list", 3, SAAT_STRING_LIST) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ /* Validate the key argument to a specified match type */
+ return sieve_match_type_validate
+ (valdtr, tst, arg, &mcht_default, &cmp_default);
+}
+
+/*
+ * Test generation
+ */
+
+static bool tst_notifymc_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ sieve_operation_emit
+ (cgenv->sblock, cmd->ext, &notify_method_capability_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool tst_notifymc_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ sieve_code_dumpf(denv, "NOTIFY_METHOD_CAPABILITY");
+ sieve_code_descend(denv);
+
+ /* Handle any optional arguments */
+ if ( sieve_match_opr_optional_dump(denv, address, NULL) != 0 )
+ return FALSE;
+
+ return
+ sieve_opr_string_dump(denv, address, "notify uri") &&
+ sieve_opr_string_dump(denv, address, "notify capability") &&
+ sieve_opr_stringlist_dump(denv, address, "key list");
+}
+
+/*
+ * Code execution
+ */
+
+static int tst_notifymc_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ struct sieve_match_type mcht =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ struct sieve_comparator cmp =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+ string_t *notify_uri, *notify_capability;
+ struct sieve_stringlist *value_list, *key_list;
+ const char *cap_value;
+ int match, ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Handle match-type and comparator operands */
+ if ( sieve_match_opr_optional_read
+ (renv, address, NULL, &ret, &cmp, &mcht) < 0 )
+ return ret;
+
+ /* Read notify uri */
+ if ( (ret=sieve_opr_string_read(renv, address, "notify-uri", &notify_uri))
+ <= 0 )
+ return ret;
+
+ /* Read notify capability */
+ if ( (ret=sieve_opr_string_read
+ (renv, address, "notify-capability", &notify_capability)) <= 0 )
+ return ret;
+
+ /* Read key-list */
+ if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list))
+ <= 0)
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "notify_method_capability test");
+
+ cap_value = ext_enotify_runtime_get_method_capability
+ (renv, notify_uri, str_c(notify_capability));
+
+ if ( cap_value != NULL ) {
+ value_list = sieve_single_stringlist_create_cstr(renv, cap_value, TRUE);
+
+ /* Perform match */
+ if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret))
+ < 0 )
+ return ret;
+ } else {
+ match = 0;
+ }
+
+ /* Set test result for subsequent conditional jump */
+ sieve_interpreter_set_test_result(renv->interp, match > 0);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c b/pigeonhole/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c
new file mode 100644
index 0000000..becc41c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/tst-valid-notify-method.c
@@ -0,0 +1,144 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-enotify-common.h"
+
+/*
+ * Valid_notify_method test
+ *
+ * Syntax:
+ * valid_notify_method <notification-uris: string-list>
+ */
+
+static bool tst_vnotifym_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool tst_vnotifym_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def valid_notify_method_test = {
+ .identifier = "valid_notify_method",
+ .type = SCT_TEST,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = tst_vnotifym_validate,
+ .generate = tst_vnotifym_generate
+};
+
+/*
+ * Valid_notify_method operation
+ */
+
+static bool tst_vnotifym_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_vnotifym_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def valid_notify_method_operation = {
+ .mnemonic = "VALID_NOTIFY_METHOD",
+ .ext_def = &enotify_extension,
+ .code = EXT_ENOTIFY_OPERATION_VALID_NOTIFY_METHOD,
+ .dump = tst_vnotifym_operation_dump,
+ .execute = tst_vnotifym_operation_execute
+};
+
+/*
+ * Test validation
+ */
+
+static bool tst_vnotifym_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "notification-uris", 1, SAAT_STRING_LIST) ) {
+ return FALSE;
+ }
+
+ return sieve_validator_argument_activate(valdtr, tst, arg, FALSE);
+}
+
+/*
+ * Test generation
+ */
+
+static bool tst_vnotifym_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &valid_notify_method_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool tst_vnotifym_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ sieve_code_dumpf(denv, "VALID_NOTIFY_METHOD");
+ sieve_code_descend(denv);
+
+ return
+ sieve_opr_stringlist_dump(denv, address, "notify-uris");
+}
+
+/*
+ * Code execution
+ */
+
+static int tst_vnotifym_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ struct sieve_stringlist *notify_uris;
+ string_t *uri_item;
+ bool all_valid = TRUE;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Read notify uris */
+ if ( (ret=sieve_opr_stringlist_read
+ (renv, address, "notify-uris", &notify_uris)) <= 0 )
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "valid_notify_method test");
+
+ uri_item = NULL;
+ while ( (ret=sieve_stringlist_next_item(notify_uris, &uri_item)) > 0 ) {
+ if ( !ext_enotify_runtime_method_validate(renv, uri_item) ) {
+ all_valid = FALSE;
+ break;
+ }
+ }
+
+ if ( ret < 0 ) {
+ sieve_runtime_trace_error(renv, "invalid method uri item");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ sieve_interpreter_set_test_result(renv->interp, all_valid);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/enotify/vmodf-encodeurl.c b/pigeonhole/src/lib-sieve/plugins/enotify/vmodf-encodeurl.c
new file mode 100644
index 0000000..801f882
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/enotify/vmodf-encodeurl.c
@@ -0,0 +1,119 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "unichar.h"
+#include "str.h"
+
+#include "sieve-common.h"
+#include "sieve-code.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-enotify-common.h"
+
+/*
+ * Encodeurl modifier
+ */
+
+static bool
+mod_encodeurl_modify(const struct sieve_variables_modifier *modf,
+ string_t *in, string_t **result);
+
+const struct sieve_variables_modifier_def encodeurl_modifier = {
+ SIEVE_OBJECT("encodeurl", &encodeurl_operand, 0),
+ 15,
+ mod_encodeurl_modify
+};
+
+/*
+ * Modifier operand
+ */
+
+static const struct sieve_extension_objects ext_enotify_modifiers =
+ SIEVE_VARIABLES_DEFINE_MODIFIER(encodeurl_modifier);
+
+const struct sieve_operand_def encodeurl_operand = {
+ .name = "modifier",
+ .ext_def = &enotify_extension,
+ .class = &sieve_variables_modifier_operand_class,
+ .interface = &ext_enotify_modifiers
+};
+
+/*
+ * Modifier implementation
+ */
+
+static const char _uri_reserved_lookup[256] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, // 20
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, // 30
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 50
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, // 70
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // F0
+};
+
+static bool
+mod_encodeurl_modify(const struct sieve_variables_modifier *modf,
+ string_t *in, string_t **result)
+{
+ size_t max_var_size =
+ sieve_variables_get_max_variable_size(modf->var_ext);
+ const unsigned char *p, *poff, *pend;
+ size_t new_size;
+
+ if ( str_len(in) == 0 ) {
+ *result = in;
+ return TRUE;
+ }
+
+ /* allocate new string */
+ new_size = str_len(in) + 32;
+ if (new_size > max_var_size)
+ new_size = max_var_size;
+ *result = t_str_new(new_size + 1);
+
+ /* escape string */
+ p = str_data(in);
+ pend = p + str_len(in);
+ poff = p;
+ while (p < pend) {
+ unsigned int i, n = uni_utf8_char_bytes(*p);
+
+ if (n > 1 || (_uri_reserved_lookup[*p] & 0x01) != 0) {
+ str_append_data(*result, poff, p - poff);
+ poff = p;
+
+ if (str_len(*result) + 3 * n > max_var_size)
+ break;
+
+ str_printfa(*result, "%%%02X", *p);
+ for (i = 1; i < n && p < pend; i++) {
+ p++;
+ poff++;
+ str_printfa(*result, "%%%02X", *p);
+ }
+
+ poff++;
+ } else if ((str_len(*result) + (p - poff) + 1) > max_var_size) {
+ break;
+ }
+ p++;
+ }
+
+ str_append_data(*result, poff, p - poff);
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/environment/Makefile.am b/pigeonhole/src/lib-sieve/plugins/environment/Makefile.am
new file mode 100644
index 0000000..916362e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/environment/Makefile.am
@@ -0,0 +1,24 @@
+noinst_LTLIBRARIES = libsieve_ext_environment.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tests = \
+ tst-environment.c
+
+libsieve_ext_environment_la_SOURCES = \
+ $(tests) \
+ ext-environment-common.c \
+ ext-environment.c
+
+public_headers = \
+ sieve-ext-environment.h
+
+headers = \
+ ext-environment-common.h
+
+pkginc_libdir=$(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
+
diff --git a/pigeonhole/src/lib-sieve/plugins/environment/Makefile.in b/pigeonhole/src/lib-sieve/plugins/environment/Makefile.in
new file mode 100644
index 0000000..68424f4
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/environment/Makefile.in
@@ -0,0 +1,753 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/environment
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(pkginc_lib_HEADERS) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_environment_la_LIBADD =
+am__objects_1 = tst-environment.lo
+am_libsieve_ext_environment_la_OBJECTS = $(am__objects_1) \
+ ext-environment-common.lo ext-environment.lo
+libsieve_ext_environment_la_OBJECTS = \
+ $(am_libsieve_ext_environment_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-environment-common.Plo \
+ ./$(DEPDIR)/ext-environment.Plo \
+ ./$(DEPDIR)/tst-environment.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_environment_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_environment_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(pkginc_libdir)"
+HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_environment.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tests = \
+ tst-environment.c
+
+libsieve_ext_environment_la_SOURCES = \
+ $(tests) \
+ ext-environment-common.c \
+ ext-environment.c
+
+public_headers = \
+ sieve-ext-environment.h
+
+headers = \
+ ext-environment-common.h
+
+pkginc_libdir = $(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/environment/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/environment/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_environment.la: $(libsieve_ext_environment_la_OBJECTS) $(libsieve_ext_environment_la_DEPENDENCIES) $(EXTRA_libsieve_ext_environment_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_environment_la_OBJECTS) $(libsieve_ext_environment_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-environment-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-environment.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-environment.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkginc_libHEADERS: $(pkginc_lib_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \
+ done
+
+uninstall-pkginc_libHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(pkginc_libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-environment-common.Plo
+ -rm -f ./$(DEPDIR)/ext-environment.Plo
+ -rm -f ./$(DEPDIR)/tst-environment.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pkginc_libHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-environment-common.Plo
+ -rm -f ./$(DEPDIR)/ext-environment.Plo
+ -rm -f ./$(DEPDIR)/tst-environment.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pkginc_libHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-pkginc_libHEADERS install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-pkginc_libHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.c b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.c
new file mode 100644
index 0000000..28d4251
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.c
@@ -0,0 +1,339 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "hash.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-interpreter.h"
+
+#include "ext-environment-common.h"
+
+struct ext_environment_interpreter_context;
+
+/*
+ * Core environment items
+ */
+
+static const struct sieve_environment_item *core_env_items[] = {
+ &domain_env_item,
+ &host_env_item,
+ &location_env_item,
+ &phase_env_item,
+ &name_env_item,
+ &version_env_item
+};
+
+static unsigned int core_env_items_count = N_ELEMENTS(core_env_items);
+
+static void
+sieve_environment_item_insert(struct ext_environment_interpreter_context *ctx,
+ const struct sieve_environment_item *item);
+
+/*
+ * Validator context
+ */
+
+struct ext_environment_interpreter_context {
+ HASH_TABLE(const char *,
+ const struct sieve_environment_item *) name_items;
+ ARRAY(const struct sieve_environment_item *) prefix_items;
+
+ bool active:1;
+};
+
+static void
+ext_environment_interpreter_extension_free(const struct sieve_extension *ext,
+ struct sieve_interpreter *interp,
+ void *context);
+
+struct sieve_interpreter_extension environment_interpreter_extension = {
+ .ext_def = &environment_extension,
+ .free = ext_environment_interpreter_extension_free,
+};
+
+static struct ext_environment_interpreter_context *
+ext_environment_interpreter_context_create(
+ const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp)
+{
+ pool_t pool = sieve_interpreter_pool(interp);
+ struct ext_environment_interpreter_context *ctx;
+
+ ctx = p_new(pool, struct ext_environment_interpreter_context, 1);
+
+ hash_table_create(&ctx->name_items, default_pool, 0, str_hash, strcmp);
+ i_array_init(&ctx->prefix_items, 16);
+
+ sieve_interpreter_extension_register(interp, this_ext,
+ &environment_interpreter_extension,
+ (void *)ctx);
+ return ctx;
+}
+
+static void
+ext_environment_interpreter_extension_free(
+ const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_interpreter *interp ATTR_UNUSED, void *context)
+{
+ struct ext_environment_interpreter_context *ctx =
+ (struct ext_environment_interpreter_context *)context;
+
+ hash_table_destroy(&ctx->name_items);
+ array_free(&ctx->prefix_items);
+}
+
+static struct ext_environment_interpreter_context *
+ext_environment_interpreter_context_get(const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp)
+{
+ struct ext_environment_interpreter_context *ctx =
+ (struct ext_environment_interpreter_context *)
+ sieve_interpreter_extension_get_context(interp, this_ext);
+
+ if (ctx == NULL) {
+ ctx = ext_environment_interpreter_context_create(
+ this_ext, interp);
+ }
+ return ctx;
+}
+
+void ext_environment_interpreter_init(const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp)
+{
+ struct ext_environment_interpreter_context *ctx;
+ unsigned int i;
+
+ /* Create our context */
+ ctx = ext_environment_interpreter_context_get(this_ext, interp);
+
+ for (i = 0; i < core_env_items_count; i++)
+ sieve_environment_item_insert(ctx, core_env_items[i]);
+
+ ctx->active = TRUE;
+}
+
+bool sieve_ext_environment_is_active(const struct sieve_extension *env_ext,
+ struct sieve_interpreter *interp)
+{
+ struct ext_environment_interpreter_context *ctx =
+ ext_environment_interpreter_context_get(env_ext, interp);
+
+ return (ctx != NULL && ctx->active);
+}
+
+/*
+ * Registration
+ */
+
+static void
+sieve_environment_item_insert(struct ext_environment_interpreter_context *ctx,
+ const struct sieve_environment_item *item)
+{
+ if (!item->prefix)
+ hash_table_insert(ctx->name_items, item->name, item);
+ else
+ array_append(&ctx->prefix_items, &item, 1);
+}
+
+void sieve_environment_item_register(const struct sieve_extension *env_ext,
+ struct sieve_interpreter *interp,
+ const struct sieve_environment_item *item)
+{
+ struct ext_environment_interpreter_context *ctx;
+
+ i_assert(sieve_extension_is(env_ext, environment_extension));
+ ctx = ext_environment_interpreter_context_get(env_ext, interp);
+
+ sieve_environment_item_insert(ctx, item);
+}
+
+/*
+ * Retrieval
+ */
+
+static const struct sieve_environment_item *
+ext_environment_item_lookup(struct ext_environment_interpreter_context *ctx,
+ const char **_name)
+{
+ const struct sieve_environment_item *item;
+ const char *name = *_name;
+
+ item = hash_table_lookup(ctx->name_items, name);
+ if (item != NULL)
+ return item;
+
+ array_foreach_elem(&ctx->prefix_items, item) {
+ size_t prefix_len;
+
+ i_assert(item->prefix);
+ prefix_len = strlen(item->name);
+
+ if (str_begins(name, item->name)) {
+ if (name[prefix_len] == '.') {
+ *_name = &name[prefix_len+1];
+ return item;
+ } else if (name[prefix_len] == '\0') {
+ *_name = &name[prefix_len+1];
+ return item;
+ }
+ }
+ }
+ return NULL;
+}
+
+const char *
+ext_environment_item_get_value(const struct sieve_extension *env_ext,
+ const struct sieve_runtime_env *renv,
+ const char *name)
+{
+ struct ext_environment_interpreter_context *ctx;
+ const struct sieve_environment_item *item;
+
+ i_assert(sieve_extension_is(env_ext, environment_extension));
+ ctx = ext_environment_interpreter_context_get(env_ext, renv->interp);
+
+ item = ext_environment_item_lookup(ctx, &name);
+ if (item == NULL)
+ return NULL;
+
+ if (item->value != NULL)
+ return item->value;
+ if (item->get_value != NULL)
+ return item->get_value(renv, name);
+ return NULL;
+}
+
+/*
+ * Default environment items
+ */
+
+/* "domain":
+
+ The primary DNS domain associated with the Sieve execution context, usually
+ but not always a proper suffix of the host name.
+ */
+
+static const char *
+envit_domain_get_value(const struct sieve_runtime_env *renv,
+ const char *name ATTR_UNUSED)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+
+ return eenv->svinst->domainname;
+}
+
+const struct sieve_environment_item domain_env_item = {
+ .name = "domain",
+ .get_value = envit_domain_get_value,
+};
+
+/* "host":
+
+ The fully-qualified domain name of the host where the Sieve script is
+ executing.
+ */
+
+static const char *
+envit_host_get_value(const struct sieve_runtime_env *renv,
+ const char *name ATTR_UNUSED)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+
+ return eenv->svinst->hostname;
+}
+
+const struct sieve_environment_item host_env_item = {
+ .name = "host",
+ .get_value = envit_host_get_value,
+};
+
+/* "location":
+
+ Sieve evaluation can be performed at various different points as messages
+ are processed. This item provides additional information about the type of
+ service that is evaluating the script. Possible values are:
+ "MTA" - the Sieve script is being evaluated by a Message Transfer Agent
+ "MDA" - evaluation is being performed by a Mail Delivery Agent
+ "MUA" - evaluation is being performed by a Mail User Agent (right...)
+ "MS" - evaluation is being performed by a Message Store
+ */
+
+static const char *
+envit_location_get_value(const struct sieve_runtime_env *renv,
+ const char *name ATTR_UNUSED)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+
+ switch (eenv->svinst->env_location ) {
+ case SIEVE_ENV_LOCATION_MDA:
+ return "MDA";
+ case SIEVE_ENV_LOCATION_MTA:
+ return "MTA";
+ case SIEVE_ENV_LOCATION_MS:
+ return "MS";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+const struct sieve_environment_item location_env_item = {
+ .name = "location",
+ .get_value = envit_location_get_value
+};
+
+/* "phase":
+
+ The point relative to final delivery where the Sieve script is being
+ evaluated. Possible values are "pre", "during", and "post", referring
+ respectively to processing before, during, and after final delivery has
+ taken place.
+ */
+
+static const char *
+envit_phase_get_value(const struct sieve_runtime_env *renv,
+ const char *name ATTR_UNUSED)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+
+ switch (eenv->svinst->delivery_phase) {
+ case SIEVE_DELIVERY_PHASE_PRE:
+ return "pre";
+ case SIEVE_DELIVERY_PHASE_DURING:
+ return "during";
+ case SIEVE_DELIVERY_PHASE_POST:
+ return "post";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+const struct sieve_environment_item phase_env_item = {
+ .name = "phase",
+ .get_value = envit_phase_get_value
+};
+
+/* "name":
+
+ The product name associated with the Sieve interpreter.
+ */
+
+const struct sieve_environment_item name_env_item = {
+ .name = "name",
+ .value = PIGEONHOLE_NAME" Sieve"
+};
+
+/* "version":
+
+ The product version associated with the Sieve interpreter. The meaning of the
+ product version string is product-specific and should always be considered
+ in the context of the product name given by the "name" item.
+ */
+
+const struct sieve_environment_item version_env_item = {
+ .name = "version",
+ .value = PIGEONHOLE_VERSION,
+};
diff --git a/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.h b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.h
new file mode 100644
index 0000000..288b82e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.h
@@ -0,0 +1,53 @@
+#ifndef EXT_ENVIRONMENT_COMMON_H
+#define EXT_ENVIRONMENT_COMMON_H
+
+#include "lib.h"
+
+#include "sieve-common.h"
+
+#include "sieve-ext-environment.h"
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def environment_extension;
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def tst_environment;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def tst_environment_operation;
+
+/*
+ * Environment items
+ */
+
+extern const struct sieve_environment_item domain_env_item;
+extern const struct sieve_environment_item host_env_item;
+extern const struct sieve_environment_item location_env_item;
+extern const struct sieve_environment_item phase_env_item;
+extern const struct sieve_environment_item name_env_item;
+extern const struct sieve_environment_item version_env_item;
+
+/*
+ * Initialization
+ */
+
+bool ext_environment_init(const struct sieve_extension *ext, void **context);
+void ext_environment_deinit(const struct sieve_extension *ext);
+
+/*
+ * Validator context
+ */
+
+void ext_environment_interpreter_init(const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/environment/ext-environment.c b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment.c
new file mode 100644
index 0000000..c2130a2
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/environment/ext-environment.c
@@ -0,0 +1,59 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension variables
+ * -------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5183
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "unichar.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+
+#include "sieve-validator.h"
+
+#include "ext-environment-common.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_environment_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+static bool ext_environment_interpreter_load
+(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_extension_def environment_extension = {
+ .name = "environment",
+ .validator_load = ext_environment_validator_load,
+ .interpreter_load = ext_environment_interpreter_load,
+ SIEVE_EXT_DEFINE_OPERATION(tst_environment_operation)
+};
+
+static bool ext_environment_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ sieve_validator_register_command(valdtr, ext, &tst_environment);
+ return TRUE;
+}
+
+static bool ext_environment_interpreter_load
+(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ ext_environment_interpreter_init(ext, renv->interp);
+ return TRUE;
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/environment/sieve-ext-environment.h b/pigeonhole/src/lib-sieve/plugins/environment/sieve-ext-environment.h
new file mode 100644
index 0000000..8ae0d18
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/environment/sieve-ext-environment.h
@@ -0,0 +1,54 @@
+#ifndef SIEVE_EXT_ENVIRONMENT_H
+#define SIEVE_EXT_ENVIRONMENT_H
+
+#include "sieve-common.h"
+
+/*
+ * Environment extension
+ */
+
+/* FIXME: this is not suitable for future plugin support */
+
+extern const struct sieve_extension_def environment_extension;
+
+static inline const struct sieve_extension *
+sieve_ext_environment_get_extension
+(struct sieve_instance *svinst)
+{
+ return sieve_extension_register
+ (svinst, &environment_extension, FALSE);
+}
+
+static inline const struct sieve_extension *
+sieve_ext_environment_require_extension
+(struct sieve_instance *svinst)
+{
+ return sieve_extension_require
+ (svinst, &environment_extension, TRUE);
+}
+
+bool sieve_ext_environment_is_active
+ (const struct sieve_extension *env_ext,
+ struct sieve_interpreter *interp);
+
+/*
+ * Environment item
+ */
+
+struct sieve_environment_item {
+ const char *name;
+ bool prefix;
+
+ const char *value;
+ const char *(*get_value)
+ (const struct sieve_runtime_env *renv, const char *name);
+};
+
+void sieve_environment_item_register
+ (const struct sieve_extension *env_ext, struct sieve_interpreter *interp,
+ const struct sieve_environment_item *item);
+const char *ext_environment_item_get_value
+ (const struct sieve_extension *env_ext,
+ const struct sieve_runtime_env *renv, const char *name);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/environment/tst-environment.c b/pigeonhole/src/lib-sieve/plugins/environment/tst-environment.c
new file mode 100644
index 0000000..9eaa099
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/environment/tst-environment.c
@@ -0,0 +1,215 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-environment-common.h"
+
+/*
+ * Environment test
+ *
+ * Syntax:
+ * environment [COMPARATOR] [MATCH-TYPE]
+ * <name: string> <key-list: string-list>
+ */
+
+static bool tst_environment_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool tst_environment_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool tst_environment_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd);
+
+const struct sieve_command_def tst_environment = {
+ .identifier = "environment",
+ .type = SCT_TEST,
+ .positional_args = 2,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_environment_registered,
+ .validate = tst_environment_validate,
+ .generate = tst_environment_generate
+};
+
+/*
+ * Environment operation
+ */
+
+static bool tst_environment_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_environment_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def tst_environment_operation = {
+ .mnemonic = "ENVIRONMENT",
+ .ext_def = &environment_extension,
+ .dump = tst_environment_operation_dump,
+ .execute = tst_environment_operation_execute
+};
+
+/*
+ * Test registration
+ */
+
+static bool tst_environment_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_command_registration *cmd_reg)
+{
+ /* The order of these is not significant */
+ sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+ sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+ return TRUE;
+}
+
+/*
+ * Test validation
+ */
+
+static bool tst_environment_validate
+(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ const struct sieve_match_type mcht_default =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ const struct sieve_comparator cmp_default =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "name", 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ arg = sieve_ast_argument_next(arg);
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ /* Validate the key argument to a specified match type */
+ return sieve_match_type_validate
+ (valdtr, tst, arg, &mcht_default, &cmp_default);
+}
+
+/*
+ * Test generation
+ */
+
+static bool tst_environment_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &tst_environment_operation);
+
+ /* Generate arguments */
+ if ( !sieve_generate_arguments(cgenv, cmd, NULL) )
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool tst_environment_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ sieve_code_dumpf(denv, "ENVIRONMENT");
+ sieve_code_descend(denv);
+
+ /* Optional operands */
+ if ( sieve_match_opr_optional_dump(denv, address, NULL) != 0 )
+ return FALSE;
+
+ return
+ sieve_opr_string_dump(denv, address, "name") &&
+ sieve_opr_stringlist_dump(denv, address, "key list");
+}
+
+/*
+ * Code execution
+ */
+
+static int tst_environment_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct sieve_match_type mcht =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ struct sieve_comparator cmp =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+ string_t *name;
+ struct sieve_stringlist *value_list, *key_list;
+ const char *env_item;
+ int match, ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Handle match-type and comparator operands */
+ if ( sieve_match_opr_optional_read
+ (renv, address, NULL, &ret, &cmp, &mcht) < 0 )
+ return ret;
+
+ /* Read source */
+ if ( (ret=sieve_opr_string_read(renv, address, "name", &name)) <= 0 )
+ return ret;
+
+ /* Read key-list */
+ if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list))
+ <= 0 )
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "environment test");
+
+ env_item = ext_environment_item_get_value
+ (this_ext, renv, str_c(name));
+
+ if ( env_item != NULL ) {
+ /* Construct value list */
+ value_list = sieve_single_stringlist_create_cstr(renv, env_item, FALSE);
+
+ /* Perform match */
+ if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret))
+ < 0 )
+ return ret;
+ } else {
+ match = 0;
+
+ sieve_runtime_trace_descend(renv);
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "environment item `%s' not found",
+ str_sanitize(str_c(name), 128));
+ }
+
+ /* Set test result for subsequent conditional jump */
+ sieve_interpreter_set_test_result(renv->interp, match > 0);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.am b/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.am
new file mode 100644
index 0000000..1eb0472
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.am
@@ -0,0 +1,22 @@
+noinst_LTLIBRARIES = libsieve_ext_ihave.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tests = \
+ tst-ihave.c
+
+commands = \
+ cmd-error.c
+
+libsieve_ext_ihave_la_SOURCES = \
+ $(tests) \
+ $(commands) \
+ ext-ihave-binary.c \
+ ext-ihave-common.c \
+ ext-ihave.c
+
+noinst_HEADERS = \
+ ext-ihave-binary.h \
+ ext-ihave-common.h
diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.in b/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.in
new file mode 100644
index 0000000..c5b8952
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/ihave/Makefile.in
@@ -0,0 +1,707 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/ihave
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_ihave_la_LIBADD =
+am__objects_1 = tst-ihave.lo
+am__objects_2 = cmd-error.lo
+am_libsieve_ext_ihave_la_OBJECTS = $(am__objects_1) $(am__objects_2) \
+ ext-ihave-binary.lo ext-ihave-common.lo ext-ihave.lo
+libsieve_ext_ihave_la_OBJECTS = $(am_libsieve_ext_ihave_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-error.Plo \
+ ./$(DEPDIR)/ext-ihave-binary.Plo \
+ ./$(DEPDIR)/ext-ihave-common.Plo ./$(DEPDIR)/ext-ihave.Plo \
+ ./$(DEPDIR)/tst-ihave.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_ihave_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_ihave_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_ihave.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tests = \
+ tst-ihave.c
+
+commands = \
+ cmd-error.c
+
+libsieve_ext_ihave_la_SOURCES = \
+ $(tests) \
+ $(commands) \
+ ext-ihave-binary.c \
+ ext-ihave-common.c \
+ ext-ihave.c
+
+noinst_HEADERS = \
+ ext-ihave-binary.h \
+ ext-ihave-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/ihave/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/ihave/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_ihave.la: $(libsieve_ext_ihave_la_OBJECTS) $(libsieve_ext_ihave_la_DEPENDENCIES) $(EXTRA_libsieve_ext_ihave_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_ihave_la_OBJECTS) $(libsieve_ext_ihave_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-error.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-ihave-binary.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-ihave-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-ihave.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-ihave.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmd-error.Plo
+ -rm -f ./$(DEPDIR)/ext-ihave-binary.Plo
+ -rm -f ./$(DEPDIR)/ext-ihave-common.Plo
+ -rm -f ./$(DEPDIR)/ext-ihave.Plo
+ -rm -f ./$(DEPDIR)/tst-ihave.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmd-error.Plo
+ -rm -f ./$(DEPDIR)/ext-ihave-binary.Plo
+ -rm -f ./$(DEPDIR)/ext-ihave-common.Plo
+ -rm -f ./$(DEPDIR)/ext-ihave.Plo
+ -rm -f ./$(DEPDIR)/tst-ihave.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/cmd-error.c b/pigeonhole/src/lib-sieve/plugins/ihave/cmd-error.c
new file mode 100644
index 0000000..6e971bd
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/ihave/cmd-error.c
@@ -0,0 +1,131 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-ihave-common.h"
+
+/*
+ * Error command
+ *
+ * Syntax
+ * error <message: string>
+ */
+
+static bool cmd_error_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool cmd_error_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def error_command = {
+ .identifier = "error",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = cmd_error_validate,
+ .generate = cmd_error_generate
+};
+
+/*
+ * Body operation
+ */
+
+static bool cmd_error_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_error_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def cmd_error_operation = {
+ .mnemonic = "ERROR",
+ .ext_def = &ihave_extension,
+ .code = EXT_IHAVE_OPERATION_ERROR,
+ .dump = cmd_error_operation_dump,
+ .execute = cmd_error_operation_execute
+};
+
+/*
+ * Validation
+ */
+
+static bool cmd_error_validate
+(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "message", 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ return sieve_validator_argument_activate(valdtr, tst, arg, FALSE);
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_error_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &cmd_error_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool cmd_error_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ sieve_code_dumpf(denv, "ERROR");
+ sieve_code_descend(denv);
+
+ return sieve_opr_string_dump(denv, address, "message");
+}
+
+/*
+ * Interpretation
+ */
+
+static int cmd_error_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ string_t *message;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Read message */
+
+ if ( (ret=sieve_opr_string_read(renv, address, "message", &message)) <= 0 )
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "error \"%s\"",
+ str_sanitize(str_c(message), 80));
+
+ sieve_runtime_error(renv, NULL, "%s", str_c(message));
+
+ return SIEVE_EXEC_FAILURE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.c b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.c
new file mode 100644
index 0000000..01b375e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.c
@@ -0,0 +1,249 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-error.h"
+#include "sieve-script.h"
+#include "sieve-binary.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-ihave-common.h"
+#include "ext-ihave-binary.h"
+
+/*
+ * Forward declarations
+ */
+
+static bool ext_ihave_binary_pre_save
+ (const struct sieve_extension *ext, struct sieve_binary *sbin,
+ void *context, enum sieve_error *error_r);
+static bool ext_ihave_binary_open
+ (const struct sieve_extension *ext, struct sieve_binary *sbin,
+ void *context);
+static bool ext_ihave_binary_up_to_date
+ (const struct sieve_extension *ext, struct sieve_binary *sbin,
+ void *context, enum sieve_compile_flags cpflags);
+
+/*
+ * Binary include extension
+ */
+
+const struct sieve_binary_extension ihave_binary_ext = {
+ .extension = &ihave_extension,
+ .binary_pre_save = ext_ihave_binary_pre_save,
+ .binary_open = ext_ihave_binary_open,
+ .binary_up_to_date = ext_ihave_binary_up_to_date
+};
+
+/*
+ * Binary context management
+ */
+
+struct ext_ihave_binary_context {
+ struct sieve_binary *binary;
+ struct sieve_binary_block *block;
+
+ ARRAY(const char *) missing_extensions;
+};
+
+static struct ext_ihave_binary_context *ext_ihave_binary_create_context
+(const struct sieve_extension *this_ext, struct sieve_binary *sbin)
+{
+ pool_t pool = sieve_binary_pool(sbin);
+
+ struct ext_ihave_binary_context *ctx =
+ p_new(pool, struct ext_ihave_binary_context, 1);
+
+ ctx->binary = sbin;
+ p_array_init(&ctx->missing_extensions, pool, 64);
+
+ sieve_binary_extension_set(sbin, this_ext, &ihave_binary_ext, ctx);
+ return ctx;
+}
+
+struct ext_ihave_binary_context *ext_ihave_binary_get_context
+(const struct sieve_extension *this_ext, struct sieve_binary *sbin)
+{
+ struct ext_ihave_binary_context *ctx = (struct ext_ihave_binary_context *)
+ sieve_binary_extension_get_context(sbin, this_ext);
+
+ if ( ctx == NULL )
+ ctx = ext_ihave_binary_create_context(this_ext, sbin);
+
+ return ctx;
+}
+
+struct ext_ihave_binary_context *ext_ihave_binary_init
+(const struct sieve_extension *this_ext, struct sieve_binary *sbin,
+ struct sieve_ast *ast)
+{
+ struct ext_ihave_ast_context *ast_ctx =
+ ext_ihave_get_ast_context(this_ext, ast);
+ struct ext_ihave_binary_context *binctx;
+ const char *const *exts;
+ unsigned int i, count;
+
+ binctx = ext_ihave_binary_get_context(this_ext, sbin);
+
+ exts = array_get(&ast_ctx->missing_extensions, &count);
+
+ if ( count > 0 ) {
+ pool_t pool = sieve_binary_pool(sbin);
+
+ if ( binctx->block == NULL )
+ binctx->block = sieve_binary_extension_create_block(sbin, this_ext);
+
+ for ( i = 0; i < count; i++ ) {
+ const char *ext_name = p_strdup(pool, exts[i]);
+
+ array_append(&binctx->missing_extensions, &ext_name, 1);
+ }
+ }
+
+ return binctx;
+}
+
+/*
+ * Binary extension
+ */
+
+static bool ext_ihave_binary_pre_save
+(const struct sieve_extension *ext, struct sieve_binary *sbin,
+ void *context, enum sieve_error *error_r ATTR_UNUSED)
+{
+ struct ext_ihave_binary_context *binctx =
+ (struct ext_ihave_binary_context *) context;
+ const char *const *exts;
+ unsigned int count, i;
+
+ exts = array_get(&binctx->missing_extensions, &count);
+
+ if ( binctx->block != NULL )
+ sieve_binary_block_clear(binctx->block);
+
+ if ( count > 0 ) {
+ if ( binctx->block == NULL )
+ binctx->block = sieve_binary_extension_create_block(sbin, ext);
+
+ sieve_binary_emit_unsigned(binctx->block, count);
+
+ for ( i = 0; i < count; i++ ) {
+ sieve_binary_emit_cstring(binctx->block, exts[i]);
+ }
+ }
+
+ return TRUE;
+}
+
+static bool ext_ihave_binary_open
+(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context)
+{
+ struct sieve_instance *svinst = ext->svinst;
+ struct ext_ihave_binary_context *binctx =
+ (struct ext_ihave_binary_context *) context;
+ struct sieve_binary_block *sblock;
+ unsigned int i, count, block_id;
+ sieve_size_t offset;
+
+ sblock = sieve_binary_extension_get_block(sbin, ext);
+
+ if ( sblock != NULL ) {
+ binctx->block = sblock;
+ block_id = sieve_binary_block_get_id(sblock);
+
+ offset = 0;
+
+ /* Read number of missing extensions to read subsequently */
+ if ( !sieve_binary_read_unsigned(sblock, &offset, &count) ) {
+ e_error(svinst->event, "ihave: "
+ "failed to read missing extension count "
+ "from block %d of binary %s",
+ block_id, sieve_binary_path(sbin));
+ return FALSE;
+ }
+
+ /* Read dependencies */
+ for ( i = 0; i < count; i++ ) {
+ string_t *ext_name;
+ const char *name;
+
+ if ( !sieve_binary_read_string(sblock, &offset, &ext_name) ) {
+ /* Binary is corrupt, recompile */
+ e_error(svinst->event, "ihave: "
+ "failed to read missing extension name "
+ "from block %d of binary %s",
+ block_id, sieve_binary_path(sbin));
+ return FALSE;
+ }
+
+ name = str_c(ext_name);
+ array_append(&binctx->missing_extensions, &name, 1);
+ }
+ }
+
+ return TRUE;
+}
+
+static bool ext_ihave_binary_up_to_date
+(const struct sieve_extension *ext, struct sieve_binary *sbin ATTR_UNUSED,
+ void *context, enum sieve_compile_flags cpflags)
+{
+ struct ext_ihave_binary_context *binctx =
+ (struct ext_ihave_binary_context *) context;
+ const struct sieve_extension *mext;
+ const char *const *mexts;
+ unsigned int count, i;
+
+ mexts = array_get(&binctx->missing_extensions, &count);
+ for ( i = 0; i < count; i++ ) {
+ if ( (mext=sieve_extension_get_by_name(ext->svinst, mexts[i])) != NULL &&
+ ((cpflags & SIEVE_COMPILE_FLAG_NOGLOBAL) == 0 || !mext->global) )
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Main extension interface
+ */
+
+bool ext_ihave_binary_load
+(const struct sieve_extension *ext, struct sieve_binary *sbin)
+{
+ (void)ext_ihave_binary_get_context(ext, sbin);
+
+ return TRUE;
+}
+
+bool ext_ihave_binary_dump
+(const struct sieve_extension *ext, struct sieve_dumptime_env *denv)
+{
+ struct sieve_binary *sbin = denv->sbin;
+ struct ext_ihave_binary_context *binctx =
+ ext_ihave_binary_get_context(ext, sbin);
+ const char *const *exts;
+ unsigned int count, i;
+
+ exts = array_get(&binctx->missing_extensions, &count);
+
+ if ( count > 0 ) {
+ sieve_binary_dump_sectionf(denv,
+ "Extensions missing at compile (block: %d)",
+ sieve_binary_block_get_id(binctx->block));
+
+ for ( i = 0; i < count; i++ ) {
+ sieve_binary_dumpf(denv, " - %s\n", exts[i]);
+ }
+ }
+
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.h b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.h
new file mode 100644
index 0000000..418f078
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-binary.h
@@ -0,0 +1,33 @@
+#ifndef EXT_IHAVE_BINARY_H
+#define EXT_IHAVE_BINARY_H
+
+/*
+ * Binary context management
+ */
+
+struct ext_ihave_binary_context;
+
+struct ext_ihave_binary_context *ext_ihave_binary_get_context
+ (const struct sieve_extension *this_ext, struct sieve_binary *sbin);
+struct ext_ihave_binary_context *ext_ihave_binary_init
+ (const struct sieve_extension *this_ext, struct sieve_binary *sbin,
+ struct sieve_ast *ast);
+
+/*
+ * Registering missing extension
+ */
+
+void ext_ihave_binary_add_missing_extension
+ (struct ext_ihave_binary_context *binctx, const char *ext_name);
+
+/*
+ * Main extension interface
+ */
+
+bool ext_ihave_binary_load
+ (const struct sieve_extension *ext, struct sieve_binary *sbin);
+bool ext_ihave_binary_dump
+ (const struct sieve_extension *ext, struct sieve_dumptime_env *denv);
+
+#endif
+
diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.c b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.c
new file mode 100644
index 0000000..1d31238
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.c
@@ -0,0 +1,52 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-ast.h"
+
+#include "ext-ihave-common.h"
+
+/*
+ * AST context management
+ */
+
+struct ext_ihave_ast_context *ext_ihave_get_ast_context
+(const struct sieve_extension *this_ext, struct sieve_ast *ast)
+{
+ struct ext_ihave_ast_context *actx = (struct ext_ihave_ast_context *)
+ sieve_ast_extension_get_context(ast, this_ext);
+ pool_t pool;
+
+ if ( actx != NULL )
+ return actx;
+
+ pool = sieve_ast_pool(ast);
+ actx = p_new(pool, struct ext_ihave_ast_context, 1);
+ p_array_init(&actx->missing_extensions, pool, 64);
+
+ sieve_ast_extension_set_context(ast, this_ext, (void *) actx);
+
+ return actx;
+}
+
+void ext_ihave_ast_add_missing_extension
+(const struct sieve_extension *this_ext, struct sieve_ast *ast,
+ const char *ext_name)
+{
+ struct ext_ihave_ast_context *actx =
+ ext_ihave_get_ast_context(this_ext, ast);
+ const char *const *exts;
+ unsigned int i, count;
+
+ exts = array_get(&actx->missing_extensions, &count);
+ for ( i = 0; i < count; i++ ) {
+ if ( strcmp(exts[i], ext_name) == 0 )
+ return;
+ }
+
+ array_append(&actx->missing_extensions, &ext_name, 1);
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.h b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.h
new file mode 100644
index 0000000..371d03d
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave-common.h
@@ -0,0 +1,52 @@
+#ifndef EXT_IHAVE_COMMON_H
+#define EXT_IHAVE_COMMON_H
+
+#include "sieve-common.h"
+
+/*
+ * Extensions
+ */
+
+extern const struct sieve_extension_def ihave_extension;
+
+/*
+ * Tests
+ */
+
+extern const struct sieve_command_def ihave_test;
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def error_command;
+
+/*
+ * Operations
+ */
+
+enum ext_ihave_opcode {
+ EXT_IHAVE_OPERATION_IHAVE,
+ EXT_IHAVE_OPERATION_ERROR
+};
+
+extern const struct sieve_operation_def tst_ihave_operation;
+extern const struct sieve_operation_def cmd_error_operation;
+
+/*
+ * AST context
+ */
+
+struct ext_ihave_ast_context {
+ ARRAY(const char *) missing_extensions;
+};
+
+struct ext_ihave_ast_context *ext_ihave_get_ast_context
+ (const struct sieve_extension *this_ext, struct sieve_ast *ast);
+
+void ext_ihave_ast_add_missing_extension
+ (const struct sieve_extension *this_ext, struct sieve_ast *ast,
+ const char *ext_name);
+
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave.c b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave.c
new file mode 100644
index 0000000..40b70eb
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/ihave/ext-ihave.c
@@ -0,0 +1,70 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension ihave
+ * ---------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5463
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-code.h"
+#include "sieve-binary.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+
+#include "ext-ihave-common.h"
+#include "ext-ihave-binary.h"
+
+/*
+ * Operations
+ */
+
+const struct sieve_operation_def *ext_ihave_operations[] = {
+ &tst_ihave_operation,
+ &cmd_error_operation
+};
+
+/*
+ * Extension
+ */
+
+static bool ext_ihave_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *validator);
+static bool ext_ihave_generator_load
+ (const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv);
+
+const struct sieve_extension_def ihave_extension = {
+ "ihave",
+ .version = 1,
+ .validator_load = ext_ihave_validator_load,
+ .generator_load = ext_ihave_generator_load,
+ .binary_load = ext_ihave_binary_load,
+ .binary_dump = ext_ihave_binary_dump,
+ SIEVE_EXT_DEFINE_OPERATIONS(ext_ihave_operations)
+};
+
+static bool ext_ihave_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *validator)
+{
+ sieve_validator_register_command(validator, ext, &ihave_test);
+ sieve_validator_register_command(validator, ext, &error_command);
+
+ return TRUE;
+}
+
+static bool ext_ihave_generator_load
+(const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv)
+{
+ (void)ext_ihave_binary_init(ext, cgenv->sbin, cgenv->ast);
+
+ return TRUE;
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c b/pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c
new file mode 100644
index 0000000..c6c8e8f
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c
@@ -0,0 +1,294 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-stringlist.h"
+#include "sieve-extensions.h"
+#include "sieve-code.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include "ext-ihave-common.h"
+
+/*
+ * Ihave test
+ *
+ * Syntax:
+ * ihave <capabilities: string-list>
+ */
+
+static bool
+tst_ihave_validate(struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool
+tst_ihave_validate_const(struct sieve_validator *valdtr,
+ struct sieve_command *tst, int *const_current,
+ int const_next);
+static bool
+tst_ihave_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *tst);
+
+const struct sieve_command_def ihave_test = {
+ .identifier = "ihave",
+ .type = SCT_TEST,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = tst_ihave_validate,
+ .validate_const = tst_ihave_validate_const,
+ .generate = tst_ihave_generate
+};
+
+/*
+ * Ihave operation
+ */
+
+static bool
+tst_ihave_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+tst_ihave_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def tst_ihave_operation = {
+ .mnemonic = "IHAVE",
+ .ext_def = &ihave_extension,
+ .code = EXT_IHAVE_OPERATION_IHAVE,
+ .dump = tst_ihave_operation_dump,
+ .execute = tst_ihave_operation_execute
+};
+
+/*
+ * Code validation
+ */
+
+static bool
+tst_ihave_validate(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct _capability {
+ const struct sieve_extension *ext;
+ struct sieve_ast_argument *arg;
+ };
+ struct sieve_ast_argument *arg = tst->first_positional;
+ struct sieve_ast_argument *stritem;
+ enum sieve_compile_flags cpflags =
+ sieve_validator_compile_flags(valdtr);
+ bool no_global = ((cpflags & SIEVE_COMPILE_FLAG_NOGLOBAL) != 0);
+ ARRAY(struct _capability) capabilities;
+ struct _capability capability;
+ const struct _capability *caps;
+ unsigned int i, count;
+ bool all_known = TRUE;
+
+ t_array_init(&capabilities, 64);
+
+ tst->data = (void *)FALSE;
+
+ /* Check stringlist argument */
+ if (!sieve_validate_positional_argument(valdtr, tst, arg,
+ "capabilities", 1,
+ SAAT_STRING_LIST))
+ return FALSE;
+
+ switch (sieve_ast_argument_type(arg)) {
+ case SAAT_STRING:
+ /* Single string */
+ capability.arg = arg;
+ capability.ext = sieve_extension_get_by_name(
+ tst->ext->svinst, sieve_ast_argument_strc(arg));
+
+ if (capability.ext == NULL ||
+ (no_global && capability.ext->global)) {
+ all_known = FALSE;
+
+ ext_ihave_ast_add_missing_extension(
+ tst->ext, tst->ast_node->ast,
+ sieve_ast_argument_strc(arg));
+ } else {
+ array_append(&capabilities, &capability, 1);
+ }
+
+ break;
+
+ case SAAT_STRING_LIST:
+ /* String list */
+ stritem = sieve_ast_strlist_first(arg);
+
+ while (stritem != NULL) {
+ capability.arg = stritem;
+ capability.ext = sieve_extension_get_by_name(
+ tst->ext->svinst,
+ sieve_ast_argument_strc(stritem));
+
+ if (capability.ext == NULL ||
+ (no_global && capability.ext->global)) {
+ all_known = FALSE;
+
+ ext_ihave_ast_add_missing_extension(
+ tst->ext, tst->ast_node->ast,
+ sieve_ast_argument_strc(stritem));
+ } else {
+ array_append(&capabilities, &capability, 1);
+ }
+ stritem = sieve_ast_strlist_next(stritem);
+ }
+
+ break;
+ default:
+ i_unreached();
+ }
+
+ if (!all_known)
+ return TRUE;
+
+ /* RFC 5463, Section 4, page 4:
+
+ The "ihave" extension is designed to be used with other extensions
+ that add tests, actions, comparators, or arguments. Implementations
+ MUST NOT allow it to be used with extensions that change the
+ underlying Sieve grammar, or extensions like encoded-character
+ [RFC5228], or variables [RFC5229] that change how the content of
+ Sieve scripts are interpreted. The test MUST fail and the extension
+ MUST NOT be enabled if such usage is attempted.
+
+ FIXME: current implementation of this restriction is hardcoded and
+ therefore highly inflexible
+ */
+ caps = array_get(&capabilities, &count);
+ for (i = 0; i < count; i++) {
+ if (sieve_extension_name_is(caps[i].ext, "variables") ||
+ sieve_extension_name_is(caps[i].ext, "encoded-character"))
+ return TRUE;
+ }
+
+ /* Load all extensions */
+ caps = array_get(&capabilities, &count);
+ for (i = 0; i < count; i++) {
+ if (!sieve_validator_extension_load(valdtr, tst, caps[i].arg,
+ caps[i].ext, FALSE))
+ return FALSE;
+ }
+
+ if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
+ return FALSE;
+
+ tst->data = (void *)TRUE;
+ return TRUE;
+}
+
+static bool
+tst_ihave_validate_const(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_command *tst, int *const_current,
+ int const_next ATTR_UNUSED)
+{
+ if ((bool)tst->data == TRUE)
+ *const_current = -1;
+ else
+ *const_current = 0;
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+bool tst_ihave_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *tst)
+{
+ /* Emit opcode */
+ sieve_operation_emit(cgenv->sblock, tst->ext, &tst_ihave_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, tst, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+tst_ihave_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ sieve_code_dumpf(denv, "IHAVE");
+ sieve_code_descend(denv);
+
+ return sieve_opr_stringlist_dump(denv, address, "capabilities");
+}
+
+/*
+ * Code execution
+ */
+
+static int
+tst_ihave_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct sieve_instance *svinst = eenv->svinst;
+ struct sieve_stringlist *capabilities;
+ string_t *cap_item;
+ bool matched;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Read capabilities */
+ if ((ret = sieve_opr_stringlist_read(renv, address, "capabilities",
+ &capabilities)) <= 0)
+ return ret;
+
+ /*
+ * Perform test
+ */
+
+ /* Perform the test */
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "ihave test");
+ sieve_runtime_trace_descend(renv);
+
+ cap_item = NULL;
+ matched = TRUE;
+ while (matched &&
+ (ret = sieve_stringlist_next_item(capabilities,
+ &cap_item)) > 0) {
+ const struct sieve_extension *ext;
+ int sret;
+
+ ext = sieve_extension_get_by_name(svinst, str_c(cap_item));
+ if (ext == NULL) {
+ sieve_runtime_trace_error(
+ renv, "ihave: invalid extension name");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ sret = sieve_interpreter_extension_start(renv->interp, ext);
+ if (sret == SIEVE_EXEC_FAILURE) {
+ sieve_runtime_trace(
+ renv, SIEVE_TRLVL_TESTS,
+ "extension `%s' not available",
+ sieve_extension_name(ext));
+ matched = FALSE;
+ } else if (sret == SIEVE_EXEC_OK) {
+ sieve_runtime_trace(
+ renv, SIEVE_TRLVL_TESTS,
+ "extension `%s' available",
+ sieve_extension_name(ext));
+ } else {
+ return sret;
+ }
+ }
+ if (ret < 0) {
+ sieve_runtime_trace_error(renv, "invalid capabilities item");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ /* Set test result for subsequent conditional jump */
+ sieve_interpreter_set_test_result(renv->interp, matched);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.am b/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.am
new file mode 100644
index 0000000..e5390d4
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.am
@@ -0,0 +1,33 @@
+noinst_LTLIBRARIES = libsieve_ext_imap4flags.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../variables \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-flag.c
+
+tests = \
+ tst-hasflag.c
+
+tags = \
+ tag-flags.c
+
+libsieve_ext_imap4flags_la_SOURCES = \
+ ext-imap4flags-common.c \
+ $(commands) \
+ $(tests) \
+ $(tags) \
+ ext-imap4flags.c \
+ ext-imapflags.c
+
+public_headers = \
+ sieve-ext-imap4flags.h
+
+headers = \
+ ext-imap4flags-common.h
+
+pkginc_libdir=$(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.in b/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.in
new file mode 100644
index 0000000..77f2dc3
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/Makefile.in
@@ -0,0 +1,776 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/imap4flags
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(pkginc_lib_HEADERS) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_imap4flags_la_LIBADD =
+am__objects_1 = cmd-flag.lo
+am__objects_2 = tst-hasflag.lo
+am__objects_3 = tag-flags.lo
+am_libsieve_ext_imap4flags_la_OBJECTS = ext-imap4flags-common.lo \
+ $(am__objects_1) $(am__objects_2) $(am__objects_3) \
+ ext-imap4flags.lo ext-imapflags.lo
+libsieve_ext_imap4flags_la_OBJECTS = \
+ $(am_libsieve_ext_imap4flags_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-flag.Plo \
+ ./$(DEPDIR)/ext-imap4flags-common.Plo \
+ ./$(DEPDIR)/ext-imap4flags.Plo ./$(DEPDIR)/ext-imapflags.Plo \
+ ./$(DEPDIR)/tag-flags.Plo ./$(DEPDIR)/tst-hasflag.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_imap4flags_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_imap4flags_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(pkginc_libdir)"
+HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_imap4flags.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../variables \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-flag.c
+
+tests = \
+ tst-hasflag.c
+
+tags = \
+ tag-flags.c
+
+libsieve_ext_imap4flags_la_SOURCES = \
+ ext-imap4flags-common.c \
+ $(commands) \
+ $(tests) \
+ $(tags) \
+ ext-imap4flags.c \
+ ext-imapflags.c
+
+public_headers = \
+ sieve-ext-imap4flags.h
+
+headers = \
+ ext-imap4flags-common.h
+
+pkginc_libdir = $(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/imap4flags/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/imap4flags/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_imap4flags.la: $(libsieve_ext_imap4flags_la_OBJECTS) $(libsieve_ext_imap4flags_la_DEPENDENCIES) $(EXTRA_libsieve_ext_imap4flags_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_imap4flags_la_OBJECTS) $(libsieve_ext_imap4flags_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-flag.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-imap4flags-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-imap4flags.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-imapflags.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag-flags.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-hasflag.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkginc_libHEADERS: $(pkginc_lib_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \
+ done
+
+uninstall-pkginc_libHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(pkginc_libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmd-flag.Plo
+ -rm -f ./$(DEPDIR)/ext-imap4flags-common.Plo
+ -rm -f ./$(DEPDIR)/ext-imap4flags.Plo
+ -rm -f ./$(DEPDIR)/ext-imapflags.Plo
+ -rm -f ./$(DEPDIR)/tag-flags.Plo
+ -rm -f ./$(DEPDIR)/tst-hasflag.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pkginc_libHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmd-flag.Plo
+ -rm -f ./$(DEPDIR)/ext-imap4flags-common.Plo
+ -rm -f ./$(DEPDIR)/ext-imap4flags.Plo
+ -rm -f ./$(DEPDIR)/ext-imapflags.Plo
+ -rm -f ./$(DEPDIR)/tag-flags.Plo
+ -rm -f ./$(DEPDIR)/tst-hasflag.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pkginc_libHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-pkginc_libHEADERS install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-pkginc_libHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/cmd-flag.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/cmd-flag.c
new file mode 100644
index 0000000..2645669
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/cmd-flag.c
@@ -0,0 +1,251 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+
+#include "sieve-code.h"
+#include "sieve-stringlist.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-imap4flags-common.h"
+
+/*
+ * Commands
+ */
+
+/* Forward declarations */
+
+static bool cmd_flag_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+/* Setflag command
+ *
+ * Syntax:
+ * setflag [<variablename: string>] <list-of-flags: string-list>
+ */
+
+const struct sieve_command_def cmd_setflag = {
+ .identifier = "setflag",
+ .type = SCT_COMMAND,
+ .positional_args = -1, /* We check positional arguments ourselves */
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = ext_imap4flags_command_validate,
+ .generate = cmd_flag_generate
+};
+
+/* Addflag command
+ *
+ * Syntax:
+ * addflag [<variablename: string>] <list-of-flags: string-list>
+ */
+
+const struct sieve_command_def cmd_addflag = {
+ .identifier = "addflag",
+ .type = SCT_COMMAND,
+ .positional_args = -1, /* We check positional arguments ourselves */
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = ext_imap4flags_command_validate,
+ .generate = cmd_flag_generate
+};
+
+
+/* Removeflag command
+ *
+ * Syntax:
+ * removeflag [<variablename: string>] <list-of-flags: string-list>
+ */
+
+const struct sieve_command_def cmd_removeflag = {
+ .identifier = "removeflag",
+ .type = SCT_COMMAND,
+ .positional_args = -1, /* We check positional arguments ourselves */
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = ext_imap4flags_command_validate,
+ .generate = cmd_flag_generate
+};
+
+/*
+ * Operations
+ */
+
+/* Forward declarations */
+
+bool cmd_flag_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_flag_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+/* Setflag operation */
+
+const struct sieve_operation_def setflag_operation = {
+ .mnemonic = "SETFLAG",
+ .ext_def = &imap4flags_extension,
+ .code = EXT_IMAP4FLAGS_OPERATION_SETFLAG,
+ .dump = cmd_flag_operation_dump,
+ .execute = cmd_flag_operation_execute
+};
+
+/* Addflag operation */
+
+const struct sieve_operation_def addflag_operation = {
+ .mnemonic = "ADDFLAG",
+ .ext_def = &imap4flags_extension,
+ .code = EXT_IMAP4FLAGS_OPERATION_ADDFLAG,
+ .dump = cmd_flag_operation_dump,
+ .execute = cmd_flag_operation_execute
+};
+
+/* Removeflag operation */
+
+const struct sieve_operation_def removeflag_operation = {
+ .mnemonic = "REMOVEFLAG",
+ .ext_def = &imap4flags_extension,
+ .code = EXT_IMAP4FLAGS_OPERATION_REMOVEFLAG,
+ .dump = cmd_flag_operation_dump,
+ .execute = cmd_flag_operation_execute
+};
+
+/*
+ * Code generation
+ */
+
+static bool cmd_flag_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg1, *arg2;
+
+ /* Emit operation */
+ if ( sieve_command_is(cmd, cmd_setflag) )
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &setflag_operation);
+ else if ( sieve_command_is(cmd, cmd_addflag) )
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &addflag_operation);
+ else if ( sieve_command_is(cmd, cmd_removeflag) )
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &removeflag_operation);
+
+ arg1 = cmd->first_positional;
+ arg2 = sieve_ast_argument_next(arg1);
+
+ if ( arg2 == NULL ) {
+ /* No variable */
+ sieve_opr_omitted_emit(cgenv->sblock);
+ if ( !sieve_generate_argument(cgenv, arg1, cmd) )
+ return FALSE;
+ } else {
+ /* Full command */
+ if ( !sieve_generate_argument(cgenv, arg1, cmd) )
+ return FALSE;
+ if ( !sieve_generate_argument(cgenv, arg2, cmd) )
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+bool cmd_flag_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ struct sieve_operand oprnd;
+
+ sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(denv->oprtn));
+ sieve_code_descend(denv);
+
+ sieve_code_mark(denv);
+ if ( !sieve_operand_read(denv->sblock, address, NULL, &oprnd) ) {
+ sieve_code_dumpf(denv, "ERROR: INVALID OPERAND");
+ return FALSE;
+ }
+
+ if ( !sieve_operand_is_omitted(&oprnd) ) {
+ return
+ sieve_opr_string_dump_data(denv, &oprnd, address, "variable name") &&
+ sieve_opr_stringlist_dump(denv, address, "list of flags");
+ }
+
+ return
+ sieve_opr_stringlist_dump(denv, address, "list of flags");
+}
+
+/*
+ * Code execution
+ */
+
+static int cmd_flag_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ const struct sieve_operation *op = renv->oprtn;
+ struct sieve_operand oprnd;
+ struct sieve_stringlist *flag_list;
+ struct sieve_variable_storage *storage;
+ unsigned int var_index;
+ ext_imapflag_flag_operation_t flag_op;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Read bare operand (two types possible) */
+ if ( (ret=sieve_operand_runtime_read
+ (renv, address, NULL, &oprnd)) <= 0 )
+ return ret;
+
+ /* Variable operand (optional) */
+ if ( !sieve_operand_is_omitted(&oprnd) ) {
+ /* Read the variable operand */
+ if ( (ret=sieve_variable_operand_read_data
+ (renv, &oprnd, address, "variable", &storage, &var_index)) <= 0 )
+ return ret;
+
+ /* Read flag list */
+ if ( (ret=sieve_opr_stringlist_read(renv, address, "flag-list", &flag_list))
+ <= 0 )
+ return ret;
+
+ /* Flag-list operand */
+ } else {
+ storage = NULL;
+ var_index = 0;
+
+ /* Read flag list */
+ if ( (ret=sieve_opr_stringlist_read(renv, address,
+ "flag-list", &flag_list)) <= 0 )
+ return ret;
+ }
+
+ /*
+ * Perform operation
+ */
+
+ /* Determine what to do */
+
+ if ( sieve_operation_is(op, setflag_operation) ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "setflag command");
+ flag_op = sieve_ext_imap4flags_set_flags;
+ } else if ( sieve_operation_is(op, addflag_operation) ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "addflag command");
+ flag_op = sieve_ext_imap4flags_add_flags;
+ } else if ( sieve_operation_is(op, removeflag_operation) ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "removeflag command");
+ flag_op = sieve_ext_imap4flags_remove_flags;
+ } else {
+ i_unreached();
+ }
+
+ sieve_runtime_trace_descend(renv);
+
+ /* Perform requested operation */
+ return flag_op(renv, op->ext, storage, var_index, flag_list);
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c
new file mode 100644
index 0000000..3c364b8
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.c
@@ -0,0 +1,733 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "mail-storage.h"
+#include "imap-arg.h"
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-stringlist.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+#include "sieve-dump.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-imap4flags-common.h"
+
+/*
+ * Tagged arguments
+ */
+
+extern const struct sieve_argument_def tag_flags;
+extern const struct sieve_argument_def tag_flags_implicit;
+
+/*
+ * Common command functions
+ */
+
+bool ext_imap4flags_command_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg = cmd->first_positional;
+ struct sieve_ast_argument *arg2;
+ const struct sieve_extension *var_ext;
+
+ /* Check arguments */
+
+ if ( arg == NULL ) {
+ sieve_command_validate_error(valdtr, cmd,
+ "the %s %s expects at least one argument, but none was found",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd));
+ return FALSE;
+ }
+
+ if ( sieve_ast_argument_type(arg) != SAAT_STRING &&
+ sieve_ast_argument_type(arg) != SAAT_STRING_LIST )
+ {
+ sieve_argument_validate_error(valdtr, arg,
+ "the %s %s expects either a string (variable name) or "
+ "a string-list (list of flags) as first argument, but %s was found",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd),
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+
+ arg2 = sieve_ast_argument_next(arg);
+ if ( arg2 != NULL ) {
+ /* First, check syntax sanity */
+
+ if ( sieve_ast_argument_type(arg) != SAAT_STRING )
+ {
+ if ( sieve_command_is(cmd, tst_hasflag) ) {
+ if ( sieve_ast_argument_type(arg) != SAAT_STRING_LIST ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "if a second argument is specified for the hasflag, the first "
+ "must be a string-list (variable-list), but %s was found",
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+ } else {
+ sieve_argument_validate_error(valdtr, arg,
+ "if a second argument is specified for the %s %s, the first "
+ "must be a string (variable name), but %s was found",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd),
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+ }
+
+ /* Then, check whether the second argument is permitted */
+
+ var_ext = sieve_ext_variables_get_extension(cmd->ext->svinst);
+
+ if ( var_ext == NULL || !sieve_ext_variables_is_active(var_ext, valdtr) )
+ {
+ sieve_argument_validate_error(valdtr,arg,
+ "the %s %s only allows for the specification of a "
+ "variable name when the variables extension is active",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd));
+ return FALSE;
+ }
+
+ if ( !sieve_variable_argument_activate(var_ext, var_ext,
+ valdtr, cmd, arg, !sieve_command_is(cmd, tst_hasflag) ) )
+ return FALSE;
+
+ if ( sieve_ast_argument_type(arg2) != SAAT_STRING &&
+ sieve_ast_argument_type(arg2) != SAAT_STRING_LIST )
+ {
+ sieve_argument_validate_error(valdtr, arg2,
+ "the %s %s expects a string list (list of flags) as "
+ "second argument when two arguments are specified, "
+ "but %s was found",
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd),
+ sieve_ast_argument_name(arg2));
+ return FALSE;
+ }
+ } else
+ arg2 = arg;
+
+ if ( !sieve_validator_argument_activate(valdtr, cmd, arg2, FALSE) )
+ return FALSE;
+
+ if ( !sieve_command_is(cmd, tst_hasflag) &&
+ sieve_argument_is_string_literal(arg2) ) {
+ struct ext_imap4flags_iter fiter;
+ const char *flag;
+
+ /* Warn the user about validity of verifiable flags */
+ ext_imap4flags_iter_init(&fiter, sieve_ast_argument_str(arg));
+
+ while ( (flag=ext_imap4flags_iter_get_flag(&fiter)) != NULL ) {
+ if ( !sieve_ext_imap4flags_flag_is_valid(flag) ) {
+ sieve_argument_validate_warning(valdtr, arg,
+ "IMAP flag '%s' specified for the %s command is invalid "
+ "and will be ignored (only first invalid is reported)",
+ str_sanitize(flag, 64), sieve_command_identifier(cmd));
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * Flags tag registration
+ */
+
+void ext_imap4flags_attach_flags_tag
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ const char *command)
+{
+ /* Register :flags tag with the command and we don't care whether it is
+ * registered or even whether it will be registered at all. The validator
+ * handles either situation gracefully
+ */
+
+ /* Tag specified by user */
+ sieve_validator_register_external_tag
+ (valdtr, command, ext, &tag_flags, SIEVE_OPT_SIDE_EFFECT);
+}
+
+void sieve_ext_imap4flags_register_side_effect
+(struct sieve_validator *valdtr, const struct sieve_extension *flg_ext,
+ const char *command)
+{
+ /* Implicit tag if none is specified */
+ sieve_validator_register_persistent_tag
+ (valdtr, command, flg_ext, &tag_flags_implicit);
+}
+
+
+/*
+ * Result context
+ */
+
+struct ext_imap4flags_result_context {
+ string_t *internal_flags;
+};
+
+static void _get_initial_flags
+(struct sieve_result *result, string_t *flags)
+{
+ const struct sieve_message_data *msgdata =
+ sieve_result_get_message_data(result);
+ enum mail_flags mail_flags;
+ const char *const *mail_keywords;
+
+ mail_flags = mail_get_flags(msgdata->mail);
+ mail_keywords = mail_get_keywords(msgdata->mail);
+
+ if ( (mail_flags & MAIL_FLAGGED) > 0 )
+ str_printfa(flags, " \\flagged");
+
+ if ( (mail_flags & MAIL_ANSWERED) > 0 )
+ str_printfa(flags, " \\answered");
+
+ if ( (mail_flags & MAIL_DELETED) > 0 )
+ str_printfa(flags, " \\deleted");
+
+ if ( (mail_flags & MAIL_SEEN) > 0 )
+ str_printfa(flags, " \\seen");
+
+ if ( (mail_flags & MAIL_DRAFT) > 0 )
+ str_printfa(flags, " \\draft");
+
+ while ( *mail_keywords != NULL ) {
+ str_printfa(flags, " %s", *mail_keywords);
+ mail_keywords++;
+ }
+}
+
+static inline struct ext_imap4flags_result_context *_get_result_context
+(const struct sieve_extension *this_ext, struct sieve_result *result)
+{
+ struct ext_imap4flags_result_context *rctx =
+ (struct ext_imap4flags_result_context *)
+ sieve_result_extension_get_context(result, this_ext);
+
+ if ( rctx == NULL ) {
+ pool_t pool = sieve_result_pool(result);
+
+ rctx =p_new(pool, struct ext_imap4flags_result_context, 1);
+ rctx->internal_flags = str_new(pool, 32);
+ _get_initial_flags(result, rctx->internal_flags);
+
+ sieve_result_extension_set_context
+ (result, this_ext, rctx);
+ }
+
+ return rctx;
+}
+
+static string_t *_get_flags_string
+(const struct sieve_extension *this_ext, struct sieve_result *result)
+{
+ struct ext_imap4flags_result_context *ctx =
+ _get_result_context(this_ext, result);
+
+ return ctx->internal_flags;
+}
+
+/*
+ * Runtime initialization
+ */
+
+static int ext_imap4flags_runtime_init
+(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ void *context ATTR_UNUSED, bool deferred ATTR_UNUSED)
+{
+ sieve_result_add_implicit_side_effect
+ (renv->result, NULL, TRUE, ext, &flags_side_effect, NULL);
+ return SIEVE_EXEC_OK;
+}
+
+const struct sieve_interpreter_extension
+imap4flags_interpreter_extension = {
+ .ext_def = &imap4flags_extension,
+ .run = ext_imap4flags_runtime_init
+};
+
+/*
+ * Flag handling
+ */
+
+/* FIXME: This currently accepts a potentially unlimited number of
+ * flags, making the internal or variable flag list indefinitely long
+ */
+
+bool sieve_ext_imap4flags_flag_is_valid(const char *flag)
+{
+ if ( *flag == '\0' )
+ return FALSE;
+
+ if ( *flag == '\\' ) {
+ /* System flag */
+ const char *atom = t_str_ucase(flag);
+
+ if (
+ (strcmp(atom, "\\ANSWERED") != 0) &&
+ (strcmp(atom, "\\FLAGGED") != 0) &&
+ (strcmp(atom, "\\DELETED") != 0) &&
+ (strcmp(atom, "\\SEEN") != 0) &&
+ (strcmp(atom, "\\DRAFT") != 0) )
+ {
+ return FALSE;
+ }
+ } else {
+ const char *p;
+
+ /* Custom keyword:
+ *
+ * Syntax (IMAP4rev1, RFC 3501, Section 9. Formal Syntax) :
+ * flag-keyword = atom
+ * atom = 1*ATOM-CHAR
+ */
+ p = flag;
+ while ( *p != '\0' ) {
+ if ( !IS_ATOM_CHAR(*p) )
+ return FALSE;
+ p++;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Flag iterator */
+
+static void ext_imap4flags_iter_clear
+(struct ext_imap4flags_iter *iter)
+{
+ i_zero(iter);
+}
+
+void ext_imap4flags_iter_init
+(struct ext_imap4flags_iter *iter, string_t *flags_list)
+{
+ ext_imap4flags_iter_clear(iter);
+ iter->flags_list = flags_list;
+}
+
+static string_t *ext_imap4flags_iter_get_flag_str
+(struct ext_imap4flags_iter *iter)
+{
+ unsigned int len;
+ const unsigned char *fp;
+ const unsigned char *fbegin;
+ const unsigned char *fstart;
+ const unsigned char *fend;
+
+ /* Return if not initialized */
+ if ( iter->flags_list == NULL ) return NULL;
+
+ /* Return if no more flags are available */
+ len = str_len(iter->flags_list);
+ if ( iter->offset >= len ) return NULL;
+
+ /* Mark string boundries */
+ fbegin = str_data(iter->flags_list);
+ fend = fbegin + len;
+
+ /* Start of this flag */
+ fstart = fbegin + iter->offset;
+
+ /* Scan for next flag */
+ fp = fstart;
+ for (;;) {
+ /* Have we reached the end or a flag boundary? */
+ if ( fp >= fend || *fp == ' ' ) {
+ /* Did we scan more than nothing ? */
+ if ( fp > fstart ) {
+ /* Return flag */
+ string_t *flag = t_str_new(fp-fstart+1);
+ str_append_data(flag, fstart, fp-fstart);
+
+ iter->last = fstart - fbegin;
+ iter->offset = fp - fbegin;
+
+ return flag;
+ }
+
+ fstart = fp + 1;
+ }
+
+ if ( fp >= fend ) break;
+
+ fp++;
+ }
+
+ iter->last = fstart - fbegin;
+ iter->offset = fp - fbegin;
+ return NULL;
+}
+
+const char *ext_imap4flags_iter_get_flag
+(struct ext_imap4flags_iter *iter)
+{
+ string_t *flag = ext_imap4flags_iter_get_flag_str(iter);
+
+ if ( flag == NULL ) return NULL;
+
+ return str_c(flag);
+}
+
+static void ext_imap4flags_iter_delete_last
+(struct ext_imap4flags_iter *iter)
+{
+ iter->offset++;
+ if ( iter->offset > str_len(iter->flags_list) )
+ iter->offset = str_len(iter->flags_list);
+ if ( iter->offset == str_len(iter->flags_list) && iter->last > 0 )
+ iter->last--;
+
+ str_delete(iter->flags_list, iter->last, iter->offset - iter->last);
+
+ iter->offset = iter->last;
+}
+
+/* Flag operations */
+
+static string_t *ext_imap4flags_get_flag_variable
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index)
+ ATTR_NULL(2);
+
+static bool flags_list_flag_exists
+(string_t *flags_list, const char *flag)
+{
+ const char *flg;
+ struct ext_imap4flags_iter flit;
+
+ ext_imap4flags_iter_init(&flit, flags_list);
+
+ while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
+ if ( strcasecmp(flg, flag) == 0 )
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void flags_list_flag_delete
+(string_t *flags_list, const char *flag)
+{
+ const char *flg;
+ struct ext_imap4flags_iter flit;
+
+ ext_imap4flags_iter_init(&flit, flags_list);
+
+ while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
+ if ( strcasecmp(flg, flag) == 0 ) {
+ ext_imap4flags_iter_delete_last(&flit);
+ }
+ }
+}
+
+static void flags_list_add_flags
+(string_t *flags_list, string_t *flags)
+{
+ const char *flg;
+ struct ext_imap4flags_iter flit;
+
+ ext_imap4flags_iter_init(&flit, flags);
+
+ while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
+ if ( sieve_ext_imap4flags_flag_is_valid(flg) &&
+ !flags_list_flag_exists(flags_list, flg) ) {
+ if ( str_len(flags_list) != 0 )
+ str_append_c(flags_list, ' ');
+ str_append(flags_list, flg);
+ }
+ }
+}
+
+static void flags_list_remove_flags
+(string_t *flags_list, string_t *flags)
+{
+ const char *flg;
+ struct ext_imap4flags_iter flit;
+
+ ext_imap4flags_iter_init(&flit, flags);
+
+ while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
+ flags_list_flag_delete(flags_list, flg);
+ }
+}
+
+static void flags_list_set_flags
+(string_t *flags_list, string_t *flags)
+{
+ str_truncate(flags_list, 0);
+ flags_list_add_flags(flags_list, flags);
+}
+
+static void flags_list_clear_flags
+(string_t *flags_list)
+{
+ str_truncate(flags_list, 0);
+}
+
+static string_t *ext_imap4flags_get_flag_variable
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index)
+{
+ string_t *flags;
+
+ if ( storage != NULL ) {
+ if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) {
+ const char *var_name, *var_id;
+
+ (void)sieve_variable_get_identifier(storage, var_index, &var_name);
+ var_id = sieve_variable_get_varid(storage, var_index);
+
+ sieve_runtime_trace(renv, 0, "update variable `%s' [%s]",
+ var_name, var_id);
+ }
+
+ if ( !sieve_variable_get_modifiable(storage, var_index, &flags) )
+ return NULL;
+ } else {
+ i_assert( sieve_extension_is(flg_ext, imap4flags_extension) );
+ flags = _get_flags_string(flg_ext, renv->result);
+ }
+
+ return flags;
+}
+
+int sieve_ext_imap4flags_set_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index,
+ struct sieve_stringlist *flags)
+{
+ string_t *cur_flags = ext_imap4flags_get_flag_variable
+ (renv, flg_ext, storage, var_index);
+
+ if ( cur_flags != NULL ) {
+ string_t *flags_item;
+ int ret;
+
+ flags_list_clear_flags(cur_flags);
+ while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+ "set flags `%s'", str_c(flags_item));
+
+ flags_list_add_flags(cur_flags, flags_item);
+ }
+
+ if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT;
+
+ return SIEVE_EXEC_OK;
+ }
+
+ return SIEVE_EXEC_BIN_CORRUPT;
+}
+
+int sieve_ext_imap4flags_add_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index,
+ struct sieve_stringlist *flags)
+{
+ string_t *cur_flags = ext_imap4flags_get_flag_variable
+ (renv, flg_ext, storage, var_index);
+
+ if ( cur_flags != NULL ) {
+ string_t *flags_item;
+ int ret;
+
+ while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+ "add flags `%s'", str_c(flags_item));
+
+ flags_list_add_flags(cur_flags, flags_item);
+ }
+
+ if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT;
+
+ return SIEVE_EXEC_OK;
+ }
+
+ return SIEVE_EXEC_BIN_CORRUPT;
+}
+
+int sieve_ext_imap4flags_remove_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index,
+ struct sieve_stringlist *flags)
+{
+ string_t *cur_flags = ext_imap4flags_get_flag_variable
+ (renv, flg_ext, storage, var_index);
+
+ if ( cur_flags != NULL ) {
+ string_t *flags_item;
+ int ret;
+
+ while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+ "remove flags `%s'", str_c(flags_item));
+
+ flags_list_remove_flags(cur_flags, flags_item);
+ }
+
+ if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT;
+
+ return SIEVE_EXEC_OK;
+ }
+
+ return SIEVE_EXEC_BIN_CORRUPT;
+}
+
+/* Flag stringlist */
+
+static int ext_imap4flags_stringlist_next_item
+ (struct sieve_stringlist *_strlist, string_t **str_r);
+static void ext_imap4flags_stringlist_reset
+ (struct sieve_stringlist *_strlist);
+
+struct ext_imap4flags_stringlist {
+ struct sieve_stringlist strlist;
+
+ struct sieve_stringlist *flags_list;
+ string_t *flags_string;
+ struct ext_imap4flags_iter flit;
+
+ bool normalize:1;
+};
+
+static struct sieve_stringlist *ext_imap4flags_stringlist_create
+(const struct sieve_runtime_env *renv, struct sieve_stringlist *flags_list,
+ bool normalize)
+{
+ struct ext_imap4flags_stringlist *strlist;
+
+ strlist = t_new(struct ext_imap4flags_stringlist, 1);
+ strlist->strlist.exec_status = SIEVE_EXEC_OK;
+ strlist->strlist.runenv = renv;
+ strlist->strlist.next_item = ext_imap4flags_stringlist_next_item;
+ strlist->strlist.reset = ext_imap4flags_stringlist_reset;
+ strlist->normalize = normalize;
+
+ strlist->flags_list = flags_list;
+
+ return &strlist->strlist;
+}
+
+static struct sieve_stringlist *ext_imap4flags_stringlist_create_single
+(const struct sieve_runtime_env *renv, string_t *flags_string, bool normalize)
+{
+ struct ext_imap4flags_stringlist *strlist;
+
+ strlist = t_new(struct ext_imap4flags_stringlist, 1);
+ strlist->strlist.exec_status = SIEVE_EXEC_OK;
+ strlist->strlist.runenv = renv;
+ strlist->strlist.next_item = ext_imap4flags_stringlist_next_item;
+ strlist->strlist.reset = ext_imap4flags_stringlist_reset;
+ strlist->normalize = normalize;
+
+ if ( normalize ) {
+ strlist->flags_string = t_str_new(256);
+ flags_list_set_flags(strlist->flags_string, flags_string);
+ } else {
+ strlist->flags_string = flags_string;
+ }
+
+ ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string);
+
+ return &strlist->strlist;
+}
+
+static int ext_imap4flags_stringlist_next_item
+(struct sieve_stringlist *_strlist, string_t **str_r)
+{
+ struct ext_imap4flags_stringlist *strlist =
+ (struct ext_imap4flags_stringlist *)_strlist;
+
+ while ( (*str_r=ext_imap4flags_iter_get_flag_str(&strlist->flit)) == NULL ) {
+ int ret;
+
+ if ( strlist->flags_list == NULL )
+ return 0;
+
+ if ( (ret=sieve_stringlist_next_item
+ (strlist->flags_list, &strlist->flags_string)) <= 0 )
+ return ret;
+
+ if ( strlist->flags_string == NULL )
+ return -1;
+
+ if ( strlist->normalize ) {
+ string_t *flags_string = t_str_new(256);
+
+ flags_list_set_flags(flags_string, strlist->flags_string);
+ strlist->flags_string = flags_string;
+ }
+
+ ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string);
+ }
+
+ return 1;
+}
+
+static void ext_imap4flags_stringlist_reset
+(struct sieve_stringlist *_strlist)
+{
+ struct ext_imap4flags_stringlist *strlist =
+ (struct ext_imap4flags_stringlist *)_strlist;
+
+ if ( strlist->flags_list != NULL ) {
+ sieve_stringlist_reset(strlist->flags_list);
+ ext_imap4flags_iter_clear(&strlist->flit);
+ } else {
+ ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string);
+ }
+}
+
+/* Flag access */
+
+struct sieve_stringlist *sieve_ext_imap4flags_get_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_stringlist *flags_list)
+{
+ if ( flags_list == NULL ) {
+ i_assert( sieve_extension_is(flg_ext, imap4flags_extension) );
+ return ext_imap4flags_stringlist_create_single
+ (renv, _get_flags_string(flg_ext, renv->result), FALSE);
+ }
+
+ return ext_imap4flags_stringlist_create(renv, flags_list, TRUE);
+}
+
+void ext_imap4flags_get_implicit_flags_init
+(struct ext_imap4flags_iter *iter, const struct sieve_extension *this_ext,
+ struct sieve_result *result)
+{
+ string_t *cur_flags = _get_flags_string(this_ext, result);
+
+ ext_imap4flags_iter_init(iter, cur_flags);
+}
+
+
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.h b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.h
new file mode 100644
index 0000000..4bedb85
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags-common.h
@@ -0,0 +1,97 @@
+#ifndef EXT_IMAP4FLAGS_COMMON_H
+#define EXT_IMAP4FLAGS_COMMON_H
+
+#include "lib.h"
+
+#include "sieve-common.h"
+#include "sieve-ext-variables.h"
+
+#include "sieve-ext-imap4flags.h"
+
+/*
+ * Side effect
+ */
+
+extern const struct sieve_side_effect_def flags_side_effect;
+
+/*
+ * Operands
+ */
+
+extern const struct sieve_operand_def flags_side_effect_operand;
+
+/*
+ * Operations
+ */
+
+enum ext_imap4flags_opcode {
+ EXT_IMAP4FLAGS_OPERATION_SETFLAG,
+ EXT_IMAP4FLAGS_OPERATION_ADDFLAG,
+ EXT_IMAP4FLAGS_OPERATION_REMOVEFLAG,
+ EXT_IMAP4FLAGS_OPERATION_HASFLAG
+};
+
+extern const struct sieve_operation_def setflag_operation;
+extern const struct sieve_operation_def addflag_operation;
+extern const struct sieve_operation_def removeflag_operation;
+extern const struct sieve_operation_def hasflag_operation;
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def cmd_setflag;
+extern const struct sieve_command_def cmd_addflag;
+extern const struct sieve_command_def cmd_removeflag;
+
+extern const struct sieve_command_def tst_hasflag;
+
+/*
+ * Common command functions
+ */
+
+bool ext_imap4flags_command_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+
+/*
+ * Flags tagged argument
+ */
+
+void ext_imap4flags_attach_flags_tag
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ const char *command);
+
+/*
+ * Flag management
+ */
+
+struct ext_imap4flags_iter {
+ string_t *flags_list;
+ unsigned int offset;
+ unsigned int last;
+};
+
+void ext_imap4flags_iter_init
+ (struct ext_imap4flags_iter *iter, string_t *flags_list);
+
+const char *ext_imap4flags_iter_get_flag
+ (struct ext_imap4flags_iter *iter);
+
+/* Flag operations */
+
+typedef int (*ext_imapflag_flag_operation_t)
+ (const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index, struct sieve_stringlist *flags)
+ ATTR_NULL(2);
+
+/* Flags access */
+
+void ext_imap4flags_get_implicit_flags_init
+ (struct ext_imap4flags_iter *iter, const struct sieve_extension *this_ext,
+ struct sieve_result *result);
+
+
+#endif
+
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c
new file mode 100644
index 0000000..23230c1
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imap4flags.c
@@ -0,0 +1,96 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension imap4flags
+ * --------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5232
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "mempool.h"
+#include "str.h"
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include "ext-imap4flags-common.h"
+
+/*
+ * Operations
+ */
+
+const struct sieve_operation_def *imap4flags_operations[] = {
+ &setflag_operation,
+ &addflag_operation,
+ &removeflag_operation,
+ &hasflag_operation
+};
+
+/*
+ * Extension
+ */
+
+static bool ext_imap4flags_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+static bool ext_imap4flags_interpreter_load
+ (const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_extension_def imap4flags_extension = {
+ .name = "imap4flags",
+ .version = 1,
+ .validator_load = ext_imap4flags_validator_load,
+ .interpreter_load = ext_imap4flags_interpreter_load,
+ SIEVE_EXT_DEFINE_OPERATIONS(imap4flags_operations),
+ SIEVE_EXT_DEFINE_OPERAND(flags_side_effect_operand)
+};
+
+static bool ext_imap4flags_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register commands */
+ sieve_validator_register_command(valdtr, ext, &cmd_setflag);
+ sieve_validator_register_command(valdtr, ext, &cmd_addflag);
+ sieve_validator_register_command(valdtr, ext, &cmd_removeflag);
+ sieve_validator_register_command(valdtr, ext, &tst_hasflag);
+
+ /* Attach :flags tag to keep and fileinto commands */
+ ext_imap4flags_attach_flags_tag(valdtr, ext, "keep");
+ ext_imap4flags_attach_flags_tag(valdtr, ext, "fileinto");
+
+ /* Attach flags side-effect to keep and fileinto actions */
+ sieve_ext_imap4flags_register_side_effect(valdtr, ext, "keep");
+ sieve_ext_imap4flags_register_side_effect(valdtr, ext, "fileinto");
+
+ return TRUE;
+}
+
+void sieve_ext_imap4flags_interpreter_load
+(const struct sieve_extension *ext, const struct sieve_runtime_env *renv)
+{
+ sieve_interpreter_extension_register
+ (renv->interp, ext, &imap4flags_interpreter_extension, NULL);
+}
+
+static bool ext_imap4flags_interpreter_load
+(const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ sieve_ext_imap4flags_interpreter_load(ext, renv);
+ return TRUE;
+}
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imapflags.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imapflags.c
new file mode 100644
index 0000000..ba99035
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/ext-imapflags.c
@@ -0,0 +1,213 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension imapflags
+ * --------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: draft-melnikov-sieve-imapflags-03.txt
+ * Implementation: full, but deprecated; provided for backwards compatibility
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "mempool.h"
+#include "str.h"
+
+#include "sieve-common.h"
+
+#include "sieve-ast.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include "ext-imap4flags-common.h"
+
+/*
+ * Commands
+ */
+
+static bool cmd_mark_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+
+/* Mark command
+ *
+ * Syntax:
+ * mark
+ */
+
+static const struct sieve_command_def cmd_mark = {
+ .identifier = "mark",
+ .type = SCT_COMMAND,
+ .positional_args = 0,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = cmd_mark_validate
+};
+
+/* Unmark command
+ *
+ * Syntax:
+ * unmark
+ */
+static const struct sieve_command_def cmd_unmark = {
+ .identifier = "unmark",
+ .type = SCT_COMMAND,
+ .positional_args = 0,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = cmd_mark_validate
+};
+
+/*
+ * Extension
+ */
+
+static bool ext_imapflags_load
+ (const struct sieve_extension *ext, void **context);
+static bool ext_imapflags_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+static bool ext_imapflags_interpreter_load
+ (const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_extension_def imapflags_extension = {
+ .name = "imapflags",
+ .load = ext_imapflags_load,
+ .validator_load = ext_imapflags_validator_load,
+ .interpreter_load = ext_imapflags_interpreter_load
+};
+
+static bool ext_imapflags_load
+(const struct sieve_extension *ext, void **context)
+{
+ if ( *context == NULL ) {
+ /* Make sure real extension is registered, it is needed by the binary */
+ *context = (void *)
+ sieve_extension_require(ext->svinst, &imap4flags_extension, FALSE);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Validator
+ */
+
+static bool ext_imapflags_validator_check_conflict
+ (const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context,
+ struct sieve_ast_argument *require_arg,
+ const struct sieve_extension *other_ext,
+ bool required);
+static bool ext_imapflags_validator_validate
+ (const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context,
+ struct sieve_ast_argument *require_arg,
+ bool required);
+
+const struct sieve_validator_extension
+imapflags_validator_extension = {
+ .ext = &imapflags_extension,
+ .check_conflict = ext_imapflags_validator_check_conflict,
+ .validate = ext_imapflags_validator_validate
+};
+
+static bool ext_imapflags_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ sieve_validator_extension_register
+ (valdtr, ext, &imapflags_validator_extension, NULL);
+
+ return TRUE;
+}
+
+static bool ext_imapflags_validator_check_conflict
+(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context ATTR_UNUSED,
+ struct sieve_ast_argument *require_arg,
+ const struct sieve_extension *ext_other,
+ bool required ATTR_UNUSED)
+{
+ const struct sieve_extension *master_ext =
+ (const struct sieve_extension *) ext->context;
+
+ if ( ext_other == master_ext ) {
+ sieve_argument_validate_error(valdtr, require_arg,
+ "the (deprecated) imapflags extension cannot be used "
+ "together with the imap4flags extension");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static bool ext_imapflags_validator_validate
+(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context ATTR_UNUSED,
+ struct sieve_ast_argument *require_arg ATTR_UNUSED,
+ bool required ATTR_UNUSED)
+{
+ const struct sieve_extension *master_ext =
+ (const struct sieve_extension *) ext->context;
+
+ /* No conflicts */
+
+ /* Register commands */
+ sieve_validator_register_command(valdtr, master_ext, &cmd_setflag);
+ sieve_validator_register_command(valdtr, master_ext, &cmd_addflag);
+ sieve_validator_register_command(valdtr, master_ext, &cmd_removeflag);
+
+ sieve_validator_register_command(valdtr, master_ext, &cmd_mark);
+ sieve_validator_register_command(valdtr, master_ext, &cmd_unmark);
+
+ /* Attach flags side-effect to keep and fileinto actions */
+ sieve_ext_imap4flags_register_side_effect(valdtr, master_ext, "keep");
+ sieve_ext_imap4flags_register_side_effect(valdtr, master_ext, "fileinto");
+
+ return TRUE;
+}
+
+/*
+ * Interpreter
+ */
+
+static bool ext_imapflags_interpreter_load
+(const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ const struct sieve_extension *master_ext =
+ (const struct sieve_extension *) ext->context;
+
+ sieve_ext_imap4flags_interpreter_load(master_ext, renv);
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool cmd_mark_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ if ( sieve_command_is(cmd, cmd_mark) )
+ cmd->def = &cmd_addflag;
+ else
+ cmd->def = &cmd_removeflag;
+
+ cmd->first_positional = sieve_ast_argument_cstring_create
+ (cmd->ast_node, "\\flagged", cmd->ast_node->source_line);
+
+ if ( !sieve_validator_argument_activate
+ (valdtr, cmd, cmd->first_positional, FALSE) )
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/sieve-ext-imap4flags.h b/pigeonhole/src/lib-sieve/plugins/imap4flags/sieve-ext-imap4flags.h
new file mode 100644
index 0000000..b38a3c8
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/sieve-ext-imap4flags.h
@@ -0,0 +1,74 @@
+#ifndef SIEVE_EXT_IMAP4FLAGS_H
+#define SIEVE_EXT_IMAP4FLAGS_H
+
+struct sieve_variable_storage;
+
+/*
+ * Imap4flags extension
+ */
+
+/* FIXME: this is not suitable for future plugin support */
+
+extern const struct sieve_extension_def imap4flags_extension;
+extern const struct sieve_interpreter_extension
+ imap4flags_interpreter_extension;
+
+static inline const struct sieve_extension *
+sieve_ext_imap4flags_require_extension
+(struct sieve_instance *svinst)
+{
+ return sieve_extension_require
+ (svinst, &imap4flags_extension, TRUE);
+}
+
+void sieve_ext_imap4flags_interpreter_load
+(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv);
+
+/*
+ * Action side-effect
+ */
+
+void sieve_ext_imap4flags_register_side_effect
+(struct sieve_validator *valdtr, const struct sieve_extension *flg_ext,
+ const char *command);
+
+/*
+ * Flag syntax
+ */
+
+bool sieve_ext_imap4flags_flag_is_valid(const char *flag);
+
+/*
+ * Flag manipulation
+ */
+
+int sieve_ext_imap4flags_set_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index,
+ struct sieve_stringlist *flags) ATTR_NULL(3);
+int sieve_ext_imap4flags_add_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index,
+ struct sieve_stringlist *flags) ATTR_NULL(3);
+int sieve_ext_imap4flags_remove_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_variable_storage *storage,
+ unsigned int var_index,
+ struct sieve_stringlist *flags) ATTR_NULL(3);
+
+/*
+ * Flag retrieval
+ */
+
+struct sieve_stringlist *sieve_ext_imap4flags_get_flags
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *flg_ext,
+ struct sieve_stringlist *flags_list);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/tag-flags.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/tag-flags.c
new file mode 100644
index 0000000..331063d
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/tag-flags.c
@@ -0,0 +1,402 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "array.h"
+#include "mail-storage.h"
+
+#include "sieve-code.h"
+#include "sieve-stringlist.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-result.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-actions.h"
+#include "sieve-dump.h"
+
+#include "ext-imap4flags-common.h"
+
+#include <ctype.h>
+
+/*
+ * Flags tagged argument
+ */
+
+static bool
+tag_flags_validate(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg, struct sieve_command *cmd);
+static bool
+tag_flags_validate_persistent(struct sieve_validator *valdtr,
+ struct sieve_command *cmd,
+ const struct sieve_extension *ext);
+static bool
+tag_flags_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd);
+
+const struct sieve_argument_def tag_flags = {
+ .identifier = "flags",
+ .validate = tag_flags_validate,
+ .generate = tag_flags_generate,
+};
+
+const struct sieve_argument_def tag_flags_implicit = {
+ .identifier = "flags-implicit",
+ .validate_persistent = tag_flags_validate_persistent,
+ .generate = tag_flags_generate,
+};
+
+/*
+ * Side effect
+ */
+
+static bool
+seff_flags_dump_context(const struct sieve_side_effect *seffect,
+ const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+seff_flags_read_context(const struct sieve_side_effect *seffect,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address, void **context);
+
+static int
+seff_flags_merge(const struct sieve_runtime_env *renv,
+ const struct sieve_action *action,
+ const struct sieve_side_effect *old_seffect,
+ const struct sieve_side_effect *new_seffect,
+ void **old_context);
+
+static void
+seff_flags_print(const struct sieve_side_effect *seffect,
+ const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv, bool *keep);
+
+static int
+seff_flags_pre_execute(const struct sieve_side_effect *seffect,
+ const struct sieve_action_exec_env *aenv,
+ void *tr_context, void **se_tr_context);
+
+const struct sieve_side_effect_def flags_side_effect = {
+ SIEVE_OBJECT("flags", &flags_side_effect_operand, 0),
+ .to_action = &act_store,
+ .dump_context = seff_flags_dump_context,
+ .read_context = seff_flags_read_context,
+ .merge = seff_flags_merge,
+ .print = seff_flags_print,
+ .pre_execute = seff_flags_pre_execute,
+};
+
+/*
+ * Operand
+ */
+
+static const struct sieve_extension_objects ext_side_effects =
+ SIEVE_EXT_DEFINE_SIDE_EFFECT(flags_side_effect);
+
+const struct sieve_operand_def flags_side_effect_operand = {
+ .name = "flags operand",
+ .ext_def = &imap4flags_extension,
+ .class = &sieve_side_effect_operand_class,
+ .interface = &ext_side_effects,
+};
+
+/*
+ * Tag validation
+ */
+
+static bool
+tag_flags_validate_persistent(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_command *cmd,
+ const struct sieve_extension *ext)
+{
+ if (sieve_command_find_argument(cmd, &tag_flags) == NULL)
+ sieve_command_add_dynamic_tag(cmd, ext, &tag_flags_implicit,
+ -1);
+ return TRUE;
+}
+
+static bool
+tag_flags_validate(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_argument_next(*arg);
+
+ /* Check syntax:
+ * :flags <list-of-flags: string-list>
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_STRING_LIST, FALSE))
+ return FALSE;
+
+ tag->parameters = *arg;
+
+ /* Detach parameter */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+tag_flags_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *param;
+
+ if (sieve_ast_argument_type(arg) != SAAT_TAG)
+ return FALSE;
+
+ sieve_opr_side_effect_emit(cgenv->sblock, arg->argument->ext,
+ &flags_side_effect);
+
+ if (sieve_argument_is(arg, tag_flags)) {
+ /* Explicit :flags tag */
+ param = arg->parameters;
+
+ /* Call the generation function for the argument */
+ if (param->argument != NULL && param->argument->def != NULL &&
+ param->argument->def->generate != NULL &&
+ !param->argument->def->generate(cgenv, param, cmd))
+ return FALSE;
+ } else if (sieve_argument_is(arg, tag_flags_implicit)) {
+ /* Implicit flags */
+ sieve_opr_omitted_emit(cgenv->sblock);
+ } else {
+ /* Something else?! */
+ i_unreached();
+ }
+ return TRUE;
+}
+
+/*
+ * Side effect implementation
+ */
+
+/* Context data */
+
+struct seff_flags_context {
+ ARRAY(const char *) keywords;
+ enum mail_flags flags;
+};
+
+/* Context coding */
+
+static bool
+seff_flags_dump_context(const struct sieve_side_effect *seffect ATTR_UNUSED,
+ const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ return sieve_opr_stringlist_dump_ex(denv, address, "flags", "INTERNAL");
+}
+
+static struct seff_flags_context *
+seff_flags_get_implicit_context(const struct sieve_extension *this_ext,
+ struct sieve_result *result)
+{
+ pool_t pool = sieve_result_pool(result);
+ struct seff_flags_context *ctx;
+ const char *flag;
+ struct ext_imap4flags_iter flit;
+
+ ctx = p_new(pool, struct seff_flags_context, 1);
+ p_array_init(&ctx->keywords, pool, 2);
+
+ T_BEGIN {
+ /* Unpack */
+ ext_imap4flags_get_implicit_flags_init(&flit, this_ext, result);
+ while ((flag = ext_imap4flags_iter_get_flag(&flit)) != NULL) {
+ if (flag != NULL && *flag != '\\') {
+ /* keyword */
+ const char *keyword = p_strdup(pool, flag);
+ array_append(&ctx->keywords, &keyword, 1);
+ } else {
+ /* system flag */
+ if (flag == NULL ||
+ strcasecmp(flag, "\\flagged") == 0)
+ ctx->flags |= MAIL_FLAGGED;
+ else if (strcasecmp(flag, "\\answered") == 0)
+ ctx->flags |= MAIL_ANSWERED;
+ else if (strcasecmp(flag, "\\deleted") == 0)
+ ctx->flags |= MAIL_DELETED;
+ else if (strcasecmp(flag, "\\seen") == 0)
+ ctx->flags |= MAIL_SEEN;
+ else if (strcasecmp(flag, "\\draft") == 0)
+ ctx->flags |= MAIL_DRAFT;
+ }
+ }
+ } T_END;
+
+ return ctx;
+}
+
+static int
+seff_flags_do_read_context(const struct sieve_side_effect *seffect,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address, void **se_context)
+{
+ pool_t pool = sieve_result_pool(renv->result);
+ struct seff_flags_context *ctx;
+ string_t *flags_item;
+ struct sieve_stringlist *flag_list = NULL;
+ int ret;
+
+ ret = sieve_opr_stringlist_read_ex(renv, address, "flags", TRUE,
+ &flag_list);
+ if (ret <= 0)
+ return ret;
+
+ if (flag_list == NULL) {
+ /* Flag list is omitted, use current value of internal
+ * variable to construct side effect context.
+ */
+ *se_context = seff_flags_get_implicit_context(
+ SIEVE_OBJECT_EXTENSION(seffect), renv->result);
+ return SIEVE_EXEC_OK;
+ }
+
+ ctx = p_new(pool, struct seff_flags_context, 1);
+ p_array_init(&ctx->keywords, pool, 2);
+
+ /* Unpack */
+ flags_item = NULL;
+ while ((ret = sieve_stringlist_next_item(flag_list, &flags_item)) > 0) {
+ const char *flag;
+ struct ext_imap4flags_iter flit;
+
+ ext_imap4flags_iter_init(&flit, flags_item);
+
+ while ((flag = ext_imap4flags_iter_get_flag(&flit)) != NULL) {
+ if (flag != NULL && *flag != '\\') {
+ /* keyword */
+ const char *keyword = p_strdup(pool, flag);
+
+ /* FIXME: should check for duplicates (cannot
+ trust variables) */
+ array_append(&ctx->keywords, &keyword, 1);
+ } else {
+ /* system flag */
+ if (flag == NULL ||
+ strcasecmp(flag, "\\flagged") == 0)
+ ctx->flags |= MAIL_FLAGGED;
+ else if (strcasecmp(flag, "\\answered") == 0)
+ ctx->flags |= MAIL_ANSWERED;
+ else if (strcasecmp(flag, "\\deleted") == 0)
+ ctx->flags |= MAIL_DELETED;
+ else if (strcasecmp(flag, "\\seen") == 0)
+ ctx->flags |= MAIL_SEEN;
+ else if (strcasecmp(flag, "\\draft") == 0)
+ ctx->flags |= MAIL_DRAFT;
+ }
+ }
+ }
+
+ if (ret < 0)
+ return flag_list->exec_status;
+
+ *se_context = (void *)ctx;
+ return SIEVE_EXEC_OK;
+}
+
+static int
+seff_flags_read_context(const struct sieve_side_effect *seffect,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address, void **se_context)
+{
+ int ret;
+
+ T_BEGIN {
+ ret = seff_flags_do_read_context(seffect, renv, address,
+ se_context);
+ } T_END;
+
+ return ret;
+}
+
+/* Result verification */
+
+static int
+seff_flags_merge(const struct sieve_runtime_env *renv ATTR_UNUSED,
+ const struct sieve_action *action ATTR_UNUSED,
+ const struct sieve_side_effect *old_seffect ATTR_UNUSED,
+ const struct sieve_side_effect *new_seffect,
+ void **old_context)
+{
+ if (new_seffect != NULL)
+ *old_context = new_seffect->context;
+ return 1;
+}
+
+/* Result printing */
+
+static void
+seff_flags_print(const struct sieve_side_effect *seffect,
+ const struct sieve_action *action ATTR_UNUSED,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep ATTR_UNUSED)
+{
+ struct sieve_result *result = rpenv->result;
+ struct seff_flags_context *ctx =
+ (struct seff_flags_context *)seffect->context;
+ unsigned int i;
+
+ if (ctx == NULL) {
+ ctx = seff_flags_get_implicit_context(
+ SIEVE_OBJECT_EXTENSION(seffect), result);
+ }
+
+ if (ctx->flags != 0 || array_count(&ctx->keywords) > 0) {
+ T_BEGIN {
+ string_t *flags = t_str_new(128);
+
+ if ((ctx->flags & MAIL_FLAGGED) > 0)
+ str_printfa(flags, " \\flagged");
+ if ((ctx->flags & MAIL_ANSWERED) > 0)
+ str_printfa(flags, " \\answered");
+ if ((ctx->flags & MAIL_DELETED) > 0)
+ str_printfa(flags, " \\deleted");
+ if ((ctx->flags & MAIL_SEEN) > 0)
+ str_printfa(flags, " \\seen");
+ if ((ctx->flags & MAIL_DRAFT) > 0)
+ str_printfa(flags, " \\draft");
+
+ for (i = 0; i < array_count(&ctx->keywords); i++) {
+ const char *const *keyword =
+ array_idx(&ctx->keywords, i);
+ str_printfa(flags, " %s",
+ str_sanitize(*keyword, 64));
+ }
+
+ sieve_result_seffect_printf(rpenv, "add IMAP flags:%s",
+ str_c(flags));
+ } T_END;
+ }
+}
+
+/* Result execution */
+
+static int
+seff_flags_pre_execute(const struct sieve_side_effect *seffect,
+ const struct sieve_action_exec_env *aenv,
+ void *tr_context, void **se_tr_context ATTR_UNUSED)
+{
+ struct seff_flags_context *ctx = seffect->context;
+ const char *const *keywords;
+
+ if (ctx == NULL) {
+ ctx = seff_flags_get_implicit_context(
+ SIEVE_OBJECT_EXTENSION(seffect), aenv->result);
+ }
+
+ (void)array_append_space(&ctx->keywords);
+ keywords = array_idx(&ctx->keywords, 0);
+
+ sieve_act_store_add_flags(aenv, tr_context, keywords, ctx->flags);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/imap4flags/tst-hasflag.c b/pigeonhole/src/lib-sieve/plugins/imap4flags/tst-hasflag.c
new file mode 100644
index 0000000..23f2acc
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/imap4flags/tst-hasflag.c
@@ -0,0 +1,248 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+
+#include "sieve-commands.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-imap4flags-common.h"
+
+/*
+ * Hasflag test
+ *
+ * Syntax:
+ * hasflag [MATCH-TYPE] [COMPARATOR] [<variable-list: string-list>]
+ * <list-of-flags: string-list>
+ */
+
+static bool tst_hasflag_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool tst_hasflag_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool tst_hasflag_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd);
+
+const struct sieve_command_def tst_hasflag = {
+ .identifier = "hasflag",
+ .type = SCT_TEST,
+ .positional_args = -1, /* We check positional arguments ourselves */
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_hasflag_registered,
+ .validate = tst_hasflag_validate,
+ .generate = tst_hasflag_generate
+};
+
+/*
+ * Hasflag operation
+ */
+
+static bool tst_hasflag_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_hasflag_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def hasflag_operation = {
+ .mnemonic = "HASFLAG",
+ .ext_def = &imap4flags_extension,
+ .code = EXT_IMAP4FLAGS_OPERATION_HASFLAG,
+ .dump = tst_hasflag_operation_dump,
+ .execute = tst_hasflag_operation_execute
+};
+
+/*
+ * Optional arguments
+ */
+
+enum tst_hasflag_optional {
+ OPT_VARIABLES = SIEVE_MATCH_OPT_LAST,
+};
+
+/*
+ * Tag registration
+ */
+
+static bool tst_hasflag_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_command_registration *cmd_reg)
+{
+ /* The order of these is not significant */
+ sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+ sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+ return TRUE;
+}
+
+/*
+ * Validation
+ */
+
+static bool tst_hasflag_validate
+(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *vars = tst->first_positional;
+ struct sieve_ast_argument *keys = sieve_ast_argument_next(vars);
+ const struct sieve_match_type mcht_default =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ const struct sieve_comparator cmp_default =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+
+ if ( !ext_imap4flags_command_validate(valdtr, tst) )
+ return FALSE;
+
+ if ( keys == NULL ) {
+ keys = vars;
+ vars = NULL;
+ } else {
+ vars->argument->id_code = OPT_VARIABLES;
+ }
+
+ /* Validate the key argument to a specified match type */
+ return sieve_match_type_validate
+ (valdtr, tst, keys, &mcht_default, &cmp_default);
+}
+
+/*
+ * Code generation
+ */
+
+static bool tst_hasflag_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &hasflag_operation);
+
+ /* Generate arguments */
+ if ( !sieve_generate_arguments(cgenv, cmd, NULL) )
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool tst_hasflag_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "HASFLAG");
+ sieve_code_descend(denv);
+
+ /* Optional operands */
+
+ for (;;) {
+ bool opok = TRUE;
+ int opt;
+
+ if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code))
+ < 0 )
+ return FALSE;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_VARIABLES:
+ opok = sieve_opr_stringlist_dump(denv, address, "variables");
+ break;
+ default:
+ return FALSE;
+ }
+
+ if ( !opok ) return FALSE;
+ }
+
+ return
+ sieve_opr_stringlist_dump(denv, address, "list of flags");
+}
+
+/*
+ * Interpretation
+ */
+
+static int tst_hasflag_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ const struct sieve_operation *op = renv->oprtn;
+ int opt_code = 0;
+ struct sieve_comparator cmp =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+ struct sieve_match_type mcht =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ struct sieve_stringlist *flag_list, *variables_list, *value_list, *key_list;
+ int match, ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Optional operands */
+
+ variables_list = NULL;
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_match_opr_optional_read
+ (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 )
+ return ret;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_VARIABLES:
+ ret = sieve_opr_stringlist_read
+ (renv, address, "variables-list", &variables_list);
+ break;
+ default:
+ sieve_runtime_trace_error(renv, "invalid optional operand");
+ ret = SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( ret <= 0 ) return ret;
+ }
+
+ /* Fixed operands */
+
+ if ( (ret=sieve_opr_stringlist_read(renv, address, "flag-list", &flag_list))
+ <= 0 )
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "hasflag test");
+
+ value_list = sieve_ext_imap4flags_get_flags
+ (renv, op->ext, variables_list);
+
+ if ( sieve_match_type_is(&mcht, is_match_type) ||
+ sieve_match_type_is(&mcht, contains_match_type) ) {
+ key_list = sieve_ext_imap4flags_get_flags
+ (renv, op->ext, flag_list);
+ } else {
+ key_list = flag_list;
+ }
+
+ /* Perform match */
+ if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 )
+ return ret;
+
+ /* Set test result for subsequent conditional jump */
+ sieve_interpreter_set_test_result(renv->interp, match > 0);
+ return SIEVE_EXEC_OK;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/include/Makefile.am b/pigeonhole/src/lib-sieve/plugins/include/Makefile.am
new file mode 100644
index 0000000..5ce1599
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/Makefile.am
@@ -0,0 +1,24 @@
+noinst_LTLIBRARIES = libsieve_ext_include.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../variables \
+ $(LIBDOVECOT_INCLUDE)
+
+cmds = \
+ cmd-include.c \
+ cmd-return.c \
+ cmd-global.c
+
+libsieve_ext_include_la_SOURCES = \
+ $(cmds) \
+ ext-include-common.c \
+ ext-include-binary.c \
+ ext-include-variables.c \
+ ext-include.c
+
+noinst_HEADERS = \
+ ext-include-common.h \
+ ext-include-limits.h \
+ ext-include-binary.h \
+ ext-include-variables.h
diff --git a/pigeonhole/src/lib-sieve/plugins/include/Makefile.in b/pigeonhole/src/lib-sieve/plugins/include/Makefile.in
new file mode 100644
index 0000000..06f003f
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/Makefile.in
@@ -0,0 +1,718 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/include
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_include_la_LIBADD =
+am__objects_1 = cmd-include.lo cmd-return.lo cmd-global.lo
+am_libsieve_ext_include_la_OBJECTS = $(am__objects_1) \
+ ext-include-common.lo ext-include-binary.lo \
+ ext-include-variables.lo ext-include.lo
+libsieve_ext_include_la_OBJECTS = \
+ $(am_libsieve_ext_include_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-global.Plo \
+ ./$(DEPDIR)/cmd-include.Plo ./$(DEPDIR)/cmd-return.Plo \
+ ./$(DEPDIR)/ext-include-binary.Plo \
+ ./$(DEPDIR)/ext-include-common.Plo \
+ ./$(DEPDIR)/ext-include-variables.Plo \
+ ./$(DEPDIR)/ext-include.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_include_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_include_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_include.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../variables \
+ $(LIBDOVECOT_INCLUDE)
+
+cmds = \
+ cmd-include.c \
+ cmd-return.c \
+ cmd-global.c
+
+libsieve_ext_include_la_SOURCES = \
+ $(cmds) \
+ ext-include-common.c \
+ ext-include-binary.c \
+ ext-include-variables.c \
+ ext-include.c
+
+noinst_HEADERS = \
+ ext-include-common.h \
+ ext-include-limits.h \
+ ext-include-binary.h \
+ ext-include-variables.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/include/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/include/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_include.la: $(libsieve_ext_include_la_OBJECTS) $(libsieve_ext_include_la_DEPENDENCIES) $(EXTRA_libsieve_ext_include_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_include_la_OBJECTS) $(libsieve_ext_include_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-global.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-include.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-return.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-include-binary.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-include-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-include-variables.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-include.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmd-global.Plo
+ -rm -f ./$(DEPDIR)/cmd-include.Plo
+ -rm -f ./$(DEPDIR)/cmd-return.Plo
+ -rm -f ./$(DEPDIR)/ext-include-binary.Plo
+ -rm -f ./$(DEPDIR)/ext-include-common.Plo
+ -rm -f ./$(DEPDIR)/ext-include-variables.Plo
+ -rm -f ./$(DEPDIR)/ext-include.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmd-global.Plo
+ -rm -f ./$(DEPDIR)/cmd-include.Plo
+ -rm -f ./$(DEPDIR)/cmd-return.Plo
+ -rm -f ./$(DEPDIR)/ext-include-binary.Plo
+ -rm -f ./$(DEPDIR)/ext-include-common.Plo
+ -rm -f ./$(DEPDIR)/ext-include-variables.Plo
+ -rm -f ./$(DEPDIR)/ext-include.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/include/cmd-global.c b/pigeonhole/src/lib-sieve/plugins/include/cmd-global.c
new file mode 100644
index 0000000..0ef37eb
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/cmd-global.c
@@ -0,0 +1,329 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+
+#include "sieve-common.h"
+#include "sieve-code.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-include-common.h"
+#include "ext-include-binary.h"
+#include "ext-include-variables.h"
+
+/*
+ * Commands
+ */
+
+static bool cmd_global_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_global_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd);
+
+const struct sieve_command_def cmd_global = {
+ .identifier = "global",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = cmd_global_validate,
+ .generate = cmd_global_generate,
+};
+
+/* DEPRICATED:
+ */
+
+/* Import command
+ *
+ * Syntax
+ * import
+ */
+const struct sieve_command_def cmd_import = {
+ .identifier = "import",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = cmd_global_validate,
+ .generate = cmd_global_generate,
+};
+
+/* Export command
+ *
+ * Syntax
+ * export
+ */
+const struct sieve_command_def cmd_export = {
+ .identifier = "export",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = cmd_global_validate,
+ .generate = cmd_global_generate,
+};
+
+/*
+ * Operations
+ */
+
+static bool opc_global_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int opc_global_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+/* Global operation */
+
+const struct sieve_operation_def global_operation = {
+ .mnemonic = "GLOBAL",
+ .ext_def = &include_extension,
+ .code = EXT_INCLUDE_OPERATION_GLOBAL,
+ .dump = opc_global_dump,
+ .execute = opc_global_execute
+};
+
+/*
+ * Validation
+ */
+
+static inline struct sieve_argument *_create_variable_argument
+(struct sieve_command *cmd, struct sieve_variable *var)
+{
+ struct sieve_argument *argument = sieve_argument_create
+ (cmd->ast_node->ast, NULL, cmd->ext, 0);
+
+ argument->data = (void *) var;
+
+ return argument;
+}
+
+static bool cmd_global_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ const struct sieve_extension *this_ext = cmd->ext;
+ struct sieve_ast_argument *arg = cmd->first_positional;
+ struct sieve_command *prev = sieve_command_prev(cmd);
+
+ /* DEPRECATED: Check valid command placement */
+ if ( !sieve_command_is(cmd, cmd_global) ) {
+ if ( !sieve_command_is_toplevel(cmd) ||
+ ( !sieve_command_is_first(cmd) && prev != NULL &&
+ !sieve_command_is(prev, cmd_require) &&
+ !sieve_command_is(prev, cmd_import) &&
+ !sieve_command_is(prev, cmd_export) ) ) {
+ sieve_command_validate_error(valdtr, cmd,
+ "the DEPRECATED %s command can only be placed at top level "
+ "at the beginning of the file after any require or "
+ "import/export commands",
+ sieve_command_identifier(cmd));
+ return FALSE;
+ }
+ }
+
+ /* Check for use of variables extension */
+ if ( !ext_include_validator_have_variables(this_ext, valdtr) ) {
+ sieve_command_validate_error(valdtr, cmd,
+ "%s command requires that variables extension is active",
+ sieve_command_identifier(cmd));
+ return FALSE;
+ }
+
+ /* Register global variable */
+ if ( sieve_ast_argument_type(arg) == SAAT_STRING ) {
+ /* Single string */
+ const char *identifier = sieve_ast_argument_strc(arg);
+ struct sieve_variable *var;
+
+ if ( (var=ext_include_variable_import_global
+ (valdtr, cmd, identifier)) == NULL )
+ return FALSE;
+
+ arg->argument = _create_variable_argument(cmd, var);
+
+ } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) {
+ /* String list */
+ struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg);
+
+ while ( stritem != NULL ) {
+ const char *identifier = sieve_ast_argument_strc(stritem);
+ struct sieve_variable *var;
+
+ if ( (var=ext_include_variable_import_global
+ (valdtr, cmd, identifier)) == NULL )
+ return FALSE;
+
+ stritem->argument = _create_variable_argument(cmd, var);
+
+ stritem = sieve_ast_strlist_next(stritem);
+ }
+ } else {
+ /* Something else */
+ sieve_argument_validate_error(valdtr, arg,
+ "the %s command accepts a single string or string list argument, "
+ "but %s was found", sieve_command_identifier(cmd),
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+
+ /* Join global commands with predecessors if possible */
+ if ( sieve_commands_equal(prev, cmd) ) {
+ /* Join this command's string list with the previous one */
+ prev->first_positional = sieve_ast_stringlist_join
+ (prev->first_positional, cmd->first_positional);
+
+ if ( prev->first_positional == NULL ) {
+ /* Not going to happen unless MAXINT stringlist items are specified */
+ sieve_command_validate_error(valdtr, cmd,
+ "compiler reached AST limit (script too complex)");
+ return FALSE;
+ }
+
+ /* Detach this command node */
+ sieve_ast_node_detach(cmd->ast_node);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_global_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg = cmd->first_positional;
+
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &global_operation);
+
+ if ( sieve_ast_argument_type(arg) == SAAT_STRING ) {
+ /* Single string */
+ struct sieve_variable *var = (struct sieve_variable *) arg->argument->data;
+
+ (void)sieve_binary_emit_unsigned(cgenv->sblock, 1);
+ (void)sieve_binary_emit_unsigned(cgenv->sblock, var->index);
+
+ } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) {
+ /* String list */
+ struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg);
+
+ (void)sieve_binary_emit_unsigned
+ (cgenv->sblock, sieve_ast_strlist_count(arg));
+
+ while ( stritem != NULL ) {
+ struct sieve_variable *var =
+ (struct sieve_variable *) stritem->argument->data;
+
+ (void)sieve_binary_emit_unsigned(cgenv->sblock, var->index);
+
+ stritem = sieve_ast_strlist_next(stritem);
+ }
+ } else {
+ i_unreached();
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool opc_global_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = denv->oprtn->ext;
+ unsigned int count, i, var_count;
+ struct sieve_variable_scope_binary *global_vars;
+ struct sieve_variable_scope *global_scope;
+ struct sieve_variable * const *vars;
+
+ if ( !sieve_binary_read_unsigned(denv->sblock, address, &count) )
+ return FALSE;
+
+ sieve_code_dumpf(denv, "GLOBAL (count: %u):", count);
+
+ global_vars = ext_include_binary_get_global_scope(this_ext, denv->sbin);
+ global_scope = sieve_variable_scope_binary_get(global_vars);
+ vars = sieve_variable_scope_get_variables(global_scope, &var_count);
+
+ sieve_code_descend(denv);
+
+ for ( i = 0; i < count; i++ ) {
+ unsigned int index;
+
+ sieve_code_mark(denv);
+ if ( !sieve_binary_read_unsigned(denv->sblock, address, &index) ||
+ index >= var_count )
+ return FALSE;
+
+ sieve_code_dumpf(denv, "%d: VAR[%d]: '%s'", i, index, vars[index]->identifier);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Execution
+ */
+
+static int opc_global_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct sieve_variable_scope_binary *global_vars;
+ struct sieve_variable_scope *global_scope;
+ struct sieve_variable_storage *storage;
+ struct sieve_variable * const *vars;
+ unsigned int var_count, count, i;
+
+ if ( !sieve_binary_read_unsigned(renv->sblock, address, &count) ) {
+ sieve_runtime_trace_error(renv,
+ "global: count operand invalid");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ global_vars = ext_include_binary_get_global_scope(this_ext, renv->sbin);
+ global_scope = sieve_variable_scope_binary_get(global_vars);
+ vars = sieve_variable_scope_get_variables(global_scope, &var_count);
+ storage = ext_include_interpreter_get_global_variables
+ (this_ext, renv->interp);
+
+ for ( i = 0; i < count; i++ ) {
+ unsigned int index;
+
+ if ( !sieve_binary_read_unsigned(renv->sblock, address, &index) ) {
+ sieve_runtime_trace_error(renv,
+ "global: variable index operand invalid");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( index >= var_count ) {
+ sieve_runtime_trace_error(renv,
+ "global: variable index %u is invalid in global storage (> %u)",
+ index, var_count);
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+ "global: exporting variable '%s' [gvid: %u, vid: %u]",
+ vars[index]->identifier, i, index);
+
+ /* Make sure variable is initialized (export) */
+ (void)sieve_variable_get_modifiable(storage, index, NULL);
+ }
+
+ return SIEVE_EXEC_OK;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/include/cmd-include.c b/pigeonhole/src/lib-sieve/plugins/include/cmd-include.c
new file mode 100644
index 0000000..92aefb4
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/cmd-include.c
@@ -0,0 +1,406 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+
+#include "sieve-common.h"
+#include "sieve-script.h"
+#include "sieve-storage.h"
+#include "sieve-ast.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-binary.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-include-common.h"
+#include "ext-include-binary.h"
+
+/*
+ * Include command
+ *
+ * Syntax:
+ * include [LOCATION] [":once"] [":optional"] <value: string>
+ *
+ * [LOCATION]:
+ * ":personal" / ":global"
+ */
+
+static bool cmd_include_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool cmd_include_pre_validate
+ (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd);
+static bool cmd_include_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_include_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def cmd_include = {
+ .identifier = "include",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_include_registered,
+ .pre_validate = cmd_include_pre_validate,
+ .validate = cmd_include_validate,
+ .generate = cmd_include_generate
+};
+
+/*
+ * Include operation
+ */
+
+static bool opc_include_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int opc_include_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def include_operation = {
+ .mnemonic = "include",
+ .ext_def = &include_extension,
+ .code = EXT_INCLUDE_OPERATION_INCLUDE,
+ .dump = opc_include_dump,
+ .execute = opc_include_execute
+};
+
+/*
+ * Context structures
+ */
+
+struct cmd_include_context_data {
+ enum ext_include_script_location location;
+
+ struct sieve_script *script;
+ enum ext_include_flags flags;
+
+ bool location_assigned:1;
+};
+
+/*
+ * Tagged arguments
+ */
+
+static bool cmd_include_validate_location_tag
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+static const struct sieve_argument_def include_personal_tag = {
+ .identifier = "personal",
+ .validate = cmd_include_validate_location_tag
+};
+
+static const struct sieve_argument_def include_global_tag = {
+ .identifier = "global",
+ .validate = cmd_include_validate_location_tag
+};
+
+static bool cmd_include_validate_boolean_tag
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+static const struct sieve_argument_def include_once_tag = {
+ .identifier = "once",
+ .validate = cmd_include_validate_boolean_tag
+};
+
+static const struct sieve_argument_def include_optional_tag = {
+ .identifier = "optional",
+ .validate = cmd_include_validate_boolean_tag
+};
+
+/*
+ * Tag validation
+ */
+
+static bool cmd_include_validate_location_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct cmd_include_context_data *ctx_data =
+ (struct cmd_include_context_data *) cmd->data;
+
+ if ( ctx_data->location_assigned) {
+ sieve_argument_validate_error(valdtr, *arg,
+ "include: cannot use location tags ':personal' and ':global' "
+ "multiple times");
+ return FALSE;
+ }
+
+ if ( sieve_argument_is(*arg, include_personal_tag) )
+ ctx_data->location = EXT_INCLUDE_LOCATION_PERSONAL;
+ else if ( sieve_argument_is(*arg, include_global_tag) )
+ ctx_data->location = EXT_INCLUDE_LOCATION_GLOBAL;
+ else
+ return FALSE;
+
+ ctx_data->location_assigned = TRUE;
+
+ /* Delete this tag (for now) */
+ *arg = sieve_ast_arguments_detach(*arg, 1);
+
+ return TRUE;
+}
+
+static bool cmd_include_validate_boolean_tag
+(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct cmd_include_context_data *ctx_data =
+ (struct cmd_include_context_data *) cmd->data;
+
+ if ( sieve_argument_is(*arg, include_once_tag) )
+ ctx_data->flags |= EXT_INCLUDE_FLAG_ONCE;
+ else
+ ctx_data->flags |= EXT_INCLUDE_FLAG_OPTIONAL;
+
+ /* Delete this tag (for now) */
+ *arg = sieve_ast_arguments_detach(*arg, 1);
+
+ return TRUE;
+}
+
+/*
+ * Command registration
+ */
+
+static bool cmd_include_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_personal_tag, 0);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_global_tag, 0);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_once_tag, 0);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_optional_tag, 0);
+
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool cmd_include_pre_validate
+(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd)
+{
+ struct cmd_include_context_data *ctx_data;
+
+ /* Assign context */
+ ctx_data = p_new(sieve_command_pool(cmd), struct cmd_include_context_data, 1);
+ ctx_data->location = EXT_INCLUDE_LOCATION_PERSONAL;
+ cmd->data = ctx_data;
+
+ return TRUE;
+}
+
+static bool cmd_include_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ const struct sieve_extension *this_ext = cmd->ext;
+ struct sieve_ast_argument *arg = cmd->first_positional;
+ struct cmd_include_context_data *ctx_data =
+ (struct cmd_include_context_data *) cmd->data;
+ struct sieve_storage *storage;
+ struct sieve_script *script;
+ const char *script_name;
+ enum sieve_error error = SIEVE_ERROR_NONE;
+ int ret;
+
+ /* Check argument */
+ if ( !sieve_validate_positional_argument
+ (valdtr, cmd, arg, "value", 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) )
+ return FALSE;
+
+ /*
+ * Variables are not allowed.
+ */
+ if ( !sieve_argument_is_string_literal(arg) ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "the include command requires a constant string for its value argument");
+ return FALSE;
+ }
+
+ /* Find the script */
+
+ script_name = sieve_ast_argument_strc(arg);
+
+ if ( !sieve_script_name_is_valid(script_name) ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "include: invalid script name '%s'",
+ str_sanitize(script_name, 80));
+ return FALSE;
+ }
+
+ storage = ext_include_get_script_storage
+ (this_ext, ctx_data->location, script_name, &error);
+ if ( storage == NULL ) {
+ // FIXME: handle ':optional' in this case
+ if (error == SIEVE_ERROR_NOT_FOUND) {
+ sieve_argument_validate_error(valdtr, arg,
+ "include: %s location for included script `%s' is unavailable "
+ "(contact system administrator for more information)",
+ ext_include_script_location_name(ctx_data->location),
+ str_sanitize(script_name, 80));
+ } else {
+ sieve_argument_validate_error(valdtr, arg,
+ "include: failed to access %s location for included script `%s' "
+ "(contact system administrator for more information)",
+ ext_include_script_location_name(ctx_data->location),
+ str_sanitize(script_name, 80));
+ }
+ return FALSE;
+ }
+
+ /* Create script object */
+ script = sieve_storage_get_script
+ (storage, script_name, &error);
+ if ( script == NULL )
+ return FALSE;
+
+ ret = sieve_script_open(script, &error);
+ if ( ret < 0 ) {
+ if ( error != SIEVE_ERROR_NOT_FOUND ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "failed to access included %s script '%s': %s",
+ ext_include_script_location_name(ctx_data->location),
+ str_sanitize(script_name, 80),
+ sieve_script_get_last_error_lcase(script));
+ sieve_script_unref(&script);
+ return FALSE;
+
+ /* Not found */
+ } else {
+ enum sieve_compile_flags cpflags =
+ sieve_validator_compile_flags(valdtr);
+
+ if ( (ctx_data->flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ) {
+ /* :optional */
+
+ } else if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) {
+ /* Script is being uploaded */
+ sieve_argument_validate_warning(valdtr, arg,
+ "included %s script '%s' does not exist (ignored during upload)",
+ ext_include_script_location_name(ctx_data->location),
+ str_sanitize(script_name, 80));
+ ctx_data->flags |= EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD;
+
+ } else {
+ /* Should have existed */
+ sieve_argument_validate_error(valdtr, arg,
+ "included %s script '%s' does not exist",
+ ext_include_script_location_name(ctx_data->location),
+ str_sanitize(script_name, 80));
+ sieve_script_unref(&script);
+ return FALSE;
+ }
+ }
+ }
+
+ ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script);
+ ctx_data->script = script;
+
+ (void)sieve_ast_arguments_detach(arg, 1);
+ return TRUE;
+}
+
+/*
+ * Code Generation
+ */
+
+static bool cmd_include_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ struct cmd_include_context_data *ctx_data =
+ (struct cmd_include_context_data *) cmd->data;
+ const struct ext_include_script_info *included;
+ int ret;
+
+ /* Compile (if necessary) and include the script into the binary.
+ * This yields the id of the binary block containing the compiled byte code.
+ */
+ if ( (ret=ext_include_generate_include
+ (cgenv, cmd, ctx_data->location, ctx_data->flags, ctx_data->script,
+ &included)) < 0 )
+ return FALSE;
+
+ if ( ret > 0 ) {
+ (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation);
+ (void)sieve_binary_emit_unsigned(cgenv->sblock, included->id);
+ (void)sieve_binary_emit_byte(cgenv->sblock, ctx_data->flags);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool opc_include_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ const struct ext_include_script_info *included;
+ struct ext_include_binary_context *binctx;
+ unsigned int include_id, flags;
+
+ sieve_code_dumpf(denv, "INCLUDE:");
+
+ sieve_code_mark(denv);
+ if ( !sieve_binary_read_unsigned(denv->sblock, address, &include_id) )
+ return FALSE;
+
+ if ( !sieve_binary_read_byte(denv->sblock, address, &flags) )
+ return FALSE;
+
+ binctx = ext_include_binary_get_context(denv->oprtn->ext, denv->sbin);
+ included = ext_include_binary_script_get_included(binctx, include_id);
+ if ( included == NULL )
+ return FALSE;
+
+ sieve_code_descend(denv);
+ sieve_code_dumpf(denv, "script: `%s' from %s %s%s[ID: %d, BLOCK: %d]",
+ sieve_script_name(included->script), sieve_script_location(included->script),
+ ((flags & EXT_INCLUDE_FLAG_ONCE) != 0 ? "(once) " : ""),
+ ((flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ? "(optional) " : ""),
+ include_id, sieve_binary_block_get_id(included->block));
+
+ return TRUE;
+}
+
+/*
+ * Execution
+ */
+
+static int opc_include_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ unsigned int include_id, flags;
+
+ if ( !sieve_binary_read_unsigned(renv->sblock, address, &include_id) ) {
+ sieve_runtime_trace_error(renv, "invalid include-id operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( !sieve_binary_read_unsigned(renv->sblock, address, &flags) ) {
+ sieve_runtime_trace_error(renv, "invalid flags operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ return ext_include_execute_include
+ (renv, include_id, (enum ext_include_flags)flags);
+}
+
+
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/include/cmd-return.c b/pigeonhole/src/lib-sieve/plugins/include/cmd-return.c
new file mode 100644
index 0000000..65a156d
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/cmd-return.c
@@ -0,0 +1,71 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+
+#include "sieve-code.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include "ext-include-common.h"
+
+/*
+ * Return command
+ *
+ * Syntax
+ * return
+ */
+
+static bool cmd_return_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd);
+
+const struct sieve_command_def cmd_return = {
+ .identifier = "return",
+ .type = SCT_COMMAND,
+ .positional_args = 0,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .generate = cmd_return_generate
+};
+
+/*
+ * Return operation
+ */
+
+static int opc_return_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def return_operation = {
+ .mnemonic = "RETURN",
+ .ext_def = &include_extension,
+ .code = EXT_INCLUDE_OPERATION_RETURN,
+ .execute = opc_return_execute
+};
+
+/*
+ * Code generation
+ */
+
+static bool cmd_return_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &return_operation);
+
+ return TRUE;
+}
+
+/*
+ * Execution
+ */
+
+static int opc_return_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED)
+{
+ ext_include_execute_return(renv);
+ return SIEVE_EXEC_OK;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c b/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c
new file mode 100644
index 0000000..3711c30
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c
@@ -0,0 +1,492 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+#include "sieve-script.h"
+#include "sieve-storage.h"
+#include "sieve-binary.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-include-common.h"
+#include "ext-include-limits.h"
+#include "ext-include-variables.h"
+#include "ext-include-binary.h"
+
+/*
+ * Forward declarations
+ */
+
+static bool ext_include_binary_pre_save
+ (const struct sieve_extension *ext, struct sieve_binary *sbin,
+ void *context, enum sieve_error *error_r);
+static bool ext_include_binary_open
+ (const struct sieve_extension *ext, struct sieve_binary *sbin,
+ void *context);
+static bool ext_include_binary_up_to_date
+ (const struct sieve_extension *ext, struct sieve_binary *sbin,
+ void *context, enum sieve_compile_flags cpflags);
+static void ext_include_binary_free
+ (const struct sieve_extension *ext, struct sieve_binary *sbin,
+ void *context);
+
+/*
+ * Binary include extension
+ */
+
+const struct sieve_binary_extension include_binary_ext = {
+ .extension = &include_extension,
+ .binary_pre_save = ext_include_binary_pre_save,
+ .binary_open = ext_include_binary_open,
+ .binary_free = ext_include_binary_free,
+ .binary_up_to_date = ext_include_binary_up_to_date
+};
+
+/*
+ * Binary context management
+ */
+
+struct ext_include_binary_context {
+ struct sieve_binary *binary;
+ struct sieve_binary_block *dependency_block;
+
+ HASH_TABLE(struct sieve_script *,
+ struct ext_include_script_info *) included_scripts;
+ ARRAY(struct ext_include_script_info *) include_index;
+
+ struct sieve_variable_scope_binary *global_vars;
+
+ bool outdated:1;
+};
+
+static struct ext_include_binary_context *ext_include_binary_create_context
+(const struct sieve_extension *this_ext, struct sieve_binary *sbin)
+{
+ pool_t pool = sieve_binary_pool(sbin);
+
+ struct ext_include_binary_context *ctx =
+ p_new(pool, struct ext_include_binary_context, 1);
+
+ ctx->binary = sbin;
+ hash_table_create(&ctx->included_scripts, pool, 0,
+ sieve_script_hash, sieve_script_cmp);
+ p_array_init(&ctx->include_index, pool, 128);
+
+ sieve_binary_extension_set(sbin, this_ext, &include_binary_ext, ctx);
+
+ return ctx;
+}
+
+struct ext_include_binary_context *ext_include_binary_get_context
+(const struct sieve_extension *this_ext, struct sieve_binary *sbin)
+{
+ struct ext_include_binary_context *ctx = (struct ext_include_binary_context *)
+ sieve_binary_extension_get_context(sbin, this_ext);
+
+ if ( ctx == NULL )
+ ctx = ext_include_binary_create_context(this_ext, sbin);
+
+ return ctx;
+}
+
+struct ext_include_binary_context *ext_include_binary_init
+(const struct sieve_extension *this_ext, struct sieve_binary *sbin,
+ struct sieve_ast *ast)
+{
+ struct ext_include_ast_context *ast_ctx =
+ ext_include_get_ast_context(this_ext, ast);
+ struct ext_include_binary_context *ctx;
+
+ /* Get/create our context from the binary we are working on */
+ ctx = ext_include_binary_get_context(this_ext, sbin);
+
+ /* Create dependency block */
+ if ( ctx->dependency_block == 0 )
+ ctx->dependency_block =
+ sieve_binary_extension_create_block(sbin, this_ext);
+
+ if ( ctx->global_vars == NULL ) {
+ ctx->global_vars =
+ sieve_variable_scope_binary_create(ast_ctx->global_vars);
+ sieve_variable_scope_binary_ref(ctx->global_vars);
+ }
+
+ return ctx;
+}
+
+/*
+ * Script inclusion
+ */
+
+struct ext_include_script_info *ext_include_binary_script_include
+(struct ext_include_binary_context *binctx,
+ enum ext_include_script_location location, enum ext_include_flags flags,
+ struct sieve_script *script, struct sieve_binary_block *inc_block)
+{
+ pool_t pool = sieve_binary_pool(binctx->binary);
+ struct ext_include_script_info *incscript;
+
+ incscript = p_new(pool, struct ext_include_script_info, 1);
+ incscript->id = array_count(&binctx->include_index)+1;
+ incscript->location = location;
+ incscript->flags = flags;
+ incscript->script = script;
+ incscript->block = inc_block;
+
+ /* Unreferenced on binary_free */
+ sieve_script_ref(script);
+
+ hash_table_insert(binctx->included_scripts, script, incscript);
+ array_append(&binctx->include_index, &incscript, 1);
+
+ return incscript;
+}
+
+struct ext_include_script_info *ext_include_binary_script_get_include_info
+(struct ext_include_binary_context *binctx, struct sieve_script *script)
+{
+ struct ext_include_script_info *incscript =
+ hash_table_lookup(binctx->included_scripts, script);
+
+ return incscript;
+}
+
+const struct ext_include_script_info *ext_include_binary_script_get_included
+(struct ext_include_binary_context *binctx, unsigned int include_id)
+{
+ if ( include_id > 0 &&
+ (include_id - 1) < array_count(&binctx->include_index) ) {
+ struct ext_include_script_info *const *sinfo =
+ array_idx(&binctx->include_index, include_id - 1);
+
+ return *sinfo;
+ }
+
+ return NULL;
+}
+
+const struct ext_include_script_info *ext_include_binary_script_get
+(struct ext_include_binary_context *binctx, struct sieve_script *script)
+{
+ return hash_table_lookup(binctx->included_scripts, script);
+}
+
+unsigned int ext_include_binary_script_get_count
+(struct ext_include_binary_context *binctx)
+{
+ return array_count(&binctx->include_index);
+}
+
+/*
+ * Variables
+ */
+
+struct sieve_variable_scope_binary *ext_include_binary_get_global_scope
+(const struct sieve_extension *this_ext, struct sieve_binary *sbin)
+{
+ struct ext_include_binary_context *binctx =
+ ext_include_binary_get_context(this_ext, sbin);
+
+ return binctx->global_vars;
+}
+
+/*
+ * Binary extension
+ */
+
+static bool ext_include_binary_pre_save
+(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_binary *sbin ATTR_UNUSED, void *context,
+ enum sieve_error *error_r)
+{
+ struct ext_include_binary_context *binctx =
+ (struct ext_include_binary_context *) context;
+ struct ext_include_script_info *const *scripts;
+ struct sieve_binary_block *sblock = binctx->dependency_block;
+ unsigned int script_count, i;
+ bool result = TRUE;
+
+ sieve_binary_block_clear(sblock);
+
+ scripts = array_get(&binctx->include_index, &script_count);
+
+ sieve_binary_emit_unsigned(sblock, script_count);
+
+ for ( i = 0; i < script_count; i++ ) {
+ struct ext_include_script_info *incscript = scripts[i];
+
+ if ( incscript->block != NULL ) {
+ sieve_binary_emit_unsigned
+ (sblock, sieve_binary_block_get_id(incscript->block));
+ } else {
+ sieve_binary_emit_unsigned(sblock, 0);
+ }
+ sieve_binary_emit_byte(sblock, incscript->location);
+ sieve_binary_emit_cstring(sblock, sieve_script_name(incscript->script));
+ sieve_binary_emit_byte(sblock, incscript->flags);
+ sieve_script_binary_write_metadata(incscript->script, sblock);
+ }
+
+ result = ext_include_variables_save(sblock, binctx->global_vars, error_r);
+
+ return result;
+}
+
+static bool ext_include_binary_open
+(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context)
+{
+ struct sieve_instance *svinst = ext->svinst;
+ struct ext_include_context *ext_ctx =
+ (struct ext_include_context *)ext->context;
+ struct ext_include_binary_context *binctx =
+ (struct ext_include_binary_context *) context;
+ struct sieve_binary_block *sblock;
+ unsigned int depcount, i, block_id;
+ sieve_size_t offset;
+
+ sblock = sieve_binary_extension_get_block(sbin, ext);
+ block_id = sieve_binary_block_get_id(sblock);
+
+ offset = 0;
+
+ if ( !sieve_binary_read_unsigned(sblock, &offset, &depcount) ) {
+ e_error(svinst->event,
+ "include: failed to read include count "
+ "for dependency block %d of binary %s", block_id,
+ sieve_binary_path(sbin));
+ return FALSE;
+ }
+
+ /* Check include limit */
+ if ( depcount > ext_ctx->max_includes ) {
+ e_error(svinst->event,
+ "include: binary %s includes too many scripts (%u > %u)",
+ sieve_binary_path(sbin), depcount, ext_ctx->max_includes);
+ return FALSE;
+ }
+
+ /* Read dependencies */
+ for ( i = 0; i < depcount; i++ ) {
+ unsigned int inc_block_id;
+ struct sieve_binary_block *inc_block = NULL;
+ unsigned int location, flags;
+ string_t *script_name;
+ struct sieve_storage *storage;
+ struct sieve_script *script;
+ enum sieve_error error;
+ int ret;
+
+ if (
+ !sieve_binary_read_unsigned(sblock, &offset, &inc_block_id) ||
+ !sieve_binary_read_byte(sblock, &offset, &location) ||
+ !sieve_binary_read_string(sblock, &offset, &script_name) ||
+ !sieve_binary_read_byte(sblock, &offset, &flags) ) {
+ /* Binary is corrupt, recompile */
+ e_error(svinst->event,
+ "include: failed to read included script "
+ "from dependency block %d of binary %s",
+ block_id, sieve_binary_path(sbin));
+ return FALSE;
+ }
+
+ if ( inc_block_id != 0 &&
+ (inc_block=sieve_binary_block_get(sbin, inc_block_id)) == NULL ) {
+ e_error(svinst->event,
+ "include: failed to find block %d for included script "
+ "from dependency block %d of binary %s",
+ inc_block_id, block_id,
+ sieve_binary_path(sbin));
+ return FALSE;
+ }
+
+ if ( location >= EXT_INCLUDE_LOCATION_INVALID ) {
+ /* Binary is corrupt, recompile */
+ e_error(svinst->event,
+ "include: dependency block %d of binary %s "
+ "uses invalid script location (id %d)",
+ block_id, sieve_binary_path(sbin), location);
+ return FALSE;
+ }
+
+ /* Can we find the script dependency ? */
+ storage = ext_include_get_script_storage
+ (ext, location, str_c(script_name), &error);
+ if ( storage == NULL ) {
+ /* No, recompile */
+ // FIXME: handle ':optional' in this case
+ return FALSE;
+ }
+
+ /* Can we open the script dependency ? */
+ script = sieve_storage_get_script
+ (storage, str_c(script_name), &error);
+ if ( script == NULL ) {
+ /* No, recompile */
+ return FALSE;
+ }
+ if ( sieve_script_open(script, &error) < 0 ) {
+ if ( error != SIEVE_ERROR_NOT_FOUND ) {
+ /* No, recompile */
+ return FALSE;
+ }
+
+ if ( (flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0 ) {
+ /* Not supposed to be missing, recompile */
+ if ( svinst->debug ) {
+ e_debug(svinst->event, "include: "
+ "script '%s' included in binary %s is missing, "
+ "so recompile",
+ str_c(script_name),
+ sieve_binary_path(sbin));
+ }
+ return FALSE;
+ }
+
+ } else if (inc_block == NULL) {
+ /* Script exists, but it is missing from the binary, recompile no matter
+ * what.
+ */
+ if ( svinst->debug ) {
+ e_debug(svinst->event, "include: "
+ "script '%s' is missing in binary %s, but is now available, "
+ "so recompile", str_c(script_name), sieve_binary_path(sbin));
+ }
+ sieve_script_unref(&script);
+ return FALSE;
+ }
+
+ /* Can we read script metadata ? */
+ if ( (ret=sieve_script_binary_read_metadata
+ (script, sblock, &offset)) < 0 ) {
+ /* Binary is corrupt, recompile */
+ e_error(svinst->event, "include: "
+ "dependency block %d of binary %s "
+ "contains invalid script metadata for script %s",
+ block_id, sieve_binary_path(sbin),
+ sieve_script_location(script));
+ sieve_script_unref(&script);
+ return FALSE;
+ }
+
+ if ( ret == 0 )
+ binctx->outdated = TRUE;
+
+ (void)ext_include_binary_script_include
+ (binctx, location, flags, script, inc_block);
+
+ sieve_script_unref(&script);
+ }
+
+ if ( !ext_include_variables_load
+ (ext, sblock, &offset, &binctx->global_vars) )
+ return FALSE;
+
+ return TRUE;
+}
+
+static bool ext_include_binary_up_to_date
+(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_binary *sbin ATTR_UNUSED, void *context,
+ enum sieve_compile_flags cpflags ATTR_UNUSED)
+{
+ struct ext_include_binary_context *binctx =
+ (struct ext_include_binary_context *) context;
+
+ return !binctx->outdated;
+}
+
+static void ext_include_binary_free
+(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_binary *sbin ATTR_UNUSED, void *context)
+{
+ struct ext_include_binary_context *binctx =
+ (struct ext_include_binary_context *) context;
+ struct hash_iterate_context *hctx;
+ struct sieve_script *script;
+ struct ext_include_script_info *incscript;
+
+ /* Release references to all included script objects */
+ hctx = hash_table_iterate_init(binctx->included_scripts);
+ while ( hash_table_iterate
+ (hctx, binctx->included_scripts, &script, &incscript) )
+ sieve_script_unref(&incscript->script);
+ hash_table_iterate_deinit(&hctx);
+
+ hash_table_destroy(&binctx->included_scripts);
+
+ if ( binctx->global_vars != NULL )
+ sieve_variable_scope_binary_unref(&binctx->global_vars);
+}
+
+/*
+ * Dumping the binary
+ */
+
+bool ext_include_binary_dump
+(const struct sieve_extension *ext, struct sieve_dumptime_env *denv)
+{
+ struct sieve_binary *sbin = denv->sbin;
+ struct ext_include_binary_context *binctx =
+ ext_include_binary_get_context(ext, sbin);
+ struct hash_iterate_context *hctx;
+ struct sieve_script *script;
+ struct ext_include_script_info *incscript;
+
+ if ( !ext_include_variables_dump(denv, binctx->global_vars) )
+ return FALSE;
+
+ hctx = hash_table_iterate_init(binctx->included_scripts);
+ while ( hash_table_iterate
+ (hctx, binctx->included_scripts, &script, &incscript) ) {
+
+ if ( incscript->block == NULL ) {
+ sieve_binary_dump_sectionf(denv, "Included %s script '%s' (MISSING)",
+ ext_include_script_location_name(incscript->location),
+ sieve_script_name(incscript->script));
+
+ } else {
+ unsigned int block_id = sieve_binary_block_get_id(incscript->block);
+
+ sieve_binary_dump_sectionf(denv, "Included %s script '%s' (block: %d)",
+ ext_include_script_location_name(incscript->location),
+ sieve_script_name(incscript->script), block_id);
+
+ denv->sblock = incscript->block;
+ denv->cdumper = sieve_code_dumper_create(denv);
+
+ if ( denv->cdumper == NULL )
+ return FALSE;
+
+ sieve_code_dumper_run(denv->cdumper);
+ sieve_code_dumper_free(&(denv->cdumper));
+ }
+ }
+ hash_table_iterate_deinit(&hctx);
+
+ return TRUE;
+}
+
+bool ext_include_code_dump
+(const struct sieve_extension *ext, const struct sieve_dumptime_env *denv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ struct sieve_binary *sbin = denv->sbin;
+ struct ext_include_binary_context *binctx =
+ ext_include_binary_get_context(ext, sbin);
+ struct ext_include_context *ectx = ext_include_get_context(ext);
+
+ sieve_ext_variables_dump_set_scope
+ (ectx->var_ext, denv, ext,
+ sieve_variable_scope_binary_get(binctx->global_vars));
+
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.h b/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.h
new file mode 100644
index 0000000..066df2f
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.h
@@ -0,0 +1,64 @@
+#ifndef EXT_INCLUDE_BINARY_H
+#define EXT_INCLUDE_BINARY_H
+
+#include "sieve-common.h"
+
+/*
+ * Binary context management
+ */
+
+struct ext_include_binary_context;
+
+struct ext_include_binary_context *ext_include_binary_init
+ (const struct sieve_extension *this_ext, struct sieve_binary *sbin,
+ struct sieve_ast *ast);
+struct ext_include_binary_context *ext_include_binary_get_context
+ (const struct sieve_extension *this_ext, struct sieve_binary *sbin);
+
+/*
+ * Variables
+ */
+
+struct sieve_variable_scope_binary *ext_include_binary_get_global_scope
+ (const struct sieve_extension *this_ext, struct sieve_binary *sbin);
+
+/*
+ * Including scripts
+ */
+
+struct ext_include_script_info {
+ unsigned int id;
+
+ struct sieve_script *script;
+ enum ext_include_flags flags;
+ enum ext_include_script_location location;
+
+ struct sieve_binary_block *block;
+};
+
+struct ext_include_script_info *ext_include_binary_script_include
+ (struct ext_include_binary_context *binctx,
+ enum ext_include_script_location location, enum ext_include_flags flags,
+ struct sieve_script *script, struct sieve_binary_block *inc_block);
+struct ext_include_script_info *ext_include_binary_script_get_include_info
+ (struct ext_include_binary_context *binctx, struct sieve_script *script);
+
+const struct ext_include_script_info *ext_include_binary_script_get_included
+ (struct ext_include_binary_context *binctx, unsigned int include_id);
+const struct ext_include_script_info *ext_include_binary_script_get
+ (struct ext_include_binary_context *binctx, struct sieve_script *script);
+unsigned int ext_include_binary_script_get_count
+ (struct ext_include_binary_context *binctx);
+
+/*
+ * Dumping the binary
+ */
+
+bool ext_include_binary_dump
+ (const struct sieve_extension *ext, struct sieve_dumptime_env *denv);
+bool ext_include_code_dump
+ (const struct sieve_extension *ext, const struct sieve_dumptime_env *denv,
+ sieve_size_t *address ATTR_UNUSED);
+
+#endif
+
diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.c b/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.c
new file mode 100644
index 0000000..7af3f00
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.c
@@ -0,0 +1,892 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "array.h"
+#include "str-sanitize.h"
+#include "home-expand.h"
+
+#include "sieve-common.h"
+#include "sieve-settings.h"
+#include "sieve-error.h"
+#include "sieve-script.h"
+#include "sieve-storage.h"
+#include "sieve-ast.h"
+#include "sieve-binary.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include "ext-include-common.h"
+#include "ext-include-limits.h"
+#include "ext-include-binary.h"
+#include "ext-include-variables.h"
+
+
+/*
+ * Forward declarations
+ */
+
+/* Generator context */
+
+struct ext_include_generator_context {
+ unsigned int nesting_depth;
+ struct sieve_script *script;
+ struct ext_include_generator_context *parent;
+};
+
+static inline struct ext_include_generator_context *
+ext_include_get_generator_context(const struct sieve_extension *ext_this,
+ struct sieve_generator *gentr);
+
+/* Interpreter context */
+
+struct ext_include_interpreter_global {
+ ARRAY(struct sieve_script *) included_scripts;
+
+ struct sieve_variable_scope_binary *var_scope;
+ struct sieve_variable_storage *var_storage;
+};
+
+struct ext_include_interpreter_context {
+ struct ext_include_interpreter_context *parent;
+ struct ext_include_interpreter_global *global;
+
+ struct sieve_interpreter *interp;
+ pool_t pool;
+
+ unsigned int nesting_depth;
+
+ struct sieve_script *script;
+ const struct ext_include_script_info *script_info;
+
+ const struct ext_include_script_info *include;
+ bool returned;
+};
+
+/*
+ * Extension configuration
+ */
+
+/* Extension hooks */
+
+bool ext_include_load(const struct sieve_extension *ext, void **context)
+{
+ struct sieve_instance *svinst = ext->svinst;
+ struct ext_include_context *ctx;
+ const char *location;
+ unsigned long long int uint_setting;
+
+ if (*context != NULL)
+ ext_include_unload(ext);
+
+ ctx = i_new(struct ext_include_context, 1);
+
+ /* Get location for :global scripts */
+ location = sieve_setting_get(svinst, "sieve_global");
+ if (location == NULL)
+ location = sieve_setting_get(svinst, "sieve_global_dir");
+
+ if (location == NULL) {
+ e_debug(svinst->event, "include: "
+ "sieve_global is not set; "
+ "it is currently not possible to include `:global' scripts.");
+ }
+
+ ctx->global_location = i_strdup(location);
+
+ /* Get limits */
+ ctx->max_nesting_depth = EXT_INCLUDE_DEFAULT_MAX_NESTING_DEPTH;
+ ctx->max_includes = EXT_INCLUDE_DEFAULT_MAX_INCLUDES;
+
+ if (sieve_setting_get_uint_value(
+ svinst, "sieve_include_max_nesting_depth", &uint_setting))
+ ctx->max_nesting_depth = (unsigned int)uint_setting;
+ if (sieve_setting_get_uint_value(
+ svinst, "sieve_include_max_includes", &uint_setting))
+ ctx->max_includes = (unsigned int)uint_setting;
+
+ /* Extension dependencies */
+ ctx->var_ext = sieve_ext_variables_get_extension(ext->svinst);
+
+ *context = (void *)ctx;
+ return TRUE;
+}
+
+void ext_include_unload(const struct sieve_extension *ext)
+{
+ struct ext_include_context *ctx =
+ (struct ext_include_context *)ext->context;
+
+ if (ctx->global_storage != NULL)
+ sieve_storage_unref(&ctx->global_storage);
+ if (ctx->personal_storage != NULL)
+ sieve_storage_unref(&ctx->personal_storage);
+
+ i_free(ctx->global_location);
+ i_free(ctx);
+}
+
+/*
+ * Script access
+ */
+
+struct sieve_storage *
+ext_include_get_script_storage(const struct sieve_extension *ext,
+ enum ext_include_script_location location,
+ const char *script_name,
+ enum sieve_error *error_r)
+{
+ struct sieve_instance *svinst = ext->svinst;
+ struct ext_include_context *ctx =
+ (struct ext_include_context *)ext->context;
+
+ switch (location) {
+ case EXT_INCLUDE_LOCATION_PERSONAL:
+ if (ctx->personal_storage == NULL) {
+ ctx->personal_storage =
+ sieve_storage_create_main(svinst, NULL, 0,
+ error_r);
+ }
+ return ctx->personal_storage;
+ case EXT_INCLUDE_LOCATION_GLOBAL:
+ if (ctx->global_location == NULL) {
+ e_info(svinst->event, "include: "
+ "sieve_global is unconfigured; "
+ "include of `:global' script `%s' is therefore not possible",
+ str_sanitize(script_name, 80));
+ if (error_r != NULL)
+ *error_r = SIEVE_ERROR_NOT_FOUND;
+ return NULL;
+ }
+ if (ctx->global_storage == NULL) {
+ ctx->global_storage = sieve_storage_create(
+ svinst, ctx->global_location, 0, error_r);
+ }
+ return ctx->global_storage;
+ default:
+ break;
+ }
+
+ i_unreached();
+ return NULL;
+}
+
+/*
+ * AST context management
+ */
+
+static void
+ext_include_ast_free(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_ast *ast ATTR_UNUSED, void *context)
+{
+ struct ext_include_ast_context *actx =
+ (struct ext_include_ast_context *)context;
+ struct sieve_script **scripts;
+ unsigned int count, i;
+
+ /* Unreference included scripts */
+ scripts = array_get_modifiable(&actx->included_scripts, &count);
+ for (i = 0; i < count; i++) {
+ sieve_script_unref(&scripts[i]);
+ }
+
+ /* Unreference variable scopes */
+ if (actx->global_vars != NULL)
+ sieve_variable_scope_unref(&actx->global_vars);
+}
+
+static const struct sieve_ast_extension include_ast_extension = {
+ &include_extension,
+ ext_include_ast_free
+};
+
+struct ext_include_ast_context *
+ext_include_create_ast_context(const struct sieve_extension *this_ext,
+ struct sieve_ast *ast, struct sieve_ast *parent)
+{
+ struct ext_include_ast_context *actx;
+
+ pool_t pool = sieve_ast_pool(ast);
+ actx = p_new(pool, struct ext_include_ast_context, 1);
+ p_array_init(&actx->included_scripts, pool, 32);
+
+ if (parent != NULL) {
+ struct ext_include_ast_context *parent_ctx =
+ (struct ext_include_ast_context *)
+ sieve_ast_extension_get_context(parent, this_ext);
+
+ actx->global_vars = parent_ctx->global_vars;
+ i_assert(actx->global_vars != NULL);
+
+ sieve_variable_scope_ref(actx->global_vars);
+ } else {
+ struct ext_include_context *ectx =
+ ext_include_get_context(this_ext);
+
+ actx->global_vars = sieve_variable_scope_create(
+ this_ext->svinst, ectx->var_ext, this_ext);
+ }
+
+ sieve_ast_extension_register(ast, this_ext, &include_ast_extension,
+ (void *)actx);
+ return actx;
+}
+
+struct ext_include_ast_context *
+ext_include_get_ast_context(const struct sieve_extension *this_ext,
+ struct sieve_ast *ast)
+{
+ struct ext_include_ast_context *actx =
+ (struct ext_include_ast_context *)
+ sieve_ast_extension_get_context(ast, this_ext);
+
+ if (actx != NULL)
+ return actx;
+ return ext_include_create_ast_context(this_ext, ast, NULL);
+}
+
+void ext_include_ast_link_included_script(
+ const struct sieve_extension *this_ext, struct sieve_ast *ast,
+ struct sieve_script *script)
+{
+ struct ext_include_ast_context *actx =
+ ext_include_get_ast_context(this_ext, ast);
+
+ array_append(&actx->included_scripts, &script, 1);
+}
+
+bool ext_include_validator_have_variables(
+ const struct sieve_extension *this_ext, struct sieve_validator *valdtr)
+{
+ struct ext_include_context *ectx = ext_include_get_context(this_ext);
+
+ return sieve_ext_variables_is_active(ectx->var_ext, valdtr);
+}
+
+/*
+ * Generator context management
+ */
+
+static struct ext_include_generator_context *
+ext_include_create_generator_context(
+ struct sieve_generator *gentr,
+ struct ext_include_generator_context *parent,
+ struct sieve_script *script)
+{
+ struct ext_include_generator_context *ctx;
+
+ pool_t pool = sieve_generator_pool(gentr);
+ ctx = p_new(pool, struct ext_include_generator_context, 1);
+ ctx->parent = parent;
+ ctx->script = script;
+ if (parent == NULL)
+ ctx->nesting_depth = 0;
+ else
+ ctx->nesting_depth = parent->nesting_depth + 1;
+
+ return ctx;
+}
+
+static inline struct ext_include_generator_context *
+ext_include_get_generator_context(const struct sieve_extension *this_ext,
+ struct sieve_generator *gentr)
+{
+ return (struct ext_include_generator_context *)
+ sieve_generator_extension_get_context(gentr, this_ext);
+}
+
+static inline void
+ext_include_initialize_generator_context(
+ const struct sieve_extension *this_ext, struct sieve_generator *gentr,
+ struct ext_include_generator_context *parent,
+ struct sieve_script *script)
+{
+ sieve_generator_extension_set_context(
+ gentr, this_ext,
+ ext_include_create_generator_context(gentr, parent, script));
+}
+
+void ext_include_register_generator_context(
+ const struct sieve_extension *this_ext,
+ const struct sieve_codegen_env *cgenv)
+{
+ struct ext_include_generator_context *ctx =
+ ext_include_get_generator_context(this_ext, cgenv->gentr);
+
+ /* Initialize generator context if necessary */
+ if (ctx == NULL) {
+ ctx = ext_include_create_generator_context(
+ cgenv->gentr, NULL, cgenv->script);
+
+ sieve_generator_extension_set_context(
+ cgenv->gentr, this_ext, (void *)ctx);
+ }
+
+ /* Initialize ast context if necessary */
+ (void)ext_include_get_ast_context(this_ext, cgenv->ast);
+ (void)ext_include_binary_init(this_ext, cgenv->sbin, cgenv->ast);
+}
+
+/*
+ * Runtime initialization
+ */
+
+static int
+ext_include_runtime_init(const struct sieve_extension *this_ext,
+ const struct sieve_runtime_env *renv,
+ void *context, bool deferred ATTR_UNUSED)
+{
+ struct ext_include_interpreter_context *ctx =
+ (struct ext_include_interpreter_context *)context;
+ struct ext_include_context *ectx = ext_include_get_context(this_ext);
+
+ if (ctx->parent == NULL) {
+ ctx->global = p_new(ctx->pool,
+ struct ext_include_interpreter_global, 1);
+ p_array_init(&ctx->global->included_scripts, ctx->pool, 10);
+
+ ctx->global->var_scope =
+ ext_include_binary_get_global_scope(
+ this_ext, renv->sbin);
+ ctx->global->var_storage =
+ sieve_variable_storage_create(ectx->var_ext, ctx->pool,
+ ctx->global->var_scope);
+ } else {
+ ctx->global = ctx->parent->global;
+ }
+
+ sieve_ext_variables_runtime_set_storage(ectx->var_ext, renv, this_ext,
+ ctx->global->var_storage);
+ return SIEVE_EXEC_OK;
+}
+
+static struct sieve_interpreter_extension include_interpreter_extension = {
+ .ext_def = &include_extension,
+ .run = ext_include_runtime_init
+};
+
+/*
+ * Interpreter context management
+ */
+
+static struct ext_include_interpreter_context *
+ext_include_interpreter_context_create(
+ struct sieve_interpreter *interp,
+ struct ext_include_interpreter_context *parent,
+ struct sieve_script *script,
+ const struct ext_include_script_info *sinfo)
+{
+ struct ext_include_interpreter_context *ctx;
+
+ pool_t pool = sieve_interpreter_pool(interp);
+ ctx = p_new(pool, struct ext_include_interpreter_context, 1);
+ ctx->pool = pool;
+ ctx->parent = parent;
+ ctx->interp = interp;
+ ctx->script = script;
+ ctx->script_info = sinfo;
+
+ if (parent == NULL)
+ ctx->nesting_depth = 0;
+ else
+ ctx->nesting_depth = parent->nesting_depth + 1;
+ return ctx;
+}
+
+static inline struct ext_include_interpreter_context *
+ext_include_get_interpreter_context(const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp)
+{
+ return (struct ext_include_interpreter_context *)
+ sieve_interpreter_extension_get_context(interp, this_ext);
+}
+
+static inline struct ext_include_interpreter_context *
+ext_include_interpreter_context_init_child(
+ const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp,
+ struct ext_include_interpreter_context *parent,
+ struct sieve_script *script,
+ const struct ext_include_script_info *sinfo)
+{
+ struct ext_include_interpreter_context *ctx =
+ ext_include_interpreter_context_create(interp, parent,
+ script, sinfo);
+
+ sieve_interpreter_extension_register(interp, this_ext,
+ &include_interpreter_extension,
+ ctx);
+ return ctx;
+}
+
+void ext_include_interpreter_context_init(
+ const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp)
+{
+ struct ext_include_interpreter_context *ctx =
+ ext_include_get_interpreter_context(this_ext, interp);
+
+ /* Is this is the top-level interpreter ? */
+ if (ctx == NULL) {
+ struct sieve_script *script;
+
+ /* Initialize top context */
+ script = sieve_interpreter_script(interp);
+ ctx = ext_include_interpreter_context_create(interp, NULL,
+ script, NULL);
+
+ sieve_interpreter_extension_register(
+ interp, this_ext, &include_interpreter_extension,
+ (void *)ctx);
+ }
+}
+
+struct sieve_variable_storage *
+ext_include_interpreter_get_global_variables(
+ const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp)
+{
+ struct ext_include_interpreter_context *ctx =
+ ext_include_get_interpreter_context(this_ext, interp);
+
+ return ctx->global->var_storage;
+}
+
+/*
+ * Including a script during code generation
+ */
+
+int ext_include_generate_include(
+ const struct sieve_codegen_env *cgenv, struct sieve_command *cmd,
+ enum ext_include_script_location location, enum ext_include_flags flags,
+ struct sieve_script *script,
+ const struct ext_include_script_info **included_r)
+{
+ const struct sieve_extension *this_ext = cmd->ext;
+ struct ext_include_context *ext_ctx =
+ (struct ext_include_context *)this_ext->context;
+ int result = 1;
+ struct sieve_ast *ast;
+ struct sieve_binary *sbin = cgenv->sbin;
+ struct sieve_generator *gentr = cgenv->gentr;
+ struct ext_include_binary_context *binctx;
+ struct sieve_generator *subgentr;
+ struct ext_include_generator_context *ctx =
+ ext_include_get_generator_context(this_ext, gentr);
+ struct ext_include_generator_context *pctx;
+ struct sieve_error_handler *ehandler =
+ sieve_generator_error_handler(gentr);
+ struct ext_include_script_info *included;
+
+ *included_r = NULL;
+
+ /* Just to be sure: do not include more scripts when errors have occured
+ already.
+ */
+ if (sieve_get_errors(ehandler) > 0)
+ return -1;
+
+ /* Limit nesting level */
+ if (ctx->nesting_depth >= ext_ctx->max_nesting_depth) {
+ sieve_command_generate_error(
+ gentr, cmd,
+ "cannot nest includes deeper than %d levels",
+ ext_ctx->max_nesting_depth);
+ return -1;
+ }
+
+ /* Check for circular include */
+ if ((flags & EXT_INCLUDE_FLAG_ONCE) == 0) {
+ pctx = ctx;
+ while (pctx != NULL) {
+ if (sieve_script_equals(pctx->script, script)) {
+ /* Just drop circular include when uploading
+ inactive script; not an error
+ */
+ if ((cgenv->flags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 &&
+ (cgenv->flags & SIEVE_COMPILE_FLAG_ACTIVATED) == 0) {
+ sieve_command_generate_warning(
+ gentr, cmd,
+ "circular include (ignored during upload)");
+ return 0;
+ }
+
+ sieve_command_generate_error(gentr, cmd,
+ "circular include");
+ return -1;
+ }
+
+ pctx = pctx->parent;
+ }
+ }
+
+ /* Get binary context */
+ binctx = ext_include_binary_init(this_ext, sbin, cgenv->ast);
+
+ /* Is the script already compiled into the current binary? */
+ included = ext_include_binary_script_get_include_info(binctx, script);
+ if (included != NULL) {
+ /* Yes, only update flags */
+ if ((flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0)
+ included->flags &= ENUM_NEGATE(EXT_INCLUDE_FLAG_OPTIONAL);
+ if ((flags & EXT_INCLUDE_FLAG_ONCE) == 0)
+ included->flags &= ENUM_NEGATE(EXT_INCLUDE_FLAG_ONCE);
+ } else {
+ const char *script_name = sieve_script_name(script);
+ enum sieve_compile_flags cpflags = cgenv->flags;
+
+ /* No, include new script */
+
+ /* Check whether include limit is exceeded */
+ if (ext_include_binary_script_get_count(binctx) >=
+ ext_ctx->max_includes) {
+ sieve_command_generate_error(
+ gentr, cmd, "failed to include script '%s': "
+ "no more than %u includes allowed",
+ str_sanitize(script_name, 80),
+ ext_ctx->max_includes);
+ return -1;
+ }
+
+ /* Allocate a new block in the binary and mark the script as
+ included. */
+ if (!sieve_script_is_open(script)) {
+ /* Just making an empty entry to mark a missing script
+ */
+ i_assert((flags & EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD) != 0 ||
+ (flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0);
+ included = ext_include_binary_script_include(
+ binctx, location, flags, script, NULL);
+ result = 0;
+
+ } else {
+ struct sieve_binary_block *inc_block =
+ sieve_binary_block_create(sbin);
+
+ /* Real include */
+ included = ext_include_binary_script_include(
+ binctx, location, flags, script, inc_block);
+
+ /* Parse */
+ if ((ast = sieve_parse(script, ehandler,
+ NULL)) == NULL) {
+ sieve_command_generate_error(
+ gentr, cmd,
+ "failed to parse included script '%s'",
+ str_sanitize(script_name, 80));
+ return -1;
+ }
+
+ /* Included scripts inherit global variable scope */
+ (void)ext_include_create_ast_context(
+ this_ext, ast, cmd->ast_node->ast);
+
+ if (location == EXT_INCLUDE_LOCATION_GLOBAL) {
+ cpflags &=
+ ENUM_NEGATE(SIEVE_EXECUTE_FLAG_NOGLOBAL);
+ } else {
+ cpflags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
+ }
+
+ /* Validate */
+ if (!sieve_validate(ast, ehandler, cpflags, NULL)) {
+ sieve_command_generate_error(
+ gentr, cmd,
+ "failed to validate included script '%s'",
+ str_sanitize(script_name, 80));
+ sieve_ast_unref(&ast);
+ return -1;
+ }
+
+ /* Generate
+
+ FIXME: It might not be a good idea to recurse code
+ generation for included scripts.
+ */
+ subgentr = sieve_generator_create(ast, ehandler, cpflags);
+ ext_include_initialize_generator_context(
+ cmd->ext, subgentr, ctx, script);
+
+ if (sieve_generator_run(subgentr, &inc_block) == NULL) {
+ sieve_command_generate_error(
+ gentr, cmd,
+ "failed to generate code for included script '%s'",
+ str_sanitize(script_name, 80));
+ result = -1;
+ }
+
+ sieve_generator_free(&subgentr);
+
+ /* Cleanup */
+ sieve_ast_unref(&ast);
+ }
+ }
+
+ if (result > 0)
+ *included_r = included;
+ return result;
+}
+
+/*
+ * Executing an included script during interpretation
+ */
+
+static bool
+ext_include_runtime_check_circular(
+ struct ext_include_interpreter_context *ctx,
+ const struct ext_include_script_info *include)
+{
+ struct ext_include_interpreter_context *pctx;
+
+ pctx = ctx;
+ while (pctx != NULL) {
+
+ if (sieve_script_equals(include->script, pctx->script))
+ return TRUE;
+
+ pctx = pctx->parent;
+ }
+
+ return FALSE;
+}
+
+static bool
+ext_include_runtime_include_mark(struct ext_include_interpreter_context *ctx,
+ const struct ext_include_script_info *include,
+ bool once)
+{
+ struct sieve_script *const *includes;
+ unsigned int count, i;
+
+ includes = array_get(&ctx->global->included_scripts, &count);
+ for (i = 0; i < count; i++) {
+ if (sieve_script_equals(include->script, includes[i]))
+ return (!once);
+ }
+
+ array_append(&ctx->global->included_scripts, &include->script, 1);
+ return TRUE;
+}
+
+int ext_include_execute_include(const struct sieve_runtime_env *renv,
+ unsigned int include_id,
+ enum ext_include_flags flags)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ int result = SIEVE_EXEC_OK;
+ struct ext_include_interpreter_context *ctx;
+ const struct ext_include_script_info *included;
+ struct ext_include_binary_context *binctx =
+ ext_include_binary_get_context(this_ext, renv->sbin);
+ bool once = ((flags & EXT_INCLUDE_FLAG_ONCE) != 0);
+ unsigned int block_id;
+
+ /* Check for invalid include id (== corrupt binary) */
+ included = ext_include_binary_script_get_included(binctx, include_id);
+ if (included == NULL) {
+ sieve_runtime_trace_error(
+ renv, "include: include id %d is invalid", include_id);
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ ctx = ext_include_get_interpreter_context(this_ext, renv->interp);
+ block_id = sieve_binary_block_get_id(included->block);
+
+ /* If :once modifier is specified, check for duplicate include */
+ if (ext_include_runtime_include_mark(ctx, included, once)) {
+ sieve_runtime_trace(
+ renv, SIEVE_TRLVL_NONE,
+ "include: start script '%s' [inc id: %d, block: %d]",
+ sieve_script_name(included->script),
+ include_id, block_id);
+ } else {
+ /* skip */
+ sieve_runtime_trace(
+ renv, SIEVE_TRLVL_NONE,
+ "include: skipped include for script '%s' "
+ "[inc id: %d, block: %d]; already run once",
+ sieve_script_name(included->script),
+ include_id, block_id);
+ return result;
+ }
+
+ /* Check circular include during interpretation as well.
+ * Let's not trust binaries.
+ */
+ if (ext_include_runtime_check_circular(ctx, included)) {
+ sieve_runtime_trace_error(renv,
+ "include: circular include of script '%s' "
+ "[inc id: %d, block: %d]",
+ sieve_script_name(included->script),
+ include_id, block_id);
+
+ /* Situation has no valid way to emerge at runtime */
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if (ctx->parent == NULL) {
+ struct ext_include_interpreter_context *curctx = NULL;
+ struct sieve_error_handler *ehandler = renv->ehandler;
+ struct sieve_interpreter *subinterp;
+ bool interrupted = FALSE;
+
+ /* We are the top-level interpreter instance */
+ if (result == SIEVE_EXEC_OK) {
+ struct sieve_execute_env eenv_new = *eenv;
+
+ if (included->location != EXT_INCLUDE_LOCATION_GLOBAL)
+ eenv_new.flags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
+ else {
+ eenv_new.flags &=
+ ENUM_NEGATE(SIEVE_EXECUTE_FLAG_NOGLOBAL);
+ }
+
+ /* Create interpreter for top-level included script
+ (first sub-interpreter)
+ */
+ subinterp = sieve_interpreter_create_for_block(
+ included->block, included->script, renv->interp,
+ &eenv_new, ehandler);
+ if (subinterp != NULL) {
+ curctx = ext_include_interpreter_context_init_child(
+ this_ext, subinterp, ctx, included->script,
+ included);
+
+ /* Activate and start the top-level included script */
+ result = sieve_interpreter_start(
+ subinterp, renv->result, &interrupted);
+ } else {
+ result = SIEVE_EXEC_BIN_CORRUPT;
+ }
+ }
+
+ /* Included scripts can have includes of their own. This is not
+ implemented recursively. Rather, the sub-interpreter
+ interrupts and defers the include to the top-level
+ interpreter, which is here. */
+ if (result == SIEVE_EXEC_OK && interrupted &&
+ !curctx->returned) {
+ while (result == SIEVE_EXEC_OK) {
+ if (((interrupted && curctx->returned) ||
+ (!interrupted)) &&
+ curctx->parent != NULL) {
+ const struct ext_include_script_info *ended_script =
+ curctx->script_info;
+
+ /* Sub-interpreter ended or executed
+ return */
+
+ /* Ascend interpreter stack */
+ curctx = curctx->parent;
+ sieve_interpreter_free(&subinterp);
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_NONE,
+ "include: script '%s' ended "
+ "[inc id: %d, block: %d]",
+ sieve_script_name(ended_script->script),
+ ended_script->id,
+ sieve_binary_block_get_id(ended_script->block));
+
+ /* This is the top-most sub-interpreter,
+ bail out */
+ if (curctx->parent == NULL)
+ break;
+
+ subinterp = curctx->interp;
+
+ /* Continue parent */
+ curctx->include = NULL;
+ curctx->returned = FALSE;
+
+ result = sieve_interpreter_continue(
+ subinterp, &interrupted);
+ } else {
+ if (curctx->include != NULL) {
+ /* Sub-include requested */
+
+ if (result == SIEVE_EXEC_OK) {
+ struct sieve_execute_env eenv_new = *eenv;
+
+ if (curctx->include->location != EXT_INCLUDE_LOCATION_GLOBAL)
+ eenv_new.flags |= SIEVE_EXECUTE_FLAG_NOGLOBAL;
+ else {
+ eenv_new.flags &=
+ ENUM_NEGATE(SIEVE_EXECUTE_FLAG_NOGLOBAL);
+ }
+
+ /* Create sub-interpreter */
+ subinterp = sieve_interpreter_create_for_block(
+ curctx->include->block, curctx->include->script,
+ curctx->interp, &eenv_new, ehandler);
+ if (subinterp != NULL) {
+ curctx = ext_include_interpreter_context_init_child(
+ this_ext, subinterp, curctx,
+ curctx->include->script, curctx->include);
+
+ /* Start the sub-include's interpreter */
+ curctx->include = NULL;
+ curctx->returned = FALSE;
+ result = sieve_interpreter_start(
+ subinterp, renv->result, &interrupted);
+ } else {
+ result = SIEVE_EXEC_BIN_CORRUPT;
+ }
+ }
+ } else {
+ /* Sub-interpreter was interrupted outside
+ this extension, probably stop command was
+ executed. Generate an interrupt ourselves,
+ ending all script execution. */
+ sieve_interpreter_interrupt(renv->interp);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Free any sub-interpreters that might still be active */
+ while (curctx != NULL && curctx->parent != NULL) {
+ struct ext_include_interpreter_context *nextctx =
+ curctx->parent;
+ struct sieve_interpreter *killed_interp = curctx->interp;
+ const struct ext_include_script_info *ended_script =
+ curctx->script_info;
+
+ /* This kills curctx too */
+ sieve_interpreter_free(&killed_interp);
+
+ sieve_runtime_trace(
+ renv, SIEVE_TRLVL_NONE,
+ "include: script '%s' ended [id: %d, block: %d]",
+ sieve_script_name(ended_script->script),
+ ended_script->id,
+ sieve_binary_block_get_id(ended_script->block));
+
+ /* Luckily we recorded the parent earlier */
+ curctx = nextctx;
+ }
+
+ } else {
+ /* We are an included script already, defer inclusion to main
+ interpreter */
+ ctx->include = included;
+ sieve_interpreter_interrupt(renv->interp);
+ }
+
+ return result;
+}
+
+void ext_include_execute_return(const struct sieve_runtime_env *renv)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct ext_include_interpreter_context *ctx =
+ ext_include_get_interpreter_context(this_ext, renv->interp);
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+ "return: exiting included script");
+ ctx->returned = TRUE;
+ sieve_interpreter_interrupt(renv->interp);
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.h b/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.h
new file mode 100644
index 0000000..44bfe9a
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-common.h
@@ -0,0 +1,170 @@
+#ifndef EXT_INCLUDE_COMMON_H
+#define EXT_INCLUDE_COMMON_H
+
+#include "lib.h"
+#include "hash.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+
+/*
+ * Forward declarations
+ */
+
+struct ext_include_script_info;
+struct ext_include_binary_context;
+
+/*
+ * Types
+ */
+
+enum ext_include_flags { // stored in one byte
+ EXT_INCLUDE_FLAG_ONCE = 0x01,
+ EXT_INCLUDE_FLAG_OPTIONAL = 0x02,
+ EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD = 0x04
+};
+
+enum ext_include_script_location {
+ EXT_INCLUDE_LOCATION_PERSONAL,
+ EXT_INCLUDE_LOCATION_GLOBAL,
+ EXT_INCLUDE_LOCATION_INVALID
+};
+
+static inline const char *
+ext_include_script_location_name(enum ext_include_script_location location)
+{
+ switch (location) {
+ case EXT_INCLUDE_LOCATION_PERSONAL:
+ return "personal";
+ case EXT_INCLUDE_LOCATION_GLOBAL:
+ return "global";
+ default:
+ break;
+ }
+
+ return "[INVALID LOCATION]";
+}
+
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def include_extension;
+extern const struct sieve_binary_extension include_binary_ext;
+
+bool ext_include_load(const struct sieve_extension *ext, void **context);
+void ext_include_unload(const struct sieve_extension *ext);
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def cmd_include;
+extern const struct sieve_command_def cmd_return;
+extern const struct sieve_command_def cmd_global;
+
+/* DEPRICATED */
+extern const struct sieve_command_def cmd_import;
+extern const struct sieve_command_def cmd_export;
+
+/*
+ * Operations
+ */
+
+enum ext_include_opcode {
+ EXT_INCLUDE_OPERATION_INCLUDE,
+ EXT_INCLUDE_OPERATION_RETURN,
+ EXT_INCLUDE_OPERATION_GLOBAL
+};
+
+extern const struct sieve_operation_def include_operation;
+extern const struct sieve_operation_def return_operation;
+extern const struct sieve_operation_def global_operation;
+
+/*
+ * Script access
+ */
+
+struct sieve_storage *
+ext_include_get_script_storage(const struct sieve_extension *ext,
+ enum ext_include_script_location location,
+ const char *script_name,
+ enum sieve_error *error_r);
+/*
+ * Context
+ */
+
+/* Extension context */
+
+struct ext_include_context {
+ /* Extension dependencies */
+ const struct sieve_extension *var_ext;
+
+ /* Configuration */
+ char *global_location;
+
+ struct sieve_storage *global_storage;
+ struct sieve_storage *personal_storage;
+
+ unsigned int max_nesting_depth;
+ unsigned int max_includes;
+};
+
+static inline struct ext_include_context *
+ext_include_get_context(const struct sieve_extension *ext)
+{
+ return (struct ext_include_context *) ext->context;
+}
+
+/* AST Context */
+
+struct ext_include_ast_context {
+ struct sieve_variable_scope *global_vars;
+
+ ARRAY(struct sieve_script *) included_scripts;
+};
+
+struct ext_include_ast_context *
+ext_include_create_ast_context(const struct sieve_extension *this_ext,
+ struct sieve_ast *ast, struct sieve_ast *parent);
+struct ext_include_ast_context *
+ext_include_get_ast_context(const struct sieve_extension *this_ext,
+ struct sieve_ast *ast);
+
+void ext_include_ast_link_included_script(
+ const struct sieve_extension *this_ext, struct sieve_ast *ast,
+ struct sieve_script *script);
+
+bool ext_include_validator_have_variables(
+ const struct sieve_extension *this_ext, struct sieve_validator *valdtr);
+
+/* Generator context */
+
+void ext_include_register_generator_context(
+ const struct sieve_extension *this_ext,
+ const struct sieve_codegen_env *cgenv);
+
+int ext_include_generate_include(
+ const struct sieve_codegen_env *cgenv, struct sieve_command *cmd,
+ enum ext_include_script_location location, enum ext_include_flags flags,
+ struct sieve_script *script,
+ const struct ext_include_script_info **included_r);
+
+/* Interpreter context */
+
+void ext_include_interpreter_context_init(
+ const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp);
+
+int ext_include_execute_include(const struct sieve_runtime_env *renv,
+ unsigned int block_id,
+ enum ext_include_flags flags);
+void ext_include_execute_return(const struct sieve_runtime_env *renv);
+
+struct sieve_variable_storage *
+ext_include_interpreter_get_global_variables(
+ const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-limits.h b/pigeonhole/src/lib-sieve/plugins/include/ext-include-limits.h
new file mode 100644
index 0000000..37246c0
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-limits.h
@@ -0,0 +1,9 @@
+#ifndef EXT_INCLUDE_LIMITS_H
+#define EXT_INCLUDE_LIMITS_H
+
+#include "sieve-common.h"
+
+#define EXT_INCLUDE_DEFAULT_MAX_NESTING_DEPTH 10
+#define EXT_INCLUDE_DEFAULT_MAX_INCLUDES 255
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.c b/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.c
new file mode 100644
index 0000000..28cd803
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.c
@@ -0,0 +1,254 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+#include "sieve-script.h"
+#include "sieve-ast.h"
+#include "sieve-binary.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-include-common.h"
+#include "ext-include-binary.h"
+#include "ext-include-variables.h"
+
+/*
+ * Variable import-export
+ */
+
+struct sieve_variable *ext_include_variable_import_global
+(struct sieve_validator *valdtr, struct sieve_command *cmd,
+ const char *variable)
+{
+ const struct sieve_extension *this_ext = cmd->ext;
+ struct sieve_ast *ast = cmd->ast_node->ast;
+ struct ext_include_ast_context *ctx =
+ ext_include_get_ast_context(this_ext, ast);
+ struct ext_include_context *ectx = ext_include_get_context(this_ext);
+ struct sieve_variable_scope *local_scope;
+ struct sieve_variable_scope *global_scope = ctx->global_vars;
+ struct sieve_variable *global_var = NULL, *local_var;
+
+ /* Sanity safeguard */
+ i_assert ( ctx->global_vars != NULL );
+
+ if ( !sieve_variable_identifier_is_valid(variable) ) {
+ sieve_command_validate_error(valdtr, cmd,
+ "invalid variable identifier '%s'", str_sanitize(variable,80));
+ return NULL;
+ }
+
+ /* Get/Declare the variable in the global scope */
+ global_var = sieve_variable_scope_declare(global_scope, variable);
+
+ /* Check whether scope is over its size limit */
+ if ( global_var == NULL ) {
+ sieve_command_validate_error(valdtr, cmd,
+ "declaration of new global variable '%s' exceeds the limit "
+ "(max variables: %u)", variable,
+ sieve_variables_get_max_scope_size(ectx->var_ext));
+ return NULL;
+ }
+
+ /* Import the global variable into the local script scope */
+ local_scope = sieve_ext_variables_get_local_scope(ectx->var_ext, valdtr);
+
+ local_var = sieve_variable_scope_get_variable(local_scope, variable);
+ if ( local_var != NULL && local_var->ext != this_ext ) {
+ /* FIXME: indicate location of conflicting set statement */
+ sieve_command_validate_error(valdtr, cmd,
+ "declaration of new global variable '%s' conflicts with earlier local "
+ "use", variable);
+ return NULL;
+ }
+
+ return sieve_variable_scope_import(local_scope, global_var);
+}
+
+/*
+ * Binary symbol table
+ */
+
+bool ext_include_variables_save
+(struct sieve_binary_block *sblock,
+ struct sieve_variable_scope_binary *global_vars,
+ enum sieve_error *error_r ATTR_UNUSED)
+{
+ struct sieve_variable_scope *global_scope =
+ sieve_variable_scope_binary_get(global_vars);
+ unsigned int count = sieve_variable_scope_size(global_scope);
+ sieve_size_t jump;
+
+ sieve_binary_emit_unsigned(sblock, count);
+
+ jump = sieve_binary_emit_offset(sblock, 0);
+
+ if ( count > 0 ) {
+ unsigned int size, i;
+ struct sieve_variable *const *vars =
+ sieve_variable_scope_get_variables(global_scope, &size);
+
+ for ( i = 0; i < size; i++ ) {
+ sieve_binary_emit_cstring(sblock, vars[i]->identifier);
+ }
+ }
+
+ sieve_binary_resolve_offset(sblock, jump);
+
+ return TRUE;
+}
+
+bool ext_include_variables_load
+(const struct sieve_extension *this_ext, struct sieve_binary_block *sblock,
+ sieve_size_t *offset, struct sieve_variable_scope_binary **global_vars_r)
+{
+ struct ext_include_context *ectx =
+ ext_include_get_context(this_ext);
+
+ /* Sanity assert */
+ i_assert( *global_vars_r == NULL );
+
+ *global_vars_r = sieve_variable_scope_binary_read
+ (this_ext->svinst, ectx->var_ext, this_ext, sblock, offset);
+
+ return ( *global_vars_r != NULL );
+}
+
+bool ext_include_variables_dump
+(struct sieve_dumptime_env *denv,
+ struct sieve_variable_scope_binary *global_vars)
+{
+ struct sieve_variable_scope *global_scope =
+ sieve_variable_scope_binary_get(global_vars);
+ unsigned int size;
+ struct sieve_variable *const *vars;
+
+ i_assert(global_scope != NULL);
+
+ vars = sieve_variable_scope_get_variables(global_scope, &size);
+
+ if ( size > 0 ) {
+ unsigned int i;
+
+ sieve_binary_dump_sectionf(denv, "Global variables");
+
+ for ( i = 0; i < size; i++ ) {
+ sieve_binary_dumpf(denv, "%3d: '%s' \n", i, vars[i]->identifier);
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * Global variables namespace
+ */
+
+bool vnspc_global_variables_validate
+ (struct sieve_validator *valdtr, const struct sieve_variables_namespace *nspc,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd,
+ ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data,
+ bool assignment);
+bool vnspc_global_variables_generate
+ (const struct sieve_codegen_env *cgenv,
+ const struct sieve_variables_namespace *nspc,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd, void *var_data);
+
+static const struct sieve_variables_namespace_def
+global_variables_namespace = {
+ SIEVE_OBJECT("global", NULL, 0),
+ .validate = vnspc_global_variables_validate,
+ .generate = vnspc_global_variables_generate
+};
+
+bool vnspc_global_variables_validate
+(struct sieve_validator *valdtr,
+ const struct sieve_variables_namespace *nspc, struct sieve_ast_argument *arg,
+ struct sieve_command *cmd ATTR_UNUSED,
+ ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data,
+ bool assignment ATTR_UNUSED)
+{
+ const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc);
+ struct sieve_ast *ast = arg->ast;
+ struct ext_include_context *ectx =
+ ext_include_get_context(this_ext);
+ struct ext_include_ast_context *ctx =
+ ext_include_get_ast_context(this_ext, ast);
+ struct sieve_variable *var = NULL;
+ const struct sieve_variable_name *name_element;
+ const char *variable;
+
+ /* Sanity safeguard */
+ i_assert ( ctx->global_vars != NULL );
+
+ /* Check variable name */
+
+ if ( array_count(var_name) != 2 ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "invalid variable name within global namespace: "
+ "encountered sub-namespace");
+ return FALSE;
+ }
+
+ name_element = array_idx(var_name, 1);
+ if ( name_element->num_variable >= 0 ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "invalid variable name within global namespace: "
+ "encountered numeric variable name");
+ return FALSE;
+ }
+
+ variable = str_c(name_element->identifier);
+
+ /* Get/Declare the variable in the global scope */
+
+ var = sieve_variable_scope_declare(ctx->global_vars, variable);
+
+ if ( var == NULL ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "(implicit) declaration of new global variable '%s' exceeds the limit "
+ "(max variables: %u)", variable,
+ sieve_variables_get_max_scope_size(ectx->var_ext));
+ return FALSE;
+ }
+
+ *var_data = (void *) var;
+
+ return TRUE;
+}
+
+bool vnspc_global_variables_generate
+(const struct sieve_codegen_env *cgenv,
+ const struct sieve_variables_namespace *nspc,
+ struct sieve_ast_argument *arg ATTR_UNUSED,
+ struct sieve_command *cmd ATTR_UNUSED, void *var_data)
+{
+ const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc);
+ struct ext_include_context *ectx = ext_include_get_context(this_ext);
+ struct sieve_variable *var = (struct sieve_variable *) var_data;
+
+ sieve_variables_opr_variable_emit(cgenv->sblock, ectx->var_ext, var);
+
+ return TRUE;
+}
+
+void ext_include_variables_global_namespace_init
+(const struct sieve_extension *this_ext, struct sieve_validator *valdtr)
+{
+ struct ext_include_context *ectx = ext_include_get_context(this_ext);
+
+ sieve_variables_namespace_register
+ (ectx->var_ext, valdtr, this_ext, &global_variables_namespace);
+}
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.h b/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.h
new file mode 100644
index 0000000..d5927b2
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-variables.h
@@ -0,0 +1,41 @@
+#ifndef EXT_INCLUDE_VARIABLES_H
+#define EXT_INCLUDE_VARIABLES_H
+
+#include "sieve-common.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-include-common.h"
+
+/*
+ * Variable import-export
+ */
+
+struct sieve_variable *ext_include_variable_import_global
+ (struct sieve_validator *valdtr, struct sieve_command *cmd,
+ const char *variable);
+
+/*
+ * Binary symbol table
+ */
+
+bool ext_include_variables_save
+ (struct sieve_binary_block *sblock,
+ struct sieve_variable_scope_binary *global_vars,
+ enum sieve_error *error_r);
+bool ext_include_variables_load
+ (const struct sieve_extension *this_ext, struct sieve_binary_block *sblock,
+ sieve_size_t *offset, struct sieve_variable_scope_binary **global_vars_r);
+bool ext_include_variables_dump
+ (struct sieve_dumptime_env *denv,
+ struct sieve_variable_scope_binary *global_vars);
+
+/*
+ * Validation
+ */
+
+void ext_include_variables_global_namespace_init
+ (const struct sieve_extension *this_ext, struct sieve_validator *valdtr);
+
+#endif
+
diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include.c b/pigeonhole/src/lib-sieve/plugins/include/ext-include.c
new file mode 100644
index 0000000..0a38687
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include.c
@@ -0,0 +1,121 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension include
+ * -----------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 6609
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+/* FIXME: Current include implementation does not allow for parts of the script
+ * to be located in external binaries; all included scripts are recompiled and
+ * the resulting byte code is imported into the main binary in separate blocks.
+ */
+
+#include "lib.h"
+
+#include "sieve-common.h"
+
+#include "sieve-extensions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-binary.h"
+#include "sieve-dump.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-include-common.h"
+#include "ext-include-binary.h"
+#include "ext-include-variables.h"
+
+/*
+ * Operations
+ */
+
+static const struct sieve_operation_def *ext_include_operations[] = {
+ &include_operation,
+ &return_operation,
+ &global_operation
+};
+
+/*
+ * Extension
+ */
+
+/* Forward declaration */
+
+static bool ext_include_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *validator);
+static bool ext_include_generator_load
+ (const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv);
+static bool ext_include_interpreter_load
+ (const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+static bool ext_include_binary_load
+ (const struct sieve_extension *ext, struct sieve_binary *binary);
+
+/* Extension objects */
+
+const struct sieve_extension_def include_extension = {
+ .name = "include",
+ .version = 1,
+
+ .load = ext_include_load,
+ .unload = ext_include_unload,
+ .validator_load = ext_include_validator_load,
+ .generator_load = ext_include_generator_load,
+ .interpreter_load = ext_include_interpreter_load,
+ .binary_load = ext_include_binary_load,
+ .binary_dump = ext_include_binary_dump,
+ .code_dump = ext_include_code_dump,
+
+ SIEVE_EXT_DEFINE_OPERATIONS(ext_include_operations)
+};
+
+static bool ext_include_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register new commands */
+ sieve_validator_register_command(valdtr, ext, &cmd_include);
+ sieve_validator_register_command(valdtr, ext, &cmd_return);
+ sieve_validator_register_command(valdtr, ext, &cmd_global);
+
+ /* DEPRICATED */
+ sieve_validator_register_command(valdtr, ext, &cmd_import);
+ sieve_validator_register_command(valdtr, ext, &cmd_export);
+
+ /* Initialize global variables namespace */
+ ext_include_variables_global_namespace_init(ext, valdtr);
+
+ return TRUE;
+}
+
+static bool ext_include_generator_load
+(const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv)
+{
+ ext_include_register_generator_context(ext, cgenv);
+
+ return TRUE;
+}
+
+static bool ext_include_interpreter_load
+(const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ ext_include_interpreter_context_init(ext, renv->interp);
+
+ return TRUE;
+}
+
+static bool ext_include_binary_load
+(const struct sieve_extension *ext, struct sieve_binary *sbin)
+{
+ (void)ext_include_binary_get_context(ext, sbin);
+
+ return TRUE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/index/Makefile.am b/pigeonhole/src/lib-sieve/plugins/index/Makefile.am
new file mode 100644
index 0000000..434ca1c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/index/Makefile.am
@@ -0,0 +1,13 @@
+noinst_LTLIBRARIES = libsieve_ext_index.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_index_la_SOURCES = \
+ ext-index-common.c \
+ ext-index.c \
+ tag-index.c
+
+noinst_HEADERS = \
+ ext-index-common.h
diff --git a/pigeonhole/src/lib-sieve/plugins/index/Makefile.in b/pigeonhole/src/lib-sieve/plugins/index/Makefile.in
new file mode 100644
index 0000000..852316c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/index/Makefile.in
@@ -0,0 +1,688 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/index
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_index_la_LIBADD =
+am_libsieve_ext_index_la_OBJECTS = ext-index-common.lo ext-index.lo \
+ tag-index.lo
+libsieve_ext_index_la_OBJECTS = $(am_libsieve_ext_index_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-index-common.Plo \
+ ./$(DEPDIR)/ext-index.Plo ./$(DEPDIR)/tag-index.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_index_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_index_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_index.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_index_la_SOURCES = \
+ ext-index-common.c \
+ ext-index.c \
+ tag-index.c
+
+noinst_HEADERS = \
+ ext-index-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/index/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/index/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_index.la: $(libsieve_ext_index_la_OBJECTS) $(libsieve_ext_index_la_DEPENDENCIES) $(EXTRA_libsieve_ext_index_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_index_la_OBJECTS) $(libsieve_ext_index_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-index-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-index.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag-index.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-index-common.Plo
+ -rm -f ./$(DEPDIR)/ext-index.Plo
+ -rm -f ./$(DEPDIR)/tag-index.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-index-common.Plo
+ -rm -f ./$(DEPDIR)/ext-index.Plo
+ -rm -f ./$(DEPDIR)/tag-index.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.c b/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.c
new file mode 100644
index 0000000..26fb706
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.c
@@ -0,0 +1,15 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "utc-offset.h"
+#include "str.h"
+
+#include "sieve-common.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-interpreter.h"
+#include "sieve-message.h"
+
+#include "ext-index-common.h"
+
diff --git a/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.h b/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.h
new file mode 100644
index 0000000..7ce3244
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/index/ext-index-common.h
@@ -0,0 +1,29 @@
+#ifndef EXT_INDEX_COMMON_H
+#define EXT_INDEX_COMMON_H
+
+#include "sieve-common.h"
+
+#include <time.h>
+
+#define SIEVE_EXT_INDEX_HDR_OVERRIDE_SEQUENCE 100
+
+/*
+ * Tagged arguments
+ */
+
+extern const struct sieve_argument_def index_tag;
+extern const struct sieve_argument_def last_tag;
+
+/*
+ * Operands
+ */
+
+extern const struct sieve_operand_def index_operand;
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def index_extension;
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/index/ext-index.c b/pigeonhole/src/lib-sieve/plugins/index/ext-index.c
new file mode 100644
index 0000000..33e7771
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/index/ext-index.c
@@ -0,0 +1,69 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension index
+ * ------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5260
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-message.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-index-common.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_index_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *validator);
+
+const struct sieve_extension_def index_extension = {
+ .name = "index",
+ .validator_load = ext_index_validator_load,
+ SIEVE_EXT_DEFINE_OPERAND(index_operand)
+};
+
+static bool ext_index_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register :index and :last tags with header, address and date test commands
+ * and we don't care whether these command are registered or even whether
+ * these will be registered at all. The validator handles either situation
+ * gracefully.
+ */
+ sieve_validator_register_external_tag
+ (valdtr, "header", ext, &index_tag, SIEVE_OPT_MESSAGE_OVERRIDE);
+ sieve_validator_register_external_tag
+ (valdtr, "header", ext, &last_tag, 0);
+
+ sieve_validator_register_external_tag
+ (valdtr, "address", ext, &index_tag, SIEVE_OPT_MESSAGE_OVERRIDE);
+ sieve_validator_register_external_tag
+ (valdtr, "address", ext, &last_tag, 0);
+
+ sieve_validator_register_external_tag
+ (valdtr, "date", ext, &index_tag, SIEVE_OPT_MESSAGE_OVERRIDE);
+ sieve_validator_register_external_tag
+ (valdtr, "date", ext, &last_tag, 0);
+
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/index/tag-index.c b/pigeonhole/src/lib-sieve/plugins/index/tag-index.c
new file mode 100644
index 0000000..d8938a5
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/index/tag-index.c
@@ -0,0 +1,278 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+
+#include "sieve-common.h"
+#include "sieve-stringlist.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-binary.h"
+#include "sieve-code.h"
+#include "sieve-message.h"
+#include "sieve-result.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+
+#include "ext-index-common.h"
+
+/*
+ * Tagged argument
+ */
+
+static bool
+tag_index_validate(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg, struct sieve_command *cmd);
+static bool
+tag_index_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg,
+ struct sieve_command *context);
+
+const struct sieve_argument_def index_tag = {
+ .identifier = "index",
+ .validate = tag_index_validate,
+ .generate = tag_index_generate
+};
+
+static bool
+tag_last_validate(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg, struct sieve_command *cmd);
+
+const struct sieve_argument_def last_tag = {
+ .identifier = "last",
+ .validate = tag_last_validate,
+};
+
+/*
+ * Header override
+ */
+
+static bool
+svmo_index_dump_context(const struct sieve_message_override *svmo,
+ const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+svmo_index_read_context(const struct sieve_message_override *svmo,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address, void **ho_context);
+static int
+svmo_index_header_override(const struct sieve_message_override *svmo,
+ const struct sieve_runtime_env *renv,
+ bool mime_decode, struct sieve_stringlist **headers);
+
+const struct sieve_message_override_def index_header_override = {
+ SIEVE_OBJECT("index", &index_operand, 0),
+ .sequence = SIEVE_EXT_INDEX_HDR_OVERRIDE_SEQUENCE,
+ .dump_context = svmo_index_dump_context,
+ .read_context = svmo_index_read_context,
+ .header_override = svmo_index_header_override
+};
+
+/*
+ * Operand
+ */
+
+static const struct sieve_extension_objects ext_header_overrides =
+ SIEVE_EXT_DEFINE_MESSAGE_OVERRIDE(index_header_override);
+
+const struct sieve_operand_def index_operand = {
+ .name = "index operand",
+ .ext_def = &index_extension,
+ .class = &sieve_message_override_operand_class,
+ .interface = &ext_header_overrides
+};
+
+/*
+ * Tag data
+ */
+
+struct tag_index_data {
+ sieve_number_t fieldno;
+ bool last:1;
+};
+
+/*
+ * Tag validation
+ */
+
+static bool
+tag_index_validate(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_ast_argument **arg, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+ struct tag_index_data *data;
+
+ /* Skip the tag itself */
+ *arg = sieve_ast_argument_next(*arg);
+
+ /* Check syntax:
+ * ":index" <fieldno: number>
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_NUMBER, FALSE))
+ return FALSE;
+
+ if (tag->argument->data == NULL) {
+ data = p_new(sieve_command_pool(cmd), struct tag_index_data, 1);
+ tag->argument->data = (void *)data;
+ } else {
+ data = (struct tag_index_data *)tag->argument->data;
+ }
+
+ data->fieldno = sieve_ast_argument_number(*arg);
+ if (data->fieldno == 0) {
+ sieve_argument_validate_error(valdtr, *arg,
+ "the :index tag for the %s %s cannot be zero",
+ sieve_command_identifier(cmd),
+ sieve_command_type_name(cmd));
+ return FALSE;
+ }
+
+ /* Detach parameter */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+ return TRUE;
+}
+
+static bool
+tag_last_validate(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_ast_argument **arg, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *index_arg;
+ struct tag_index_data *data;
+
+ index_arg = sieve_command_find_argument(cmd, &index_tag);
+ if (index_arg == NULL) {
+ sieve_argument_validate_error(
+ valdtr, *arg,
+ "the :last tag for the %s %s cannot be specified "
+ "without the :index tag",
+ sieve_command_identifier(cmd),
+ sieve_command_type_name(cmd));
+ return FALSE;
+ }
+
+ /* Set :last flag */
+ if (index_arg->argument->data == NULL) {
+ data = p_new(sieve_command_pool(cmd), struct tag_index_data, 1);
+ index_arg->argument->data = (void*)data;
+ } else {
+ data = (struct tag_index_data *)index_arg->argument->data;
+ }
+ data->last = TRUE;
+
+ /* Detach */
+ *arg = sieve_ast_arguments_detach(*arg, 1);
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+tag_index_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg,
+ struct sieve_command *cmd ATTR_UNUSED)
+{
+ struct tag_index_data *data =
+ (struct tag_index_data *)arg->argument->data;
+
+ if (sieve_ast_argument_type(arg) != SAAT_TAG)
+ return FALSE;
+
+ sieve_opr_message_override_emit(cgenv->sblock, arg->argument->ext,
+ &index_header_override);
+
+ (void)sieve_binary_emit_integer (cgenv->sblock, data->fieldno);
+ (void)sieve_binary_emit_byte(cgenv->sblock, (data->last ? 1 : 0));
+ return TRUE;
+}
+
+/*
+ * Header override implementation
+ */
+
+/* Context data */
+
+struct svmo_index_context {
+ unsigned int fieldno;
+ bool last:1;
+};
+
+/* Context coding */
+
+static bool
+svmo_index_dump_context(const struct sieve_message_override *svmo ATTR_UNUSED,
+ const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ sieve_number_t fieldno = 0;
+ unsigned int last;
+
+ if (!sieve_binary_read_integer(denv->sblock, address, &fieldno) ||
+ fieldno == 0)
+ return FALSE;
+
+ sieve_code_dumpf(denv, "fieldno: %llu", (unsigned long long) fieldno);
+
+ if (!sieve_binary_read_byte(denv->sblock, address, &last))
+ return FALSE;
+
+ if (last > 0)
+ sieve_code_dumpf(denv, "last");
+ return TRUE;
+}
+
+static int
+svmo_index_read_context(const struct sieve_message_override *svmo ATTR_UNUSED,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address, void **ho_context)
+{
+ pool_t pool = sieve_result_pool(renv->result);
+ struct svmo_index_context *ctx;
+ sieve_number_t fieldno;
+ unsigned int last = 0;
+
+ if (!sieve_binary_read_integer(renv->sblock, address, &fieldno)) {
+ sieve_runtime_trace_error(renv, "fieldno: invalid number");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ if (fieldno == 0) {
+ sieve_runtime_trace_error(renv, "fieldno: index is zero");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ if (!sieve_binary_read_byte(renv->sblock, address, &last)) {
+ sieve_runtime_trace_error(renv, "last: invalid byte");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ ctx = p_new(pool, struct svmo_index_context, 1);
+ ctx->fieldno = fieldno;
+ ctx->last = (last == 0 ? FALSE : TRUE);
+
+ *ho_context = (void *) ctx;
+ return SIEVE_EXEC_OK;
+}
+
+/* Override */
+
+static int
+svmo_index_header_override(const struct sieve_message_override *svmo,
+ const struct sieve_runtime_env *renv,
+ bool mime_decode ATTR_UNUSED,
+ struct sieve_stringlist **headers)
+{
+ struct svmo_index_context *ctx =
+ (struct svmo_index_context *)svmo->context;
+
+ sieve_runtime_trace(
+ renv, SIEVE_TRLVL_MATCHING,
+ "header index override: only returning index %d%s",
+ ctx->fieldno, (ctx->last ? " (from last)" : ""));
+
+ *headers = sieve_index_stringlist_create(
+ renv, *headers, (int)ctx->fieldno * (ctx->last ? -1 : 1));
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.am b/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.am
new file mode 100644
index 0000000..b627735
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.am
@@ -0,0 +1,26 @@
+noinst_LTLIBRARIES = libsieve_ext_mailbox.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tags = \
+ tag-mailbox-create.c
+
+tests = \
+ tst-mailboxexists.c
+
+libsieve_ext_mailbox_la_SOURCES = \
+ $(tags) \
+ $(tests) \
+ ext-mailbox.c
+
+public_headers = \
+ sieve-ext-mailbox.h
+
+headers = \
+ ext-mailbox-common.h
+
+pkginc_libdir=$(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.in b/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.in
new file mode 100644
index 0000000..6d5ed09
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mailbox/Makefile.in
@@ -0,0 +1,757 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/mailbox
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(pkginc_lib_HEADERS) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_mailbox_la_LIBADD =
+am__objects_1 = tag-mailbox-create.lo
+am__objects_2 = tst-mailboxexists.lo
+am_libsieve_ext_mailbox_la_OBJECTS = $(am__objects_1) $(am__objects_2) \
+ ext-mailbox.lo
+libsieve_ext_mailbox_la_OBJECTS = \
+ $(am_libsieve_ext_mailbox_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-mailbox.Plo \
+ ./$(DEPDIR)/tag-mailbox-create.Plo \
+ ./$(DEPDIR)/tst-mailboxexists.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_mailbox_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_mailbox_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(pkginc_libdir)"
+HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_mailbox.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tags = \
+ tag-mailbox-create.c
+
+tests = \
+ tst-mailboxexists.c
+
+libsieve_ext_mailbox_la_SOURCES = \
+ $(tags) \
+ $(tests) \
+ ext-mailbox.c
+
+public_headers = \
+ sieve-ext-mailbox.h
+
+headers = \
+ ext-mailbox-common.h
+
+pkginc_libdir = $(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/mailbox/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/mailbox/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_mailbox.la: $(libsieve_ext_mailbox_la_OBJECTS) $(libsieve_ext_mailbox_la_DEPENDENCIES) $(EXTRA_libsieve_ext_mailbox_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_mailbox_la_OBJECTS) $(libsieve_ext_mailbox_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-mailbox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag-mailbox-create.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-mailboxexists.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkginc_libHEADERS: $(pkginc_lib_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \
+ done
+
+uninstall-pkginc_libHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(pkginc_libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-mailbox.Plo
+ -rm -f ./$(DEPDIR)/tag-mailbox-create.Plo
+ -rm -f ./$(DEPDIR)/tst-mailboxexists.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pkginc_libHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-mailbox.Plo
+ -rm -f ./$(DEPDIR)/tag-mailbox-create.Plo
+ -rm -f ./$(DEPDIR)/tst-mailboxexists.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pkginc_libHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-pkginc_libHEADERS install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-pkginc_libHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox-common.h b/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox-common.h
new file mode 100644
index 0000000..96cff39
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox-common.h
@@ -0,0 +1,39 @@
+#ifndef EXT_MAILBOX_COMMON_H
+#define EXT_MAILBOX_COMMON_H
+
+#include "sieve-common.h"
+
+#include "sieve-ext-mailbox.h"
+
+/*
+ * Tagged arguments
+ */
+
+extern const struct sieve_argument_def mailbox_create_tag;
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def mailboxexists_test;
+
+/*
+ * Operands
+ */
+
+extern const struct sieve_operand_def mailbox_create_operand;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def mailboxexists_operation;
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def mailbox_extension;
+
+#endif
+
diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox.c b/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox.c
new file mode 100644
index 0000000..a2dcd88
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mailbox/ext-mailbox.c
@@ -0,0 +1,72 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension mailbox
+ * ------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5490
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "ext-mailbox-common.h"
+
+/*
+ * Tag registration
+ */
+
+void sieve_ext_mailbox_register_create_tag
+(struct sieve_validator *valdtr, const struct sieve_extension *mailbox_ext,
+ const char *command)
+{
+ if ( sieve_validator_extension_loaded(valdtr, mailbox_ext) ) {
+ sieve_validator_register_external_tag(valdtr, command,
+ mailbox_ext, &mailbox_create_tag, SIEVE_OPT_SIDE_EFFECT);
+ }
+}
+
+
+/*
+ * Extension
+ */
+
+static bool ext_mailbox_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def mailbox_extension = {
+ .name = "mailbox",
+ .validator_load = ext_mailbox_validator_load,
+ SIEVE_EXT_DEFINE_OPERATION(mailboxexists_operation),
+ SIEVE_EXT_DEFINE_OPERAND(mailbox_create_operand)
+};
+
+static bool ext_mailbox_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register :create tag with fileinto command and we don't care whether this
+ * command is registered or even whether it will be registered at all. The
+ * validator handles either situation gracefully
+ */
+ sieve_validator_register_external_tag
+ (valdtr, "fileinto", ext, &mailbox_create_tag, SIEVE_OPT_SIDE_EFFECT);
+
+ /* Register new test */
+ sieve_validator_register_command(valdtr, ext, &mailboxexists_test);
+
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/sieve-ext-mailbox.h b/pigeonhole/src/lib-sieve/plugins/mailbox/sieve-ext-mailbox.h
new file mode 100644
index 0000000..f7fa4cf
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mailbox/sieve-ext-mailbox.h
@@ -0,0 +1,21 @@
+#ifndef SIEVE_EXT_MAILBOX_H
+#define SIEVE_EXT_MAILBOX_H
+
+/* sieve_ext_mailbox_get_extension():
+ * Get the extension struct for the mailbox extension.
+ */
+static inline const struct sieve_extension *sieve_ext_mailbox_get_extension
+(struct sieve_instance *svinst)
+{
+ return sieve_extension_get_by_name(svinst, "mailbox");
+}
+
+/* sieve_ext_mailbox_register_create_tag():
+ * Register the :create tagged argument for a command other than fileinto and
+ * redirect.
+ */
+void sieve_ext_mailbox_register_create_tag
+ (struct sieve_validator *valdtr, const struct sieve_extension *mailbox_ext,
+ const char *command);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c b/pigeonhole/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
new file mode 100644
index 0000000..f3c64cf
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
@@ -0,0 +1,186 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-actions.h"
+#include "sieve-code.h"
+#include "sieve-actions.h"
+#include "sieve-result.h"
+#include "sieve-generator.h"
+
+#include "ext-mailbox-common.h"
+
+/*
+ * Tagged argument
+ */
+
+static bool
+tag_mailbox_create_validate(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool
+tag_mailbox_create_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg,
+ struct sieve_command *context);
+
+const struct sieve_argument_def mailbox_create_tag = {
+ .identifier = "create",
+ .validate = tag_mailbox_create_validate,
+ .generate = tag_mailbox_create_generate
+};
+
+/*
+ * Side effect
+ */
+
+static void
+seff_mailbox_create_print(const struct sieve_side_effect *seffect,
+ const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep);
+static int
+seff_mailbox_create_pre_execute(const struct sieve_side_effect *seffect,
+ const struct sieve_action_exec_env *aenv,
+ void *tr_context,
+ void **se_tr_context ATTR_UNUSED);
+
+const struct sieve_side_effect_def mailbox_create_side_effect = {
+ SIEVE_OBJECT("create", &mailbox_create_operand, 0),
+ .precedence = 100,
+ .to_action = &act_store,
+ .print = seff_mailbox_create_print,
+ .pre_execute = seff_mailbox_create_pre_execute
+};
+
+/*
+ * Operand
+ */
+
+static const struct sieve_extension_objects ext_side_effects =
+ SIEVE_EXT_DEFINE_SIDE_EFFECT(mailbox_create_side_effect);
+
+const struct sieve_operand_def mailbox_create_operand = {
+ .name = "create operand",
+ .ext_def = &mailbox_extension,
+ .class = &sieve_side_effect_operand_class,
+ .interface = &ext_side_effects
+};
+
+/*
+ * Tag validation
+ */
+
+static bool
+tag_mailbox_create_validate(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd ATTR_UNUSED)
+{
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+tag_mailbox_create_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg,
+ struct sieve_command *context ATTR_UNUSED)
+{
+ if (sieve_ast_argument_type(arg) != SAAT_TAG)
+ return FALSE;
+
+ sieve_opr_side_effect_emit(cgenv->sblock, arg->argument->ext,
+ &mailbox_create_side_effect);
+ return TRUE;
+}
+
+/*
+ * Side effect implementation
+ */
+
+static void
+seff_mailbox_create_print(const struct sieve_side_effect *seffect ATTR_UNUSED,
+ const struct sieve_action *action ATTR_UNUSED,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep ATTR_UNUSED)
+{
+ sieve_result_seffect_printf(
+ rpenv, "create mailbox if it does not exist");
+}
+
+static int
+seff_mailbox_create_pre_execute(
+ const struct sieve_side_effect *seffect ATTR_UNUSED,
+ const struct sieve_action_exec_env *aenv, void *tr_context,
+ void **se_tr_context ATTR_UNUSED)
+{
+ struct act_store_transaction *trans = tr_context;
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct mailbox *box = trans->box;
+
+ /* Check whether creation is necessary */
+ if (box == NULL || trans->disabled)
+ return SIEVE_EXEC_OK;
+
+ eenv->exec_status->last_storage = mailbox_get_storage(box);
+
+ /* Open the mailbox (may already be open) */
+ if (trans->error_code == MAIL_ERROR_NONE) {
+ if (mailbox_open(box) < 0)
+ sieve_act_store_get_storage_error(aenv, trans);
+ }
+
+ /* Check whether creation has a chance of working */
+ switch (trans->error_code) {
+ case MAIL_ERROR_NONE:
+ return SIEVE_EXEC_OK;
+ case MAIL_ERROR_NOTFOUND:
+ break;
+ case MAIL_ERROR_TEMP:
+ return SIEVE_EXEC_TEMP_FAILURE;
+ default:
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ trans->error = NULL;
+ trans->error_code = MAIL_ERROR_NONE;
+
+ /* Create mailbox */
+ if (mailbox_create(box, NULL, FALSE) < 0) {
+ sieve_act_store_get_storage_error(aenv, trans);
+ if (trans->error_code == MAIL_ERROR_EXISTS) {
+ trans->error = NULL;
+ trans->error_code = MAIL_ERROR_NONE;
+ } else {
+ return (trans->error_code == MAIL_ERROR_TEMP ?
+ SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE);
+ }
+ }
+
+ /* Subscribe to it if necessary */
+ if (eenv->scriptenv->mailbox_autosubscribe) {
+ (void)mailbox_list_set_subscribed(
+ mailbox_get_namespace(box)->list,
+ mailbox_get_name(box), TRUE);
+ }
+
+ /* Try opening again */
+ if (mailbox_open(box) < 0) {
+ /* Failed definitively */
+ sieve_act_store_get_storage_error(aenv, trans);
+ return (trans->error_code == MAIL_ERROR_TEMP ?
+ SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE);
+ }
+ return SIEVE_EXEC_OK;
+}
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c b/pigeonhole/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c
new file mode 100644
index 0000000..e0fd33d
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c
@@ -0,0 +1,284 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+
+#include "sieve-common.h"
+#include "sieve-actions.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-mailbox-common.h"
+
+/*
+ * Mailboxexists command
+ *
+ * Syntax:
+ * mailboxexists <mailbox-names: string-list>
+ */
+
+static bool
+tst_mailboxexists_validate(struct sieve_validator *valdtr,
+ struct sieve_command *tst);
+static bool
+tst_mailboxexists_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+const struct sieve_command_def mailboxexists_test = {
+ .identifier = "mailboxexists",
+ .type = SCT_TEST,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = tst_mailboxexists_validate,
+ .generate = tst_mailboxexists_generate,
+};
+
+/*
+ * Mailboxexists operation
+ */
+
+static bool
+tst_mailboxexists_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+tst_mailboxexists_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def mailboxexists_operation = {
+ .mnemonic = "MAILBOXEXISTS",
+ .ext_def = &mailbox_extension,
+ .dump = tst_mailboxexists_operation_dump,
+ .execute = tst_mailboxexists_operation_execute,
+};
+
+/*
+ * Test validation
+ */
+
+struct _validate_context {
+ struct sieve_validator *valdtr;
+ struct sieve_command *tst;
+};
+
+static int
+tst_mailboxexists_mailbox_validate(void *context,
+ struct sieve_ast_argument *arg)
+{
+ struct _validate_context *valctx =
+ (struct _validate_context *)context;
+
+ if (sieve_argument_is_string_literal(arg)) {
+ const char *mailbox = sieve_ast_argument_strc(arg), *error;
+
+ if (!sieve_mailbox_check_name(mailbox, &error)) {
+ sieve_argument_validate_warning(
+ valctx->valdtr, arg, "%s test: "
+ "invalid mailbox name `%s' specified: %s",
+ sieve_command_identifier(valctx->tst),
+ str_sanitize(mailbox, 256), error);
+ }
+ }
+
+ return 1;
+}
+
+static bool
+tst_mailboxexists_validate(struct sieve_validator *valdtr,
+ struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ struct sieve_ast_argument *aarg;
+ struct _validate_context valctx;
+
+ if (!sieve_validate_positional_argument(
+ valdtr, tst, arg, "mailbox-names", 1, SAAT_STRING_LIST))
+ return FALSE;
+
+ if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
+ return FALSE;
+
+ aarg = arg;
+ i_zero(&valctx);
+ valctx.valdtr = valdtr;
+ valctx.tst = tst;
+
+ return (sieve_ast_stringlist_map(
+ &aarg, (void*)&valctx,
+ tst_mailboxexists_mailbox_validate) >= 0);
+}
+
+/*
+ * Test generation
+ */
+
+static bool
+tst_mailboxexists_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *tst)
+{
+ sieve_operation_emit(cgenv->sblock, tst->ext, &mailboxexists_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, tst, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+tst_mailboxexists_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ sieve_code_dumpf(denv, "MAILBOXEXISTS");
+ sieve_code_descend(denv);
+
+ return sieve_opr_stringlist_dump(denv, address, "mailbox-names");
+}
+
+/*
+ * Code execution
+ */
+
+static int
+tst_mailboxexists_test_mailbox(const struct sieve_runtime_env *renv,
+ const char *mailbox, bool trace,
+ bool *all_exist_r)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct mailbox *box;
+ const char *error;
+
+ /* Check validity of mailbox name */
+ if (!sieve_mailbox_check_name(mailbox, &error)) {
+ sieve_runtime_warning(
+ renv, NULL, "mailboxexists test: "
+ "invalid mailbox name `%s' specified: %s",
+ str_sanitize(mailbox, 256), error);
+ *all_exist_r = FALSE;
+ return SIEVE_EXEC_OK;
+ }
+
+ /* Open the box */
+ box = mailbox_alloc_for_user(eenv->scriptenv->user,
+ mailbox,
+ MAILBOX_FLAG_POST_SESSION);
+
+ if (mailbox_open(box) < 0) {
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0,
+ "mailbox `%s' cannot be opened",
+ str_sanitize(mailbox, 80));
+ }
+ mailbox_free(&box);
+ *all_exist_r = FALSE;
+ return SIEVE_EXEC_OK;
+ }
+
+ /* Also fail when it is readonly */
+ if (mailbox_is_readonly(box)) {
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0,
+ "mailbox `%s' is read-only",
+ str_sanitize(mailbox, 80));
+ }
+ mailbox_free(&box);
+ *all_exist_r = FALSE;
+ return SIEVE_EXEC_OK;
+ }
+
+ /* FIXME: check acl for 'p' or 'i' ACL permissions as
+ required by RFC */
+
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox `%s' exists",
+ str_sanitize(mailbox, 80));
+ }
+
+ /* Close mailbox */
+ mailbox_free(&box);
+ return SIEVE_EXEC_OK;
+}
+
+static int
+tst_mailboxexists_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct sieve_stringlist *mailbox_names;
+ string_t *mailbox_item;
+ bool trace = FALSE;
+ bool all_exist = TRUE;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Read notify uris */
+ ret = sieve_opr_stringlist_read(renv, address, "mailbox-names",
+ &mailbox_names);
+ if (ret <= 0)
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS)) {
+ sieve_runtime_trace(renv, 0, "mailboxexists test");
+ sieve_runtime_trace_descend(renv);
+
+ trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
+ }
+
+ if (eenv->scriptenv->user == NULL) {
+ sieve_runtime_trace(renv, 0, "no mail user; yield true");
+ sieve_interpreter_set_test_result(renv->interp, TRUE);
+ return SIEVE_EXEC_OK;
+ }
+
+ mailbox_item = NULL;
+ while (all_exist &&
+ (ret = sieve_stringlist_next_item(mailbox_names,
+ &mailbox_item)) > 0) {
+ const char *mailbox = str_c(mailbox_item);
+
+ ret = tst_mailboxexists_test_mailbox(renv, mailbox,
+ trace, &all_exist);
+ if (ret <= 0)
+ return ret;
+ }
+
+ if (ret < 0) {
+ sieve_runtime_trace_error(
+ renv, "invalid mailbox name item");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if (trace) {
+ if (all_exist) {
+ sieve_runtime_trace(renv, 0,
+ "all mailboxes are available");
+ } else {
+ sieve_runtime_trace(renv, 0,
+ "some mailboxes are unavailable");
+ }
+ }
+
+ sieve_interpreter_set_test_result(renv->interp, all_exist);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.am b/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.am
new file mode 100644
index 0000000..6d69ba8
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.am
@@ -0,0 +1,24 @@
+noinst_LTLIBRARIES = libsieve_ext_metadata.la
+
+libsieve_ext_metadata_la_LDFLAGS = -module -avoid-version
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../variables \
+ $(LIBDOVECOT_INCLUDE) \
+ $(LIBDOVECOT_STORAGE_INCLUDE)
+
+tests = \
+ tst-metadata.c \
+ tst-metadataexists.c
+
+extensions = \
+ ext-metadata.c
+
+libsieve_ext_metadata_la_SOURCES = \
+ $(tests) \
+ $(extensions)
+
+noinst_HEADERS = \
+ ext-metadata-common.h
+
diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.in b/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.in
new file mode 100644
index 0000000..0b95819
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/metadata/Makefile.in
@@ -0,0 +1,705 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/metadata
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_metadata_la_LIBADD =
+am__objects_1 = tst-metadata.lo tst-metadataexists.lo
+am__objects_2 = ext-metadata.lo
+am_libsieve_ext_metadata_la_OBJECTS = $(am__objects_1) \
+ $(am__objects_2)
+libsieve_ext_metadata_la_OBJECTS = \
+ $(am_libsieve_ext_metadata_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libsieve_ext_metadata_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libsieve_ext_metadata_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-metadata.Plo \
+ ./$(DEPDIR)/tst-metadata.Plo \
+ ./$(DEPDIR)/tst-metadataexists.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_metadata_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_metadata_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_metadata.la
+libsieve_ext_metadata_la_LDFLAGS = -module -avoid-version
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../variables \
+ $(LIBDOVECOT_INCLUDE) \
+ $(LIBDOVECOT_STORAGE_INCLUDE)
+
+tests = \
+ tst-metadata.c \
+ tst-metadataexists.c
+
+extensions = \
+ ext-metadata.c
+
+libsieve_ext_metadata_la_SOURCES = \
+ $(tests) \
+ $(extensions)
+
+noinst_HEADERS = \
+ ext-metadata-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/metadata/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/metadata/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_metadata.la: $(libsieve_ext_metadata_la_OBJECTS) $(libsieve_ext_metadata_la_DEPENDENCIES) $(EXTRA_libsieve_ext_metadata_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsieve_ext_metadata_la_LINK) $(libsieve_ext_metadata_la_OBJECTS) $(libsieve_ext_metadata_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-metadata.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-metadata.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-metadataexists.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-metadata.Plo
+ -rm -f ./$(DEPDIR)/tst-metadata.Plo
+ -rm -f ./$(DEPDIR)/tst-metadataexists.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-metadata.Plo
+ -rm -f ./$(DEPDIR)/tst-metadata.Plo
+ -rm -f ./$(DEPDIR)/tst-metadataexists.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata-common.h b/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata-common.h
new file mode 100644
index 0000000..a4b25a0
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata-common.h
@@ -0,0 +1,40 @@
+#ifndef EXT_METADATA_COMMON_H
+#define EXT_METADATA_COMMON_H
+
+#include "lib.h"
+#include "mail-storage.h"
+#include "imap-metadata.h"
+
+#include "sieve-common.h"
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def mboxmetadata_extension;
+extern const struct sieve_extension_def servermetadata_extension;
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def metadata_test;
+extern const struct sieve_command_def servermetadata_test;
+extern const struct sieve_command_def metadataexists_test;
+extern const struct sieve_command_def servermetadataexists_test;
+
+/*
+ * Operations
+ */
+
+enum ext_metadata_opcode {
+ EXT_METADATA_OPERATION_METADATA,
+ EXT_METADATA_OPERATION_METADATAEXISTS
+};
+
+extern const struct sieve_operation_def metadata_operation;
+extern const struct sieve_operation_def servermetadata_operation;
+extern const struct sieve_operation_def metadataexists_operation;
+extern const struct sieve_operation_def servermetadataexists_operation;
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata.c b/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata.c
new file mode 100644
index 0000000..882116b
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/metadata/ext-metadata.c
@@ -0,0 +1,83 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-binary.h"
+
+#include "sieve-validator.h"
+#include "sieve-interpreter.h"
+
+#include "ext-metadata-common.h"
+
+/*
+ * Extension mboxmetadata
+ * -----------------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5490; Section 3
+ * Implementation: skeleton
+ * Status: development
+ *
+ */
+
+const struct sieve_operation_def *mboxmetadata_operations[] = {
+ &metadata_operation,
+ &metadataexists_operation,
+};
+
+static bool ext_mboxmetadata_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def mboxmetadata_extension = {
+ .name = "mboxmetadata",
+ .validator_load = ext_mboxmetadata_validator_load,
+ SIEVE_EXT_DEFINE_OPERATIONS(mboxmetadata_operations)
+};
+
+static bool ext_mboxmetadata_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ sieve_validator_register_command(valdtr, ext, &metadata_test);
+ sieve_validator_register_command(valdtr, ext, &metadataexists_test);
+
+ return TRUE;
+}
+
+/*
+ * Extension servermetadata
+ * -----------------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5490; Section 4
+ * Implementation: skeleton
+ * Status: development
+ *
+ */
+
+const struct sieve_operation_def *servermetadata_operations[] = {
+ &servermetadata_operation,
+ &servermetadataexists_operation,
+};
+
+static bool ext_servermetadata_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def servermetadata_extension = {
+ .name = "servermetadata",
+ .validator_load = ext_servermetadata_validator_load,
+ SIEVE_EXT_DEFINE_OPERATIONS(servermetadata_operations)
+};
+
+static bool ext_servermetadata_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ sieve_validator_register_command(valdtr, ext, &servermetadata_test);
+ sieve_validator_register_command(valdtr, ext, &servermetadataexists_test);
+
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadata.c b/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadata.c
new file mode 100644
index 0000000..2e14535
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadata.c
@@ -0,0 +1,433 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "istream.h"
+
+#include "sieve-common.h"
+#include "sieve-limits.h"
+#include "sieve-commands.h"
+#include "sieve-actions.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-metadata-common.h"
+
+#define TST_METADATA_MAX_MATCH_SIZE SIEVE_MAX_STRING_LEN
+
+/*
+ * Test definitions
+ */
+
+/* Forward declarations */
+
+static bool
+tst_metadata_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool
+tst_metadata_validate(struct sieve_validator *valdtr,
+ struct sieve_command *tst);
+static bool
+tst_metadata_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd);
+
+/* Metadata test
+ *
+ * Syntax:
+ * metadata [MATCH-TYPE] [COMPARATOR]
+ * <mailbox: string>
+ * <annotation-name: string> <key-list: string-list>
+ */
+
+const struct sieve_command_def metadata_test = {
+ .identifier = "metadata",
+ .type = SCT_TEST,
+ .positional_args = 3,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_metadata_registered,
+ .validate = tst_metadata_validate,
+ .generate = tst_metadata_generate,
+};
+
+/* Servermetadata test
+ *
+ * Syntax:
+ * servermetadata [MATCH-TYPE] [COMPARATOR]
+ * <annotation-name: string> <key-list: string-list>
+ */
+
+const struct sieve_command_def servermetadata_test = {
+ .identifier = "servermetadata",
+ .type = SCT_TEST,
+ .positional_args = 2,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_metadata_registered,
+ .validate = tst_metadata_validate,
+ .generate = tst_metadata_generate,
+};
+
+/*
+ * Opcode definitions
+ */
+
+static bool
+tst_metadata_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+tst_metadata_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+/* Metadata operation */
+
+const struct sieve_operation_def metadata_operation = {
+ .mnemonic = "METADATA",
+ .ext_def = &mboxmetadata_extension,
+ .code = EXT_METADATA_OPERATION_METADATA,
+ .dump = tst_metadata_operation_dump,
+ .execute = tst_metadata_operation_execute,
+};
+
+/* Servermetadata operation */
+
+const struct sieve_operation_def servermetadata_operation = {
+ .mnemonic = "SERVERMETADATA",
+ .ext_def = &servermetadata_extension,
+ .code = EXT_METADATA_OPERATION_METADATA,
+ .dump = tst_metadata_operation_dump,
+ .execute = tst_metadata_operation_execute,
+};
+
+/*
+ * Test registration
+ */
+
+static bool
+tst_metadata_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_command_registration *cmd_reg)
+{
+ /* The order of these is not significant */
+ sieve_comparators_link_tag(valdtr, cmd_reg,
+ SIEVE_MATCH_OPT_COMPARATOR);
+ sieve_match_types_link_tags(valdtr, cmd_reg,
+ SIEVE_MATCH_OPT_MATCH_TYPE);
+ return TRUE;
+}
+
+/*
+ * Test validation
+ */
+
+static bool
+tst_metadata_validate(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ const struct sieve_match_type mcht_default =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ const struct sieve_comparator cmp_default =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+ unsigned int arg_index = 1;
+ const char *error;
+
+ /* mailbox */
+ if (sieve_command_is(tst, metadata_test)) {
+ if (!sieve_validate_positional_argument(
+ valdtr, tst, arg, "mailbox", arg_index++,
+ SAAT_STRING))
+ return FALSE;
+
+ if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
+ return FALSE;
+
+ /* Check name validity when mailbox argument is not a variable
+ */
+ if (sieve_argument_is_string_literal(arg)) {
+ const char *mailbox = sieve_ast_argument_strc(arg);
+ const char *error;
+
+ if (!sieve_mailbox_check_name(mailbox, &error)) {
+ sieve_argument_validate_warning(
+ valdtr, arg, "%s test: "
+ "invalid mailbox name `%s' specified: %s",
+ sieve_command_identifier(tst),
+ str_sanitize(mailbox, 256), error);
+ }
+ }
+ arg = sieve_ast_argument_next(arg);
+ }
+
+ /* annotation-name */
+ if (!sieve_validate_positional_argument(valdtr, tst, arg,
+ "annotation-name", arg_index++,
+ SAAT_STRING))
+ return FALSE;
+
+ if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
+ return FALSE;
+
+ if (sieve_argument_is_string_literal(arg)) {
+ string_t *aname = sieve_ast_argument_str(arg);
+
+ if (!imap_metadata_verify_entry_name(str_c(aname), &error)) {
+ sieve_argument_validate_warning(
+ valdtr, arg, "%s test: "
+ "specified annotation name `%s' is invalid: %s",
+ sieve_command_identifier(tst),
+ str_sanitize(str_c(aname), 256),
+ sieve_error_from_external(error));
+ }
+ }
+
+ arg = sieve_ast_argument_next(arg);
+
+ /* key-list */
+ if (!sieve_validate_positional_argument(valdtr, tst, arg,
+ "key-list", arg_index++,
+ SAAT_STRING_LIST))
+ return FALSE;
+
+ if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
+ return FALSE;
+
+ /* Validate the key argument to a specified match type */
+ return sieve_match_type_validate(valdtr, tst, arg,
+ &mcht_default, &cmp_default);
+}
+
+/*
+ * Test generation
+ */
+
+static bool
+tst_metadata_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *tst)
+{
+ if (sieve_command_is(tst, metadata_test)) {
+ sieve_operation_emit(cgenv->sblock, tst->ext,
+ &metadata_operation);
+ } else if (sieve_command_is(tst, servermetadata_test)) {
+ sieve_operation_emit(cgenv->sblock, tst->ext,
+ &servermetadata_operation);
+ } else {
+ i_unreached();
+ }
+
+ /* Generate arguments */
+ if (!sieve_generate_arguments(cgenv, tst, NULL))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+tst_metadata_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ bool metadata = sieve_operation_is(denv->oprtn, metadata_operation);
+
+ if (metadata)
+ sieve_code_dumpf(denv, "METADATA");
+ else
+ sieve_code_dumpf(denv, "SERVERMETADATA");
+
+ sieve_code_descend(denv);
+
+ /* Handle any optional arguments */
+ if (sieve_match_opr_optional_dump(denv, address, NULL) != 0)
+ return FALSE;
+ if (metadata && !sieve_opr_string_dump(denv, address, "mailbox"))
+ return FALSE;
+
+ return (sieve_opr_string_dump(denv, address, "annotation-name") &&
+ sieve_opr_stringlist_dump(denv, address, "key list"));
+}
+
+/*
+ * Code execution
+ */
+
+static int
+tst_metadata_get_annotation(const struct sieve_runtime_env *renv,
+ const char *mailbox, const char *aname,
+ const char **annotation_r)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct mail_user *user = eenv->scriptenv->user;
+ struct mailbox *box;
+ struct imap_metadata_transaction *imtrans;
+ struct mail_attribute_value avalue;
+ int status, ret;
+
+ *annotation_r = NULL;
+
+ if (user == NULL)
+ return SIEVE_EXEC_OK;
+
+ if (mailbox != NULL) {
+ struct mail_namespace *ns;
+ ns = mail_namespace_find(user->namespaces, mailbox);
+ box = mailbox_alloc(ns->list, mailbox, 0);
+ imtrans = imap_metadata_transaction_begin(box);
+ } else {
+ box = NULL;
+ imtrans = imap_metadata_transaction_begin_server(user);
+ }
+
+ status = SIEVE_EXEC_OK;
+ ret = imap_metadata_get(imtrans, aname, &avalue);
+ if (ret < 0) {
+ enum mail_error error_code;
+ const char *error;
+
+ error = imap_metadata_transaction_get_last_error(
+ imtrans, &error_code);
+
+ sieve_runtime_error(
+ renv, NULL, "%s test: "
+ "failed to retrieve annotation `%s': %s%s",
+ (mailbox != NULL ? "metadata" : "servermetadata"),
+ str_sanitize(aname, 256),
+ sieve_error_from_external(error),
+ (error_code == MAIL_ERROR_TEMP ?
+ " (temporary failure)" : ""));
+
+ status = (error_code == MAIL_ERROR_TEMP ?
+ SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE);
+
+ } else if (avalue.value != NULL) {
+ *annotation_r = avalue.value;
+ }
+ (void)imap_metadata_transaction_commit(&imtrans, NULL, NULL);
+ if (box != NULL)
+ mailbox_free(&box);
+ return status;
+}
+
+static int
+tst_metadata_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ bool metadata = sieve_operation_is(renv->oprtn, metadata_operation);
+ struct sieve_match_type mcht =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ struct sieve_comparator cmp =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+ string_t *mailbox, *aname;
+ struct sieve_stringlist *value_list, *key_list;
+ const char *annotation = NULL, *error;
+ int match, ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Handle match-type and comparator operands */
+ if (sieve_match_opr_optional_read(renv, address, NULL,
+ &ret, &cmp, &mcht) < 0)
+ return ret;
+
+ /* Read mailbox */
+ if (metadata) {
+ ret = sieve_opr_string_read(renv, address, "mailbox", &mailbox);
+ if (ret <= 0)
+ return ret;
+ }
+
+ /* Read annotation-name */
+ ret = sieve_opr_string_read(renv, address, "annotation-name", &aname);
+ if (ret <= 0)
+ return ret;
+
+ /* Read key-list */
+ ret = sieve_opr_stringlist_read(renv, address, "key-list", &key_list);
+ if (ret <= 0)
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ if (metadata) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "metadata test");
+ } else {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "servermetadata test");
+ }
+ sieve_runtime_trace_descend(renv);
+
+ if (!imap_metadata_verify_entry_name(str_c(aname), &error)) {
+ sieve_runtime_warning(
+ renv, NULL, "%s test: "
+ "specified annotation name `%s' is invalid: %s",
+ (metadata ? "metadata" : "servermetadata"),
+ str_sanitize(str_c(aname), 256),
+ sieve_error_from_external(error));
+ sieve_interpreter_set_test_result(renv->interp, FALSE);
+ return SIEVE_EXEC_OK;
+ }
+
+ if (metadata) {
+ if (!sieve_mailbox_check_name(str_c(mailbox), &error)) {
+ sieve_runtime_warning(
+ renv, NULL, "metadata test: "
+ "invalid mailbox name `%s' specified: %s",
+ str_sanitize(str_c(mailbox), 256), error);
+ sieve_interpreter_set_test_result(renv->interp, FALSE);
+ return SIEVE_EXEC_OK;
+ }
+
+ sieve_runtime_trace(
+ renv, SIEVE_TRLVL_TESTS,
+ "retrieving annotation `%s' from mailbox `%s'",
+ str_sanitize(str_c(aname), 256),
+ str_sanitize(str_c(mailbox), 80));
+ } else {
+ sieve_runtime_trace(
+ renv, SIEVE_TRLVL_TESTS,
+ "retrieving server annotation `%s'",
+ str_sanitize(str_c(aname), 256));
+ }
+
+ /* Get annotation */
+ ret = tst_metadata_get_annotation(renv,
+ (metadata ? str_c(mailbox) : NULL),
+ str_c(aname), &annotation);
+ if (ret == SIEVE_EXEC_OK) {
+ /* Perform match */
+ if (annotation != NULL) {
+ /* Create value stringlist */
+ value_list = sieve_single_stringlist_create_cstr(
+ renv, annotation, FALSE);
+
+ /* Perform match */
+ match = sieve_match(renv, &mcht, &cmp,
+ value_list, key_list, &ret);
+ if (ret < 0)
+ return ret;
+ } else {
+ match = 0;
+ }
+ }
+
+ /* Set test result for subsequent conditional jump */
+ if (ret == SIEVE_EXEC_OK)
+ sieve_interpreter_set_test_result(renv->interp, match > 0);
+ return ret;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadataexists.c b/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadataexists.c
new file mode 100644
index 0000000..e11d692
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/metadata/tst-metadataexists.c
@@ -0,0 +1,431 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-actions.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-metadata-common.h"
+
+/*
+ * Command definitions
+ */
+
+/* Forward declarations */
+
+static bool
+tst_metadataexists_validate(struct sieve_validator *valdtr,
+ struct sieve_command *tst);
+static bool
+tst_metadataexists_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+/* Metadataexists command
+ *
+ * Syntax:
+ * metadataexists <mailbox: string> <annotation-names: string-list>
+ */
+
+static bool
+tst_metadataexists_validate(struct sieve_validator *valdtr,
+ struct sieve_command *tst);
+static bool
+tst_metadataexists_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+const struct sieve_command_def metadataexists_test = {
+ .identifier = "metadataexists",
+ .type = SCT_TEST,
+ .positional_args = 2,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = tst_metadataexists_validate,
+ .generate = tst_metadataexists_generate,
+};
+
+/* Servermetadataexists command
+ *
+ * Syntax:
+ * servermetadataexists <annotation-names: string-list>
+ */
+
+const struct sieve_command_def servermetadataexists_test = {
+ .identifier = "servermetadataexists",
+ .type = SCT_TEST,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = tst_metadataexists_validate,
+ .generate = tst_metadataexists_generate,
+};
+
+/*
+ * Opcode definitions
+ */
+
+static bool
+tst_metadataexists_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+tst_metadataexists_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+/* Metadata operation */
+
+const struct sieve_operation_def metadataexists_operation = {
+ .mnemonic = "METADATAEXISTS",
+ .ext_def = &mboxmetadata_extension,
+ .code = EXT_METADATA_OPERATION_METADATAEXISTS,
+ .dump = tst_metadataexists_operation_dump,
+ .execute = tst_metadataexists_operation_execute,
+};
+
+/* Mailboxexists operation */
+
+const struct sieve_operation_def servermetadataexists_operation = {
+ .mnemonic = "SERVERMETADATAEXISTS",
+ .ext_def = &servermetadata_extension,
+ .code = EXT_METADATA_OPERATION_METADATAEXISTS,
+ .dump = tst_metadataexists_operation_dump,
+ .execute = tst_metadataexists_operation_execute,
+};
+
+/*
+ * Test validation
+ */
+
+struct _validate_context {
+ struct sieve_validator *valdtr;
+ struct sieve_command *tst;
+};
+
+static int
+tst_metadataexists_annotation_validate(void *context,
+ struct sieve_ast_argument *arg)
+{
+ struct _validate_context *valctx =
+ (struct _validate_context *)context;
+
+ if (sieve_argument_is_string_literal(arg)) {
+ const char *aname = sieve_ast_strlist_strc(arg);
+ const char *error;
+
+ if (!imap_metadata_verify_entry_name(aname, &error)) {
+ sieve_argument_validate_warning(
+ valctx->valdtr, arg, "%s test: "
+ "specified annotation name `%s' is invalid: %s",
+ sieve_command_identifier(valctx->tst),
+ str_sanitize(aname, 256),
+ sieve_error_from_external(error));
+ }
+ }
+ return 1; /* Can't check at compile time */
+}
+
+static bool
+tst_metadataexists_validate(struct sieve_validator *valdtr,
+ struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ struct sieve_ast_argument *aarg;
+ struct _validate_context valctx;
+ unsigned int arg_index = 1;
+
+ if (sieve_command_is(tst, metadataexists_test)) {
+ if (!sieve_validate_positional_argument(valdtr, tst, arg,
+ "mailbox", arg_index++,
+ SAAT_STRING))
+ return FALSE;
+
+ if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
+ return FALSE;
+
+ /* Check name validity when mailbox argument is not a variable */
+ if (sieve_argument_is_string_literal(arg)) {
+ const char *mailbox = sieve_ast_argument_strc(arg);
+ const char *error;
+
+ if (!sieve_mailbox_check_name(mailbox, &error)) {
+ sieve_argument_validate_warning(
+ valdtr, arg, "%s test: "
+ "invalid mailbox name `%s' specified: %s",
+ sieve_command_identifier(tst),
+ str_sanitize(mailbox, 256), error);
+ }
+ }
+ arg = sieve_ast_argument_next(arg);
+ }
+
+ if (!sieve_validate_positional_argument(valdtr, tst, arg,
+ "annotation-names", arg_index++,
+ SAAT_STRING_LIST))
+ return FALSE;
+ if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
+ return FALSE;
+
+ aarg = arg;
+ i_zero(&valctx);
+ valctx.valdtr = valdtr;
+ valctx.tst = tst;
+
+ return (sieve_ast_stringlist_map(
+ &aarg, (void*)&valctx,
+ tst_metadataexists_annotation_validate) >= 0);
+}
+
+/*
+ * Test generation
+ */
+
+static bool
+tst_metadataexists_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *tst)
+{
+ if (sieve_command_is(tst, metadataexists_test)) {
+ sieve_operation_emit(cgenv->sblock, tst->ext,
+ &metadataexists_operation);
+ } else if (sieve_command_is(tst, servermetadataexists_test)) {
+ sieve_operation_emit(cgenv->sblock, tst->ext,
+ &servermetadataexists_operation);
+ } else {
+ i_unreached();
+ }
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, tst, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+tst_metadataexists_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ bool metadata = sieve_operation_is(denv->oprtn,
+ metadataexists_operation);
+
+ if (metadata)
+ sieve_code_dumpf(denv, "METADATAEXISTS");
+ else
+ sieve_code_dumpf(denv, "SERVERMETADATAEXISTS");
+
+ sieve_code_descend(denv);
+
+ if (metadata && !sieve_opr_string_dump(denv, address, "mailbox"))
+ return FALSE;
+
+ return sieve_opr_stringlist_dump(denv, address, "annotation-names");
+}
+
+/*
+ * Code execution
+ */
+
+static int
+tst_metadataexists_check_annotation(const struct sieve_runtime_env *renv,
+ struct imap_metadata_transaction *imtrans,
+ const char *mailbox, const char *aname,
+ bool *all_exist_r)
+{
+ struct mail_attribute_value avalue;
+ const char *error;
+ int ret;
+
+ if (!imap_metadata_verify_entry_name(aname, &error)) {
+ sieve_runtime_warning(
+ renv, NULL, "%s test: "
+ "specified annotation name `%s' is invalid: %s",
+ (mailbox != NULL ?
+ "metadataexists" : "servermetadataexists"),
+ str_sanitize(aname, 256),
+ sieve_error_from_external(error));
+ *all_exist_r = FALSE;
+ return SIEVE_EXEC_OK;
+ }
+
+ ret = imap_metadata_get(imtrans, aname, &avalue);
+ if (ret < 0) {
+ enum mail_error error_code;
+ const char *error;
+
+ error = imap_metadata_transaction_get_last_error(
+ imtrans, &error_code);
+ sieve_runtime_error(
+ renv, NULL, "%s test: "
+ "failed to retrieve annotation `%s': %s%s",
+ (mailbox != NULL ?
+ "metadataexists" : "servermetadataexists"),
+ str_sanitize(aname, 256),
+ sieve_error_from_external(error),
+ (error_code == MAIL_ERROR_TEMP ?
+ " (temporary failure)" : ""));
+
+ *all_exist_r = FALSE;
+ return (error_code == MAIL_ERROR_TEMP ?
+ SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE);
+ }
+ if (avalue.value == NULL && avalue.value_stream == NULL) {
+ sieve_runtime_trace(renv, 0,
+ "annotation `%s': not found", aname);
+ *all_exist_r = FALSE;
+ }
+
+ sieve_runtime_trace(renv, 0, "annotation `%s': found", aname);
+ return SIEVE_EXEC_OK;
+}
+
+static int
+tst_metadataexists_check_annotations(const struct sieve_runtime_env *renv,
+ const char *mailbox,
+ struct sieve_stringlist *anames,
+ bool *all_exist_r)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct mail_user *user = eenv->scriptenv->user;
+ struct mailbox *box = NULL;
+ struct imap_metadata_transaction *imtrans;
+ string_t *aname;
+ bool all_exist = TRUE;
+ int ret, sret, status;
+
+ *all_exist_r = FALSE;
+
+ if (user == NULL)
+ return SIEVE_EXEC_OK;
+
+ if (mailbox != NULL) {
+ struct mail_namespace *ns;
+ ns = mail_namespace_find(user->namespaces, mailbox);
+ box = mailbox_alloc(ns->list, mailbox, 0);
+ imtrans = imap_metadata_transaction_begin(box);
+ } else {
+ imtrans = imap_metadata_transaction_begin_server(user);
+ }
+
+ if (mailbox != NULL) {
+ sieve_runtime_trace(
+ renv, SIEVE_TRLVL_TESTS,
+ "checking annotations of mailbox `%s':",
+ str_sanitize(mailbox, 80));
+ } else {
+ sieve_runtime_trace(
+ renv, SIEVE_TRLVL_TESTS,
+ "checking server annotations");
+ }
+
+ aname = NULL;
+ status = SIEVE_EXEC_OK;
+ while (all_exist &&
+ (sret = sieve_stringlist_next_item(anames, &aname)) > 0) {
+ ret = tst_metadataexists_check_annotation(
+ renv, imtrans, mailbox, str_c(aname), &all_exist);
+ if (ret <= 0) {
+ status = ret;
+ break;
+ }
+ }
+
+ if (sret < 0) {
+ sieve_runtime_trace_error(
+ renv, "invalid annotation name stringlist item");
+ status = SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ (void)imap_metadata_transaction_commit(&imtrans, NULL, NULL);
+ if (box != NULL)
+ mailbox_free(&box);
+
+ *all_exist_r = all_exist;
+ return status;
+}
+
+static int
+tst_metadataexists_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ bool metadata = sieve_operation_is(renv->oprtn,
+ metadataexists_operation);
+ struct sieve_stringlist *anames;
+ string_t *mailbox;
+ bool trace = FALSE, all_exist = TRUE;
+ const char *error;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Read mailbox */
+ if (metadata) {
+ ret = sieve_opr_string_read(renv, address, "mailbox", &mailbox);
+ if (ret <= 0)
+ return ret;
+ }
+
+ /* Read annotation names */
+ ret = sieve_opr_stringlist_read(renv, address, "annotation-names",
+ &anames);
+ if (ret <= 0)
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ if (metadata &&
+ !sieve_mailbox_check_name(str_c(mailbox), &error)) {
+ sieve_runtime_warning(
+ renv, NULL, "metadataexists test: "
+ "invalid mailbox name `%s' specified: %s",
+ str_sanitize(str_c(mailbox), 256), error);
+ sieve_interpreter_set_test_result(renv->interp, FALSE);
+ return SIEVE_EXEC_OK;
+ }
+
+ if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS)) {
+ if (metadata) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "metadataexists test");
+ } else {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "servermetadataexists test");
+ }
+
+ sieve_runtime_trace_descend(renv);
+
+ trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
+ }
+
+ ret = tst_metadataexists_check_annotations(
+ renv, (metadata ? str_c(mailbox) : NULL), anames, &all_exist);
+ if (ret <= 0)
+ return ret;
+
+ if (trace) {
+ if (all_exist) {
+ sieve_runtime_trace(renv, 0,
+ "all annotations exist");
+ } else {
+ sieve_runtime_trace(renv, 0,
+ "some annotations do not exist");
+ }
+ }
+
+ sieve_interpreter_set_test_result(renv->interp, all_exist);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/Makefile.am b/pigeonhole/src/lib-sieve/plugins/mime/Makefile.am
new file mode 100644
index 0000000..8ded659
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/Makefile.am
@@ -0,0 +1,30 @@
+noinst_LTLIBRARIES = libsieve_ext_mime.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../util \
+ -I$(srcdir)/../variables \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-foreverypart.c \
+ cmd-break.c \
+ cmd-extracttext.c
+
+tags = \
+ tag-mime.c
+
+extensions = \
+ ext-mime.c \
+ ext-foreverypart.c \
+ ext-extracttext.c
+
+libsieve_ext_mime_la_SOURCES = \
+ ext-mime-common.c \
+ $(commands) \
+ $(tags) \
+ $(extensions)
+
+noinst_HEADERS = \
+ ext-mime-common.h
+
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/Makefile.in b/pigeonhole/src/lib-sieve/plugins/mime/Makefile.in
new file mode 100644
index 0000000..cfe25ff
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/Makefile.in
@@ -0,0 +1,727 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/mime
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_mime_la_LIBADD =
+am__objects_1 = cmd-foreverypart.lo cmd-break.lo cmd-extracttext.lo
+am__objects_2 = tag-mime.lo
+am__objects_3 = ext-mime.lo ext-foreverypart.lo ext-extracttext.lo
+am_libsieve_ext_mime_la_OBJECTS = ext-mime-common.lo $(am__objects_1) \
+ $(am__objects_2) $(am__objects_3)
+libsieve_ext_mime_la_OBJECTS = $(am_libsieve_ext_mime_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-break.Plo \
+ ./$(DEPDIR)/cmd-extracttext.Plo \
+ ./$(DEPDIR)/cmd-foreverypart.Plo \
+ ./$(DEPDIR)/ext-extracttext.Plo \
+ ./$(DEPDIR)/ext-foreverypart.Plo \
+ ./$(DEPDIR)/ext-mime-common.Plo ./$(DEPDIR)/ext-mime.Plo \
+ ./$(DEPDIR)/tag-mime.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_mime_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_mime_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_mime.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../util \
+ -I$(srcdir)/../variables \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-foreverypart.c \
+ cmd-break.c \
+ cmd-extracttext.c
+
+tags = \
+ tag-mime.c
+
+extensions = \
+ ext-mime.c \
+ ext-foreverypart.c \
+ ext-extracttext.c
+
+libsieve_ext_mime_la_SOURCES = \
+ ext-mime-common.c \
+ $(commands) \
+ $(tags) \
+ $(extensions)
+
+noinst_HEADERS = \
+ ext-mime-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/mime/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/mime/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_mime.la: $(libsieve_ext_mime_la_OBJECTS) $(libsieve_ext_mime_la_DEPENDENCIES) $(EXTRA_libsieve_ext_mime_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_mime_la_OBJECTS) $(libsieve_ext_mime_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-break.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-extracttext.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-foreverypart.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-extracttext.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-foreverypart.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-mime-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-mime.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag-mime.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmd-break.Plo
+ -rm -f ./$(DEPDIR)/cmd-extracttext.Plo
+ -rm -f ./$(DEPDIR)/cmd-foreverypart.Plo
+ -rm -f ./$(DEPDIR)/ext-extracttext.Plo
+ -rm -f ./$(DEPDIR)/ext-foreverypart.Plo
+ -rm -f ./$(DEPDIR)/ext-mime-common.Plo
+ -rm -f ./$(DEPDIR)/ext-mime.Plo
+ -rm -f ./$(DEPDIR)/tag-mime.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmd-break.Plo
+ -rm -f ./$(DEPDIR)/cmd-extracttext.Plo
+ -rm -f ./$(DEPDIR)/cmd-foreverypart.Plo
+ -rm -f ./$(DEPDIR)/ext-extracttext.Plo
+ -rm -f ./$(DEPDIR)/ext-foreverypart.Plo
+ -rm -f ./$(DEPDIR)/ext-mime-common.Plo
+ -rm -f ./$(DEPDIR)/ext-mime.Plo
+ -rm -f ./$(DEPDIR)/tag-mime.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/cmd-break.c b/pigeonhole/src/lib-sieve/plugins/mime/cmd-break.c
new file mode 100644
index 0000000..e6ade4c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/cmd-break.c
@@ -0,0 +1,273 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-binary.h"
+#include "sieve-dump.h"
+
+#include "ext-mime-common.h"
+
+#include <ctype.h>
+
+/* break
+ *
+ * Syntax:
+ * break [":name" <name: string>]
+ *
+ */
+
+static bool cmd_break_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool cmd_break_pre_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_break_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_break_generate
+ (const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+const struct sieve_command_def cmd_break = {
+ .identifier = "break",
+ .type = SCT_COMMAND,
+ .positional_args = 0,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_break_registered,
+ .pre_validate = cmd_break_pre_validate,
+ .validate = cmd_break_validate,
+ .generate = cmd_break_generate,
+};
+
+/*
+ * Tagged arguments
+ */
+
+/* Forward declarations */
+
+static bool cmd_break_validate_name_tag
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+/* Argument objects */
+
+static const struct sieve_argument_def break_name_tag = {
+ .identifier = "name",
+ .validate = cmd_break_validate_name_tag
+};
+
+/*
+ * Break operation
+ */
+
+static bool cmd_break_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_break_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def break_operation = {
+ .mnemonic = "BREAK",
+ .ext_def = &foreverypart_extension,
+ .code = EXT_FOREVERYPART_OPERATION_BREAK,
+ .dump = cmd_break_operation_dump,
+ .execute = cmd_break_operation_execute
+};
+
+/*
+ * Validation data
+ */
+
+struct cmd_break_data {
+ struct sieve_ast_argument *name;
+ struct sieve_command *loop_cmd;
+};
+
+/*
+ * Tag validation
+ */
+
+static bool cmd_break_validate_name_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct cmd_break_data *data =
+ (struct cmd_break_data *)cmd->data;
+ struct sieve_ast_argument *tag = *arg;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg, 1);
+
+ /* Check syntax:
+ * :name <string>
+ */
+ if ( !sieve_validate_tag_parameter
+ (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, TRUE) )
+ return FALSE;
+ data->name = *arg;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+ return TRUE;
+}
+
+/*
+ * Command registration
+ */
+
+static bool cmd_break_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &break_name_tag, 0);
+
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool cmd_break_pre_validate
+(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd)
+{
+ struct cmd_break_data *data;
+ pool_t pool = sieve_command_pool(cmd);
+
+ data = p_new(pool, struct cmd_break_data, 1);
+ cmd->data = data;
+ return TRUE;
+}
+
+static bool cmd_break_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct cmd_break_data *data =
+ (struct cmd_break_data *)cmd->data;
+ struct sieve_ast_node *node = cmd->ast_node;
+ const char *name = ( data->name == NULL ?
+ NULL : sieve_ast_argument_strc(data->name) );
+
+ i_assert(node != NULL);
+ while ( node != NULL && node->command != NULL ) {
+ if ( sieve_command_is(node->command, cmd_foreverypart) ) {
+ struct ext_foreverypart_loop *loop =
+ (struct ext_foreverypart_loop *)node->command->data;
+ if ( name == NULL ||
+ (name != NULL && loop->name != NULL &&
+ strcmp(name, loop->name) == 0) ) {
+ data->loop_cmd = node->command;
+ break;
+ }
+ }
+ node = sieve_ast_node_parent(node);
+ }
+
+ if ( data->loop_cmd == NULL ) {
+ if ( name == NULL ) {
+ sieve_command_validate_error(valdtr, cmd,
+ "the break command is not placed inside "
+ "a foreverypart loop");
+ } else {
+ sieve_command_validate_error(valdtr, cmd,
+ "the break command is not placed inside "
+ "a foreverypart loop named `%s'",
+ name);
+ }
+ return FALSE;
+ }
+
+ sieve_command_exit_block_unconditionally(cmd);
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_break_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ struct cmd_break_data *data =
+ (struct cmd_break_data *)cmd->data;
+ struct ext_foreverypart_loop *loop;
+
+ i_assert( data->loop_cmd != NULL );
+ loop = (struct ext_foreverypart_loop *)data->loop_cmd->data;
+
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &break_operation);
+ sieve_jumplist_add(loop->exit_jumps,
+ sieve_binary_emit_offset(cgenv->sblock, 0));
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool cmd_break_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ unsigned int pc = *address;
+ sieve_offset_t offset;
+
+ sieve_code_dumpf(denv, "BREAK");
+ sieve_code_descend(denv);
+
+ if ( !sieve_binary_read_offset(denv->sblock, address, &offset) )
+ return FALSE;
+
+ sieve_code_dumpf(denv, "END: %d [%08x]", offset, pc + offset);
+ return TRUE;
+}
+
+/*
+ * Code execution
+ */
+
+static int cmd_break_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ struct sieve_interpreter_loop *loop;
+ unsigned int pc = *address;
+ sieve_offset_t offset;
+ sieve_size_t loop_end;
+
+ /*
+ * Read operands
+ */
+
+ if ( !sieve_binary_read_offset(renv->sblock, address, &offset) )
+ {
+ sieve_runtime_trace_error(renv, "invalid loop end offset");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ loop_end = pc + offset;
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "break command");
+ sieve_runtime_trace_descend(renv);
+
+ loop = sieve_interpreter_loop_get
+ (renv->interp, loop_end, &foreverypart_extension);
+ if ( loop == NULL ) {
+ sieve_runtime_trace_error(renv, "no matching loop found");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ sieve_interpreter_loop_break(renv->interp, loop);
+ return SIEVE_EXEC_OK;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/cmd-extracttext.c b/pigeonhole/src/lib-sieve/plugins/mime/cmd-extracttext.c
new file mode 100644
index 0000000..d9b31a2
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/cmd-extracttext.c
@@ -0,0 +1,370 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-code.h"
+#include "sieve-ast.h"
+#include "sieve-commands.h"
+#include "sieve-binary.h"
+#include "sieve-message.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-mime-common.h"
+
+/*
+ * Extracttext command
+ *
+ * Syntax:
+ * extracttext [MODIFIER] [":first" number] <varname: string>
+ */
+
+static bool cmd_extracttext_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool cmd_extracttext_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_extracttext_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def cmd_extracttext = {
+ .identifier = "extracttext",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_extracttext_registered,
+ .validate = cmd_extracttext_validate,
+ .generate = cmd_extracttext_generate
+};
+
+/*
+ * Extracttext command tags
+ */
+
+enum cmd_extracttext_optional {
+ CMD_EXTRACTTEXT_OPT_END,
+ CMD_EXTRACTTEXT_OPT_FIRST
+};
+
+static bool cmd_extracttext_validate_first_tag
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+static const struct sieve_argument_def extracttext_from_tag = {
+ .identifier = "first",
+ .validate = cmd_extracttext_validate_first_tag
+};
+
+/*
+ * Extracttext operation
+ */
+
+static bool cmd_extracttext_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_extracttext_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def extracttext_operation = {
+ .mnemonic = "EXTRACTTEXT",
+ .ext_def = &extracttext_extension,
+ .dump = cmd_extracttext_operation_dump,
+ .execute = cmd_extracttext_operation_execute
+};
+
+/*
+ * Compiler context
+ */
+
+struct cmd_extracttext_context {
+ ARRAY_TYPE(sieve_variables_modifier) modifiers;
+};
+
+/*
+ * Tag validation
+ */
+
+static bool cmd_extracttext_validate_first_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :first <number>
+ */
+ if ( !sieve_validate_tag_parameter
+ (valdtr, cmd, tag, *arg, NULL, 0, SAAT_NUMBER, FALSE) )
+ return FALSE;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+/* Command registration */
+
+static bool cmd_extracttext_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ struct ext_extracttext_context *ectx =
+ (struct ext_extracttext_context *)ext->context;
+
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &extracttext_from_tag, CMD_EXTRACTTEXT_OPT_FIRST);
+ sieve_variables_modifiers_link_tag
+ (valdtr, ectx->var_ext, cmd_reg);
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool cmd_extracttext_validate(struct sieve_validator *valdtr,
+ struct sieve_command *cmd)
+{
+ const struct sieve_extension *this_ext = cmd->ext;
+ struct ext_extracttext_context *ectx =
+ (struct ext_extracttext_context *)this_ext->context;
+ struct sieve_ast_node *node = cmd->ast_node;
+ struct sieve_ast_argument *arg = cmd->first_positional;
+ pool_t pool = sieve_command_pool(cmd);
+ struct cmd_extracttext_context *sctx;
+
+ /* Create command context */
+ sctx = p_new(pool, struct cmd_extracttext_context, 1);
+ p_array_init(&sctx->modifiers, pool, 4);
+ cmd->data = (void *) sctx;
+
+ /* Validate modifiers */
+ if ( !sieve_variables_modifiers_validate
+ (valdtr, cmd, &sctx->modifiers) )
+ return FALSE;
+
+ /* Validate varname argument */
+ if ( !sieve_validate_positional_argument
+ (valdtr, cmd, arg, "varname", 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+ if ( !sieve_variable_argument_activate
+ (ectx->var_ext, ectx->var_ext, valdtr, cmd, arg, TRUE) )
+ return FALSE;
+
+ /* Check foreverypart context */
+ i_assert(node != NULL);
+ while ( node != NULL ) {
+ if ( node->command != NULL &&
+ sieve_command_is(node->command, cmd_foreverypart) )
+ break;
+ node = sieve_ast_node_parent(node);
+ }
+
+ if ( node == NULL ) {
+ sieve_command_validate_error(valdtr, cmd,
+ "the extracttext command is not placed inside "
+ "a foreverypart loop");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_extracttext_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ const struct sieve_extension *this_ext = cmd->ext;
+ struct sieve_binary_block *sblock = cgenv->sblock;
+ struct cmd_extracttext_context *sctx =
+ (struct cmd_extracttext_context *) cmd->data;
+
+ sieve_operation_emit(sblock, this_ext, &extracttext_operation);
+
+ /* Generate arguments */
+ if ( !sieve_generate_arguments(cgenv, cmd, NULL) )
+ return FALSE;
+
+ /* Generate modifiers */
+ if ( !sieve_variables_modifiers_generate
+ (cgenv, &sctx->modifiers) )
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool cmd_extracttext_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "EXTRACTTEXT");
+ sieve_code_descend(denv);
+
+ /* Dump optional operands */
+
+ for (;;) {
+ int opt;
+ bool opok = TRUE;
+
+ if ( (opt=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 )
+ return FALSE;
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case CMD_EXTRACTTEXT_OPT_FIRST:
+ opok = sieve_opr_number_dump(denv, address, "first");
+ break;
+ default:
+ return FALSE;
+ }
+ if ( !opok ) return FALSE;
+ }
+
+ /* Print both variable name and string value */
+ if ( !sieve_opr_string_dump(denv, address, "varname") )
+ return FALSE;
+
+ return sieve_variables_modifiers_code_dump(denv, address);
+}
+
+/*
+ * Code execution
+ */
+
+static int cmd_extracttext_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct ext_extracttext_context *ectx =
+ (struct ext_extracttext_context *)this_ext->context;
+ struct sieve_variable_storage *storage;
+ ARRAY_TYPE(sieve_variables_modifier) modifiers;
+ struct ext_foreverypart_runtime_loop *sfploop;
+ struct sieve_message_part *mpart;
+ struct sieve_message_part_data mpart_data;
+ int opt_code = 0;
+ sieve_number_t first = 0;
+ string_t *value;
+ unsigned int var_index;
+ bool have_first = FALSE;
+ int ret = SIEVE_EXEC_OK;
+
+ /*
+ * Read the normal operands
+ */
+
+ /* Optional operands */
+
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_opr_optional_read
+ (renv, address, &opt_code)) < 0 )
+ return SIEVE_EXEC_BIN_CORRUPT;
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case CMD_EXTRACTTEXT_OPT_FIRST:
+ ret = sieve_opr_number_read
+ (renv, address, "first", &first);
+ have_first = TRUE;
+ break;
+ default:
+ sieve_runtime_trace_error(renv, "unknown optional operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ if ( ret <= 0 ) return ret;
+ }
+
+ /* Varname operand */
+
+ if ( (ret=sieve_variable_operand_read
+ (renv, address, "varname", &storage, &var_index)) <= 0 )
+ return ret;
+
+ /* Modifiers */
+
+ if ( (ret=sieve_variables_modifiers_code_read
+ (renv, ectx->var_ext, address, &modifiers)) <= 0 )
+ return ret;
+
+ /*
+ * Determine and assign the value
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "extracttext command");
+ sieve_runtime_trace_descend(renv);
+
+ sfploop = ext_foreverypart_runtime_loop_get_current(renv);
+ if ( sfploop == NULL ) {
+ sieve_runtime_trace_error(renv,
+ "outside foreverypart context");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ /* Get current message part */
+ mpart = sieve_message_part_iter_current(&sfploop->part_iter);
+ i_assert( mpart != NULL );
+
+ /* Get message part content */
+ sieve_message_part_get_data(mpart, &mpart_data, TRUE);
+
+ /* Apply ":first" limit, if any */
+ if ( !have_first || (size_t)first > mpart_data.size ) {
+ value = t_str_new_const(mpart_data.content, mpart_data.size);
+ } else {
+ value = t_str_new((size_t)first);
+ str_append_data(value, mpart_data.content, (size_t)first);
+ }
+
+ /* Apply modifiers */
+ if ( (ret=sieve_variables_modifiers_apply
+ (renv, ectx->var_ext, &modifiers, &value)) <= 0 )
+ return ret;
+
+ /* Actually assign the value if all is well */
+ i_assert ( value != NULL );
+ if ( !sieve_variable_assign(storage, var_index, value) )
+ return SIEVE_EXEC_BIN_CORRUPT;
+
+ /* Trace */
+ if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) {
+ const char *var_name, *var_id;
+
+ (void)sieve_variable_get_identifier(storage, var_index, &var_name);
+ var_id = sieve_variable_get_varid(storage, var_index);
+
+ sieve_runtime_trace_here(renv, 0, "assign `%s' [%s] = \"%s\"",
+ var_name, var_id, str_c(value));
+ }
+
+ return SIEVE_EXEC_OK;
+}
+
+
+
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/cmd-foreverypart.c b/pigeonhole/src/lib-sieve/plugins/mime/cmd-foreverypart.c
new file mode 100644
index 0000000..435cb5c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/cmd-foreverypart.c
@@ -0,0 +1,377 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-limits.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-binary.h"
+#include "sieve-dump.h"
+#include "sieve-message.h"
+
+#include "ext-mime-common.h"
+
+#include <ctype.h>
+
+/* Foreverypart
+ *
+ * Syntax:
+ * foreverypart [":name" <name: string>] <block>
+ *
+ */
+
+static bool cmd_foreverypart_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool cmd_foreverypart_pre_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_foreverypart_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_foreverypart_generate
+ (const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+const struct sieve_command_def cmd_foreverypart = {
+ .identifier = "foreverypart",
+ .type = SCT_COMMAND,
+ .positional_args = 0,
+ .subtests = 0,
+ .block_allowed = TRUE,
+ .block_required = TRUE,
+ .registered = cmd_foreverypart_registered,
+ .pre_validate = cmd_foreverypart_pre_validate,
+ .validate = cmd_foreverypart_validate,
+ .generate = cmd_foreverypart_generate
+};
+
+/*
+ * Tagged arguments
+ */
+
+/* Forward declarations */
+
+static bool cmd_foreverypart_validate_name_tag
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+/* Argument objects */
+
+static const struct sieve_argument_def foreverypart_name_tag = {
+ .identifier = "name",
+ .validate = cmd_foreverypart_validate_name_tag,
+};
+
+/*
+ * foreverypart operation
+ */
+
+static bool cmd_foreverypart_begin_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_foreverypart_begin_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def foreverypart_begin_operation = {
+ .mnemonic = "FOREVERYPART_BEGIN",
+ .ext_def = &foreverypart_extension,
+ .code = EXT_FOREVERYPART_OPERATION_FOREVERYPART_BEGIN,
+ .dump = cmd_foreverypart_begin_operation_dump,
+ .execute = cmd_foreverypart_begin_operation_execute
+};
+
+static bool cmd_foreverypart_end_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_foreverypart_end_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def foreverypart_end_operation = {
+ .mnemonic = "FOREVERYPART_END",
+ .ext_def = &foreverypart_extension,
+ .code = EXT_FOREVERYPART_OPERATION_FOREVERYPART_END,
+ .dump = cmd_foreverypart_end_operation_dump,
+ .execute = cmd_foreverypart_end_operation_execute
+};
+
+/*
+ * Tag validation
+ */
+
+static bool cmd_foreverypart_validate_name_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct ext_foreverypart_loop *loop =
+ (struct ext_foreverypart_loop *)cmd->data;
+ struct sieve_ast_argument *tag = *arg;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg, 1);
+
+ /* Check syntax:
+ * :name <string>
+ */
+ if ( !sieve_validate_tag_parameter
+ (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING, TRUE) )
+ return FALSE;
+ loop->name = sieve_ast_argument_strc(*arg);
+
+ /* Detach parameter */
+ *arg = sieve_ast_arguments_detach(*arg, 1);
+ return TRUE;
+}
+
+/*
+ * Command registration
+ */
+
+static bool cmd_foreverypart_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &foreverypart_name_tag, 0);
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool cmd_foreverypart_pre_validate
+(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd)
+{
+ struct ext_foreverypart_loop *loop;
+ pool_t pool = sieve_command_pool(cmd);
+
+ loop = p_new(pool, struct ext_foreverypart_loop, 1);
+ cmd->data = loop;
+
+ return TRUE;
+}
+
+static bool cmd_foreverypart_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct sieve_ast_node *node = cmd->ast_node;
+ unsigned int nesting = 0;
+
+ /* Determine nesting depth of foreverypart commands at this point. */
+ i_assert(node != NULL);
+ node = sieve_ast_node_parent(node);
+ while ( node != NULL && node->command != NULL ) {
+ if ( sieve_command_is(node->command, cmd_foreverypart) )
+ nesting++;
+ node = sieve_ast_node_parent(node);
+ }
+
+ /* Enforce nesting limit
+ NOTE: this only recognizes the foreverypart command as a loop; if
+ new loop commands are introduced in the future, these must be
+ recognized somehow. */
+ if ( nesting + 1 > SIEVE_MAX_LOOP_DEPTH ) {
+ sieve_command_validate_error(valdtr, cmd,
+ "the nested foreverypart loop exceeds "
+ "the nesting limit (<= %u levels)",
+ SIEVE_MAX_LOOP_DEPTH);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_foreverypart_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ struct ext_foreverypart_loop *loop =
+ (struct ext_foreverypart_loop *)cmd->data;
+ sieve_size_t block_begin, loop_jump;
+
+ /* Emit FOREVERYPART_BEGIN operation */
+ sieve_operation_emit(cgenv->sblock,
+ cmd->ext, &foreverypart_begin_operation);
+
+ /* Emit exit address */
+ loop->exit_jumps = sieve_jumplist_create
+ (sieve_command_pool(cmd), cgenv->sblock);
+ sieve_jumplist_add(loop->exit_jumps,
+ sieve_binary_emit_offset(cgenv->sblock, 0));
+ block_begin = sieve_binary_block_get_size(cgenv->sblock);
+
+ /* Generate loop block */
+ if ( !sieve_generate_block(cgenv, cmd->ast_node) )
+ return FALSE;
+
+ /* Emit FOREVERYPART_END operation */
+ sieve_operation_emit(cgenv->sblock,
+ cmd->ext, &foreverypart_end_operation);
+ loop_jump = sieve_binary_block_get_size(cgenv->sblock);
+ i_assert(loop_jump > block_begin);
+ (void)sieve_binary_emit_offset
+ (cgenv->sblock, (loop_jump - block_begin));
+
+ /* Resolve exit address */
+ sieve_jumplist_resolve(loop->exit_jumps);
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool cmd_foreverypart_begin_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ unsigned int pc = *address;
+ sieve_offset_t offset;
+
+ sieve_code_dumpf(denv, "FOREVERYPART_BEGIN");
+ sieve_code_descend(denv);
+
+ if ( !sieve_binary_read_offset(denv->sblock, address, &offset) )
+ return FALSE;
+
+ sieve_code_dumpf(denv, "END: %d [%08x]", offset, pc + offset);
+ return TRUE;
+}
+
+static bool cmd_foreverypart_end_operation_dump
+(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ unsigned int pc = *address;
+ sieve_offset_t offset;
+
+ sieve_code_dumpf(denv, "FOREVERYPART_END");
+ sieve_code_descend(denv);
+
+ if ( !sieve_binary_read_offset(denv->sblock, address, &offset) )
+ return FALSE;
+
+ sieve_code_dumpf(denv, "BEGIN: -%d [%08x]", offset, pc - offset);
+ return TRUE;
+}
+
+/*
+ * Code execution
+ */
+
+static int cmd_foreverypart_begin_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ struct sieve_interpreter_loop *loop;
+ struct ext_foreverypart_runtime_loop *fploop, *sfploop;
+ unsigned int pc = *address;
+ sieve_offset_t offset;
+ sieve_size_t loop_end;
+ pool_t pool;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ if ( !sieve_binary_read_offset(renv->sblock, address, &offset) )
+ {
+ sieve_runtime_trace_error(renv, "invalid loop end offset");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ loop_end = pc + offset;
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+ "foreverypart loop begin");
+ sieve_runtime_trace_descend(renv);
+
+ sfploop = ext_foreverypart_runtime_loop_get_current(renv);
+
+ if ( (ret=sieve_interpreter_loop_start(renv->interp,
+ loop_end, &foreverypart_extension, &loop)) <= 0 )
+ return ret;
+
+ pool = sieve_interpreter_loop_get_pool(loop);
+ fploop = p_new(pool, struct ext_foreverypart_runtime_loop, 1);
+
+ if ( sfploop == NULL ) {
+ if ( (ret=sieve_message_part_iter_init
+ (&fploop->part_iter, renv)) <= 0 )
+ return ret;
+ } else {
+ sieve_message_part_iter_children(&sfploop->part_iter,
+ &fploop->part_iter);
+ }
+ fploop->part = sieve_message_part_iter_current(&fploop->part_iter);
+ if (fploop->part != NULL) {
+ sieve_interpreter_loop_set_context(loop, (void*)fploop);
+ } else {
+ /* No children parts to iterate */
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
+ "no children at this level");
+ sieve_interpreter_loop_break(renv->interp, loop);
+ }
+ return SIEVE_EXEC_OK;
+}
+
+static int cmd_foreverypart_end_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ struct sieve_interpreter_loop *loop;
+ struct ext_foreverypart_runtime_loop *fploop;
+ unsigned int pc = *address;
+ sieve_offset_t offset;
+ sieve_size_t loop_begin;
+
+ /*
+ * Read operands
+ */
+
+ if ( !sieve_binary_read_offset(renv->sblock, address, &offset) )
+ {
+ sieve_runtime_trace_error(renv, "invalid loop begin offset");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ loop_begin = pc - offset;
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv,
+ SIEVE_TRLVL_COMMANDS, "foreverypart loop end");
+ sieve_runtime_trace_descend(renv);
+
+ loop = sieve_interpreter_loop_get
+ (renv->interp, *address, &foreverypart_extension);
+ if ( loop == NULL ) {
+ sieve_runtime_trace_error(renv, "no matching loop found");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ fploop = (struct ext_foreverypart_runtime_loop *)
+ sieve_interpreter_loop_get_context(loop);
+ i_assert(fploop->part != NULL);
+ fploop->part = sieve_message_part_iter_next(&fploop->part_iter);
+ if ( fploop->part == NULL ) {
+ sieve_runtime_trace(renv,
+ SIEVE_TRLVL_COMMANDS, "no more message parts");
+ return sieve_interpreter_loop_break(renv->interp, loop);
+ }
+
+ sieve_runtime_trace(renv,
+ SIEVE_TRLVL_COMMANDS, "switched to next message part");
+ return sieve_interpreter_loop_next(renv->interp, loop, loop_begin);
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/ext-extracttext.c b/pigeonhole/src/lib-sieve/plugins/mime/ext-extracttext.c
new file mode 100644
index 0000000..4eab76b
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/ext-extracttext.c
@@ -0,0 +1,130 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension extracttext
+ * ---------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5703, Section 7
+ * Implementation: full
+ * Status: experimental
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-mime-common.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_extracttext_load
+ (const struct sieve_extension *ext, void **context);
+static void ext_extracttext_unload
+ (const struct sieve_extension *ext);
+static bool ext_extracttext_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def extracttext_extension = {
+ .name = "extracttext",
+ .load = ext_extracttext_load,
+ .unload = ext_extracttext_unload,
+ .validator_load = ext_extracttext_validator_load,
+ SIEVE_EXT_DEFINE_OPERATION(extracttext_operation)
+};
+
+static bool ext_extracttext_load
+(const struct sieve_extension *ext, void **context)
+{
+ struct sieve_instance *svinst = ext->svinst;
+ struct ext_extracttext_context *ectx;
+
+ if ( *context != NULL )
+ ext_extracttext_unload(ext);
+
+ ectx = i_new(struct ext_extracttext_context, 1);
+ ectx->var_ext = sieve_ext_variables_get_extension(ext->svinst);
+ ectx->fep_ext = sieve_extension_register
+ (svinst, &foreverypart_extension, FALSE);
+ *context = (void *)ectx;
+ return TRUE;
+}
+
+static void ext_extracttext_unload
+(const struct sieve_extension *ext)
+{
+ struct ext_extracttext_context *ctx =
+ (struct ext_extracttext_context *) ext->context;
+
+ i_free(ctx);
+}
+
+/*
+ * Extension validation
+ */
+
+static bool ext_extracttext_validator_validate
+ (const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context,
+ struct sieve_ast_argument *require_arg,
+ bool required);
+
+const struct sieve_validator_extension
+extracttext_validator_extension = {
+ .ext = &extracttext_extension,
+ .validate = ext_extracttext_validator_validate
+};
+
+static bool ext_extracttext_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register validator extension to check for conflict with eextracttext */
+ sieve_validator_extension_register
+ (valdtr, ext, &extracttext_validator_extension, NULL);
+
+ /* Register new commands */
+ sieve_validator_register_command(valdtr, ext, &cmd_extracttext);
+
+ return TRUE;
+}
+
+static bool ext_extracttext_validator_validate
+(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context ATTR_UNUSED,
+ struct sieve_ast_argument *require_arg,
+ bool required ATTR_UNUSED)
+{
+ struct ext_extracttext_context *ectx =
+ (struct ext_extracttext_context *)ext->context;
+
+ if ( ectx->var_ext == NULL ||
+ !sieve_ext_variables_is_active
+ (ectx->var_ext, valdtr) ) {
+ sieve_argument_validate_error(valdtr, require_arg,
+ "extracttext extension cannot be used "
+ "without variables extension");
+ return FALSE;
+ }
+ if ( ectx->fep_ext == NULL ||
+ !sieve_validator_extension_loaded
+ (valdtr, ectx->fep_ext) ) {
+ sieve_argument_validate_error(valdtr, require_arg,
+ "extracttext extension cannot be used "
+ "without foreverypart extension");
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/ext-foreverypart.c b/pigeonhole/src/lib-sieve/plugins/mime/ext-foreverypart.c
new file mode 100644
index 0000000..6bca199
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/ext-foreverypart.c
@@ -0,0 +1,62 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension foreverypart
+ * ----------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5703, Section 3
+ * Implementation: full
+ * Status: experimental
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "ext-mime-common.h"
+
+/*
+ * Operations
+ */
+
+const struct sieve_operation_def *ext_foreverypart_operations[] = {
+ &foreverypart_begin_operation,
+ &foreverypart_end_operation,
+ &break_operation
+};
+
+/*
+ * Extension
+ */
+
+static bool ext_foreverypart_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def foreverypart_extension = {
+ .name = "foreverypart",
+ .validator_load = ext_foreverypart_validator_load,
+ SIEVE_EXT_DEFINE_OPERATIONS(ext_foreverypart_operations)
+};
+
+/*
+ * Extension validation
+ */
+
+static bool ext_foreverypart_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register new commands */
+ sieve_validator_register_command(valdtr, ext, &cmd_foreverypart);
+ sieve_validator_register_command(valdtr, ext, &cmd_break);
+
+ return TRUE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.c b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.c
new file mode 100644
index 0000000..5b38bcb
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.c
@@ -0,0 +1,27 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-interpreter.h"
+
+#include "ext-mime-common.h"
+
+struct ext_foreverypart_runtime_loop *
+ext_foreverypart_runtime_loop_get_current
+(const struct sieve_runtime_env *renv)
+{
+ struct sieve_interpreter_loop *loop;
+ struct ext_foreverypart_runtime_loop *fploop;
+
+ loop = sieve_interpreter_loop_get_global
+ (renv->interp, NULL, &foreverypart_extension);
+ if ( loop == NULL ) {
+ fploop = NULL;
+ } else {
+ fploop = (struct ext_foreverypart_runtime_loop *)
+ sieve_interpreter_loop_get_context(loop);
+ i_assert(fploop->part != NULL);
+ }
+
+ return fploop;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.h b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.h
new file mode 100644
index 0000000..8b0054d
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime-common.h
@@ -0,0 +1,85 @@
+#ifndef EXT_FOREVERYPART_COMMON_H
+#define EXT_FOREVERYPART_COMMON_H
+
+#include "sieve-message.h"
+
+/*
+ * Extension
+ */
+
+struct ext_extracttext_context {
+ const struct sieve_extension *var_ext;
+ const struct sieve_extension *fep_ext;
+};
+
+extern const struct sieve_extension_def foreverypart_extension;
+extern const struct sieve_extension_def mime_extension;
+extern const struct sieve_extension_def extracttext_extension;
+
+/*
+ * Tagged arguments
+ */
+
+extern const struct sieve_argument_def mime_tag;
+extern const struct sieve_argument_def mime_anychild_tag;
+extern const struct sieve_argument_def mime_type_tag;
+extern const struct sieve_argument_def mime_subtype_tag;
+extern const struct sieve_argument_def mime_contenttype_tag;
+extern const struct sieve_argument_def mime_param_tag;
+
+/*
+ * Commands
+ */
+
+struct ext_foreverypart_loop {
+ const char *name;
+ struct sieve_jumplist *exit_jumps;
+};
+
+extern const struct sieve_command_def cmd_foreverypart;
+extern const struct sieve_command_def cmd_break;
+extern const struct sieve_command_def cmd_extracttext;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def foreverypart_begin_operation;
+extern const struct sieve_operation_def foreverypart_end_operation;
+extern const struct sieve_operation_def break_operation;
+extern const struct sieve_operation_def extracttext_operation;
+
+enum ext_foreverypart_opcode {
+ EXT_FOREVERYPART_OPERATION_FOREVERYPART_BEGIN,
+ EXT_FOREVERYPART_OPERATION_FOREVERYPART_END,
+ EXT_FOREVERYPART_OPERATION_BREAK,
+};
+
+/*
+ * Operands
+ */
+
+enum ext_mime_option {
+ EXT_MIME_OPTION_NONE = 0,
+ EXT_MIME_OPTION_TYPE,
+ EXT_MIME_OPTION_SUBTYPE,
+ EXT_MIME_OPTION_CONTENTTYPE,
+ EXT_MIME_OPTION_PARAM
+};
+
+extern const struct sieve_operand_def mime_operand;
+
+/*
+ * Foreverypart loop
+ */
+
+struct ext_foreverypart_runtime_loop {
+ struct sieve_message_part_iter part_iter;
+ struct sieve_message_part *part;
+};
+
+struct ext_foreverypart_runtime_loop *
+ext_foreverypart_runtime_loop_get_current
+(const struct sieve_runtime_env *renv);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/ext-mime.c b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime.c
new file mode 100644
index 0000000..da9963f
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/ext-mime.c
@@ -0,0 +1,77 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension mime
+ * --------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5703, Section 4
+ * Implementation: full
+ * Status: experimental
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-message.h"
+#include "sieve-result.h"
+
+#include "ext-mime-common.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_mime_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def mime_extension = {
+ .name = "mime",
+ .validator_load = ext_mime_validator_load,
+ SIEVE_EXT_DEFINE_OPERAND(mime_operand)
+};
+
+/*
+ * Extension validation
+ */
+
+static bool ext_mime_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register :mime tag and friends with header, address and exists test
+ * commands and we don't care whether these command are registered or
+ * even whether these will be registered at all. The validator handles
+ * either situation gracefully.
+ */
+ sieve_validator_register_external_tag
+ (valdtr, "header", ext, &mime_tag, SIEVE_OPT_MESSAGE_OVERRIDE);
+ sieve_validator_register_external_tag
+ (valdtr, "header", ext, &mime_anychild_tag, 0);
+ sieve_validator_register_external_tag
+ (valdtr, "header", ext, &mime_type_tag, 0);
+ sieve_validator_register_external_tag
+ (valdtr, "header", ext, &mime_subtype_tag, 0);
+ sieve_validator_register_external_tag
+ (valdtr, "header", ext, &mime_contenttype_tag, 0);
+ sieve_validator_register_external_tag
+ (valdtr, "header", ext, &mime_param_tag, 0);
+
+ sieve_validator_register_external_tag
+ (valdtr, "address", ext, &mime_tag, SIEVE_OPT_MESSAGE_OVERRIDE);
+ sieve_validator_register_external_tag
+ (valdtr, "address", ext, &mime_anychild_tag, 0);
+
+ sieve_validator_register_external_tag
+ (valdtr, "exists", ext, &mime_tag, SIEVE_OPT_MESSAGE_OVERRIDE);
+ sieve_validator_register_external_tag
+ (valdtr, "exists", ext, &mime_anychild_tag, 0);
+
+ return TRUE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/mime/tag-mime.c b/pigeonhole/src/lib-sieve/plugins/mime/tag-mime.c
new file mode 100644
index 0000000..379365e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/mime/tag-mime.c
@@ -0,0 +1,757 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "rfc822-parser.h"
+#include "rfc2231-parser.h"
+#include "mail-storage.h"
+
+#include "sieve-common.h"
+#include "sieve-stringlist.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-binary.h"
+#include "sieve-code.h"
+#include "sieve-message.h"
+#include "sieve-result.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+
+#include "ext-mime-common.h"
+
+/*
+ * Tagged argument
+ */
+
+static bool tag_mime_validate
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool tag_mime_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *context);
+
+const struct sieve_argument_def mime_tag = {
+ .identifier = "mime",
+ .validate = tag_mime_validate,
+ .generate = tag_mime_generate
+};
+
+static bool tag_mime_option_validate
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+const struct sieve_argument_def mime_anychild_tag = {
+ .identifier = "anychild",
+ .validate = tag_mime_option_validate
+};
+
+const struct sieve_argument_def mime_type_tag = {
+ .identifier = "type",
+ .validate = tag_mime_option_validate
+};
+
+const struct sieve_argument_def mime_subtype_tag = {
+ .identifier = "subtype",
+ .validate = tag_mime_option_validate
+};
+
+const struct sieve_argument_def mime_contenttype_tag = {
+ .identifier = "contenttype",
+ .validate = tag_mime_option_validate
+};
+
+const struct sieve_argument_def mime_param_tag = {
+ .identifier = "param",
+ .validate = tag_mime_option_validate
+};
+
+/*
+ * Header override
+ */
+
+static bool svmo_mime_dump_context
+ (const struct sieve_message_override *svmo,
+ const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int svmo_mime_read_context
+ (const struct sieve_message_override *svmo,
+ const struct sieve_runtime_env *renv, sieve_size_t *address,
+ void **ho_context);
+static int svmo_mime_header_override
+ (const struct sieve_message_override *svmo,
+ const struct sieve_runtime_env *renv,
+ bool mime_decode, struct sieve_stringlist **headers);
+
+const struct sieve_message_override_def mime_header_override = {
+ SIEVE_OBJECT("mime", &mime_operand, 0),
+ .sequence = 0, /* Completely replace header source */
+ .dump_context = svmo_mime_dump_context,
+ .read_context = svmo_mime_read_context,
+ .header_override = svmo_mime_header_override
+};
+
+/*
+ * Operand
+ */
+
+static const struct sieve_extension_objects ext_header_overrides =
+ SIEVE_EXT_DEFINE_MESSAGE_OVERRIDE(mime_header_override);
+
+const struct sieve_operand_def mime_operand = {
+ .name = "mime operand",
+ .ext_def = &mime_extension,
+ .class = &sieve_message_override_operand_class,
+ .interface = &ext_header_overrides
+};
+
+/*
+ * Tag data
+ */
+
+struct tag_mime_data {
+ enum ext_mime_option mimeopt;
+ struct sieve_ast_argument *param_arg;
+ bool anychild:1;
+};
+
+/*
+ * Tag validation
+ */
+
+static struct tag_mime_data *
+tag_mime_get_data(struct sieve_command *cmd,
+ struct sieve_ast_argument *tag)
+{
+ struct tag_mime_data *data;
+
+ if (tag->argument->data == NULL) {
+ data = p_new(sieve_command_pool(cmd), struct tag_mime_data, 1);
+ tag->argument->data = (void *)data;
+ } else {
+ data = (struct tag_mime_data *)tag->argument->data;
+ }
+
+ return data;
+}
+
+static bool tag_mime_validate
+(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_ast_argument **arg, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+
+ /* Skip the tag itself */
+ *arg = sieve_ast_argument_next(*arg);
+
+ (void)tag_mime_get_data(cmd, tag);
+ return TRUE;
+}
+
+static bool tag_mime_option_validate
+(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_ast_argument **arg, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+ struct sieve_ast_argument *mime_arg;
+ struct tag_mime_data *data;
+
+ i_assert(tag != NULL);
+
+ /* Detach tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Find required ":mime" tag */
+ mime_arg = sieve_command_find_argument(cmd, &mime_tag);
+ if ( mime_arg == NULL ) {
+ sieve_argument_validate_error(valdtr, tag,
+ "the :%s tag for the %s %s cannot be specified "
+ "without the :mime tag", sieve_ast_argument_tag(tag),
+ sieve_command_identifier(cmd), sieve_command_type_name(cmd));
+ return FALSE;
+ }
+
+ /* Annotate ":mime" tag with the data provided by this option tag */
+ data = tag_mime_get_data(cmd, mime_arg);
+ if ( sieve_argument_is(tag, mime_anychild_tag) )
+ data->anychild = TRUE;
+ else {
+ if ( data->mimeopt != EXT_MIME_OPTION_NONE ) {
+ sieve_argument_validate_error(valdtr, *arg,
+ "the :type, :subtype, :contenttype, and :param "
+ "arguments for the %s test are mutually exclusive, "
+ "but more than one was specified",
+ sieve_command_identifier(cmd));
+ return FALSE;
+ }
+ if ( sieve_argument_is(tag, mime_type_tag) )
+ data->mimeopt = EXT_MIME_OPTION_TYPE;
+ else if ( sieve_argument_is(tag, mime_subtype_tag) )
+ data->mimeopt = EXT_MIME_OPTION_SUBTYPE;
+ else if ( sieve_argument_is(tag, mime_contenttype_tag) )
+ data->mimeopt = EXT_MIME_OPTION_CONTENTTYPE;
+ else if ( sieve_argument_is(tag, mime_param_tag) ) {
+ /* Check syntax:
+ * ":param" <param-list: string-list>
+ */
+ if ( !sieve_validate_tag_parameter
+ (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING_LIST, FALSE) ) {
+ return FALSE;
+ }
+
+ data->mimeopt = EXT_MIME_OPTION_PARAM;
+ data->param_arg = *arg;
+
+ /* Detach parameter */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+ } else
+ i_unreached();
+ }
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool tag_mime_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *cmd)
+{
+ struct tag_mime_data *data =
+ (struct tag_mime_data *)arg->argument->data;
+
+ if ( sieve_ast_argument_type(arg) != SAAT_TAG )
+ return FALSE;
+
+ sieve_opr_message_override_emit
+ (cgenv->sblock, arg->argument->ext, &mime_header_override);
+
+ (void)sieve_binary_emit_byte
+ (cgenv->sblock, ( data->anychild ? 1 : 0 ));
+ (void)sieve_binary_emit_byte
+ (cgenv->sblock, data->mimeopt);
+ if ( data->mimeopt == EXT_MIME_OPTION_PARAM &&
+ !sieve_generate_argument(cgenv, data->param_arg, cmd) )
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Content-type stringlist
+ */
+
+enum content_type_part {
+ CONTENT_TYPE_PART_NONE = 0,
+ CONTENT_TYPE_PART_TYPE,
+ CONTENT_TYPE_PART_SUBTYPE,
+ CONTENT_TYPE_PART_CONTENTTYPE,
+};
+
+/* Object */
+
+static int content_header_stringlist_next_item
+ (struct sieve_stringlist *_strlist, string_t **str_r);
+static void content_header_stringlist_reset
+ (struct sieve_stringlist *_strlist);
+static int content_header_stringlist_get_length
+ (struct sieve_stringlist *_strlist);
+static void content_header_stringlist_set_trace
+ (struct sieve_stringlist *strlist, bool trace);
+
+struct content_header_stringlist {
+ struct sieve_stringlist strlist;
+
+ struct sieve_header_list *source;
+
+ enum ext_mime_option option;
+ const char *const *params;
+
+ const char *const *param_values;
+};
+
+static struct sieve_stringlist *content_header_stringlist_create
+(const struct sieve_runtime_env *renv,
+ struct sieve_header_list *source,
+ enum ext_mime_option option, const char *const *params)
+{
+ struct content_header_stringlist *strlist;
+
+ strlist = t_new(struct content_header_stringlist, 1);
+ strlist->strlist.runenv = renv;
+ strlist->strlist.exec_status = SIEVE_EXEC_OK;
+ strlist->strlist.next_item = content_header_stringlist_next_item;
+ strlist->strlist.reset = content_header_stringlist_reset;
+ strlist->strlist.set_trace = content_header_stringlist_set_trace;
+ strlist->source = source;
+ strlist->option = option;
+ strlist->params = params;
+
+ if ( option != EXT_MIME_OPTION_PARAM ) {
+ /* One header can have multiple parameters, so we cannot rely
+ on the source length for the :param option. */
+ strlist->strlist.get_length = content_header_stringlist_get_length;
+ }
+
+ return &strlist->strlist;
+}
+
+/* Implementation */
+
+static inline int _decode_hex_digit(const unsigned char digit)
+{
+ switch ( digit ) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return digit - '0';
+
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ return digit - 'a' + 0x0a;
+
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ return digit - 'A' + 0x0A;
+ }
+ return -1;
+}
+
+static string_t *
+content_type_param_decode(const char *value)
+{
+ const unsigned char *p, *plast;
+
+ string_t *str = t_str_new(64);
+ plast = p = (const unsigned char *)value;
+ while ( *p != '\0' ) {
+ unsigned char ch;
+ int digit;
+
+ if ( *p == '%' ) {
+ if ( p - plast > 0 )
+ str_append_data(str, plast, (p - plast));
+ p++;
+ if ( *p == '\0' || (digit=_decode_hex_digit(*p)) < 0 )
+ return NULL;
+ ch = (unsigned char)digit;
+ p++;
+ if ( *p == '\0' || (digit=_decode_hex_digit(*p)) < 0 )
+ return NULL;
+ ch = (ch << 4) + (unsigned char)digit;
+ str_append_data(str, &ch, 1);
+ plast = p + 1;
+ }
+ p++;
+ }
+ if ( p - plast > 0 )
+ str_append_data(str, plast, (p - plast));
+ return str;
+}
+
+static string_t *
+content_type_param_next(struct content_header_stringlist *strlist)
+{
+ const struct sieve_runtime_env *renv = strlist->strlist.runenv;
+ const char *const *values = strlist->param_values;
+ bool trace = strlist->strlist.trace;
+
+ i_assert( strlist->params != NULL );
+
+ /* Iterate over all parsed parameter values */
+ for ( ; *values != NULL; values += 2 ) {
+ const char *const *params = strlist->params;
+ const char *name = values[0], *value = values[1];
+ size_t nlen = strlen(name);
+
+ /* Iterate over all interesting parameter names */
+ for ( ; *params != NULL; params++ ) {
+ size_t plen = strlen(*params);
+
+ if ( plen != nlen &&
+ (nlen != plen + 1 || name[nlen-1] != '*') )
+ continue;
+
+ if ( plen == nlen ) {
+ if ( strcasecmp(name, *params) == 0 ) {
+ /* Return raw value */
+ if ( trace ) {
+ sieve_runtime_trace(renv, 0,
+ "found mime parameter `%s' in mime header",
+ *params);
+ }
+
+ strlist->param_values = values + 2;
+ return t_str_new_const(value, strlen(value));
+ }
+ } else {
+ if ( trace ) {
+ sieve_runtime_trace(renv, 0,
+ "found encoded parameter `%s' in mime header",
+ *params);
+ }
+
+ if ( strncasecmp(name, *params, plen) == 0 ) {
+ string_t *result = NULL;
+
+ strlist->param_values = values + 2;
+
+ /* Decode value first */
+ // FIXME: transcode charset
+ value = strchr(value, '\'');
+ if (value != NULL)
+ value = strchr(value+1, '\'');
+ if (value != NULL)
+ result = content_type_param_decode(value + 1);
+ if (result == NULL)
+ strlist->param_values = NULL;
+ return result;
+ }
+ }
+ }
+ }
+
+ strlist->param_values = NULL;
+ return NULL;
+}
+
+// FIXME: not too happy with the use of string_t like this.
+// Sieve should have a special runtime string type (TODO)
+static string_t *
+content_header_parse(struct content_header_stringlist *strlist,
+ const char *hdr_name, string_t *str)
+{
+ const struct sieve_runtime_env *renv = strlist->strlist.runenv;
+ bool trace = strlist->strlist.trace;
+ struct rfc822_parser_context parser;
+ const char *type, *p;
+ bool is_ctype = FALSE;
+ string_t *content;
+
+ if ( strlist->option == EXT_MIME_OPTION_NONE )
+ return str;
+
+ if ( strcasecmp(hdr_name, "content-type") == 0 )
+ is_ctype = TRUE;
+ else if ( strcasecmp(hdr_name, "content-disposition") != 0 ) {
+ if ( trace ) {
+ sieve_runtime_trace(renv, 0,
+ "non-mime header yields empty string");
+ }
+ return t_str_new(0);
+ }
+
+ /* Initialize parsing */
+ rfc822_parser_init(&parser, str_data(str), str_len(str), NULL);
+ (void)rfc822_skip_lwsp(&parser);
+
+ /* Parse content type/disposition */
+ content = t_str_new(64);
+ if ( is_ctype ){
+ if (rfc822_parse_content_type(&parser, content) < 0) {
+ str_truncate(content, 0);
+ return content;
+ }
+ } else {
+ if (rfc822_parse_mime_token(&parser, content) < 0) {
+ str_truncate(content, 0);
+ return content;
+ }
+ }
+
+ /* Content-type value must end here, otherwise it is invalid after all */
+ (void)rfc822_skip_lwsp(&parser);
+ if ( parser.data != parser.end && *parser.data != ';' ) {
+ str_truncate(content, 0);
+ return content;
+ }
+
+ if ( strlist->option == EXT_MIME_OPTION_PARAM ) {
+ string_t *param_val;
+
+ /* MIME parameter */
+ i_assert( strlist->params != NULL );
+
+ // FIXME: not very nice when multiple parameters in the same header
+ // are queried in successive tests.
+ str_truncate(content, 0);
+ rfc2231_parse(&parser, &strlist->param_values);
+
+ param_val = content_type_param_next(strlist);
+ if ( param_val != NULL )
+ content = param_val;
+ } else {
+ /* Get :type/:subtype:/:contenttype value */
+ type = str_c(content);
+ p = strchr(type, '/');
+ switch ( strlist->option ) {
+ case EXT_MIME_OPTION_TYPE:
+ if ( trace ) {
+ sieve_runtime_trace(renv, 0,
+ "extracted MIME type");
+ }
+ if ( p != NULL ) {
+ i_assert( is_ctype );
+ str_truncate(content, (p - type));
+ }
+ break;
+ case EXT_MIME_OPTION_SUBTYPE:
+ if ( p == NULL ) {
+ i_assert( !is_ctype );
+ if ( trace ) {
+ sieve_runtime_trace(renv, 0,
+ "no MIME sub-type for content-disposition");
+ }
+ str_truncate(content, 0);
+ break;
+ }
+
+ i_assert( is_ctype );
+ if ( trace ) {
+ sieve_runtime_trace(renv, 0,
+ "extracted MIME sub-type");
+ }
+ str_delete(content, 0, (p - type) + 1);
+ break;
+ case EXT_MIME_OPTION_CONTENTTYPE:
+ sieve_runtime_trace(renv, 0,
+ "extracted full MIME contenttype");
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Success */
+ return content;
+}
+
+static int content_header_stringlist_next_item
+(struct sieve_stringlist *_strlist, string_t **str_r)
+{
+ struct content_header_stringlist *strlist =
+ (struct content_header_stringlist *)_strlist;
+ const char *hdr_name;
+ int ret;
+
+ if ( strlist->param_values != NULL ) {
+ string_t *param_val;
+
+ i_assert( strlist->option == EXT_MIME_OPTION_PARAM );
+ param_val = content_type_param_next(strlist);
+ if ( param_val != NULL ) {
+ *str_r = param_val;
+ return 1;
+ }
+ }
+
+ if ( (ret=sieve_header_list_next_item
+ (strlist->source, &hdr_name, str_r)) <= 0 ) {
+ if (ret < 0) {
+ _strlist->exec_status =
+ strlist->source->strlist.exec_status;
+ }
+ return ret;
+ }
+
+ *str_r = content_header_parse(strlist, hdr_name, *str_r);
+ return 1;
+}
+
+static void content_header_stringlist_reset
+(struct sieve_stringlist *_strlist)
+{
+ struct content_header_stringlist *strlist =
+ (struct content_header_stringlist *)_strlist;
+ sieve_header_list_reset(strlist->source);
+}
+
+static int content_header_stringlist_get_length
+(struct sieve_stringlist *_strlist)
+{
+ struct content_header_stringlist *strlist =
+ (struct content_header_stringlist *)_strlist;
+ return sieve_header_list_get_length(strlist->source);
+}
+
+static void content_header_stringlist_set_trace
+(struct sieve_stringlist *_strlist, bool trace)
+{
+ struct content_header_stringlist *strlist =
+ (struct content_header_stringlist *)_strlist;
+ sieve_header_list_set_trace(strlist->source, trace);
+}
+
+/*
+ * Header override implementation
+ */
+
+/* Context data */
+
+struct svmo_mime_context {
+ enum ext_mime_option mimeopt;
+ const char *const *params;
+ bool anychild:1;
+};
+
+/* Context coding */
+
+static bool svmo_mime_dump_context
+(const struct sieve_message_override *svmo ATTR_UNUSED,
+ const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ unsigned int anychild, mimeopt;
+
+ if ( !sieve_binary_read_byte(denv->sblock, address, &anychild) )
+ return FALSE;
+ if ( anychild > 0 )
+ sieve_code_dumpf(denv, "anychild");
+
+ if ( !sieve_binary_read_byte(denv->sblock, address, &mimeopt) )
+ return FALSE;
+
+ switch ( mimeopt ) {
+ case EXT_MIME_OPTION_NONE:
+ break;
+ case EXT_MIME_OPTION_TYPE:
+ sieve_code_dumpf(denv, "option: type");
+ break;
+ case EXT_MIME_OPTION_SUBTYPE:
+ sieve_code_dumpf(denv, "option: subtype");
+ break;
+ case EXT_MIME_OPTION_CONTENTTYPE:
+ sieve_code_dumpf(denv, "option: contenttype");
+ break;
+ case EXT_MIME_OPTION_PARAM:
+ sieve_code_dumpf(denv, "option: param");
+ sieve_code_descend(denv);
+ if ( !sieve_opr_stringlist_dump(denv, address, "param-list") )
+ return FALSE;
+ sieve_code_ascend(denv);
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int svmo_mime_read_context
+(const struct sieve_message_override *svmo ATTR_UNUSED,
+ const struct sieve_runtime_env *renv, sieve_size_t *address,
+ void **ho_context)
+{
+ pool_t pool = sieve_result_pool(renv->result); // FIXME: investigate
+ struct svmo_mime_context *ctx;
+ unsigned int anychild = 0, mimeopt = EXT_MIME_OPTION_NONE;
+ struct sieve_stringlist *param_list = NULL;
+ int ret;
+
+ if ( !sieve_binary_read_byte
+ (renv->sblock, address, &anychild) ) {
+ sieve_runtime_trace_error(renv,
+ "anychild: invalid byte");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( !sieve_binary_read_byte
+ (renv->sblock, address, &mimeopt) ||
+ mimeopt > EXT_MIME_OPTION_PARAM ) {
+ sieve_runtime_trace_error(renv,
+ "option: invalid mime option code");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( mimeopt == EXT_MIME_OPTION_PARAM &&
+ (ret=sieve_opr_stringlist_read
+ (renv, address, "param-list", &param_list)) <= 0 )
+ return ret;
+
+ ctx = p_new(pool, struct svmo_mime_context, 1);
+ ctx->anychild = (anychild == 0 ? FALSE : TRUE);
+ ctx->mimeopt = (enum ext_mime_option)mimeopt;
+
+ if ( param_list != NULL && sieve_stringlist_read_all
+ (param_list, pool, &ctx->params) < 0 ) {
+ sieve_runtime_trace_error(renv,
+ "failed to read param-list operand");
+ return param_list->exec_status;
+ }
+
+ *ho_context = (void *) ctx;
+ return SIEVE_EXEC_OK;
+}
+
+/* Override */
+
+static int svmo_mime_header_override
+(const struct sieve_message_override *svmo,
+ const struct sieve_runtime_env *renv, bool mime_decode,
+ struct sieve_stringlist **headers_r)
+{
+ struct svmo_mime_context *ctx =
+ (struct svmo_mime_context *)svmo->context;
+ struct ext_foreverypart_runtime_loop *sfploop;
+ struct sieve_header_list *headers;
+ struct sieve_stringlist *values;
+ int ret;
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING,
+ "header mime override:");
+ sieve_runtime_trace_descend(renv);
+
+ if ( ctx->anychild ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING,
+ "headers from current mime part and children");
+ } else {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING,
+ "headers from current mime part");
+ }
+
+ sfploop = ext_foreverypart_runtime_loop_get_current(renv);
+ if ( sfploop != NULL ) {
+ headers = sieve_mime_header_list_create
+ (renv, *headers_r, &sfploop->part_iter,
+ mime_decode, ctx->anychild);
+ } else if ( ctx->anychild ) {
+ struct sieve_message_part_iter part_iter;
+
+ if ( (ret=sieve_message_part_iter_init
+ (&part_iter, renv)) <= 0 )
+ return ret;
+
+ headers = sieve_mime_header_list_create
+ (renv, *headers_r, &part_iter, mime_decode, TRUE);
+ } else {
+ headers = sieve_message_header_list_create
+ (renv, *headers_r, mime_decode);
+ }
+ values = &headers->strlist;
+
+ switch ( ctx->mimeopt ) {
+ case EXT_MIME_OPTION_NONE:
+ break;
+ case EXT_MIME_OPTION_TYPE:
+ sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING,
+ "extract mime type from header value");
+ break;
+ case EXT_MIME_OPTION_SUBTYPE:
+ sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING,
+ "extract mime subtype from header value");
+ break;
+ case EXT_MIME_OPTION_CONTENTTYPE:
+ sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING,
+ "extract mime contenttype from header value");
+ break;
+ case EXT_MIME_OPTION_PARAM:
+ sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING,
+ "extract mime parameters from header value");
+ break;
+ default:
+ i_unreached();
+ }
+
+ if ( ctx->mimeopt != EXT_MIME_OPTION_NONE ) {
+ values = content_header_stringlist_create
+ (renv, headers, ctx->mimeopt, ctx->params);
+ }
+ *headers_r = values;
+
+ sieve_runtime_trace_ascend(renv);
+ return SIEVE_EXEC_OK;
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/notify/Makefile.am b/pigeonhole/src/lib-sieve/plugins/notify/Makefile.am
new file mode 100644
index 0000000..aaa76b3
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/notify/Makefile.am
@@ -0,0 +1,20 @@
+noinst_LTLIBRARIES = libsieve_ext_notify.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../util \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-notify.c \
+ cmd-denotify.c
+
+libsieve_ext_notify_la_SOURCES = \
+ ext-notify.c \
+ ext-notify-common.c \
+ $(commands)
+
+noinst_HEADERS = \
+ ext-notify-common.h \
+ ext-notify-limits.h
+
diff --git a/pigeonhole/src/lib-sieve/plugins/notify/Makefile.in b/pigeonhole/src/lib-sieve/plugins/notify/Makefile.in
new file mode 100644
index 0000000..910007d
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/notify/Makefile.in
@@ -0,0 +1,699 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/notify
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_notify_la_LIBADD =
+am__objects_1 = cmd-notify.lo cmd-denotify.lo
+am_libsieve_ext_notify_la_OBJECTS = ext-notify.lo ext-notify-common.lo \
+ $(am__objects_1)
+libsieve_ext_notify_la_OBJECTS = $(am_libsieve_ext_notify_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-denotify.Plo \
+ ./$(DEPDIR)/cmd-notify.Plo ./$(DEPDIR)/ext-notify-common.Plo \
+ ./$(DEPDIR)/ext-notify.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_notify_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_notify_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_notify.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../util \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-notify.c \
+ cmd-denotify.c
+
+libsieve_ext_notify_la_SOURCES = \
+ ext-notify.c \
+ ext-notify-common.c \
+ $(commands)
+
+noinst_HEADERS = \
+ ext-notify-common.h \
+ ext-notify-limits.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/notify/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/notify/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_notify.la: $(libsieve_ext_notify_la_OBJECTS) $(libsieve_ext_notify_la_DEPENDENCIES) $(EXTRA_libsieve_ext_notify_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_notify_la_OBJECTS) $(libsieve_ext_notify_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-denotify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-notify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-notify-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-notify.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmd-denotify.Plo
+ -rm -f ./$(DEPDIR)/cmd-notify.Plo
+ -rm -f ./$(DEPDIR)/ext-notify-common.Plo
+ -rm -f ./$(DEPDIR)/ext-notify.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmd-denotify.Plo
+ -rm -f ./$(DEPDIR)/cmd-notify.Plo
+ -rm -f ./$(DEPDIR)/ext-notify-common.Plo
+ -rm -f ./$(DEPDIR)/ext-notify.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/notify/cmd-denotify.c b/pigeonhole/src/lib-sieve/plugins/notify/cmd-denotify.c
new file mode 100644
index 0000000..8769808
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/notify/cmd-denotify.c
@@ -0,0 +1,389 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+
+#include "sieve-common.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-ast.h"
+#include "sieve-commands.h"
+#include "sieve-match-types.h"
+#include "sieve-comparators.h"
+#include "sieve-match.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-result.h"
+
+#include "ext-notify-common.h"
+
+/*
+ * Denotify command
+ *
+ * Syntax:
+ * denotify [MATCH-TYPE string] [<":low" / ":normal" / ":high">]
+ */
+
+static bool cmd_denotify_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool cmd_denotify_pre_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_denotify_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_denotify_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd);
+
+const struct sieve_command_def cmd_denotify = {
+ .identifier = "denotify",
+ .type = SCT_COMMAND,
+ .positional_args = 0,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_denotify_registered,
+ .pre_validate = cmd_denotify_pre_validate,
+ .validate = cmd_denotify_validate,
+ .generate = cmd_denotify_generate
+};
+
+/*
+ * Tagged arguments
+ */
+
+/* Forward declarations */
+
+static bool tag_match_type_is_instance_of
+ (struct sieve_validator *validator, struct sieve_command *cmd,
+ const struct sieve_extension *ext, const char *identifier, void **data);
+static bool tag_match_type_validate
+ (struct sieve_validator *validator, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+/* Argument object */
+
+const struct sieve_argument_def denotify_match_tag = {
+ .identifier = "MATCH-TYPE-STRING",
+ .is_instance_of = tag_match_type_is_instance_of,
+ .validate = tag_match_type_validate
+};
+
+/* Codes for optional operands */
+
+enum cmd_denotify_optional {
+ OPT_END,
+ OPT_IMPORTANCE,
+ OPT_MATCH_TYPE,
+ OPT_MATCH_KEY
+};
+
+/*
+ * Denotify operation
+ */
+
+static bool cmd_denotify_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_denotify_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def denotify_operation = {
+ .mnemonic = "DENOTIFY",
+ .ext_def = &notify_extension,
+ .code = EXT_NOTIFY_OPERATION_DENOTIFY,
+ .dump = cmd_denotify_operation_dump,
+ .execute = cmd_denotify_operation_execute
+};
+
+/*
+ * Command validation context
+ */
+
+struct cmd_denotify_context_data {
+ struct sieve_ast_argument *match_key_arg;
+};
+
+/*
+ * Tag validation
+ */
+
+static bool tag_match_type_is_instance_of
+(struct sieve_validator *valdtr, struct sieve_command *cmd,
+ const struct sieve_extension *ext, const char *identifier, void **data)
+{
+ return match_type_tag.is_instance_of(valdtr, cmd, ext, identifier, data);
+}
+
+static bool tag_match_type_validate
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct cmd_denotify_context_data *cmd_data =
+ (struct cmd_denotify_context_data *) cmd->data;
+ struct sieve_ast_argument *tag = *arg;
+
+ i_assert(tag != NULL);
+
+ if ( !match_type_tag.validate(valdtr, arg, cmd) )
+ return FALSE;
+
+ if ( *arg == NULL ) {
+ sieve_argument_validate_error(valdtr, tag,
+ "the MATCH-TYPE argument (:%s) for the denotify command requires "
+ "an additional key-string parameter, but no more arguments were found",
+ sieve_ast_argument_tag(tag));
+ return FALSE;
+ }
+
+ if ( sieve_ast_argument_type(*arg) != SAAT_STRING )
+ {
+ sieve_argument_validate_error(valdtr, *arg,
+ "the MATCH-TYPE argument (:%s) for the denotify command requires "
+ "an additional key-string parameter, but %s was found",
+ sieve_ast_argument_tag(tag), sieve_ast_argument_name(*arg));
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, cmd, *arg, FALSE) )
+ return FALSE;
+
+ tag->argument->def = &match_type_tag;
+ tag->argument->ext = NULL;
+
+ (*arg)->argument->id_code = OPT_MATCH_KEY;
+ cmd_data->match_key_arg = *arg;
+
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+/*
+ * Command registration
+ */
+
+static bool cmd_denotify_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &denotify_match_tag, OPT_MATCH_TYPE);
+
+ ext_notify_register_importance_tags(valdtr, cmd_reg, ext, OPT_IMPORTANCE);
+
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool cmd_denotify_pre_validate
+(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_command *cmd)
+{
+ struct cmd_denotify_context_data *ctx_data;
+
+ /* Assign context */
+ ctx_data = p_new(sieve_command_pool(cmd),
+ struct cmd_denotify_context_data, 1);
+ cmd->data = (void *) ctx_data;
+
+ return TRUE;
+}
+
+static bool cmd_denotify_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct cmd_denotify_context_data *ctx_data =
+ (struct cmd_denotify_context_data *) cmd->data;
+ struct sieve_ast_argument *key_arg = ctx_data->match_key_arg;
+ const struct sieve_match_type mcht_default =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ const struct sieve_comparator cmp_default =
+ SIEVE_COMPARATOR_DEFAULT(i_octet_comparator);
+
+ if ( key_arg != NULL ) {
+ if ( !sieve_match_type_validate
+ (valdtr, cmd, key_arg, &mcht_default, &cmp_default) )
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_denotify_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &denotify_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool cmd_denotify_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ const struct sieve_operation *op = denv->oprtn;
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op));
+ sieve_code_descend(denv);
+
+ for (;;) {
+ int opt;
+ bool opok = TRUE;
+
+ if ( (opt=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 )
+ return FALSE;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_MATCH_KEY:
+ opok = sieve_opr_string_dump(denv, address, "key-string");
+ break;
+ case OPT_MATCH_TYPE:
+ opok = sieve_opr_match_type_dump(denv, address);
+ break;
+ case OPT_IMPORTANCE:
+ opok = sieve_opr_number_dump(denv, address, "importance");
+ break;
+ default:
+ return FALSE;
+ }
+
+ if ( !opok ) return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code execution
+ */
+
+static int cmd_denotify_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ int opt_code = 0;
+ struct sieve_match_type mcht =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ const struct sieve_comparator cmp =
+ SIEVE_COMPARATOR_DEFAULT(i_octet_comparator);
+ struct sieve_stringlist *match_key = NULL;
+ sieve_number_t importance = 0;
+ struct sieve_match_context *mctx;
+ struct sieve_result_iterate_context *rictx;
+ const struct sieve_action *action;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Optional operands */
+
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_opr_optional_read(renv, address, &opt_code)) < 0 )
+ return SIEVE_EXEC_BIN_CORRUPT;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_MATCH_TYPE:
+ ret = sieve_opr_match_type_read(renv, address, &mcht);
+ break;
+ case OPT_MATCH_KEY:
+ ret = sieve_opr_stringlist_read(renv, address, "match key", &match_key);
+ break;
+ case OPT_IMPORTANCE:
+ ret = sieve_opr_number_read(renv, address, "importance", &importance);
+ break;
+ default:
+ sieve_runtime_trace_error(renv, "unknown optional operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( ret <= 0 ) return ret;
+ }
+
+ /*
+ * Perform operation
+ */
+
+ /* Enforce 0 < importance < 4 (just to be sure) */
+
+ if ( importance < 1 )
+ importance = 1;
+ else if ( importance > 3 )
+ importance = 3;
+
+ /* Trace */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "denotify action");
+
+ /* Either do string matching or just kill all notify actions */
+ if ( match_key != NULL ) {
+ /* Initialize match */
+ mctx = sieve_match_begin(renv, &mcht, &cmp);
+
+ /* Iterate through all notify actions and delete those that match */
+ rictx = sieve_result_iterate_init(renv->result);
+
+ while ( (action=sieve_result_iterate_next(rictx, NULL)) != NULL ) {
+ if ( sieve_action_is(action, act_notify_old) ) {
+ struct ext_notify_action *nact =
+ (struct ext_notify_action *) action->context;
+
+ if ( importance == 0 || nact->importance == importance ) {
+ int match;
+
+ if ( (match=sieve_match_value
+ (mctx, nact->id, strlen(nact->id), match_key)) < 0 )
+ break;
+
+ if ( match > 0 )
+ sieve_result_iterate_delete(rictx);
+ }
+ }
+ }
+
+ /* Finish match */
+ if ( sieve_match_end(&mctx, &ret) < 0 )
+ return ret;
+
+ } else {
+ /* Delete all notify actions */
+ rictx = sieve_result_iterate_init(renv->result);
+
+ while ( (action=sieve_result_iterate_next(rictx, NULL)) != NULL ) {
+
+ if ( sieve_action_is(action, act_notify_old) ) {
+ struct ext_notify_action *nact =
+ (struct ext_notify_action *) action->context;
+
+ if ( importance == 0 || nact->importance == importance )
+ sieve_result_iterate_delete(rictx);
+ }
+ }
+ }
+
+ return SIEVE_EXEC_OK;
+}
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/notify/cmd-notify.c b/pigeonhole/src/lib-sieve/plugins/notify/cmd-notify.c
new file mode 100644
index 0000000..a683c31
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/notify/cmd-notify.c
@@ -0,0 +1,900 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "ioloop.h"
+#include "str-sanitize.h"
+#include "ostream.h"
+#include "message-date.h"
+#include "mail-storage.h"
+
+#include "rfc2822.h"
+
+#include "sieve-common.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-result.h"
+#include "sieve-address.h"
+#include "sieve-message.h"
+#include "sieve-smtp.h"
+
+#include "ext-notify-common.h"
+#include "ext-notify-limits.h"
+
+#include <ctype.h>
+
+/* Notify command (DEPRECATED)
+ *
+ * Syntax:
+ * notify [":method" string] [":id" string] [":options" string-list]
+ * [<":low" / ":normal" / ":high">] ["message:" string]
+ *
+ */
+
+static bool
+cmd_notify_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool
+cmd_notify_pre_validate(struct sieve_validator *valdtr,
+ struct sieve_command *cmd);
+static bool
+cmd_notify_validate(struct sieve_validator *valdtr,
+ struct sieve_command *cmd);
+static bool
+cmd_notify_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+const struct sieve_command_def cmd_notify_old = {
+ .identifier = "notify",
+ .type = SCT_COMMAND,
+ .positional_args = 0,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_notify_registered,
+ .pre_validate = cmd_notify_pre_validate,
+ .validate = cmd_notify_validate,
+ .generate = cmd_notify_generate
+};
+
+/*
+ * Tagged arguments
+ */
+
+/* Forward declarations */
+
+static bool
+cmd_notify_validate_string_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool
+cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+/* Argument objects */
+
+static const struct sieve_argument_def notify_method_tag = {
+ .identifier = "method",
+ .validate = cmd_notify_validate_string_tag
+};
+
+static const struct sieve_argument_def notify_options_tag = {
+ .identifier = "options",
+ .validate = cmd_notify_validate_stringlist_tag
+};
+
+static const struct sieve_argument_def notify_id_tag = {
+ .identifier = "id",
+ .validate = cmd_notify_validate_string_tag
+};
+
+static const struct sieve_argument_def notify_message_tag = {
+ .identifier = "message",
+ .validate = cmd_notify_validate_string_tag
+};
+
+/*
+ * Notify operation
+ */
+
+static bool
+cmd_notify_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+cmd_notify_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def notify_old_operation = {
+ .mnemonic = "NOTIFY",
+ .ext_def = &notify_extension,
+ .code = EXT_NOTIFY_OPERATION_NOTIFY,
+ .dump = cmd_notify_operation_dump,
+ .execute = cmd_notify_operation_execute
+};
+
+/* Codes for optional operands */
+
+enum cmd_notify_optional {
+ OPT_END,
+ OPT_MESSAGE,
+ OPT_IMPORTANCE,
+ OPT_OPTIONS,
+ OPT_ID
+};
+
+/*
+ * Notify action
+ */
+
+/* Forward declarations */
+
+static int
+act_notify_check_duplicate(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other);
+static void
+act_notify_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv, bool *keep);
+static int
+act_notify_commit(const struct sieve_action_exec_env *aenv,
+ void *tr_context);
+
+/* Action object */
+
+const struct sieve_action_def act_notify_old = {
+ .name = "notify",
+ .check_duplicate = act_notify_check_duplicate,
+ .print = act_notify_print,
+ .commit = act_notify_commit
+};
+
+/*
+ * Command validation context
+ */
+
+struct cmd_notify_context_data {
+ struct sieve_ast_argument *id;
+ struct sieve_ast_argument *method;
+ struct sieve_ast_argument *options;
+ struct sieve_ast_argument *message;
+};
+
+/*
+ * Tag validation
+ */
+
+static bool
+cmd_notify_validate_string_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+ struct cmd_notify_context_data *ctx_data =
+ (struct cmd_notify_context_data *)cmd->data;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg, 1);
+
+ /* Check syntax:
+ * :id <string>
+ * :method <string>
+ * :message <string>
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_STRING, FALSE))
+ return FALSE;
+
+ if (sieve_argument_is(tag, notify_method_tag)) {
+ ctx_data->method = *arg;
+
+ /* Removed */
+ *arg = sieve_ast_arguments_detach(*arg, 1);
+ } else if (sieve_argument_is(tag, notify_id_tag)) {
+ ctx_data->id = *arg;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+ } else if (sieve_argument_is(tag, notify_message_tag)) {
+ ctx_data->message = *arg;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+ }
+ return TRUE;
+}
+
+static bool
+cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+ struct cmd_notify_context_data *ctx_data =
+ (struct cmd_notify_context_data *)cmd->data;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :options string-list
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_STRING_LIST, FALSE))
+ return FALSE;
+
+ /* Assign context */
+ ctx_data->options = *arg;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+/*
+ * Command registration
+ */
+
+static bool
+cmd_notify_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &notify_method_tag, 0);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &notify_id_tag, OPT_ID);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &notify_message_tag, OPT_MESSAGE);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &notify_options_tag, OPT_OPTIONS);
+
+ ext_notify_register_importance_tags(valdtr, cmd_reg, ext,
+ OPT_IMPORTANCE);
+
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool
+cmd_notify_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_command *cmd)
+{
+ struct cmd_notify_context_data *ctx_data;
+
+ /* Create context */
+ ctx_data = p_new(sieve_command_pool(cmd),
+ struct cmd_notify_context_data, 1);
+ cmd->data = ctx_data;
+
+ return TRUE;
+}
+
+static int
+cmd_notify_address_validate(void *context, struct sieve_ast_argument *arg)
+{
+ struct sieve_validator *valdtr = (struct sieve_validator *)context;
+
+ if (sieve_argument_is_string_literal(arg)) {
+ string_t *address = sieve_ast_argument_str(arg);
+ const char *error;
+ int result;
+
+ T_BEGIN {
+ result = (sieve_address_validate_str(address, &error) ?
+ 1 : -1);
+
+ if (result <= 0) {
+ sieve_argument_validate_error(
+ valdtr, arg,
+ "specified :options address '%s' is invalid for "
+ "the mailto notify method: %s",
+ str_sanitize(str_c(address), 128),
+ error);
+ }
+ } T_END;
+
+ return result;
+ }
+
+ return 1;
+}
+
+static bool
+cmd_notify_validate(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct cmd_notify_context_data *ctx_data =
+ (struct cmd_notify_context_data *)cmd->data;
+
+ /* Check :method argument */
+ if (ctx_data->method != NULL) {
+ const char *method = sieve_ast_argument_strc(ctx_data->method);
+
+ if (strcasecmp(method, "mailto") != 0) {
+ sieve_command_validate_error(
+ valdtr, cmd,
+ "the notify command of the deprecated notify extension "
+ "only supports the 'mailto' notification method");
+ return FALSE;
+ }
+ }
+
+ /* Check :options argument */
+ if (ctx_data->options != NULL) {
+ struct sieve_ast_argument *option = ctx_data->options;
+
+ /* Parse and check options */
+ if (sieve_ast_stringlist_map(
+ &option, (void *)valdtr,
+ cmd_notify_address_validate) <= 0) {
+ return FALSE;
+ }
+ } else {
+ sieve_command_validate_warning(
+ valdtr, cmd,
+ "no :options (and hence recipients) specified for the notify command");
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+cmd_notify_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &notify_old_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+cmd_notify_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "NOTIFY");
+ sieve_code_descend(denv);
+
+ /* Dump optional operands */
+ for (;;) {
+ int opt;
+ bool opok = TRUE;
+
+ if ((opt = sieve_opr_optional_dump(denv, address,
+ &opt_code)) < 0)
+ return FALSE;
+
+ if (opt == 0)
+ break;
+
+ switch (opt_code) {
+ case OPT_IMPORTANCE:
+ opok = sieve_opr_number_dump(denv, address,
+ "importance");
+ break;
+ case OPT_ID:
+ opok = sieve_opr_string_dump(denv, address,
+ "id");
+ break;
+ case OPT_OPTIONS:
+ opok = sieve_opr_stringlist_dump(denv, address,
+ "options");
+ break;
+ case OPT_MESSAGE:
+ opok = sieve_opr_string_dump(denv, address,
+ "message");
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (!opok)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code execution
+ */
+
+
+static int
+cmd_notify_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct ext_notify_action *act;
+ pool_t pool;
+ int opt_code = 0;
+ sieve_number_t importance = 1;
+ struct sieve_stringlist *options = NULL;
+ string_t *message = NULL, *id = NULL;
+ int ret = 0;
+
+ /*
+ * Read operands
+ */
+
+ /* Optional operands */
+
+ for (;;) {
+ int opt;
+
+ if ((opt = sieve_opr_optional_read(renv, address,
+ &opt_code)) < 0)
+ return SIEVE_EXEC_BIN_CORRUPT;
+
+ if (opt == 0)
+ break;
+
+ switch (opt_code) {
+ case OPT_IMPORTANCE:
+ ret = sieve_opr_number_read(renv, address, "importance",
+ &importance);
+ break;
+ case OPT_ID:
+ ret = sieve_opr_string_read(renv, address, "id", &id);
+ break;
+ case OPT_MESSAGE:
+ ret = sieve_opr_string_read(renv, address, "from",
+ &message);
+ break;
+ case OPT_OPTIONS:
+ ret = sieve_opr_stringlist_read(renv, address,
+ "options", &options);
+ break;
+ default:
+ sieve_runtime_trace_error(
+ renv, "unknown optional operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if (ret <= 0) return ret;
+ }
+
+ /*
+ * Perform operation
+ */
+
+ /* Enforce 0 < importance < 4 (just to be sure) */
+
+ if (importance < 1)
+ importance = 1;
+ else if (importance > 3)
+ importance = 3;
+
+ /* Trace */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "notify action");
+
+ /* Compose action */
+ if (options != NULL) {
+ string_t *raw_address;
+ string_t *out_message;
+
+ pool = sieve_result_pool(renv->result);
+ act = p_new(pool, struct ext_notify_action, 1);
+ if (id != NULL)
+ act->id = p_strdup(pool, str_c(id));
+ act->importance = importance;
+
+ /* Process message */
+
+ out_message = t_str_new(1024);
+ if ((ret = ext_notify_construct_message(
+ renv, (message == NULL ? NULL : str_c(message)),
+ out_message)) <= 0)
+ return ret;
+ act->message = p_strdup(pool, str_c(out_message));
+
+ /* Normalize and verify all :options addresses */
+
+ sieve_stringlist_reset(options);
+
+ p_array_init(&act->recipients, pool, 4);
+
+ raw_address = NULL;
+ while ((ret = sieve_stringlist_next_item(
+ options, &raw_address)) > 0) {
+ const char *error = NULL;
+ const struct smtp_address *address;
+
+ /* Add if valid address */
+ address = sieve_address_parse_str(raw_address, &error);
+ if (address != NULL) {
+ const struct ext_notify_recipient *rcpts;
+ unsigned int rcpt_count, i;
+
+ /* Prevent duplicates */
+ rcpts = array_get(&act->recipients, &rcpt_count);
+
+ for (i = 0; i < rcpt_count; i++) {
+ if (smtp_address_equals(rcpts[i].address,
+ address))
+ break;
+ }
+
+ /* Add only if unique */
+ if (i != rcpt_count) {
+ sieve_runtime_warning(
+ renv, NULL,
+ "duplicate recipient '%s' specified in the :options argument of "
+ "the deprecated notify command",
+ str_sanitize(str_c(raw_address), 128));
+
+ } else if (array_count(&act->recipients) >=
+ EXT_NOTIFY_MAX_RECIPIENTS) {
+ sieve_runtime_warning(renv, NULL,
+ "more than the maximum %u recipients are specified "
+ "for the deprecated notify command; "
+ "the rest is discarded",
+ EXT_NOTIFY_MAX_RECIPIENTS);
+ break;
+
+ } else {
+ struct ext_notify_recipient recipient;
+
+ recipient.full =
+ p_strdup(pool, str_c(raw_address));
+ recipient.address =
+ smtp_address_clone(pool, address);
+
+ array_append(&act->recipients, &recipient, 1);
+ }
+ } else {
+ sieve_runtime_error(
+ renv, NULL,
+ "specified :options address '%s' is invalid for "
+ "the deprecated notify command: %s",
+ str_sanitize(str_c(raw_address), 128), error);
+ return SIEVE_EXEC_FAILURE;
+ }
+ }
+
+ if (ret < 0) {
+ sieve_runtime_trace_error(
+ renv, "invalid options stringlist");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if (sieve_result_add_action(renv, this_ext, "notify",
+ &act_notify_old, NULL, (void *)act,
+ 0, FALSE) < 0)
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ return SIEVE_EXEC_OK;
+}
+
+/*
+ * Action
+ */
+
+/* Runtime verification */
+
+static int
+act_notify_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED,
+ const struct sieve_action *act ATTR_UNUSED,
+ const struct sieve_action *act_other ATTR_UNUSED)
+{
+ struct ext_notify_action *new_nact, *old_nact;
+ const struct ext_notify_recipient *new_rcpts;
+ const struct ext_notify_recipient *old_rcpts;
+ unsigned int new_count, old_count, i, j;
+ unsigned int del_start = 0, del_len = 0;
+
+ if (act->context == NULL || act_other->context == NULL)
+ return 0;
+
+ new_nact = (struct ext_notify_action *)act->context;
+ old_nact = (struct ext_notify_action *)act_other->context;
+
+ new_rcpts = array_get(&new_nact->recipients, &new_count);
+ old_rcpts = array_get(&old_nact->recipients, &old_count);
+
+ for (i = 0; i < new_count; i++) {
+ for (j = 0; j < old_count; j++) {
+ if (smtp_address_equals(new_rcpts[i].address,
+ old_rcpts[j].address))
+ break;
+ }
+
+ if (j == old_count) {
+ /* Not duplicate */
+ if (del_len > 0) {
+ /* Perform pending deletion */
+ array_delete(&new_nact->recipients,
+ del_start, del_len);
+
+ /* Make sure the loop integrity is maintained */
+ i -= del_len;
+ new_rcpts = array_get(&new_nact->recipients,
+ &new_count);
+ }
+
+ del_len = 0;
+ } else {
+ /* Mark deletion */
+ if (del_len == 0)
+ del_start = i;
+ del_len++;
+ }
+ }
+
+ /* Perform pending deletion */
+ if (del_len > 0)
+ array_delete(&new_nact->recipients, del_start, del_len);
+
+ return (array_count(&new_nact->recipients) > 0 ? 0 : 1);
+}
+
+/* Result printing */
+
+static void
+act_notify_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep ATTR_UNUSED)
+{
+ const struct ext_notify_action *act =
+ (const struct ext_notify_action *)action->context;
+ const struct ext_notify_recipient *recipients;
+ unsigned int count, i;
+
+ sieve_result_action_printf(
+ rpenv, "send (deprecated) notification with method 'mailto':");
+
+ /* Print main method parameters */
+
+ sieve_result_printf(rpenv, " => importance : %llu\n",
+ (unsigned long long)act->importance);
+
+ if (act->message != NULL) {
+ sieve_result_printf(
+ rpenv, " => message : %s\n", act->message);
+ }
+ if (act->id != NULL) {
+ sieve_result_printf(
+ rpenv, " => id : %s \n", act->id);
+ }
+
+ /* Print mailto: recipients */
+
+ sieve_result_printf(rpenv, " => recipients :\n");
+
+ recipients = array_get(&act->recipients, &count);
+ if (count == 0) {
+ sieve_result_printf(
+ rpenv, " NONE, action has no effect\n");
+ } else {
+ for (i = 0; i < count; i++) {
+ sieve_result_printf(
+ rpenv, " + To: %s\n", recipients[i].full);
+ }
+ }
+
+ /* Finish output with an empty line */
+
+ sieve_result_printf(rpenv, "\n");
+}
+
+/* Result execution */
+
+static bool contains_8bit(const char *msg)
+{
+ const unsigned char *s = (const unsigned char *)msg;
+
+ for (; *s != '\0'; s++) {
+ if ((*s & 0x80) != 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static bool
+act_notify_send(const struct sieve_action_exec_env *aenv,
+ const struct ext_notify_action *act)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ const struct sieve_script_env *senv = eenv->scriptenv;
+ const struct ext_notify_recipient *recipients;
+ struct sieve_smtp_context *sctx;
+ unsigned int count, i;
+ struct ostream *output;
+ string_t *msg, *to, *all;
+ const char *outmsgid, *error;
+ int ret;
+
+ /* Get recipients */
+ recipients = array_get(&act->recipients, &count);
+ if (count == 0) {
+ sieve_result_warning(
+ aenv, "notify action specifies no recipients; "
+ "action has no effect");
+ return TRUE;
+ }
+
+ /* Just to be sure */
+ if (!sieve_smtp_available(senv)) {
+ sieve_result_global_warning(
+ aenv, "notify action has no means to send mail");
+ return TRUE;
+ }
+
+ /* Compose common headers */
+ msg = t_str_new(512);
+ rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION);
+ rfc2822_header_write(msg, "Date", message_date_create(ioloop_time));
+
+ /* Set importance */
+ switch (act->importance) {
+ case 1:
+ rfc2822_header_write(msg, "X-Priority", "1 (Highest)");
+ rfc2822_header_write(msg, "Importance", "High");
+ break;
+ case 3:
+ rfc2822_header_write(msg, "X-Priority", "5 (Lowest)");
+ rfc2822_header_write(msg, "Importance", "Low");
+ break;
+ case 2:
+ default:
+ rfc2822_header_write(msg, "X-Priority", "3 (Normal)");
+ rfc2822_header_write(msg, "Importance", "Normal");
+ break;
+ }
+
+ rfc2822_header_write(msg, "From", sieve_get_postmaster_address(senv));
+
+ rfc2822_header_write(msg, "Subject", "[SIEVE] New mail notification");
+
+ rfc2822_header_write(msg, "Auto-Submitted", "auto-generated (notify)");
+ rfc2822_header_write(msg, "Precedence", "bulk");
+
+ rfc2822_header_write(msg, "MIME-Version", "1.0");
+ if (contains_8bit(act->message)) {
+ rfc2822_header_write(msg, "Content-Type",
+ "text/plain; charset=utf-8");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit");
+ } else {
+ rfc2822_header_write(msg, "Content-Type",
+ "text/plain; charset=us-ascii");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit");
+ }
+
+ outmsgid = sieve_message_get_new_id(eenv->svinst);
+ rfc2822_header_write(msg, "Message-ID", outmsgid);
+
+ if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 &&
+ sieve_message_get_sender(aenv->msgctx) != NULL) {
+ sctx = sieve_smtp_start(senv, sieve_get_postmaster_smtp(senv));
+ } else {
+ sctx = sieve_smtp_start(senv, NULL);
+ }
+
+ /* Add all recipients (and compose To header field) */
+ to = t_str_new(128);
+ all = t_str_new(256);
+ for (i = 0; i < count; i++) {
+ sieve_smtp_add_rcpt(sctx, recipients[i].address);
+ if (i > 0)
+ str_append(to, ", ");
+ str_append(to, recipients[i].full);
+ if (i < 3) {
+ if (i > 0)
+ str_append(all, ", ");
+ str_append(all, smtp_address_encode_path(
+ recipients[i].address));
+ } else if (i == 3) {
+ str_printfa(all, ", ... (%u total)", count);
+ }
+ }
+
+ rfc2822_header_write_address(msg, "To", str_c(to));
+
+ /* Generate message body */
+ str_printfa(msg, "\r\n%s\r\n", act->message);
+
+ output = sieve_smtp_send(sctx);
+ o_stream_nsend(output, str_data(msg), str_len(msg));
+
+ if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) {
+ if (ret < 0) {
+ sieve_result_global_error(
+ aenv, "failed to send mail notification to %s: "
+ "%s (temporary failure)", str_c(all),
+ str_sanitize(error, 512));
+ } else {
+ sieve_result_global_log_error(
+ aenv, "failed to send mail notification to %s: "
+ "%s (permanent failure)", str_c(all),
+ str_sanitize(error, 512));
+ }
+ } else {
+ struct event_passthrough *e =
+ sieve_action_create_finish_event(aenv)->
+ add_str("notify_target", str_c(all));
+
+ sieve_result_event_log(aenv, e->event(),
+ "sent mail notification to %s",
+ str_c(all));
+ }
+
+ return TRUE;
+}
+
+static int
+act_notify_commit(const struct sieve_action_exec_env *aenv,
+ void *tr_context ATTR_UNUSED)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct mail *mail = eenv->msgdata->mail;
+ const struct ext_notify_action *act =
+ (const struct ext_notify_action *)aenv->action->context;
+ const char *const *hdsp;
+ bool result;
+ int ret;
+
+ /* Is the message an automatic reply ? */
+ if ((ret = mail_get_headers(mail, "auto-submitted", &hdsp)) < 0) {
+ return sieve_result_mail_error(
+ aenv, mail,
+ "failed to read `auto-submitted' header field");
+ }
+
+ /* Theoretically multiple headers could exist, so lets make sure */
+ if (ret > 0) {
+ while (*hdsp != NULL) {
+ if (strcasecmp(*hdsp, "no") != 0) {
+ const struct smtp_address *sender = NULL;
+ const char *from;
+
+ if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0)
+ sender = sieve_message_get_sender(aenv->msgctx);
+ from = (sender == NULL ? "" :
+ t_strdup_printf(" from <%s>",
+ smtp_address_encode(sender)));
+
+ sieve_result_global_log(
+ aenv,
+ "not sending notification for auto-submitted message%s",
+ from);
+ return SIEVE_EXEC_OK;
+ }
+ hdsp++;
+ }
+ }
+
+ T_BEGIN {
+ result = act_notify_send(aenv, act);
+ } T_END;
+
+ if (!result)
+ return SIEVE_EXEC_FAILURE;
+ eenv->exec_status->significant_action_executed = TRUE;
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.c b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.c
new file mode 100644
index 0000000..589b21b
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.c
@@ -0,0 +1,361 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "istream.h"
+#include "rfc822-parser.h"
+#include "message-parser.h"
+#include "message-decoder.h"
+#include "mail-storage.h"
+
+#include "sieve-common.h"
+#include "sieve-code.h"
+#include "sieve-message.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-result.h"
+
+#include "ext-notify-common.h"
+
+#include <ctype.h>
+
+/*
+ * Importance argument
+ */
+
+static bool
+tag_importance_validate(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+static const struct sieve_argument_def importance_low_tag = {
+ .identifier = "low",
+ .validate = tag_importance_validate,
+};
+
+static const struct sieve_argument_def importance_normal_tag = {
+ .identifier = "normal",
+ .validate = tag_importance_validate,
+};
+
+static const struct sieve_argument_def importance_high_tag = {
+ .identifier = "high",
+ .validate = tag_importance_validate,
+};
+
+static bool
+tag_importance_validate(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd ATTR_UNUSED)
+{
+ struct sieve_ast_argument *tag = *arg;
+
+ if (sieve_argument_is(tag, importance_low_tag))
+ sieve_ast_argument_number_substitute(tag, 3);
+ else if (sieve_argument_is(tag, importance_normal_tag))
+ sieve_ast_argument_number_substitute(tag, 2);
+ else
+ sieve_ast_argument_number_substitute(tag, 1);
+
+ tag->argument = sieve_argument_create(tag->ast, &number_argument,
+ tag->argument->ext,
+ tag->argument->id_code);
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+void ext_notify_register_importance_tags(
+ struct sieve_validator *valdtr,
+ struct sieve_command_registration *cmd_reg,
+ const struct sieve_extension *ext, unsigned int id_code)
+{
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &importance_low_tag, id_code);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &importance_normal_tag, id_code);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &importance_high_tag, id_code);
+}
+
+/*
+ * Body extraction
+ */
+
+/* FIXME: overlaps somewhat with body extension */
+
+struct ext_notify_message_context {
+ pool_t pool;
+ buffer_t *body_text;
+};
+
+static struct ext_notify_message_context *
+ext_notify_get_message_context(const struct sieve_extension *this_ext,
+ struct sieve_message_context *msgctx)
+{
+ struct ext_notify_message_context *ctx;
+
+ /* Get message context (contains cached message body information) */
+ ctx = (struct ext_notify_message_context *)
+ sieve_message_context_extension_get(msgctx, this_ext);
+
+ /* Create it if it does not exist already */
+ if (ctx == NULL) {
+ pool_t pool = sieve_message_context_pool(msgctx);
+ ctx = p_new(pool, struct ext_notify_message_context, 1);
+ ctx->pool = pool;
+ ctx->body_text = NULL;
+
+ /* Register context */
+ sieve_message_context_extension_set(msgctx, this_ext,
+ (void *)ctx);
+ }
+
+ return ctx;
+}
+
+static bool _is_text_content(const struct message_header_line *hdr)
+{
+ struct rfc822_parser_context parser;
+ string_t *content_type;
+ const char *data;
+
+ /* Initialize parsing */
+ rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
+ (void)rfc822_skip_lwsp(&parser);
+
+ /* Parse content type */
+ content_type = t_str_new(64);
+ if (rfc822_parse_content_type(&parser, content_type) < 0)
+ return FALSE;
+
+ /* Content-type value must end here, otherwise it is invalid after all
+ */
+ (void)rfc822_skip_lwsp(&parser);
+ if (parser.data != parser.end && *parser.data != ';')
+ return FALSE;
+
+ /* Success */
+ data = str_c(content_type);
+ if (str_begins(data, "text/"))
+ return TRUE;
+ return FALSE;
+}
+
+static int
+cmd_notify_extract_body_text(const struct sieve_runtime_env *renv,
+ const char **body_text_r, size_t *body_size_r)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct ext_notify_message_context *mctx;
+ struct mail *mail = eenv->msgdata->mail;
+ struct message_parser_ctx *parser;
+ struct message_decoder_context *decoder;
+ struct message_part *parts;
+ struct message_block block, decoded;
+ struct istream *input;
+ bool is_text, save_body;
+ int ret = 1;
+
+ *body_text_r = NULL;
+ *body_size_r = 0;
+
+ /* Return cached result if available */
+ mctx = ext_notify_get_message_context(this_ext, renv->msgctx);
+ if (mctx->body_text != NULL) {
+ *body_text_r = (const char *)
+ buffer_get_data(mctx->body_text, body_size_r);
+ return SIEVE_EXEC_OK;
+ }
+
+ /* Create buffer */
+ mctx->body_text = buffer_create_dynamic(mctx->pool, 1024*64);
+
+ /* Get the message stream */
+ if (mail_get_stream(mail, NULL, NULL, &input) < 0) {
+ return sieve_runtime_mail_error(renv, mail, "notify action: "
+ "failed to read input message");
+ }
+
+ /* Initialize body decoder */
+ decoder = message_decoder_init(NULL, 0);
+
+ struct message_parser_settings mparser_set = {
+ .hdr_flags = 0,
+ .flags = 0,
+ };
+ parser = message_parser_init(mctx->pool, input, &mparser_set);
+ is_text = TRUE;
+ save_body = FALSE;
+ while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
+ if (block.hdr != NULL || block.size == 0) {
+ /* Decode block */
+ (void)message_decoder_decode_next_block(decoder, &block,
+ &decoded);
+
+ /* Check for end of headers */
+ if (block.hdr == NULL) {
+ save_body = is_text;
+ continue;
+ }
+
+ /* We're interested of only Content-Type: header */
+ if (strcasecmp(block.hdr->name, "Content-Type") != 0)
+ continue;
+
+ /* Header can have folding whitespace. Acquire the full
+ value before continuing */
+ if (block.hdr->continues) {
+ block.hdr->use_full_value = TRUE;
+ continue;
+ }
+
+ /* Is it a text part? */
+ T_BEGIN {
+ is_text = _is_text_content(block.hdr);
+ } T_END;
+
+ continue;
+ }
+
+ /* Read text body */
+ if (save_body) {
+ (void)message_decoder_decode_next_block(decoder, &block,
+ &decoded);
+ buffer_append(mctx->body_text, decoded.data,
+ decoded.size);
+ is_text = TRUE;
+ }
+ }
+
+ /* Cleanup */
+ (void)message_parser_deinit(&parser, &parts);
+ message_decoder_deinit(&decoder);
+
+ if (ret < 0 && input->stream_errno != 0) {
+ sieve_runtime_critical(renv, NULL, "notify action: "
+ "failed to read input message",
+ "notify action: read(%s) failed: %s",
+ i_stream_get_name(input),
+ i_stream_get_error(input));
+ return SIEVE_EXEC_TEMP_FAILURE;
+ }
+
+ /* Return status */
+ *body_text_r = (const char *)buffer_get_data(mctx->body_text,
+ body_size_r);
+ return SIEVE_EXEC_OK;
+}
+
+int ext_notify_construct_message(const struct sieve_runtime_env *renv,
+ const char *msg_format,
+ string_t *out_msg)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ const struct sieve_message_data *msgdata = eenv->msgdata;
+ struct sieve_message_context *msgctx = renv->msgctx;
+ const struct smtp_address *return_path =
+ sieve_message_get_sender(msgctx);
+ const char *p;
+ int ret;
+
+ if (msg_format == NULL)
+ msg_format = "$from$: $subject$";
+
+ /* Scan message for substitutions */
+ p = msg_format;
+ while (*p != '\0') {
+ const char *header;
+
+ if (strncasecmp(p, "$from$", 6) == 0) {
+ p += 6;
+
+ /* Fetch sender from original message */
+ if ((ret = mail_get_first_header_utf8(
+ msgdata->mail, "from", &header)) < 0) {
+ return sieve_runtime_mail_error(
+ renv, msgdata->mail,
+ "failed to read header field `from'");
+ }
+ if (ret > 0)
+ str_append(out_msg, header);
+ } else if (strncasecmp(p, "$env-from$", 10) == 0) {
+ p += 10;
+
+ if (return_path != NULL)
+ smtp_address_write(out_msg, return_path);
+ } else if (strncasecmp(p, "$subject$", 9) == 0) {
+ p += 9;
+
+ /* Fetch sender from oriinal message */
+ if ((ret = mail_get_first_header_utf8(
+ msgdata->mail, "subject", &header)) < 0) {
+ return sieve_runtime_mail_error(
+ renv, msgdata->mail,
+ "failed to read header field `subject'");
+ }
+ if (ret > 0)
+ str_append(out_msg, header);
+ } else if (strncasecmp(p, "$text", 5) == 0 &&
+ (p[5] == '[' || p[5] == '$')) {
+ size_t num = 0;
+ const char *begin = p;
+ bool valid = TRUE;
+
+ p += 5;
+ if (*p == '[') {
+ p += 1;
+
+ while (i_isdigit(*p)) {
+ num = num * 10 + (*p - '0');
+ p++;
+ }
+
+ if (*p++ != ']' || *p++ != '$') {
+ str_append_data(out_msg, begin,
+ p-begin);
+ valid = FALSE;
+ }
+ } else {
+ p += 1;
+ }
+
+ if (valid) {
+ size_t body_size;
+ const char *body_text;
+
+ if ((ret = cmd_notify_extract_body_text(
+ renv, &body_text, &body_size)) <= 0)
+ return ret;
+
+ if (num > 0 && num < body_size) {
+ str_append_data(out_msg, body_text,
+ num);
+ } else {
+ str_append_data(out_msg, body_text,
+ body_size);
+ }
+ }
+ } else {
+ size_t len;
+
+ /* Find next substitution */
+ len = strcspn(p + 1, "$") + 1;
+
+ /* Copy normal text */
+ str_append_data(out_msg, p, len);
+ p += len;
+ }
+ }
+
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.h b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.h
new file mode 100644
index 0000000..09db2cb
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-common.h
@@ -0,0 +1,66 @@
+#ifndef EXT_NOTIFY_COMMON_H
+#define EXT_NOTIFY_COMMON_H
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def notify_extension;
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def cmd_notify_old;
+extern const struct sieve_command_def cmd_denotify;
+
+/*
+ * Arguments
+ */
+
+void ext_notify_register_importance_tags(
+ struct sieve_validator *valdtr,
+ struct sieve_command_registration *cmd_reg,
+ const struct sieve_extension *this_ext, unsigned int id_code);
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def notify_old_operation;
+extern const struct sieve_operation_def denotify_operation;
+
+enum ext_notify_opcode {
+ EXT_NOTIFY_OPERATION_NOTIFY,
+ EXT_NOTIFY_OPERATION_DENOTIFY,
+};
+
+/*
+ * Actions
+ */
+
+extern const struct sieve_action_def act_notify_old;
+
+struct ext_notify_recipient {
+ const char *full;
+ const struct smtp_address *address;
+};
+
+ARRAY_DEFINE_TYPE(recipients, struct ext_notify_recipient);
+
+struct ext_notify_action {
+ const char *id;
+ const char *message;
+ sieve_number_t importance;
+
+ ARRAY_TYPE(recipients) recipients;
+};
+
+/*
+ * Message construct
+ */
+
+int ext_notify_construct_message(const struct sieve_runtime_env *renv,
+ const char *msg_format, string_t *out_msg);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-limits.h b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-limits.h
new file mode 100644
index 0000000..5fab7cb
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify-limits.h
@@ -0,0 +1,7 @@
+#ifndef EXT_NOTIFY_LIMITS_H
+#define EXT_NOTIFY_LIMITS_H
+
+#define EXT_NOTIFY_MAX_RECIPIENTS 8
+#define EXT_NOTIFY_MAX_MESSAGE 256
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/notify/ext-notify.c b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify.c
new file mode 100644
index 0000000..e79e049
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/notify/ext-notify.c
@@ -0,0 +1,108 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension notify
+ * ----------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: draft-ietf-sieve-notify-00.txt
+ * Implementation: full, but deprecated; provided for backwards compatibility
+ * Status: testing
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "ext-notify-common.h"
+
+/*
+ * Operations
+ */
+
+const struct sieve_operation_def *ext_notify_operations[] = {
+ &notify_old_operation,
+ &denotify_operation
+};
+
+/*
+ * Extension
+ */
+
+static bool ext_notify_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def notify_extension = {
+ .name = "notify",
+ .validator_load = ext_notify_validator_load,
+ SIEVE_EXT_DEFINE_OPERATIONS(ext_notify_operations)
+};
+
+/*
+ * Extension validation
+ */
+
+static bool ext_notify_validator_check_conflict
+ (const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context,
+ struct sieve_ast_argument *require_arg,
+ const struct sieve_extension *ext_other,
+ bool required);
+static bool ext_notify_validator_validate
+ (const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context,
+ struct sieve_ast_argument *require_arg,
+ bool required);
+
+const struct sieve_validator_extension notify_validator_extension = {
+ .ext = &notify_extension,
+ .check_conflict = ext_notify_validator_check_conflict,
+ .validate = ext_notify_validator_validate
+};
+
+static bool ext_notify_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register validator extension to check for conflict with enotify */
+ sieve_validator_extension_register
+ (valdtr, ext, &notify_validator_extension, NULL);
+ return TRUE;
+}
+
+static bool ext_notify_validator_check_conflict
+(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_validator *valdtr, void *context ATTR_UNUSED,
+ struct sieve_ast_argument *require_arg,
+ const struct sieve_extension *ext_other,
+ bool required ATTR_UNUSED)
+{
+ /* Check for conflict with enotify */
+ if ( sieve_extension_name_is(ext_other, "enotify") ) {
+ sieve_argument_validate_error(valdtr, require_arg,
+ "the (deprecated) notify extension cannot be used "
+ "together with the enotify extension");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static bool ext_notify_validator_validate
+(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context ATTR_UNUSED,
+ struct sieve_ast_argument *require_arg ATTR_UNUSED,
+ bool required ATTR_UNUSED)
+{
+ /* No conflicts: register new commands */
+ sieve_validator_register_command(valdtr, ext, &cmd_notify_old);
+ sieve_validator_register_command(valdtr, ext, &cmd_denotify);
+ return TRUE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/regex/Makefile.am b/pigeonhole/src/lib-sieve/plugins/regex/Makefile.am
new file mode 100644
index 0000000..eeb735e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/regex/Makefile.am
@@ -0,0 +1,13 @@
+noinst_LTLIBRARIES = libsieve_ext_regex.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_regex_la_SOURCES = \
+ mcht-regex.c \
+ ext-regex-common.c \
+ ext-regex.c
+
+noinst_HEADERS = \
+ ext-regex-common.h
diff --git a/pigeonhole/src/lib-sieve/plugins/regex/Makefile.in b/pigeonhole/src/lib-sieve/plugins/regex/Makefile.in
new file mode 100644
index 0000000..379d384
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/regex/Makefile.in
@@ -0,0 +1,688 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/regex
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_regex_la_LIBADD =
+am_libsieve_ext_regex_la_OBJECTS = mcht-regex.lo ext-regex-common.lo \
+ ext-regex.lo
+libsieve_ext_regex_la_OBJECTS = $(am_libsieve_ext_regex_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-regex-common.Plo \
+ ./$(DEPDIR)/ext-regex.Plo ./$(DEPDIR)/mcht-regex.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_regex_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_regex_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_regex.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_regex_la_SOURCES = \
+ mcht-regex.c \
+ ext-regex-common.c \
+ ext-regex.c
+
+noinst_HEADERS = \
+ ext-regex-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/regex/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/regex/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_regex.la: $(libsieve_ext_regex_la_OBJECTS) $(libsieve_ext_regex_la_DEPENDENCIES) $(EXTRA_libsieve_ext_regex_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_regex_la_OBJECTS) $(libsieve_ext_regex_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-regex-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-regex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-regex.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-regex-common.Plo
+ -rm -f ./$(DEPDIR)/ext-regex.Plo
+ -rm -f ./$(DEPDIR)/mcht-regex.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-regex-common.Plo
+ -rm -f ./$(DEPDIR)/ext-regex.Plo
+ -rm -f ./$(DEPDIR)/mcht-regex.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.c b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.c
new file mode 100644
index 0000000..975f4fb
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.c
@@ -0,0 +1,22 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-match-types.h"
+
+#include "ext-regex-common.h"
+
+/*
+ * Regex match type operand
+ */
+
+static const struct sieve_extension_objects ext_match_types =
+ SIEVE_EXT_DEFINE_MATCH_TYPE(regex_match_type);
+
+const struct sieve_operand_def regex_match_type_operand = {
+ .name = "regex match",
+ .ext_def = &regex_extension,
+ .class = &sieve_match_type_operand_class,
+ .interface = &ext_match_types
+};
+
diff --git a/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.h b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.h
new file mode 100644
index 0000000..e9d809f
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex-common.h
@@ -0,0 +1,24 @@
+#ifndef EXT_REGEX_COMMON_H
+#define EXT_REGEX_COMMON_H
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def regex_extension;
+
+/*
+ * Operand
+ */
+
+extern const struct sieve_operand_def regex_match_type_operand;
+
+/*
+ * Match type
+ */
+
+extern const struct sieve_match_type_def regex_match_type;
+
+#endif
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/regex/ext-regex.c b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex.c
new file mode 100644
index 0000000..0440b30
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/regex/ext-regex.c
@@ -0,0 +1,65 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension regex
+ * ---------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: draft-murchison-sieve-regex-08 (not latest)
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+/* FIXME: Regular expressions are compiled during compilation and
+ * again during interpretation. This is suboptimal and should be
+ * changed. This requires dumping the compiled regex to the binary.
+ * Most likely, this will only be possible when we implement regular
+ * expressions ourselves.
+ *
+ */
+
+#include "lib.h"
+#include "mempool.h"
+#include "buffer.h"
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include "ext-regex-common.h"
+
+#include <sys/types.h>
+#include <regex.h>
+
+/*
+ * Extension
+ */
+
+static bool ext_regex_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *validator);
+
+const struct sieve_extension_def regex_extension = {
+ .name = "regex",
+ .validator_load = ext_regex_validator_load,
+ SIEVE_EXT_DEFINE_OPERAND(regex_match_type_operand)
+};
+
+static bool ext_regex_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ sieve_match_type_register(valdtr, ext, &regex_match_type);
+
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/regex/mcht-regex.c b/pigeonhole/src/lib-sieve/plugins/regex/mcht-regex.c
new file mode 100644
index 0000000..f0c630e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/regex/mcht-regex.c
@@ -0,0 +1,385 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Match-type ':regex'
+ */
+
+#include "lib.h"
+#include "mempool.h"
+#include "buffer.h"
+#include "array.h"
+#include "str.h"
+#include "str-sanitize.h"
+
+#include "sieve-common.h"
+#include "sieve-limits.h"
+#include "sieve-ast.h"
+#include "sieve-stringlist.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-interpreter.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-match.h"
+
+#include "ext-regex-common.h"
+
+#include <sys/types.h>
+#include <regex.h>
+#include <ctype.h>
+
+/*
+ * Configuration
+ */
+
+#define MCHT_REGEX_MAX_SUBSTITUTIONS SIEVE_MAX_MATCH_VALUES
+
+/*
+ * Match type
+ */
+
+static bool mcht_regex_validate_context
+(struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
+ struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg);
+
+static void mcht_regex_match_init(struct sieve_match_context *mctx);
+static int mcht_regex_match_keys
+ (struct sieve_match_context *mctx, const char *val, size_t val_size,
+ struct sieve_stringlist *key_list);
+static void mcht_regex_match_deinit(struct sieve_match_context *mctx);
+
+const struct sieve_match_type_def regex_match_type = {
+ SIEVE_OBJECT("regex", &regex_match_type_operand, 0),
+ .validate_context = mcht_regex_validate_context,
+ .match_init = mcht_regex_match_init,
+ .match_keys = mcht_regex_match_keys,
+ .match_deinit = mcht_regex_match_deinit
+};
+
+/*
+ * Match type validation
+ */
+
+/* Wrapper around the regerror function for easy access */
+static const char *_regexp_error(regex_t *regexp, int errorcode)
+{
+ size_t errsize = regerror(errorcode, regexp, NULL, 0);
+
+ if ( errsize > 0 ) {
+ char *errbuf;
+
+ buffer_t *error_buf =
+ buffer_create_dynamic(pool_datastack_create(), errsize);
+ errbuf = buffer_get_space_unsafe(error_buf, 0, errsize);
+
+ errsize = regerror(errorcode, regexp, errbuf, errsize);
+
+ /* We don't want the error to start with a capital letter */
+ errbuf[0] = i_tolower(errbuf[0]);
+
+ buffer_append_space_unsafe(error_buf, errsize);
+
+ return str_c(error_buf);
+ }
+
+ return "";
+}
+
+static int mcht_regex_validate_regexp
+(struct sieve_validator *valdtr,
+ struct sieve_match_type_context *mtctx ATTR_UNUSED,
+ struct sieve_ast_argument *key, int cflags)
+{
+ int ret;
+ regex_t regexp;
+ const char *regex_str = sieve_ast_argument_strc(key);
+
+ if ( (ret=regcomp(&regexp, regex_str, cflags)) != 0 ) {
+ sieve_argument_validate_error(valdtr, key,
+ "invalid regular expression '%s' for regex match: %s",
+ str_sanitize(regex_str, 128), _regexp_error(&regexp, ret));
+
+ regfree(&regexp);
+ return -1;
+ }
+
+ regfree(&regexp);
+ return 1;
+}
+
+struct _regex_key_context {
+ struct sieve_validator *valdtr;
+ struct sieve_match_type_context *mtctx;
+ int cflags;
+};
+
+static int mcht_regex_validate_key_argument
+(void *context, struct sieve_ast_argument *key)
+{
+ struct _regex_key_context *keyctx = (struct _regex_key_context *) context;
+
+ /* FIXME: We can currently only handle string literal argument, so
+ * variables are not allowed.
+ */
+ if ( sieve_argument_is_string_literal(key) ) {
+ return mcht_regex_validate_regexp
+ (keyctx->valdtr, keyctx->mtctx, key, keyctx->cflags);
+ }
+
+ return 1;
+}
+
+static bool mcht_regex_validate_context
+(struct sieve_validator *valdtr, struct sieve_ast_argument *arg ATTR_UNUSED,
+ struct sieve_match_type_context *mtctx, struct sieve_ast_argument *key_arg)
+{
+ const struct sieve_comparator *cmp = mtctx->comparator;
+ int cflags = REG_EXTENDED | REG_NOSUB;
+ struct _regex_key_context keyctx;
+ struct sieve_ast_argument *kitem;
+
+ if ( cmp != NULL ) {
+ if ( sieve_comparator_is(cmp, i_ascii_casemap_comparator) )
+ cflags = REG_EXTENDED | REG_NOSUB | REG_ICASE;
+ else if ( sieve_comparator_is(cmp, i_octet_comparator) )
+ cflags = REG_EXTENDED | REG_NOSUB;
+ else {
+ sieve_argument_validate_error(valdtr, mtctx->argument,
+ "regex match type only supports "
+ "i;octet and i;ascii-casemap comparators" );
+ return FALSE;
+ }
+ }
+
+ /* Validate regular expression keys */
+
+ keyctx.valdtr = valdtr;
+ keyctx.mtctx = mtctx;
+ keyctx.cflags = cflags;
+
+ kitem = key_arg;
+ if ( sieve_ast_stringlist_map(&kitem, (void *) &keyctx,
+ mcht_regex_validate_key_argument) <= 0 )
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Match type implementation
+ */
+
+struct mcht_regex_key {
+ regex_t regexp;
+ int status;
+};
+
+struct mcht_regex_context {
+ ARRAY(struct mcht_regex_key) reg_expressions;
+ regmatch_t *pmatch;
+ size_t nmatch;
+ bool all_compiled:1;
+};
+
+static void mcht_regex_match_init
+(struct sieve_match_context *mctx)
+{
+ pool_t pool = mctx->pool;
+ struct mcht_regex_context *ctx;
+
+ /* Create context */
+ ctx = p_new(pool, struct mcht_regex_context, 1);
+
+ /* Create storage for match values if match values are requested */
+ if ( sieve_match_values_are_enabled(mctx->runenv) ) {
+ ctx->pmatch = p_new(pool, regmatch_t, MCHT_REGEX_MAX_SUBSTITUTIONS);
+ ctx->nmatch = MCHT_REGEX_MAX_SUBSTITUTIONS;
+ } else {
+ ctx->pmatch = NULL;
+ ctx->nmatch = 0;
+ }
+
+ /* Assign context */
+ mctx->data = (void *) ctx;
+}
+
+static int mcht_regex_match_key
+(struct sieve_match_context *mctx, const char *val,
+ const regex_t *regexp)
+{
+ struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data;
+ int ret;
+
+ /* Execute regex */
+
+ ret = regexec(regexp, val, ctx->nmatch, ctx->pmatch, 0);
+
+ /* Handle match values if necessary */
+
+ if ( ret == 0 ) {
+ if ( ctx->nmatch > 0 ) {
+ struct sieve_match_values *mvalues;
+ size_t i;
+ int skipped = 0;
+ string_t *subst = t_str_new(32);
+
+ /* Start new list of match values */
+ mvalues = sieve_match_values_start(mctx->runenv);
+
+ i_assert( mvalues != NULL );
+
+ /* Add match values from regular expression */
+ for ( i = 0; i < ctx->nmatch; i++ ) {
+ str_truncate(subst, 0);
+
+ if ( ctx->pmatch[i].rm_so != -1 ) {
+ if ( skipped > 0 ) {
+ sieve_match_values_skip(mvalues, skipped);
+ skipped = 0;
+ }
+
+ str_append_data(subst, val + ctx->pmatch[i].rm_so,
+ ctx->pmatch[i].rm_eo - ctx->pmatch[i].rm_so);
+ sieve_match_values_add(mvalues, subst);
+ } else
+ skipped++;
+ }
+
+ /* Substitute the new match values */
+ sieve_match_values_commit(mctx->runenv, &mvalues);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int mcht_regex_match_keys
+(struct sieve_match_context *mctx, const char *val, size_t val_size ATTR_UNUSED,
+ struct sieve_stringlist *key_list)
+{
+ const struct sieve_runtime_env *renv = mctx->runenv;
+ bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
+ struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data;
+ const struct sieve_comparator *cmp = mctx->comparator;
+ int match;
+
+ if ( !ctx->all_compiled ) {
+ string_t *key_item = NULL;
+ unsigned int i;
+ int ret;
+
+ /* Regular expressions still need to be compiled */
+
+ if ( !array_is_created(&ctx->reg_expressions) )
+ p_array_init(&ctx->reg_expressions, mctx->pool, 16);
+
+ i = 0;
+ match = 0;
+ while ( match == 0 &&
+ (ret=sieve_stringlist_next_item(key_list, &key_item)) > 0 ) {
+
+ T_BEGIN {
+ struct mcht_regex_key *rkey;
+
+ if ( i >= array_count(&ctx->reg_expressions) ) {
+ int cflags;
+
+ rkey = array_append_space(&ctx->reg_expressions);
+
+ /* Configure case-sensitivity according to comparator */
+ if ( sieve_comparator_is(cmp, i_octet_comparator) )
+ cflags = REG_EXTENDED;
+ else if ( sieve_comparator_is(cmp, i_ascii_casemap_comparator) )
+ cflags = REG_EXTENDED | REG_ICASE;
+ else
+ rkey->status = -1; /* Not supported */
+
+ if ( rkey->status >= 0 ) {
+ const char *regex_str = str_c(key_item);
+ int rxret;
+
+ /* Indicate whether match values need to be produced */
+ if ( ctx->nmatch == 0 ) cflags |= REG_NOSUB;
+
+ /* Compile regular expression */
+ if ( (rxret=regcomp(&rkey->regexp, regex_str, cflags)) != 0 ) {
+ sieve_runtime_error(renv, NULL,
+ "invalid regular expression '%s' for regex match: %s",
+ str_sanitize(regex_str, 128),
+ _regexp_error(&rkey->regexp, rxret));
+ rkey->status = -1;
+ } else {
+ rkey->status = 1;
+ }
+ }
+ } else {
+ rkey = array_idx_modifiable(&ctx->reg_expressions, 1);
+ }
+
+ if ( rkey->status > 0 ) {
+ match = mcht_regex_match_key(mctx, val, &rkey->regexp);
+
+ if ( trace ) {
+ sieve_runtime_trace(renv, 0,
+ "with regex `%s' [id=%d] => %d",
+ str_sanitize(str_c(key_item), 80),
+ array_count(&ctx->reg_expressions)-1, match);
+ }
+ }
+ } T_END;
+
+ i++;
+ }
+
+ if ( ret == 0 ) {
+ ctx->all_compiled = TRUE;
+ } else if ( ret < 0 ) {
+ mctx->exec_status = key_list->exec_status;
+ match = -1;
+ }
+
+ } else {
+ const struct mcht_regex_key *rkeys;
+ unsigned int i, count;
+
+ /* Regular expressions are compiled */
+
+ rkeys = array_get(&ctx->reg_expressions, &count);
+
+ i = 0;
+ match = 0;
+ while ( match == 0 && i < count ) {
+ if ( rkeys[i].status > 0 ) {
+ match = mcht_regex_match_key(mctx, val, &rkeys[i].regexp);
+
+ if ( trace ) {
+ sieve_runtime_trace(renv, 0,
+ "with compiled regex [id=%d] => %d", i, match);
+ }
+ }
+
+ i++;
+ }
+ }
+
+ return match;
+}
+
+void mcht_regex_match_deinit
+(struct sieve_match_context *mctx)
+{
+ struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data;
+ struct mcht_regex_key *rkeys;
+ unsigned int count, i;
+
+ /* Clean up compiled regular expressions */
+ if ( array_is_created(&ctx->reg_expressions) ) {
+ rkeys = array_get_modifiable(&ctx->reg_expressions, &count);
+ for ( i = 0; i < count; i++ ) {
+ regfree(&rkeys[i].regexp);
+ }
+ }
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/relational/Makefile.am b/pigeonhole/src/lib-sieve/plugins/relational/Makefile.am
new file mode 100644
index 0000000..dd28cc5
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/relational/Makefile.am
@@ -0,0 +1,14 @@
+noinst_LTLIBRARIES = libsieve_ext_relational.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_relational_la_SOURCES = \
+ ext-relational-common.c \
+ mcht-value.c \
+ mcht-count.c \
+ ext-relational.c
+
+noinst_HEADERS = \
+ ext-relational-common.h
diff --git a/pigeonhole/src/lib-sieve/plugins/relational/Makefile.in b/pigeonhole/src/lib-sieve/plugins/relational/Makefile.in
new file mode 100644
index 0000000..4977808
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/relational/Makefile.in
@@ -0,0 +1,694 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/relational
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_relational_la_LIBADD =
+am_libsieve_ext_relational_la_OBJECTS = ext-relational-common.lo \
+ mcht-value.lo mcht-count.lo ext-relational.lo
+libsieve_ext_relational_la_OBJECTS = \
+ $(am_libsieve_ext_relational_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-relational-common.Plo \
+ ./$(DEPDIR)/ext-relational.Plo ./$(DEPDIR)/mcht-count.Plo \
+ ./$(DEPDIR)/mcht-value.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_relational_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_relational_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_relational.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_relational_la_SOURCES = \
+ ext-relational-common.c \
+ mcht-value.c \
+ mcht-count.c \
+ ext-relational.c
+
+noinst_HEADERS = \
+ ext-relational-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/relational/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/relational/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_relational.la: $(libsieve_ext_relational_la_OBJECTS) $(libsieve_ext_relational_la_DEPENDENCIES) $(EXTRA_libsieve_ext_relational_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_relational_la_OBJECTS) $(libsieve_ext_relational_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-relational-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-relational.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-count.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-value.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-relational-common.Plo
+ -rm -f ./$(DEPDIR)/ext-relational.Plo
+ -rm -f ./$(DEPDIR)/mcht-count.Plo
+ -rm -f ./$(DEPDIR)/mcht-value.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-relational-common.Plo
+ -rm -f ./$(DEPDIR)/ext-relational.Plo
+ -rm -f ./$(DEPDIR)/mcht-count.Plo
+ -rm -f ./$(DEPDIR)/mcht-value.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.c b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.c
new file mode 100644
index 0000000..a6dd1ed
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.c
@@ -0,0 +1,171 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Syntax:
+
+ MATCH-TYPE =/ COUNT / VALUE
+ COUNT = ":count" relational-match
+ VALUE = ":value" relational-match
+ relational-match = DQUOTE ( "gt" / "ge" / "lt"
+ / "le" / "eq" / "ne" ) DQUOTE
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+
+#include "sieve-common.h"
+#include "sieve-ast.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include "ext-relational-common.h"
+
+/*
+ * Forward declarations
+ */
+
+const struct sieve_match_type_def *rel_match_types[];
+
+/*
+ * Validation
+ */
+
+bool mcht_relational_validate(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_match_type_context *ctx)
+{
+ struct sieve_match_type *mcht;
+ enum relational_match rel_match = REL_MATCH_INVALID;
+ pool_t pool = sieve_ast_argument_pool(ctx->argument);
+ string_t *rel_match_ident;
+
+ /* Check syntax:
+ relational-match = DQUOTE ( "gt" / "ge" / "lt"
+ / "le" / "eq" / "ne" ) DQUOTE
+
+ So, actually this must be a constant string and it is implemented as
+ such.
+ */
+
+ /* Did we get a string in the first place? */
+ if (*arg == NULL || (*arg)->type != SAAT_STRING) {
+ sieve_argument_validate_error(
+ valdtr, (*arg == NULL ? ctx->argument : *arg),
+ "the :%s match-type requires a constant string argument being "
+ "one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", "
+ "but %s was found",
+ sieve_match_type_name(ctx->match_type),
+ (*arg == NULL ?
+ "none" : sieve_ast_argument_name(*arg)));
+ return FALSE;
+ }
+
+ /* Check the relational match id */
+
+ rel_match_ident = sieve_ast_argument_str(*arg);
+ if (str_len(rel_match_ident) == 2) {
+ const char *rel_match_id = str_c(rel_match_ident);
+
+ switch (rel_match_id[0]) {
+ /* "gt" or "ge" */
+ case 'g':
+ switch (rel_match_id[1]) {
+ case 't':
+ rel_match = REL_MATCH_GREATER;
+ break;
+ case 'e':
+ rel_match = REL_MATCH_GREATER_EQUAL;
+ break;
+ default:
+ rel_match = REL_MATCH_INVALID;
+ }
+ break;
+ /* "lt" or "le" */
+ case 'l':
+ switch (rel_match_id[1]) {
+ case 't':
+ rel_match = REL_MATCH_LESS;
+ break;
+ case 'e':
+ rel_match = REL_MATCH_LESS_EQUAL;
+ break;
+ default:
+ rel_match = REL_MATCH_INVALID;
+ }
+ break;
+ /* "eq" */
+ case 'e':
+ if (rel_match_id[1] == 'q')
+ rel_match = REL_MATCH_EQUAL;
+ else
+ rel_match = REL_MATCH_INVALID;
+ break;
+ /* "ne" */
+ case 'n':
+ if (rel_match_id[1] == 'e')
+ rel_match = REL_MATCH_NOT_EQUAL;
+ else
+ rel_match = REL_MATCH_INVALID;
+ break;
+ /* invalid */
+ default:
+ rel_match = REL_MATCH_INVALID;
+ }
+ }
+
+ if (rel_match >= REL_MATCH_INVALID) {
+ sieve_argument_validate_error(
+ valdtr, *arg,
+ "the :%s match-type requires a constant string argument being "
+ "one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", "
+ "but \"%s\" was found",
+ sieve_match_type_name(ctx->match_type),
+ str_sanitize(str_c(rel_match_ident), 32));
+ return FALSE;
+ }
+
+ /* Delete argument */
+ *arg = sieve_ast_arguments_detach(*arg, 1);
+
+ /* Not used just yet */
+ ctx->ctx_data = (void *) rel_match;
+
+ /* Override the actual match type with a parameter-specific one
+ * FIXME: ugly!
+ */
+ mcht = p_new(pool, struct sieve_match_type, 1);
+ mcht->object.ext = ctx->match_type->object.ext;
+ SIEVE_OBJECT_SET_DEF(mcht, rel_match_types[
+ REL_MATCH_INDEX(ctx->match_type->object.def->code, rel_match)]);
+ ctx->match_type = mcht;
+
+ return TRUE;
+}
+
+/*
+ * Relational match-type operand
+ */
+
+const struct sieve_match_type_def *rel_match_types[] = {
+ &rel_match_value_gt, &rel_match_value_ge, &rel_match_value_lt,
+ &rel_match_value_le, &rel_match_value_eq, &rel_match_value_ne,
+ &rel_match_count_gt, &rel_match_count_ge, &rel_match_count_lt,
+ &rel_match_count_le, &rel_match_count_eq, &rel_match_count_ne,
+};
+
+static const struct sieve_extension_objects ext_match_types =
+ SIEVE_EXT_DEFINE_MATCH_TYPES(rel_match_types);
+
+const struct sieve_operand_def rel_match_type_operand = {
+ .name = "relational match",
+ .ext_def = &relational_extension,
+ .class = &sieve_match_type_operand_class,
+ .interface = &ext_match_types,
+};
diff --git a/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.h b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.h
new file mode 100644
index 0000000..c0f7d3b
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.h
@@ -0,0 +1,86 @@
+#ifndef EXT_RELATIONAL_COMMON_H
+#define EXT_RELATIONAL_COMMON_H
+
+#include "lib.h"
+#include "str.h"
+
+#include "sieve-common.h"
+
+/*
+ * Types
+ */
+
+enum ext_relational_match_type {
+ RELATIONAL_VALUE,
+ RELATIONAL_COUNT
+};
+
+enum relational_match {
+ REL_MATCH_GREATER,
+ REL_MATCH_GREATER_EQUAL,
+ REL_MATCH_LESS,
+ REL_MATCH_LESS_EQUAL,
+ REL_MATCH_EQUAL,
+ REL_MATCH_NOT_EQUAL,
+ REL_MATCH_INVALID
+};
+
+#define REL_MATCH_INDEX(type, match) (type * REL_MATCH_INVALID + match)
+#define REL_MATCH_TYPE(index) (index / REL_MATCH_INVALID)
+#define REL_MATCH(index) (index % REL_MATCH_INVALID)
+
+/*
+ * Extension definitions
+ */
+
+extern const struct sieve_extension_def relational_extension;
+
+/*
+ * Match types
+ */
+
+/* Registered for validation */
+
+extern const struct sieve_match_type_def value_match_type;
+extern const struct sieve_match_type_def count_match_type;
+
+/* Used in byte code */
+
+extern const struct sieve_match_type_def rel_match_count_gt;
+extern const struct sieve_match_type_def rel_match_count_ge;
+extern const struct sieve_match_type_def rel_match_count_lt;
+extern const struct sieve_match_type_def rel_match_count_le;
+extern const struct sieve_match_type_def rel_match_count_eq;
+extern const struct sieve_match_type_def rel_match_count_ne;
+
+extern const struct sieve_match_type_def rel_match_value_gt;
+extern const struct sieve_match_type_def rel_match_value_ge;
+extern const struct sieve_match_type_def rel_match_value_lt;
+extern const struct sieve_match_type_def rel_match_value_le;
+extern const struct sieve_match_type_def rel_match_value_eq;
+extern const struct sieve_match_type_def rel_match_value_ne;
+
+/*
+ * Operand
+ */
+
+extern const struct sieve_operand_def rel_match_type_operand;
+
+
+/*
+ * Match type validation
+ */
+
+bool mcht_relational_validate(struct sieve_validator *validator,
+ struct sieve_ast_argument **arg,
+ struct sieve_match_type_context *ctx);
+
+/*
+ * Value match function (also used by :count)
+ */
+
+int mcht_value_match_key(struct sieve_match_context *mctx,
+ const char *val, size_t val_size,
+ const char *key, size_t key_size);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/relational/ext-relational.c b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational.c
new file mode 100644
index 0000000..28c278f
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/relational/ext-relational.c
@@ -0,0 +1,53 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension relational
+ * --------------------
+ *
+ * Author: Stephan Bosch
+ * Specification: RFC 3431
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "str.h"
+
+#include "sieve-common.h"
+
+#include "sieve-ast.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include "ext-relational-common.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_relational_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def relational_extension = {
+ .name = "relational",
+ .validator_load = ext_relational_validator_load,
+ SIEVE_EXT_DEFINE_OPERAND(rel_match_type_operand)
+};
+
+static bool ext_relational_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ sieve_match_type_register(valdtr, ext, &value_match_type);
+ sieve_match_type_register(valdtr, ext, &count_match_type);
+
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/relational/mcht-count.c b/pigeonhole/src/lib-sieve/plugins/relational/mcht-count.c
new file mode 100644
index 0000000..e31a63a
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/relational/mcht-count.c
@@ -0,0 +1,119 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Match-type ':count'
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+
+#include "sieve-common.h"
+#include "sieve-ast.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-runtime-trace.h"
+#include "sieve-match.h"
+
+#include "ext-relational-common.h"
+
+/*
+ * Forward declarations
+ */
+
+static int mcht_count_match
+ (struct sieve_match_context *mctx, struct sieve_stringlist *value_list,
+ struct sieve_stringlist *key_list);
+
+/*
+ * Match-type objects
+ */
+
+const struct sieve_match_type_def count_match_type = {
+ SIEVE_OBJECT("count",
+ &rel_match_type_operand, RELATIONAL_COUNT),
+ .validate = mcht_relational_validate
+};
+
+#define COUNT_MATCH_TYPE(name, rel_match) \
+const struct sieve_match_type_def rel_match_count_ ## name = { \
+ SIEVE_OBJECT("count-" #name, \
+ &rel_match_type_operand, \
+ REL_MATCH_INDEX(RELATIONAL_COUNT, rel_match)), \
+ .match = mcht_count_match, \
+}
+
+COUNT_MATCH_TYPE(gt, REL_MATCH_GREATER);
+COUNT_MATCH_TYPE(ge, REL_MATCH_GREATER_EQUAL);
+COUNT_MATCH_TYPE(lt, REL_MATCH_LESS);
+COUNT_MATCH_TYPE(le, REL_MATCH_LESS_EQUAL);
+COUNT_MATCH_TYPE(eq, REL_MATCH_EQUAL);
+COUNT_MATCH_TYPE(ne, REL_MATCH_NOT_EQUAL);
+
+/*
+ * Match-type implementation
+ */
+
+static int mcht_count_match
+(struct sieve_match_context *mctx, struct sieve_stringlist *value_list,
+ struct sieve_stringlist *key_list)
+{
+ const struct sieve_runtime_env *renv = mctx->runenv;
+ bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
+ int count;
+ string_t *key_item;
+ int match, ret;
+
+ if ( (count=sieve_stringlist_get_length(value_list)) < 0 ) {
+ mctx->exec_status = value_list->exec_status;
+ return -1;
+ }
+
+ sieve_stringlist_reset(key_list);
+
+ string_t *value = t_str_new(20);
+ str_printfa(value, "%d", count);
+
+ if ( trace ) {
+ sieve_runtime_trace(renv, 0,
+ "matching count value `%s'", str_sanitize(str_c(value), 80));
+ }
+
+ sieve_runtime_trace_descend(renv);
+
+ /* Match to all key values */
+ key_item = NULL;
+ match = 0;
+ while ( match == 0 &&
+ (ret=sieve_stringlist_next_item(key_list, &key_item)) > 0 )
+ {
+ match = mcht_value_match_key
+ (mctx, str_c(value), str_len(value), str_c(key_item), str_len(key_item));
+
+ if ( trace ) {
+ sieve_runtime_trace(renv, 0,
+ "with key `%s' => %d", str_sanitize(str_c(key_item), 80), ret);
+ }
+ }
+
+ sieve_runtime_trace_ascend(renv);
+
+ if ( ret < 0 ) {
+ mctx->exec_status = key_list->exec_status;
+ match = -1;
+ }
+
+ return match;
+}
+
+
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/relational/mcht-value.c b/pigeonhole/src/lib-sieve/plugins/relational/mcht-value.c
new file mode 100644
index 0000000..dd55590
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/relational/mcht-value.c
@@ -0,0 +1,80 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+
+#include "sieve-common.h"
+
+#include "sieve-ast.h"
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-match.h"
+
+#include "ext-relational-common.h"
+
+/*
+ * Match-type objects
+ */
+
+const struct sieve_match_type_def value_match_type = {
+ SIEVE_OBJECT("value",
+ &rel_match_type_operand, RELATIONAL_VALUE),
+ .validate = mcht_relational_validate
+};
+
+#define VALUE_MATCH_TYPE(name, rel_match) \
+const struct sieve_match_type_def rel_match_value_ ## name = { \
+ SIEVE_OBJECT("value-" #name, \
+ &rel_match_type_operand, \
+ REL_MATCH_INDEX(RELATIONAL_VALUE, rel_match)), \
+ .match_key = mcht_value_match_key, \
+}
+
+VALUE_MATCH_TYPE(gt, REL_MATCH_GREATER);
+VALUE_MATCH_TYPE(ge, REL_MATCH_GREATER_EQUAL);
+VALUE_MATCH_TYPE(lt, REL_MATCH_LESS);
+VALUE_MATCH_TYPE(le, REL_MATCH_LESS_EQUAL);
+VALUE_MATCH_TYPE(eq, REL_MATCH_EQUAL);
+VALUE_MATCH_TYPE(ne, REL_MATCH_NOT_EQUAL);
+
+/*
+ * Match-type implementation
+ */
+
+int mcht_value_match_key
+(struct sieve_match_context *mctx, const char *val, size_t val_size,
+ const char *key, size_t key_size)
+{
+ const struct sieve_match_type *mtch = mctx->match_type;
+ unsigned int rel_match = REL_MATCH(mtch->object.def->code);
+ int cmp_result;
+
+ cmp_result = mctx->comparator->def->
+ compare(mctx->comparator, val, val_size, key, key_size);
+
+ switch ( rel_match ) {
+ case REL_MATCH_GREATER:
+ return ( cmp_result > 0 ? 1 : 0 );
+ case REL_MATCH_GREATER_EQUAL:
+ return ( cmp_result >= 0 ? 1 : 0 );
+ case REL_MATCH_LESS:
+ return ( cmp_result < 0 ? 1 : 0 );
+ case REL_MATCH_LESS_EQUAL:
+ return ( cmp_result <= 0 ? 1 : 0 );
+ case REL_MATCH_EQUAL:
+ return ( cmp_result == 0 ? 1 : 0);
+ case REL_MATCH_NOT_EQUAL:
+ return ( cmp_result != 0 ? 1 : 0);
+ }
+
+ i_unreached();
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.am b/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.am
new file mode 100644
index 0000000..6a15bdd
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.am
@@ -0,0 +1,16 @@
+noinst_LTLIBRARIES = libsieve_ext_spamvirustest.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tests = \
+ tst-spamvirustest.c
+
+libsieve_ext_spamvirustest_la_SOURCES = \
+ $(tests) \
+ ext-spamvirustest-common.c \
+ ext-spamvirustest.c
+
+noinst_HEADERS = \
+ ext-spamvirustest-common.h
diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.in b/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.in
new file mode 100644
index 0000000..dca31e3
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/Makefile.in
@@ -0,0 +1,694 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/spamvirustest
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_spamvirustest_la_LIBADD =
+am__objects_1 = tst-spamvirustest.lo
+am_libsieve_ext_spamvirustest_la_OBJECTS = $(am__objects_1) \
+ ext-spamvirustest-common.lo ext-spamvirustest.lo
+libsieve_ext_spamvirustest_la_OBJECTS = \
+ $(am_libsieve_ext_spamvirustest_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-spamvirustest-common.Plo \
+ ./$(DEPDIR)/ext-spamvirustest.Plo \
+ ./$(DEPDIR)/tst-spamvirustest.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_spamvirustest_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_spamvirustest_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_spamvirustest.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tests = \
+ tst-spamvirustest.c
+
+libsieve_ext_spamvirustest_la_SOURCES = \
+ $(tests) \
+ ext-spamvirustest-common.c \
+ ext-spamvirustest.c
+
+noinst_HEADERS = \
+ ext-spamvirustest-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/spamvirustest/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/spamvirustest/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_spamvirustest.la: $(libsieve_ext_spamvirustest_la_OBJECTS) $(libsieve_ext_spamvirustest_la_DEPENDENCIES) $(EXTRA_libsieve_ext_spamvirustest_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_spamvirustest_la_OBJECTS) $(libsieve_ext_spamvirustest_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-spamvirustest-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-spamvirustest.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-spamvirustest.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-spamvirustest-common.Plo
+ -rm -f ./$(DEPDIR)/ext-spamvirustest.Plo
+ -rm -f ./$(DEPDIR)/tst-spamvirustest.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-spamvirustest-common.Plo
+ -rm -f ./$(DEPDIR)/ext-spamvirustest.Plo
+ -rm -f ./$(DEPDIR)/tst-spamvirustest.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c
new file mode 100644
index 0000000..38cb5d8
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.c
@@ -0,0 +1,674 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "strfuncs.h"
+#include "mail-storage.h"
+
+#include "sieve-common.h"
+#include "sieve-settings.h"
+#include "sieve-error.h"
+#include "sieve-extensions.h"
+#include "sieve-message.h"
+#include "sieve-interpreter.h"
+#include "sieve-runtime-trace.h"
+
+#include "ext-spamvirustest-common.h"
+
+#include <sys/types.h>
+#include <regex.h>
+#include <ctype.h>
+
+/*
+ * Extension data
+ */
+
+enum ext_spamvirustest_status_type {
+ EXT_SPAMVIRUSTEST_STATUS_TYPE_SCORE,
+ EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN,
+ EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT,
+};
+
+struct ext_spamvirustest_header_spec {
+ const char *header_name;
+ regex_t regexp;
+ bool regexp_match;
+};
+
+struct ext_spamvirustest_data {
+ pool_t pool;
+
+ int reload;
+
+ struct ext_spamvirustest_header_spec status_header;
+ struct ext_spamvirustest_header_spec max_header;
+
+ enum ext_spamvirustest_status_type status_type;
+
+ float max_value;
+
+ const char *text_values[11];
+};
+
+/*
+ * Regexp utility
+ */
+
+static bool _regexp_compile
+(regex_t *regexp, const char *data, const char **error_r)
+{
+ size_t errsize;
+ int ret;
+
+ *error_r = "";
+
+ if ( (ret=regcomp(regexp, data, REG_EXTENDED)) == 0 ) {
+ return TRUE;
+ }
+
+ errsize = regerror(ret, regexp, NULL, 0);
+
+ if ( errsize > 0 ) {
+ char *errbuf = t_malloc0(errsize);
+
+ (void)regerror(ret, regexp, errbuf, errsize);
+
+ /* We don't want the error to start with a capital letter */
+ errbuf[0] = i_tolower(errbuf[0]);
+
+ *error_r = errbuf;
+ }
+
+ return FALSE;
+}
+
+static const char *_regexp_match_get_value
+(const char *string, int index, regmatch_t pmatch[], int nmatch)
+{
+ if ( index > -1 && index < nmatch && pmatch[index].rm_so != -1 ) {
+ return t_strndup(string + pmatch[index].rm_so,
+ pmatch[index].rm_eo - pmatch[index].rm_so);
+ }
+ return NULL;
+}
+
+/*
+ * Configuration parser
+ */
+
+static bool ext_spamvirustest_header_spec_parse
+(struct ext_spamvirustest_header_spec *spec, pool_t pool, const char *data,
+ const char **error_r)
+{
+ const char *p;
+ const char *regexp_error;
+
+ if ( *data == '\0' ) {
+ *error_r = "empty header specification";
+ return FALSE;
+ }
+
+ /* Parse header name */
+
+ p = data;
+
+ while ( *p == ' ' || *p == '\t' ) p++;
+ while ( *p != ':' && *p != '\0' && *p != ' ' && *p != '\t' ) p++;
+
+ if ( *p == '\0' ) {
+ spec->header_name = p_strdup(pool, data);
+ return TRUE;
+ }
+
+ spec->header_name = p_strdup_until(pool, data, p);
+ while ( *p == ' ' || *p == '\t' ) p++;
+
+ if ( *p == '\0' ) {
+ spec->regexp_match = FALSE;
+ return TRUE;
+ }
+
+ /* Parse and compile regular expression */
+
+ if ( *p != ':' ) {
+ *error_r = t_strdup_printf("expecting ':', but found '%c'", *p);
+ return FALSE;
+ }
+ p++;
+ while ( *p == ' ' || *p == '\t' ) p++;
+
+ spec->regexp_match = TRUE;
+ if ( !_regexp_compile(&spec->regexp, p, &regexp_error) ) {
+ *error_r = t_strdup_printf("failed to compile regular expression '%s': "
+ "%s", p, regexp_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void ext_spamvirustest_header_spec_free
+(struct ext_spamvirustest_header_spec *spec)
+{
+ regfree(&spec->regexp);
+}
+
+static bool ext_spamvirustest_parse_strlen_value
+(const char *str_value, float *value_r, const char **error_r)
+{
+ const char *p = str_value;
+ char ch = *p;
+
+ if ( *str_value == '\0' ) {
+ *value_r = 0;
+ return TRUE;
+ }
+
+ while ( *p == ch ) p++;
+
+ if ( *p != '\0' ) {
+ *error_r = t_strdup_printf(
+ "different character '%c' encountered in strlen value",
+ *p);
+ return FALSE;
+ }
+
+ *value_r = ( p - str_value );
+
+ return TRUE;
+}
+
+static bool ext_spamvirustest_parse_decimal_value
+(const char *str_value, float *value_r, const char **error_r)
+{
+ const char *p = str_value;
+ float value;
+ float sign = 1;
+ int digits;
+
+ if ( *p == '\0' ) {
+ *error_r = "empty value";
+ return FALSE;
+ }
+
+ if ( *p == '+' || *p == '-' ) {
+ if ( *p == '-' )
+ sign = -1;
+
+ p++;
+ }
+
+ value = 0;
+ digits = 0;
+ while ( i_isdigit(*p) ) {
+ value = value*10 + (*p-'0');
+ if ( digits++ > 4 ) {
+ *error_r = t_strdup_printf
+ ("decimal value has too many digits before radix point: %s",
+ str_value);
+ return FALSE;
+ }
+ p++;
+ }
+
+ if ( *p == '.' || *p == ',' ) {
+ float radix = .1;
+ p++;
+
+ digits = 0;
+ while ( i_isdigit(*p) ) {
+ value = value + (*p-'0')*radix;
+
+ if ( digits++ > 4 ) {
+ *error_r = t_strdup_printf
+ ("decimal value has too many digits after radix point: %s",
+ str_value);
+ return FALSE;
+ }
+ radix /= 10;
+ p++;
+ }
+ }
+
+ if ( *p != '\0' ) {
+ *error_r = t_strdup_printf
+ ("invalid decimal point value: %s", str_value);
+ return FALSE;
+ }
+
+ *value_r = value * sign;
+
+ return TRUE;
+}
+
+/*
+ * Extension initialization
+ */
+
+bool ext_spamvirustest_load
+(const struct sieve_extension *ext, void **context)
+{
+ struct ext_spamvirustest_data *ext_data =
+ (struct ext_spamvirustest_data *) *context;
+ struct sieve_instance *svinst = ext->svinst;
+ const char *ext_name, *status_header, *max_header, *status_type,
+ *max_value;
+ enum ext_spamvirustest_status_type type;
+ const char *error;
+ pool_t pool;
+ bool result = TRUE;
+ int reload = 0;
+
+ if ( *context != NULL ) {
+ reload = ext_data->reload + 1;
+ ext_spamvirustest_unload(ext);
+ *context = NULL;
+ }
+
+ /* FIXME:
+ * Prevent loading of both spamtest and spamtestplus: let these share
+ * contexts.
+ */
+
+ if ( sieve_extension_is(ext, spamtest_extension) ||
+ sieve_extension_is(ext, spamtestplus_extension) ) {
+ ext_name = spamtest_extension.name;
+ } else {
+ ext_name = sieve_extension_name(ext);
+ }
+
+ /* Get settings */
+
+ status_header = sieve_setting_get
+ (svinst, t_strconcat("sieve_", ext_name, "_status_header", NULL));
+ status_type = sieve_setting_get
+ (svinst, t_strconcat("sieve_", ext_name, "_status_type", NULL));
+ max_header = sieve_setting_get
+ (svinst, t_strconcat("sieve_", ext_name, "_max_header", NULL));
+ max_value = sieve_setting_get
+ (svinst, t_strconcat("sieve_", ext_name, "_max_value", NULL));
+
+ /* Base configuration */
+
+ if ( status_header == NULL ) {
+ return TRUE;
+ }
+
+ if ( status_type == NULL || strcmp(status_type, "score") == 0 ) {
+ type = EXT_SPAMVIRUSTEST_STATUS_TYPE_SCORE;
+ } else if ( strcmp(status_type, "strlen") == 0 ) {
+ type = EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN;
+ } else if ( strcmp(status_type, "text") == 0 ) {
+ type = EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT;
+ } else {
+ e_error(svinst->event, "%s: "
+ "invalid status type '%s'", ext_name, status_type);
+ return FALSE;
+ }
+
+ /* Verify settings */
+
+ if ( type != EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT ) {
+
+ if ( max_header != NULL && max_value != NULL ) {
+ e_error(svinst->event, "%s: "
+ "sieve_%s_max_header and sieve_%s_max_value "
+ "cannot both be configured",
+ ext_name, ext_name, ext_name);
+ return TRUE;
+ }
+
+ if ( max_header == NULL && max_value == NULL ) {
+ e_error(svinst->event, "%s: "
+ "none of sieve_%s_max_header or sieve_%s_max_value "
+ "is configured", ext_name, ext_name, ext_name);
+ return TRUE;
+ }
+ } else {
+ if ( max_header != NULL ) {
+ e_warning(svinst->event, "%s: "
+ "setting sieve_%s_max_header has no meaning "
+ "for sieve_%s_status_type=text",
+ ext_name, ext_name, ext_name);
+ }
+
+ if ( max_value != NULL ) {
+ e_warning(svinst->event, "%s: "
+ "setting sieve_%s_max_value has no meaning "
+ "for sieve_%s_status_type=text",
+ ext_name, ext_name, ext_name);
+ }
+ }
+
+ pool = pool_alloconly_create("spamvirustest_data", 512);
+ ext_data = p_new(pool, struct ext_spamvirustest_data, 1);
+ ext_data->pool = pool;
+ ext_data->reload = reload;
+ ext_data->status_type = type;
+
+ if ( !ext_spamvirustest_header_spec_parse
+ (&ext_data->status_header, ext_data->pool, status_header, &error) ) {
+ e_error(svinst->event, "%s: "
+ "invalid status header specification '%s': %s",
+ ext_name, status_header, error);
+ result = FALSE;
+ }
+
+ if ( result ) {
+ if ( type != EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT ) {
+ /* Parse max header */
+
+ if ( max_header != NULL && !ext_spamvirustest_header_spec_parse
+ (&ext_data->max_header, ext_data->pool, max_header, &error) ) {
+ e_error(svinst->event, "%s: "
+ "invalid max header specification "
+ "'%s': %s", ext_name, max_header,
+ error);
+ result = FALSE;
+ }
+
+ /* Parse max value */
+
+ if ( result && max_value != NULL ) {
+ if ( !ext_spamvirustest_parse_decimal_value
+ (max_value, &ext_data->max_value, &error) ) {
+ e_error(svinst->event, "%s: "
+ "invalid max value specification "
+ "'%s': %s", ext_name, max_value,
+ error);
+ result = FALSE;
+ }
+ }
+
+ } else {
+ unsigned int i, max_text;
+
+ max_text = ( sieve_extension_is(ext, virustest_extension) ? 5 : 10 );
+
+ /* Get text values */
+ for ( i = 0; i <= max_text; i++ ) {
+ const char *value = sieve_setting_get
+ (svinst, t_strdup_printf("sieve_%s_text_value%d", ext_name, i));
+
+ if ( value != NULL && *value != '\0' )
+ ext_data->text_values[i] = p_strdup(ext_data->pool, value);
+ }
+
+ ext_data->max_value = 1;
+ }
+ }
+
+ if ( result ) {
+ *context = (void *) ext_data;
+ } else {
+ e_warning(svinst->event, "%s: "
+ "extension not configured, "
+ "tests will always match against \"0\"",
+ ext_name);
+ ext_spamvirustest_unload(ext);
+ *context = NULL;
+ }
+
+ return result;
+}
+
+void ext_spamvirustest_unload(const struct sieve_extension *ext)
+{
+ struct ext_spamvirustest_data *ext_data =
+ (struct ext_spamvirustest_data *) ext->context;
+
+ if ( ext_data != NULL ) {
+ ext_spamvirustest_header_spec_free(&ext_data->status_header);
+ ext_spamvirustest_header_spec_free(&ext_data->max_header);
+ pool_unref(&ext_data->pool);
+ }
+}
+
+/*
+ * Runtime
+ */
+
+struct ext_spamvirustest_message_context {
+ int reload;
+ float score_ratio;
+};
+
+static const char *ext_spamvirustest_get_score
+(const struct sieve_extension *ext, float score_ratio, bool percent)
+{
+ int score;
+
+ if ( score_ratio < 0 )
+ return "0";
+
+ if ( score_ratio > 1 )
+ score_ratio = 1;
+
+ if ( percent )
+ score = score_ratio * 100 + 0.001;
+ else if ( sieve_extension_is(ext, virustest_extension) )
+ score = score_ratio * 4 + 1.001;
+ else
+ score = score_ratio * 9 + 1.001;
+
+ return t_strdup_printf("%d", score);
+}
+
+int ext_spamvirustest_get_value
+(const struct sieve_runtime_env *renv, const struct sieve_extension *ext,
+ bool percent, const char **value_r)
+{
+ struct ext_spamvirustest_data *ext_data =
+ (struct ext_spamvirustest_data *) ext->context;
+ struct ext_spamvirustest_header_spec *status_header, *max_header;
+ struct sieve_message_context *msgctx = renv->msgctx;
+ struct ext_spamvirustest_message_context *mctx;
+ struct mail *mail;
+ regmatch_t match_values[2];
+ const char *header_value, *error;
+ const char *status = NULL, *max = NULL;
+ float status_value, max_value;
+ unsigned int i, max_text;
+ pool_t pool = sieve_interpreter_pool(renv->interp);
+
+ *value_r = "0";
+
+ /*
+ * Check whether extension is properly configured
+ */
+ if ( ext_data == NULL ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "error: extension not configured");
+ return SIEVE_EXEC_OK;
+ }
+
+ /*
+ * Check wether a cached result is available
+ */
+
+ mctx = (struct ext_spamvirustest_message_context *)
+ sieve_message_context_extension_get(msgctx, ext);
+
+ if ( mctx == NULL ) {
+ /* Create new context */
+ mctx = p_new(pool, struct ext_spamvirustest_message_context, 1);
+ sieve_message_context_extension_set(msgctx, ext, (void *)mctx);
+ } else if ( mctx->reload == ext_data->reload ) {
+ /* Use cached result */
+ *value_r = ext_spamvirustest_get_score(ext, mctx->score_ratio, percent);
+ return SIEVE_EXEC_OK;
+ } else {
+ /* Extension was reloaded (probably in testsuite) */
+ }
+
+ mctx->reload = ext_data->reload;
+
+ /*
+ * Get max status value
+ */
+
+ mail = sieve_message_get_mail(renv->msgctx);
+ status_header = &ext_data->status_header;
+ max_header = &ext_data->max_header;
+
+ if ( ext_data->status_type != EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT ) {
+ if ( max_header->header_name != NULL ) {
+ /* Get header from message */
+ if ( mail_get_first_header_utf8
+ (mail, max_header->header_name, &header_value) < 0 ) {
+ return sieve_runtime_mail_error (renv, mail,
+ "%s test: failed to read header field `%s'",
+ sieve_extension_name(ext), max_header->header_name);
+ }
+ if ( header_value == NULL ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "header '%s' not found in message",
+ max_header->header_name);
+ goto failed;
+ }
+
+ if ( max_header->regexp_match ) {
+ /* Execute regex */
+ if ( regexec(&max_header->regexp, header_value, 2, match_values, 0)
+ != 0 ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "regexp for header '%s' did not match "
+ "on value '%s'", max_header->header_name, header_value);
+ goto failed;
+ }
+
+ max = _regexp_match_get_value(header_value, 1, match_values, 2);
+ if ( max == NULL ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "regexp did not return match value "
+ "for string '%s'", header_value);
+ goto failed;
+ }
+ } else {
+ max = header_value;
+ }
+
+ if ( !ext_spamvirustest_parse_decimal_value(max, &max_value, &error) ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "failed to parse maximum value: %s", error);
+ goto failed;
+ }
+ } else {
+ max_value = ext_data->max_value;
+ }
+
+ if ( max_value == 0 ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "error: max value is 0");
+ goto failed;
+ }
+ } else {
+ max_value = ( sieve_extension_is(ext, virustest_extension) ? 5 : 10 );
+ }
+
+ /*
+ * Get status value
+ */
+
+ /* Get header from message */
+ if ( mail_get_first_header_utf8
+ (mail, status_header->header_name, &header_value) < 0 ) {
+ return sieve_runtime_mail_error (renv, mail,
+ "%s test: failed to read header field `%s'",
+ sieve_extension_name(ext), status_header->header_name);
+ }
+ if ( header_value == NULL ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "header '%s' not found in message",
+ status_header->header_name);
+ goto failed;
+ }
+
+ /* Execute regex */
+ if ( status_header->regexp_match ) {
+ if ( regexec(&status_header->regexp, header_value, 2, match_values, 0)
+ != 0 ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "regexp for header '%s' did not match on value '%s'",
+ status_header->header_name, header_value);
+ goto failed;
+ }
+
+ status = _regexp_match_get_value(header_value, 1, match_values, 2);
+ if ( status == NULL ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "regexp did not return match value for string '%s'",
+ header_value);
+ goto failed;
+ }
+ } else {
+ status = header_value;
+ }
+
+ switch ( ext_data->status_type ) {
+ case EXT_SPAMVIRUSTEST_STATUS_TYPE_SCORE:
+ if ( !ext_spamvirustest_parse_decimal_value
+ (status, &status_value, &error) ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "failed to parse status value '%s': %s",
+ status, error);
+ goto failed;
+ }
+ break;
+ case EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN:
+ if ( !ext_spamvirustest_parse_strlen_value
+ (status, &status_value, &error) ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "failed to parse status value '%s': %s",
+ status, error);
+ goto failed;
+ }
+ break;
+ case EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT:
+ max_text = ( sieve_extension_is(ext, virustest_extension) ? 5 : 10 );
+ status_value = 0;
+
+ i = 0;
+ while ( i <= max_text ) {
+ if ( ext_data->text_values[i] != NULL &&
+ strcmp(status, ext_data->text_values[i]) == 0 ) {
+ status_value = (float) i;
+ break;
+ }
+ i++;
+ }
+
+ if ( i > max_text ) {
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "failed to match textstatus value '%s'",
+ status);
+ goto failed;
+ }
+ break;
+ default:
+ i_unreached();
+ break;
+ }
+
+ /* Calculate value */
+ if ( status_value < 0 )
+ mctx->score_ratio = 0;
+ else if ( status_value > max_value )
+ mctx->score_ratio = 1;
+ else
+ mctx->score_ratio = (status_value / max_value);
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
+ "extracted score=%.3f, max=%.3f, ratio=%.0f %%",
+ status_value, max_value, mctx->score_ratio * 100);
+
+ *value_r = ext_spamvirustest_get_score(ext, mctx->score_ratio, percent);
+ return SIEVE_EXEC_OK;
+
+failed:
+ mctx->score_ratio = -1;
+ *value_r = "0";
+ return SIEVE_EXEC_OK;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.h b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.h
new file mode 100644
index 0000000..331a637
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-common.h
@@ -0,0 +1,35 @@
+#ifndef EXT_SPAMVIRUSTEST_COMMON_H
+#define EXT_SPAMVIRUSTEST_COMMON_H
+
+#include "sieve-common.h"
+
+/*
+ * Extensions
+ */
+
+extern const struct sieve_extension_def spamtest_extension;
+extern const struct sieve_extension_def spamtestplus_extension;
+extern const struct sieve_extension_def virustest_extension;
+
+bool ext_spamvirustest_load(const struct sieve_extension *ext, void **context);
+void ext_spamvirustest_unload(const struct sieve_extension *ext);
+
+/*
+ * Tests
+ */
+
+extern const struct sieve_command_def spamtest_test;
+extern const struct sieve_command_def virustest_test;
+
+int ext_spamvirustest_get_value
+(const struct sieve_runtime_env *renv, const struct sieve_extension *ext,
+ bool percent, const char **value_r);
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def spamtest_operation;
+extern const struct sieve_operation_def virustest_operation;
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c
new file mode 100644
index 0000000..e0c9b54
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest.c
@@ -0,0 +1,146 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extensions spamtest, spamtestplus and virustest
+ * -----------------------------------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5235
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+/* Configuration examples:
+ *
+ * # 1: X-Spam-Score: No, score=-3.2
+ *
+ * sieve_spamtest_status_header = \
+ * X-Spam-Score: [[:alnum:]]+, score=(-?[[:digit:]]+\.[[:digit:]])
+ * sieve_spamtest_max_value = 5.0
+ *
+ * # 2: X-Spam-Status: Yes
+ *
+ * sieve_spamtest_status_header = X-Spam-Status
+ * sieve_spamtest_status_type = yesno
+ * sieve_spamtest_max_value = Yes
+ *
+ * # 3: X-Spam-Score: sssssss
+ * sieve_spamtest_status_header = X-Spam-Score
+ * sieve_spamtest_status_type = strlen
+ * sieve_spamtest_max_value = 5
+ *
+ * # 4: X-Spam-Score: status=3.2 required=5.0
+ *
+ * sieve_spamtest_status_header = \
+ * X-Spam-Score: score=(-?[[:digit:]]+\.[[:digit:]]).*
+ * sieve_spamtest_max_header = \
+ * X-Spam-Score: score=-?[[:digit:]]+\.[[:digit:]] required=([[:digit:]]+\.[[:digit:]])
+ *
+ * # 5: X-Virus-Scan: Found to be clean.
+ *
+ * sieve_virustest_status_header = \
+ * X-Virus-Scan: Found to be (.+)\.
+ * sieve_virustest_status_type = text
+ * sieve_virustest_text_value1 = clean
+ * sieve_virustest_text_value5 = infected
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-common.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+
+#include "sieve-validator.h"
+
+#include "ext-spamvirustest-common.h"
+
+/*
+ * Extensions
+ */
+
+/* Spamtest */
+
+static bool ext_spamvirustest_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *validator);
+
+const struct sieve_extension_def spamtest_extension = {
+ .name = "spamtest",
+ .load = ext_spamvirustest_load,
+ .unload = ext_spamvirustest_unload,
+ .validator_load = ext_spamvirustest_validator_load,
+ SIEVE_EXT_DEFINE_OPERATION(spamtest_operation)
+};
+
+const struct sieve_extension_def spamtestplus_extension = {
+ .name = "spamtestplus",
+ .load = ext_spamvirustest_load,
+ .unload = ext_spamvirustest_unload,
+ .validator_load = ext_spamvirustest_validator_load,
+ SIEVE_EXT_DEFINE_OPERATION(spamtest_operation)
+};
+
+const struct sieve_extension_def virustest_extension = {
+ .name = "virustest",
+ .load = ext_spamvirustest_load,
+ .unload = ext_spamvirustest_unload,
+ .validator_load = ext_spamvirustest_validator_load,
+ SIEVE_EXT_DEFINE_OPERATION(virustest_operation)
+};
+
+/*
+ * Implementation
+ */
+
+static bool ext_spamtest_validator_check_conflict
+ (const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context,
+ struct sieve_ast_argument *require_arg,
+ const struct sieve_extension *ext_other,
+ bool required);
+
+const struct sieve_validator_extension spamtest_validator_extension = {
+ .ext = &spamtest_extension,
+ .check_conflict = ext_spamtest_validator_check_conflict
+};
+
+static bool ext_spamvirustest_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register new test */
+
+ if ( sieve_extension_is(ext, virustest_extension) ) {
+ sieve_validator_register_command(valdtr, ext, &virustest_test);
+ } else {
+ if ( sieve_extension_is(ext, spamtest_extension) ) {
+ /* Register validator extension to warn for duplicate */
+ sieve_validator_extension_register
+ (valdtr, ext, &spamtest_validator_extension, NULL);
+ }
+
+ sieve_validator_register_command(valdtr, ext, &spamtest_test);
+ }
+
+ return TRUE;
+}
+
+static bool ext_spamtest_validator_check_conflict
+(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_validator *valdtr, void *context ATTR_UNUSED,
+ struct sieve_ast_argument *require_arg,
+ const struct sieve_extension *ext_other,
+ bool required ATTR_UNUSED)
+{
+ if ( sieve_extension_name_is(ext_other, "spamtestplus") ) {
+ sieve_argument_validate_warning(valdtr, require_arg,
+ "the spamtest and spamtestplus extensions should "
+ "not be specified at the same time");
+ }
+
+ return TRUE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c b/pigeonhole/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c
new file mode 100644
index 0000000..26d051e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/spamvirustest/tst-spamvirustest.c
@@ -0,0 +1,304 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-spamvirustest-common.h"
+
+/*
+ * Tests
+ */
+
+static bool tst_spamvirustest_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool tst_spamvirustest_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+static bool tst_spamvirustest_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+
+/* Spamtest test
+ *
+ * Syntax:
+ * spamtest [":percent"] [COMPARATOR] [MATCH-TYPE] <value: string>
+ */
+
+const struct sieve_command_def spamtest_test = {
+ .identifier = "spamtest",
+ .type = SCT_TEST,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_spamvirustest_registered,
+ .validate = tst_spamvirustest_validate,
+ .generate = tst_spamvirustest_generate
+};
+
+/* Virustest test
+ *
+ * Syntax:
+ * virustest [COMPARATOR] [MATCH-TYPE] <value: string>
+ */
+
+const struct sieve_command_def virustest_test = {
+ .identifier = "virustest",
+ .type = SCT_TEST,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_spamvirustest_registered,
+ .validate = tst_spamvirustest_validate,
+ .generate = tst_spamvirustest_generate
+};
+
+/*
+ * Tagged arguments
+ */
+
+static bool tst_spamtest_validate_percent_tag
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *tst);
+
+static const struct sieve_argument_def spamtest_percent_tag = {
+ .identifier = "percent",
+ .validate = tst_spamtest_validate_percent_tag
+};
+
+/*
+ * Spamtest and virustest operations
+ */
+
+static bool tst_spamvirustest_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_spamvirustest_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def spamtest_operation = {
+ .mnemonic = "SPAMTEST",
+ .ext_def = &spamtest_extension,
+ .dump = tst_spamvirustest_operation_dump,
+ .execute = tst_spamvirustest_operation_execute
+};
+
+const struct sieve_operation_def virustest_operation = {
+ .mnemonic = "VIRUSTEST",
+ .ext_def = &virustest_extension,
+ .dump = tst_spamvirustest_operation_dump,
+ .execute = tst_spamvirustest_operation_execute
+};
+
+/*
+ * Optional operands
+ */
+
+enum tst_spamvirustest_optional {
+ OPT_SPAMTEST_PERCENT = SIEVE_MATCH_OPT_LAST,
+ OPT_SPAMTEST_LAST
+};
+
+/*
+ * Test registration
+ */
+
+static bool tst_spamvirustest_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+ sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+ if ( sieve_extension_is(ext, spamtestplus_extension) ||
+ sieve_extension_is(ext, spamtest_extension) ) {
+ sieve_validator_register_tag
+ (valdtr, cmd_reg, ext, &spamtest_percent_tag, OPT_SPAMTEST_PERCENT);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Validation
+ */
+
+static bool tst_spamtest_validate_percent_tag
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *tst)
+{
+ if ( !sieve_extension_is(tst->ext, spamtestplus_extension) ) {
+ sieve_argument_validate_error(valdtr, *arg,
+ "the spamtest test only accepts the :percent argument when "
+ "the spamtestplus extension is active");
+ return FALSE;
+ }
+
+ /* Skip tag */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+static bool tst_spamvirustest_validate
+(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ const struct sieve_match_type mcht_default =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ const struct sieve_comparator cmp_default =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+
+ /* Check value */
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "value", 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ /* Validate the key argument to a specified match type */
+ return sieve_match_type_validate
+ (valdtr, tst, arg, &mcht_default, &cmp_default);
+}
+
+/*
+ * Code generation
+ */
+
+static bool tst_spamvirustest_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *tst)
+{
+ if ( sieve_command_is(tst, spamtest_test) )
+ sieve_operation_emit(cgenv->sblock, tst->ext, &spamtest_operation);
+ else if ( sieve_command_is(tst, virustest_test) )
+ sieve_operation_emit(cgenv->sblock, tst->ext, &virustest_operation);
+ else
+ i_unreached();
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, tst, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool tst_spamvirustest_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ int opt_code = 0;
+ const struct sieve_operation *op = denv->oprtn;
+
+ sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op));
+ sieve_code_descend(denv);
+
+ /* Optional operands */
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code)) < 0 )
+ return FALSE;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_SPAMTEST_PERCENT:
+ sieve_code_dumpf(denv, "percent");
+ break;
+ default:
+ return FALSE;
+ }
+ }
+
+ return
+ sieve_opr_string_dump(denv, address, "value");
+}
+
+/*
+ * Code execution
+ */
+
+static int tst_spamvirustest_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ const struct sieve_operation *op = renv->oprtn;
+ const struct sieve_extension *this_ext = op->ext;
+ int opt_code = 0;
+ struct sieve_match_type mcht =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ struct sieve_comparator cmp =
+ SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
+ bool percent = FALSE;
+ struct sieve_stringlist *value_list, *key_list;
+ const char *score_value;
+ int match, ret;
+
+ /* Read optional operands */
+ for (;;) {
+ int opt;
+
+ if ( (opt=sieve_match_opr_optional_read
+ (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 )
+ return ret;
+
+ if ( opt == 0 ) break;
+
+ switch ( opt_code ) {
+ case OPT_SPAMTEST_PERCENT:
+ percent = TRUE;
+ break;
+ default:
+ sieve_runtime_trace_error(renv, "unknown optional operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ }
+
+ /* Read value part */
+ if ( (ret=sieve_opr_stringlist_read(renv, address, "value", &key_list)) <= 0 )
+ return ret;
+
+ /* Perform test */
+
+ if ( sieve_operation_is(op, spamtest_operation) ) {
+ sieve_runtime_trace
+ (renv, SIEVE_TRLVL_TESTS, "spamtest test [percent=%s]",
+ ( percent ? "true" : "false" ));
+ } else {
+ sieve_runtime_trace
+ (renv, SIEVE_TRLVL_TESTS, "virustest test");
+ }
+
+ /* Get score value */
+ sieve_runtime_trace_descend(renv);
+ if ( (ret=ext_spamvirustest_get_value
+ (renv, this_ext, percent, &score_value)) <= 0 )
+ return ret;
+ sieve_runtime_trace_ascend(renv);
+
+ /* Construct value list */
+ value_list = sieve_single_stringlist_create_cstr(renv, score_value, TRUE);
+
+ /* Perform match */
+ if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 )
+ return ret;
+
+ /* Set test result for subsequent conditional jump */
+ sieve_interpreter_set_test_result(renv->interp, match > 0);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.am b/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.am
new file mode 100644
index 0000000..0f31aee
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.am
@@ -0,0 +1,22 @@
+noinst_LTLIBRARIES = libsieve_ext_special_use.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tags = \
+ tag-specialuse.c
+
+tests = \
+ tst-specialuse-exists.c
+
+libsieve_ext_special_use_la_SOURCES = \
+ $(tags) \
+ $(tests) \
+ ext-special-use-common.c \
+ ext-special-use.c
+
+headers = \
+ ext-special-use-common.h
+
+noinst_HEADERS = $(headers)
diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.in b/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.in
new file mode 100644
index 0000000..5032dae
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/special-use/Makefile.in
@@ -0,0 +1,703 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/special-use
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_special_use_la_LIBADD =
+am__objects_1 = tag-specialuse.lo
+am__objects_2 = tst-specialuse-exists.lo
+am_libsieve_ext_special_use_la_OBJECTS = $(am__objects_1) \
+ $(am__objects_2) ext-special-use-common.lo ext-special-use.lo
+libsieve_ext_special_use_la_OBJECTS = \
+ $(am_libsieve_ext_special_use_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-special-use-common.Plo \
+ ./$(DEPDIR)/ext-special-use.Plo ./$(DEPDIR)/tag-specialuse.Plo \
+ ./$(DEPDIR)/tst-specialuse-exists.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_special_use_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_special_use_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_special_use.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+tags = \
+ tag-specialuse.c
+
+tests = \
+ tst-specialuse-exists.c
+
+libsieve_ext_special_use_la_SOURCES = \
+ $(tags) \
+ $(tests) \
+ ext-special-use-common.c \
+ ext-special-use.c
+
+headers = \
+ ext-special-use-common.h
+
+noinst_HEADERS = $(headers)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/special-use/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/special-use/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_special_use.la: $(libsieve_ext_special_use_la_OBJECTS) $(libsieve_ext_special_use_la_DEPENDENCIES) $(EXTRA_libsieve_ext_special_use_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_special_use_la_OBJECTS) $(libsieve_ext_special_use_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-special-use-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-special-use.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag-specialuse.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-specialuse-exists.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-special-use-common.Plo
+ -rm -f ./$(DEPDIR)/ext-special-use.Plo
+ -rm -f ./$(DEPDIR)/tag-specialuse.Plo
+ -rm -f ./$(DEPDIR)/tst-specialuse-exists.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-special-use-common.Plo
+ -rm -f ./$(DEPDIR)/ext-special-use.Plo
+ -rm -f ./$(DEPDIR)/tag-specialuse.Plo
+ -rm -f ./$(DEPDIR)/tst-specialuse-exists.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.c b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.c
new file mode 100644
index 0000000..fcaf1b5
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.c
@@ -0,0 +1,31 @@
+/* Copyright (c) 2019 Pigeonhole authors, see the included COPYING file */
+
+#include "lib.h"
+#include "imap-arg.h"
+
+#include "ext-special-use-common.h"
+
+bool ext_special_use_flag_valid(const char *flag)
+{
+ const char *p = flag;
+
+ /* RFC 6154, Section 6:
+
+ use-attr = "\All" / "\Archive" / "\Drafts" / "\Flagged" /
+ "\Junk" / "\Sent" / "\Trash" / use-attr-ext
+ use-attr-ext = "\" atom
+ */
+
+ /* "\" */
+ if (*p != '\\')
+ return FALSE;
+ p++;
+
+ /* atom */
+ for (; *p != '\0'; p++) {
+ if (!IS_ATOM_CHAR(*p))
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.h b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.h
new file mode 100644
index 0000000..61f23d0
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use-common.h
@@ -0,0 +1,43 @@
+#ifndef EXT_SPECIAL_USE_COMMON_H
+#define EXT_SPECIAL_USE_COMMON_H
+
+#include "sieve-common.h"
+
+/*
+ * Tagged arguments
+ */
+
+extern const struct sieve_argument_def specialuse_tag;
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def specialuse_exists_test;
+
+/*
+ * Operands
+ */
+
+extern const struct sieve_operand_def specialuse_operand;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def specialuse_exists_operation;
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def special_use_extension;
+
+/*
+ * Flag checking
+ */
+
+bool ext_special_use_flag_valid(const char *flag);
+
+#endif /* EXT_SPECIAL_USE_COMMON_H */
+
diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use.c b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use.c
new file mode 100644
index 0000000..acbb13e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/special-use/ext-special-use.c
@@ -0,0 +1,57 @@
+/* Copyright (c) 2019 Pigeonhole authors, see the included COPYING file */
+
+/* Extension special-use
+ * ---------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 8579
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "ext-special-use-common.h"
+
+/*
+ * Extension
+ */
+
+static bool
+ext_special_use_validator_load(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr);
+
+const struct sieve_extension_def special_use_extension = {
+ .name = "special-use",
+ .validator_load = ext_special_use_validator_load,
+ SIEVE_EXT_DEFINE_OPERATION(specialuse_exists_operation),
+ SIEVE_EXT_DEFINE_OPERAND(specialuse_operand)
+};
+
+static bool
+ext_special_use_validator_load(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr)
+{
+ /* Register :specialuse tag with fileinto command and we don't care
+ whether this command is registered or even whether it will be
+ registered at all. The validator handles either situation gracefully.
+ */
+ sieve_validator_register_external_tag(
+ valdtr, "fileinto", ext, &specialuse_tag,
+ SIEVE_OPT_SIDE_EFFECT);
+
+ /* Register new test */
+ sieve_validator_register_command(valdtr, ext, &specialuse_exists_test);
+
+ return TRUE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/tag-specialuse.c b/pigeonhole/src/lib-sieve/plugins/special-use/tag-specialuse.c
new file mode 100644
index 0000000..0f6d32a
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/special-use/tag-specialuse.c
@@ -0,0 +1,316 @@
+/* Copyright (c) 2019 Pigeonhole authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-actions.h"
+#include "sieve-code.h"
+#include "sieve-actions.h"
+#include "sieve-result.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include "ext-special-use-common.h"
+
+/*
+ * Flags tagged argument
+ */
+
+static bool
+tag_specialuse_validate(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool
+tag_specialuse_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg,
+ struct sieve_command *cmd);
+
+const struct sieve_argument_def specialuse_tag = {
+ .identifier = "specialuse",
+ .validate = tag_specialuse_validate,
+ .generate = tag_specialuse_generate
+};
+
+/*
+ * Side effect
+ */
+
+static bool
+seff_specialuse_dump_context(const struct sieve_side_effect *seffect,
+ const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+seff_specialuse_read_context(const struct sieve_side_effect *seffect,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address, void **context);
+
+static int
+seff_specialuse_merge(const struct sieve_runtime_env *renv,
+ const struct sieve_action *action,
+ const struct sieve_side_effect *old_seffect,
+ const struct sieve_side_effect *new_seffect,
+ void **old_context);
+
+static void
+seff_specialuse_print(const struct sieve_side_effect *seffect,
+ const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv, bool *keep);
+
+static int
+seff_specialuse_pre_execute(const struct sieve_side_effect *seffect,
+ const struct sieve_action_exec_env *aenv,
+ void *tr_context, void **se_tr_context ATTR_UNUSED);
+
+const struct sieve_side_effect_def specialuse_side_effect = {
+ SIEVE_OBJECT("specialuse", &specialuse_operand, 0),
+ .precedence = 200,
+ .to_action = &act_store,
+ .dump_context = seff_specialuse_dump_context,
+ .read_context = seff_specialuse_read_context,
+ .merge = seff_specialuse_merge,
+ .print = seff_specialuse_print,
+ .pre_execute = seff_specialuse_pre_execute
+};
+
+/*
+ * Operand
+ */
+
+static const struct sieve_extension_objects ext_side_effects =
+ SIEVE_EXT_DEFINE_SIDE_EFFECT(specialuse_side_effect);
+
+const struct sieve_operand_def specialuse_operand = {
+ .name = "specialuse operand",
+ .ext_def = &special_use_extension,
+ .class = &sieve_side_effect_operand_class,
+ .interface = &ext_side_effects
+};
+
+/*
+ * Tag validation
+ */
+
+static bool
+tag_specialuse_validate(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_argument_next(*arg);
+
+ /* Check syntax:
+ * :specialuse <special-use-flag: string>
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_STRING, FALSE))
+ return FALSE;
+ if (sieve_argument_is_string_literal(*arg)) {
+ const char *use_flag = sieve_ast_argument_strc(*arg);
+
+ if (!ext_special_use_flag_valid(use_flag)) {
+ sieve_argument_validate_error(
+ valdtr, *arg, "specialuse tag: "
+ "invalid special-use flag `%s' specified",
+ str_sanitize(use_flag, 64));
+ return FALSE;
+ }
+ }
+
+ tag->parameters = *arg;
+
+ /* Detach parameter */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+tag_specialuse_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_ast_argument *arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *param;
+
+ if (sieve_ast_argument_type(arg) != SAAT_TAG)
+ return FALSE;
+
+ sieve_opr_side_effect_emit(cgenv->sblock, arg->argument->ext,
+ &specialuse_side_effect);
+
+ /* Explicit :specialuse tag */
+ param = arg->parameters;
+
+ /* Call the generation function for the argument */
+ if (param->argument != NULL && param->argument->def != NULL &&
+ param->argument->def->generate != NULL &&
+ !param->argument->def->generate(cgenv, param, cmd))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Side effect implementation
+ */
+
+/* Context data */
+
+struct seff_specialuse_context {
+ const char *special_use_flag;
+};
+
+/* Context coding */
+
+static bool
+seff_specialuse_dump_context(
+ const struct sieve_side_effect *seffect ATTR_UNUSED,
+ const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ return sieve_opr_stringlist_dump(denv, address, "specialuse");
+}
+
+static int
+seff_specialuse_read_context(
+ const struct sieve_side_effect *seffect ATTR_UNUSED,
+ const struct sieve_runtime_env *renv, sieve_size_t *address,
+ void **se_context)
+{
+ pool_t pool = sieve_result_pool(renv->result);
+ struct seff_specialuse_context *ctx;
+ string_t *special_use_flag;
+ const char *use_flag;
+ int ret;
+
+ if ((ret = sieve_opr_string_read(renv, address, "specialuse",
+ &special_use_flag)) <= 0)
+ return ret;
+
+ use_flag = str_c(special_use_flag);
+ if (!ext_special_use_flag_valid(use_flag)) {
+ sieve_runtime_error(
+ renv, NULL, "specialuse tag: "
+ "invalid special-use flag `%s' specified",
+ str_sanitize(use_flag, 64));
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ ctx = p_new(pool, struct seff_specialuse_context, 1);
+ ctx->special_use_flag = p_strdup(pool, use_flag);
+
+ *se_context = (void *) ctx;
+
+ return SIEVE_EXEC_OK;
+}
+
+/* Result verification */
+
+static int
+seff_specialuse_merge(const struct sieve_runtime_env *renv ATTR_UNUSED,
+ const struct sieve_action *action ATTR_UNUSED,
+ const struct sieve_side_effect *old_seffect ATTR_UNUSED,
+ const struct sieve_side_effect *new_seffect,
+ void **old_context)
+{
+ if (new_seffect != NULL)
+ *old_context = new_seffect->context;
+
+ return 1;
+}
+
+/* Result printing */
+
+static void
+seff_specialuse_print(const struct sieve_side_effect *seffect,
+ const struct sieve_action *action ATTR_UNUSED,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep ATTR_UNUSED)
+{
+ struct seff_specialuse_context *ctx =
+ (struct seff_specialuse_context *)seffect->context;
+
+ sieve_result_seffect_printf(
+ rpenv,
+ "use mailbox with special-use flag `%s' instead if accessible",
+ ctx->special_use_flag);
+}
+
+/* Result execution */
+
+static int
+seff_specialuse_pre_execute(const struct sieve_side_effect *seffect,
+ const struct sieve_action_exec_env *aenv,
+ void *tr_context, void **se_tr_context ATTR_UNUSED)
+{
+ struct seff_specialuse_context *ctx =
+ (struct seff_specialuse_context *)seffect->context;
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct act_store_transaction *trans =
+ (struct act_store_transaction *)tr_context;
+ struct mailbox *box;
+
+ if (trans->box == NULL || trans->disabled)
+ return SIEVE_EXEC_OK;
+
+ /* Check whether something already failed */
+ switch (trans->error_code) {
+ case MAIL_ERROR_NONE:
+ break;
+ case MAIL_ERROR_TEMP:
+ return SIEVE_EXEC_TEMP_FAILURE;
+ default:
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ trans->error = NULL;
+ trans->error_code = MAIL_ERROR_NONE;
+
+ box = mailbox_alloc_for_user(eenv->scriptenv->user,
+ ctx->special_use_flag,
+ (MAILBOX_FLAG_POST_SESSION |
+ MAILBOX_FLAG_SPECIAL_USE));
+
+ /* We still override the allocate default mailbox with ours below even
+ when the default and special-use mailbox are identical. Choosing
+ either one is (currently) equal and setting trans->mailbox_identifier
+ for SPECIAL-USE needs to be done either way, so we use the same code
+ path. */
+
+ /* Try to open the mailbox */
+ eenv->exec_status->last_storage = mailbox_get_storage(box);
+ if (mailbox_open(box) == 0) {
+ pool_t pool = sieve_result_pool(aenv->result);
+
+ /* Success */
+ mailbox_free(&trans->box);
+ trans->box = box;
+ trans->mailbox_identifier = p_strdup_printf(pool,
+ "[SPECIAL-USE %s]", ctx->special_use_flag);
+
+ } else {
+ /* Failure */
+ if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTFOUND) {
+ /* Not found; revert to default */
+ mailbox_free(&box);
+ } else {
+ /* Total failure */
+ mailbox_free(&trans->box);
+ trans->box = box;
+ sieve_act_store_get_storage_error(aenv, trans);
+ return (trans->error_code == MAIL_ERROR_TEMP ?
+ SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE);
+ }
+ }
+
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c b/pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c
new file mode 100644
index 0000000..a1aa878
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c
@@ -0,0 +1,525 @@
+/* Copyright (c) 2019 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+
+#include "sieve-common.h"
+#include "sieve-actions.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-special-use-common.h"
+
+/*
+ * specialuse_exists command
+ *
+ * Syntax:
+ * specialuse_exists [<mailbox: string>]
+ * <special-use-flags: string-list>
+ */
+
+static bool
+tst_specialuse_exists_validate(struct sieve_validator *valdtr,
+ struct sieve_command *tst);
+static bool
+tst_specialuse_exists_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+const struct sieve_command_def specialuse_exists_test = {
+ .identifier = "specialuse_exists",
+ .type = SCT_TEST,
+ .positional_args = -1, /* We check positional arguments ourselves */
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = tst_specialuse_exists_validate,
+ .generate = tst_specialuse_exists_generate,
+};
+
+/*
+ * Mailboxexists operation
+ */
+
+static bool
+tst_specialuse_exists_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+tst_specialuse_exists_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def specialuse_exists_operation = {
+ .mnemonic = "SPECIALUSE_EXISTS",
+ .ext_def = &special_use_extension,
+ .dump = tst_specialuse_exists_operation_dump,
+ .execute = tst_specialuse_exists_operation_execute,
+};
+
+/*
+ * Test validation
+ */
+
+struct _validate_context {
+ struct sieve_validator *valdtr;
+ struct sieve_command *tst;
+};
+
+static int
+tst_specialuse_exists_flag_validate(void *context,
+ struct sieve_ast_argument *arg)
+{
+ struct _validate_context *valctx = (struct _validate_context *)context;
+
+ if (sieve_argument_is_string_literal(arg)) {
+ const char *flag = sieve_ast_argument_strc(arg);
+
+ if (!ext_special_use_flag_valid(flag)) {
+ sieve_argument_validate_error(
+ valctx->valdtr, arg, "%s test: "
+ "invalid special-use flag `%s' specified",
+ sieve_command_identifier(valctx->tst),
+ str_sanitize(flag, 64));
+ }
+ }
+
+ return 1;
+}
+
+static bool
+tst_specialuse_exists_validate(struct sieve_validator *valdtr,
+ struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ struct sieve_ast_argument *arg2;
+ struct sieve_ast_argument *aarg;
+ struct _validate_context valctx;
+
+ if (arg == NULL) {
+ sieve_command_validate_error(
+ valdtr, tst, "the %s %s expects at least one argument, "
+ "but none was found",
+ sieve_command_identifier(tst),
+ sieve_command_type_name(tst));
+ return FALSE;
+ }
+
+ if (sieve_ast_argument_type(arg) != SAAT_STRING &&
+ sieve_ast_argument_type(arg) != SAAT_STRING_LIST) {
+ sieve_argument_validate_error(
+ valdtr, arg,
+ "the %s %s expects either a string (mailbox) or "
+ "a string-list (special-use flags) as first argument, "
+ "but %s was found",
+ sieve_command_identifier(tst),
+ sieve_command_type_name(tst),
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+
+ arg2 = sieve_ast_argument_next(arg);
+ if (arg2 != NULL) {
+ /* First, check syntax sanity */
+ if (sieve_ast_argument_type(arg) != SAAT_STRING) {
+ sieve_argument_validate_error(
+ valdtr, arg,
+ "if a second argument is specified for the %s %s, "
+ "the first must be a string (mailbox), "
+ "but %s was found",
+ sieve_command_identifier(tst),
+ sieve_command_type_name(tst),
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+ if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
+ return FALSE;
+
+ /* Check name validity when mailbox argument is not a variable */
+ if (sieve_argument_is_string_literal(arg)) {
+ const char *mailbox = sieve_ast_argument_strc(arg);
+ const char *error;
+
+ if (!sieve_mailbox_check_name(mailbox, &error)) {
+ sieve_argument_validate_warning(
+ valdtr, arg, "%s test: "
+ "invalid mailbox name `%s' specified: %s",
+ sieve_command_identifier(tst),
+ str_sanitize(mailbox, 256), error);
+ }
+ }
+
+ if (sieve_ast_argument_type(arg2) != SAAT_STRING &&
+ sieve_ast_argument_type(arg2) != SAAT_STRING_LIST) {
+ sieve_argument_validate_error(
+ valdtr, arg2,
+ "the %s %s expects a string list (special-use flags) as "
+ "second argument when two arguments are specified, "
+ "but %s was found",
+ sieve_command_identifier(tst),
+ sieve_command_type_name(tst),
+ sieve_ast_argument_name(arg2));
+ return FALSE;
+ }
+ } else
+ arg2 = arg;
+
+ if (!sieve_validator_argument_activate(valdtr, tst, arg2, FALSE))
+ return FALSE;
+
+ aarg = arg2;
+ memset(&valctx, 0, sizeof(valctx));
+ valctx.valdtr = valdtr;
+ valctx.tst = tst;
+
+ return (sieve_ast_stringlist_map(
+ &aarg, (void*)&valctx,
+ tst_specialuse_exists_flag_validate) >= 0);
+}
+
+/*
+ * Test generation
+ */
+
+static bool
+tst_specialuse_exists_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ struct sieve_ast_argument *arg2;
+
+ sieve_operation_emit(cgenv->sblock,
+ tst->ext, &specialuse_exists_operation);
+
+ /* Generate arguments */
+ arg2 = sieve_ast_argument_next(arg);
+ if (arg2 != NULL) {
+ if (!sieve_generate_argument(cgenv, arg, tst))
+ return FALSE;
+ } else {
+ sieve_opr_omitted_emit(cgenv->sblock);
+ arg2 = arg;
+ }
+ return sieve_generate_argument(cgenv, arg2, tst);
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+tst_specialuse_exists_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ struct sieve_operand oprnd;
+
+ sieve_code_dumpf(denv, "SPECIALUSE_EXISTS");
+ sieve_code_descend(denv);
+
+ sieve_code_mark(denv);
+ if (!sieve_operand_read(denv->sblock, address, NULL, &oprnd)) {
+ sieve_code_dumpf(denv, "ERROR: INVALID OPERAND");
+ return FALSE;
+ }
+
+ if (!sieve_operand_is_omitted(&oprnd)) {
+ return (sieve_opr_string_dump_data(denv, &oprnd,
+ address, "mailbox") &&
+ sieve_opr_stringlist_dump(denv, address,
+ "special-use-flags"));
+ }
+
+ return sieve_opr_stringlist_dump(denv, address, "special-use-flags");
+}
+
+/*
+ * Code execution
+ */
+
+static int
+tst_specialuse_find_mailbox(const struct sieve_runtime_env *renv,
+ const char *mailbox, struct mailbox **box_r)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct mail_user *user = eenv->scriptenv->user;
+ struct mailbox *box;
+ bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
+ enum mail_error error_code;
+ const char *error;
+
+ *box_r = NULL;
+
+ if (user == NULL)
+ return 0;
+
+ /* Open the box */
+ box = mailbox_alloc_for_user(user, mailbox, MAILBOX_FLAG_POST_SESSION);
+ if (mailbox_open(box) < 0) {
+ error = mailbox_get_last_internal_error(box, &error_code);
+
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox `%s' cannot be opened: %s",
+ str_sanitize(mailbox, 256), error);
+ }
+
+ mailbox_free(&box);
+
+ if (error_code == MAIL_ERROR_TEMP) {
+ sieve_runtime_error(
+ renv, NULL, "specialuse_exists test: "
+ "failed to open mailbox `%s': %s",
+ str_sanitize(mailbox, 256), error);
+ return -1;
+ }
+ return 0;
+ }
+
+ /* Also fail when it is readonly */
+ if (mailbox_is_readonly(box)) {
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox `%s' is read-only",
+ str_sanitize(mailbox, 256));
+ }
+
+ mailbox_free(&box);
+ return 0;
+ }
+
+ *box_r = box;
+ return 1;
+}
+
+static int
+tst_specialuse_find_specialuse(const struct sieve_runtime_env *renv,
+ const char *special_use)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct mail_user *user = eenv->scriptenv->user;
+ struct mailbox *box;
+ bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
+ enum mail_error error_code;
+ const char *error;
+
+ if (user == NULL)
+ return 0;
+
+ /* Open the box */
+ box = mailbox_alloc_for_user(user, special_use,
+ (MAILBOX_FLAG_POST_SESSION |
+ MAILBOX_FLAG_SPECIAL_USE));
+ if (mailbox_open(box) < 0) {
+ error = mailbox_get_last_internal_error(box, &error_code);
+
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox with special-use flag `%s' "
+ "cannot be opened: %s",
+ str_sanitize(special_use, 64), error);
+ }
+
+ mailbox_free(&box);
+
+ if (error_code == MAIL_ERROR_TEMP) {
+ sieve_runtime_error(
+ renv, NULL, "specialuse_exists test: "
+ "failed to open mailbox with special-use flag`%s': %s",
+ str_sanitize(special_use, 64), error);
+ return -1;
+ }
+ return 0;
+ }
+
+ /* Also fail when it is readonly */
+ if (mailbox_is_readonly(box)) {
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0,
+ "mailbox with special-use flag `%s' is read-only",
+ str_sanitize(special_use, 64));
+ }
+
+ mailbox_free(&box);
+ return 0;
+ }
+
+ mailbox_free(&box);
+ return 1;
+}
+
+static int
+tst_specialuse_exists_check_flag(const struct sieve_runtime_env *renv,
+ struct mailbox *box, const char *use_flag,
+ bool trace, bool *all_exist_r)
+{
+ int ret;
+
+ if (!ext_special_use_flag_valid(use_flag)) {
+ sieve_runtime_error(
+ renv, NULL, "specialuse_exists test: "
+ "invalid special-use flag `%s' specified",
+ str_sanitize(use_flag, 64));
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ if (box != NULL) {
+ /* Mailbox has this SPECIAL-USE flag? */
+ if (!mailbox_has_special_use(box, use_flag)) {
+ *all_exist_r = FALSE;
+ return SIEVE_EXEC_OK;
+ }
+ } else {
+ /* Is there mailbox with this SPECIAL-USE flag? */
+ ret = tst_specialuse_find_specialuse(renv, use_flag);
+ if (ret < 0)
+ return SIEVE_EXEC_TEMP_FAILURE;
+ if (ret == 0) {
+ *all_exist_r = FALSE;
+ return SIEVE_EXEC_OK;
+ }
+ }
+
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0, "special-use flag `%s' exists",
+ str_sanitize(use_flag, 80));
+ }
+
+ return SIEVE_EXEC_OK;
+}
+
+static int
+tst_specialuse_exists_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ struct sieve_operand oprnd;
+ struct sieve_stringlist *special_use_flags;
+ string_t *mailbox, *special_use_flag;
+ struct mailbox *box = NULL;
+ const char *error;
+ bool trace = FALSE, all_exist = TRUE;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Read bare operand (two types possible) */
+ ret = sieve_operand_runtime_read(renv, address, NULL, &oprnd);
+ if (ret <= 0)
+ return ret;
+
+ /* Mailbox operand (optional) */
+ mailbox = NULL;
+ if (!sieve_operand_is_omitted(&oprnd)) {
+ /* Read the mailbox operand */
+ ret = sieve_opr_string_read_data(renv, &oprnd, address,
+ "mailbox", &mailbox);
+ if (ret <= 0)
+ return ret;
+
+ /* Read flag list */
+ ret = sieve_opr_stringlist_read(renv, address,
+ "special-use-flags",
+ &special_use_flags);
+ if (ret <= 0)
+ return ret;
+
+ /* Flag-list operand */
+ } else {
+ /* Read flag list */
+ ret = sieve_opr_stringlist_read(renv, address,
+ "special-use-flags",
+ &special_use_flags);
+ if (ret <= 0)
+ return ret;
+ }
+
+ /*
+ * Perform operation
+ */
+
+ if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS)) {
+ sieve_runtime_trace(renv, 0, "specialuse_exists test");
+ sieve_runtime_trace_descend(renv);
+
+ trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
+ }
+
+ if (mailbox != NULL) {
+ if (!sieve_mailbox_check_name(str_c(mailbox), &error)) {
+ sieve_runtime_warning(
+ renv, NULL, "specialuse_exists test: "
+ "invalid mailbox name `%s' specified: %s",
+ str_sanitize(str_c(mailbox), 256), error);
+ sieve_interpreter_set_test_result(renv->interp, FALSE);
+ return SIEVE_EXEC_OK;
+ }
+
+ if (tst_specialuse_find_mailbox(renv, str_c(mailbox), &box) < 0)
+ return SIEVE_EXEC_TEMP_FAILURE;
+ }
+
+ if (box == NULL && mailbox != NULL) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox `%s' is not accessible",
+ str_sanitize(str_c(mailbox), 80));
+ sieve_interpreter_set_test_result(renv->interp, FALSE);
+ return SIEVE_EXEC_OK;
+ }
+
+ if (mailbox != NULL) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox `%s' is accessible",
+ str_sanitize(str_c(mailbox), 80));
+ }
+
+ ret = 0;
+ special_use_flag = NULL;
+ while (all_exist &&
+ (ret = sieve_stringlist_next_item(
+ special_use_flags, &special_use_flag)) > 0) {
+ const char *use_flag = str_c(special_use_flag);
+
+ ret = tst_specialuse_exists_check_flag(
+ renv, box, use_flag, trace, &all_exist);
+ if (ret <= 0) {
+ if (box != NULL) {
+ /* Close mailbox */
+ mailbox_free(&box);
+ }
+ return ret;
+ }
+ }
+
+ if (box != NULL) {
+ /* Close mailbox */
+ mailbox_free(&box);
+ }
+
+ if (ret < 0) {
+ sieve_runtime_trace_error(
+ renv, "invalid special-use flag item");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if (trace) {
+ if (all_exist) {
+ sieve_runtime_trace(
+ renv, 0, "all special-use flags are set");
+ } else {
+ sieve_runtime_trace(
+ renv, 0, "some special-use are not set");
+ }
+ }
+
+ sieve_interpreter_set_test_result(renv->interp, all_exist);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.am b/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.am
new file mode 100644
index 0000000..2bab53f
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.am
@@ -0,0 +1,8 @@
+noinst_LTLIBRARIES = libsieve_ext_subaddress.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_subaddress_la_SOURCES = \
+ ext-subaddress.c
diff --git a/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.in b/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.in
new file mode 100644
index 0000000..c5b6b7c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/subaddress/Makefile.in
@@ -0,0 +1,673 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/subaddress
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_subaddress_la_LIBADD =
+am_libsieve_ext_subaddress_la_OBJECTS = ext-subaddress.lo
+libsieve_ext_subaddress_la_OBJECTS = \
+ $(am_libsieve_ext_subaddress_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-subaddress.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_subaddress_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_subaddress_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_subaddress.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_subaddress_la_SOURCES = \
+ ext-subaddress.c
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/subaddress/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/subaddress/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_subaddress.la: $(libsieve_ext_subaddress_la_OBJECTS) $(libsieve_ext_subaddress_la_DEPENDENCIES) $(EXTRA_libsieve_ext_subaddress_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_subaddress_la_OBJECTS) $(libsieve_ext_subaddress_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-subaddress.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-subaddress.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-subaddress.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/subaddress/ext-subaddress.c b/pigeonhole/src/lib-sieve/plugins/subaddress/ext-subaddress.c
new file mode 100644
index 0000000..abaa7ae
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/subaddress/ext-subaddress.c
@@ -0,0 +1,191 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension subaddress
+ * --------------------
+ *
+ * Author: Stephan Bosch
+ * Specification: RFC 3598
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-settings.h"
+#include "sieve-code.h"
+#include "sieve-address.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-address-parts.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+
+#include <string.h>
+
+/*
+ * Configuration
+ */
+
+#define SUBADDRESS_DEFAULT_DELIM "+"
+
+struct ext_subaddress_config {
+ char *delimiter;
+};
+
+/*
+ * Forward declarations
+ */
+
+const struct sieve_address_part_def user_address_part;
+const struct sieve_address_part_def detail_address_part;
+
+static struct sieve_operand_def subaddress_operand;
+
+/*
+ * Extension
+ */
+
+static bool ext_subaddress_load
+ (const struct sieve_extension *ext, void **context);
+static void ext_subaddress_unload
+ (const struct sieve_extension *ext);
+static bool ext_subaddress_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *validator);
+
+const struct sieve_extension_def subaddress_extension = {
+ .name = "subaddress",
+ .load = ext_subaddress_load,
+ .unload = ext_subaddress_unload,
+ .validator_load = ext_subaddress_validator_load,
+ SIEVE_EXT_DEFINE_OPERAND(subaddress_operand)
+};
+
+static bool ext_subaddress_load
+(const struct sieve_extension *ext, void **context)
+{
+ struct ext_subaddress_config *config;
+ const char *delim;
+
+ if ( *context != NULL ) {
+ ext_subaddress_unload(ext);
+ }
+
+ delim = sieve_setting_get(ext->svinst, "recipient_delimiter");
+
+ if ( delim == NULL )
+ delim = SUBADDRESS_DEFAULT_DELIM;
+
+ config = i_new(struct ext_subaddress_config, 1);
+ config->delimiter = i_strdup(delim);
+
+ *context = (void *) config;
+
+ return TRUE;
+}
+
+static void ext_subaddress_unload
+(const struct sieve_extension *ext)
+{
+ struct ext_subaddress_config *config =
+ (struct ext_subaddress_config *) ext->context;
+
+ i_free(config->delimiter);
+ i_free(config);
+}
+
+static bool ext_subaddress_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *validator)
+{
+ sieve_address_part_register(validator, ext, &user_address_part);
+ sieve_address_part_register(validator, ext, &detail_address_part);
+
+ return TRUE;
+}
+
+/*
+ * Address parts
+ */
+
+enum ext_subaddress_address_part {
+ SUBADDRESS_USER,
+ SUBADDRESS_DETAIL
+};
+
+/* Forward declarations */
+
+static const char *subaddress_user_extract_from
+ (const struct sieve_address_part *addrp, const struct smtp_address *address);
+static const char *subaddress_detail_extract_from
+ (const struct sieve_address_part *addrp, const struct smtp_address *address);
+
+/* Address part objects */
+
+const struct sieve_address_part_def user_address_part = {
+ SIEVE_OBJECT("user",
+ &subaddress_operand, SUBADDRESS_USER),
+ subaddress_user_extract_from
+};
+
+const struct sieve_address_part_def detail_address_part = {
+ SIEVE_OBJECT("detail",
+ &subaddress_operand, SUBADDRESS_DETAIL),
+ .extract_from = subaddress_detail_extract_from
+};
+
+/* Address part implementation */
+
+static const char *subaddress_user_extract_from
+(const struct sieve_address_part *addrp, const struct smtp_address *address)
+{
+ struct ext_subaddress_config *config =
+ (struct ext_subaddress_config *) addrp->object.ext->context;
+ const char *delim;
+ size_t idx;
+
+ idx = strcspn(address->localpart, config->delimiter);
+ delim = address->localpart[idx] != '\0' ? address->localpart + idx : NULL;
+
+ if ( delim == NULL ) return address->localpart;
+
+ return t_strdup_until(address->localpart, delim);
+}
+
+static const char *subaddress_detail_extract_from
+(const struct sieve_address_part *addrp, const struct smtp_address *address)
+{
+ struct ext_subaddress_config *config =
+ (struct ext_subaddress_config *) addrp->object.ext->context;
+ const char *delim;
+ size_t idx;
+
+ idx = strcspn(address->localpart, config->delimiter);
+ delim = address->localpart[idx] != '\0' ? address->localpart + idx + 1: NULL;
+
+ /* Just to be sure */
+ if ( delim == NULL ||
+ delim > (address->localpart + strlen(address->localpart)) )
+ return NULL;
+ return delim;
+}
+
+/*
+ * Operand
+ */
+
+const struct sieve_address_part_def *ext_subaddress_parts[] = {
+ &user_address_part, &detail_address_part
+};
+
+static const struct sieve_extension_objects ext_address_parts =
+ SIEVE_EXT_DEFINE_ADDRESS_PARTS(ext_subaddress_parts);
+
+static struct sieve_operand_def subaddress_operand = {
+ .name = "address-part",
+ .ext_def = &subaddress_extension,
+ .class = &sieve_address_part_operand_class,
+ .interface = &ext_address_parts
+};
+
diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.am b/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.am
new file mode 100644
index 0000000..09df27b
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.am
@@ -0,0 +1,19 @@
+noinst_LTLIBRARIES = libsieve_ext_vacation.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../util \
+ $(LIBDOVECOT_INCLUDE)
+
+cmds = \
+ cmd-vacation.c
+
+libsieve_ext_vacation_la_SOURCES = \
+ $(cmds) \
+ ext-vacation-common.c \
+ ext-vacation.c \
+ ext-vacation-seconds.c
+
+noinst_HEADERS = \
+ ext-vacation-common.h
+
diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.in b/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.in
new file mode 100644
index 0000000..f18806a
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vacation/Makefile.in
@@ -0,0 +1,700 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/vacation
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_vacation_la_LIBADD =
+am__objects_1 = cmd-vacation.lo
+am_libsieve_ext_vacation_la_OBJECTS = $(am__objects_1) \
+ ext-vacation-common.lo ext-vacation.lo ext-vacation-seconds.lo
+libsieve_ext_vacation_la_OBJECTS = \
+ $(am_libsieve_ext_vacation_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-vacation.Plo \
+ ./$(DEPDIR)/ext-vacation-common.Plo \
+ ./$(DEPDIR)/ext-vacation-seconds.Plo \
+ ./$(DEPDIR)/ext-vacation.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_vacation_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_vacation_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_vacation.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../util \
+ $(LIBDOVECOT_INCLUDE)
+
+cmds = \
+ cmd-vacation.c
+
+libsieve_ext_vacation_la_SOURCES = \
+ $(cmds) \
+ ext-vacation-common.c \
+ ext-vacation.c \
+ ext-vacation-seconds.c
+
+noinst_HEADERS = \
+ ext-vacation-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/vacation/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/vacation/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_vacation.la: $(libsieve_ext_vacation_la_OBJECTS) $(libsieve_ext_vacation_la_DEPENDENCIES) $(EXTRA_libsieve_ext_vacation_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_vacation_la_OBJECTS) $(libsieve_ext_vacation_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-vacation.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vacation-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vacation-seconds.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vacation.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmd-vacation.Plo
+ -rm -f ./$(DEPDIR)/ext-vacation-common.Plo
+ -rm -f ./$(DEPDIR)/ext-vacation-seconds.Plo
+ -rm -f ./$(DEPDIR)/ext-vacation.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmd-vacation.Plo
+ -rm -f ./$(DEPDIR)/ext-vacation-common.Plo
+ -rm -f ./$(DEPDIR)/ext-vacation-seconds.Plo
+ -rm -f ./$(DEPDIR)/ext-vacation.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/cmd-vacation.c b/pigeonhole/src/lib-sieve/plugins/vacation/cmd-vacation.c
new file mode 100644
index 0000000..ddc14be
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -0,0 +1,1578 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "strfuncs.h"
+#include "md5.h"
+#include "hostpid.h"
+#include "str-sanitize.h"
+#include "ostream.h"
+#include "message-address.h"
+#include "message-date.h"
+#include "var-expand.h"
+#include "ioloop.h"
+#include "mail-storage.h"
+
+#include "rfc2822.h"
+
+#include "sieve-common.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-address.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-result.h"
+#include "sieve-message.h"
+#include "sieve-smtp.h"
+
+#include "ext-vacation-common.h"
+
+#include <stdio.h>
+
+/*
+ * Forward declarations
+ */
+
+static const struct sieve_argument_def vacation_days_tag;
+static const struct sieve_argument_def vacation_subject_tag;
+static const struct sieve_argument_def vacation_from_tag;
+static const struct sieve_argument_def vacation_addresses_tag;
+static const struct sieve_argument_def vacation_mime_tag;
+static const struct sieve_argument_def vacation_handle_tag;
+
+/*
+ * Vacation command
+ *
+ * Syntax:
+ * vacation [":days" number] [":subject" string]
+ * [":from" string] [":addresses" string-list]
+ * [":mime"] [":handle" string] <reason: string>
+ */
+
+static bool
+cmd_vacation_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool
+cmd_vacation_pre_validate(struct sieve_validator *valdtr,
+ struct sieve_command *cmd);
+static bool
+cmd_vacation_validate(struct sieve_validator *valdtr,
+ struct sieve_command *cmd);
+static bool
+cmd_vacation_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd);
+
+const struct sieve_command_def vacation_command = {
+ .identifier = "vacation",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_vacation_registered,
+ .pre_validate = cmd_vacation_pre_validate,
+ .validate = cmd_vacation_validate,
+ .generate = cmd_vacation_generate,
+};
+
+/*
+ * Vacation command tags
+ */
+
+/* Forward declarations */
+
+static bool
+cmd_vacation_validate_number_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool
+cmd_vacation_validate_string_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool
+cmd_vacation_validate_stringlist_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+static bool
+cmd_vacation_validate_mime_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+/* Argument objects */
+
+static const struct sieve_argument_def vacation_days_tag = {
+ .identifier = "days",
+ .validate = cmd_vacation_validate_number_tag
+};
+
+static const struct sieve_argument_def vacation_seconds_tag = {
+ .identifier = "seconds",
+ .validate = cmd_vacation_validate_number_tag
+};
+
+static const struct sieve_argument_def vacation_subject_tag = {
+ .identifier = "subject",
+ .validate = cmd_vacation_validate_string_tag
+};
+
+static const struct sieve_argument_def vacation_from_tag = {
+ .identifier = "from",
+ .validate = cmd_vacation_validate_string_tag
+};
+
+static const struct sieve_argument_def vacation_addresses_tag = {
+ .identifier = "addresses",
+ .validate = cmd_vacation_validate_stringlist_tag
+};
+
+static const struct sieve_argument_def vacation_mime_tag = {
+ .identifier = "mime",
+ .validate = cmd_vacation_validate_mime_tag
+};
+
+static const struct sieve_argument_def vacation_handle_tag = {
+ .identifier = "handle",
+ .validate = cmd_vacation_validate_string_tag
+};
+
+/* Codes for optional arguments */
+
+enum cmd_vacation_optional {
+ OPT_END,
+ OPT_SECONDS,
+ OPT_SUBJECT,
+ OPT_FROM,
+ OPT_ADDRESSES,
+ OPT_MIME
+};
+
+/*
+ * Vacation operation
+ */
+
+static bool
+ext_vacation_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+ext_vacation_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def vacation_operation = {
+ .mnemonic = "VACATION",
+ .ext_def = &vacation_extension,
+ .dump = ext_vacation_operation_dump,
+ .execute = ext_vacation_operation_execute
+};
+
+/*
+ * Vacation action
+ */
+
+/* Forward declarations */
+
+static int
+act_vacation_check_duplicate(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other);
+int act_vacation_check_conflict(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other);
+static void
+act_vacation_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv, bool *keep);
+static int
+act_vacation_commit(const struct sieve_action_exec_env *aenv, void *tr_context);
+
+/* Action object */
+
+const struct sieve_action_def act_vacation = {
+ .name = "vacation",
+ .flags = SIEVE_ACTFLAG_SENDS_RESPONSE,
+ .check_duplicate = act_vacation_check_duplicate,
+ .check_conflict = act_vacation_check_conflict,
+ .print = act_vacation_print,
+ .commit = act_vacation_commit
+};
+
+/* Action context information */
+
+struct act_vacation_context {
+ const char *reason;
+
+ sieve_number_t seconds;
+ const char *subject;
+ const char *handle;
+ bool mime;
+ const char *from;
+ const struct smtp_address *from_address;
+ const struct smtp_address *const *addresses;
+};
+
+/*
+ * Command validation context
+ */
+
+struct cmd_vacation_context_data {
+ string_t *from;
+ string_t *subject;
+
+ bool mime;
+
+ struct sieve_ast_argument *handle_arg;
+};
+
+/*
+ * Tag validation
+ */
+
+static bool
+cmd_vacation_validate_number_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ const struct sieve_extension *ext = sieve_argument_ext(*arg);
+ const struct ext_vacation_config *config =
+ (const struct ext_vacation_config *)ext->context;
+ struct sieve_ast_argument *tag = *arg;
+ sieve_number_t period, seconds;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :days number
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_NUMBER, FALSE))
+ return FALSE;
+
+ period = sieve_ast_argument_number(*arg);
+ if (sieve_argument_is(tag, vacation_days_tag))
+ seconds = period * (24*60*60);
+ else if (sieve_argument_is(tag, vacation_seconds_tag))
+ seconds = period;
+ else
+ i_unreached();
+
+ /* Enforce :seconds >= min_period */
+ if (seconds < config->min_period) {
+ seconds = config->min_period;
+
+ sieve_argument_validate_warning(
+ valdtr, *arg,
+ "specified :%s value '%llu' is under the minimum",
+ sieve_argument_identifier(tag),
+ (unsigned long long)period);
+ /* Enforce :days <= max_period */
+ } else if (config->max_period > 0 && seconds > config->max_period) {
+ seconds = config->max_period;
+
+ sieve_argument_validate_warning(
+ valdtr, *arg,
+ "specified :%s value '%llu' is over the maximum",
+ sieve_argument_identifier(tag),
+ (unsigned long long)period);
+ }
+
+ sieve_ast_argument_number_set(*arg, seconds);
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+static bool
+cmd_vacation_validate_string_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+ struct cmd_vacation_context_data *ctx_data =
+ (struct cmd_vacation_context_data *)cmd->data;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :subject string
+ * :from string
+ * :handle string
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_STRING, FALSE))
+ return FALSE;
+
+ if (sieve_argument_is(tag, vacation_from_tag)) {
+ if (sieve_argument_is_string_literal(*arg)) {
+ string_t *address = sieve_ast_argument_str(*arg);
+ const char *error;
+ bool result;
+
+ T_BEGIN {
+ result = sieve_address_validate_str(address,
+ &error);
+
+ if (!result) {
+ sieve_argument_validate_error(
+ valdtr, *arg,
+ "specified :from address '%s' is invalid for vacation action: %s",
+ str_sanitize(str_c(address), 128),
+ error);
+ }
+ } T_END;
+
+ if (!result)
+ return FALSE;
+ }
+
+ ctx_data->from = sieve_ast_argument_str(*arg);
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+ } else if (sieve_argument_is(tag, vacation_subject_tag)) {
+ ctx_data->subject = sieve_ast_argument_str(*arg);
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+ } else if (sieve_argument_is(tag, vacation_handle_tag)) {
+ ctx_data->handle_arg = *arg;
+
+ /* Detach optional argument (emitted as mandatory) */
+ *arg = sieve_ast_arguments_detach(*arg, 1);
+ }
+ return TRUE;
+}
+
+static bool
+cmd_vacation_validate_stringlist_tag(struct sieve_validator *valdtr,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *tag = *arg;
+
+ /* Detach the tag itself */
+ *arg = sieve_ast_arguments_detach(*arg,1);
+
+ /* Check syntax:
+ * :addresses string-list
+ */
+ if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
+ SAAT_STRING_LIST, FALSE))
+ return FALSE;
+
+ /* Skip parameter */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+static bool
+cmd_vacation_validate_mime_tag(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ struct cmd_vacation_context_data *ctx_data =
+ (struct cmd_vacation_context_data *)cmd->data;
+
+ ctx_data->mime = TRUE;
+
+ /* Skip tag */
+ *arg = sieve_ast_argument_next(*arg);
+
+ return TRUE;
+}
+
+/*
+ * Command registration
+ */
+
+static bool
+cmd_vacation_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &vacation_days_tag, OPT_SECONDS);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &vacation_subject_tag, OPT_SUBJECT);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &vacation_from_tag, OPT_FROM);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &vacation_addresses_tag, OPT_ADDRESSES);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &vacation_mime_tag, OPT_MIME);
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &vacation_handle_tag, 0);
+ return TRUE;
+}
+
+bool ext_vacation_register_seconds_tag(
+ struct sieve_validator *valdtr,
+ const struct sieve_extension *vacation_ext)
+{
+ sieve_validator_register_external_tag(
+ valdtr, vacation_command.identifier, vacation_ext,
+ &vacation_seconds_tag, OPT_SECONDS);
+
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool
+cmd_vacation_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED,
+ struct sieve_command *cmd)
+{
+ struct cmd_vacation_context_data *ctx_data;
+
+ /* Assign context */
+ ctx_data = p_new(sieve_command_pool(cmd),
+ struct cmd_vacation_context_data, 1);
+ cmd->data = ctx_data;
+
+ return TRUE;
+}
+
+static const char _handle_empty_subject[] = "<default-subject>";
+static const char _handle_empty_from[] = "<default-from>";
+static const char _handle_mime_enabled[] = "<MIME>";
+static const char _handle_mime_disabled[] = "<NO-MIME>";
+
+static bool
+cmd_vacation_validate(struct sieve_validator *valdtr,
+ struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg = cmd->first_positional;
+ struct cmd_vacation_context_data *ctx_data =
+ (struct cmd_vacation_context_data *)cmd->data;
+
+ if (!sieve_validate_positional_argument(valdtr, cmd, arg, "reason", 1,
+ SAAT_STRING))
+ return FALSE;
+
+ if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE))
+ return FALSE;
+
+ /* Construct handle if not set explicitly */
+ if (ctx_data->handle_arg == NULL) {
+ T_BEGIN {
+ string_t *handle;
+ string_t *reason = sieve_ast_argument_str(arg);
+ unsigned int size = str_len(reason);
+
+ /* Precalculate the size of it all */
+ size += (ctx_data->subject == NULL ?
+ sizeof(_handle_empty_subject) - 1 :
+ str_len(ctx_data->subject));
+ size += (ctx_data->from == NULL ?
+ sizeof(_handle_empty_from) - 1 :
+ str_len(ctx_data->from));
+ size += (ctx_data->mime ?
+ sizeof(_handle_mime_enabled) - 1 :
+ sizeof(_handle_mime_disabled) - 1);
+
+ /* Construct the string */
+ handle = t_str_new(size);
+ str_append_str(handle, reason);
+
+ if (ctx_data->subject != NULL)
+ str_append_str(handle, ctx_data->subject);
+ else
+ str_append(handle, _handle_empty_subject);
+
+ if (ctx_data->from != NULL)
+ str_append_str(handle, ctx_data->from);
+ else
+ str_append(handle, _handle_empty_from);
+
+ str_append(handle, (ctx_data->mime ?
+ _handle_mime_enabled :
+ _handle_mime_disabled));
+
+ /* Create positional handle argument */
+ ctx_data->handle_arg =
+ sieve_ast_argument_string_create(
+ cmd->ast_node, handle,
+ sieve_ast_node_line(cmd->ast_node));
+ } T_END;
+
+ if (!sieve_validator_argument_activate(
+ valdtr, cmd, ctx_data->handle_arg, TRUE))
+ return FALSE;
+ } else {
+ /* Attach explicit handle argument as positional */
+ (void)sieve_ast_argument_attach(cmd->ast_node,
+ ctx_data->handle_arg);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+cmd_vacation_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &vacation_operation);
+
+ /* Generate arguments */
+ if (!sieve_generate_arguments(cgenv, cmd, NULL))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+ext_vacation_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "VACATION");
+ sieve_code_descend(denv);
+
+ /* Dump optional operands */
+
+ for (;;) {
+ int opt;
+ bool opok = TRUE;
+
+ if ((opt = sieve_opr_optional_dump(denv, address,
+ &opt_code)) < 0)
+ return FALSE;
+
+ if (opt == 0)
+ break;
+
+ switch (opt_code) {
+ case OPT_SECONDS:
+ opok = sieve_opr_number_dump(denv, address, "seconds");
+ break;
+ case OPT_SUBJECT:
+ opok = sieve_opr_string_dump(denv, address, "subject");
+ break;
+ case OPT_FROM:
+ opok = sieve_opr_string_dump(denv, address, "from");
+ break;
+ case OPT_ADDRESSES:
+ opok = sieve_opr_stringlist_dump(denv, address,
+ "addresses");
+ break;
+ case OPT_MIME:
+ sieve_code_dumpf(denv, "mime");
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (!opok)
+ return FALSE;
+ }
+
+ /* Dump reason and handle operands */
+ return (sieve_opr_string_dump(denv, address, "reason") &&
+ sieve_opr_string_dump(denv, address, "handle"));
+}
+
+/*
+ * Code execution
+ */
+
+static int
+ext_vacation_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ const struct ext_vacation_config *config =
+ (const struct ext_vacation_config *)this_ext->context;
+ struct sieve_side_effects_list *slist = NULL;
+ struct act_vacation_context *act;
+ pool_t pool;
+ int opt_code = 0;
+ sieve_number_t seconds = config->default_period;
+ bool mime = FALSE;
+ struct sieve_stringlist *addresses = NULL;
+ string_t *reason, *subject = NULL, *from = NULL, *handle = NULL;
+ const struct smtp_address *from_address = NULL;
+ int ret;
+
+ /*
+ * Read code
+ */
+
+ /* Optional operands */
+
+ for (;;) {
+ int opt;
+
+ if ((opt = sieve_opr_optional_read(renv, address,
+ &opt_code)) < 0)
+ return SIEVE_EXEC_BIN_CORRUPT;
+
+ if (opt == 0)
+ break;
+
+ switch (opt_code) {
+ case OPT_SECONDS:
+ ret = sieve_opr_number_read(renv, address, "seconds",
+ &seconds);
+ break;
+ case OPT_SUBJECT:
+ ret = sieve_opr_string_read(renv, address, "subject",
+ &subject);
+ break;
+ case OPT_FROM:
+ ret = sieve_opr_string_read(renv, address, "from",
+ &from);
+ break;
+ case OPT_ADDRESSES:
+ ret = sieve_opr_stringlist_read(renv, address,
+ "addresses",
+ &addresses);
+ break;
+ case OPT_MIME:
+ mime = TRUE;
+ ret = SIEVE_EXEC_OK;
+ break;
+ default:
+ sieve_runtime_trace_error(
+ renv, "unknown optional operand");
+ ret = SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if (ret <= 0)
+ return ret;
+ }
+
+ /* Fixed operands */
+
+ if ((ret =sieve_opr_string_read(renv, address,
+ "reason", &reason)) <= 0 ||
+ (ret = sieve_opr_string_read(renv, address,
+ "handle", &handle)) <= 0)
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ /* Trace */
+
+ if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) {
+ sieve_runtime_trace(renv, 0, "vacation action");
+ sieve_runtime_trace_descend(renv);
+ sieve_runtime_trace(renv, 0, "auto-reply with message `%s'",
+ str_sanitize(str_c(reason), 80));
+ }
+
+ /* Parse :from address */
+ if (from != NULL) {
+ const char *error;
+
+ from_address = sieve_address_parse_str(from, &error);
+ if (from_address == NULL) {
+ sieve_runtime_error(
+ renv, NULL,
+ "specified :from address '%s' is invalid for vacation action: %s",
+ str_sanitize(str_c(from), 128), error);
+ }
+ }
+
+ /* Add vacation action to the result */
+
+ pool = sieve_result_pool(renv->result);
+ act = p_new(pool, struct act_vacation_context, 1);
+ act->reason = p_strdup(pool, str_c(reason));
+ act->handle = p_strdup(pool, str_c(handle));
+ act->seconds = seconds;
+ act->mime = mime;
+ if (subject != NULL)
+ act->subject = p_strdup(pool, str_c(subject));
+ if (from != NULL) {
+ act->from = p_strdup(pool, str_c(from));
+ act->from_address = smtp_address_clone(pool, from_address);
+ }
+
+ /* Normalize all addresses */
+ if (addresses != NULL) {
+ ARRAY_TYPE(smtp_address_const) addrs;
+ string_t *raw_address;
+ int ret;
+
+ sieve_stringlist_reset(addresses);
+
+ p_array_init(&addrs, pool, 4);
+
+ raw_address = NULL;
+ while ((ret = sieve_stringlist_next_item(addresses,
+ &raw_address)) > 0) {
+ const struct smtp_address *addr;
+ const char *error;
+
+ addr = sieve_address_parse_str(raw_address, &error);
+ if (addr != NULL) {
+ addr = smtp_address_clone(pool, addr);
+ array_append(&addrs, &addr, 1);
+ } else {
+ sieve_runtime_error(
+ renv, NULL,
+ "specified :addresses item '%s' is invalid: "
+ "%s for vacation action (ignored)",
+ str_sanitize(str_c(raw_address),128),
+ error);
+ }
+ }
+
+ if (ret < 0) {
+ sieve_runtime_trace_error(
+ renv, "invalid addresses stringlist");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ (void)array_append_space(&addrs);
+ act->addresses = array_idx(&addrs, 0);
+ }
+
+ if (sieve_result_add_action(renv, this_ext, "vacation", &act_vacation,
+ slist, (void *)act, 0, FALSE) < 0)
+ return SIEVE_EXEC_FAILURE;
+
+ return SIEVE_EXEC_OK;
+}
+
+/*
+ * Action
+ */
+
+/* Runtime verification */
+
+static int
+act_vacation_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other)
+{
+ if (!sieve_action_is_executed(act_other, renv->result)) {
+ sieve_runtime_error(
+ renv, act->location,
+ "duplicate vacation action not allowed "
+ "(previously triggered one was here: %s)",
+ act_other->location);
+ return -1;
+ }
+
+ /* Not an error if executed in preceeding script */
+ return 1;
+}
+
+int act_vacation_check_conflict(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other)
+{
+ if ((act_other->def->flags & SIEVE_ACTFLAG_SENDS_RESPONSE) > 0) {
+ if (!sieve_action_is_executed(act_other, renv->result)) {
+ sieve_runtime_error(
+ renv, act->location,
+ "vacation action conflicts with other action: "
+ "the %s action (%s) also sends a response back to the sender",
+ act_other->def->name, act_other->location);
+ return -1;
+ } else {
+ /* Not an error if executed in preceeding script */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Result printing */
+
+static void act_vacation_print(const struct sieve_action *action ATTR_UNUSED,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep ATTR_UNUSED)
+{
+ struct act_vacation_context *ctx =
+ (struct act_vacation_context *)action->context;
+
+ sieve_result_action_printf(rpenv, "send vacation message:");
+ sieve_result_printf(rpenv, " => seconds : %llu\n",
+ (unsigned long long)ctx->seconds);
+ if (ctx->subject != NULL) {
+ sieve_result_printf(rpenv, " => subject : %s\n",
+ ctx->subject);
+ }
+ if (ctx->from != NULL) {
+ sieve_result_printf(rpenv, " => from : %s\n",
+ ctx->from);
+ }
+ if (ctx->handle != NULL) {
+ sieve_result_printf(rpenv, " => handle : %s\n",
+ ctx->handle);
+ }
+ sieve_result_printf(rpenv, "\nSTART MESSAGE\n%s\nEND MESSAGE\n",
+ ctx->reason);
+}
+
+/* Result execution */
+
+/* Headers known to be associated with mailing lists
+ */
+static const char * const _list_headers[] = {
+ "list-id",
+ "list-owner",
+ "list-subscribe",
+ "list-post",
+ "list-unsubscribe",
+ "list-help",
+ "list-archive",
+ NULL
+};
+
+/* Headers that should be searched for the user's own mail address(es)
+ */
+
+static const char * const _my_address_headers[] = {
+ "to",
+ "cc",
+ "bcc",
+ "resent-to",
+ "resent-cc",
+ "resent-bcc",
+ NULL
+};
+
+/* Headers that should be searched for the full sender address
+ */
+
+static const char * const _sender_headers[] = {
+ "sender",
+ "resent-from",
+ "from",
+ NULL
+};
+
+static inline bool _is_system_address(const struct smtp_address *address)
+{
+ if (strcasecmp(address->localpart, "MAILER-DAEMON") == 0)
+ return TRUE;
+ if (strcasecmp(address->localpart, "LISTSERV") == 0)
+ return TRUE;
+ if (strcasecmp(address->localpart, "majordomo") == 0)
+ return TRUE;
+ if (strstr(address->localpart, "-request") != NULL)
+ return TRUE;
+ if (str_begins(address->localpart, "owner-"))
+ return TRUE;
+ return FALSE;
+}
+
+static bool
+_msg_address_equals(const struct message_address *addr1,
+ const struct smtp_address *addr2)
+{
+ struct smtp_address saddr;
+
+ i_assert(addr1->mailbox != NULL);
+ return (smtp_address_init_from_msg(&saddr, addr1) >= 0 &&
+ smtp_address_equals_icase(addr2, &saddr));
+}
+
+static inline bool
+_header_contains_my_address(const char *header_val,
+ const struct smtp_address *my_address)
+{
+ const struct message_address *msg_addr;
+
+ msg_addr = message_address_parse(pool_datastack_create(),
+ (const unsigned char *)header_val,
+ strlen(header_val), 256, 0);
+ while (msg_addr != NULL) {
+ if (msg_addr->domain != NULL) {
+ if (_msg_address_equals(msg_addr, my_address))
+ return TRUE;
+ }
+
+ msg_addr = msg_addr->next;
+ }
+
+ return FALSE;
+}
+
+static inline bool
+_contains_my_address(const char * const *headers,
+ const struct smtp_address *my_address)
+{
+ const char *const *hdsp = headers;
+
+ while (*hdsp != NULL) {
+ bool result;
+
+ T_BEGIN {
+ result = _header_contains_my_address(*hdsp, my_address);
+ } T_END;
+
+ if (result)
+ return TRUE;
+
+ hdsp++;
+ }
+
+ return FALSE;
+}
+
+static bool _contains_8bit(const char *text)
+{
+ const unsigned char *p = (const unsigned char *)text;
+
+ for (; *p != '\0'; p++) {
+ if ((*p & 0x80) != 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static bool
+_header_get_full_reply_recipient(const struct ext_vacation_config *config,
+ const struct smtp_address *smtp_to,
+ const char *header,
+ struct message_address *reply_to_r)
+{
+ const struct message_address *addr;
+
+ addr = message_address_parse(
+ pool_datastack_create(),
+ (const unsigned char *)header,
+ strlen(header), 256, 0);
+
+ for (; addr != NULL; addr = addr->next) {
+ bool matched = config->to_header_ignore_envelope;
+
+ if (addr->domain == NULL || addr->invalid_syntax)
+ continue;
+
+ if (!matched)
+ matched = _msg_address_equals(addr, smtp_to);
+
+ if (matched) {
+ *reply_to_r = *addr;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static int
+_get_full_reply_recipient(const struct sieve_action_exec_env *aenv,
+ const struct ext_vacation_config *config,
+ const struct smtp_address *smtp_to,
+ struct message_address *reply_to_r)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ const struct sieve_message_data *msgdata = eenv->msgdata;
+ const char *const *hdsp;
+ int ret;
+
+ hdsp = _sender_headers;
+ for (; *hdsp != NULL; hdsp++) {
+ const char *header;
+
+ if ((ret = mail_get_first_header(msgdata->mail, *hdsp,
+ &header)) < 0) {
+ return sieve_result_mail_error(
+ aenv, msgdata->mail,
+ "failed to read header field `%s'", *hdsp);
+ }
+ if (ret == 0 || header == NULL)
+ continue;
+
+ if (_header_get_full_reply_recipient(config, smtp_to,
+ header, reply_to_r))
+ return SIEVE_EXEC_OK;
+ }
+
+ reply_to_r->mailbox = smtp_to->localpart;
+ reply_to_r->domain = smtp_to->domain;
+ return SIEVE_EXEC_OK;
+}
+
+static const struct var_expand_table *
+_get_var_expand_table(const struct sieve_action_exec_env *aenv ATTR_UNUSED,
+ const char *subject)
+{
+ const struct var_expand_table stack_tab[] = {
+ { '$', subject, "subject" },
+ { '\0', NULL, NULL }
+ };
+
+ return p_memdup(unsafe_data_stack_pool, stack_tab, sizeof(stack_tab));
+}
+
+static int
+act_vacation_get_default_subject(const struct sieve_action_exec_env *aenv,
+ const struct ext_vacation_config *config,
+ const char **subject_r)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ const struct sieve_message_data *msgdata = eenv->msgdata;
+ const char *header, *error;
+ string_t *str;
+ const struct var_expand_table *tab;
+ int ret;
+
+ *subject_r = (config->default_subject == NULL ?
+ "Automated reply" : config->default_subject);
+ if ((ret = mail_get_first_header_utf8(msgdata->mail, "subject",
+ &header)) < 0) {
+ return sieve_result_mail_error(
+ aenv, msgdata->mail,
+ "failed to read header field `subject'");
+ }
+ if (ret == 0)
+ return SIEVE_EXEC_OK;
+ if (config->default_subject_template == NULL) {
+ *subject_r = t_strconcat("Auto: ", header, NULL);
+ return SIEVE_EXEC_OK;
+ }
+
+ str = t_str_new(256);
+ tab = _get_var_expand_table(aenv, header);
+ if (var_expand(str, config->default_subject_template,
+ tab, &error) <= 0) {
+ i_error("Failed to expand deliver_log_format=%s: %s",
+ config->default_subject_template, error);
+ *subject_r = t_strconcat("Auto: ", header, NULL);
+ return SIEVE_EXEC_OK;
+ }
+
+ *subject_r = str_c(str);
+ return SIEVE_EXEC_OK;
+}
+
+static int
+act_vacation_send(const struct sieve_action_exec_env *aenv,
+ const struct ext_vacation_config *config,
+ struct act_vacation_context *ctx,
+ const struct smtp_address *smtp_to,
+ const struct smtp_address *smtp_from,
+ const struct message_address *reply_from)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ const struct sieve_message_data *msgdata = eenv->msgdata;
+ const struct sieve_script_env *senv = eenv->scriptenv;
+ struct sieve_smtp_context *sctx;
+ struct ostream *output;
+ string_t *msg;
+ struct message_address reply_to;
+ const char *header, *outmsgid, *subject, *error;
+ int ret;
+
+ /* Check smpt functions just to be sure */
+
+ if (!sieve_smtp_available(senv)) {
+ sieve_result_global_warning(
+ aenv, "vacation action has no means to send mail");
+ return SIEVE_EXEC_OK;
+ }
+
+ /* Make sure we have a subject for our reply */
+
+ if (ctx->subject == NULL || *(ctx->subject) == '\0') {
+ if ((ret = act_vacation_get_default_subject(aenv, config,
+ &subject)) <= 0)
+ return ret;
+ } else {
+ subject = ctx->subject;
+ }
+
+ subject = str_sanitize_utf8(subject, config->max_subject_codepoints);
+
+ /* Obtain full To address for reply */
+
+ i_zero(&reply_to);
+ reply_to.mailbox = smtp_to->localpart;
+ reply_to.domain = smtp_to->domain;
+ if ((ret = _get_full_reply_recipient(aenv, config, smtp_to,
+ &reply_to)) <= 0)
+ return ret;
+
+ /* Open smtp session */
+
+ sctx = sieve_smtp_start_single(senv, smtp_to, smtp_from, &output);
+
+ outmsgid = sieve_message_get_new_id(eenv->svinst);
+
+ /* Produce a proper reply */
+
+ msg = t_str_new(512);
+ rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION);
+ rfc2822_header_write(msg, "Message-ID", outmsgid);
+ rfc2822_header_write(msg, "Date", message_date_create(ioloop_time));
+
+ if (ctx->from != NULL && *(ctx->from) != '\0') {
+ rfc2822_header_write_address(msg, "From", ctx->from);
+ } else {
+ if (reply_from == NULL || reply_from->mailbox == NULL ||
+ *reply_from->mailbox == '\0')
+ reply_from = sieve_get_postmaster(senv);
+ rfc2822_header_write(
+ msg, "From",
+ message_address_first_to_string(reply_from));
+ }
+
+ rfc2822_header_write(msg, "To",
+ message_address_first_to_string(&reply_to));
+
+ if (_contains_8bit(subject))
+ rfc2822_header_utf8_printf(msg, "Subject", "%s", subject);
+ else
+ rfc2822_header_printf(msg, "Subject", "%s", subject);
+
+ /* Compose proper in-reply-to and references headers */
+
+ if ((ret = mail_get_first_header(msgdata->mail, "references",
+ &header)) < 0) {
+ sieve_smtp_abort(sctx);
+ return sieve_result_mail_error(
+ aenv, msgdata->mail,
+ "failed to read header field `references'");
+ }
+
+ if (msgdata->id != NULL) {
+ rfc2822_header_write(msg, "In-Reply-To", msgdata->id);
+
+ if (ret > 0 && header != NULL) {
+ rfc2822_header_write(
+ msg, "References",
+ t_strconcat(header, " ", msgdata->id, NULL));
+ } else {
+ rfc2822_header_write(msg, "References", msgdata->id);
+ }
+ } else if (ret > 0 && header != NULL) {
+ rfc2822_header_write(msg, "References", header);
+ }
+
+ rfc2822_header_write(msg, "Auto-Submitted", "auto-replied (vacation)");
+ rfc2822_header_write(msg, "Precedence", "bulk");
+
+ /* Prevent older Microsoft products from replying to this message */
+ rfc2822_header_write(msg, "X-Auto-Response-Suppress", "All");
+
+ rfc2822_header_write(msg, "MIME-Version", "1.0");
+
+ if (!ctx->mime) {
+ rfc2822_header_write(msg, "Content-Type",
+ "text/plain; charset=utf-8");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit");
+ str_append(msg, "\r\n");
+ }
+
+ str_printfa(msg, "%s\r\n", ctx->reason);
+ o_stream_nsend(output, str_data(msg), str_len(msg));
+
+ /* Close smtp session */
+ if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) {
+ if (ret < 0) {
+ sieve_result_global_error(
+ aenv, "failed to send vacation response to %s: "
+ "<%s> (temporary error)",
+ smtp_address_encode(smtp_to),
+ str_sanitize(error, 512));
+ } else {
+ sieve_result_global_log_error(
+ aenv, "failed to send vacation response to %s: "
+ "<%s> (permanent error)",
+ smtp_address_encode(smtp_to),
+ str_sanitize(error, 512));
+ }
+ /* This error will be ignored in the end */
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ eenv->exec_status->significant_action_executed = TRUE;
+ return SIEVE_EXEC_OK;
+}
+
+static void
+act_vacation_hash(struct act_vacation_context *vctx, const char *sender,
+ unsigned char hash_r[])
+{
+ const char *rpath = t_str_lcase(sender);
+ struct md5_context ctx;
+
+ md5_init(&ctx);
+ md5_update(&ctx, rpath, strlen(rpath));
+
+ md5_update(&ctx, vctx->handle, strlen(vctx->handle));
+
+ md5_final(&ctx, hash_r);
+}
+
+static int
+act_vacation_commit(const struct sieve_action_exec_env *aenv,
+ void *tr_context ATTR_UNUSED)
+{
+ const struct sieve_action *action = aenv->action;
+ const struct sieve_extension *ext = action->ext;
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_instance *svinst = eenv->svinst;
+ const struct ext_vacation_config *config =
+ (const struct ext_vacation_config *)ext->context;
+ struct act_vacation_context *ctx =
+ (struct act_vacation_context *)action->context;
+ unsigned char dupl_hash[MD5_RESULTLEN];
+ struct mail *mail = sieve_message_get_mail(aenv->msgctx);
+ const struct smtp_address *sender, *recipient;
+ const struct smtp_address *orig_recipient, *user_email;
+ const struct smtp_address *smtp_from;
+ struct message_address reply_from;
+ const char *const *hdsp, *const *headers;
+ int ret;
+
+ if ((eenv->flags & SIEVE_EXECUTE_FLAG_SKIP_RESPONSES) != 0) {
+ sieve_result_global_log(
+ aenv, "not sending vacation reply (skipped)");
+ return SIEVE_EXEC_OK;
+ }
+
+ sender = sieve_message_get_sender(aenv->msgctx);
+ recipient = sieve_message_get_final_recipient(aenv->msgctx);
+
+ i_zero(&reply_from);
+ smtp_from = orig_recipient = user_email = NULL;
+
+ /* Is the recipient unset?
+ */
+ if (smtp_address_isnull(recipient)) {
+ sieve_result_global_warning(
+ aenv, "vacation action aborted: "
+ "envelope recipient is <>");
+ return SIEVE_EXEC_OK;
+ }
+
+ /* Is the return path unset ?
+ */
+ if (smtp_address_isnull(sender)) {
+ sieve_result_global_log(aenv, "discarded vacation reply to <>");
+ return SIEVE_EXEC_OK;
+ }
+
+ /* Are we perhaps trying to respond to ourselves ?
+ */
+ if (smtp_address_equals_icase(sender, recipient)) {
+ sieve_result_global_log(
+ aenv, "discarded vacation reply to own address <%s>",
+ smtp_address_encode(sender));
+ return SIEVE_EXEC_OK;
+ }
+
+ /* Are we perhaps trying to respond to one of our alternative :addresses?
+ */
+ if (ctx->addresses != NULL) {
+ const struct smtp_address * const *alt_address;
+
+ alt_address = ctx->addresses;
+ while (*alt_address != NULL) {
+ if (smtp_address_equals_icase(sender, *alt_address)) {
+ sieve_result_global_log(
+ aenv,
+ "discarded vacation reply to own address <%s> "
+ "(as specified using :addresses argument)",
+ smtp_address_encode(sender));
+ return SIEVE_EXEC_OK;
+ }
+ alt_address++;
+ }
+ }
+
+ /* Did whe respond to this user before? */
+ if (sieve_action_duplicate_check_available(aenv)) {
+ bool duplicate;
+
+ act_vacation_hash(ctx, smtp_address_encode(sender), dupl_hash);
+
+ ret = sieve_action_duplicate_check(aenv, dupl_hash,
+ sizeof(dupl_hash),
+ &duplicate);
+ if (ret < SIEVE_EXEC_OK) {
+ sieve_result_critical(
+ aenv, "failed to check for duplicate vacation response",
+ "failed to check for duplicate vacation response%s",
+ (ret == SIEVE_EXEC_TEMP_FAILURE ?
+ " (temporaty failure)" : ""));
+ return ret;
+ }
+ if (duplicate) {
+ sieve_result_global_log(
+ aenv,
+ "discarded duplicate vacation response to <%s>",
+ smtp_address_encode(sender));
+ return SIEVE_EXEC_OK;
+ }
+ }
+
+ /* Are we trying to respond to a mailing list ? */
+ hdsp = _list_headers;
+ while (*hdsp != NULL) {
+ if ((ret = mail_get_headers(mail, *hdsp, &headers)) < 0) {
+ return sieve_result_mail_error(
+ aenv, mail,
+ "failed to read header field `%s'", *hdsp);
+ }
+
+ if (ret > 0 && headers[0] != NULL) {
+ /* Yes, bail out */
+ sieve_result_global_log(
+ aenv, "discarding vacation response "
+ "to mailinglist recipient <%s>",
+ smtp_address_encode(sender));
+ return SIEVE_EXEC_OK;
+ }
+ hdsp++;
+ }
+
+ /* Is the message that we are replying to an automatic reply ? */
+ if ((ret = mail_get_headers(mail, "auto-submitted", &headers)) < 0) {
+ return sieve_result_mail_error(
+ aenv, mail,
+ "failed to read header field `auto-submitted'");
+ }
+ /* Theoretically multiple headers could exist, so lets make sure */
+ if (ret > 0) {
+ hdsp = headers;
+ while (*hdsp != NULL) {
+ if (strcasecmp(*hdsp, "no") != 0) {
+ sieve_result_global_log(
+ aenv, "discarding vacation response "
+ "to auto-submitted message from <%s>",
+ smtp_address_encode(sender));
+ return SIEVE_EXEC_OK;
+ }
+ hdsp++;
+ }
+ }
+
+ /* Check for the (non-standard) precedence header */
+ if ((ret = mail_get_headers(mail, "precedence", &headers)) < 0) {
+ return sieve_result_mail_error(
+ aenv, mail, "failed to read header field `precedence'");
+ }
+ /* Theoretically multiple headers could exist, so lets make sure */
+ if (ret > 0) {
+ hdsp = headers;
+ while (*hdsp != NULL) {
+ if (strcasecmp(*hdsp, "junk") == 0 ||
+ strcasecmp(*hdsp, "bulk") == 0 ||
+ strcasecmp(*hdsp, "list") == 0) {
+ sieve_result_global_log(
+ aenv, "discarding vacation response "
+ "to precedence=%s message from <%s>",
+ *hdsp, smtp_address_encode(sender));
+ return SIEVE_EXEC_OK;
+ }
+ hdsp++;
+ }
+ }
+
+ /* Check for the (non-standard) Microsoft X-Auto-Response-Suppress header */
+ if ((ret = mail_get_headers(mail, "x-auto-response-suppress",
+ &headers)) < 0) {
+ return sieve_result_mail_error(
+ aenv, mail,
+ "failed to read header field `x-auto-response-suppress'");
+ }
+ /* Theoretically multiple headers could exist, so lets make sure */
+ if (ret > 0) {
+ hdsp = headers;
+ while (*hdsp != NULL) {
+ const char *const *flags = t_strsplit(*hdsp, ",");
+
+ while (*flags != NULL) {
+ const char *flag = t_str_trim(*flags, " \t");
+
+ if (strcasecmp(flag, "All") == 0 ||
+ strcasecmp(flag, "OOF") == 0) {
+ sieve_result_global_log(
+ aenv, "discarding vacation response to message from <%s> "
+ "(`%s' flag found in x-auto-response-suppress header)",
+ smtp_address_encode(sender), flag);
+ return SIEVE_EXEC_OK;
+ }
+ flags++;
+ }
+ hdsp++;
+ }
+ }
+
+ /* Do not reply to system addresses */
+ if (_is_system_address(sender)) {
+ sieve_result_global_log(
+ aenv, "not sending vacation response to system address <%s>",
+ smtp_address_encode(sender));
+ return SIEVE_EXEC_OK;
+ }
+
+ /* Fetch original recipient if necessary */
+ if (config->use_original_recipient)
+ orig_recipient = sieve_message_get_orig_recipient(aenv->msgctx);
+ /* Fetch explicitly configured user email address */
+ if (svinst->user_email != NULL)
+ user_email = svinst->user_email;
+
+ /* Is the original message directly addressed to the user or the addresses
+ * specified using the :addresses tag?
+ */
+ hdsp = _my_address_headers;
+ while (*hdsp != NULL) {
+ if ((ret = mail_get_headers(mail, *hdsp, &headers)) < 0) {
+ return sieve_result_mail_error(
+ aenv, mail, "failed to read header field `%s'",
+ *hdsp);
+ }
+ if (ret > 0 && headers[0] != NULL) {
+ /* Final recipient directly listed in headers? */
+ if (_contains_my_address(headers, recipient)) {
+ smtp_from = recipient;
+ message_address_init_from_smtp(
+ &reply_from, NULL, recipient);
+ break;
+ }
+
+ /* Original recipient directly listed in headers? */
+ if (!smtp_address_isnull(orig_recipient) &&
+ _contains_my_address(headers, orig_recipient)) {
+ smtp_from = orig_recipient;
+ message_address_init_from_smtp(
+ &reply_from, NULL, orig_recipient);
+ break;
+ }
+
+ /* User-provided :addresses listed in headers? */
+ if (ctx->addresses != NULL) {
+ bool found = FALSE;
+ const struct smtp_address * const *my_address;
+
+ my_address = ctx->addresses;
+ while (!found && *my_address != NULL) {
+ if ((found = _contains_my_address(headers, *my_address))) {
+ /* Avoid letting user determine SMTP sender directly */
+ smtp_from = (orig_recipient == NULL ?
+ recipient : orig_recipient);
+ message_address_init_from_smtp(
+ &reply_from, NULL, *my_address);
+ }
+ my_address++;
+ }
+
+ if (found) break;
+ }
+
+ /* Explicitly-configured user email address directly listed in
+ headers? */
+ if (user_email != NULL &&
+ _contains_my_address(headers, user_email)) {
+ smtp_from = user_email;
+ message_address_init_from_smtp(
+ &reply_from, NULL, smtp_from);
+ break;
+ }
+ }
+ hdsp++;
+ }
+
+ /* My address not found in the headers; we got an implicit delivery */
+ if (*hdsp == NULL) {
+ if (config->dont_check_recipient) {
+ /* Send reply from envelope recipient address */
+ smtp_from = (orig_recipient == NULL ?
+ recipient : orig_recipient);
+ if (user_email == NULL)
+ user_email = sieve_get_user_email(svinst);
+ message_address_init_from_smtp(&reply_from,
+ NULL, user_email);
+ } else {
+ const char *orig_rcpt_str = "", *user_email_str = "";
+
+ /* Bail out */
+ if (config->use_original_recipient) {
+ orig_rcpt_str =
+ t_strdup_printf("original-recipient=<%s>, ",
+ (orig_recipient == NULL ? "UNAVAILABLE" :
+ smtp_address_encode(orig_recipient)));
+ }
+
+ if (user_email != NULL) {
+ user_email_str = t_strdup_printf(
+ "user-email=<%s>, ",
+ smtp_address_encode(user_email));
+ }
+
+ sieve_result_global_log(
+ aenv, "discarding vacation response for implicitly delivered message; "
+ "no known (envelope) recipient address found in message headers "
+ "(recipient=<%s>, %s%sand%s additional `:addresses' are specified)",
+ smtp_address_encode(recipient),
+ orig_rcpt_str, user_email_str,
+ (ctx->addresses == NULL || *ctx->addresses == NULL ?
+ " no" : ""));
+ return SIEVE_EXEC_OK;
+ }
+ }
+
+ /* Send the message */
+
+ T_BEGIN {
+ ret = act_vacation_send(
+ aenv, config, ctx, sender,
+ (config->send_from_recipient ? smtp_from : NULL),
+ &reply_from);
+ } T_END;
+
+ if (ret == SIEVE_EXEC_OK) {
+ sieve_number_t seconds;
+
+ eenv->exec_status->significant_action_executed = TRUE;
+
+ struct event_passthrough *e =
+ sieve_action_create_finish_event(aenv);
+
+ sieve_result_event_log(aenv, e->event(),
+ "sent vacation response to <%s>",
+ smtp_address_encode(sender));
+
+ /* Check period limits once more */
+ seconds = ctx->seconds;
+ if (seconds < config->min_period)
+ seconds = config->min_period;
+ else if (config->max_period > 0 && seconds > config->max_period)
+ seconds = config->max_period;
+
+ /* Mark as replied */
+ if (seconds > 0) {
+ sieve_action_duplicate_mark(aenv, dupl_hash,
+ sizeof(dupl_hash),
+ ioloop_time + seconds);
+ }
+ }
+
+ if (ret == SIEVE_EXEC_TEMP_FAILURE)
+ return SIEVE_EXEC_TEMP_FAILURE;
+
+ /* Ignore all other errors */
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.c b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.c
new file mode 100644
index 0000000..97be3a5
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.c
@@ -0,0 +1,114 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+#include "sieve-settings.h"
+#include "sieve-extensions.h"
+
+#include "ext-vacation-common.h"
+
+bool ext_vacation_load
+(const struct sieve_extension *ext, void **context)
+{
+ struct sieve_instance *svinst = ext->svinst;
+ struct ext_vacation_config *config;
+ sieve_number_t min_period, max_period, default_period;
+ bool use_original_recipient, dont_check_recipient, send_from_recipient,
+ to_header_ignore_envelope;
+ unsigned long long max_subject_codepoints;
+ const char *default_subject, *default_subject_template;
+
+ if ( *context != NULL ) {
+ ext_vacation_unload(ext);
+ }
+
+ if ( !sieve_setting_get_duration_value
+ (svinst, "sieve_vacation_min_period", &min_period) ) {
+ min_period = EXT_VACATION_DEFAULT_MIN_PERIOD;
+ }
+
+ if ( !sieve_setting_get_duration_value
+ (svinst, "sieve_vacation_max_period", &max_period) ) {
+ max_period = EXT_VACATION_DEFAULT_MAX_PERIOD;
+ }
+
+ if ( !sieve_setting_get_duration_value
+ (svinst, "sieve_vacation_default_period", &default_period) ) {
+ default_period = EXT_VACATION_DEFAULT_PERIOD;
+ }
+
+ if ( max_period > 0
+ && (min_period > max_period || default_period < min_period
+ || default_period > max_period) ) {
+ min_period = EXT_VACATION_DEFAULT_MIN_PERIOD;
+ max_period = EXT_VACATION_DEFAULT_MAX_PERIOD;
+ default_period = EXT_VACATION_DEFAULT_PERIOD;
+
+ e_warning(svinst->event, "vacation extension: "
+ "invalid settings: violated "
+ "sieve_vacation_min_period < "
+ "sieve_vacation_default_period < "
+ "sieve_vacation_max_period");
+ }
+
+ default_subject = sieve_setting_get(
+ svinst, "sieve_vacation_default_subject");
+ default_subject_template = sieve_setting_get(
+ svinst, "sieve_vacation_default_subject_template");
+
+ if ( !sieve_setting_get_uint_value
+ (svinst, "sieve_vacation_max_subject_codepoints", &max_subject_codepoints) ) {
+ max_subject_codepoints = EXT_VACATION_DEFAULT_MAX_SUBJECT_CODEPOINTS;
+ }
+
+ if ( !sieve_setting_get_bool_value
+ (svinst, "sieve_vacation_use_original_recipient", &use_original_recipient) ) {
+ use_original_recipient = FALSE;
+ }
+
+ if ( !sieve_setting_get_bool_value
+ (svinst, "sieve_vacation_dont_check_recipient", &dont_check_recipient) ) {
+ dont_check_recipient = FALSE;
+ }
+
+ if ( !sieve_setting_get_bool_value
+ (svinst, "sieve_vacation_send_from_recipient", &send_from_recipient) ) {
+ send_from_recipient = FALSE;
+ }
+
+ if ( !sieve_setting_get_bool_value(svinst,
+ "sieve_vacation_to_header_ignore_envelope",
+ &to_header_ignore_envelope) ) {
+ to_header_ignore_envelope = FALSE;
+ }
+
+ config = i_new(struct ext_vacation_config, 1);
+ config->min_period = min_period;
+ config->max_period = max_period;
+ config->default_period = default_period;
+ config->max_subject_codepoints = max_subject_codepoints;
+ config->default_subject = i_strdup_empty(default_subject);
+ config->default_subject_template = i_strdup_empty(default_subject_template);
+ config->use_original_recipient = use_original_recipient;
+ config->dont_check_recipient = dont_check_recipient;
+ config->send_from_recipient = send_from_recipient;
+ config->to_header_ignore_envelope = to_header_ignore_envelope;
+
+ *context = (void *) config;
+
+ return TRUE;
+}
+
+void ext_vacation_unload
+(const struct sieve_extension *ext)
+{
+ struct ext_vacation_config *config =
+ (struct ext_vacation_config *) ext->context;
+
+ i_free(config->default_subject);
+ i_free(config->default_subject_template);
+ i_free(config);
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.h b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.h
new file mode 100644
index 0000000..3a38cf6
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-common.h
@@ -0,0 +1,60 @@
+#ifndef EXT_VACATION_COMMON_H
+#define EXT_VACATION_COMMON_H
+
+#include "sieve-common.h"
+
+/*
+ * Extension configuration
+ */
+
+#define EXT_VACATION_DEFAULT_PERIOD (7*24*60*60)
+#define EXT_VACATION_DEFAULT_MIN_PERIOD (24*60*60)
+#define EXT_VACATION_DEFAULT_MAX_PERIOD 0
+#define EXT_VACATION_DEFAULT_MAX_SUBJECT_CODEPOINTS 256
+
+struct ext_vacation_config {
+ unsigned int min_period;
+ unsigned int max_period;
+ unsigned int default_period;
+ unsigned long long max_subject_codepoints;
+ char *default_subject;
+ char *default_subject_template;
+ bool use_original_recipient;
+ bool dont_check_recipient;
+ bool send_from_recipient;
+ bool to_header_ignore_envelope;
+};
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def vacation_command;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def vacation_operation;
+
+/*
+ * Extensions
+ */
+
+/* Vacation */
+
+extern const struct sieve_extension_def vacation_extension;
+
+bool ext_vacation_load
+ (const struct sieve_extension *ext, void **context);
+void ext_vacation_unload
+ (const struct sieve_extension *ext);
+
+/* Vacation-seconds */
+
+extern const struct sieve_extension_def vacation_seconds_extension;
+
+bool ext_vacation_register_seconds_tag
+ (struct sieve_validator *valdtr, const struct sieve_extension *vacation_ext);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c
new file mode 100644
index 0000000..41c0f06
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation-seconds.c
@@ -0,0 +1,66 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension vacation-seconds
+ * --------------------------
+ *
+ * Authors: Stephan Bosch <stephan@rename-it.nl>
+ * Specification: RFC 6131
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+
+#include "sieve-common.h"
+
+#include "sieve-extensions.h"
+#include "sieve-validator.h"
+
+#include "ext-vacation-common.h"
+
+/*
+ * Extension
+ */
+
+bool ext_vacation_seconds_load
+ (const struct sieve_extension *ext, void **context);
+static bool ext_vacation_seconds_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def vacation_seconds_extension = {
+ .name = "vacation-seconds",
+ .load = ext_vacation_seconds_load,
+ .validator_load = ext_vacation_seconds_validator_load,
+};
+
+bool ext_vacation_seconds_load
+(const struct sieve_extension *ext, void **context)
+{
+ if ( *context == NULL ) {
+ /* Make sure vacation extension is registered */
+ *context = (void *)
+ sieve_extension_require(ext->svinst, &vacation_extension, TRUE);
+ }
+
+ return TRUE;
+}
+
+static bool ext_vacation_seconds_validator_load
+(const struct sieve_extension *ext ATTR_UNUSED, struct sieve_validator *valdtr)
+{
+ const struct sieve_extension *vacation_ext;
+
+ /* Load vacation extension implicitly */
+
+ vacation_ext = sieve_validator_extension_load_implicit
+ (valdtr, vacation_extension.name);
+
+ if ( vacation_ext == NULL )
+ return FALSE;
+
+ /* Add seconds tag to vacation command */
+
+ return ext_vacation_register_seconds_tag(valdtr, vacation_ext);
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation.c b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation.c
new file mode 100644
index 0000000..8d3d9a7
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vacation/ext-vacation.c
@@ -0,0 +1,131 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension vacation
+ * ------------------
+ *
+ * Authors: Stephan Bosch <stephan@rename-it.nl>
+ * Specification: RFC 5230
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+
+#include "sieve-common.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-vacation-common.h"
+
+/*
+ * Extension
+ */
+
+static bool
+ext_vacation_validator_load(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr);
+static bool
+ext_vacation_interpreter_load(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+static bool
+ext_vacation_validator_validate(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context,
+ struct sieve_ast_argument *require_arg,
+ bool required);
+static int
+ext_vacation_interpreter_run(const struct sieve_extension *this_ext,
+ const struct sieve_runtime_env *renv,
+ void *context, bool deferred);
+
+const struct sieve_extension_def vacation_extension = {
+ .name = "vacation",
+ .load = ext_vacation_load,
+ .unload = ext_vacation_unload,
+ .validator_load = ext_vacation_validator_load,
+ .interpreter_load = ext_vacation_interpreter_load,
+ SIEVE_EXT_DEFINE_OPERATION(vacation_operation)
+};
+const struct sieve_validator_extension
+vacation_validator_extension = {
+ .ext = &vacation_extension,
+ .validate = ext_vacation_validator_validate
+};
+const struct sieve_interpreter_extension
+vacation_interpreter_extension = {
+ .ext_def = &vacation_extension,
+ .run = ext_vacation_interpreter_run
+};
+
+static bool
+ext_vacation_validator_load(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr)
+{
+ /* Register new command */
+ sieve_validator_register_command(valdtr, ext, &vacation_command);
+
+ sieve_validator_extension_register(valdtr, ext,
+ &vacation_validator_extension, NULL);
+ return TRUE;
+}
+
+static bool
+ext_vacation_interpreter_load(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ sieve_interpreter_extension_register(
+ renv->interp, ext, &vacation_interpreter_extension, NULL);
+ return TRUE;
+}
+
+static bool
+ext_vacation_validator_validate(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr,
+ void *context ATTR_UNUSED,
+ struct sieve_ast_argument *require_arg,
+ bool required)
+{
+ if (required) {
+ enum sieve_compile_flags flags =
+ sieve_validator_compile_flags(valdtr);
+
+ if ((flags & SIEVE_COMPILE_FLAG_NO_ENVELOPE) != 0) {
+ sieve_argument_validate_error(
+ valdtr, require_arg,
+ "the %s extension cannot be used in this context "
+ "(needs access to message envelope)",
+ sieve_extension_name(ext));
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static int
+ext_vacation_interpreter_run(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ void *context ATTR_UNUSED, bool deferred)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+
+ if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) {
+ if (!deferred) {
+ sieve_runtime_error(
+ renv, NULL,
+ "the %s extension cannot be used in this context "
+ "(needs access to message envelope)",
+ sieve_extension_name(ext));
+ }
+ return SIEVE_EXEC_FAILURE;
+ }
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/Makefile.am b/pigeonhole/src/lib-sieve/plugins/variables/Makefile.am
new file mode 100644
index 0000000..354bad2
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/Makefile.am
@@ -0,0 +1,41 @@
+noinst_LTLIBRARIES = libsieve_ext_variables.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+cmds = \
+ cmd-set.c
+
+tsts = \
+ tst-string.c
+
+libsieve_ext_variables_la_SOURCES = \
+ ext-variables-common.c \
+ ext-variables-name.c \
+ ext-variables-namespaces.c \
+ ext-variables-arguments.c \
+ ext-variables-operands.c \
+ ext-variables-modifiers.c \
+ ext-variables-dump.c \
+ $(cmds) \
+ $(tsts) \
+ ext-variables.c
+
+public_headers = \
+ sieve-ext-variables.h
+
+headers = \
+ ext-variables-common.h \
+ ext-variables-limits.h \
+ ext-variables-name.h \
+ ext-variables-namespaces.h \
+ ext-variables-arguments.h \
+ ext-variables-operands.h \
+ ext-variables-modifiers.h \
+ ext-variables-dump.h
+
+pkginc_libdir=$(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
+
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/Makefile.in b/pigeonhole/src/lib-sieve/plugins/variables/Makefile.in
new file mode 100644
index 0000000..701d574
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/Makefile.in
@@ -0,0 +1,801 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/variables
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(pkginc_lib_HEADERS) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_variables_la_LIBADD =
+am__objects_1 = cmd-set.lo
+am__objects_2 = tst-string.lo
+am_libsieve_ext_variables_la_OBJECTS = ext-variables-common.lo \
+ ext-variables-name.lo ext-variables-namespaces.lo \
+ ext-variables-arguments.lo ext-variables-operands.lo \
+ ext-variables-modifiers.lo ext-variables-dump.lo \
+ $(am__objects_1) $(am__objects_2) ext-variables.lo
+libsieve_ext_variables_la_OBJECTS = \
+ $(am_libsieve_ext_variables_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-set.Plo \
+ ./$(DEPDIR)/ext-variables-arguments.Plo \
+ ./$(DEPDIR)/ext-variables-common.Plo \
+ ./$(DEPDIR)/ext-variables-dump.Plo \
+ ./$(DEPDIR)/ext-variables-modifiers.Plo \
+ ./$(DEPDIR)/ext-variables-name.Plo \
+ ./$(DEPDIR)/ext-variables-namespaces.Plo \
+ ./$(DEPDIR)/ext-variables-operands.Plo \
+ ./$(DEPDIR)/ext-variables.Plo ./$(DEPDIR)/tst-string.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_variables_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_variables_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(pkginc_libdir)"
+HEADERS = $(noinst_HEADERS) $(pkginc_lib_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_variables.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+cmds = \
+ cmd-set.c
+
+tsts = \
+ tst-string.c
+
+libsieve_ext_variables_la_SOURCES = \
+ ext-variables-common.c \
+ ext-variables-name.c \
+ ext-variables-namespaces.c \
+ ext-variables-arguments.c \
+ ext-variables-operands.c \
+ ext-variables-modifiers.c \
+ ext-variables-dump.c \
+ $(cmds) \
+ $(tsts) \
+ ext-variables.c
+
+public_headers = \
+ sieve-ext-variables.h
+
+headers = \
+ ext-variables-common.h \
+ ext-variables-limits.h \
+ ext-variables-name.h \
+ ext-variables-namespaces.h \
+ ext-variables-arguments.h \
+ ext-variables-operands.h \
+ ext-variables-modifiers.h \
+ ext-variables-dump.h
+
+pkginc_libdir = $(dovecot_pkgincludedir)/sieve
+pkginc_lib_HEADERS = $(public_headers)
+noinst_HEADERS = $(headers)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/variables/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/variables/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_variables.la: $(libsieve_ext_variables_la_OBJECTS) $(libsieve_ext_variables_la_DEPENDENCIES) $(EXTRA_libsieve_ext_variables_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_variables_la_OBJECTS) $(libsieve_ext_variables_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-set.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-arguments.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-dump.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-modifiers.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-name.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-namespaces.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables-operands.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-variables.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-string.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkginc_libHEADERS: $(pkginc_lib_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \
+ done
+
+uninstall-pkginc_libHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(pkginc_libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmd-set.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-arguments.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-common.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-dump.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-modifiers.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-name.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-namespaces.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-operands.Plo
+ -rm -f ./$(DEPDIR)/ext-variables.Plo
+ -rm -f ./$(DEPDIR)/tst-string.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pkginc_libHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmd-set.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-arguments.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-common.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-dump.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-modifiers.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-name.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-namespaces.Plo
+ -rm -f ./$(DEPDIR)/ext-variables-operands.Plo
+ -rm -f ./$(DEPDIR)/ext-variables.Plo
+ -rm -f ./$(DEPDIR)/tst-string.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pkginc_libHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-pkginc_libHEADERS install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-pkginc_libHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/cmd-set.c b/pigeonhole/src/lib-sieve/plugins/variables/cmd-set.c
new file mode 100644
index 0000000..d0fdc97
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/cmd-set.c
@@ -0,0 +1,235 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+
+#include "sieve-code.h"
+#include "sieve-ast.h"
+#include "sieve-commands.h"
+#include "sieve-binary.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-variables-common.h"
+
+/*
+ * Set command
+ *
+ * Syntax:
+ * set [MODIFIER] <name: string> <value: string>
+ */
+
+static bool cmd_set_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool cmd_set_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool cmd_set_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def cmd_set = {
+ .identifier = "set",
+ .type = SCT_COMMAND,
+ .positional_args = 2,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_set_registered,
+ .validate = cmd_set_validate,
+ .generate = cmd_set_generate,
+};
+
+/*
+ * Set operation
+ */
+
+static bool cmd_set_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_set_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def cmd_set_operation = {
+ .mnemonic = "SET",
+ .ext_def = &variables_extension,
+ .code = EXT_VARIABLES_OPERATION_SET,
+ .dump = cmd_set_operation_dump,
+ .execute = cmd_set_operation_execute
+};
+
+/*
+ * Compiler context
+ */
+
+struct cmd_set_context {
+ ARRAY_TYPE(sieve_variables_modifier) modifiers;
+};
+
+/* Command registration */
+
+static bool cmd_set_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_variables_modifiers_link_tag(valdtr, ext, cmd_reg);
+
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool cmd_set_validate(struct sieve_validator *valdtr,
+ struct sieve_command *cmd)
+{
+ const struct sieve_extension *this_ext = cmd->ext;
+ struct sieve_ast_argument *arg = cmd->first_positional;
+ pool_t pool = sieve_command_pool(cmd);
+ struct cmd_set_context *sctx;
+
+ /* Create command context */
+ sctx = p_new(pool, struct cmd_set_context, 1);
+ p_array_init(&sctx->modifiers, pool, 4);
+ cmd->data = (void *) sctx;
+
+ /* Validate modifiers */
+ if ( !sieve_variables_modifiers_validate
+ (valdtr, cmd, &sctx->modifiers) )
+ return FALSE;
+
+ /* Validate name argument */
+ if ( !sieve_validate_positional_argument
+ (valdtr, cmd, arg, "name", 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+ if ( !sieve_variable_argument_activate
+ (this_ext, this_ext, valdtr, cmd, arg, TRUE) ) {
+ return FALSE;
+ }
+ arg = sieve_ast_argument_next(arg);
+
+ /* Validate value argument */
+ if ( !sieve_validate_positional_argument
+ (valdtr, cmd, arg, "value", 2, SAAT_STRING) ) {
+ return FALSE;
+ }
+ return sieve_validator_argument_activate
+ (valdtr, cmd, arg, FALSE);
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_set_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ const struct sieve_extension *this_ext = cmd->ext;
+ struct sieve_binary_block *sblock = cgenv->sblock;
+ struct cmd_set_context *sctx = (struct cmd_set_context *) cmd->data;
+
+ sieve_operation_emit(sblock, this_ext, &cmd_set_operation);
+
+ /* Generate arguments */
+ if ( !sieve_generate_arguments(cgenv, cmd, NULL) )
+ return FALSE;
+
+ /* Generate modifiers */
+ if ( !sieve_variables_modifiers_generate
+ (cgenv, &sctx->modifiers) )
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool cmd_set_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ sieve_code_dumpf(denv, "SET");
+ sieve_code_descend(denv);
+
+ /* Print both variable name and string value */
+ if ( !sieve_opr_string_dump(denv, address, "variable") ||
+ !sieve_opr_string_dump(denv, address, "value") )
+ return FALSE;
+
+ return sieve_variables_modifiers_code_dump(denv, address);
+}
+
+/*
+ * Code execution
+ */
+
+static int cmd_set_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct sieve_variable_storage *storage;
+ ARRAY_TYPE(sieve_variables_modifier) modifiers;
+ unsigned int var_index;
+ string_t *value;
+ int ret = SIEVE_EXEC_OK;
+
+ /*
+ * Read the normal operands
+ */
+
+ if ( (ret=sieve_variable_operand_read
+ (renv, address, "variable", &storage, &var_index)) <= 0 )
+ return ret;
+
+ if ( (ret=sieve_opr_string_read(renv, address, "string", &value)) <= 0 )
+ return ret;
+
+ if ( (ret=sieve_variables_modifiers_code_read
+ (renv, this_ext, address, &modifiers)) <= 0 )
+ return ret;
+
+ /*
+ * Determine and assign the value
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "set command");
+ sieve_runtime_trace_descend(renv);
+
+ /* Apply modifiers */
+ if ( (ret=sieve_variables_modifiers_apply
+ (renv, this_ext, &modifiers, &value)) <= 0 )
+ return ret;
+
+ /* Actually assign the value if all is well */
+ i_assert ( value != NULL );
+ if ( !sieve_variable_assign(storage, var_index, value) )
+ return SIEVE_EXEC_BIN_CORRUPT;
+
+ /* Trace */
+ if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) {
+ const char *var_name, *var_id;
+
+ (void)sieve_variable_get_identifier(storage, var_index, &var_name);
+ var_id = sieve_variable_get_varid(storage, var_index);
+
+ sieve_runtime_trace_here(renv, 0, "assign `%s' [%s] = \"%s\"",
+ var_name, var_id, str_c(value));
+ }
+
+ return SIEVE_EXEC_OK;
+}
+
+
+
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c
new file mode 100644
index 0000000..2ac773c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c
@@ -0,0 +1,420 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-ast.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-dump.h"
+
+#include "ext-variables-common.h"
+#include "ext-variables-limits.h"
+#include "ext-variables-name.h"
+#include "ext-variables-operands.h"
+#include "ext-variables-namespaces.h"
+#include "ext-variables-arguments.h"
+
+/*
+ * Variable argument implementation
+ */
+
+static bool arg_variable_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *context);
+
+const struct sieve_argument_def variable_argument = {
+ .identifier = "@variable",
+ .generate = arg_variable_generate
+};
+
+static bool ext_variables_variable_argument_activate
+(const struct sieve_extension *var_ext,
+ const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
+ const char *variable)
+{
+ struct sieve_ast *ast = arg->ast;
+ struct sieve_variable *var;
+
+ var = ext_variables_validator_declare_variable(this_ext, valdtr, variable);
+
+ if ( var == NULL ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "(implicit) declaration of new variable '%s' exceeds the limit "
+ "(max variables: %u)", variable,
+ sieve_variables_get_max_scope_size(var_ext));
+ return FALSE;
+ }
+
+ arg->argument = sieve_argument_create(ast, &variable_argument, this_ext, 0);
+ arg->argument->data = (void *) var;
+ return TRUE;
+}
+
+static struct sieve_ast_argument *ext_variables_variable_argument_create
+(const struct sieve_extension *this_ext, struct sieve_validator *valdtr,
+ struct sieve_ast_argument *parent_arg, const char *variable)
+{
+ struct sieve_ast *ast = parent_arg->ast;
+ struct sieve_ast_argument *new_arg;
+
+ new_arg = sieve_ast_argument_create(ast, sieve_ast_argument_line(parent_arg));
+ new_arg->type = SAAT_STRING;
+
+ if ( !ext_variables_variable_argument_activate
+ (this_ext, this_ext, valdtr, new_arg, variable) )
+ return NULL;
+
+ return new_arg;
+}
+
+static bool arg_variable_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *context ATTR_UNUSED)
+{
+ struct sieve_argument *argument = arg->argument;
+ struct sieve_variable *var = (struct sieve_variable *) argument->data;
+
+ sieve_variables_opr_variable_emit(cgenv->sblock, argument->ext, var);
+
+ return TRUE;
+}
+
+/*
+ * Match value argument implementation
+ */
+
+static bool arg_match_value_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *context ATTR_UNUSED);
+
+const struct sieve_argument_def match_value_argument = {
+ .identifier = "@match_value",
+ .generate = arg_match_value_generate
+};
+
+static bool ext_variables_match_value_argument_activate
+(const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
+ unsigned int index, bool assignment)
+{
+ struct sieve_ast *ast = arg->ast;
+
+ if ( assignment ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "cannot assign to match variable");
+ return FALSE;
+ }
+
+ if ( index > EXT_VARIABLES_MAX_MATCH_INDEX ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "match value index %u out of range (max: %u)", index,
+ EXT_VARIABLES_MAX_MATCH_INDEX);
+ return FALSE;
+ }
+
+ arg->argument = sieve_argument_create
+ (ast, &match_value_argument, this_ext, 0);
+ arg->argument->data = (void *) POINTER_CAST(index);
+ return TRUE;
+}
+
+static struct sieve_ast_argument *ext_variables_match_value_argument_create
+(const struct sieve_extension *this_ext, struct sieve_validator *valdtr,
+ struct sieve_ast_argument *parent_arg, unsigned int index)
+{
+ struct sieve_ast *ast = parent_arg->ast;
+ struct sieve_ast_argument *new_arg;
+
+ new_arg = sieve_ast_argument_create(ast, sieve_ast_argument_line(parent_arg));
+ new_arg->type = SAAT_STRING;
+
+ if ( !ext_variables_match_value_argument_activate
+ (this_ext, valdtr, new_arg, index, FALSE) ) {
+ return NULL;
+ }
+
+ return new_arg;
+}
+
+static bool arg_match_value_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *context ATTR_UNUSED)
+{
+ struct sieve_argument *argument = arg->argument;
+ unsigned int index = POINTER_CAST_TO(argument->data, unsigned int);
+
+ sieve_variables_opr_match_value_emit(cgenv->sblock, argument->ext, index);
+
+ return TRUE;
+}
+
+/*
+ * Variable string argument implementation
+ */
+
+static bool arg_variable_string_validate
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+const struct sieve_argument_def variable_string_argument = {
+ .identifier = "@variable-string",
+ .validate = arg_variable_string_validate,
+ .generate = sieve_arg_catenated_string_generate,
+};
+
+static bool arg_variable_string_validate
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ const struct sieve_extension *this_ext = (*arg)->argument->ext;
+ enum { ST_NONE, ST_OPEN, ST_VARIABLE, ST_CLOSE } state = ST_NONE;
+ pool_t pool = sieve_ast_pool((*arg)->ast);
+ struct sieve_arg_catenated_string *catstr = NULL;
+ string_t *str = sieve_ast_argument_str(*arg);
+ const char *p, *strstart, *substart = NULL;
+ const char *strval = (const char *) str_data(str);
+ const char *strend = strval + str_len(str);
+ bool result = TRUE;
+ ARRAY_TYPE(sieve_variable_name) substitution;
+ int nelements = 0;
+
+ T_BEGIN {
+ /* Initialize substitution structure */
+ t_array_init(&substitution, 2);
+
+ p = strval;
+ strstart = p;
+ while ( result && p < strend ) {
+ switch ( state ) {
+
+ /* Nothing found yet */
+ case ST_NONE:
+ if ( *p == '$' ) {
+ substart = p;
+ state = ST_OPEN;
+ }
+ p++;
+ break;
+
+ /* Got '$' */
+ case ST_OPEN:
+ if ( *p == '{' ) {
+ state = ST_VARIABLE;
+ p++;
+ } else
+ state = ST_NONE;
+ break;
+
+ /* Got '${' */
+ case ST_VARIABLE:
+ nelements = ext_variable_name_parse(&substitution, &p, strend);
+
+ if ( nelements < 0 )
+ state = ST_NONE;
+ else
+ state = ST_CLOSE;
+
+ break;
+
+ /* Finished parsing name, expecting '}' */
+ case ST_CLOSE:
+ if ( *p == '}' ) {
+ struct sieve_ast_argument *strarg;
+
+ /* We now know that the substitution is valid */
+
+ if ( catstr == NULL ) {
+ catstr = sieve_arg_catenated_string_create(*arg);
+ }
+
+ /* Add the substring that is before the substitution to the
+ * variable-string AST.
+ *
+ * FIXME: For efficiency, if the variable is not found we should
+ * coalesce this substring with the one after the substitution.
+ */
+ if ( substart > strstart ) {
+ string_t *newstr = str_new(pool, substart - strstart);
+ str_append_data(newstr, strstart, substart - strstart);
+
+ strarg = sieve_ast_argument_string_create_raw
+ ((*arg)->ast, newstr, (*arg)->source_line);
+ sieve_arg_catenated_string_add_element(catstr, strarg);
+
+ /* Give other substitution extensions a chance to do their work */
+ if ( !sieve_validator_argument_activate_super
+ (valdtr, cmd, strarg, FALSE) ) {
+ result = FALSE;
+ break;
+ }
+ }
+
+ /* Find the variable */
+ if ( nelements == 1 ) {
+ const struct sieve_variable_name *cur_element =
+ array_idx(&substitution, 0);
+
+ if ( cur_element->num_variable == -1 ) {
+ /* Add variable argument '${identifier}' */
+
+ strarg = ext_variables_variable_argument_create
+ (this_ext, valdtr, *arg, str_c(cur_element->identifier));
+
+ } else {
+ /* Add match value argument '${000}' */
+
+ strarg = ext_variables_match_value_argument_create
+ (this_ext, valdtr, *arg, cur_element->num_variable);
+ }
+ } else {
+ strarg = ext_variables_namespace_argument_create
+ (this_ext, valdtr, *arg, cmd, &substitution);
+ }
+
+ if ( strarg != NULL )
+ sieve_arg_catenated_string_add_element(catstr, strarg);
+
+ strstart = p + 1;
+ substart = strstart;
+
+ p++;
+ }
+
+ /* Finished, reset for the next substitution */
+ state = ST_NONE;
+ }
+ }
+ } T_END;
+
+ /* Bail out early if substitution is invalid */
+ if ( !result ) return FALSE;
+
+ /* Check whether any substitutions were found */
+ if ( catstr == NULL ) {
+ /* No substitutions in this string, pass it on to any other substution
+ * extension.
+ */
+ return sieve_validator_argument_activate_super(valdtr, cmd, *arg, TRUE);
+ }
+
+ /* Add the final substring that comes after the last substitution to the
+ * variable-string AST.
+ */
+ if ( strend > strstart ) {
+ struct sieve_ast_argument *strarg;
+ string_t *newstr = str_new(pool, strend - strstart);
+ str_append_data(newstr, strstart, strend - strstart);
+
+ strarg = sieve_ast_argument_string_create_raw
+ ((*arg)->ast, newstr, (*arg)->source_line);
+ sieve_arg_catenated_string_add_element(catstr, strarg);
+
+ /* Give other substitution extensions a chance to do their work */
+ if ( !sieve_validator_argument_activate_super
+ (valdtr, cmd, strarg, FALSE) )
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Variable argument interface
+ */
+
+static bool _sieve_variable_argument_activate
+(const struct sieve_extension *var_ext,
+ const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_command *cmd,
+ struct sieve_ast_argument *arg, bool assignment)
+{
+ bool result = FALSE;
+ string_t *variable;
+ const char *varstr, *varend;
+ ARRAY_TYPE(sieve_variable_name) vname;
+ int nelements = 0;
+
+ T_BEGIN {
+ t_array_init(&vname, 2);
+
+ variable = sieve_ast_argument_str(arg);
+ varstr = str_c(variable);
+ varend = PTR_OFFSET(varstr, str_len(variable));
+ nelements = ext_variable_name_parse(&vname, &varstr, varend);
+
+ /* Check whether name parsing succeeded */
+ if ( nelements <= 0 || varstr != varend ) {
+ /* Parse failed */
+ sieve_argument_validate_error(valdtr, arg,
+ "invalid variable name '%s'", str_sanitize(str_c(variable),80));
+ } else if ( nelements == 1 ) {
+ /* Normal (match) variable */
+
+ const struct sieve_variable_name *cur_element =
+ array_idx(&vname, 0);
+
+ if ( cur_element->num_variable < 0 ) {
+ /* Variable */
+ result = ext_variables_variable_argument_activate(var_ext,
+ this_ext, valdtr, arg, str_c(cur_element->identifier));
+
+ } else {
+ /* Match value */
+ result = ext_variables_match_value_argument_activate
+ (this_ext, valdtr, arg, cur_element->num_variable, assignment);
+ }
+
+ } else {
+ /* Namespace variable */
+ result = ext_variables_namespace_argument_activate
+ (this_ext, valdtr, arg, cmd, &vname, assignment);
+ }
+ } T_END;
+
+ return result;
+}
+
+bool sieve_variable_argument_activate
+(const struct sieve_extension *var_ext,
+ const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_command *cmd,
+ struct sieve_ast_argument *arg, bool assignment)
+{
+ if ( sieve_ast_argument_type(arg) == SAAT_STRING ) {
+ /* Single string */
+ return _sieve_variable_argument_activate(var_ext,
+ this_ext, valdtr, cmd, arg, assignment);
+
+ } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) {
+ /* String list */
+ struct sieve_ast_argument *stritem;
+
+ i_assert ( !assignment );
+
+ stritem = sieve_ast_strlist_first(arg);
+ while ( stritem != NULL ) {
+ if ( !_sieve_variable_argument_activate(var_ext,
+ this_ext, valdtr, cmd, stritem, assignment) )
+ return FALSE;
+
+ stritem = sieve_ast_strlist_next(stritem);
+ }
+
+ arg->argument = sieve_argument_create
+ (arg->ast, &string_list_argument, NULL, 0);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.h
new file mode 100644
index 0000000..87413c8
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.h
@@ -0,0 +1,24 @@
+#ifndef EXT_VARIABLES_ARGUMENTS_H
+#define EXT_VARIABLES_ARGUMENTS_H
+
+#include "sieve-common.h"
+
+/*
+ * Variable argument
+ */
+
+extern const struct sieve_argument_def variable_argument;
+
+/*
+ * Match value argument
+ */
+
+extern const struct sieve_argument_def match_value_argument;
+
+/*
+ * Variable string argument
+ */
+
+extern const struct sieve_argument_def variable_string_argument;
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c
new file mode 100644
index 0000000..be9f677
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c
@@ -0,0 +1,950 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "hash.h"
+#include "str.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-settings.h"
+
+#include "sieve-ast.h"
+#include "sieve-binary.h"
+#include "sieve-code.h"
+#include "sieve-objects.h"
+#include "sieve-match-types.h"
+
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-dump.h"
+#include "sieve-interpreter.h"
+
+#include "ext-variables-common.h"
+#include "ext-variables-limits.h"
+#include "ext-variables-name.h"
+#include "ext-variables-modifiers.h"
+
+/*
+ * Limits
+ */
+
+unsigned int
+sieve_variables_get_max_scope_size(const struct sieve_extension *var_ext)
+{
+ const struct ext_variables_config *config =
+ ext_variables_get_config(var_ext);
+
+ return config->max_scope_size;
+}
+
+size_t
+sieve_variables_get_max_variable_size(const struct sieve_extension *var_ext)
+{
+ const struct ext_variables_config *config =
+ ext_variables_get_config(var_ext);
+
+ return config->max_variable_size;
+}
+
+/*
+ * Extension configuration
+ */
+
+bool
+ext_variables_load(const struct sieve_extension *ext, void **context)
+{
+ struct sieve_instance *svinst = ext->svinst;
+ struct ext_variables_config *config;
+ unsigned long long int uint_setting;
+ size_t size_setting;
+
+ if (*context != NULL)
+ ext_variables_unload(ext);
+
+ config = i_new(struct ext_variables_config, 1);
+
+ /* Get limits */
+ config->max_scope_size = EXT_VARIABLES_DEFAULT_MAX_SCOPE_SIZE;
+ config->max_variable_size = EXT_VARIABLES_DEFAULT_MAX_VARIABLE_SIZE;
+
+ if (sieve_setting_get_uint_value(
+ svinst, "sieve_variables_max_scope_size", &uint_setting)) {
+ if (uint_setting < EXT_VARIABLES_REQUIRED_MAX_SCOPE_SIZE) {
+ e_warning(svinst->event, "variables: "
+ "setting sieve_variables_max_scope_size "
+ "is lower than required by standards "
+ "(>= %llu items)",
+ (unsigned long long)EXT_VARIABLES_REQUIRED_MAX_SCOPE_SIZE);
+ } else {
+ config->max_scope_size = (unsigned int)uint_setting;
+ }
+ }
+
+ if (sieve_setting_get_size_value(
+ svinst, "sieve_variables_max_variable_size", &size_setting)) {
+ if (size_setting < EXT_VARIABLES_REQUIRED_MAX_VARIABLE_SIZE) {
+ e_warning(svinst->event, "variables: "
+ "setting sieve_variables_max_variable_size "
+ "is lower than required by standards "
+ "(>= %zu bytes)",
+ (size_t)EXT_VARIABLES_REQUIRED_MAX_VARIABLE_SIZE);
+ } else {
+ config->max_variable_size = size_setting;
+ }
+ }
+
+ *context = (void *)config;
+ return TRUE;
+}
+
+void ext_variables_unload(const struct sieve_extension *ext)
+{
+ struct ext_variables_config *config =
+ (struct ext_variables_config *)ext->context;
+
+ i_free(config);
+}
+
+const struct ext_variables_config *
+ext_variables_get_config(const struct sieve_extension *var_ext)
+{
+ const struct ext_variables_config *config =
+ (const struct ext_variables_config *)var_ext->context;
+
+ i_assert(var_ext->def == &variables_extension);
+ return config;
+}
+
+/*
+ * Variable scope
+ */
+
+struct sieve_variable_scope {
+ pool_t pool;
+ int refcount;
+
+ struct sieve_instance *svinst;
+ const struct sieve_extension *var_ext;
+ const struct sieve_extension *ext;
+
+ struct sieve_variable *error_var;
+
+ HASH_TABLE(const char *, struct sieve_variable *) variables;
+ ARRAY(struct sieve_variable *) variable_index;
+};
+
+struct sieve_variable_scope_binary {
+ struct sieve_variable_scope *scope;
+
+ unsigned int size;
+ struct sieve_binary_block *sblock;
+ sieve_size_t address;
+};
+
+struct sieve_variable_scope_iter {
+ struct sieve_variable_scope *scope;
+ struct hash_iterate_context *hctx;
+};
+
+struct sieve_variable_scope *
+sieve_variable_scope_create(struct sieve_instance *svinst,
+ const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext)
+{
+ struct sieve_variable_scope *scope;
+ pool_t pool;
+
+ i_assert(var_ext->def == &variables_extension);
+
+ pool = pool_alloconly_create("sieve_variable_scope", 4096);
+ scope = p_new(pool, struct sieve_variable_scope, 1);
+ scope->pool = pool;
+ scope->refcount = 1;
+
+ scope->svinst = svinst;
+ scope->var_ext = var_ext;
+ scope->ext = ext;
+
+ hash_table_create(&scope->variables, pool, 0, strcase_hash, strcasecmp);
+ p_array_init(&scope->variable_index, pool, 128);
+
+ return scope;
+}
+
+void sieve_variable_scope_ref(struct sieve_variable_scope *scope)
+{
+ scope->refcount++;
+}
+
+void sieve_variable_scope_unref(struct sieve_variable_scope **_scope)
+{
+ struct sieve_variable_scope *scope = *_scope;
+
+ i_assert(scope->refcount > 0);
+
+ if (--scope->refcount != 0)
+ return;
+
+ hash_table_destroy(&scope->variables);
+
+ *_scope = NULL;
+ pool_unref(&scope->pool);
+}
+
+pool_t sieve_variable_scope_pool(struct sieve_variable_scope *scope)
+{
+ return scope->pool;
+}
+
+struct sieve_variable *
+sieve_variable_scope_declare(struct sieve_variable_scope *scope,
+ const char *identifier)
+{
+ unsigned int max_scope_size;
+ struct sieve_variable *var;
+
+ var = hash_table_lookup(scope->variables, identifier);
+ if (var != NULL)
+ return var;
+
+ max_scope_size = sieve_variables_get_max_scope_size(scope->var_ext);
+ if (array_count(&scope->variable_index) >= max_scope_size) {
+ if (scope->error_var == NULL) {
+ var = p_new(scope->pool, struct sieve_variable, 1);
+ var->identifier = "@ERROR@";
+ var->index = 0;
+
+ scope->error_var = var;
+ return NULL;
+ }
+
+ return scope->error_var;
+ }
+
+ var = p_new(scope->pool, struct sieve_variable, 1);
+ var->ext = scope->ext;
+ var->identifier = p_strdup(scope->pool, identifier);
+ var->index = array_count(&scope->variable_index);
+
+ hash_table_insert(scope->variables, var->identifier, var);
+ array_append(&scope->variable_index, &var, 1);
+ return var;
+}
+
+struct sieve_variable *
+sieve_variable_scope_get_variable(struct sieve_variable_scope *scope,
+ const char *identifier)
+{
+ return hash_table_lookup(scope->variables, identifier);
+}
+
+struct sieve_variable *
+sieve_variable_scope_import(struct sieve_variable_scope *scope,
+ struct sieve_variable *var)
+{
+ struct sieve_variable *old_var, *new_var;
+
+ old_var = sieve_variable_scope_get_variable(scope, var->identifier);
+ if (old_var != NULL) {
+ i_assert(memcmp(old_var, var, sizeof(*var)) == 0);
+ return old_var;
+ }
+
+ new_var = p_new(scope->pool, struct sieve_variable, 1);
+ memcpy(new_var, var, sizeof(*new_var));
+
+ hash_table_insert(scope->variables, new_var->identifier, new_var);
+
+ /* Not entered into the index because it is an external variable
+ (This can be done unlimited; only limited by the size of the external
+ scope)
+ */
+ return new_var;
+}
+
+struct sieve_variable_scope_iter *
+sieve_variable_scope_iterate_init(struct sieve_variable_scope *scope)
+{
+ struct sieve_variable_scope_iter *iter;
+
+ iter = t_new(struct sieve_variable_scope_iter, 1);
+ iter->scope = scope;
+ iter->hctx = hash_table_iterate_init(scope->variables);
+
+ return iter;
+}
+
+bool sieve_variable_scope_iterate(struct sieve_variable_scope_iter *iter,
+ struct sieve_variable **var_r)
+{
+ const char *key;
+
+ return hash_table_iterate(iter->hctx, iter->scope->variables,
+ &key, var_r);
+}
+
+void sieve_variable_scope_iterate_deinit(
+ struct sieve_variable_scope_iter **iter)
+{
+ hash_table_iterate_deinit(&(*iter)->hctx);
+ *iter = NULL;
+}
+
+unsigned int
+sieve_variable_scope_declarations(struct sieve_variable_scope *scope)
+{
+ return hash_table_count(scope->variables);
+}
+
+unsigned int sieve_variable_scope_size(struct sieve_variable_scope *scope)
+{
+ return array_count(&scope->variable_index);
+}
+
+struct sieve_variable * const *
+sieve_variable_scope_get_variables(struct sieve_variable_scope *scope,
+ unsigned int *size_r)
+{
+ return array_get(&scope->variable_index, size_r);
+}
+
+struct sieve_variable *
+sieve_variable_scope_get_indexed(struct sieve_variable_scope *scope,
+ unsigned int index)
+{
+ struct sieve_variable * const *var;
+
+ if (index >= array_count(&scope->variable_index))
+ return NULL;
+
+ var = array_idx(&scope->variable_index, index);
+ return *var;
+}
+
+/* Scope binary */
+
+struct sieve_variable_scope *
+sieve_variable_scope_binary_dump(struct sieve_instance *svinst,
+ const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext,
+ const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ struct sieve_variable_scope *local_scope;
+ unsigned int i, scope_size;
+ sieve_size_t pc;
+ sieve_offset_t end_offset;
+
+ /* Read scope size */
+ sieve_code_mark(denv);
+ if (!sieve_binary_read_unsigned(denv->sblock, address, &scope_size))
+ return NULL;
+
+ /* Read offset */
+ pc = *address;
+ if (!sieve_binary_read_offset(denv->sblock, address, &end_offset))
+ return NULL;
+
+ /* Create scope */
+ local_scope = sieve_variable_scope_create(svinst, var_ext, ext);
+
+ /* Read and dump scope itself */
+
+ sieve_code_dumpf(denv, "VARIABLES SCOPE [%u] (end: %08x)",
+ scope_size, (unsigned int)(pc + end_offset));
+
+ for (i = 0; i < scope_size; i++) {
+ string_t *identifier;
+
+ sieve_code_mark(denv);
+ if (!sieve_binary_read_string(denv->sblock, address,
+ &identifier))
+ return NULL;
+
+ sieve_code_dumpf(denv, "%3d: '%s'", i, str_c(identifier));
+
+ (void)sieve_variable_scope_declare(local_scope,
+ str_c(identifier));
+ }
+
+ return local_scope;
+}
+
+struct sieve_variable_scope_binary *
+sieve_variable_scope_binary_create(struct sieve_variable_scope *scope)
+{
+ struct sieve_variable_scope_binary *scpbin;
+
+ scpbin = p_new(scope->pool, struct sieve_variable_scope_binary, 1);
+ scpbin->scope = scope;
+
+ return scpbin;
+}
+
+void sieve_variable_scope_binary_ref(struct sieve_variable_scope_binary *scpbin)
+{
+ sieve_variable_scope_ref(scpbin->scope);
+}
+
+void sieve_variable_scope_binary_unref(
+ struct sieve_variable_scope_binary **scpbin)
+{
+ sieve_variable_scope_unref(&(*scpbin)->scope);
+ *scpbin = NULL;
+}
+
+struct sieve_variable_scope_binary *
+sieve_variable_scope_binary_read(struct sieve_instance *svinst,
+ const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext,
+ struct sieve_binary_block *sblock,
+ sieve_size_t *address)
+{
+ struct sieve_variable_scope *scope;
+ struct sieve_variable_scope_binary *scpbin;
+ unsigned int scope_size, max_scope_size;
+ const char *ext_name = (ext == NULL ? "variables" :
+ sieve_extension_name(ext));
+ sieve_size_t pc;
+ sieve_offset_t end_offset;
+
+ /* Read scope size */
+ if (!sieve_binary_read_unsigned(sblock, address, &scope_size)) {
+ e_error(svinst->event, "%s: "
+ "variable scope: failed to read size", ext_name);
+ return NULL;
+ }
+
+ /* Check size limit */
+ max_scope_size = sieve_variables_get_max_scope_size(var_ext);
+ if (scope_size > max_scope_size) {
+ e_error(svinst->event, "%s: "
+ "variable scope: size exceeds the limit (%u > %u)",
+ ext_name, scope_size, max_scope_size);
+ return NULL;
+ }
+
+ /* Read offset */
+ pc = *address;
+ if (!sieve_binary_read_offset(sblock, address, &end_offset)) {
+ e_error(svinst->event, "%s: "
+ "variable scope: failed to read end offset", ext_name);
+ return NULL;
+ }
+
+ /* Create scope */
+ scope = sieve_variable_scope_create(svinst, var_ext, ext);
+
+ scpbin = sieve_variable_scope_binary_create(scope);
+ scpbin->size = scope_size;
+ scpbin->sblock = sblock;
+ scpbin->address = *address;
+
+ *address = pc + end_offset;
+
+ return scpbin;
+}
+
+struct sieve_variable_scope *
+sieve_variable_scope_binary_get(struct sieve_variable_scope_binary *scpbin)
+{
+ const struct sieve_extension *ext = scpbin->scope->ext;
+ struct sieve_instance *svinst = scpbin->scope->svinst;
+ const char *ext_name = (ext == NULL ? "variables" :
+ sieve_extension_name(ext));
+ unsigned int i;
+
+ if (scpbin->sblock != NULL) {
+ sieve_size_t *address = &scpbin->address;
+
+ /* Read scope itself */
+ for (i = 0; i < scpbin->size; i++) {
+ struct sieve_variable *var;
+ string_t *identifier;
+
+ if (!sieve_binary_read_string(scpbin->sblock, address,
+ &identifier)) {
+ e_error(svinst->event, "%s: variable scope: "
+ "failed to read variable name",
+ ext_name);
+ return NULL;
+ }
+
+ var = sieve_variable_scope_declare(scpbin->scope,
+ str_c(identifier));
+
+ i_assert(var != NULL);
+ i_assert(var->index == i);
+ }
+
+ scpbin->sblock = NULL;
+ }
+
+ return scpbin->scope;
+}
+
+unsigned int
+sieve_variable_scope_binary_get_size(
+ struct sieve_variable_scope_binary *scpbin)
+{
+ if (scpbin->sblock != NULL)
+ return scpbin->size;
+
+ return array_count(&scpbin->scope->variable_index);
+}
+
+/*
+ * Variable storage
+ */
+
+struct sieve_variable_storage {
+ pool_t pool;
+ const struct sieve_extension *var_ext;
+ struct sieve_variable_scope *scope;
+ struct sieve_variable_scope_binary *scope_bin;
+ unsigned int max_size;
+ ARRAY(string_t *) var_values;
+};
+
+struct sieve_variable_storage *
+sieve_variable_storage_create(const struct sieve_extension *var_ext,
+ pool_t pool,
+ struct sieve_variable_scope_binary *scpbin)
+{
+ struct sieve_variable_storage *storage;
+
+ storage = p_new(pool, struct sieve_variable_storage, 1);
+ storage->pool = pool;
+ storage->var_ext = var_ext;
+ storage->scope_bin = scpbin;
+ storage->scope = NULL;
+
+ storage->max_size = sieve_variable_scope_binary_get_size(scpbin);
+
+ p_array_init(&storage->var_values, pool, 4);
+
+ return storage;
+}
+
+static inline bool
+sieve_variable_valid(struct sieve_variable_storage *storage,
+ unsigned int index)
+{
+ if (storage->scope_bin == NULL)
+ return TRUE;
+
+ return (index < storage->max_size);
+}
+
+bool sieve_variable_get_identifier(struct sieve_variable_storage *storage,
+ unsigned int index, const char **identifier)
+{
+ struct sieve_variable * const *var;
+
+ *identifier = NULL;
+
+ if (storage->scope_bin == NULL)
+ return TRUE;
+
+ if (storage->scope == NULL) {
+ storage->scope =
+ sieve_variable_scope_binary_get(storage->scope_bin);
+ if (storage->scope == NULL)
+ return FALSE;
+ }
+
+ /* FIXME: direct invasion of the scope object is a bit ugly */
+ if (index >= array_count(&storage->scope->variable_index))
+ return FALSE;
+
+ var = array_idx(&storage->scope->variable_index, index);
+ if (*var != NULL)
+ *identifier = (*var)->identifier;
+ return TRUE;
+}
+
+const char *
+sieve_variable_get_varid(struct sieve_variable_storage *storage,
+ unsigned int index)
+{
+ if (storage->scope_bin == NULL)
+ return t_strdup_printf("%ld", (long)index);
+
+ if (storage->scope == NULL) {
+ storage->scope =
+ sieve_variable_scope_binary_get(storage->scope_bin);
+ if (storage->scope == NULL)
+ return NULL;
+ }
+
+ return sieve_ext_variables_get_varid(storage->scope->ext, index);
+}
+
+bool sieve_variable_get(struct sieve_variable_storage *storage,
+ unsigned int index, string_t **value)
+{
+ *value = NULL;
+
+ if (index < array_count(&storage->var_values)) {
+ string_t * const *varent;
+
+ varent = array_idx(&storage->var_values, index);
+
+ *value = *varent;
+ } else if (!sieve_variable_valid(storage, index)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+bool sieve_variable_get_modifiable(struct sieve_variable_storage *storage,
+ unsigned int index, string_t **value)
+{
+ string_t *dummy;
+
+ if (value == NULL)
+ value = &dummy;
+
+ if (!sieve_variable_get(storage, index, value))
+ return FALSE;
+
+ if (*value == NULL) {
+ *value = str_new(storage->pool, 256);
+ array_idx_set(&storage->var_values, index, value);
+ }
+ return TRUE;
+}
+
+bool sieve_variable_assign(struct sieve_variable_storage *storage,
+ unsigned int index, const string_t *value)
+{
+ const struct ext_variables_config *config =
+ ext_variables_get_config(storage->var_ext);
+ string_t *varval;
+
+ if (!sieve_variable_get_modifiable(storage, index, &varval))
+ return FALSE;
+
+ str_truncate(varval, 0);
+ str_append_str(varval, value);
+
+ /* Just a precaution, caller should prevent this in the first place */
+ if (str_len(varval) > config->max_variable_size)
+ str_truncate_utf8(varval, config->max_variable_size);
+
+ return TRUE;
+}
+
+bool sieve_variable_assign_cstr(struct sieve_variable_storage *storage,
+ unsigned int index, const char *value)
+{
+ const struct ext_variables_config *config =
+ ext_variables_get_config(storage->var_ext);
+ string_t *varval;
+
+ if (!sieve_variable_get_modifiable(storage, index, &varval))
+ return FALSE;
+
+ str_truncate(varval, 0);
+ str_append(varval, value);
+
+ /* Just a precaution, caller should prevent this in the first place */
+ if (str_len(varval) > config->max_variable_size)
+ str_truncate_utf8(varval, config->max_variable_size);
+
+ return TRUE;
+}
+
+/*
+ * AST Context
+ */
+
+static void
+ext_variables_ast_free(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_ast *ast ATTR_UNUSED, void *context)
+{
+ struct sieve_variable_scope *local_scope =
+ (struct sieve_variable_scope *)context;
+
+ /* Unreference main variable scope */
+ sieve_variable_scope_unref(&local_scope);
+}
+
+static const struct sieve_ast_extension variables_ast_extension = {
+ &variables_extension,
+ ext_variables_ast_free
+};
+
+static struct sieve_variable_scope *
+ext_variables_create_local_scope(const struct sieve_extension *this_ext,
+ struct sieve_ast *ast)
+{
+ struct sieve_variable_scope *scope;
+
+ scope = sieve_variable_scope_create(this_ext->svinst, this_ext, NULL);
+
+ sieve_ast_extension_register(ast, this_ext, &variables_ast_extension,
+ (void *)scope);
+ return scope;
+}
+
+static struct sieve_variable_scope *
+ext_variables_ast_get_local_scope(const struct sieve_extension *this_ext,
+ struct sieve_ast *ast)
+{
+ struct sieve_variable_scope *local_scope =
+ (struct sieve_variable_scope *)
+ sieve_ast_extension_get_context(ast, this_ext);
+
+ return local_scope;
+}
+
+/*
+ * Validator context
+ */
+
+static struct ext_variables_validator_context *
+ext_variables_validator_context_create(const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr)
+{
+ pool_t pool = sieve_validator_pool(valdtr);
+ struct ext_variables_validator_context *ctx;
+ struct sieve_ast *ast = sieve_validator_ast(valdtr);
+
+ ctx = p_new(pool, struct ext_variables_validator_context, 1);
+ ctx->modifiers = sieve_validator_object_registry_create(valdtr);
+ ctx->namespaces = sieve_validator_object_registry_create(valdtr);
+ ctx->local_scope = ext_variables_create_local_scope(this_ext, ast);
+
+ sieve_validator_extension_set_context(valdtr, this_ext, (void *)ctx);
+ return ctx;
+}
+
+struct ext_variables_validator_context *
+ext_variables_validator_context_get(const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr)
+{
+ struct ext_variables_validator_context *ctx;
+
+ i_assert(sieve_extension_is(this_ext, variables_extension));
+ ctx = (struct ext_variables_validator_context *)
+ sieve_validator_extension_get_context(valdtr, this_ext);
+
+ if (ctx == NULL)
+ ctx = ext_variables_validator_context_create(this_ext, valdtr);
+ return ctx;
+}
+
+void ext_variables_validator_initialize(const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr)
+{
+ struct ext_variables_validator_context *ctx;
+
+ /* Create our context */
+ ctx = ext_variables_validator_context_get(this_ext, valdtr);
+
+ ext_variables_register_core_modifiers(this_ext, ctx);
+
+ ctx->active = TRUE;
+}
+
+struct sieve_variable *ext_variables_validator_get_variable(
+ const struct sieve_extension *this_ext,
+ struct sieve_validator *validator, const char *variable)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(this_ext, validator);
+
+ return sieve_variable_scope_get_variable(ctx->local_scope, variable);
+}
+
+struct sieve_variable *
+ext_variables_validator_declare_variable(const struct sieve_extension *this_ext,
+ struct sieve_validator *validator,
+ const char *variable)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(this_ext, validator);
+
+ return sieve_variable_scope_declare(ctx->local_scope, variable);
+}
+
+struct sieve_variable_scope *
+sieve_ext_variables_get_local_scope(const struct sieve_extension *var_ext,
+ struct sieve_validator *validator)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(var_ext, validator);
+
+ return ctx->local_scope;
+}
+
+bool sieve_ext_variables_is_active(const struct sieve_extension *var_ext,
+ struct sieve_validator *valdtr)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(var_ext, valdtr);
+
+ return (ctx != NULL && ctx->active);
+}
+
+/*
+ * Code generation
+ */
+
+bool ext_variables_generator_load(const struct sieve_extension *ext,
+ const struct sieve_codegen_env *cgenv)
+{
+ struct sieve_variable_scope *local_scope =
+ ext_variables_ast_get_local_scope(ext, cgenv->ast);
+ unsigned int count = sieve_variable_scope_size(local_scope);
+ sieve_size_t jump;
+
+ sieve_binary_emit_unsigned(cgenv->sblock, count);
+
+ jump = sieve_binary_emit_offset(cgenv->sblock, 0);
+
+ if (count > 0) {
+ unsigned int size, i;
+ struct sieve_variable *const *vars =
+ sieve_variable_scope_get_variables(local_scope, &size);
+
+ for (i = 0; i < size; i++) {
+ sieve_binary_emit_cstring(cgenv->sblock,
+ vars[i]->identifier);
+ }
+ }
+
+ sieve_binary_resolve_offset(cgenv->sblock, jump);
+ return TRUE;
+}
+
+/*
+ * Interpreter context
+ */
+
+struct ext_variables_interpreter_context {
+ pool_t pool;
+
+ struct sieve_variable_scope *local_scope;
+ struct sieve_variable_scope_binary *local_scope_bin;
+
+ struct sieve_variable_storage *local_storage;
+ ARRAY(struct sieve_variable_storage *) ext_storages;
+};
+
+static void
+ext_variables_interpreter_free(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_interpreter *interp ATTR_UNUSED,
+ void *context)
+{
+ struct ext_variables_interpreter_context *ctx =
+ (struct ext_variables_interpreter_context *)context;
+
+ sieve_variable_scope_binary_unref(&ctx->local_scope_bin);
+}
+
+static struct sieve_interpreter_extension
+variables_interpreter_extension = {
+ .ext_def = &variables_extension,
+ .free = ext_variables_interpreter_free
+};
+
+static struct ext_variables_interpreter_context *
+ext_variables_interpreter_context_create(
+ const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp,
+ struct sieve_variable_scope_binary *scpbin)
+{
+ pool_t pool = sieve_interpreter_pool(interp);
+ struct ext_variables_interpreter_context *ctx;
+
+ ctx = p_new(pool, struct ext_variables_interpreter_context, 1);
+ ctx->pool = pool;
+ ctx->local_scope = NULL;
+ ctx->local_scope_bin = scpbin;
+ ctx->local_storage =
+ sieve_variable_storage_create(this_ext, pool, scpbin);
+ p_array_init(&ctx->ext_storages, pool,
+ sieve_extensions_get_count(this_ext->svinst));
+
+ sieve_interpreter_extension_register(interp, this_ext,
+ &variables_interpreter_extension,
+ (void *)ctx);
+ return ctx;
+}
+
+bool ext_variables_interpreter_load(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct sieve_variable_scope_binary *scpbin;
+
+ scpbin = sieve_variable_scope_binary_read(eenv->svinst, ext, NULL,
+ renv->sblock, address);
+ if (scpbin == NULL)
+ return FALSE;
+
+ /* Create our context */
+ (void)ext_variables_interpreter_context_create(ext, renv->interp,
+ scpbin);
+
+ /* Enable support for match values */
+ (void)sieve_match_values_set_enabled(renv, TRUE);
+
+ return TRUE;
+}
+
+static inline struct ext_variables_interpreter_context *
+ext_variables_interpreter_context_get(const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp)
+{
+ struct ext_variables_interpreter_context *ctx;
+
+ i_assert(sieve_extension_is(this_ext, variables_extension));
+ ctx = (struct ext_variables_interpreter_context *)
+ sieve_interpreter_extension_get_context(interp, this_ext);
+ return ctx;
+}
+
+struct sieve_variable_storage *
+sieve_ext_variables_runtime_get_storage(const struct sieve_extension *var_ext,
+ const struct sieve_runtime_env *renv,
+ const struct sieve_extension *ext)
+{
+ struct ext_variables_interpreter_context *ctx =
+ ext_variables_interpreter_context_get(var_ext, renv->interp);
+ struct sieve_variable_storage * const *storage;
+
+ if (ext == NULL)
+ return ctx->local_storage;
+
+ if (ext->id >= (int)array_count(&ctx->ext_storages))
+ storage = NULL;
+ else
+ storage = array_idx(&ctx->ext_storages, ext->id);
+
+ if (storage == NULL)
+ return NULL;
+ return *storage;
+}
+
+void sieve_ext_variables_runtime_set_storage(
+ const struct sieve_extension *var_ext,
+ const struct sieve_runtime_env *renv, const struct sieve_extension *ext,
+ struct sieve_variable_storage *storage)
+{
+ struct ext_variables_interpreter_context *ctx =
+ ext_variables_interpreter_context_get(var_ext, renv->interp);
+
+ if (ctx == NULL || ext == NULL || storage == NULL)
+ return;
+ if (ext->id < 0)
+ return;
+
+ array_idx_set(&ctx->ext_storages, (unsigned int) ext->id, &storage);
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.h
new file mode 100644
index 0000000..401d943
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.h
@@ -0,0 +1,102 @@
+#ifndef EXT_VARIABLES_COMMON_H
+#define EXT_VARIABLES_COMMON_H
+
+#include "sieve-common.h"
+#include "sieve-validator.h"
+
+#include "sieve-ext-variables.h"
+
+/*
+ * Extension
+ */
+
+struct ext_variables_config {
+ /* Maximum number of variables (in a scope) */
+ unsigned int max_scope_size;
+ /* Maximum size of variable value */
+ size_t max_variable_size;
+};
+
+extern const struct sieve_extension_def variables_extension;
+
+bool ext_variables_load(const struct sieve_extension *ext, void **context);
+void ext_variables_unload(const struct sieve_extension *ext);
+
+const struct ext_variables_config *
+ext_variables_get_config(const struct sieve_extension *var_ext);
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def cmd_set;
+extern const struct sieve_command_def tst_string;
+
+/*
+ * Operands
+ */
+
+enum ext_variables_operand {
+ EXT_VARIABLES_OPERAND_VARIABLE,
+ EXT_VARIABLES_OPERAND_MATCH_VALUE,
+ EXT_VARIABLES_OPERAND_NAMESPACE_VARIABLE,
+ EXT_VARIABLES_OPERAND_MODIFIER
+};
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def cmd_set_operation;
+extern const struct sieve_operation_def tst_string_operation;
+
+enum ext_variables_opcode {
+ EXT_VARIABLES_OPERATION_SET,
+ EXT_VARIABLES_OPERATION_STRING
+};
+
+/*
+ * Validator context
+ */
+
+struct ext_variables_validator_context {
+ bool active;
+
+ struct sieve_validator_object_registry *modifiers;
+ struct sieve_validator_object_registry *namespaces;
+
+ struct sieve_variable_scope *local_scope;
+};
+
+void ext_variables_validator_initialize(const struct sieve_extension *this_ext,
+ struct sieve_validator *validator);
+
+struct ext_variables_validator_context *
+ext_variables_validator_context_get(const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr);
+
+struct sieve_variable *
+ext_variables_validator_get_variable(const struct sieve_extension *this_ext,
+ struct sieve_validator *validator,
+ const char *variable);
+struct sieve_variable *
+ext_variables_validator_declare_variable(const struct sieve_extension *this_ext,
+ struct sieve_validator *validator,
+ const char *variable);
+
+/*
+ * Code generation
+ */
+
+bool ext_variables_generator_load(const struct sieve_extension *ext,
+ const struct sieve_codegen_env *cgenv);
+
+/*
+ * Interpreter context
+ */
+
+bool ext_variables_interpreter_load(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.c
new file mode 100644
index 0000000..26bd015
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.c
@@ -0,0 +1,137 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+
+#include "sieve-common.h"
+#include "sieve-dump.h"
+#include "sieve-binary.h"
+#include "sieve-code.h"
+
+#include "ext-variables-common.h"
+#include "ext-variables-dump.h"
+
+/*
+ * Code dumper extension
+ */
+
+static void ext_variables_code_dumper_free
+ (struct sieve_code_dumper *dumper, void *context);
+
+static const struct sieve_code_dumper_extension
+variables_dump_extension = {
+ &variables_extension,
+ ext_variables_code_dumper_free
+};
+
+/*
+ * Code dump context
+ */
+
+struct ext_variables_dump_context {
+ struct sieve_variable_scope *local_scope;
+ ARRAY(struct sieve_variable_scope *) ext_scopes;
+};
+
+static void ext_variables_code_dumper_free
+(struct sieve_code_dumper *dumper ATTR_UNUSED, void *context)
+{
+ struct ext_variables_dump_context *dctx =
+ (struct ext_variables_dump_context *) context;
+
+ if ( dctx == NULL || dctx->local_scope == NULL )
+ return;
+
+ sieve_variable_scope_unref(&dctx->local_scope);
+}
+
+static struct ext_variables_dump_context *ext_variables_dump_get_context
+(const struct sieve_extension *this_ext, const struct sieve_dumptime_env *denv)
+{
+ struct sieve_code_dumper *dumper = denv->cdumper;
+ struct ext_variables_dump_context *dctx;
+ pool_t pool;
+
+ i_assert( sieve_extension_is(this_ext, variables_extension) );
+ dctx = sieve_dump_extension_get_context(dumper, this_ext);
+
+ if ( dctx == NULL ) {
+ /* Create dumper context */
+ pool = sieve_code_dumper_pool(dumper);
+ dctx = p_new(pool, struct ext_variables_dump_context, 1);
+ p_array_init(&dctx->ext_scopes, pool,
+ sieve_extensions_get_count(this_ext->svinst));
+
+ sieve_dump_extension_register
+ (dumper, this_ext, &variables_dump_extension, dctx);
+ }
+
+ return dctx;
+}
+
+bool ext_variables_code_dump
+(const struct sieve_extension *ext,
+ const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ struct ext_variables_dump_context *dctx;
+ struct sieve_variable_scope *local_scope;
+
+ local_scope = sieve_variable_scope_binary_dump
+ (ext->svinst, ext, NULL, denv, address);
+
+ dctx = ext_variables_dump_get_context(ext, denv);
+ dctx->local_scope = local_scope;
+
+ return TRUE;
+}
+
+/*
+ * Scope registry
+ */
+
+void sieve_ext_variables_dump_set_scope
+(const struct sieve_extension *var_ext, const struct sieve_dumptime_env *denv,
+ const struct sieve_extension *ext, struct sieve_variable_scope *scope)
+{
+ struct ext_variables_dump_context *dctx =
+ ext_variables_dump_get_context(var_ext, denv);
+
+ if ( ext->id < 0 ) return;
+
+ array_idx_set(&dctx->ext_scopes, (unsigned int) ext->id, &scope);
+}
+
+/*
+ * Variable identifier dump
+ */
+
+const char *ext_variables_dump_get_identifier
+(const struct sieve_extension *var_ext, const struct sieve_dumptime_env *denv,
+ const struct sieve_extension *ext, unsigned int index)
+{
+ struct ext_variables_dump_context *dctx =
+ ext_variables_dump_get_context(var_ext, denv);
+ struct sieve_variable_scope *scope;
+ struct sieve_variable *var;
+
+ if ( ext == NULL )
+ scope = dctx->local_scope;
+ else {
+ struct sieve_variable_scope *const *ext_scope;
+
+ if ( ext->id < 0 || ext->id >= (int) array_count(&dctx->ext_scopes) )
+ return NULL;
+
+ ext_scope = array_idx(&dctx->ext_scopes, (unsigned int) ext->id);
+ scope = *ext_scope;
+ }
+
+ if ( scope == NULL )
+ return NULL;
+
+ var = sieve_variable_scope_get_indexed(scope, index);
+
+ return var->identifier;
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.h
new file mode 100644
index 0000000..da72a5d
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-dump.h
@@ -0,0 +1,22 @@
+#ifndef EXT_VARIABLES_DUMP_H
+#define EXT_VARIABLES_DUMP_H
+
+#include "sieve-common.h"
+
+/*
+ * Code dump context
+ */
+
+bool ext_variables_code_dump
+ (const struct sieve_extension *ext, const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+
+/*
+ * Variable identifier dump
+ */
+
+const char *ext_variables_dump_get_identifier
+(const struct sieve_extension *var_ext, const struct sieve_dumptime_env *denv,
+ const struct sieve_extension *ext, unsigned int index);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-limits.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-limits.h
new file mode 100644
index 0000000..61260c8
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-limits.h
@@ -0,0 +1,35 @@
+#ifndef EXT_VARIABLES_LIMITS_H
+#define EXT_VARIABLES_LIMITS_H
+
+#include "sieve-limits.h"
+
+/* From RFC 5229:
+ *
+ * 6. Implementation Limits
+ *
+ * An implementation of this document MUST support at least 128 distinct
+ * variables. The supported length of variable names MUST be at least
+ * 32 characters. Each variable MUST be able to hold at least 4000
+ * characters. Attempts to set the variable to a value larger than what
+ * the implementation supports SHOULD be reported as an error at
+ * compile-time if possible. If the attempt is discovered during run-
+ * time, the value SHOULD be truncated, and it MUST NOT be treated as an
+ * error.
+
+ * Match variables ${1} through ${9} MUST be supported. References to
+ * higher indices than those the implementation supports MUST be treated
+ * as a syntax error, which SHOULD be discovered at compile-time.
+ */
+
+#define EXT_VARIABLES_DEFAULT_MAX_SCOPE_SIZE 255
+#define EXT_VARIABLES_DEFAULT_MAX_VARIABLE_SIZE (4 * 1024)
+
+#define EXT_VARIABLES_REQUIRED_MAX_SCOPE_SIZE 128
+#define EXT_VARIABLES_REQUIRED_MAX_VARIABLE_SIZE 4000
+
+#define EXT_VARIABLES_MAX_VARIABLE_NAME_LEN 64
+#define EXT_VARIABLES_MAX_NAMESPACE_ELEMENTS 10
+
+#define EXT_VARIABLES_MAX_MATCH_INDEX SIEVE_MAX_MATCH_VALUES
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c
new file mode 100644
index 0000000..dd21c88
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c
@@ -0,0 +1,578 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "unichar.h"
+#include "str-sanitize.h"
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-binary.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-runtime.h"
+
+#include "ext-variables-common.h"
+#include "ext-variables-limits.h"
+#include "ext-variables-modifiers.h"
+
+#include <ctype.h>
+
+/*
+ * Core modifiers
+ */
+
+extern const struct sieve_variables_modifier_def lower_modifier;
+extern const struct sieve_variables_modifier_def upper_modifier;
+extern const struct sieve_variables_modifier_def lowerfirst_modifier;
+extern const struct sieve_variables_modifier_def upperfirst_modifier;
+extern const struct sieve_variables_modifier_def quotewildcard_modifier;
+extern const struct sieve_variables_modifier_def length_modifier;
+
+enum ext_variables_modifier_code {
+ EXT_VARIABLES_MODIFIER_LOWER,
+ EXT_VARIABLES_MODIFIER_UPPER,
+ EXT_VARIABLES_MODIFIER_LOWERFIRST,
+ EXT_VARIABLES_MODIFIER_UPPERFIRST,
+ EXT_VARIABLES_MODIFIER_QUOTEWILDCARD,
+ EXT_VARIABLES_MODIFIER_LENGTH
+};
+
+const struct sieve_variables_modifier_def *ext_variables_core_modifiers[] = {
+ &lower_modifier,
+ &upper_modifier,
+ &lowerfirst_modifier,
+ &upperfirst_modifier,
+ &quotewildcard_modifier,
+ &length_modifier
+};
+
+const unsigned int ext_variables_core_modifiers_count =
+ N_ELEMENTS(ext_variables_core_modifiers);
+
+#define ext_variables_modifier_name(modf) \
+ (modf)->object->def->name
+#define ext_variables_modifiers_equal(modf1, modf2) \
+ ( (modf1)->def == (modf2)->def )
+#define ext_variables_modifiers_equal_precedence(modf1, modf2) \
+ ( (modf1)->def->precedence == (modf2)->def->precendence )
+
+/*
+ * Modifier registry
+ */
+
+void sieve_variables_modifier_register
+(const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ const struct sieve_variables_modifier_def *smodf_def)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(var_ext, valdtr);
+
+ sieve_validator_object_registry_add(ctx->modifiers, ext, &smodf_def->obj_def);
+}
+
+bool ext_variables_modifier_exists
+(const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ const char *identifier)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(var_ext, valdtr);
+
+ return sieve_validator_object_registry_find(ctx->modifiers, identifier, NULL);
+}
+
+const struct sieve_variables_modifier *ext_variables_modifier_create_instance
+(const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ struct sieve_command *cmd, const char *identifier)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(var_ext, valdtr);
+ struct sieve_object object;
+ struct sieve_variables_modifier *modf;
+ pool_t pool;
+
+ if ( !sieve_validator_object_registry_find
+ (ctx->modifiers, identifier, &object) )
+ return NULL;
+
+ pool = sieve_command_pool(cmd);
+ modf = p_new(pool, struct sieve_variables_modifier, 1);
+ modf->object = object;
+ modf->var_ext = var_ext;
+ modf->def = (const struct sieve_variables_modifier_def *) object.def;
+
+ return modf;
+}
+
+void ext_variables_register_core_modifiers
+(const struct sieve_extension *ext, struct ext_variables_validator_context *ctx)
+{
+ unsigned int i;
+
+ /* Register core modifiers*/
+ for ( i = 0; i < ext_variables_core_modifiers_count; i++ ) {
+ sieve_validator_object_registry_add
+ (ctx->modifiers, ext, &(ext_variables_core_modifiers[i]->obj_def));
+ }
+}
+
+/*
+ * Core modifiers
+ */
+
+/* Forward declarations */
+
+static bool
+mod_lower_modify(const struct sieve_variables_modifier *modf,
+ string_t *in, string_t **result);
+static bool
+mod_upper_modify(const struct sieve_variables_modifier *modf,
+ string_t *in, string_t **result);
+static bool
+mod_lowerfirst_modify(const struct sieve_variables_modifier *modf,
+ string_t *in, string_t **result);
+static bool
+mod_upperfirst_modify(const struct sieve_variables_modifier *modf,
+ string_t *in, string_t **result);
+static bool
+mod_length_modify(const struct sieve_variables_modifier *modf,
+ string_t *in, string_t **result);
+static bool
+mod_quotewildcard_modify(const struct sieve_variables_modifier *modf,
+ string_t *in, string_t **result);
+
+/* Modifier objects */
+
+const struct sieve_variables_modifier_def lower_modifier = {
+ SIEVE_OBJECT("lower", &modifier_operand, EXT_VARIABLES_MODIFIER_LOWER),
+ 40,
+ mod_lower_modify
+};
+
+const struct sieve_variables_modifier_def upper_modifier = {
+ SIEVE_OBJECT("upper", &modifier_operand, EXT_VARIABLES_MODIFIER_UPPER),
+ 40,
+ mod_upper_modify
+};
+
+const struct sieve_variables_modifier_def lowerfirst_modifier = {
+ SIEVE_OBJECT
+ ("lowerfirst", &modifier_operand, EXT_VARIABLES_MODIFIER_LOWERFIRST),
+ 30,
+ mod_lowerfirst_modify
+};
+
+const struct sieve_variables_modifier_def upperfirst_modifier = {
+ SIEVE_OBJECT
+ ("upperfirst", &modifier_operand, EXT_VARIABLES_MODIFIER_UPPERFIRST),
+ 30,
+ mod_upperfirst_modify
+};
+
+const struct sieve_variables_modifier_def quotewildcard_modifier = {
+ SIEVE_OBJECT
+ ("quotewildcard", &modifier_operand, EXT_VARIABLES_MODIFIER_QUOTEWILDCARD),
+ 20,
+ mod_quotewildcard_modify
+};
+
+const struct sieve_variables_modifier_def length_modifier = {
+ SIEVE_OBJECT("length", &modifier_operand, EXT_VARIABLES_MODIFIER_LENGTH),
+ 10,
+ mod_length_modify
+};
+
+/* Modifier implementations */
+
+static bool
+mod_upperfirst_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED,
+ string_t *in, string_t **result)
+{
+ char *content;
+
+ if ( str_len(in) == 0 ) {
+ *result = in;
+ return TRUE;
+ }
+
+ *result = t_str_new(str_len(in));
+ str_append_str(*result, in);
+
+ content = str_c_modifiable(*result);
+ content[0] = i_toupper(content[0]);
+
+ return TRUE;
+}
+
+static bool
+mod_lowerfirst_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED,
+ string_t *in, string_t **result)
+{
+ char *content;
+
+ if ( str_len(in) == 0 ) {
+ *result = in;
+ return TRUE;
+ }
+
+ *result = t_str_new(str_len(in));
+ str_append_str(*result, in);
+
+ content = str_c_modifiable(*result);
+ content[0] = i_tolower(content[0]);
+
+ return TRUE;
+}
+
+static bool
+mod_upper_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED,
+ string_t *in, string_t **result)
+{
+ char *content;
+
+ if ( str_len(in) == 0 ) {
+ *result = in;
+ return TRUE;
+ }
+
+ *result = t_str_new(str_len(in));
+ str_append_str(*result, in);
+
+ content = str_c_modifiable(*result);
+ (void)str_ucase(content);
+
+ return TRUE;
+}
+
+static bool
+mod_lower_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED,
+ string_t *in, string_t **result)
+{
+ char *content;
+
+ if ( str_len(in) == 0 ) {
+ *result = in;
+ return TRUE;
+ }
+
+ *result = t_str_new(str_len(in));
+ str_append_str(*result, in);
+
+ content = str_c_modifiable(*result);
+ (void)str_lcase(content);
+
+ return TRUE;
+}
+
+static bool
+mod_length_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED,
+ string_t *in, string_t **result)
+{
+ *result = t_str_new(64);
+ str_printfa(*result, "%llu", (unsigned long long)
+ uni_utf8_strlen_n(str_data(in), str_len(in)));
+ return TRUE;
+}
+
+static bool
+mod_quotewildcard_modify(const struct sieve_variables_modifier *modf,
+ string_t *in, string_t **result)
+{
+ size_t max_var_size =
+ sieve_variables_get_max_variable_size(modf->var_ext);
+ const unsigned char *p, *poff, *pend;
+ size_t new_size;
+
+ if ( str_len(in) == 0 ) {
+ /* empty string */
+ *result = in;
+ return TRUE;
+ }
+
+ /* allocate new string */
+ new_size = str_len(in) + 16;
+ if (new_size > max_var_size)
+ new_size = max_var_size;
+ *result = t_str_new(new_size + 1);
+
+ /* escape string */
+ p = str_data(in);
+ pend = p + str_len(in);
+ poff = p;
+ while (p < pend) {
+ unsigned int n = uni_utf8_char_bytes((char)*p);
+
+ if (n == 1 && (*p == '*' || *p == '?' || *p == '\\')) {
+ str_append_data(*result, poff, p - poff);
+ poff = p;
+
+ if (str_len(*result) + 2 > max_var_size)
+ break;
+
+ str_append_c(*result, '\\');
+ } else if ((str_len(*result) + (p - poff) + n) > max_var_size) {
+ break;
+ }
+ if (p + n > pend) {
+ p = pend;
+ break;
+ }
+ p += n;
+ }
+
+ str_append_data(*result, poff, p - poff);
+
+ return TRUE;
+}
+
+/*
+ * Modifier argument
+ */
+
+/* [MODIFIER]:
+ * ":lower" / ":upper" / ":lowerfirst" / ":upperfirst" /
+ * ":quotewildcard" / ":length"
+ */
+
+/* Forward declarations */
+
+static bool tag_modifier_is_instance_of
+ (struct sieve_validator *valdtr, struct sieve_command *cmd,
+ const struct sieve_extension *ext, const char *identifier, void **context);
+
+/* Modifier tag object */
+
+static const struct sieve_argument_def modifier_tag = {
+ .identifier = "MODIFIER",
+ .flags = SIEVE_ARGUMENT_FLAG_MULTIPLE,
+ .is_instance_of = tag_modifier_is_instance_of
+};
+
+/* Modifier tag implementation */
+
+static bool tag_modifier_is_instance_of
+(struct sieve_validator *valdtr, struct sieve_command *cmd,
+ const struct sieve_extension *ext, const char *identifier, void **data)
+{
+ const struct sieve_variables_modifier *modf;
+
+ if ( data == NULL ) {
+ return ext_variables_modifier_exists(ext, valdtr, identifier);
+ }
+
+ if ( (modf=ext_variables_modifier_create_instance
+ (ext, valdtr, cmd, identifier)) == NULL )
+ return FALSE;
+
+ *data = (void *) modf;
+
+ return TRUE;
+}
+
+/* Registration */
+
+void sieve_variables_modifiers_link_tag
+(struct sieve_validator *valdtr, const struct sieve_extension *var_ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag(valdtr, cmd_reg, var_ext, &modifier_tag, 0);
+}
+
+/* Validation */
+
+bool sieve_variables_modifiers_validate
+(struct sieve_validator *valdtr, struct sieve_command *cmd,
+ ARRAY_TYPE(sieve_variables_modifier) *modifiers)
+{
+ struct sieve_ast_argument *arg;
+
+ arg = sieve_command_first_argument(cmd);
+ while ( arg != NULL && arg != cmd->first_positional ) {
+ const struct sieve_variables_modifier *modfs;
+ const struct sieve_variables_modifier *modf;
+ unsigned int i, modf_count;
+ bool inserted;
+
+ if ( !sieve_argument_is(arg, modifier_tag) ) {
+ arg = sieve_ast_argument_next(arg);
+ continue;
+ }
+ modf = (const struct sieve_variables_modifier *)
+ arg->argument->data;
+
+ inserted = FALSE;
+ modfs = array_get(modifiers, &modf_count);
+ for ( i = 0; i < modf_count && !inserted; i++ ) {
+
+ if ( modfs[i].def->precedence == modf->def->precedence ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "modifiers :%s and :%s specified for the set command conflict "
+ "having equal precedence",
+ modfs[i].def->obj_def.identifier, modf->def->obj_def.identifier);
+ return FALSE;
+ }
+
+ if ( modfs[i].def->precedence < modf->def->precedence ) {
+ array_insert(modifiers, i, modf, 1);
+ inserted = TRUE;
+ }
+ }
+
+ if ( !inserted )
+ array_append(modifiers, modf, 1);
+
+ /* Added to modifier list;
+ self-destruct to prevent implicit code generation */
+ arg = sieve_ast_arguments_detach(arg, 1);
+ }
+ return TRUE;
+}
+
+bool sieve_variables_modifiers_generate
+(const struct sieve_codegen_env *cgenv,
+ ARRAY_TYPE(sieve_variables_modifier) *modifiers)
+{
+ struct sieve_binary_block *sblock = cgenv->sblock;
+ const struct sieve_variables_modifier *modfs;
+ unsigned int i, modf_count;
+
+ sieve_binary_emit_byte(sblock, array_count(modifiers));
+
+ modfs = array_get(modifiers, &modf_count);
+ for ( i = 0; i < modf_count; i++ ) {
+ ext_variables_opr_modifier_emit(sblock,
+ modfs[i].object.ext, modfs[i].def);
+ }
+ return TRUE;
+}
+
+/*
+ * Modifier coding
+ */
+
+const struct sieve_operand_class sieve_variables_modifier_operand_class =
+ { "modifier" };
+
+static const struct sieve_extension_objects core_modifiers =
+ SIEVE_VARIABLES_DEFINE_MODIFIERS(ext_variables_core_modifiers);
+
+const struct sieve_operand_def modifier_operand = {
+ .name = "modifier",
+ .ext_def = &variables_extension,
+ .code = EXT_VARIABLES_OPERAND_MODIFIER,
+ .class = &sieve_variables_modifier_operand_class,
+ .interface = &core_modifiers
+};
+
+bool sieve_variables_modifiers_code_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ unsigned int mdfs, i;
+
+ /* Read the number of applied modifiers we need to read */
+ if ( !sieve_binary_read_byte(denv->sblock, address, &mdfs) )
+ return FALSE;
+
+ /* Print all modifiers (sorted during code generation already) */
+ for ( i = 0; i < mdfs; i++ ) {
+ if ( !ext_variables_opr_modifier_dump(denv, address) )
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int sieve_variables_modifiers_code_read(
+ const struct sieve_runtime_env *renv,
+ const struct sieve_extension *var_ext, sieve_size_t *address,
+ ARRAY_TYPE(sieve_variables_modifier) *modifiers)
+{
+ unsigned int lprec, mdfs, i;
+
+ if ( !sieve_binary_read_byte(renv->sblock, address, &mdfs) ) {
+ sieve_runtime_trace_error(renv, "invalid modifier count");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ t_array_init(modifiers, mdfs);
+
+ lprec = (unsigned int)-1;
+ for ( i = 0; i < mdfs; i++ ) {
+ struct sieve_variables_modifier modf;
+
+ if ( !ext_variables_opr_modifier_read(renv, var_ext,
+ address, &modf) )
+ return SIEVE_EXEC_BIN_CORRUPT;
+ if ( modf.def != NULL ) {
+ if ( modf.def->precedence >= lprec ) {
+ sieve_runtime_trace_error(renv,
+ "unsorted modifier precedence");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ lprec = modf.def->precedence;
+ }
+
+ array_append(modifiers, &modf, 1);
+ }
+
+ return SIEVE_EXEC_OK;
+}
+
+/*
+ * Modifier application
+ */
+
+int sieve_variables_modifiers_apply
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *var_ext,
+ ARRAY_TYPE(sieve_variables_modifier) *modifiers,
+ string_t **value)
+{
+ const struct ext_variables_config *config =
+ ext_variables_get_config(var_ext);
+ const struct sieve_variables_modifier *modfs;
+ unsigned int i, modf_count;
+
+ /* Hold value within limits */
+ if ( str_len(*value) > config->max_variable_size ) {
+ /* assume variable originates from code, so copy it first */
+ string_t *new_value = t_str_new(config->max_variable_size+3);
+ str_append_str(new_value, *value);
+ *value = new_value;
+ str_truncate_utf8(*value, config->max_variable_size);
+ }
+
+ if ( !array_is_created(modifiers) )
+ return SIEVE_EXEC_OK;
+
+ modfs = array_get(modifiers, &modf_count);
+ if ( modf_count == 0 )
+ return SIEVE_EXEC_OK;
+
+ for ( i = 0; i < modf_count; i++ ) {
+ string_t *new_value;
+ const struct sieve_variables_modifier *modf = &modfs[i];
+
+ if ( modf->def != NULL && modf->def->modify != NULL ) {
+ if ( !modf->def->modify(modf, *value, &new_value) )
+ return SIEVE_EXEC_FAILURE;
+
+ *value = new_value;
+ if ( *value == NULL )
+ return SIEVE_EXEC_FAILURE;
+
+ sieve_runtime_trace_here
+ (renv, SIEVE_TRLVL_COMMANDS,
+ "modify :%s \"%s\" => \"%s\"",
+ sieve_variables_modifier_name(modf),
+ str_sanitize(str_c(*value), 256),
+ str_sanitize(str_c(new_value), 256));
+
+ /* Hold value within limits */
+ if ( str_len(*value) > config->max_variable_size )
+ str_truncate_utf8(*value, config->max_variable_size);
+ }
+ }
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.h
new file mode 100644
index 0000000..50b4256
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.h
@@ -0,0 +1,66 @@
+#ifndef EXT_VARIABLES_MODIFIERS_H
+#define EXT_VARIABLES_MODIFIERS_H
+
+#include "sieve-common.h"
+#include "sieve-runtime-trace.h"
+
+#include "ext-variables-common.h"
+
+#define ext_variables_namespace_name(nspc) \
+ (nspc)->object->def->name
+#define ext_variables_namespaces_equal(nspc1, nspc2) \
+ ( (nspc1)->def == (nspc2)->def ))
+
+/*
+ * Modifier registry
+ */
+
+bool ext_variables_modifier_exists
+ (const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ const char *identifier);
+const struct sieve_variables_modifier *ext_variables_modifier_create_instance
+ (const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ struct sieve_command *cmd, const char *identifier);
+
+void ext_variables_register_core_modifiers
+ (const struct sieve_extension *var_ext,
+ struct ext_variables_validator_context *ctx);
+
+/*
+ * Modifier operand
+ */
+
+extern const struct sieve_operand_def modifier_operand;
+
+static inline void ext_variables_opr_modifier_emit
+(struct sieve_binary_block *sblock, const struct sieve_extension *ext,
+ const struct sieve_variables_modifier_def *modf_def)
+{
+ sieve_opr_object_emit(sblock, ext, &modf_def->obj_def);
+}
+
+static inline bool
+ext_variables_opr_modifier_read(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *var_ext,
+ sieve_size_t *address,
+ struct sieve_variables_modifier *modf)
+{
+ if ( !sieve_opr_object_read
+ (renv, &sieve_variables_modifier_operand_class, address, &modf->object) ) {
+ sieve_runtime_trace_error(renv, "invalid modifier operand");
+ return FALSE;
+ }
+
+ modf->def = (const struct sieve_variables_modifier_def *) modf->object.def;
+ modf->var_ext = var_ext;
+ return TRUE;
+}
+
+static inline bool ext_variables_opr_modifier_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ return sieve_opr_object_dump
+ (denv, &sieve_variables_modifier_operand_class, address, NULL);
+}
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.c
new file mode 100644
index 0000000..fac0b71
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.c
@@ -0,0 +1,110 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "array.h"
+
+#include "sieve-common.h"
+
+#include "ext-variables-common.h"
+#include "ext-variables-limits.h"
+#include "ext-variables-name.h"
+
+#include <ctype.h>
+
+bool sieve_variable_identifier_is_valid(const char *identifier)
+{
+ const char *p = identifier;
+ size_t plen = strlen(identifier);
+ const char *pend;
+
+ if ( plen == 0 || plen >= EXT_VARIABLES_MAX_VARIABLE_NAME_LEN )
+ return FALSE;
+
+ pend = PTR_OFFSET(identifier, plen);
+
+ if ( *p == '_' || i_isalpha(*p) ) {
+ p++;
+
+ while ( p < pend && (*p == '_' || i_isalnum(*p)) ) {
+ p++;
+ }
+ }
+
+ return ( p == pend );
+}
+
+int ext_variable_name_parse
+(ARRAY_TYPE(sieve_variable_name) *vname, const char **str, const char *strend)
+{
+ const char *p = *str;
+
+ array_clear(vname);
+
+ while ( p < strend ) {
+ struct sieve_variable_name *cur_element;
+ string_t *cur_ident;
+
+ /* Acquire current position in the array */
+
+ if ( array_count(vname) >= EXT_VARIABLES_MAX_NAMESPACE_ELEMENTS )
+ return -1;
+
+ cur_element = array_append_space(vname);
+ cur_ident = cur_element->identifier = t_str_new(32);
+
+ /* Parse element */
+
+ /* Identifier */
+ if ( *p == '_' || i_isalpha(*p) ) {
+ cur_element->num_variable = -1;
+ str_truncate(cur_ident, 0);
+ str_append_c(cur_ident, *p);
+ p++;
+
+ while ( p < strend && (*p == '_' || i_isalnum(*p)) ) {
+ if ( str_len(cur_ident) >= EXT_VARIABLES_MAX_VARIABLE_NAME_LEN )
+ return -1;
+ str_append_c(cur_ident, *p);
+ p++;
+ }
+
+ /* Num-variable */
+ } else if ( i_isdigit(*p) ) {
+ cur_element->num_variable = *p - '0';
+ p++;
+
+ while ( p < strend && i_isdigit(*p) ) {
+ cur_element->num_variable = cur_element->num_variable*10 + (*p - '0');
+ p++;
+ }
+
+ /* If a num-variable is first, no more elements can follow because no
+ * namespace is specified.
+ */
+ if ( array_count(vname) == 1 ) {
+ *str = p;
+ return 1;
+ }
+ } else {
+ *str = p;
+ return -1;
+ }
+
+ /* Check whether next name element is present */
+ if ( p < strend && *p == '.' ) {
+ p++;
+
+ /* It may not be empty */
+ if ( p >= strend )
+ return -1;
+ } else
+ break;
+ }
+
+ *str = p;
+ return array_count(vname);
+}
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.h
new file mode 100644
index 0000000..289a978
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-name.h
@@ -0,0 +1,43 @@
+#ifndef EXT_VARIABLES_NAME_H
+#define EXT_VARIABLES_NAME_H
+
+/* Variable Substitution
+ * ---------------------
+ *
+ * The variable strings are preprocessed into an AST list consisting of variable
+ * substitutions and constant parts of the string. The variables to which
+ * the substitutions link are looked up and their index in their scope storage
+ * is what is added to the list and eventually emitted as byte code. So, in
+ * bytecode a variable string will look as a series of substrings interrupted by
+ * integer operands that refer to variables. During execution, the strings and
+ * the looked-up variables are concatenated to obtain the desired result. The
+ * the variable references are simple indexes into an array of variables, so
+ * looking these up during execution is a trivial process.
+ *
+ * However (RFC 5229):
+ * Tests or actions in future extensions may need to access the
+ * unexpanded version of the string argument and, e.g., do the expansion
+ * after setting variables in its namespace. The design of the
+ * implementation should allow this.
+ *
+ * Various options exist to provide this feature. If the extension is entirely
+ * namespace-based there is actually not very much of a problem. The variable
+ * list can easily be extended with new argument-types that refer to a variable
+ * identifier instead of an index in the variable's storage.
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-common.h"
+
+#include "ext-variables-common.h"
+
+/*
+ * Variable name parsing
+ */
+
+int ext_variable_name_parse
+ (ARRAY_TYPE(sieve_variable_name) *vname, const char **str, const char *strend);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.c
new file mode 100644
index 0000000..4df62e2
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.c
@@ -0,0 +1,236 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-binary.h"
+#include "sieve-dump.h"
+
+#include "ext-variables-common.h"
+#include "ext-variables-namespaces.h"
+
+#include <ctype.h>
+
+/*
+ * Namespace registry
+ */
+
+void sieve_variables_namespace_register
+(const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ const struct sieve_variables_namespace_def *nspc_def)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(var_ext, valdtr);
+
+ sieve_validator_object_registry_add(ctx->namespaces, ext, &nspc_def->obj_def);
+}
+
+bool ext_variables_namespace_exists
+(const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ const char *identifier)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(var_ext, valdtr);
+
+ return sieve_validator_object_registry_find
+ (ctx->namespaces, identifier, NULL);
+}
+
+const struct sieve_variables_namespace *ext_variables_namespace_create_instance
+(const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ struct sieve_command *cmd, const char *identifier)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(var_ext, valdtr);
+ struct sieve_object object;
+ struct sieve_variables_namespace *nspc;
+ pool_t pool;
+
+ if ( !sieve_validator_object_registry_find
+ (ctx->namespaces, identifier, &object) )
+ return NULL;
+
+ pool = sieve_command_pool(cmd);
+ nspc = p_new(pool, struct sieve_variables_namespace, 1);
+ nspc->object = object;
+ nspc->def = (const struct sieve_variables_namespace_def *) object.def;
+
+ return nspc;
+}
+
+/*
+ * Namespace variable argument
+ */
+
+struct arg_namespace_variable {
+ const struct sieve_variables_namespace *namespace;
+
+ void *data;
+};
+
+static bool arg_namespace_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *context ATTR_UNUSED);
+
+const struct sieve_argument_def namespace_argument = {
+ .identifier = "@namespace",
+ .generate = arg_namespace_generate
+};
+
+bool ext_variables_namespace_argument_activate
+(const struct sieve_extension *this_ext, struct sieve_validator *valdtr,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd,
+ ARRAY_TYPE(sieve_variable_name) *var_name, bool assignment)
+{
+ pool_t pool = sieve_command_pool(cmd);
+ struct sieve_ast *ast = arg->ast;
+ const struct sieve_variables_namespace *nspc;
+ struct arg_namespace_variable *var;
+ const struct sieve_variable_name *name_element = array_idx(var_name, 0);
+ void *var_data = NULL;
+
+ nspc = ext_variables_namespace_create_instance
+ (this_ext, valdtr, cmd, str_c(name_element->identifier));
+ if ( nspc == NULL ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "referring to variable in unknown namespace '%s'",
+ str_c(name_element->identifier));
+ return FALSE;
+ }
+
+ if ( nspc->def != NULL && nspc->def->validate != NULL &&
+ !nspc->def->validate
+ (valdtr, nspc, arg, cmd, var_name, &var_data, assignment) ) {
+ return FALSE;
+ }
+
+ var = p_new(pool, struct arg_namespace_variable, 1);
+ var->namespace = nspc;
+ var->data = var_data;
+
+ arg->argument = sieve_argument_create(ast, &namespace_argument, this_ext, 0);
+ arg->argument->data = (void *) var;
+
+ return TRUE;
+}
+
+struct sieve_ast_argument *ext_variables_namespace_argument_create
+(const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_ast_argument *parent_arg,
+ struct sieve_command *cmd, ARRAY_TYPE(sieve_variable_name) *var_name)
+{
+ struct sieve_ast *ast = parent_arg->ast;
+ struct sieve_ast_argument *new_arg;
+
+ new_arg = sieve_ast_argument_create(ast, sieve_ast_argument_line(parent_arg));
+ new_arg->type = SAAT_STRING;
+
+ if ( !ext_variables_namespace_argument_activate
+ (this_ext, valdtr, new_arg, cmd, var_name, FALSE) )
+ return NULL;
+
+ return new_arg;
+}
+
+static bool arg_namespace_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *cmd)
+{
+ struct sieve_argument *argument = arg->argument;
+ struct arg_namespace_variable *var =
+ (struct arg_namespace_variable *) argument->data;
+ const struct sieve_variables_namespace *nspc = var->namespace;
+
+ if ( nspc->def != NULL && nspc->def->generate != NULL )
+ return nspc->def->generate(cgenv, nspc, arg, cmd, var->data);
+
+ return TRUE;
+}
+
+/*
+ * Namespace variable operands
+ */
+
+const struct sieve_operand_class sieve_variables_namespace_operand_class =
+ { "variable-namespace" };
+
+static bool opr_namespace_variable_dump
+ (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd,
+ sieve_size_t *address);
+static int opr_namespace_variable_read
+ (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+ sieve_size_t *address, string_t **str_r);
+
+static const struct sieve_opr_string_interface namespace_variable_interface = {
+ opr_namespace_variable_dump,
+ opr_namespace_variable_read
+};
+
+const struct sieve_operand_def namespace_variable_operand = {
+ .name = "namespace",
+ .ext_def = &variables_extension,
+ .code = EXT_VARIABLES_OPERAND_NAMESPACE_VARIABLE,
+ .class = &string_class,
+ .interface = &namespace_variable_interface
+};
+
+void sieve_variables_opr_namespace_variable_emit
+(struct sieve_binary_block *sblock, const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext,
+ const struct sieve_variables_namespace_def *nspc_def)
+{
+ i_assert( sieve_extension_is(var_ext, variables_extension) );
+ sieve_operand_emit(sblock, var_ext, &namespace_variable_operand);
+ sieve_opr_object_emit(sblock, ext, &nspc_def->obj_def);
+}
+
+static bool opr_namespace_variable_dump
+(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd,
+ sieve_size_t *address)
+{
+ struct sieve_variables_namespace nspc;
+ struct sieve_operand nsoprnd;
+
+ if ( !sieve_operand_read(denv->sblock, address, NULL, &nsoprnd) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_opr_object_read_data
+ (denv->sblock, &nsoprnd, &sieve_variables_namespace_operand_class, address,
+ &nspc.object) ) {
+ return FALSE;
+ }
+
+ nspc.def = (const struct sieve_variables_namespace_def *) nspc.object.def;
+
+ if ( nspc.def == NULL || nspc.def->dump_variable == NULL )
+ return FALSE;
+
+ return nspc.def->dump_variable(denv, &nspc, oprnd, address);
+}
+
+static int opr_namespace_variable_read
+(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+ sieve_size_t *address, string_t **str_r)
+{
+ struct sieve_variables_namespace nspc;
+
+ if ( !sieve_opr_object_read
+ (renv, &sieve_variables_namespace_operand_class, address, &nspc.object) ) {
+ sieve_runtime_trace_operand_error(renv, oprnd,
+ "variable namespace operand corrupt: failed to read");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ nspc.def = (const struct sieve_variables_namespace_def *) nspc.object.def;
+
+ if ( nspc.def == NULL || nspc.def->read_variable == NULL )
+ return SIEVE_EXEC_FAILURE;
+
+ return nspc.def->read_variable(renv, &nspc, oprnd, address, str_r);
+}
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.h
new file mode 100644
index 0000000..a687582
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-namespaces.h
@@ -0,0 +1,43 @@
+#ifndef EXT_VARIABLES_NAMESPACES_H
+#define EXT_VARIABLES_NAMESPACES_H
+
+#include "sieve-common.h"
+
+#include "ext-variables-common.h"
+#include "sieve-ext-variables.h"
+
+/*
+ * Namespace registry
+ */
+
+bool ext_variables_namespace_exists
+ (const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ const char *identifier);
+const struct sieve_variables_namespace *ext_variables_namespace_create_instance
+ (const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ struct sieve_command *cmd, const char *identifier);
+
+void ext_variables_register_core_namespaces
+ (const struct sieve_extension *var_ext,
+ struct ext_variables_validator_context *ctx);
+
+/*
+ * Namespace argument
+ */
+
+struct sieve_ast_argument *ext_variables_namespace_argument_create
+ (const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_ast_argument *parent_arg,
+ struct sieve_command *cmd, ARRAY_TYPE(sieve_variable_name) *var_name);
+bool ext_variables_namespace_argument_activate
+ (const struct sieve_extension *this_ext, struct sieve_validator *valdtr,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd,
+ ARRAY_TYPE(sieve_variable_name) *var_name, bool assignment);
+
+/*
+ * Namespace operand
+ */
+
+extern const struct sieve_operand_def namespace_variable_operand;
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.c
new file mode 100644
index 0000000..359f3bf
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.c
@@ -0,0 +1,279 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "hash.h"
+#include "str.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-ast.h"
+#include "sieve-binary.h"
+#include "sieve-code.h"
+#include "sieve-match-types.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-dump.h"
+#include "sieve-interpreter.h"
+
+#include "ext-variables-common.h"
+#include "ext-variables-limits.h"
+#include "ext-variables-name.h"
+#include "ext-variables-dump.h"
+#include "ext-variables-operands.h"
+
+/*
+ * Variable operand
+ */
+
+static bool opr_variable_dump
+ (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd,
+ sieve_size_t *address);
+static int opr_variable_read_value
+ (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+ sieve_size_t *address, string_t **str_r);
+
+const struct sieve_opr_string_interface variable_interface = {
+ opr_variable_dump,
+ opr_variable_read_value
+};
+
+const struct sieve_operand_def variable_operand = {
+ .name = "variable",
+ .ext_def = &variables_extension,
+ .code = EXT_VARIABLES_OPERAND_VARIABLE,
+ .class = &string_class,
+ .interface = &variable_interface
+};
+
+void sieve_variables_opr_variable_emit
+(struct sieve_binary_block *sblock, const struct sieve_extension *var_ext,
+ struct sieve_variable *var)
+{
+ i_assert( sieve_extension_is(var_ext, variables_extension) );
+
+ if ( var->ext == NULL ) {
+ /* Default variable storage */
+ (void) sieve_operand_emit(sblock, var_ext, &variable_operand);
+ (void) sieve_binary_emit_byte(sblock, 0); /* Default */
+ (void) sieve_binary_emit_unsigned(sblock, var->index);
+ return;
+ }
+
+ (void) sieve_operand_emit(sblock, var_ext, &variable_operand);
+ (void) sieve_binary_emit_extension(sblock, var->ext, 1); /* Extension */
+ (void) sieve_binary_emit_unsigned(sblock, var->index);
+}
+
+static bool opr_variable_dump
+(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd,
+ sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = oprnd->ext;
+ unsigned int index = 0;
+ const struct sieve_extension *ext;
+ unsigned int code = 1; /* Initially set to offset value */
+ const char *identifier;
+
+ if ( !sieve_binary_read_extension(denv->sblock, address, &code, &ext) )
+ return FALSE;
+
+ if ( !sieve_binary_read_unsigned(denv->sblock, address, &index) )
+ return FALSE;
+
+ identifier = ext_variables_dump_get_identifier(this_ext, denv, ext, index);
+ identifier = identifier == NULL ? "??" : identifier;
+
+ if ( oprnd->field_name != NULL )
+ sieve_code_dumpf(denv, "%s: VAR[%s] ${%s}",
+ oprnd->field_name, sieve_ext_variables_get_varid(ext, index), identifier);
+ else
+ sieve_code_dumpf(denv, "VAR[%s] ${%s}",
+ sieve_ext_variables_get_varid(ext, index), identifier);
+
+ return TRUE;
+}
+
+static int opr_variable_read_value
+(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+ sieve_size_t *address, string_t **str_r)
+{
+ const struct sieve_extension *this_ext = oprnd->ext;
+ const struct sieve_extension *ext;
+ unsigned int code = 1; /* Initially set to offset value */
+ struct sieve_variable_storage *storage;
+ unsigned int index = 0;
+
+ if ( !sieve_binary_read_extension(renv->sblock, address, &code, &ext) ) {
+ sieve_runtime_trace_operand_error(renv, oprnd,
+ "variable operand corrupt: invalid extension byte");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ storage = sieve_ext_variables_runtime_get_storage
+ (this_ext, renv, ext);
+ if ( storage == NULL ) {
+ sieve_runtime_trace_operand_error(renv, oprnd,
+ "variable operand corrupt: extension has no storage");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( sieve_binary_read_unsigned(renv->sblock, address, &index) ) {
+ /* Parameter str can be NULL if we are requested to only skip and not
+ * actually read the argument.
+ */
+ if ( str_r != NULL ) {
+ if ( !sieve_variable_get(storage, index, str_r) )
+ return SIEVE_EXEC_FAILURE;
+
+ if ( *str_r == NULL ) *str_r = t_str_new(0);
+ }
+
+ return SIEVE_EXEC_OK;
+ }
+
+ sieve_runtime_trace_operand_error(renv, oprnd,
+ "variable operand corrupt: invalid variable index");
+ return SIEVE_EXEC_BIN_CORRUPT;
+}
+
+int sieve_variable_operand_read_data
+(const struct sieve_runtime_env *renv, struct sieve_operand *oprnd,
+ sieve_size_t *address, const char *field_name,
+ struct sieve_variable_storage **storage_r, unsigned int *var_index_r)
+{
+ const struct sieve_extension *ext;
+ unsigned int code = 1; /* Initially set to offset value */
+ unsigned int idx = 0;
+
+ oprnd->field_name = field_name;
+
+ if ( !sieve_operand_is_variable(oprnd) ) {
+ sieve_runtime_trace_operand_error(renv, oprnd,
+ "expected variable operand but found %s", sieve_operand_name(oprnd));
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( !sieve_binary_read_extension(renv->sblock, address, &code, &ext) ) {
+ sieve_runtime_trace_operand_error(renv, oprnd,
+ "variable operand corrupt: invalid extension byte");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ *storage_r = sieve_ext_variables_runtime_get_storage
+ (oprnd->ext, renv, ext);
+ if ( *storage_r == NULL ) {
+ sieve_runtime_trace_operand_error(renv, oprnd,
+ "variable operand corrupt: extension has no storage");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( !sieve_binary_read_unsigned(renv->sblock, address, &idx) ) {
+ sieve_runtime_trace_operand_error(renv, oprnd,
+ "variable operand corrupt: invalid variable index");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ *var_index_r = idx;
+ return SIEVE_EXEC_OK;
+}
+
+int sieve_variable_operand_read
+(const struct sieve_runtime_env *renv, sieve_size_t *address,
+ const char *field_name, struct sieve_variable_storage **storage_r,
+ unsigned int *var_index_r)
+{
+ struct sieve_operand operand;
+ int ret;
+
+ if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand))
+ <= 0)
+ return ret;
+
+ return sieve_variable_operand_read_data
+ (renv, &operand, address, field_name, storage_r, var_index_r);
+}
+
+/*
+ * Match value operand
+ */
+
+static bool opr_match_value_dump
+ (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd,
+ sieve_size_t *address);
+static int opr_match_value_read
+ (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+ sieve_size_t *address, string_t **str_r);
+
+const struct sieve_opr_string_interface match_value_interface = {
+ opr_match_value_dump,
+ opr_match_value_read
+};
+
+const struct sieve_operand_def match_value_operand = {
+ .name = "match-value",
+ .ext_def = &variables_extension,
+ .code = EXT_VARIABLES_OPERAND_MATCH_VALUE,
+ .class = &string_class,
+ .interface = &match_value_interface
+};
+
+void sieve_variables_opr_match_value_emit
+(struct sieve_binary_block *sblock, const struct sieve_extension *var_ext,
+ unsigned int index)
+{
+ i_assert( sieve_extension_is(var_ext, variables_extension) );
+ (void) sieve_operand_emit(sblock, var_ext, &match_value_operand);
+ (void) sieve_binary_emit_unsigned(sblock, index);
+}
+
+static bool opr_match_value_dump
+(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd,
+ sieve_size_t *address)
+{
+ unsigned int index = 0;
+
+ if (sieve_binary_read_unsigned(denv->sblock, address, &index) ) {
+ if ( oprnd->field_name != NULL )
+ sieve_code_dumpf
+ (denv, "%s: MATCHVAL %lu", oprnd->field_name, (unsigned long) index);
+ else
+ sieve_code_dumpf(denv, "MATCHVAL %lu", (unsigned long) index);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int opr_match_value_read
+(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
+ sieve_size_t *address, string_t **str_r)
+{
+ const struct sieve_extension *this_ext = oprnd->ext;
+ const struct ext_variables_config *config =
+ ext_variables_get_config(this_ext);
+ unsigned int index = 0;
+
+ if ( sieve_binary_read_unsigned(renv->sblock, address, &index) ) {
+ /* Parameter str can be NULL if we are requested to only skip and not
+ * actually read the argument.
+ */
+ if ( str_r != NULL ) {
+ sieve_match_values_get(renv, index, str_r);
+
+ if ( *str_r == NULL )
+ *str_r = t_str_new(0);
+ else if ( str_len(*str_r) > config->max_variable_size )
+ str_truncate_utf8(*str_r, config->max_variable_size);
+ }
+
+ return SIEVE_EXEC_OK;
+ }
+
+ sieve_runtime_trace_operand_error(renv, oprnd,
+ "match value operand corrupt: invalid index data");
+ return SIEVE_EXEC_BIN_CORRUPT;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.h b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.h
new file mode 100644
index 0000000..64a7781
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.h
@@ -0,0 +1,37 @@
+#ifndef EXT_VARIABLES_OPERANDS_H
+#define EXT_VARIABLES_OPERANDS_H
+
+#include "lib.h"
+#include "hash.h"
+#include "str.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "ext-variables-common.h"
+
+/*
+ * Variable operand
+ */
+
+extern const struct sieve_operand_def variable_operand;
+
+bool ext_variables_opr_variable_read
+ (const struct sieve_runtime_env *renv, sieve_size_t *address,
+ struct sieve_variable_storage **storage, unsigned int *var_index);
+
+/*
+ * Match value operand
+ */
+
+extern const struct sieve_operand_def match_value_operand;
+
+/*
+ * Variable string operand
+ */
+
+void ext_variables_opr_variable_string_emit
+ (struct sieve_binary *sbin, unsigned int elements);
+
+
+#endif
+
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables.c
new file mode 100644
index 0000000..6bdef58
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables.c
@@ -0,0 +1,84 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension variables
+ * -------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5229
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "unichar.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+
+#include "sieve-validator.h"
+
+#include "ext-variables-common.h"
+#include "ext-variables-arguments.h"
+#include "ext-variables-operands.h"
+#include "ext-variables-namespaces.h"
+#include "ext-variables-modifiers.h"
+#include "ext-variables-dump.h"
+
+/*
+ * Operations
+ */
+
+const struct sieve_operation_def *ext_variables_operations[] = {
+ &cmd_set_operation,
+ &tst_string_operation
+};
+
+/*
+ * Operands
+ */
+
+const struct sieve_operand_def *ext_variables_operands[] = {
+ &variable_operand,
+ &match_value_operand,
+ &namespace_variable_operand,
+ &modifier_operand
+};
+
+/*
+ * Extension
+ */
+
+static bool ext_variables_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *validator);
+
+const struct sieve_extension_def variables_extension = {
+ .name = "variables",
+ .load = ext_variables_load,
+ .unload = ext_variables_unload,
+ .validator_load = ext_variables_validator_load,
+ .generator_load = ext_variables_generator_load,
+ .interpreter_load = ext_variables_interpreter_load,
+ .code_dump = ext_variables_code_dump,
+ SIEVE_EXT_DEFINE_OPERATIONS(ext_variables_operations),
+ SIEVE_EXT_DEFINE_OPERANDS(ext_variables_operands)
+};
+
+static bool ext_variables_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *validator)
+{
+ sieve_validator_argument_override
+ (validator, SAT_VAR_STRING, ext, &variable_string_argument);
+
+ sieve_validator_register_command(validator, ext, &cmd_set);
+ sieve_validator_register_command(validator, ext, &tst_string);
+
+ ext_variables_validator_initialize(ext, validator);
+
+ return TRUE;
+}
+
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/sieve-ext-variables.h b/pigeonhole/src/lib-sieve/plugins/variables/sieve-ext-variables.h
new file mode 100644
index 0000000..ce0d0bc
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/sieve-ext-variables.h
@@ -0,0 +1,364 @@
+#ifndef SIEVE_EXT_VARIABLES_H
+#define SIEVE_EXT_VARIABLES_H
+
+#include "lib.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-objects.h"
+#include "sieve-code.h"
+
+/* Public interface for other extensions to use
+ */
+
+/*
+ * Limits
+ */
+
+unsigned int
+sieve_variables_get_max_scope_size(const struct sieve_extension *var_ext);
+size_t
+sieve_variables_get_max_variable_size(const struct sieve_extension *var_ext);
+
+/*
+ * Variable extension
+ */
+
+/* FIXME: this is not suitable for future plugin support */
+
+extern const struct sieve_extension_def variables_extension;
+
+static inline const struct sieve_extension *sieve_ext_variables_get_extension
+(struct sieve_instance *svinst)
+{
+ return sieve_extension_register(svinst, &variables_extension, FALSE);
+}
+
+/*
+ * Variable name
+ */
+
+struct sieve_variable_name {
+ string_t *identifier;
+ int num_variable;
+};
+
+ARRAY_DEFINE_TYPE(sieve_variable_name, struct sieve_variable_name);
+
+bool sieve_variable_identifier_is_valid(const char *identifier);
+
+/*
+ * Variable scope
+ */
+
+struct sieve_variable {
+ const char *identifier;
+ unsigned int index;
+
+ const struct sieve_extension *ext;
+ void *context;
+};
+
+struct sieve_variable_scope;
+
+struct sieve_variable_scope *sieve_variable_scope_create
+ (struct sieve_instance *svinst, const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext);
+void sieve_variable_scope_ref
+ (struct sieve_variable_scope *scope);
+void sieve_variable_scope_unref
+ (struct sieve_variable_scope **scope);
+pool_t sieve_variable_scope_pool
+ (struct sieve_variable_scope *scope);
+
+struct sieve_variable *sieve_variable_scope_declare
+ (struct sieve_variable_scope *scope, const char *identifier);
+struct sieve_variable *sieve_variable_scope_import
+ (struct sieve_variable_scope *scope, struct sieve_variable *var);
+struct sieve_variable *sieve_variable_scope_get_variable
+ (struct sieve_variable_scope *scope, const char *identifier);
+struct sieve_variable *sieve_variable_scope_get_indexed
+ (struct sieve_variable_scope *scope, unsigned int index);
+
+/* Binary */
+
+struct sieve_variable_scope_binary *sieve_variable_scope_binary_create
+ (struct sieve_variable_scope *scope);
+
+void sieve_variable_scope_binary_ref
+ (struct sieve_variable_scope_binary *scpbin);
+void sieve_variable_scope_binary_unref
+ (struct sieve_variable_scope_binary **scpbin);
+
+struct sieve_variable_scope *sieve_variable_scope_binary_dump
+ (struct sieve_instance *svinst,
+ const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext,
+ const struct sieve_dumptime_env *denv, sieve_size_t *address);
+struct sieve_variable_scope_binary *sieve_variable_scope_binary_read
+ (struct sieve_instance *svinst,
+ const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext,
+ struct sieve_binary_block *sblock, sieve_size_t *address);
+
+struct sieve_variable_scope *sieve_variable_scope_binary_get
+ (struct sieve_variable_scope_binary *scpbin);
+unsigned int sieve_variable_scope_binary_get_size
+ (struct sieve_variable_scope_binary *scpbin);
+
+/*
+ * Variable namespaces
+ */
+
+struct sieve_variables_namespace;
+
+struct sieve_variables_namespace_def {
+ struct sieve_object_def obj_def;
+
+ bool (*validate)
+ (struct sieve_validator *valdtr,
+ const struct sieve_variables_namespace *nspc,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd,
+ ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data,
+ bool assignment);
+ bool (*generate)
+ (const struct sieve_codegen_env *cgenv,
+ const struct sieve_variables_namespace *nspc,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd,
+ void *var_data);
+
+ bool (*dump_variable)
+ (const struct sieve_dumptime_env *denv,
+ const struct sieve_variables_namespace *nspc,
+ const struct sieve_operand *oprnd, sieve_size_t *address);
+ int (*read_variable)
+ (const struct sieve_runtime_env *renv,
+ const struct sieve_variables_namespace *nspc,
+ const struct sieve_operand *oprnd, sieve_size_t *address, string_t **str);
+};
+
+#define SIEVE_VARIABLES_DEFINE_NAMESPACE(OP) SIEVE_EXT_DEFINE_OBJECT(OP)
+#define SIEVE_VARIABLES_DEFINE_NAMESPACES(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS)
+
+struct sieve_variables_namespace {
+ struct sieve_object object;
+
+ const struct sieve_variables_namespace_def *def;
+};
+
+void sieve_variables_namespace_register
+(const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ const struct sieve_variables_namespace_def *nspc_def);
+
+extern const struct sieve_operand_class sieve_variables_namespace_operand_class;
+
+void sieve_variables_opr_namespace_variable_emit
+ (struct sieve_binary_block *sblock, const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext,
+ const struct sieve_variables_namespace_def *nspc_def);
+
+/* Iteration over all declared variables */
+
+struct sieve_variable_scope_iter;
+
+struct sieve_variable_scope_iter *sieve_variable_scope_iterate_init
+ (struct sieve_variable_scope *scope);
+bool sieve_variable_scope_iterate
+ (struct sieve_variable_scope_iter *iter, struct sieve_variable **var_r);
+void sieve_variable_scope_iterate_deinit
+ (struct sieve_variable_scope_iter **iter);
+
+/* Statistics */
+
+unsigned int sieve_variable_scope_declarations
+ (struct sieve_variable_scope *scope);
+unsigned int sieve_variable_scope_size
+ (struct sieve_variable_scope *scope);
+
+/* Get all native variables */
+
+struct sieve_variable * const *sieve_variable_scope_get_variables
+ (struct sieve_variable_scope *scope, unsigned int *size_r);
+
+/*
+ * Variable storage
+ */
+
+struct sieve_variable_storage;
+
+struct sieve_variable_storage *sieve_variable_storage_create
+ (const struct sieve_extension *var_ext, pool_t pool,
+ struct sieve_variable_scope_binary *scpbin);
+bool sieve_variable_get
+ (struct sieve_variable_storage *storage, unsigned int index,
+ string_t **value);
+bool sieve_variable_get_modifiable
+ (struct sieve_variable_storage *storage, unsigned int index,
+ string_t **value);
+bool sieve_variable_assign
+ (struct sieve_variable_storage *storage, unsigned int index,
+ const string_t *value);
+bool sieve_variable_assign_cstr
+ (struct sieve_variable_storage *storage, unsigned int index,
+ const char *value);
+bool sieve_variable_get_identifier
+ (struct sieve_variable_storage *storage, unsigned int index,
+ const char **identifier);
+const char *sieve_variable_get_varid
+ (struct sieve_variable_storage *storage, unsigned int index);
+
+/*
+ * Variables access
+ */
+
+bool sieve_ext_variables_is_active
+ (const struct sieve_extension *var_ext, struct sieve_validator *valdtr);
+
+struct sieve_variable_scope *sieve_ext_variables_get_local_scope
+ (const struct sieve_extension *var_ext, struct sieve_validator *valdtr);
+
+/* Runtime */
+
+static inline const char *sieve_ext_variables_get_varid
+(const struct sieve_extension *ext, unsigned int index)
+{
+ if ( ext == NULL )
+ return t_strdup_printf("%ld", (long) index);
+
+ return t_strdup_printf("%s:%ld", sieve_extension_name(ext), (long) index);
+}
+
+struct sieve_variable_storage *sieve_ext_variables_runtime_get_storage
+ (const struct sieve_extension *var_ext, const struct sieve_runtime_env *renv,
+ const struct sieve_extension *ext);
+void sieve_ext_variables_runtime_set_storage
+ (const struct sieve_extension *var_ext, const struct sieve_runtime_env *renv,
+ const struct sieve_extension *ext, struct sieve_variable_storage *storage);
+
+const char *sieve_ext_variables_runtime_get_identifier
+(const struct sieve_extension *var_ext, const struct sieve_runtime_env *renv,
+ const struct sieve_extension *ext, unsigned int index);
+
+/*
+ * Variable arguments
+ */
+
+bool sieve_variable_argument_activate
+ (const struct sieve_extension *var_ext,
+ const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_command *cmd,
+ struct sieve_ast_argument *arg, bool assignment);
+
+/*
+ * Variable operands
+ */
+
+extern const struct sieve_operand_def variable_operand;
+
+void sieve_variables_opr_variable_emit
+ (struct sieve_binary_block *sblock, const struct sieve_extension *var_ext,
+ struct sieve_variable *var);
+void sieve_variables_opr_match_value_emit
+ (struct sieve_binary_block *sblock, const struct sieve_extension *var_ext,
+ unsigned int index);
+
+int sieve_variable_operand_read_data
+ (const struct sieve_runtime_env *renv, struct sieve_operand *operand,
+ sieve_size_t *address, const char *field_name,
+ struct sieve_variable_storage **storage_r, unsigned int *var_index_r);
+int sieve_variable_operand_read
+ (const struct sieve_runtime_env *renv, sieve_size_t *address,
+ const char *field_name, struct sieve_variable_storage **storage_r,
+ unsigned int *var_index_r);
+
+static inline bool sieve_operand_is_variable
+(const struct sieve_operand *operand)
+{
+ return ( operand != NULL && operand->def != NULL &&
+ operand->def == &variable_operand );
+}
+
+/*
+ * Modifiers
+ */
+
+/* Definition */
+
+struct sieve_variables_modifier;
+
+struct sieve_variables_modifier_def {
+ struct sieve_object_def obj_def;
+
+ unsigned int precedence;
+
+ bool (*modify)(const struct sieve_variables_modifier *modf,
+ string_t *in, string_t **result);
+};
+
+struct sieve_variables_modifier {
+ struct sieve_object object;
+ const struct sieve_extension *var_ext;
+
+ const struct sieve_variables_modifier_def *def;
+};
+
+#define SIEVE_VARIABLES_DEFINE_MODIFIER(OP) SIEVE_EXT_DEFINE_OBJECT(OP)
+#define SIEVE_VARIABLES_DEFINE_MODIFIERS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS)
+
+#define sieve_variables_modifier_name(smodf) \
+ ( (smodf)->object.def->identifier )
+
+ARRAY_DEFINE_TYPE(sieve_variables_modifier,
+ struct sieve_variables_modifier);
+
+/* Registry */
+
+void sieve_variables_modifier_register
+ (const struct sieve_extension *var_ext, struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ const struct sieve_variables_modifier_def *smodf);
+
+/* Tagged argument */
+
+void sieve_variables_modifiers_link_tag
+ (struct sieve_validator *valdtr, const struct sieve_extension *var_ext,
+ struct sieve_command_registration *cmd_reg);
+
+bool sieve_variables_modifiers_validate
+ (struct sieve_validator *valdtr, struct sieve_command *cmd,
+ ARRAY_TYPE(sieve_variables_modifier) *modifiers);
+
+bool sieve_variables_modifiers_generate
+ (const struct sieve_codegen_env *cgenv,
+ ARRAY_TYPE(sieve_variables_modifier) *modifiers);
+
+/* Coding */
+
+extern const struct sieve_operand_class
+ sieve_variables_modifier_operand_class;
+
+bool sieve_variables_modifiers_code_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+int sieve_variables_modifiers_code_read(
+ const struct sieve_runtime_env *renv,
+ const struct sieve_extension *var_ext, sieve_size_t *address,
+ ARRAY_TYPE(sieve_variables_modifier) *modifiers);
+
+/* Application */
+
+int sieve_variables_modifiers_apply
+(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *var_ext,
+ ARRAY_TYPE(sieve_variables_modifier) *modifiers,
+ string_t **value);
+
+/*
+ * Code dumping
+ */
+
+void sieve_ext_variables_dump_set_scope
+(const struct sieve_extension *var_ext, const struct sieve_dumptime_env *denv,
+ const struct sieve_extension *ext, struct sieve_variable_scope *scope);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/tst-string.c b/pigeonhole/src/lib-sieve/plugins/variables/tst-string.c
new file mode 100644
index 0000000..91b0b82
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/tst-string.c
@@ -0,0 +1,271 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-variables-common.h"
+
+/*
+ * String test
+ *
+ * Syntax:
+ * string [COMPARATOR] [MATCH-TYPE]
+ * <source: string-list> <key-list: string-list>
+ */
+
+static bool tst_string_registered
+ (struct sieve_validator *valdtr, const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool tst_string_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool tst_string_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def tst_string = {
+ .identifier = "string",
+ .type = SCT_TEST,
+ .positional_args = 2,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = tst_string_registered,
+ .validate = tst_string_validate,
+ .generate = tst_string_generate
+};
+
+/*
+ * String operation
+ */
+
+static bool tst_string_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_string_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def tst_string_operation = {
+ .mnemonic = "STRING",
+ .ext_def = &variables_extension,
+ .code = EXT_VARIABLES_OPERATION_STRING,
+ .dump = tst_string_operation_dump,
+ .execute = tst_string_operation_execute
+};
+
+/*
+ * Optional arguments
+ */
+
+enum tst_string_optional {
+ OPT_END,
+ OPT_COMPARATOR,
+ OPT_MATCH_TYPE
+};
+
+/*
+ * Test registration
+ */
+
+static bool tst_string_registered
+(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_command_registration *cmd_reg)
+{
+ /* The order of these is not significant */
+ sieve_comparators_link_tag(valdtr, cmd_reg, OPT_COMPARATOR);
+ sieve_match_types_link_tags(valdtr, cmd_reg, OPT_MATCH_TYPE);
+
+ return TRUE;
+}
+
+/*
+ * Test validation
+ */
+
+static bool tst_string_validate
+(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ const struct sieve_match_type mcht_default =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ const struct sieve_comparator cmp_default =
+ SIEVE_COMPARATOR_DEFAULT(i_octet_comparator);
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "source", 1, SAAT_STRING_LIST) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ arg = sieve_ast_argument_next(arg);
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) {
+ return FALSE;
+ }
+
+ if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+ return FALSE;
+
+ /* Validate the key argument to a specified match type */
+ return sieve_match_type_validate
+ (valdtr, tst, arg, &mcht_default, &cmp_default);
+}
+
+/*
+ * Test generation
+ */
+
+static bool tst_string_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &tst_string_operation);
+
+ /* Generate arguments */
+ if ( !sieve_generate_arguments(cgenv, cmd, NULL) )
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Code dump
+ */
+
+static bool tst_string_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ sieve_code_dumpf(denv, "STRING-TEST");
+ sieve_code_descend(denv);
+
+ /* Optional operands */
+ if ( sieve_match_opr_optional_dump(denv, address, NULL) != 0 )
+ return FALSE;
+
+ return
+ sieve_opr_stringlist_dump(denv, address, "source") &&
+ sieve_opr_stringlist_dump(denv, address, "key list");
+}
+
+/*
+ * Code execution
+ */
+
+static int tst_string_stringlist_next_item
+ (struct sieve_stringlist *_strlist, string_t **str_r);
+static void tst_string_stringlist_reset
+ (struct sieve_stringlist *_strlist);
+static int tst_string_stringlist_get_length
+ (struct sieve_stringlist *_strlist);
+
+struct tst_string_stringlist {
+ struct sieve_stringlist strlist;
+
+ struct sieve_stringlist *value_list;
+};
+
+static struct sieve_stringlist *tst_string_stringlist_create
+(const struct sieve_runtime_env *renv, struct sieve_stringlist *value_list)
+{
+ struct tst_string_stringlist *strlist;
+
+ strlist = t_new(struct tst_string_stringlist, 1);
+ strlist->strlist.runenv = renv;
+ strlist->strlist.exec_status = SIEVE_EXEC_OK;
+ strlist->strlist.next_item = tst_string_stringlist_next_item;
+ strlist->strlist.reset = tst_string_stringlist_reset;
+ strlist->strlist.get_length = tst_string_stringlist_get_length;
+ strlist->value_list = value_list;
+
+ return &strlist->strlist;
+}
+
+static int tst_string_stringlist_next_item
+(struct sieve_stringlist *_strlist, string_t **str_r)
+{
+ struct tst_string_stringlist *strlist =
+ (struct tst_string_stringlist *)_strlist;
+
+ return sieve_stringlist_next_item(strlist->value_list, str_r);
+}
+
+static void tst_string_stringlist_reset
+(struct sieve_stringlist *_strlist)
+{
+ struct tst_string_stringlist *strlist =
+ (struct tst_string_stringlist *)_strlist;
+
+ sieve_stringlist_reset(strlist->value_list);
+}
+
+static int tst_string_stringlist_get_length
+(struct sieve_stringlist *_strlist)
+{
+ struct tst_string_stringlist *strlist =
+ (struct tst_string_stringlist *)_strlist;
+ string_t *item;
+ int length = 0;
+ int ret;
+
+ while ( (ret=sieve_stringlist_next_item(strlist->value_list, &item)) > 0 ) {
+ if ( str_len(item) > 0 )
+ length++;
+ }
+
+ return ( ret < 0 ? -1 : length );
+}
+
+static int tst_string_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ struct sieve_match_type mcht =
+ SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
+ struct sieve_comparator cmp =
+ SIEVE_COMPARATOR_DEFAULT(i_octet_comparator);
+ struct sieve_stringlist *source, *value_list, *key_list;
+ int match, ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Handle match-type and comparator operands */
+ if ( sieve_match_opr_optional_read
+ (renv, address, NULL, &ret, &cmp, &mcht) < 0 )
+ return ret;
+
+ /* Read source */
+ if ( (ret=sieve_opr_stringlist_read(renv, address, "source", &source)) <= 0 )
+ return ret;
+
+ /* Read key-list */
+ if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list))
+ <= 0 )
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "string test");
+
+ /* Create wrapper string list wich does not count empty string items */
+ value_list = tst_string_stringlist_create(renv, source);
+
+ /* Perform match */
+ if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 )
+ return ret;
+
+ /* Set test result for subsequent conditional jump */
+ sieve_interpreter_set_test_result(renv->interp, match > 0);
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.am b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.am
new file mode 100644
index 0000000..f409e2b
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = debug environment report
+
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.in b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.in
new file mode 100644
index 0000000..43680f1
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/Makefile.in
@@ -0,0 +1,692 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/vnd.dovecot
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = debug environment report
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am clean clean-generic clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.am b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.am
new file mode 100644
index 0000000..1e08946
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.am
@@ -0,0 +1,15 @@
+noinst_LTLIBRARIES = libsieve_ext_debug.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-debug-log.c
+
+libsieve_ext_debug_la_SOURCES = \
+ $(commands) \
+ ext-debug.c
+
+noinst_HEADERS = \
+ ext-debug-common.h
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.in b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.in
new file mode 100644
index 0000000..f9b5450
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/Makefile.in
@@ -0,0 +1,687 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/vnd.dovecot/debug
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_debug_la_LIBADD =
+am__objects_1 = cmd-debug-log.lo
+am_libsieve_ext_debug_la_OBJECTS = $(am__objects_1) ext-debug.lo
+libsieve_ext_debug_la_OBJECTS = $(am_libsieve_ext_debug_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-debug-log.Plo \
+ ./$(DEPDIR)/ext-debug.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_debug_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_debug_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_debug.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../../.. \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-debug-log.c
+
+libsieve_ext_debug_la_SOURCES = \
+ $(commands) \
+ ext-debug.c
+
+noinst_HEADERS = \
+ ext-debug-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/debug/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/debug/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_debug.la: $(libsieve_ext_debug_la_OBJECTS) $(libsieve_ext_debug_la_DEPENDENCIES) $(EXTRA_libsieve_ext_debug_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_debug_la_OBJECTS) $(libsieve_ext_debug_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-debug-log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-debug.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmd-debug-log.Plo
+ -rm -f ./$(DEPDIR)/ext-debug.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmd-debug-log.Plo
+ -rm -f ./$(DEPDIR)/ext-debug.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/cmd-debug-log.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/cmd-debug-log.c
new file mode 100644
index 0000000..355daa5
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/cmd-debug-log.c
@@ -0,0 +1,130 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-debug-common.h"
+
+/*
+ * Debug_log command
+ *
+ * Syntax
+ * debug_log <message: string>
+ */
+
+static bool cmd_debug_log_validate
+ (struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool cmd_debug_log_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
+
+const struct sieve_command_def debug_log_command = {
+ .identifier = "debug_log",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = cmd_debug_log_validate,
+ .generate = cmd_debug_log_generate
+};
+
+/*
+ * Body operation
+ */
+
+static bool cmd_debug_log_operation_dump
+ (const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int cmd_debug_log_operation_execute
+ (const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation_def debug_log_operation = {
+ .mnemonic = "DEBUG_LOG",
+ .ext_def = &vnd_debug_extension,
+ .dump = cmd_debug_log_operation_dump,
+ .execute = cmd_debug_log_operation_execute
+};
+
+/*
+ * Validation
+ */
+
+static bool cmd_debug_log_validate
+(struct sieve_validator *valdtr, struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+
+ if ( !sieve_validate_positional_argument
+ (valdtr, tst, arg, "message", 1, SAAT_STRING) ) {
+ return FALSE;
+ }
+
+ return sieve_validator_argument_activate(valdtr, tst, arg, FALSE);
+}
+
+/*
+ * Code generation
+ */
+
+static bool cmd_debug_log_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
+{
+ (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &debug_log_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool cmd_debug_log_operation_dump
+(const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+ sieve_code_dumpf(denv, "DEBUG_LOG");
+ sieve_code_descend(denv);
+
+ return sieve_opr_string_dump(denv, address, "key list");
+}
+
+/*
+ * Interpretation
+ */
+
+static int cmd_debug_log_operation_execute
+(const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+ string_t *message;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Read message */
+
+ if ( (ret=sieve_opr_string_read(renv, address, "message", &message)) <= 0 )
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "debug_log \"%s\"",
+ str_sanitize(str_c(message), 80));
+
+ sieve_runtime_log(renv, NULL, "DEBUG: %s", str_c(message));
+
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug-common.h b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug-common.h
new file mode 100644
index 0000000..88ae3db
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug-common.h
@@ -0,0 +1,22 @@
+#ifndef EXT_DEBUG_COMMON_H
+#define EXT_DEBUG_COMMON_H
+
+/*
+ * Extensions
+ */
+
+extern const struct sieve_extension_def vnd_debug_extension;
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def debug_log_command;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def debug_log_operation;
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c
new file mode 100644
index 0000000..2781e83
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/debug/ext-debug.c
@@ -0,0 +1,70 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension debug
+ * ---------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: vendor-defined; spec-bosch-sieve-debug
+ * Implementation: full
+ * Status: experimental
+ *
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-debug-common.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_debug_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *validator);
+static bool ext_debug_interpreter_load
+ (const struct sieve_extension *ext ATTR_UNUSED,
+ const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED);
+
+
+const struct sieve_extension_def vnd_debug_extension = {
+ .name = "vnd.dovecot.debug",
+ .validator_load = ext_debug_validator_load,
+ .interpreter_load = ext_debug_interpreter_load,
+ SIEVE_EXT_DEFINE_OPERATION(debug_log_operation),
+};
+
+static bool ext_debug_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *validator)
+{
+ /* Register new test */
+ sieve_validator_register_command(validator, ext, &debug_log_command);
+
+ return TRUE;
+}
+
+static bool ext_debug_interpreter_load
+(const struct sieve_extension *ext ATTR_UNUSED,
+ const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED)
+{
+ if ( renv->ehandler != NULL ) {
+ sieve_error_handler_accept_infolog(renv->ehandler, TRUE);
+ }
+
+ return TRUE;
+}
+
+
+
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.am b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.am
new file mode 100644
index 0000000..96618ff
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.am
@@ -0,0 +1,16 @@
+noinst_LTLIBRARIES = libsieve_ext_vnd_environment.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../../.. \
+ -I$(srcdir)/../../environment \
+ -I$(srcdir)/../../variables \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_vnd_environment_la_SOURCES = \
+ ext-vnd-environment.c \
+ ext-vnd-environment-items.c \
+ ext-vnd-environment-variables.c
+
+noinst_HEADERS = \
+ ext-vnd-environment-common.h
+
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.in b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.in
new file mode 100644
index 0000000..b5d8b20
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/Makefile.in
@@ -0,0 +1,692 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/vnd.dovecot/environment
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_vnd_environment_la_LIBADD =
+am_libsieve_ext_vnd_environment_la_OBJECTS = ext-vnd-environment.lo \
+ ext-vnd-environment-items.lo ext-vnd-environment-variables.lo
+libsieve_ext_vnd_environment_la_OBJECTS = \
+ $(am_libsieve_ext_vnd_environment_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ext-vnd-environment-items.Plo \
+ ./$(DEPDIR)/ext-vnd-environment-variables.Plo \
+ ./$(DEPDIR)/ext-vnd-environment.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_vnd_environment_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_vnd_environment_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_vnd_environment.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../../.. \
+ -I$(srcdir)/../../environment \
+ -I$(srcdir)/../../variables \
+ $(LIBDOVECOT_INCLUDE)
+
+libsieve_ext_vnd_environment_la_SOURCES = \
+ ext-vnd-environment.c \
+ ext-vnd-environment-items.c \
+ ext-vnd-environment-variables.c
+
+noinst_HEADERS = \
+ ext-vnd-environment-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/environment/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/environment/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_vnd_environment.la: $(libsieve_ext_vnd_environment_la_OBJECTS) $(libsieve_ext_vnd_environment_la_DEPENDENCIES) $(EXTRA_libsieve_ext_vnd_environment_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_vnd_environment_la_OBJECTS) $(libsieve_ext_vnd_environment_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vnd-environment-items.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vnd-environment-variables.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vnd-environment.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ext-vnd-environment-items.Plo
+ -rm -f ./$(DEPDIR)/ext-vnd-environment-variables.Plo
+ -rm -f ./$(DEPDIR)/ext-vnd-environment.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ext-vnd-environment-items.Plo
+ -rm -f ./$(DEPDIR)/ext-vnd-environment-variables.Plo
+ -rm -f ./$(DEPDIR)/ext-vnd-environment.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-common.h b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-common.h
new file mode 100644
index 0000000..980c830
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-common.h
@@ -0,0 +1,37 @@
+#ifndef EXT_VND_ENVIRONMENT_COMMON_H
+#define EXT_VND_ENVIRONMENT_COMMON_H
+
+#include "sieve-ext-environment.h"
+
+/*
+ * Extension
+ */
+
+struct ext_vnd_environment_context {
+ const struct sieve_extension *env_ext;
+ const struct sieve_extension *var_ext;
+};
+
+extern const struct sieve_extension_def vnd_environment_extension;
+
+/*
+ * Operands
+ */
+
+extern const struct sieve_operand_def environment_namespace_operand;
+
+/*
+ * Environment items
+ */
+
+void ext_vnd_environment_items_register
+(const struct sieve_extension *ext, const struct sieve_runtime_env *renv);
+
+/*
+ * Variables
+ */
+
+void ext_environment_variables_init
+(const struct sieve_extension *this_ext, struct sieve_validator *valdtr);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c
new file mode 100644
index 0000000..af03255
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-items.c
@@ -0,0 +1,95 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-settings.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-vnd-environment-common.h"
+
+/*
+ * Environment items
+ */
+
+/* default_mailbox */
+
+static const char *
+envit_default_mailbox_get_value(const struct sieve_runtime_env *renv,
+ const char *name ATTR_UNUSED)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+
+ i_assert(eenv->scriptenv->default_mailbox != NULL);
+ return eenv->scriptenv->default_mailbox;
+}
+
+const struct sieve_environment_item default_mailbox_env_item = {
+ .name = "vnd.dovecot.default-mailbox",
+ .get_value = envit_default_mailbox_get_value
+};
+
+/* username */
+
+static const char *
+envit_username_get_value(const struct sieve_runtime_env *renv,
+ const char *name ATTR_UNUSED)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+
+ return eenv->svinst->username;
+}
+
+const struct sieve_environment_item username_env_item = {
+ .name = "vnd.dovecot.username",
+ .get_value = envit_username_get_value
+};
+
+/* config.* */
+
+static const char *
+envit_config_get_value(const struct sieve_runtime_env *renv, const char *name)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+
+ if (*name == '\0')
+ return NULL;
+
+ return sieve_setting_get(eenv->svinst,
+ t_strconcat("sieve_env_", name, NULL));
+}
+
+const struct sieve_environment_item config_env_item = {
+ .name = "vnd.dovecot.config",
+ .prefix = TRUE,
+ .get_value = envit_config_get_value
+};
+
+/*
+ * Register
+ */
+
+void ext_vnd_environment_items_register(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv)
+{
+ struct ext_vnd_environment_context *ectx =
+ (struct ext_vnd_environment_context *)ext->context;
+
+ sieve_environment_item_register(ectx->env_ext, renv->interp,
+ &default_mailbox_env_item);
+ sieve_environment_item_register(ectx->env_ext, renv->interp,
+ &username_env_item);
+ sieve_environment_item_register(ectx->env_ext, renv->interp,
+ &config_env_item);
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-variables.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-variables.c
new file mode 100644
index 0000000..1466ee7
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment-variables.c
@@ -0,0 +1,206 @@
+/* Copyright (c) 2015-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+
+#include "sieve-common.h"
+#include "sieve-ast.h"
+#include "sieve-binary.h"
+#include "sieve-code.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-vnd-environment-common.h"
+
+static bool vnspc_vnd_environment_validate
+ (struct sieve_validator *valdtr,
+ const struct sieve_variables_namespace *nspc,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd,
+ ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data,
+ bool assignment);
+static bool vnspc_vnd_environment_generate
+ (const struct sieve_codegen_env *cgenv,
+ const struct sieve_variables_namespace *nspc,
+ struct sieve_ast_argument *arg,
+ struct sieve_command *cmd, void *var_data);
+static bool vnspc_vnd_environment_dump_variable
+ (const struct sieve_dumptime_env *denv,
+ const struct sieve_variables_namespace *nspc,
+ const struct sieve_operand *oprnd, sieve_size_t *address);
+static int vnspc_vnd_environment_read_variable
+ (const struct sieve_runtime_env *renv,
+ const struct sieve_variables_namespace *nspc,
+ const struct sieve_operand *oprnd,
+ sieve_size_t *address, string_t **str_r);
+
+static const struct sieve_variables_namespace_def
+environment_namespace = {
+ SIEVE_OBJECT("env", &environment_namespace_operand, 0),
+ .validate = vnspc_vnd_environment_validate,
+ .generate = vnspc_vnd_environment_generate,
+ .dump_variable = vnspc_vnd_environment_dump_variable,
+ .read_variable = vnspc_vnd_environment_read_variable
+};
+
+static bool vnspc_vnd_environment_validate
+(struct sieve_validator *valdtr,
+ const struct sieve_variables_namespace *nspc ATTR_UNUSED,
+ struct sieve_ast_argument *arg, struct sieve_command *cmd ATTR_UNUSED,
+ ARRAY_TYPE(sieve_variable_name) *var_name, void **var_data,
+ bool assignment)
+{
+ struct sieve_ast *ast = arg->ast;
+ const struct sieve_variable_name *name_elements;
+ unsigned int i, count;
+ const char *variable;
+ string_t *name;
+
+ /* Compose environment name from parsed variable name */
+ name = t_str_new(64);
+ name_elements = array_get(var_name, &count);
+ i_assert(count > 1);
+ for (i = 1; i < count; i++) {
+ if ( name_elements[i].num_variable >= 0 ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "vnd.dovecot.environment: invalid variable name within "
+ "env namespace `env.%d': "
+ "encountered numeric variable name",
+ name_elements[i].num_variable);
+ return FALSE;
+ }
+ if (str_len(name) > 0)
+ str_append_c(name, '.');
+ str_append_str(name, name_elements[i].identifier);
+ }
+
+ variable = str_c(name);
+
+ if ( assignment ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "vnd.dovecot.environment: cannot assign to environment "
+ "variable `env.%s'", variable);
+ return FALSE;
+ }
+
+ *var_data = (void *) p_strdup(sieve_ast_pool(ast), variable);
+ return TRUE;
+}
+
+static bool vnspc_vnd_environment_generate
+(const struct sieve_codegen_env *cgenv,
+ const struct sieve_variables_namespace *nspc,
+ struct sieve_ast_argument *arg ATTR_UNUSED,
+ struct sieve_command *cmd ATTR_UNUSED, void *var_data)
+{
+ const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc);
+ const char *variable = (const char *) var_data;
+ struct ext_vnd_environment_context *ext_data;
+
+ if ( this_ext == NULL )
+ return FALSE;
+
+ ext_data = (struct ext_vnd_environment_context *) this_ext->context;
+
+ sieve_variables_opr_namespace_variable_emit
+ (cgenv->sblock, ext_data->var_ext, this_ext, &environment_namespace);
+ sieve_binary_emit_cstring(cgenv->sblock, variable);
+
+ return TRUE;
+}
+
+static bool vnspc_vnd_environment_dump_variable
+(const struct sieve_dumptime_env *denv,
+ const struct sieve_variables_namespace *nspc ATTR_UNUSED,
+ const struct sieve_operand *oprnd, sieve_size_t *address)
+{
+ string_t *var_name;
+
+ if ( !sieve_binary_read_string(denv->sblock, address, &var_name) )
+ return FALSE;
+
+ if ( oprnd->field_name != NULL )
+ sieve_code_dumpf(denv, "%s: VAR ${env.%s}",
+ oprnd->field_name, str_c(var_name));
+ else
+ sieve_code_dumpf(denv, "VAR ${env.%s}",
+ str_c(var_name));
+
+ return TRUE;
+}
+
+static int vnspc_vnd_environment_read_variable
+(const struct sieve_runtime_env *renv,
+ const struct sieve_variables_namespace *nspc,
+ const struct sieve_operand *oprnd, sieve_size_t *address,
+ string_t **str_r)
+{
+ const struct sieve_extension *this_ext = SIEVE_OBJECT_EXTENSION(nspc);
+ struct ext_vnd_environment_context *ectx =
+ (struct ext_vnd_environment_context *) this_ext->context;
+ string_t *var_name;
+ const char *ext_value;
+
+ if ( !sieve_binary_read_string(renv->sblock, address, &var_name) ) {
+ sieve_runtime_trace_operand_error(renv, oprnd,
+ "environment variable operand corrupt: invalid name");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if ( str_r != NULL ) {
+ const char *vname = str_c(var_name);
+
+ ext_value = ext_environment_item_get_value
+ (ectx->env_ext, renv, vname);
+
+ if ( ext_value == NULL && strchr(vname, '_') != NULL) {
+ char *p, *aname;
+
+ /* Try again with '_' replaced with '-' */
+ aname = t_strdup_noconst(vname);
+ for (p = aname; *p != '\0'; p++) {
+ if (*p == '_')
+ *p = '-';
+ }
+ ext_value = ext_environment_item_get_value
+ (ectx->env_ext, renv, aname);
+ }
+
+ if ( ext_value == NULL ) {
+ *str_r = t_str_new_const("", 0);
+ return SIEVE_EXEC_OK;
+ }
+
+ *str_r = t_str_new_const(ext_value, strlen(ext_value));
+ }
+ return SIEVE_EXEC_OK;
+}
+
+/*
+ * Namespace registration
+ */
+
+static const struct sieve_extension_objects environment_namespaces =
+ SIEVE_VARIABLES_DEFINE_NAMESPACE(environment_namespace);
+
+const struct sieve_operand_def environment_namespace_operand = {
+ .name = "env-namespace",
+ .ext_def = &vnd_environment_extension,
+ .class = &sieve_variables_namespace_operand_class,
+ .interface = &environment_namespaces
+};
+
+void ext_environment_variables_init
+(const struct sieve_extension *this_ext, struct sieve_validator *valdtr)
+{
+ struct ext_vnd_environment_context *ext_data =
+ (struct ext_vnd_environment_context *) this_ext->context;
+
+ sieve_variables_namespace_register
+ (ext_data->var_ext, valdtr, this_ext, &environment_namespace);
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment.c
new file mode 100644
index 0000000..f8c7028
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/environment/ext-vnd-environment.c
@@ -0,0 +1,112 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension vnd.dovecot.environment
+ * ---------------------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: vendor-defined;
+ * spec-bosch-sieve-dovecot-environment
+ * Implementation: preliminary
+ * Status: experimental
+ *
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "sieve-ext-variables.h"
+
+#include "ext-vnd-environment-common.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_vnd_environment_load
+ (const struct sieve_extension *ext, void **context);
+static void ext_vnd_environment_unload
+ (const struct sieve_extension *ext);
+static bool ext_vnd_environment_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+static bool ext_vnd_environment_interpreter_load
+ (const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_extension_def vnd_environment_extension = {
+ .name = "vnd.dovecot.environment",
+ .load = ext_vnd_environment_load,
+ .unload = ext_vnd_environment_unload,
+ .validator_load = ext_vnd_environment_validator_load,
+ .interpreter_load = ext_vnd_environment_interpreter_load,
+ SIEVE_EXT_DEFINE_OPERAND(environment_namespace_operand)
+};
+
+static bool ext_vnd_environment_load
+(const struct sieve_extension *ext, void **context)
+{
+ struct ext_vnd_environment_context *ectx;
+
+ if ( *context != NULL )
+ ext_vnd_environment_unload(ext);
+
+ ectx = i_new(struct ext_vnd_environment_context, 1);
+ ectx->env_ext = sieve_ext_environment_require_extension(ext->svinst);
+ ectx->var_ext = sieve_ext_variables_get_extension(ext->svinst);
+ *context = (void *) ectx;
+
+ return TRUE;
+}
+
+static void ext_vnd_environment_unload
+(const struct sieve_extension *ext)
+{
+ struct ext_vnd_environment_context *ectx =
+ (struct ext_vnd_environment_context *) ext->context;
+
+ i_free(ectx);
+}
+
+/*
+ * Validator
+ */
+
+static bool ext_vnd_environment_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ const struct sieve_extension *env_ext;
+
+ /* Load environment extension implicitly */
+
+ env_ext = sieve_validator_extension_load_implicit
+ (valdtr, environment_extension.name);
+ if ( env_ext == NULL )
+ return FALSE;
+
+ ext_environment_variables_init(ext, valdtr);
+ return TRUE;
+}
+
+/*
+ * Interpreter
+ */
+
+static bool ext_vnd_environment_interpreter_load
+(const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ ext_vnd_environment_items_register(ext, renv);
+ return TRUE;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.am b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.am
new file mode 100644
index 0000000..599765f
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.am
@@ -0,0 +1,17 @@
+noinst_LTLIBRARIES = libsieve_ext_vnd_report.la
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../../.. \
+ -I$(srcdir)/../../../util \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-report.c
+
+libsieve_ext_vnd_report_la_SOURCES = \
+ ext-vnd-report.c \
+ ext-vnd-report-common.c \
+ $(commands)
+
+noinst_HEADERS = \
+ ext-vnd-report-common.h
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.in b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.in
new file mode 100644
index 0000000..1c2ec8c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/Makefile.in
@@ -0,0 +1,695 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib-sieve/plugins/vnd.dovecot/report
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/dummy-config.h \
+ $(top_builddir)/pigeonhole-config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsieve_ext_vnd_report_la_LIBADD =
+am__objects_1 = cmd-report.lo
+am_libsieve_ext_vnd_report_la_OBJECTS = ext-vnd-report.lo \
+ ext-vnd-report-common.lo $(am__objects_1)
+libsieve_ext_vnd_report_la_OBJECTS = \
+ $(am_libsieve_ext_vnd_report_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmd-report.Plo \
+ ./$(DEPDIR)/ext-vnd-report-common.Plo \
+ ./$(DEPDIR)/ext-vnd-report.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsieve_ext_vnd_report_la_SOURCES)
+DIST_SOURCES = $(libsieve_ext_vnd_report_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINARY_CFLAGS = @BINARY_CFLAGS@
+BINARY_LDFLAGS = @BINARY_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@
+DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@
+DOVECOT_CFLAGS = @DOVECOT_CFLAGS@
+DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@
+DOVECOT_INSTALLED = @DOVECOT_INSTALLED@
+DOVECOT_LIBS = @DOVECOT_LIBS@
+DOVECOT_LUA_CFLAGS = @DOVECOT_LUA_CFLAGS@
+DOVECOT_LUA_LIBS = @DOVECOT_LUA_LIBS@
+DOVECOT_SQL_LIBS = @DOVECOT_SQL_LIBS@
+DOVECOT_SSL_LIBS = @DOVECOT_SSL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBDOVECOT = @LIBDOVECOT@
+LIBDOVECOT_ACL_INCLUDE = @LIBDOVECOT_ACL_INCLUDE@
+LIBDOVECOT_AUTH_INCLUDE = @LIBDOVECOT_AUTH_INCLUDE@
+LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@
+LIBDOVECOT_COMPRESS_DEPS = @LIBDOVECOT_COMPRESS_DEPS@
+LIBDOVECOT_CONFIG_INCLUDE = @LIBDOVECOT_CONFIG_INCLUDE@
+LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@
+LIBDOVECOT_DOVEADM_INCLUDE = @LIBDOVECOT_DOVEADM_INCLUDE@
+LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@
+LIBDOVECOT_DSYNC_DEPS = @LIBDOVECOT_DSYNC_DEPS@
+LIBDOVECOT_DSYNC_INCLUDE = @LIBDOVECOT_DSYNC_INCLUDE@
+LIBDOVECOT_FTS_INCLUDE = @LIBDOVECOT_FTS_INCLUDE@
+LIBDOVECOT_IMAPC_INCLUDE = @LIBDOVECOT_IMAPC_INCLUDE@
+LIBDOVECOT_IMAP_INCLUDE = @LIBDOVECOT_IMAP_INCLUDE@
+LIBDOVECOT_IMAP_LOGIN_INCLUDE = @LIBDOVECOT_IMAP_LOGIN_INCLUDE@
+LIBDOVECOT_INCLUDE = @LIBDOVECOT_INCLUDE@
+LIBDOVECOT_LDA = @LIBDOVECOT_LDA@
+LIBDOVECOT_LDA_DEPS = @LIBDOVECOT_LDA_DEPS@
+LIBDOVECOT_LDA_INCLUDE = @LIBDOVECOT_LDA_INCLUDE@
+LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@
+LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@
+LIBDOVECOT_LIBFTS_INCLUDE = @LIBDOVECOT_LIBFTS_INCLUDE@
+LIBDOVECOT_LMTP_INCLUDE = @LIBDOVECOT_LMTP_INCLUDE@
+LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@
+LIBDOVECOT_LOGIN_DEPS = @LIBDOVECOT_LOGIN_DEPS@
+LIBDOVECOT_LOGIN_INCLUDE = @LIBDOVECOT_LOGIN_INCLUDE@
+LIBDOVECOT_LUA = @LIBDOVECOT_LUA@
+LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@
+LIBDOVECOT_LUA_INCLUDE = @LIBDOVECOT_LUA_INCLUDE@
+LIBDOVECOT_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@
+LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@
+LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@
+LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@
+LIBDOVECOT_SQL = @LIBDOVECOT_SQL@
+LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@
+LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@
+LIBDOVECOT_SSL = @LIBDOVECOT_SSL@
+LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@
+LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@
+LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@
+LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@
+LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+RANLIB = @RANLIB@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RUN_TEST = @RUN_TEST@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dovecot_docdir = @dovecot_docdir@
+dovecot_installed_moduledir = @dovecot_installed_moduledir@
+dovecot_moduledir = @dovecot_moduledir@
+dovecot_pkgincludedir = @dovecot_pkgincludedir@
+dovecot_pkglibdir = @dovecot_pkglibdir@
+dovecot_pkglibexecdir = @dovecot_pkglibexecdir@
+dovecot_statedir = @dovecot_statedir@
+dovecotdir = @dovecotdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sieve_docdir = @sieve_docdir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libsieve_ext_vnd_report.la
+AM_CPPFLAGS = \
+ -I$(srcdir)/../../.. \
+ -I$(srcdir)/../../../util \
+ $(LIBDOVECOT_INCLUDE)
+
+commands = \
+ cmd-report.c
+
+libsieve_ext_vnd_report_la_SOURCES = \
+ ext-vnd-report.c \
+ ext-vnd-report-common.c \
+ $(commands)
+
+noinst_HEADERS = \
+ ext-vnd-report-common.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/report/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib-sieve/plugins/vnd.dovecot/report/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsieve_ext_vnd_report.la: $(libsieve_ext_vnd_report_la_OBJECTS) $(libsieve_ext_vnd_report_la_DEPENDENCIES) $(EXTRA_libsieve_ext_vnd_report_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsieve_ext_vnd_report_la_OBJECTS) $(libsieve_ext_vnd_report_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-report.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vnd-report-common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-vnd-report.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmd-report.Plo
+ -rm -f ./$(DEPDIR)/ext-vnd-report-common.Plo
+ -rm -f ./$(DEPDIR)/ext-vnd-report.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmd-report.Plo
+ -rm -f ./$(DEPDIR)/ext-vnd-report-common.Plo
+ -rm -f ./$(DEPDIR)/ext-vnd-report.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c
new file mode 100644
index 0000000..af53e42
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c
@@ -0,0 +1,692 @@
+/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "ioloop.h"
+#include "hostpid.h"
+#include "str-sanitize.h"
+#include "istream.h"
+#include "ostream.h"
+#include "message-date.h"
+#include "message-size.h"
+#include "mail-storage.h"
+
+#include "rfc2822.h"
+
+#include "sieve-common.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-address.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-message.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-result.h"
+#include "sieve-address.h"
+#include "sieve-message.h"
+#include "sieve-smtp.h"
+
+#include "ext-vnd-report-common.h"
+
+#include <ctype.h>
+
+/* Report command
+ *
+ * Syntax:
+ * report [:headers_only] <feedback-type: string>
+ * <message: string> <address: string>
+ *
+ */
+
+static bool
+cmd_report_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg);
+static bool
+cmd_report_validate(struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool
+cmd_report_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+const struct sieve_command_def cmd_report = {
+ .identifier = "report",
+ .type = SCT_COMMAND,
+ .positional_args = 3,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .registered = cmd_report_registered,
+ .validate = cmd_report_validate,
+ .generate = cmd_report_generate
+};
+
+/*
+ * Tagged arguments
+ */
+
+static const struct sieve_argument_def report_headers_only_tag = {
+ .identifier = "headers_only"
+};
+
+/*
+ * Report operation
+ */
+
+static bool
+cmd_report_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+cmd_report_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def report_operation = {
+ .mnemonic = "REPORT",
+ .ext_def = &vnd_report_extension,
+ .code = 0,
+ .dump = cmd_report_operation_dump,
+ .execute = cmd_report_operation_execute
+};
+
+/* Codes for optional operands */
+
+enum cmd_report_optional {
+ OPT_END,
+ OPT_HEADERS_ONLY
+};
+
+/*
+ * Report action
+ */
+
+/* Forward declarations */
+
+static int
+act_report_check_duplicate(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other);
+static void
+act_report_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv, bool *keep);
+static int
+act_report_commit(const struct sieve_action_exec_env *aenv, void *tr_context);
+
+/* Action object */
+
+const struct sieve_action_def act_report = {
+ .name = "report",
+ .check_duplicate = act_report_check_duplicate,
+ .print = act_report_print,
+ .commit = act_report_commit
+};
+
+/* Action data */
+
+struct act_report_data {
+ const char *feedback_type;
+ const char *message;
+ struct smtp_address *to_address;
+ bool headers_only:1;
+};
+
+/*
+ * Command registration
+ */
+
+static bool
+cmd_report_registered(struct sieve_validator *valdtr,
+ const struct sieve_extension *ext,
+ struct sieve_command_registration *cmd_reg)
+{
+ sieve_validator_register_tag(valdtr, cmd_reg, ext,
+ &report_headers_only_tag,
+ OPT_HEADERS_ONLY);
+ return TRUE;
+}
+
+/*
+ * Command validation
+ */
+
+static bool
+cmd_report_validate(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg = cmd->first_positional;
+
+ /* type */
+ if (!sieve_validate_positional_argument(valdtr, cmd, arg,
+ "feedback-type", 1,
+ SAAT_STRING))
+ return FALSE;
+ if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE))
+ return FALSE;
+
+ if (sieve_argument_is_string_literal(arg)) {
+ string_t *fbtype = sieve_ast_argument_str(arg);
+ const char *feedback_type;
+
+ T_BEGIN {
+ /* Check feedback type */
+ feedback_type =
+ ext_vnd_report_parse_feedback_type(str_c(fbtype));
+
+ if (feedback_type == NULL) {
+ sieve_argument_validate_error(
+ valdtr, arg,
+ "specified feedback type `%s' is invalid",
+ str_sanitize(str_c(fbtype),128));
+ }
+ } T_END;
+
+ if (feedback_type == NULL)
+ return FALSE;
+ }
+ arg = sieve_ast_argument_next(arg);
+
+ /* message */
+ if (!sieve_validate_positional_argument(valdtr, cmd, arg, "message",
+ 2, SAAT_STRING))
+ return FALSE;
+ if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE))
+ return FALSE;
+ arg = sieve_ast_argument_next(arg);
+
+ /* address */
+ if (!sieve_validate_positional_argument(valdtr, cmd, arg, "address",
+ 3, SAAT_STRING))
+ return FALSE;
+ if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE))
+ return FALSE;
+
+ /* We can only assess the validity of the outgoing address when it is a
+ string literal. For runtime-generated strings this needs to be done
+ at runtime.
+ */
+ if (sieve_argument_is_string_literal(arg)) {
+ string_t *raw_address = sieve_ast_argument_str(arg);
+ const char *error;
+ bool result;
+
+ T_BEGIN {
+ /* Parse the address */
+ result = sieve_address_validate_str(raw_address, &error);
+ if (!result) {
+ sieve_argument_validate_error(
+ valdtr, arg,
+ "specified report address '%s' is invalid: %s",
+ str_sanitize(str_c(raw_address),128),
+ error);
+ }
+ } T_END;
+
+ return result;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+cmd_report_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd)
+{
+ sieve_operation_emit(cgenv->sblock, cmd->ext, &report_operation);
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+cmd_report_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ int opt_code = 0;
+
+ sieve_code_dumpf(denv, "REPORT");
+ sieve_code_descend(denv);
+
+ /* Dump optional operands */
+ for (;;) {
+ int opt;
+
+ if ((opt = sieve_opr_optional_dump(denv, address,
+ &opt_code)) < 0)
+ return FALSE;
+
+ if (opt == 0)
+ break;
+
+ switch (opt_code) {
+ case OPT_HEADERS_ONLY:
+ sieve_code_dumpf(denv, "headers_only");
+ break;
+ default:
+ return FALSE;
+ }
+ }
+
+ return (sieve_opr_string_dump(denv, address, "feedback-type") &&
+ sieve_opr_string_dump(denv, address, "message") &&
+ sieve_opr_string_dump(denv, address, "address"));
+}
+
+/*
+ * Code execution
+ */
+
+
+static int
+cmd_report_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ const struct sieve_extension *this_ext = renv->oprtn->ext;
+ struct act_report_data *act;
+ string_t *fbtype, *message, *to_address;
+ const char *feedback_type, *error;
+ const struct smtp_address *parsed_address;
+ int opt_code = 0, ret = 0;
+ bool headers_only = FALSE;
+ pool_t pool;
+
+ /*
+ * Read operands
+ */
+
+ /* Optional operands */
+
+ for (;;) {
+ int opt;
+
+ if ((opt = sieve_opr_optional_read(renv, address,
+ &opt_code)) < 0)
+ return SIEVE_EXEC_BIN_CORRUPT;
+
+ if (opt == 0)
+ break;
+
+ switch (opt_code) {
+ case OPT_HEADERS_ONLY:
+ headers_only = TRUE;
+ break;
+ default:
+ sieve_runtime_trace_error(
+ renv, "unknown optional operand");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+ }
+
+ /* Fixed operands */
+
+ if ((ret = sieve_opr_string_read(renv, address, "feedback-type",
+ &fbtype)) <= 0)
+ return ret;
+ if ((ret = sieve_opr_string_read(renv, address, "message",
+ &message)) <= 0)
+ return ret;
+ if ((ret = sieve_opr_string_read(renv, address, "address",
+ &to_address)) <= 0)
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ /* Verify and trim feedback type */
+ feedback_type = ext_vnd_report_parse_feedback_type(str_c(fbtype));
+ if (feedback_type == NULL) {
+ sieve_runtime_error(
+ renv, NULL,
+ "specified report feedback type `%s' is invalid",
+ str_sanitize(str_c(fbtype), 256));
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ /* Verify and normalize the address to 'local_part@domain' */
+ parsed_address = sieve_address_parse_str(to_address, &error);
+ if (parsed_address == NULL) {
+ sieve_runtime_error(
+ renv, NULL,
+ "specified report address '%s' is invalid: %s",
+ str_sanitize(str_c(to_address),128), error);
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ /* Trace */
+ if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) {
+ sieve_runtime_trace(renv, 0, "report action");
+ sieve_runtime_trace_descend(renv);
+ sieve_runtime_trace(
+ renv, 0, "report incoming message as `%s' to address %s",
+ str_sanitize(str_c(fbtype), 32),
+ smtp_address_encode_path(parsed_address));
+ }
+
+ /* Add report action to the result */
+
+ pool = sieve_result_pool(renv->result);
+ act = p_new(pool, struct act_report_data, 1);
+ act->headers_only = headers_only;
+ act->feedback_type = p_strdup(pool, feedback_type);
+ act->message = p_strdup(pool, str_c(message));
+ act->to_address = smtp_address_clone(pool, parsed_address);
+
+ if (sieve_result_add_action(renv, this_ext, "report", &act_report, NULL,
+ (void *)act, 0, TRUE) < 0)
+ return SIEVE_EXEC_FAILURE;
+ return SIEVE_EXEC_OK;
+}
+
+/*
+ * Action
+ */
+
+/* Runtime verification */
+
+static bool
+act_report_equals(const struct sieve_script_env *senv ATTR_UNUSED,
+ const struct sieve_action *act1,
+ const struct sieve_action *act2)
+{
+ struct act_report_data *rdd1 = (struct act_report_data *)act1->context;
+ struct act_report_data *rdd2 = (struct act_report_data *)act2->context;
+
+ /* Address is already normalized */
+ return (smtp_address_equals(rdd1->to_address, rdd2->to_address));
+}
+
+static int
+act_report_check_duplicate(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+
+ return (act_report_equals(eenv->scriptenv, act, act_other) ? 1 : 0);
+}
+
+/* Result printing */
+
+static void
+act_report_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv,
+ bool *keep ATTR_UNUSED)
+{
+ const struct act_report_data *rdd =
+ (struct act_report_data *)action->context;
+
+ sieve_result_action_printf(rpenv,
+ "report incoming message as `%s' to: %s",
+ str_sanitize(rdd->feedback_type, 32),
+ smtp_address_encode_path(rdd->to_address));
+}
+
+/* Result execution */
+
+static bool _contains_8bit(const char *msg)
+{
+ const unsigned char *s = (const unsigned char *)msg;
+
+ for (; *s != '\0'; s++) {
+ if ((*s & 0x80) != 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+act_report_send(const struct sieve_action_exec_env *aenv,
+ const struct ext_report_config *config,
+ const struct act_report_data *act)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_instance *svinst = eenv->svinst;
+ struct sieve_message_context *msgctx = aenv->msgctx;
+ const struct sieve_script_env *senv = eenv->scriptenv;
+ const struct sieve_message_data *msgdata = eenv->msgdata;
+ struct sieve_address_source report_from = config->report_from;
+ const struct smtp_address *sender, *user;
+ struct sieve_smtp_context *sctx;
+ struct istream *input;
+ struct ostream *output;
+ string_t *msg;
+ const char *const *headers;
+ const char *outmsgid, *boundary, *error, *subject, *from;
+ int ret;
+
+ /* Just to be sure */
+ if (!sieve_smtp_available(senv)) {
+ sieve_result_global_warning(
+ aenv, "report action has no means to send mail");
+ return SIEVE_EXEC_OK;
+ }
+
+ /* Make sure we have a subject for our report */
+ if ((ret = mail_get_headers_utf8(msgdata->mail, "subject",
+ &headers)) < 0) {
+ return sieve_result_mail_error(
+ aenv, msgdata->mail,
+ "failed to read header field `subject'");
+ }
+ if (ret > 0 && headers[0] != NULL)
+ subject = t_strconcat("Report: ", headers[0], NULL);
+ else
+ subject = "Report: (message without subject)";
+
+ /* Determine from address */
+ if (report_from.type == SIEVE_ADDRESS_SOURCE_POSTMASTER) {
+ report_from.type = SIEVE_ADDRESS_SOURCE_DEFAULT;
+ report_from.address = NULL;
+ }
+ if (sieve_address_source_get_address(
+ &report_from, svinst, senv, msgctx, eenv->flags,
+ &sender) > 0 && sender != NULL)
+ from = smtp_address_encode_path(sender);
+ else
+ from = sieve_get_postmaster_address(senv);
+
+ /* Start message */
+ sctx = sieve_smtp_start_single(senv, act->to_address, NULL, &output);
+
+ outmsgid = sieve_message_get_new_id(svinst);
+ boundary = t_strdup_printf("%s/%s", my_pid, svinst->hostname);
+
+ /* Compose main report headers */
+ msg = t_str_new(512);
+ rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION);
+ rfc2822_header_write(msg, "Message-ID", outmsgid);
+ rfc2822_header_write(msg, "Date", message_date_create(ioloop_time));
+
+ rfc2822_header_write(msg, "From", from);
+ rfc2822_header_write(msg, "To",
+ smtp_address_encode_path(act->to_address));
+
+ if (_contains_8bit(subject))
+ rfc2822_header_utf8_printf(msg, "Subject", "%s", subject);
+ else
+ rfc2822_header_printf(msg, "Subject", "%s", subject);
+
+ rfc2822_header_write(msg, "Auto-Submitted", "auto-generated (report)");
+
+ rfc2822_header_write(msg, "MIME-Version", "1.0");
+ rfc2822_header_printf(msg, "Content-Type",
+ "multipart/report; report-type=feedback-report;\n"
+ "boundary=\"%s\"", boundary);
+
+ str_append(msg, "\r\nThis is a MIME-encapsulated message\r\n\r\n");
+
+ /* Human-readable report */
+ str_printfa(msg, "--%s\r\n", boundary);
+ if (_contains_8bit(act->message)) {
+ rfc2822_header_write(msg, "Content-Type",
+ "text/plain; charset=utf-8");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit");
+ } else {
+ rfc2822_header_write(msg, "Content-Type",
+ "text/plain; charset=us-ascii");
+ rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit");
+ }
+ rfc2822_header_write(msg, "Content-Disposition", "inline");
+
+ str_printfa(msg, "\r\n%s\r\n\r\n", act->message);
+ o_stream_nsend(output, str_data(msg), str_len(msg));
+
+ /* Machine-readable report */
+ str_truncate(msg, 0);
+ str_printfa(msg, "--%s\r\n", boundary);
+ rfc2822_header_write(msg, "Content-Type", "message/feedback-report");
+ str_append(msg, "\r\n");
+
+ rfc2822_header_write(msg, "Version", "1");
+ rfc2822_header_write(msg, "Feedback-Type", act->feedback_type);
+ rfc2822_header_write(msg, "User-Agent",
+ PACKAGE_NAME "/" PACKAGE_VERSION " "
+ PIGEONHOLE_NAME "/" PIGEONHOLE_VERSION);
+
+ if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) {
+ const struct smtp_address *sender, *orig_recipient;
+
+ sender = sieve_message_get_sender(msgctx);
+ orig_recipient = sieve_message_get_orig_recipient(msgctx);
+
+ rfc2822_header_write(msg, "Original-Mail-From",
+ smtp_address_encode_path(sender));
+ if (orig_recipient != NULL) {
+ rfc2822_header_write(
+ msg, "Original-Rcpt-To",
+ smtp_address_encode_path(orig_recipient));
+ }
+ }
+ if (svinst->user_email != NULL)
+ user = svinst->user_email;
+ else if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0 ||
+ (user = sieve_message_get_orig_recipient(msgctx)) == NULL)
+ user = sieve_get_user_email(svinst);
+ if (user != NULL) {
+ rfc2822_header_write(msg, "Dovecot-Reporting-User",
+ smtp_address_encode_path(user));
+ }
+ str_append(msg, "\r\n");
+
+ o_stream_nsend(output, str_data(msg), str_len(msg));
+
+ /* Original message */
+ str_truncate(msg, 0);
+ str_printfa(msg, "--%s\r\n", boundary);
+ if (act->headers_only) {
+ rfc2822_header_write(msg, "Content-Type",
+ "text/rfc822-headers");
+ } else {
+ rfc2822_header_write(msg, "Content-Type", "message/rfc822");
+ }
+ rfc2822_header_write(msg, "Content-Disposition", "attachment");
+ str_append(msg, "\r\n");
+ o_stream_nsend(output, str_data(msg), str_len(msg));
+
+ if (act->headers_only) {
+ struct message_size hdr_size;
+ ret = mail_get_hdr_stream(msgdata->mail, &hdr_size, &input);
+ if (ret >= 0) {
+ input = i_stream_create_limit(
+ input, hdr_size.physical_size);
+ }
+ } else {
+ ret = mail_get_stream(msgdata->mail, NULL, NULL, &input);
+ if (ret >= 0)
+ i_stream_ref(input);
+ }
+ if (ret < 0) {
+ sieve_smtp_abort(sctx);
+ return sieve_result_mail_error(aenv, msgdata->mail,
+ "failed to read input message");
+ }
+
+ o_stream_nsend_istream(output, input);
+
+ if (input->stream_errno != 0) {
+ /* Error; clean up */
+ sieve_result_critical(aenv, "failed to read input message",
+ "read(%s) failed: %s",
+ i_stream_get_name(input),
+ i_stream_get_error(input));
+ i_stream_unref(&input);
+ sieve_smtp_abort(sctx);
+ return SIEVE_EXEC_OK;
+ }
+ i_stream_unref(&input);
+
+ str_truncate(msg, 0);
+ if (!act->headers_only)
+ str_printfa(msg, "\r\n");
+ str_printfa(msg, "\r\n--%s--\r\n", boundary);
+ o_stream_nsend(output, str_data(msg), str_len(msg));
+
+ /* Finish sending message */
+ if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) {
+ if (ret < 0) {
+ sieve_result_global_error(
+ aenv, "failed to send `%s' report to <%s>: %s "
+ "(temporary failure)",
+ str_sanitize(act->feedback_type, 32),
+ smtp_address_encode(act->to_address),
+ str_sanitize(error, 512));
+ } else {
+ sieve_result_global_log_error(
+ aenv, "failed to send `%s' report to <%s>: %s "
+ "(permanent failure)",
+ str_sanitize(act->feedback_type, 32),
+ smtp_address_encode(act->to_address),
+ str_sanitize(error, 512));
+ }
+ } else {
+ eenv->exec_status->significant_action_executed = TRUE;
+
+ struct event_passthrough *e =
+ sieve_action_create_finish_event(aenv)->
+ add_str("report_target",
+ smtp_address_encode(act->to_address))->
+ add_str("report_type",
+ str_sanitize(act->feedback_type, 32));
+
+ sieve_result_event_log(aenv, e->event(),
+ "sent `%s' report to <%s>",
+ str_sanitize(act->feedback_type, 32),
+ smtp_address_encode(act->to_address));
+ }
+
+ return SIEVE_EXEC_OK;
+}
+
+static int
+act_report_commit(const struct sieve_action_exec_env *aenv,
+ void *tr_context ATTR_UNUSED)
+{
+ const struct sieve_action *action = aenv->action;
+ const struct sieve_extension *ext = action->ext;
+ const struct ext_report_config *config =
+ (const struct ext_report_config *)ext->context;
+ const struct act_report_data *act =
+ (const struct act_report_data *)action->context;
+ int ret;
+
+ T_BEGIN {
+ ret = act_report_send(aenv, config, act);
+ } T_END;
+
+ if (ret == SIEVE_EXEC_TEMP_FAILURE)
+ return SIEVE_EXEC_TEMP_FAILURE;
+
+ /* Ignore all other errors */
+ return SIEVE_EXEC_OK;
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c
new file mode 100644
index 0000000..d22ad16
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c
@@ -0,0 +1,51 @@
+/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "rfc822-parser.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+
+#include "ext-vnd-report-common.h"
+
+bool ext_report_load
+(const struct sieve_extension *ext, void **context)
+{
+ struct sieve_instance *svinst = ext->svinst;
+ struct ext_report_config *config;
+
+ config = p_new(svinst->pool, struct ext_report_config, 1);
+
+ (void)sieve_address_source_parse_from_setting(svinst,
+ svinst->pool, "sieve_report_from", &config->report_from);
+
+ *context = (void *) config;
+ return TRUE;
+}
+
+const char *
+ext_vnd_report_parse_feedback_type(const char *feedback_type)
+{
+ struct rfc822_parser_context parser;
+ string_t *token;
+
+ /* Initialize parsing */
+ rfc822_parser_init(&parser,
+ (const unsigned char *)feedback_type, strlen(feedback_type), NULL);
+ (void)rfc822_skip_lwsp(&parser);
+
+ /* Parse MIME token */
+ token = t_str_new(64);
+ if (rfc822_parse_mime_token(&parser, token) < 0)
+ return NULL;
+
+ /* Content-type value must end here, otherwise it is invalid after all */
+ (void)rfc822_skip_lwsp(&parser);
+ if ( parser.data != parser.end )
+ return NULL;
+
+ /* Success */
+ return str_c(token);
+}
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h
new file mode 100644
index 0000000..11a3757
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h
@@ -0,0 +1,40 @@
+#ifndef EXT_REPORT_COMMON_H
+#define EXT_REPORT_COMMON_H
+
+/*
+ * Extension configuration
+ */
+
+struct ext_report_config {
+ struct sieve_address_source report_from;
+};
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def vnd_report_extension;
+
+bool ext_report_load
+ (const struct sieve_extension *ext, void **context);
+
+/*
+ * Commands
+ */
+
+extern const struct sieve_command_def cmd_report;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation_def report_operation;
+
+/*
+ * RFC 5965 feedback-type
+ */
+
+const char *
+ext_vnd_report_parse_feedback_type(const char *feedback_type);
+
+#endif
diff --git a/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c
new file mode 100644
index 0000000..a5fb64c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c
@@ -0,0 +1,52 @@
+/* Copyright (c) 2016-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension report
+ * ----------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: draft-ietf-sieve-report-00.txt
+ * Implementation: full, but deprecated; provided for backwards compatibility
+ * Status: testing
+ *
+ */
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-actions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include "ext-vnd-report-common.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_report_validator_load
+ (const struct sieve_extension *ext, struct sieve_validator *valdtr);
+
+const struct sieve_extension_def vnd_report_extension = {
+ .name = "vnd.dovecot.report",
+ .load = ext_report_load,
+ .validator_load = ext_report_validator_load,
+ SIEVE_EXT_DEFINE_OPERATION(report_operation)
+};
+
+/*
+ * Extension validation
+ */
+
+static bool ext_report_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *valdtr)
+{
+ /* Register new commands */
+ sieve_validator_register_command(valdtr, ext, &cmd_report);
+
+ return TRUE;
+}