dovecot-2.3-pigeonhole-0.5.16/0000755000000000000000000000000014103200147015663 5ustar00rootroot00000000000000dovecot-2.3-pigeonhole-0.5.16/COPYING0000644000000000000000000000024314103200126016712 0ustar00rootroot00000000000000See AUTHORS file for list of copyright holders. Everything is licenced under LGPLv2.1 (see COPYING.LGPL) unless otherwise mentioned at the beginning of the file. dovecot-2.3-pigeonhole-0.5.16/README0000644000000000000000000003552214103200126016547 0ustar00rootroot00000000000000Pigeonhole for Dovecot v2.4 Introduction ============ This package is part of the Pigeonhole project (http://pigeonhole.dovecot.org). It adds support for the Sieve language (RFC 5228) and the ManageSieve protocol (RFC 5804) to the Dovecot Secure IMAP Server. In the literal sense, a pigeonhole is a a hole or recess inside a dovecot for pigeons to nest in. It is, however, also the name for one of a series of small, open compartments in a cabinet used for filing or sorting mail. As a verb, it describes the act of putting an item into one of those pigeonholes. The name `Pigeonhole' therefore well describes an important part of the functionality that this project adds to Dovecot: sorting and filing e-mail messages. The Sieve language is used to specify how e-mail needs to be processed. By writing Sieve scripts, users can customize how messages are delivered, e.g. whether they are forwarded or stored in special folders. Unwanted messages can be discarded or rejected, and, when the user is not available, the Sieve interpreter can send an automated reply. Above all, the Sieve language is meant to be simple, extensible and system independent. And, unlike most other mail filtering script languages, it does not allow users to execute arbitrary programs. This is particularly useful to prevent virtual users from having full access to the mail store. The intention of the language is to make it impossible for users to do anything more complex (and dangerous) than write simple mail filters. Using the ManageSieve protocol, users can upload their Sieve scripts remotely, without needing direct filesystem access through FTP or SCP. Additionally, a ManageSieve server always makes sure that uploaded scripts are valid, preventing compile failures at mail delivery. This package provides Sieve support as a plugin to Dovecot's Local Delivery Agent (LDA) and Dovecot's LMTP service. The ManageSieve protocol is provided is an additional service, next to Dovecot's own POP3 and IMAP services. Features ======== * The Pigeonhole Sieve implementation aims to be admin- and user-friendly. Much like Dovecot itself, common error messages are made as easily understandable as possible. Any crash, no matter how it happened, is considered a bug that will be fixed. The compiler does not bail on the first error, but it looks for more script errors to make debugging more efficient. * The Pigeonhole Sieve implementation is, much like the Sieve language itself, highly extensible with new Sieve capabilities. This includes support for third-party plugins. It should eventually provide the necessary infrastructure for at least all currently known relevant (proposed) Sieve extensions. The goal is to keep the extension interface provided by the Sieve implementation as generic as possible, i.e. without explicit support for specific extensions. New similar extensions can then use the same interface methods without changes to the Sieve engine code. If an extension is not loaded using the require command, the compiler truly does not know of its existence. * The Pigeonhole Sieve plugin is backwards compatible with the old CMUSieve plugin, which provided Sieve support for older versions of Dovecot. All Sieve extensions supported by the old plugin are also supported by the Pigeonhole Sieve plugin, including those that are now considered to be deprecated. * The Pigeonhole Sieve implementation supports executing multiple Sieve scripts sequentially. Using this feature it is possible to execute administrator-controlled Sieve scripts before and after the user's personal Sieve script, guaranteeing that responses and message deliveries are never duplicated. This implementation is based on a draft specification (http://tools.ietf.org/html/draft-degener-sieve-multiscript-00), which defines the Sieve behavior when multiple scripts are executed sequentially on the same message. * The Pigeonhole Sieve implementation includes a test suite to automatically assess whether the compiled Sieve engine works correctly. The test suite is an extension to the Sieve language and is therefore easily extended with new tests. Currently, the test suite is mostly limited to testing script processing. The performed actions are not tested fully yet. * The Pigeonhole Sieve implementation supports the new and very useful variables extension, which allows maintaining state information throughout a Sieve script across subsequent rules. * The Pigeonhole Sieve plugin is distributed with a sieve-test tool that simplifies testing Sieve scripts and provides additional debugging facilities. Sieve Implementation Status =========================== The core of the language (as specified in RFC 5228) is fully supported. In addition to that, this Sieve implementation features various extensions. The following list outlines the implementation status of each supported extension: The language extensions defined in the base specification are fully supported: encoded-character (RFC 5228; page 10) fileinto (RFC 5228; page 23) envelope (RFC 5228; page 27) The following Sieve language extensions are also supported: copy (RFC 3894): fully supported. body (RFC 5173): fully supported. environment (RFC 5183): fully supported (v0.4.0+). variables (RFC 5229): fully supported. vacation (RFC 5230): fully supported. + vacation-seconds (RFC 6131): fully supported (v0.2.3+). relational (RFC 5231): fully supported. imap4flags (RFC 5232): fully supported. subaddress (RFC 5233): fully supported, but with limited configurability. spamtest and virustest (RFC 5235): fully supported (v0.1.16+). date (RFC 5260; Section 4): fully supported (v0.1.12+). index (RFC 5260; Section 6): fully supported (v0.4.7+). editheader (RFC 5293): fully supported (v0.3.0+). reject (RFC 5429; Section 2.2): fully supported. enotify (RFC 5435): fully supported (v0.1.3+). mailto method (RFC 5436): fully supported (v0.1.3+). xmpp method (RFC 5437): is under development and will become available as a plugin. ihave (RFC 5463): fully supported (v0.2.4+). mailbox (RFC 5490; Section 3): fully supported (v0.1.10+), but ACL permissions are not verified for mailboxexists. mboxmetadata and servermetadata (RFC 5490): fully supported (v0.4.7+) foreverypart (RFC 5703; Section 3): fully supported (v0.4.10+). mime (RFC 5703; Section 4): fully supported (v0.4.10+). extracttext (RFC 5703; Section 7): fully supported (v0.4.12+). include (RFC 6609): fully supported (v0.4.0+). imapsieve (RFC 6785): fully supported (v0.4.14+). duplicate (RFC 7352): fully supported (v0.4.3+). regex (draft v08; not latest version): almost fully supported, but UTF-8 is not supported. The following deprecated extensions are supported for backwards compatibility: imapflags (obsolete draft): fully backwards compatible (v0.1.3+) notify (obsolete draft): fully backwards compatible (v0.1.15+) The availability of these deprecated extensions is disabled by default. The following Dovecot-specific Sieve extensions are available: vnd.dovecot.debug (v0.3.0+): Allows logging debug messages. vnd.dovecot.execute (v0.4.0+; sieve_extprograms plugin): Implements executing a pre-defined set of external programs with the option to process string data through the external program. vnd.dovecot.filter (v0.4.0+; sieve_extprograms plugin): Implements filtering messages through a pre-defined set of external programs. vnd.dovecot.pipe (v0.4.0+; sieve_extprograms plugin): Implements piping messages to a pre-defined set of external programs. vnd.dovecot.report (v0.4.14): Implements sending MARF reports (RFC 5965). The following extensions are under development: ereject (RFC 5429; page 4): implemented, but currently equal to reject. Many more extensions to the language exist. Not all of these extensions are useful for Dovecot in particular, but many of them are. Currently, the author has taken notice of the following extensions: replace (RFC 5703; Section 5): planned. enclose (RFC 5703; Section 6): planned. envelope-dsn, envelope-deliverby, redirect-dsn and redirect-deliverby (RFC 6009): planned; depends on lib-smtp changes in Dovecot. extlists (RFC 6134): planned. convert (RFC 6558): under consideration. These extensions will be added as soon as the necessary infrastructure is available. Check the TODO file for an up-to-date list of open issues and current development. Compiling and Configuring ========================= Refer to INSTALL file. Sieve Tools =========== To test the sieve engine outside deliver, it is useful to try the commands that exist in the src/sieve-tools/ directory of this package. After installation, these are available at your $prefix/bin directory. The following commands are installed: sievec - Compiles sieve scripts into a binary representation for later execution. Refer to the next section on manually compiling Sieve scripts. sieve-test - This is a universal Sieve test tool for testing the effect of a Sieve script on a particular message. It allows compiling, running and testing Sieve scripts. It can either be used to display the actions that would be performed on the provided test message or it can be used to test the actual delivery of the message and show the messages that would normally be sent through SMTP. sieve-dump - Dumps the content of a Sieve binary file for (development) debugging purposes. sieve-filter - Allow running Sieve filters on messages already stored in a mailbox. When installed, man pages are also available for these commands. In this package the man pages are present in doc/man and can be viewed before install using e.g.: man -l doc/man/sieve-test.1 Various example scripts are bundled in the directory 'examples'. These scripts were downloaded from various locations. View the top comment in the scripts for url and author information. Compiling Sieve Scripts ======================= When the LDA Sieve plugin executes a script for the first time (or after it has been changed), it is compiled into into a binary form. The Pigeonhole Sieve implementation uses the .svbin extension to store compiled Sieve scripts (e.g. .dovecot.svbin). To store the binary, the plugin needs write access in the directory in which the script is located. A problem occurs when a global script is encountered by the plugin. For security reasons, global script directories are not supposed to be writable by the user. Therefore, the plugin cannot store the binary when the script is first compiled. Note that this doesn't mean that the old compiled version of the script is used when the binary cannot be written: it compiles and uses the current script version. The only real problem is that the plugin will not be able to update the binary on disk, meaning that the global script needs to be recompiled each time it needs to be executed, i.e. for every incoming message, which is inefficient. To mitigate this problem, the administrator must manually pre-compile global scripts using the sievec command line tool. For example: sievec /var/lib/dovecot/sieve/global/ This is often necessary for scripts listed in the sieve_default, sieve_before and sieve_after settings. For global scripts that are only included in other scripts using the include extension, this step is not necessary, since included scripts are incorporated into the binary produced for the main script located in a user directory. Compile and Runtime Logging =========================== Log messages produced at runtime by the Sieve plugin are written to two locations: * Messages are primarily logged to the user log. By default this log file is located in the same directory as the user's main active personal script (as specified by the sieve setting). This log file bears the name of that script file appended with ".log", e.g. ".dovecot.sieve.log". The location of the user log file can also be explicitly configured using the sieve_user_log setting (e.g. for when Sieve scripts are not stored on the local file system). If there are errors or warnings in the script, the messages are appended to that log file until it eventually grows too large. When that happens, the old log file is rotated to a ".log.0" file and an empty log file is started. Informational messages are not written to this log file and the log file is not created until messages are actually logged, i.e. when an error or warning is produced. * Messages that could be of interest to the system administrator are also written to the Dovecot LDA logging facility (usually syslog). This includes informational messages that indicate what actions are executed on incoming messages. Compile errors encountered in the user's private script are not logged here. The ManageSieve service reports compile errors and warnings only back to the user. System and configuration-related messages are written to the Dovecot logging facility. Known issues ============ Sieve ----- Most open issues are outlined in the TODO file. The more generic ones are (re-) listed here: * Compile errors are sometimes a bit obscure and long. This needs work. Suggestions for improvement are welcome. * The documentation needs work. ManageSieve ----------- * Although this ManageSieve server should comply with the draft specification of the ManageSieve protocol, quite a few clients don't. This is particularly true for the TLS support. However, now that Cyrus' Timsieved has changed its behavior towards protocol compliance, all those clients will follow eventually. Clients known to have TLS issues: - Thunderbird Sieve add-on: fixed as per version 0.1.5 - AvelSieve: patch on the wiki: http://wiki.dovecot.org/ManageSieve - KMail + kio_sieve: TLS broken for old versions. This issue is fixed at least in kmail 1.9.9 / kde 3.5.9. Unfortunately, there is no reliable way to provide a workaround for this problem. We will have to wait for the authors of these clients to make the proper adjustments. * Other client issues: - SmartSieve, WebSieve: These clients are specifically written for Cyrus timsieved and fail on multiple stages of the protocol when connected to Pigeonhole ManageSieve. Authors ======= Refer to AUTHORS file. Contact Info ============ Stephan Bosch IRC: Freenode, #dovecot, S[r]us Web: http://pigeonhole.dovecot.org Please use the Dovecot mailing list for questions about this package. You can post to the list without subscribing, the mail then waits in a moderator queue for a while. See http://dovecot.org/mailinglists.html dovecot-2.3-pigeonhole-0.5.16/install-sh0000755000000000000000000003601014103200135017664 0ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2018-03-11.20; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) # Note that $RANDOM variable is not portable (e.g. dash); Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p' feature. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: dovecot-2.3-pigeonhole-0.5.16/src/0000755000000000000000000000000014103200147016452 5ustar00rootroot00000000000000dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/0000755000000000000000000000000014103200145020327 5ustar00rootroot00000000000000dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-address.c0000644000000000000000000003327414103200126023241 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "str-sanitize.h" #include "rfc822-parser.h" #include "message-address.h" #include "sieve-common.h" #include "sieve-runtime-trace.h" #include "sieve-address.h" #include /* * Header address list */ /* Forward declarations */ static int sieve_header_address_list_next_string_item(struct sieve_stringlist *_strlist, string_t **str_r); static int sieve_header_address_list_next_item(struct sieve_address_list *_addrlist, struct smtp_address *addr_r, string_t **unparsed_r); static void sieve_header_address_list_reset(struct sieve_stringlist *_strlist); static void sieve_header_address_list_set_trace(struct sieve_stringlist *_strlist, bool trace); /* Stringlist object */ struct sieve_header_address_list { struct sieve_address_list addrlist; struct sieve_stringlist *field_values; const struct message_address *cur_address; }; struct sieve_address_list * sieve_header_address_list_create(const struct sieve_runtime_env *renv, struct sieve_stringlist *field_values) { struct sieve_header_address_list *addrlist; addrlist = t_new(struct sieve_header_address_list, 1); addrlist->addrlist.strlist.runenv = renv; addrlist->addrlist.strlist.exec_status = SIEVE_EXEC_OK; addrlist->addrlist.strlist.next_item = sieve_header_address_list_next_string_item; addrlist->addrlist.strlist.reset = sieve_header_address_list_reset; addrlist->addrlist.strlist.set_trace = sieve_header_address_list_set_trace; addrlist->addrlist.next_item = sieve_header_address_list_next_item; addrlist->field_values = field_values; return &addrlist->addrlist; } static int sieve_header_address_list_next_address( struct sieve_header_address_list *addrlist, struct smtp_address *addr_r) { struct smtp_address adummy; int ret = 0; if (addr_r == NULL) addr_r = &adummy; while (addrlist->cur_address != NULL) { const struct message_address *aitem = addrlist->cur_address; addrlist->cur_address = addrlist->cur_address->next; if (!aitem->invalid_syntax && aitem->domain != NULL && smtp_address_init_from_msg(addr_r, aitem) >= 0) return 1; ret = -1; } return ret; } static int sieve_header_address_list_next_item(struct sieve_address_list *_addrlist, struct smtp_address *addr_r, string_t **unparsed_r) { struct sieve_header_address_list *addrlist = (struct sieve_header_address_list *)_addrlist; const struct sieve_runtime_env *runenv = _addrlist->strlist.runenv; string_t *value_item = NULL; bool trace = _addrlist->strlist.trace; if (addr_r != NULL) smtp_address_init(addr_r, NULL, NULL); if (unparsed_r != NULL) *unparsed_r = NULL; for (;;) { int ret; if ((ret = sieve_header_address_list_next_address( addrlist, addr_r)) < 0 && value_item != NULL) { /* completely invalid address list is returned as-is */ if (trace) { sieve_runtime_trace( runenv, 0, "invalid address value `%s'", str_sanitize(str_c(value_item), 80)); } if (unparsed_r != NULL) *unparsed_r = value_item; return 1; } if (ret > 0) { if (trace) { sieve_runtime_trace( runenv, 0, "address value `%s'", str_sanitize(smtp_address_encode(addr_r), 80)); } return 1; } /* Read next header value from source list */ if ((ret = sieve_stringlist_next_item(addrlist->field_values, &value_item)) <= 0) return ret; if (str_len(value_item) == 0) { /* empty header value is returned as-is */ if (trace) { sieve_runtime_trace(runenv, 0, "empty address value"); } addrlist->cur_address = NULL; if (unparsed_r != NULL) *unparsed_r = value_item; return 1; } if (trace) { sieve_runtime_trace( runenv, 0, "parsing address header value `%s'", str_sanitize(str_c(value_item), 80)); } addrlist->cur_address = message_address_parse( pool_datastack_create(), (const unsigned char *)str_data(value_item), str_len(value_item), 256, 0); } i_unreached(); } static int sieve_header_address_list_next_string_item(struct sieve_stringlist *_strlist, string_t **str_r) { struct sieve_address_list *addrlist = (struct sieve_address_list *)_strlist; struct smtp_address addr; int ret; if ((ret = sieve_header_address_list_next_item( addrlist, &addr, str_r)) <= 0) return ret; if (addr.localpart != NULL) { const char *addr_str = smtp_address_encode(&addr); if (str_r != NULL) *str_r = t_str_new_const(addr_str, strlen(addr_str)); } return 1; } static void sieve_header_address_list_reset(struct sieve_stringlist *_strlist) { struct sieve_header_address_list *addrlist = (struct sieve_header_address_list *)_strlist; sieve_stringlist_reset(addrlist->field_values); addrlist->cur_address = NULL; } static void sieve_header_address_list_set_trace(struct sieve_stringlist *_strlist, bool trace) { struct sieve_header_address_list *addrlist = (struct sieve_header_address_list *)_strlist; sieve_stringlist_set_trace(addrlist->field_values, trace); } /* * RFC 2822 addresses */ /* Mail message address according to RFC 2822 and implemented in the Dovecot message address parser: address = mailbox / group mailbox = name-addr / addr-spec name-addr = [display-name] angle-addr angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr group = display-name ":" [mailbox-list / CFWS] ";" [CFWS] display-name = phrase addr-spec = local-part "@" domain local-part = dot-atom / quoted-string / obs-local-part domain = dot-atom / domain-literal / obs-domain domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS] dcontent = dtext / quoted-pair dtext = NO-WS-CTL / ; Non white space controls %d33-90 / ; The rest of the US-ASCII %d94-126 ; characters not including "[", ; "]", or "\" atext = ALPHA / DIGIT / ; Any character except controls, "!" / "#" / ; SP, and specials. "$" / "%" / ; Used for atoms "&" / "'" / "*" / "+" / "-" / "/" / "=" / "?" / "^" / "_" / "`" / "{" / "|" / "}" / "~" atom = [CFWS] 1*atext [CFWS] dot-atom = [CFWS] dot-atom-text [CFWS] dot-atom-text = 1*atext *("." 1*atext) word = atom / quoted-string phrase = 1*word / obs-phrase Message address specification as allowed bij the RFC 5228 SIEVE specification: sieve-address = addr-spec ; simple address / phrase "<" addr-spec ">" ; name & addr-spec\ Which concisely is about equal to: sieve-address = mailbox */ /* * Address parse context */ struct sieve_message_address_parser { struct rfc822_parser_context parser; string_t *str; string_t *local_part; string_t *domain; string_t *error; }; /* * Error handling */ static inline void ATTR_FORMAT(2, 3) sieve_address_error(struct sieve_message_address_parser *ctx, const char *fmt, ...) { va_list args; if (str_len(ctx->error) == 0) { va_start(args, fmt); str_vprintfa(ctx->error, fmt, args); va_end(args); } } /* Partial RFC 2822 address parser FIXME: lots of overlap with dovecot/src/lib-mail/message-parser.c --> this implementation adds textual error reporting MERGE! */ static int check_local_part(struct sieve_message_address_parser *ctx) { const unsigned char *p, *pend; p = str_data(ctx->local_part); pend = p + str_len(ctx->local_part); while (p < pend) { if (*p < 0x20 || *p > 0x7e) return -1; p++; } return 0; } static int parse_local_part(struct sieve_message_address_parser *ctx) { int ret; /* local-part = dot-atom / quoted-string / obs-local-part obs-local-part = word *("." word) */ if (ctx->parser.data == ctx->parser.end) { sieve_address_error(ctx, "empty local part"); return -1; } str_truncate(ctx->local_part, 0); if (*ctx->parser.data == '"') { ret = rfc822_parse_quoted_string(&ctx->parser, ctx->local_part); } else { ret = -1; /* NOTE: this deviates from dot-atom syntax to allow some Japanese mail addresses with dots at non-standard places to be accepted. */ do { while (*ctx->parser.data == '.') { str_append_c(ctx->local_part, '.'); ctx->parser.data++; if (ctx->parser.data == ctx->parser.end) { /* @domain is missing, but local-part parsing was successful */ return 0; } ret = 1; } if (*ctx->parser.data == '@') break; ret = rfc822_parse_atom(&ctx->parser, ctx->local_part); } while (ret > 0 && *ctx->parser.data == '.'); } if (ret < 0 || check_local_part(ctx) < 0) { sieve_address_error(ctx, "invalid local part"); return -1; } return ret; } static int parse_domain(struct sieve_message_address_parser *ctx) { int ret; str_truncate(ctx->domain, 0); if ((ret = rfc822_parse_domain(&ctx->parser, ctx->domain)) < 0) { sieve_address_error(ctx, "invalid or missing domain"); return -1; } return ret; } static int parse_addr_spec(struct sieve_message_address_parser *ctx) { /* addr-spec = local-part "@" domain */ int ret; if ((ret = parse_local_part(ctx)) < 0) return ret; if (ret > 0 && *ctx->parser.data == '@') { return parse_domain(ctx); } sieve_address_error( ctx, "invalid or lonely local part '%s' (expecting '@')", str_sanitize(str_c(ctx->local_part), 80)); return -1; } static int parse_mailbox(struct sieve_message_address_parser *ctx) { int ret; const unsigned char *start; /* sieve-address = addr-spec ; simple address / phrase "<" addr-spec ">" ; name & addr-spec */ /* Record parser state in case we fail at our first attempt */ start = ctx->parser.data; /* First try: phrase "<" addr-spec ">" ; name & addr-spec */ str_truncate(ctx->str, 0); if (rfc822_parse_phrase(&ctx->parser, ctx->str) <= 0 || *ctx->parser.data != '<') { /* Failed; try just bare addr-spec */ ctx->parser.data = start; return parse_addr_spec(ctx); } /* "<" addr-spec ">" */ ctx->parser.data++; if ((ret = rfc822_skip_lwsp(&ctx->parser)) <= 0) { if (ret < 0) sieve_address_error(ctx, "invalid characters after <"); return ret; } if (parse_addr_spec(ctx) < 0) return -1; if (*ctx->parser.data != '>') { sieve_address_error(ctx, "missing '>'"); return -1; } ctx->parser.data++; if ((ret = rfc822_skip_lwsp(&ctx->parser)) < 0) { sieve_address_error( ctx, "address ends with invalid characters"); } return ret; } static bool parse_mailbox_address(struct sieve_message_address_parser *ctx, const unsigned char *address, unsigned int addr_size) { /* Initialize parser */ rfc822_parser_init(&ctx->parser, address, addr_size, NULL); /* Parse */ rfc822_skip_lwsp(&ctx->parser); if (ctx->parser.data == ctx->parser.end) { sieve_address_error(ctx, "empty address"); return FALSE; } if (parse_mailbox(ctx) < 0) return FALSE; if (ctx->parser.data != ctx->parser.end) { if (*ctx->parser.data == ',') { sieve_address_error( ctx, "not a single addres (found ',')"); } else { sieve_address_error( ctx, "address ends in invalid characters"); } return FALSE; } if (str_len(ctx->domain) == 0) { /* Not gonna happen */ sieve_address_error(ctx, "missing domain"); return FALSE; } if (str_len(ctx->local_part) == 0) { sieve_address_error(ctx, "missing local part"); return FALSE; } return TRUE; } static bool sieve_address_do_validate(const unsigned char *address, size_t size, const char **error_r) { struct sieve_message_address_parser ctx; *error_r = NULL; if (address == NULL) { *error_r = "null address"; return FALSE; } i_zero(&ctx); ctx.local_part = t_str_new(128); ctx.domain = t_str_new(128); ctx.str = t_str_new(128); ctx.error = t_str_new(128); if (!parse_mailbox_address(&ctx, address, size)) { *error_r = str_c(ctx.error); return FALSE; } return TRUE; } static const struct smtp_address * sieve_address_do_parse(const unsigned char *address, size_t size, const char **error_r) { struct sieve_message_address_parser ctx; *error_r = NULL; if (address == NULL) return NULL; i_zero(&ctx); ctx.local_part = t_str_new(128); ctx.domain = t_str_new(128); ctx.str = t_str_new(128); ctx.error = t_str_new(128); if (!parse_mailbox_address(&ctx, address, size)) { *error_r = str_c(ctx.error); return NULL; } (void)str_lcase(str_c_modifiable(ctx.domain)); return smtp_address_create_temp(str_c(ctx.local_part), str_c(ctx.domain)); } /* * Sieve address */ const struct smtp_address * sieve_address_parse(const char *address, const char **error_r) { return sieve_address_do_parse((const unsigned char *)address, strlen(address), error_r); } const struct smtp_address * sieve_address_parse_str(string_t *address, const char **error_r) { return sieve_address_do_parse(str_data(address), str_len(address), error_r); } bool sieve_address_validate(const char *address, const char **error_r) { return sieve_address_do_validate((const unsigned char *)address, strlen(address), error_r); } bool sieve_address_validate_str(string_t *address, const char **error_r) { return sieve_address_do_validate(str_data(address), str_len(address), error_r); } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-commands.h0000644000000000000000000002035414103200126023415 0ustar00rootroot00000000000000#ifndef SIEVE_COMMANDS_H #define SIEVE_COMMANDS_H #include "lib.h" #include "sieve-common.h" #include "sieve-ast.h" /* * Argument definition */ enum sieve_argument_flag { /* More than one of this (type of) tagged argument is allowed */ SIEVE_ARGUMENT_FLAG_MULTIPLE = (1 << 0) }; struct sieve_argument_def { const char *identifier; enum sieve_argument_flag flags; bool (*is_instance_of) (struct sieve_validator *valdtr, struct sieve_command *cmd, const struct sieve_extension *ext, const char *identifier, void **data); bool (*validate) (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *cmd); bool (*validate_context) (struct sieve_validator *valdtr, struct sieve_ast_argument *arg, struct sieve_command *cmd); bool (*validate_persistent) (struct sieve_validator *valdtr, struct sieve_command *cmd, const struct sieve_extension *ext); bool (*generate) (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd); }; /* * Argument instance */ struct sieve_argument { const struct sieve_argument_def *def; const struct sieve_extension *ext; int id_code; /* Context data */ void *data; }; #define sieve_argument_is(ast_arg, definition) \ ( (ast_arg)->argument->def == &(definition) ) #define sieve_argument_ext(ast_arg) \ ( (ast_arg)->argument->ext ) #define sieve_argument_identifier(ast_arg) \ ( (ast_arg)->argument->def->identifier ) /* Utility macros */ #define sieve_argument_is_string_literal(arg) \ ( (arg)->argument->def == &string_argument ) /* Error handling */ #define sieve_argument_validate_error(validator, arg_node, ...) \ sieve_validator_error(validator, \ ((arg_node) == NULL ? 0 : (arg_node)->source_line), \ __VA_ARGS__) #define sieve_argument_validate_warning(validator, arg_node, ...) \ sieve_validator_warning(validator, \ ((arg_node) == NULL ? 0 : (arg_node)->source_line), \ __VA_ARGS__) #define sieve_argument_generate_error(gentr, arg_node, ...) \ sieve_generator_error(gentr, \ ((arg_node) == NULL ? 0 : (arg_node)->source_line), \ __VA_ARGS__) #define sieve_argument_generate_warning(gentr, arg_node, ...) \ sieve_generator_warning(gentr, \ ((arg_node) == NULL ? 0 : (arg_node)->source_line), \ __VA_ARGS__) /* Argument API */ struct sieve_argument *sieve_argument_create (struct sieve_ast *ast, const struct sieve_argument_def *def, const struct sieve_extension *ext, int id_code); /* Literal arguments */ extern const struct sieve_argument_def number_argument; extern const struct sieve_argument_def string_argument; extern const struct sieve_argument_def string_list_argument; /* Catenated string argument */ bool sieve_arg_catenated_string_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *context); struct sieve_arg_catenated_string; struct sieve_arg_catenated_string *sieve_arg_catenated_string_create (struct sieve_ast_argument *orig_arg); void sieve_arg_catenated_string_add_element (struct sieve_arg_catenated_string *strdata, struct sieve_ast_argument *element); /* * Command definition */ enum sieve_command_type { SCT_NONE, SCT_COMMAND, SCT_TEST, SCT_HYBRID }; struct sieve_command_def { const char *identifier; enum sieve_command_type type; /* High-level command syntax */ int positional_args; int subtests; bool block_allowed; bool block_required; bool (*registered) (struct sieve_validator *valdtr, const struct sieve_extension *ext, struct sieve_command_registration *cmd_reg); bool (*pre_validate) (struct sieve_validator *valdtr, struct sieve_command *cmd); bool (*validate) (struct sieve_validator *valdtr, struct sieve_command *cmd); bool (*validate_const) (struct sieve_validator *valdtr, struct sieve_command *cmd, int *const_current, int const_next); bool (*generate) (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); bool (*control_generate) (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, struct sieve_jumplist *jumps, bool jump_true); }; /* * Command instance */ struct sieve_command { const struct sieve_command_def *def; const struct sieve_extension *ext; /* The registration of this command in the validator (sieve-validator.h) */ struct sieve_command_registration *reg; /* The ast node of this command */ struct sieve_ast_node *ast_node; /* First positional argument, found during argument validation */ struct sieve_ast_argument *first_positional; /* The child ast node that unconditionally exits this command's block */ struct sieve_command *block_exit_command; /* Context data*/ void *data; }; #define sieve_command_is(cmd, definition) \ ( (cmd)->def == &(definition) ) #define sieve_command_identifier(cmd) \ ( (cmd)->def->identifier ) #define sieve_commands_equal(cmd1, cmd2) \ ( (cmd1) != NULL && (cmd2) != NULL && (cmd1)->def == (cmd2)->def ) /* Context API */ struct sieve_command *sieve_command_create (struct sieve_ast_node *cmd_node, const struct sieve_extension *ext, const struct sieve_command_def *cmd_def, struct sieve_command_registration *cmd_reg); const char *sieve_command_def_type_name (const struct sieve_command_def *cmd_def); const char *sieve_command_type_name (const struct sieve_command *cmd); struct sieve_command *sieve_command_prev (struct sieve_command *cmd); struct sieve_command *sieve_command_parent (struct sieve_command *cmd); struct sieve_ast_argument *sieve_command_add_dynamic_tag (struct sieve_command *cmd, const struct sieve_extension *ext, const struct sieve_argument_def *tag, int id_code); struct sieve_ast_argument *sieve_command_find_argument (struct sieve_command *cmd, const struct sieve_argument_def *argument); void sieve_command_exit_block_unconditionally (struct sieve_command *cmd); bool sieve_command_block_exits_unconditionally (struct sieve_command *cmd); /* Error handling */ #define sieve_command_validate_error(validator, context, ...) \ sieve_validator_error(validator, \ ((context) == NULL ? 0 : (context)->ast_node->source_line), \ __VA_ARGS__) #define sieve_command_validate_warning(validator, context, ...) \ sieve_validator_warning(validator, \ ((context) == NULL ? 0 : (context)->ast_node->source_line), \ __VA_ARGS__) #define sieve_command_generate_error(gentr, context, ...) \ sieve_generator_error(gentr, \ ((context) == NULL ? 0 : (context)->ast_node->source_line), \ __VA_ARGS__) #define sieve_command_generate_warning(gentr, context, ...) \ sieve_generator_warning(gentr, \ ((context) == NULL ? 0 : (context)->ast_node->source_line), \ __VA_ARGS__) /* Utility macros */ #define sieve_command_pool(context) \ sieve_ast_node_pool((context)->ast_node) #define sieve_command_source_line(context) \ (context)->ast_node->source_line #define sieve_command_first_argument(context) \ sieve_ast_argument_first((context)->ast_node) #define sieve_command_is_toplevel(context) \ ( sieve_ast_node_type(sieve_ast_node_parent((context)->ast_node)) == SAT_ROOT ) #define sieve_command_is_first(context) \ ( sieve_ast_node_prev((context)->ast_node) == NULL ) /* * Core commands */ extern const struct sieve_command_def cmd_require; extern const struct sieve_command_def cmd_stop; extern const struct sieve_command_def cmd_if; extern const struct sieve_command_def cmd_elsif; extern const struct sieve_command_def cmd_else; extern const struct sieve_command_def cmd_redirect; extern const struct sieve_command_def cmd_keep; extern const struct sieve_command_def cmd_discard; extern const struct sieve_command_def *sieve_core_commands[]; extern const unsigned int sieve_core_commands_count; /* * Core tests */ extern const struct sieve_command_def tst_true; extern const struct sieve_command_def tst_false; extern const struct sieve_command_def tst_not; extern const struct sieve_command_def tst_anyof; extern const struct sieve_command_def tst_allof; extern const struct sieve_command_def tst_address; extern const struct sieve_command_def tst_header; extern const struct sieve_command_def tst_exists; extern const struct sieve_command_def tst_size; extern const struct sieve_command_def *sieve_core_tests[]; extern const unsigned int sieve_core_tests_count; /* * Command utility functions */ bool sieve_command_verify_headers_argument (struct sieve_validator *valdtr, struct sieve_ast_argument *headers); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-validator.c0000644000000000000000000012770514103200126023604 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "str-sanitize.h" #include "array.h" #include "buffer.h" #include "mempool.h" #include "hash.h" #include "sieve-common.h" #include "sieve-extensions.h" #include "sieve-script.h" #include "sieve-ast.h" #include "sieve-commands.h" #include "sieve-validator.h" #include "sieve-comparators.h" #include "sieve-address-parts.h" /* * Forward declarations */ static void sieve_validator_register_core_commands(struct sieve_validator *valdtr); static void sieve_validator_register_core_tests(struct sieve_validator *valdtr); /* * Types */ /* Tag registration */ struct sieve_tag_registration { const struct sieve_argument_def *tag_def; const struct sieve_extension *ext; const char *identifier; int id_code; }; /* Command registration */ struct sieve_command_registration { const struct sieve_command_def *cmd_def; const struct sieve_extension *ext; ARRAY(struct sieve_tag_registration *) normal_tags; ARRAY(struct sieve_tag_registration *) instanced_tags; ARRAY(struct sieve_tag_registration *) persistent_tags; }; /* Default (literal) arguments */ struct sieve_default_argument { const struct sieve_argument_def *arg_def; const struct sieve_extension *ext; struct sieve_default_argument *overrides; }; /* * Validator extension */ struct sieve_validator_extension_reg { const struct sieve_validator_extension *valext; const struct sieve_extension *ext; struct sieve_ast_argument *arg; void *context; bool loaded:1; bool required:1; }; /* * Validator */ struct sieve_validator { pool_t pool; struct sieve_instance *svinst; struct sieve_ast *ast; struct sieve_script *script; enum sieve_compile_flags flags; struct sieve_error_handler *ehandler; bool finished_require; /* Registries */ HASH_TABLE(const char *, struct sieve_command_registration *) commands; ARRAY(struct sieve_validator_extension_reg) extensions; /* This is currently a wee bit ugly and needs more thought */ struct sieve_default_argument default_arguments[SAT_COUNT]; /* Default argument processing state (FIXME: ugly) */ struct sieve_default_argument *current_defarg; enum sieve_argument_type current_defarg_type; bool current_defarg_constant; }; /* * Validator object */ struct sieve_validator * sieve_validator_create(struct sieve_ast *ast, struct sieve_error_handler *ehandler, enum sieve_compile_flags flags) { pool_t pool; struct sieve_validator *valdtr; const struct sieve_extension *const *ext_preloaded; unsigned int i, ext_count; pool = pool_alloconly_create("sieve_validator", 16384); valdtr = p_new(pool, struct sieve_validator, 1); valdtr->pool = pool; valdtr->ehandler = ehandler; sieve_error_handler_ref(ehandler); valdtr->ast = ast; sieve_ast_ref(ast); valdtr->script = sieve_ast_script(ast); valdtr->svinst = sieve_script_svinst(valdtr->script); valdtr->flags = flags; /* Setup default arguments */ valdtr->default_arguments[SAT_NUMBER].arg_def = &number_argument; valdtr->default_arguments[SAT_NUMBER].ext = NULL; valdtr->default_arguments[SAT_VAR_STRING].arg_def = &string_argument; valdtr->default_arguments[SAT_VAR_STRING].ext = NULL; valdtr->default_arguments[SAT_CONST_STRING].arg_def = &string_argument; valdtr->default_arguments[SAT_CONST_STRING].ext = NULL; valdtr->default_arguments[SAT_STRING_LIST].arg_def = &string_list_argument; valdtr->default_arguments[SAT_STRING_LIST].ext = NULL; /* Setup storage for extension contexts */ p_array_init(&valdtr->extensions, pool, sieve_extensions_get_count(valdtr->svinst)); /* Setup command registry */ hash_table_create(&valdtr->commands, pool, 0, strcase_hash, strcasecmp); sieve_validator_register_core_commands(valdtr); sieve_validator_register_core_tests(valdtr); /* Pre-load core language features implemented as 'extensions' */ ext_preloaded = sieve_extensions_get_preloaded(valdtr->svinst, &ext_count); for (i = 0; i < ext_count; i++) { const struct sieve_extension_def *ext_def = ext_preloaded[i]->def; if (ext_def != NULL && ext_def->validator_load != NULL) (void)ext_def->validator_load(ext_preloaded[i], valdtr); } return valdtr; } void sieve_validator_free(struct sieve_validator **valdtr) { const struct sieve_validator_extension_reg *extrs; unsigned int ext_count, i; hash_table_destroy(&(*valdtr)->commands); sieve_ast_unref(&(*valdtr)->ast); sieve_error_handler_unref(&(*valdtr)->ehandler); /* Signal registered extensions that the validator is being destroyed */ extrs = array_get(&(*valdtr)->extensions, &ext_count); for (i = 0; i < ext_count; i++) { if (extrs[i].valext != NULL && extrs[i].valext->free != NULL) extrs[i].valext->free(extrs[i].ext, *valdtr, extrs[i].context); } pool_unref(&(*valdtr)->pool); *valdtr = NULL; } /* * Accessors */ // FIXME: build validate environment pool_t sieve_validator_pool(struct sieve_validator *valdtr) { return valdtr->pool; } struct sieve_error_handler * sieve_validator_error_handler(struct sieve_validator *valdtr) { return valdtr->ehandler; } struct sieve_ast *sieve_validator_ast(struct sieve_validator *valdtr) { return valdtr->ast; } struct sieve_script *sieve_validator_script(struct sieve_validator *valdtr) { return valdtr->script; } struct sieve_instance *sieve_validator_svinst(struct sieve_validator *valdtr) { return valdtr->svinst; } enum sieve_compile_flags sieve_validator_compile_flags(struct sieve_validator *valdtr) { return valdtr->flags; } /* * Command registry */ /* Dummy command object to mark unknown commands in the registry */ static bool _cmd_unknown_validate(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd ATTR_UNUSED) { i_unreached(); return FALSE; } static const struct sieve_command_def unknown_command = { .identifier = "", .type = SCT_NONE, .positional_args = 0, .subtests = 0, .block_allowed = FALSE, .block_required = FALSE, .validate = _cmd_unknown_validate }; /* Registration of the core commands of the language */ static void sieve_validator_register_core_tests(struct sieve_validator *valdtr) { unsigned int i; for (i = 0; i < sieve_core_tests_count; i++) { sieve_validator_register_command(valdtr, NULL, sieve_core_tests[i]); } } static void sieve_validator_register_core_commands(struct sieve_validator *valdtr) { unsigned int i; for (i = 0; i < sieve_core_commands_count; i++) { sieve_validator_register_command(valdtr, NULL, sieve_core_commands[i]); } } /* Registry functions */ static struct sieve_command_registration * sieve_validator_find_command_registration(struct sieve_validator *valdtr, const char *command) { return hash_table_lookup(valdtr->commands, command); } static struct sieve_command_registration * _sieve_validator_register_command(struct sieve_validator *valdtr, const struct sieve_extension *ext, const struct sieve_command_def *cmd_def, const char *identifier) { struct sieve_command_registration *cmd_reg = p_new(valdtr->pool, struct sieve_command_registration, 1); cmd_reg->cmd_def = cmd_def; cmd_reg->ext = ext; hash_table_insert(valdtr->commands, identifier, cmd_reg); return cmd_reg; } void sieve_validator_register_command(struct sieve_validator *valdtr, const struct sieve_extension *ext, const struct sieve_command_def *cmd_def) { struct sieve_command_registration *cmd_reg = sieve_validator_find_command_registration( valdtr, cmd_def->identifier); if (cmd_reg == NULL) { cmd_reg = _sieve_validator_register_command( valdtr, ext, cmd_def, cmd_def->identifier); } else { cmd_reg->cmd_def = cmd_def; cmd_reg->ext = ext; } if (cmd_def->registered != NULL) cmd_def->registered(valdtr, ext, cmd_reg); } static void sieve_validator_register_unknown_command(struct sieve_validator *valdtr, const char *command) { struct sieve_command_registration *cmd_reg = sieve_validator_find_command_registration(valdtr, command); if (cmd_reg == NULL) { (void)_sieve_validator_register_command( valdtr, NULL, &unknown_command, command); } else { i_assert(cmd_reg->cmd_def == NULL); cmd_reg->cmd_def = &unknown_command; } } /*const struct sieve_command *sieve_validator_find_command (struct sieve_validator *valdtr, const char *command) { struct sieve_command_registration *cmd_reg = sieve_validator_find_command_registration(valdtr, command); return ( record == NULL ? NULL : record->command ); }*/ /* * Per-command tagged argument registry */ /* Dummy argument object to mark unknown arguments in the registry */ static bool _unknown_tag_validate(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg ATTR_UNUSED, struct sieve_command *tst ATTR_UNUSED) { i_unreached(); return FALSE; } static const struct sieve_argument_def _unknown_tag = { .identifier = "", .validate = _unknown_tag_validate, }; static inline bool _tag_registration_is_unknown(struct sieve_tag_registration *tag_reg) { return (tag_reg != NULL && tag_reg->tag_def == &_unknown_tag); } /* Registry functions */ static void _sieve_validator_register_tag(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg, const struct sieve_extension *ext, const struct sieve_argument_def *tag_def, const char *identifier, int id_code) { struct sieve_tag_registration *reg; reg = p_new(valdtr->pool, struct sieve_tag_registration, 1); reg->ext = ext; reg->tag_def = tag_def; reg->id_code = id_code; if (identifier == NULL) reg->identifier = tag_def->identifier; else reg->identifier = p_strdup(valdtr->pool, identifier); if (!array_is_created(&cmd_reg->normal_tags)) p_array_init(&cmd_reg->normal_tags, valdtr->pool, 4); array_append(&cmd_reg->normal_tags, ®, 1); } void sieve_validator_register_persistent_tag( struct sieve_validator *valdtr, const char *command, const struct sieve_extension *ext, const struct sieve_argument_def *tag_def) { /* Add the tag to the persistent tags list if necessary */ if (tag_def->validate_persistent != NULL) { struct sieve_command_registration *cmd_reg = sieve_validator_find_command_registration( valdtr, command); if (cmd_reg == NULL) { cmd_reg = _sieve_validator_register_command( valdtr, NULL, NULL, command); } struct sieve_tag_registration *reg; if (!array_is_created(&cmd_reg->persistent_tags)) { p_array_init(&cmd_reg->persistent_tags, valdtr->pool, 4); } else { struct sieve_tag_registration *reg_idx; /* Avoid dupplicate registration */ array_foreach_elem(&cmd_reg->persistent_tags, reg_idx) { if (reg_idx->tag_def == tag_def) return; } } reg = p_new(valdtr->pool, struct sieve_tag_registration, 1); reg->ext = ext; reg->tag_def = tag_def; reg->id_code = -1; array_append(&cmd_reg->persistent_tags, ®, 1); } } void sieve_validator_register_external_tag( struct sieve_validator *valdtr, const char *command, const struct sieve_extension *ext, const struct sieve_argument_def *tag_def, int id_code) { struct sieve_command_registration *cmd_reg = sieve_validator_find_command_registration(valdtr, command); if (cmd_reg == NULL) { cmd_reg = _sieve_validator_register_command( valdtr, NULL, NULL, command); } _sieve_validator_register_tag(valdtr, cmd_reg, ext, tag_def, NULL, id_code); } void sieve_validator_register_tag( struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg, const struct sieve_extension *ext, const struct sieve_argument_def *tag_def, int id_code) { if (tag_def->is_instance_of == NULL) { _sieve_validator_register_tag(valdtr, cmd_reg, ext, tag_def, NULL, id_code); } else { struct sieve_tag_registration *reg = p_new(valdtr->pool, struct sieve_tag_registration, 1); reg->ext = ext; reg->tag_def = tag_def; reg->id_code = id_code; if (!array_is_created(&cmd_reg->instanced_tags)) p_array_init(&cmd_reg->instanced_tags, valdtr->pool, 4); array_append(&cmd_reg->instanced_tags, ®, 1); } } static void sieve_validator_register_unknown_tag(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg, const char *tag) { _sieve_validator_register_tag(valdtr, cmd_reg, NULL, &_unknown_tag, tag, 0); } static struct sieve_tag_registration * _sieve_validator_command_tag_get(struct sieve_validator *valdtr, struct sieve_command *cmd, const char *tag, void **data) { struct sieve_command_registration *cmd_reg = cmd->reg; struct sieve_tag_registration * const *regs; unsigned int i, reg_count; /* First check normal tags */ if (array_is_created(&cmd_reg->normal_tags)) { regs = array_get(&cmd_reg->normal_tags, ®_count); for (i = 0; i < reg_count; i++) { if (regs[i]->tag_def != NULL && strcasecmp(regs[i]->identifier, tag) == 0) { return regs[i]; } } } /* Not found so far, try the instanced tags */ if (array_is_created(&cmd_reg->instanced_tags)) { regs = array_get(&cmd_reg->instanced_tags, ®_count); for (i = 0; i < reg_count; i++) { if (regs[i]->tag_def != NULL) { if (regs[i]->tag_def->is_instance_of( valdtr, cmd, regs[i]->ext, tag, data)) return regs[i]; } } } return NULL; } static bool sieve_validator_command_tag_exists(struct sieve_validator *valdtr, struct sieve_command *cmd, const char *tag) { return (_sieve_validator_command_tag_get(valdtr, cmd, tag, NULL) != NULL); } static struct sieve_tag_registration * sieve_validator_command_tag_get(struct sieve_validator *valdtr, struct sieve_command *cmd, struct sieve_ast_argument *arg, void **data) { const char *tag = sieve_ast_argument_tag(arg); return _sieve_validator_command_tag_get(valdtr, cmd, tag, data); } /* * Extension support */ static bool sieve_validator_extensions_check_conficts(struct sieve_validator *valdtr, struct sieve_ast_argument *ext_arg, const struct sieve_extension *ext) { struct sieve_validator_extension_reg *ext_reg; struct sieve_validator_extension_reg *regs; unsigned int count, i; if (ext->id < 0) return TRUE; ext_reg = array_idx_get_space(&valdtr->extensions, (unsigned int) ext->id); regs = array_get_modifiable(&valdtr->extensions, &count); for (i = 0; i < count; i++) { bool required = ext_reg->required && regs[i].required; if (regs[i].ext == NULL) continue; if (regs[i].ext == ext) continue; if (!regs[i].loaded) continue; /* Check this extension vs other extension */ if (ext_reg->valext != NULL && ext_reg->valext->check_conflict != NULL) { struct sieve_ast_argument *this_ext_arg = (ext_arg == NULL ? regs[i].arg : ext_arg); if (!ext_reg->valext->check_conflict( ext, valdtr, ext_reg->context, this_ext_arg, regs[i].ext, required)) return FALSE; } /* Check other extension vs this extension */ if (regs[i].valext != NULL && regs[i].valext->check_conflict != NULL) { if (!regs[i].valext->check_conflict( regs[i].ext, valdtr, regs[i].context, regs[i].arg, ext, required)) return FALSE; } } return TRUE; } bool sieve_validator_extension_load(struct sieve_validator *valdtr, struct sieve_command *cmd, struct sieve_ast_argument *ext_arg, const struct sieve_extension *ext, bool required) { const struct sieve_extension_def *extdef = ext->def; struct sieve_validator_extension_reg *reg = NULL; if (ext->global && (valdtr->flags & SIEVE_COMPILE_FLAG_NOGLOBAL) != 0) { const char *cmd_prefix = (cmd == NULL ? "" : t_strdup_printf("%s %s: ", sieve_command_identifier(cmd), sieve_command_type_name(cmd))); sieve_argument_validate_error( valdtr, ext_arg, "%sfailed to load Sieve capability `%s': " "its use is restricted to global scripts", cmd_prefix, sieve_extension_name(ext)); return FALSE; } /* Register extension no matter what and store the * AST argument registering it */ if (ext->id >= 0) { reg = array_idx_get_space(&valdtr->extensions, (unsigned int)ext->id); i_assert(reg->ext == NULL || reg->ext == ext); reg->ext = ext; reg->required = reg->required || required; if (reg->arg == NULL) reg->arg = ext_arg; } if (extdef->validator_load != NULL && !extdef->validator_load(ext, valdtr)) { const char *cmd_prefix = (cmd == NULL ? "" : t_strdup_printf("%s %s: ", sieve_command_identifier(cmd), sieve_command_type_name(cmd))); sieve_argument_validate_error( valdtr, ext_arg, "%sfailed to load Sieve capability `%s'", cmd_prefix, sieve_extension_name(ext)); return FALSE; } /* Check conflicts with other extensions */ if (!sieve_validator_extensions_check_conficts(valdtr, ext_arg, ext)) return FALSE; /* Link extension to AST for use at code generation */ if (reg != NULL) { sieve_ast_extension_link(valdtr->ast, ext, reg->required); reg->loaded = TRUE; } return TRUE; } const struct sieve_extension * sieve_validator_extension_load_by_name(struct sieve_validator *valdtr, struct sieve_command *cmd, struct sieve_ast_argument *ext_arg, const char *ext_name) { const struct sieve_extension *ext; ext = sieve_extension_get_by_name(valdtr->svinst, ext_name); if (ext == NULL || ext->def == NULL || !ext->enabled) { unsigned int i; bool core_test = FALSE; bool core_command = FALSE; for (i = 0; !core_command && i < sieve_core_commands_count; i++) { if (strcasecmp(sieve_core_commands[i]->identifier, ext_name) == 0) core_command = TRUE; } for (i = 0; !core_test && i < sieve_core_tests_count; i++) { if (strcasecmp(sieve_core_tests[i]->identifier, ext_name) == 0) core_test = TRUE; } if (core_test || core_command) { sieve_argument_validate_error( valdtr, ext_arg, "%s %s: `%s' is not known as a Sieve capability, " "but it is known as a Sieve %s that is always available", sieve_command_identifier(cmd), sieve_command_type_name(cmd), str_sanitize(ext_name, 128), (core_test ? "test" : "command")); } else { sieve_argument_validate_error( valdtr, ext_arg, "%s %s: unknown Sieve capability `%s'", sieve_command_identifier(cmd), sieve_command_type_name(cmd), str_sanitize(ext_name, 128)); } return NULL; } if (!sieve_validator_extension_load(valdtr, cmd, ext_arg, ext, TRUE)) return NULL; return ext; } const struct sieve_extension * sieve_validator_extension_load_implicit(struct sieve_validator *valdtr, const char *ext_name) { const struct sieve_extension *ext; ext = sieve_extension_get_by_name(valdtr->svinst, ext_name); if (ext == NULL || ext->def == NULL) return NULL; if (!sieve_validator_extension_load(valdtr, NULL, NULL, ext, TRUE)) return NULL; return ext; } void sieve_validator_extension_register( struct sieve_validator *valdtr, const struct sieve_extension *ext, const struct sieve_validator_extension *valext, void *context) { struct sieve_validator_extension_reg *reg; if (ext->id < 0) return; reg = array_idx_get_space(&valdtr->extensions, (unsigned int) ext->id); i_assert(reg->ext == NULL || reg->ext == ext); reg->ext = ext; reg->valext = valext; reg->context = context; } bool sieve_validator_extension_loaded(struct sieve_validator *valdtr, const struct sieve_extension *ext) { const struct sieve_validator_extension_reg *reg; if (ext->id < 0 || ext->id >= (int) array_count(&valdtr->extensions)) return FALSE; reg = array_idx(&valdtr->extensions, (unsigned int) ext->id); return (reg->loaded); } void sieve_validator_extension_set_context(struct sieve_validator *valdtr, const struct sieve_extension *ext, void *context) { struct sieve_validator_extension_reg *reg; if (ext->id < 0) return; reg = array_idx_get_space(&valdtr->extensions, (unsigned int) ext->id); reg->context = context; } void *sieve_validator_extension_get_context(struct sieve_validator *valdtr, const struct sieve_extension *ext) { const struct sieve_validator_extension_reg *reg; if (ext->id < 0 || ext->id >= (int) array_count(&valdtr->extensions)) return NULL; reg = array_idx(&valdtr->extensions, (unsigned int) ext->id); return reg->context; } /* * Overriding the default literal arguments */ void sieve_validator_argument_override(struct sieve_validator *valdtr, enum sieve_argument_type type, const struct sieve_extension *ext, const struct sieve_argument_def *arg_def) { struct sieve_default_argument *arg; if (valdtr->default_arguments[type].arg_def != NULL) { arg = p_new(valdtr->pool, struct sieve_default_argument, 1); *arg = valdtr->default_arguments[type]; valdtr->default_arguments[type].overrides = arg; } valdtr->default_arguments[type].arg_def = arg_def; valdtr->default_arguments[type].ext = ext; } static bool sieve_validator_argument_default_activate(struct sieve_validator *valdtr, struct sieve_command *cmd, struct sieve_default_argument *defarg, struct sieve_ast_argument *arg) { bool result = TRUE; struct sieve_default_argument *prev_defarg; prev_defarg = valdtr->current_defarg; valdtr->current_defarg = defarg; if (arg->argument == NULL) { arg->argument = sieve_argument_create(arg->ast, defarg->arg_def, defarg->ext, 0); } else { arg->argument->def = defarg->arg_def; arg->argument->ext = defarg->ext; } if (defarg->arg_def != NULL && defarg->arg_def->validate != NULL) result = defarg->arg_def->validate(valdtr, &arg, cmd); valdtr->current_defarg = prev_defarg; return result; } bool sieve_validator_argument_activate_super(struct sieve_validator *valdtr, struct sieve_command *cmd, struct sieve_ast_argument *arg, bool constant ATTR_UNUSED) { struct sieve_default_argument *defarg; if (valdtr->current_defarg == NULL || valdtr->current_defarg->overrides == NULL) return FALSE; if (valdtr->current_defarg->overrides->arg_def == &string_argument) { switch (valdtr->current_defarg_type) { case SAT_CONST_STRING: if (!valdtr->current_defarg_constant) { valdtr->current_defarg_type = SAT_VAR_STRING; defarg = &valdtr->default_arguments[SAT_VAR_STRING]; } else { defarg = valdtr->current_defarg->overrides; } break; case SAT_VAR_STRING: defarg = valdtr->current_defarg->overrides; break; default: return FALSE; } } else { defarg = valdtr->current_defarg->overrides; } return sieve_validator_argument_default_activate(valdtr, cmd, defarg, arg); } /* * Argument Validation API */ bool sieve_validator_argument_activate(struct sieve_validator *valdtr, struct sieve_command *cmd, struct sieve_ast_argument *arg, bool constant) { struct sieve_default_argument *defarg; switch (sieve_ast_argument_type(arg)) { case SAAT_NUMBER: valdtr->current_defarg_type = SAT_NUMBER; break; case SAAT_STRING: valdtr->current_defarg_type = SAT_CONST_STRING; break; case SAAT_STRING_LIST: valdtr->current_defarg_type = SAT_STRING_LIST; break; default: return FALSE; } valdtr->current_defarg_constant = constant; defarg = &valdtr->default_arguments[valdtr->current_defarg_type]; if (!constant && defarg->arg_def == &string_argument) { valdtr->current_defarg_type = SAT_VAR_STRING; defarg = &valdtr->default_arguments[SAT_VAR_STRING]; } return sieve_validator_argument_default_activate(valdtr, cmd, defarg, arg); } bool sieve_validate_positional_argument(struct sieve_validator *valdtr, struct sieve_command *cmd, struct sieve_ast_argument *arg, const char *arg_name, unsigned int arg_pos, enum sieve_ast_argument_type req_type) { i_assert(arg != NULL); if (sieve_ast_argument_type(arg) != req_type && (sieve_ast_argument_type(arg) != SAAT_STRING || req_type != SAAT_STRING_LIST)) { sieve_argument_validate_error( valdtr, arg, "the %s %s expects %s as argument %d (%s), " "but %s was found", sieve_command_identifier(cmd), sieve_command_type_name(cmd), sieve_ast_argument_type_name(req_type), arg_pos, arg_name, sieve_ast_argument_name(arg)); return FALSE; } return TRUE; } bool sieve_validate_tag_parameter(struct sieve_validator *valdtr, struct sieve_command *cmd, struct sieve_ast_argument *tag, struct sieve_ast_argument *param, const char *arg_name, unsigned int arg_pos, enum sieve_ast_argument_type req_type, bool constant) { i_assert(tag != NULL); if (param == NULL) { const char *position = (arg_pos == 0 ? "" : t_strdup_printf(" %d (%s)", arg_pos, arg_name)); sieve_argument_validate_error( valdtr, tag, "the :%s tag for the %s %s requires %s as parameter%s, " "but no parameters were found", sieve_ast_argument_tag(tag), sieve_command_identifier(cmd), sieve_command_type_name(cmd), sieve_ast_argument_type_name(req_type), position); return FALSE; } if (sieve_ast_argument_type(param) != req_type && (sieve_ast_argument_type(param) != SAAT_STRING || req_type != SAAT_STRING_LIST)) { const char *position = (arg_pos == 0 ? "" : t_strdup_printf(" %d (%s)", arg_pos, arg_name)); sieve_argument_validate_error( valdtr, param, "the :%s tag for the %s %s requires %s as parameter%s, " "but %s was found", sieve_ast_argument_tag(tag), sieve_command_identifier(cmd), sieve_command_type_name(cmd), sieve_ast_argument_type_name(req_type), position, sieve_ast_argument_name(param)); return FALSE; } if (!sieve_validator_argument_activate(valdtr, cmd, param, constant)) return FALSE; param->argument->id_code = tag->argument->id_code; return TRUE; } /* * Command argument validation */ static bool sieve_validate_command_arguments(struct sieve_validator *valdtr, struct sieve_command *cmd) { int arg_count = cmd->def->positional_args; int real_count = 0; struct sieve_ast_argument *arg; struct sieve_command_registration *cmd_reg = cmd->reg; /* Resolve tagged arguments */ arg = sieve_ast_argument_first(cmd->ast_node); while (arg != NULL) { void *arg_data = NULL; struct sieve_tag_registration *tag_reg; const struct sieve_argument_def *tag_def; if (sieve_ast_argument_type(arg) != SAAT_TAG) { arg = sieve_ast_argument_next(arg); continue; } tag_reg = sieve_validator_command_tag_get(valdtr, cmd, arg, &arg_data); if (tag_reg == NULL) { sieve_argument_validate_error( valdtr, arg, "unknown tagged argument ':%s' for the %s %s " "(reported only once at first occurrence)", sieve_ast_argument_tag(arg), sieve_command_identifier(cmd), sieve_command_type_name(cmd)); sieve_validator_register_unknown_tag( valdtr, cmd_reg, sieve_ast_argument_tag(arg)); return FALSE; } /* Check whether previously tagged as unknown */ if (_tag_registration_is_unknown(tag_reg)) return FALSE; tag_def = tag_reg->tag_def; /* Assign the tagged argument type to the ast for later reference */ arg->argument = sieve_argument_create( arg->ast, tag_def, tag_reg->ext, tag_reg->id_code); arg->argument->data = arg_data; arg = sieve_ast_argument_next(arg); } /* Validate tagged arguments */ arg = sieve_ast_argument_first(cmd->ast_node); while (arg != NULL && sieve_ast_argument_type(arg) == SAAT_TAG) { const struct sieve_argument_def *tag_def = arg->argument->def; struct sieve_ast_argument *parg; /* Scan backwards for any duplicates */ if ((tag_def->flags & SIEVE_ARGUMENT_FLAG_MULTIPLE) == 0) { parg = sieve_ast_argument_prev(arg); while (parg != NULL) { if ((sieve_ast_argument_type(parg) == SAAT_TAG && parg->argument->def == tag_def) || (arg->argument->id_code > 0 && parg->argument != NULL && parg->argument->id_code == arg->argument->id_code)) { const char *tag_id = sieve_ast_argument_tag(arg); const char *tag_desc = strcmp(tag_def->identifier, tag_id) != 0 ? t_strdup_printf("%s argument (:%s)", tag_def->identifier, tag_id) : t_strdup_printf(":%s argument", tag_def->identifier); sieve_argument_validate_error( valdtr, arg, "encountered duplicate %s for the %s %s", tag_desc, sieve_command_identifier(cmd), sieve_command_type_name(cmd)); return FALSE; } parg = sieve_ast_argument_prev(parg); } } /* Call the validation function for the tag (if present) Fail if the validation fails: Let's not whine multiple times about a single command having multiple bad arguments... */ if (tag_def->validate != NULL) { if (!tag_def->validate(valdtr, &arg, cmd)) return FALSE; } else { arg = sieve_ast_argument_next(arg); } } /* Remaining arguments should be positional (tags are not allowed here) */ cmd->first_positional = arg; while (arg != NULL) { if (sieve_ast_argument_type(arg) == SAAT_TAG) { sieve_argument_validate_error( valdtr, arg, "encountered an unexpected tagged argument ':%s' " "while validating positional arguments for the %s %s", sieve_ast_argument_tag(arg), sieve_command_identifier(cmd), sieve_command_type_name(cmd)); return FALSE; } real_count++; arg = sieve_ast_argument_next(arg); } /* Check the required count versus the real number of arguments */ if (arg_count >= 0 && real_count != arg_count) { sieve_command_validate_error( valdtr, cmd, "the %s %s requires %d positional argument(s), " "but %d is/are specified", sieve_command_identifier(cmd), sieve_command_type_name(cmd), arg_count, real_count); return FALSE; } /* Call initial validation for persistent arguments */ if (array_is_created(&cmd_reg->persistent_tags)) { struct sieve_tag_registration * const *regs; unsigned int i, reg_count; regs = array_get(&cmd_reg->persistent_tags, ®_count); for (i = 0; i < reg_count; i++) { const struct sieve_argument_def *tag_def = regs[i]->tag_def; if (tag_def != NULL && tag_def->validate_persistent != NULL) { /* To be sure */ if (!tag_def->validate_persistent( valdtr, cmd, regs[i]->ext)) return FALSE; } } } return TRUE; } static bool sieve_validate_arguments_context(struct sieve_validator *valdtr, struct sieve_command *cmd) { struct sieve_ast_argument *arg = sieve_command_first_argument(cmd); while (arg != NULL) { const struct sieve_argument *argument = arg->argument; if (argument != NULL && argument->def != NULL && argument->def->validate_context != NULL) { if (!argument->def->validate_context(valdtr, arg, cmd)) return FALSE; } arg = sieve_ast_argument_next(arg); } return TRUE; } /* * Command Validation API */ static bool sieve_validate_command_subtests(struct sieve_validator *valdtr, struct sieve_command *cmd, const unsigned int count) { switch (count) { case 0: if (sieve_ast_test_count(cmd->ast_node) > 0) { /* Unexpected command specified */ enum sieve_command_type ctype = SCT_NONE; struct sieve_command_registration *cmd_reg; struct sieve_ast_node *test = sieve_ast_test_first(cmd->ast_node); cmd_reg = sieve_validator_find_command_registration( valdtr, test->identifier); /* First check what we are dealing with */ if (cmd_reg != NULL && cmd_reg->cmd_def != NULL) ctype = cmd_reg->cmd_def->type; switch (ctype) { case SCT_TEST: /* Spurious test */ case SCT_HYBRID: sieve_command_validate_error( valdtr, cmd, "the %s %s accepts no sub-tests, " "but tests are specified", sieve_command_identifier(cmd), sieve_command_type_name(cmd)); break; case SCT_NONE: /* Unknown command */ /* Is it perhaps a tag for which the ':' was omitted ? */ if (sieve_validator_command_tag_exists( valdtr, cmd, test->identifier)) { sieve_command_validate_error( valdtr, cmd, "missing colon ':' before ':%s' tag in %s %s", test->identifier, sieve_command_identifier(cmd), sieve_command_type_name(cmd)); break; } /* Fall through */ case SCT_COMMAND: sieve_command_validate_error( valdtr, cmd, "missing semicolon ';' after %s %s", sieve_command_identifier(cmd), sieve_command_type_name(cmd)); break; } return FALSE; } break; case 1: if (sieve_ast_test_count(cmd->ast_node) == 0) { sieve_command_validate_error( valdtr, cmd, "the %s %s requires one sub-test, " "but none is specified", sieve_command_identifier(cmd), sieve_command_type_name(cmd)); return FALSE; } else if (sieve_ast_test_count(cmd->ast_node) > 1 || cmd->ast_node->test_list) { sieve_command_validate_error( valdtr, cmd, "the %s %s requires one sub-test, " "but a list of tests is specified", sieve_command_identifier(cmd), sieve_command_type_name(cmd)); return FALSE; } break; default: if (sieve_ast_test_count(cmd->ast_node) == 0) { sieve_command_validate_error( valdtr, cmd, "the %s %s requires a list of sub-tests, " "but none is specified", sieve_command_identifier(cmd), sieve_command_type_name(cmd)); return FALSE; } else if (sieve_ast_test_count(cmd->ast_node) == 1 && !cmd->ast_node->test_list) { sieve_command_validate_error( valdtr, cmd, "the %s %s requires a list of sub-tests, " "but a single test is specified", sieve_command_identifier(cmd), sieve_command_type_name(cmd)); return FALSE; } break; } return TRUE; } static bool sieve_validate_command_block(struct sieve_validator *valdtr, struct sieve_command *cmd, bool block_allowed, bool block_required) { i_assert(cmd->ast_node->type == SAT_COMMAND); if (block_required) { if (!cmd->ast_node->block) { sieve_command_validate_error( valdtr, cmd, "the %s command requires a command block, " "but it is missing", sieve_command_identifier(cmd)); return FALSE; } } else if (!block_allowed && cmd->ast_node->block) { sieve_command_validate_error( valdtr, cmd, "the %s command does not accept a command block, " "but one is specified anyway", sieve_command_identifier(cmd)); return FALSE; } return TRUE; } /* * AST Validation */ static bool sieve_validate_test_list(struct sieve_validator *valdtr, struct sieve_ast_node *test_list, int *const_r); static bool sieve_validate_block(struct sieve_validator *valdtr, struct sieve_ast_node *block); static bool sieve_validate_command(struct sieve_validator *valdtr, struct sieve_ast_node *cmd_node, int *const_r); static bool sieve_validate_command_context(struct sieve_validator *valdtr, struct sieve_ast_node *cmd_node) { enum sieve_ast_type ast_type = sieve_ast_node_type(cmd_node); struct sieve_command_registration *cmd_reg; i_assert(ast_type == SAT_TEST || ast_type == SAT_COMMAND); /* Verify the command specified by this node */ cmd_reg = sieve_validator_find_command_registration( valdtr, cmd_node->identifier); if (cmd_reg != NULL && cmd_reg->cmd_def != NULL) { const struct sieve_command_def *cmd_def = cmd_reg->cmd_def; /* Identifier = "" when the command was previously marked as unknown */ if (*(cmd_def->identifier) != '\0') { if ((cmd_def->type == SCT_COMMAND && ast_type == SAT_TEST) || (cmd_def->type == SCT_TEST && ast_type == SAT_COMMAND)) { sieve_validator_error( valdtr, cmd_node->source_line, "attempted to use %s '%s' as %s", sieve_command_def_type_name(cmd_def), cmd_node->identifier, sieve_ast_type_name(ast_type)); return FALSE; } cmd_node->command = sieve_command_create( cmd_node, cmd_reg->ext, cmd_def, cmd_reg); } else { return FALSE; } } else { sieve_validator_error( valdtr, cmd_node->source_line, "unknown %s '%s' (only reported once at first occurrence)", sieve_ast_type_name(ast_type), cmd_node->identifier); sieve_validator_register_unknown_command( valdtr, cmd_node->identifier); return FALSE; } return TRUE; } static bool sieve_validate_command(struct sieve_validator *valdtr, struct sieve_ast_node *cmd_node, int *const_r) { enum sieve_ast_type ast_type = sieve_ast_node_type(cmd_node); struct sieve_command *cmd = (cmd_node == NULL ? NULL : cmd_node->command); const struct sieve_command_def *cmd_def = (cmd != NULL ? cmd->def : NULL); bool result = TRUE; i_assert(ast_type == SAT_TEST || ast_type == SAT_COMMAND); if (cmd_def != NULL && *(cmd_def->identifier) != '\0') { if (cmd_def->pre_validate == NULL || cmd_def->pre_validate(valdtr, cmd)) { /* Check argument syntax */ if (!sieve_validate_command_arguments(valdtr, cmd)) { result = FALSE; /* A missing ':' causes a tag to become a test. This can be the cause of the arguments validation failing. Therefore we must produce an error for the sub-tests as well if appropriate. */ (void)sieve_validate_command_subtests( valdtr, cmd, cmd_def->subtests); } else if (!sieve_validate_command_subtests( valdtr, cmd, cmd_def->subtests) || (ast_type == SAT_COMMAND && !sieve_validate_command_block( valdtr, cmd, cmd_def->block_allowed, cmd_def->block_required))) { result = FALSE; } else { /* Call command validation function if specified */ if (cmd_def->validate != NULL) { result = cmd_def->validate(valdtr, cmd) && result; } } } else { /* If pre-validation fails, don't bother to validate further as context might be missing and doing so is not very useful for further error reporting anyway */ return FALSE; } result = result && sieve_validate_arguments_context(valdtr, cmd); } /* * Descend further into the AST */ if (cmd_def != NULL) { /* Tests */ if (cmd_def->subtests > 0) { if (result || sieve_errors_more_allowed(valdtr->ehandler)) { result = sieve_validate_test_list( valdtr, cmd_node, const_r) && result; } } else if (result) { if (cmd_def->validate_const != NULL) { (void)cmd_def->validate_const( valdtr, cmd, const_r, -1); } else { *const_r = -1; } } /* Skip block if result of test is const FALSE */ if (result && *const_r == 0) return TRUE; /* Command block */ if (cmd_def->block_allowed && ast_type == SAT_COMMAND && (result || sieve_errors_more_allowed(valdtr->ehandler))) { result = sieve_validate_block(valdtr, cmd_node) && result; } } return result; } static bool sieve_validate_test_list(struct sieve_validator *valdtr, struct sieve_ast_node *test_node, int *const_r) { struct sieve_command *tst = test_node->command; const struct sieve_command_def *tst_def = (tst != NULL ? tst->def : NULL); struct sieve_ast_node *test; bool result = TRUE; if (tst_def != NULL && tst_def->validate_const != NULL) { if (!tst_def->validate_const(valdtr, tst, const_r, -2)) return TRUE; } test = sieve_ast_test_first(test_node); while (test != NULL && (result || sieve_errors_more_allowed(valdtr->ehandler))) { int const_value = -2; result = sieve_validate_command_context(valdtr, test) && sieve_validate_command(valdtr, test, &const_value) && result; if (result) { if (tst_def != NULL && tst_def->validate_const != NULL) { if (!tst_def->validate_const( valdtr, tst, const_r, const_value)) return TRUE; } else { *const_r = -1; } } if (result && const_value >= 0) test = sieve_ast_node_detach(test); else test = sieve_ast_test_next(test); } return result; } static bool sieve_validate_block(struct sieve_validator *valdtr, struct sieve_ast_node *block) { bool result = TRUE, fatal = FALSE; struct sieve_ast_node *cmd_node, *next; T_BEGIN { cmd_node = sieve_ast_command_first(block); while (!fatal && cmd_node != NULL && (result || sieve_errors_more_allowed(valdtr->ehandler))) { bool command_success; int const_value = -2; next = sieve_ast_command_next(cmd_node); /* Check if this is the first non-require command */ if (sieve_ast_node_type(block) == SAT_ROOT && !valdtr->finished_require && strcasecmp(cmd_node->identifier, cmd_require.identifier) != 0) { const struct sieve_validator_extension_reg *extrs; const struct sieve_extension *const *exts; unsigned int ext_count, i; valdtr->finished_require = TRUE; /* Load implicit extensions */ exts = sieve_extensions_get_all(valdtr->svinst, &ext_count); for (i = 0; i < ext_count; i++) { if (exts[i]->implicit) { (void)sieve_validator_extension_load( valdtr, NULL, NULL, exts[i], TRUE); } } /* Validate all 'require'd extensions */ extrs = array_get(&valdtr->extensions, &ext_count); for (i = 0; i < ext_count; i++) { if (extrs[i].loaded && extrs[i].valext != NULL && extrs[i].valext->validate != NULL) { if (!extrs[i].valext->validate( extrs[i].ext, valdtr, extrs[i].context, extrs[i].arg, extrs[i].required)) { fatal = TRUE; break; } } } } command_success = sieve_validate_command_context(valdtr, cmd_node); result = command_success && result; result = !fatal && sieve_validate_command(valdtr, cmd_node, &const_value) && result; cmd_node = next; } } T_END; return result && !fatal; } bool sieve_validator_run(struct sieve_validator *valdtr) { return sieve_validate_block(valdtr, sieve_ast_root(valdtr->ast)); } /* * Validator object registry */ struct sieve_validator_object_reg { const struct sieve_object_def *obj_def; const struct sieve_extension *ext; }; struct sieve_validator_object_registry { struct sieve_validator *valdtr; ARRAY(struct sieve_validator_object_reg) registrations; }; struct sieve_validator_object_registry * sieve_validator_object_registry_get(struct sieve_validator *valdtr, const struct sieve_extension *ext) { return (struct sieve_validator_object_registry *) sieve_validator_extension_get_context(valdtr, ext); } void sieve_validator_object_registry_add( struct sieve_validator_object_registry *regs, const struct sieve_extension *ext, const struct sieve_object_def *obj_def) { struct sieve_validator_object_reg *reg; reg = array_append_space(®s->registrations); reg->ext = ext; reg->obj_def = obj_def; } bool sieve_validator_object_registry_find( struct sieve_validator_object_registry *regs, const char *identifier, struct sieve_object *obj) { unsigned int i; for (i = 0; i < array_count(®s->registrations); i++) { const struct sieve_validator_object_reg *reg = array_idx(®s->registrations, i); if (strcasecmp(reg->obj_def->identifier, identifier) == 0) { if (obj != NULL) { obj->def = reg->obj_def; obj->ext = reg->ext; } return TRUE; } } return FALSE; } struct sieve_validator_object_registry * sieve_validator_object_registry_create(struct sieve_validator *valdtr) { pool_t pool = valdtr->pool; struct sieve_validator_object_registry *regs = p_new(pool, struct sieve_validator_object_registry, 1); /* Setup registry */ p_array_init(®s->registrations, valdtr->pool, 4); regs->valdtr = valdtr; return regs; } struct sieve_validator_object_registry * sieve_validator_object_registry_init(struct sieve_validator *valdtr, const struct sieve_extension *ext) { struct sieve_validator_object_registry *regs = sieve_validator_object_registry_create(valdtr); sieve_validator_extension_set_context(valdtr, ext, regs); return regs; } /* * Error handling */ #undef sieve_validator_error void sieve_validator_error(struct sieve_validator *valdtr, const char *csrc_filename, unsigned int csrc_linenum, unsigned int source_line, const char *fmt, ...) { struct sieve_error_params params = { .log_type = LOG_TYPE_ERROR, .csrc = { .filename = csrc_filename, .linenum = csrc_linenum, }, }; va_list args; params.location = sieve_error_script_location(valdtr->script, source_line); va_start(args, fmt); sieve_logv(valdtr->ehandler, ¶ms, fmt, args); va_end(args); } #undef sieve_validator_warning void sieve_validator_warning(struct sieve_validator *valdtr, const char *csrc_filename, unsigned int csrc_linenum, unsigned int source_line, const char *fmt, ...) { struct sieve_error_params params = { .log_type = LOG_TYPE_WARNING, .csrc = { .filename = csrc_filename, .linenum = csrc_linenum, }, }; va_list args; params.location = sieve_error_script_location(valdtr->script, source_line); va_start(args, fmt); sieve_logv(valdtr->ehandler, ¶ms, fmt, args); va_end(args); } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-commands.c0000644000000000000000000002360214103200126023407 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "str-sanitize.h" #include "rfc2822.h" #include "sieve-common.h" #include "sieve-ast.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-binary.h" #include "sieve-commands.h" #include "sieve-code.h" #include "sieve-interpreter.h" /* * Literal arguments */ /* Forward declarations */ static bool arg_number_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *context); static bool arg_string_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *context); static bool arg_string_list_validate (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *context); static bool arg_string_list_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *context); /* Argument objects */ const struct sieve_argument_def number_argument = { .identifier = "@number", .generate = arg_number_generate }; const struct sieve_argument_def string_argument = { .identifier = "@string", .generate = arg_string_generate }; const struct sieve_argument_def string_list_argument = { .identifier = "@string-list", .validate = arg_string_list_validate, .generate = arg_string_list_generate }; /* Argument implementations */ static bool arg_number_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd ATTR_UNUSED) { sieve_opr_number_emit(cgenv->sblock, sieve_ast_argument_number(arg)); return TRUE; } static bool arg_string_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd ATTR_UNUSED) { sieve_opr_string_emit(cgenv->sblock, sieve_ast_argument_str(arg)); return TRUE; } static bool arg_string_list_validate (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *cmd) { struct sieve_ast_argument *stritem; stritem = sieve_ast_strlist_first(*arg); while ( stritem != NULL ) { if ( !sieve_validator_argument_activate(valdtr, cmd, stritem, FALSE) ) return FALSE; stritem = sieve_ast_strlist_next(stritem); } return TRUE; } static bool emit_string_list_operand (const struct sieve_codegen_env *cgenv, const struct sieve_ast_argument *strlist, struct sieve_command *cmd) { void *list_context; struct sieve_ast_argument *stritem; sieve_opr_stringlist_emit_start (cgenv->sblock, sieve_ast_strlist_count(strlist), &list_context); stritem = sieve_ast_strlist_first(strlist); while ( stritem != NULL ) { if ( !sieve_generate_argument(cgenv, stritem, cmd) ) return FALSE; stritem = sieve_ast_strlist_next(stritem); } sieve_opr_stringlist_emit_end(cgenv->sblock, list_context); return TRUE; } static bool arg_string_list_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd) { if ( sieve_ast_argument_type(arg) == SAAT_STRING ) { return ( sieve_generate_argument(cgenv, arg, cmd) ); } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) { bool result = TRUE; if ( sieve_ast_strlist_count(arg) == 1 ) return ( sieve_generate_argument (cgenv, sieve_ast_strlist_first(arg), cmd) ); else { T_BEGIN { result=emit_string_list_operand(cgenv, arg, cmd); } T_END; } return result; } return FALSE; } /* * Abstract arguments * * (Generated by processing and not by parsing the grammar) */ /* Catenated string */ struct sieve_arg_catenated_string { struct sieve_ast_arg_list *str_parts; }; struct sieve_arg_catenated_string *sieve_arg_catenated_string_create (struct sieve_ast_argument *orig_arg) { pool_t pool = sieve_ast_pool(orig_arg->ast); struct sieve_ast_arg_list *arglist; struct sieve_arg_catenated_string *catstr; arglist = sieve_ast_arg_list_create(pool); catstr = p_new(pool, struct sieve_arg_catenated_string, 1); catstr->str_parts = arglist; (orig_arg)->argument->data = (void *) catstr; return catstr; } void sieve_arg_catenated_string_add_element (struct sieve_arg_catenated_string *catstr, struct sieve_ast_argument *element) { sieve_ast_arg_list_add(catstr->str_parts, element); } #define _cat_string_first(catstr) __AST_LIST_FIRST((catstr)->str_parts) #define _cat_string_count(catstr) __AST_LIST_COUNT((catstr)->str_parts) #define _cat_string_next(item) __AST_LIST_NEXT(item) bool sieve_arg_catenated_string_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd) { struct sieve_arg_catenated_string *catstr = (struct sieve_arg_catenated_string *) arg->argument->data; struct sieve_ast_argument *strpart; if ( _cat_string_count(catstr) == 1 ) sieve_generate_argument(cgenv, _cat_string_first(catstr), cmd); else { sieve_opr_catenated_string_emit(cgenv->sblock, _cat_string_count(catstr)); strpart = _cat_string_first(catstr); while ( strpart != NULL ) { if ( !sieve_generate_argument(cgenv, strpart, cmd) ) return FALSE; strpart = _cat_string_next(strpart); } } return TRUE; } /* * Argument creation */ struct sieve_argument *sieve_argument_create (struct sieve_ast *ast, const struct sieve_argument_def *def, const struct sieve_extension *ext, int id_code) { struct sieve_argument *arg; pool_t pool; pool = sieve_ast_pool(ast); arg = p_new(pool, struct sieve_argument, 1); arg->def = def; arg->ext = ext; arg->id_code = id_code; return arg; } /* * Core tests and commands */ const struct sieve_command_def *sieve_core_tests[] = { &tst_false, &tst_true, &tst_not, &tst_anyof, &tst_allof, &tst_address, &tst_header, &tst_exists, &tst_size }; const unsigned int sieve_core_tests_count = N_ELEMENTS(sieve_core_tests); const struct sieve_command_def *sieve_core_commands[] = { &cmd_require, &cmd_stop, &cmd_if, &cmd_elsif, &cmd_else, &cmd_keep, &cmd_discard, &cmd_redirect }; const unsigned int sieve_core_commands_count = N_ELEMENTS(sieve_core_commands); /* * Command context */ struct sieve_command *sieve_command_prev (struct sieve_command *cmd) { struct sieve_ast_node *node = sieve_ast_node_prev(cmd->ast_node); if ( node != NULL ) { return node->command; } return NULL; } struct sieve_command *sieve_command_parent (struct sieve_command *cmd) { struct sieve_ast_node *node = sieve_ast_node_parent(cmd->ast_node); return ( node != NULL ? node->command : NULL ); } struct sieve_command *sieve_command_create (struct sieve_ast_node *cmd_node, const struct sieve_extension *ext, const struct sieve_command_def *cmd_def, struct sieve_command_registration *cmd_reg) { struct sieve_command *cmd; cmd = p_new(sieve_ast_node_pool(cmd_node), struct sieve_command, 1); cmd->ast_node = cmd_node; cmd->def = cmd_def; cmd->ext = ext; cmd->reg = cmd_reg; cmd->block_exit_command = NULL; return cmd; } const char *sieve_command_def_type_name (const struct sieve_command_def *cmd_def) { switch ( cmd_def->type ) { case SCT_NONE: return "command of unspecified type (bug)"; case SCT_TEST: return "test"; case SCT_COMMAND: return "command"; case SCT_HYBRID: return "command or test"; default: break; } return "??COMMAND-TYPE??"; } const char *sieve_command_type_name (const struct sieve_command *cmd) { switch ( cmd->def->type ) { case SCT_NONE: return "command of unspecified type (bug)"; case SCT_TEST: return "test"; case SCT_COMMAND: return "command"; case SCT_HYBRID: if ( cmd->ast_node->type == SAT_TEST ) return "test"; return "command"; default: break; } return "??COMMAND-TYPE??"; } struct sieve_ast_argument *sieve_command_add_dynamic_tag (struct sieve_command *cmd, const struct sieve_extension *ext, const struct sieve_argument_def *tag, int id_code) { struct sieve_ast_argument *arg; if ( cmd->first_positional != NULL ) arg = sieve_ast_argument_tag_insert (cmd->first_positional, tag->identifier, cmd->ast_node->source_line); else arg = sieve_ast_argument_tag_create (cmd->ast_node, tag->identifier, cmd->ast_node->source_line); arg->argument = sieve_argument_create(cmd->ast_node->ast, tag, ext, id_code); return arg; } struct sieve_ast_argument *sieve_command_find_argument (struct sieve_command *cmd, const struct sieve_argument_def *arg_def) { struct sieve_ast_argument *arg = sieve_ast_argument_first(cmd->ast_node); /* Visit tagged and optional arguments */ while ( arg != NULL ) { if ( arg->argument != NULL && arg->argument->def == arg_def ) return arg; arg = sieve_ast_argument_next(arg); } return arg; } /* Use this function with caution. The command commits to exiting the block. * When it for some reason does not, the interpretation will break later on, * because exiting jumps are not generated when they would otherwise be * necessary. */ void sieve_command_exit_block_unconditionally (struct sieve_command *cmd) { struct sieve_command *parent = sieve_command_parent(cmd); /* Only the first unconditional exit is of importance */ if ( parent != NULL && parent->block_exit_command == NULL ) parent->block_exit_command = cmd; } bool sieve_command_block_exits_unconditionally (struct sieve_command *cmd) { return ( cmd->block_exit_command != NULL ); } /* * Command utility functions */ /* NOTE: this may be moved */ static int _verify_header_name_item (void *context, struct sieve_ast_argument *header) { struct sieve_validator *valdtr = (struct sieve_validator *) context; string_t *name = sieve_ast_argument_str(header); if ( sieve_argument_is_string_literal(header) && !rfc2822_header_field_name_verify(str_c(name), str_len(name)) ) { sieve_argument_validate_warning (valdtr, header, "specified header field name '%s' is invalid", str_sanitize(str_c(name), 80)); return 0; } return 1; } bool sieve_command_verify_headers_argument (struct sieve_validator *valdtr, struct sieve_ast_argument *headers) { return ( sieve_ast_stringlist_map (&headers, (void *) valdtr, _verify_header_name_item) >= 0 ); } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-script.c0000644000000000000000000005366314103200126023124 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "compat.h" #include "unichar.h" #include "str.h" #include "str-sanitize.h" #include "hash.h" #include "array.h" #include "eacces-error.h" #include "istream.h" #include "sieve-common.h" #include "sieve-limits.h" #include "sieve-settings.h" #include "sieve-error.h" #include "sieve-dump.h" #include "sieve-binary.h" #include "sieve-storage-private.h" #include "sieve-script-private.h" /* * Script name */ bool sieve_script_name_is_valid(const char *scriptname) { ARRAY_TYPE(unichars) uni_name; unsigned int count, i; const unichar_t *name_chars; size_t namelen = strlen(scriptname); /* Check minimum length */ if (namelen == 0) return FALSE; /* Check worst-case maximum length */ if (namelen > SIEVE_MAX_SCRIPT_NAME_LEN * 4) return FALSE; /* Intialize array for unicode characters */ t_array_init(&uni_name, namelen * 4); /* Convert UTF-8 to UCS4/UTF-32 */ if (uni_utf8_to_ucs4(scriptname, &uni_name) < 0) return FALSE; name_chars = array_get(&uni_name, &count); /* Check true maximum length */ if (count > SIEVE_MAX_SCRIPT_NAME_LEN) return FALSE; /* Scan name for invalid characters * FIXME: compliance with Net-Unicode Definition (Section 2 of * RFC 5198) is not checked fully and no normalization * is performed. */ for (i = 0; i < count; i++) { /* 0000-001F; [CONTROL CHARACTERS] */ if (name_chars[i] <= 0x001f) return FALSE; /* 002F; SLASH (not RFC-prohibited, but '/' is dangerous) */ if (name_chars[i] == 0x002f) return FALSE; /* 007F; DELETE */ if (name_chars[i] == 0x007f) return FALSE; /* 0080-009F; [CONTROL CHARACTERS] */ if (name_chars[i] >= 0x0080 && name_chars[i] <= 0x009f) return FALSE; /* 00FF */ if (name_chars[i] == 0x00ff) return FALSE; /* 2028; LINE SEPARATOR */ /* 2029; PARAGRAPH SEPARATOR */ if (name_chars[i] == 0x2028 || name_chars[i] == 0x2029) return FALSE; } return TRUE; } /* * Script instance */ void sieve_script_init(struct sieve_script *script, struct sieve_storage *storage, const struct sieve_script *script_class, const char *location, const char *name) { i_assert(storage != NULL); script->script_class = script_class; script->refcount = 1; script->storage = storage; script->location = p_strdup_empty(script->pool, location); script->name = p_strdup(script->pool, name); script->event = event_create(storage->event); event_add_str(script->event, "script_name", name); event_add_str(script->event, "script_location", location); if (name == NULL) event_set_append_log_prefix(script->event, "script: "); else { event_set_append_log_prefix( script->event, t_strdup_printf("script `%s': ", name)); } sieve_storage_ref(storage); } struct sieve_script * sieve_script_create(struct sieve_instance *svinst, const char *location, const char *name, enum sieve_error *error_r) { struct sieve_storage *storage; struct sieve_script *script; enum sieve_error error; if (error_r != NULL) *error_r = SIEVE_ERROR_NONE; else error_r = &error; storage = sieve_storage_create(svinst, location, 0, error_r); if (storage == NULL) return NULL; script = sieve_storage_get_script(storage, name, error_r); sieve_storage_unref(&storage); return script; } void sieve_script_ref(struct sieve_script *script) { script->refcount++; } void sieve_script_unref(struct sieve_script **_script) { struct sieve_script *script = *_script; *_script = NULL; if (script == NULL) return; i_assert(script->refcount > 0); if (--script->refcount != 0) return; if (script->stream != NULL) { struct event_passthrough *e = event_create_passthrough(script->event)-> set_name("sieve_script_closed"); e_debug(e->event(), "Closed script"); } i_stream_unref(&script->stream); if (script->v.destroy != NULL) script->v.destroy(script); sieve_storage_unref(&script->storage); event_unref(&script->event); pool_unref(&script->pool); } int sieve_script_open(struct sieve_script *script, enum sieve_error *error_r) { enum sieve_error error; if (error_r != NULL) *error_r = SIEVE_ERROR_NONE; else error_r = &error; if (script->open) return 0; if (script->v.open(script, error_r) < 0) return -1; i_assert(script->location != NULL); i_assert(script->name != NULL); script->open = TRUE; if (*script->name != '\0') { e_debug(script->event, "Opened script `%s' from `%s'", script->name, script->location); } else { e_debug(script->event, "Opened nameless script from `%s'", script->location); } return 0; } int sieve_script_open_as(struct sieve_script *script, const char *name, enum sieve_error *error_r) { if (sieve_script_open(script, error_r) < 0) return -1; /* override name */ script->name = p_strdup(script->pool, name); event_add_str(script->event, "script_name", name); return 0; } struct sieve_script * sieve_script_create_open(struct sieve_instance *svinst, const char *location, const char *name, enum sieve_error *error_r) { struct sieve_script *script; script = sieve_script_create(svinst, location, name, error_r); if (script == NULL) return NULL; if (sieve_script_open(script, error_r) < 0) { sieve_script_unref(&script); return NULL; } return script; } int sieve_script_check(struct sieve_instance *svinst, const char *location, const char *name, enum sieve_error *error_r) { struct sieve_script *script; enum sieve_error error; if (error_r == NULL) error_r = &error; script = sieve_script_create_open(svinst, location, name, error_r); if (script == NULL) return (*error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1); sieve_script_unref(&script); return 1; } /* * Properties */ const char *sieve_script_name(const struct sieve_script *script) { return script->name; } const char *sieve_script_location(const struct sieve_script *script) { return script->location; } struct sieve_instance *sieve_script_svinst(const struct sieve_script *script) { return script->storage->svinst; } int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r) { struct istream *stream; int ret; if (script->v.get_size != NULL) { if ((ret = script->v.get_size(script, size_r)) != 0) return ret; } /* Try getting size from the stream */ if (script->stream == NULL && sieve_script_get_stream(script, &stream, NULL) < 0) return -1; if (i_stream_get_size(script->stream, TRUE, size_r) < 0) { sieve_storage_set_critical(script->storage, "i_stream_get_size(%s) failed: %s", i_stream_get_name(script->stream), i_stream_get_error(script->stream)); return -1; } return 0; } bool sieve_script_is_open(const struct sieve_script *script) { return script->open; } bool sieve_script_is_default(const struct sieve_script *script) { return script->storage->is_default; } /* * Stream management */ int sieve_script_get_stream(struct sieve_script *script, struct istream **stream_r, enum sieve_error *error_r) { struct sieve_storage *storage = script->storage; enum sieve_error error; int ret; if (error_r != NULL) *error_r = SIEVE_ERROR_NONE; else error_r = &error; if (script->stream != NULL) { *stream_r = script->stream; return 0; } // FIXME: necessary? i_assert(script->open); T_BEGIN { ret = script->v.get_stream(script, &script->stream, error_r); } T_END; if (ret < 0) { struct event_passthrough *e = event_create_passthrough(script->event)-> add_str("error", storage->error)-> set_name("sieve_script_opened"); e_debug(e->event(), "Failed to open script for reading: %s", storage->error); return -1; } struct event_passthrough *e = event_create_passthrough(script->event)-> set_name("sieve_script_opened"); e_debug(e->event(), "Opened script for reading"); *stream_r = script->stream; return 0; } /* * Comparison */ bool sieve_script_equals(const struct sieve_script *script, const struct sieve_script *other) { if (script == other) return TRUE; if (script == NULL || other == NULL) return FALSE; if (script->script_class != other->script_class) return FALSE; if (script->v.equals == NULL) { i_assert (script->location != NULL && other->location != NULL); return (strcmp(script->location, other->location) == 0); } return script->v.equals(script, other); } unsigned int sieve_script_hash(const struct sieve_script *script) { i_assert(script->name != NULL); return str_hash(script->name); } /* * Binary */ int sieve_script_binary_read_metadata(struct sieve_script *script, struct sieve_binary_block *sblock, sieve_size_t *offset) { struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); string_t *storage_class, *location; unsigned int version; if ((sieve_binary_block_get_size(sblock) - *offset) == 0) return 0; /* storage class */ if (!sieve_binary_read_string(sblock, offset, &storage_class)) { e_error(script->event, "Binary `%s' has invalid metadata for script `%s': " "Invalid storage class", sieve_binary_path(sbin), script->location); return -1; } if (strcmp(str_c(storage_class), script->driver_name) != 0) { e_debug(script->event, "Binary `%s' reports unexpected driver name for script `%s' " "(`%s' rather than `%s')", sieve_binary_path(sbin), script->location, str_c(storage_class), script->driver_name); return 0; } /* version */ if (!sieve_binary_read_unsigned(sblock, offset, &version)) { e_error(script->event, "Binary `%s' has invalid metadata for script `%s': " "Invalid version", sieve_binary_path(sbin), script->location); return -1; } if (script->storage->version != version) { e_debug(script->event, "Binary `%s' was compiled with " "a different version of the `%s' script storage class " "(compiled v%d, expected v%d; " "automatically fixed when re-compiled)", sieve_binary_path(sbin), script->driver_name, version, script->storage->version); return 0; } /* location */ if (!sieve_binary_read_string(sblock, offset, &location)) { e_error(script->event, "Binary `%s' has invalid metadata for script `%s': " "Invalid location", sieve_binary_path(sbin), script->location); return -1; } i_assert(script->location != NULL); if (strcmp(str_c(location), script->location) != 0) { e_debug(script->event, "Binary `%s' reports different location " "for script `%s' (binary points to `%s')", sieve_binary_path(sbin), script->location, str_c(location)); return 0; } if (script->v.binary_read_metadata == NULL) return 1; return script->v.binary_read_metadata(script, sblock, offset); } void sieve_script_binary_write_metadata(struct sieve_script *script, struct sieve_binary_block *sblock) { sieve_binary_emit_cstring(sblock, script->driver_name); sieve_binary_emit_unsigned(sblock, script->storage->version); sieve_binary_emit_cstring(sblock, (script->location == NULL ? "" : script->location)); if (script->v.binary_write_metadata == NULL) return; script->v.binary_write_metadata(script, sblock); } bool sieve_script_binary_dump_metadata(struct sieve_script *script, struct sieve_dumptime_env *denv, struct sieve_binary_block *sblock, sieve_size_t *offset) { struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock); struct sieve_instance *svinst = sieve_binary_svinst(sbin); string_t *storage_class, *location; struct sieve_script *adhoc_script = NULL; unsigned int version; bool result = TRUE; /* storage class */ if (!sieve_binary_read_string(sblock, offset, &storage_class)) return FALSE; sieve_binary_dumpf(denv, "class = %s\n", str_c(storage_class)); /* version */ if (!sieve_binary_read_unsigned(sblock, offset, &version)) return FALSE; sieve_binary_dumpf(denv, "class.version = %d\n", version); /* location */ if (!sieve_binary_read_string(sblock, offset, &location)) return FALSE; sieve_binary_dumpf(denv, "location = %s\n", str_c(location)); if (script == NULL) { script = adhoc_script = sieve_script_create(svinst, str_c(location), NULL, NULL); } if (script != NULL && script->v.binary_dump_metadata != NULL) { result = script->v.binary_dump_metadata( script, denv, sblock, offset); } if (adhoc_script != NULL) sieve_script_unref(&adhoc_script); return result; } struct sieve_binary * sieve_script_binary_load(struct sieve_script *script, enum sieve_error *error_r) { if (script->v.binary_load == NULL) { *error_r = SIEVE_ERROR_NOT_POSSIBLE; return NULL; } return script->v.binary_load(script, error_r); } int sieve_script_binary_save(struct sieve_script *script, struct sieve_binary *sbin, bool update, enum sieve_error *error_r) { struct sieve_script *bin_script = sieve_binary_script(sbin); enum sieve_error error; if (error_r != NULL) *error_r = SIEVE_ERROR_NONE; else error_r = &error; i_assert(bin_script == NULL || sieve_script_equals(bin_script, script)); if (script->v.binary_save == NULL) { *error_r = SIEVE_ERROR_NOT_POSSIBLE; return -1; } return script->v.binary_save(script, sbin, update, error_r); } const char *sieve_script_binary_get_prefix(struct sieve_script *script) { struct sieve_storage *storage = script->storage; if (storage->bin_dir != NULL && sieve_storage_setup_bindir(storage, 0700) >= 0) { return t_strconcat(storage->bin_dir, "/", script->name, NULL); } if (script->v.binary_get_prefix == NULL) return NULL; return script->v.binary_get_prefix(script); } /* * Management */ static int sieve_script_copy_from_default(struct sieve_script *script, const char *newname) { struct sieve_storage *storage = script->storage; struct istream *input; int ret; /* copy from default */ if ((ret = sieve_script_open(script, NULL)) < 0 || (ret = sieve_script_get_stream(script, &input, NULL)) < 0) { sieve_storage_copy_error(storage->default_for, storage); return ret; } ret = sieve_storage_save_as(storage->default_for, input, newname); if (ret < 0) { sieve_storage_copy_error(storage, storage->default_for); } else if (sieve_script_is_active(script) > 0) { struct sieve_script *newscript; enum sieve_error error; newscript = sieve_storage_open_script(storage->default_for, newname, &error); if (newscript == NULL) { /* Somehow not actually saved */ ret = (error == SIEVE_ERROR_NOT_FOUND ? 0 : -1); } else if (sieve_script_activate(newscript, (time_t)-1) < 0) { /* Failed to activate; roll back */ ret = -1; (void)sieve_script_delete(newscript, TRUE); } if (newscript != NULL) sieve_script_unref(&newscript); if (ret < 0) { e_error(storage->event, "Failed to implicitly activate script `%s' " "after rename", newname); sieve_storage_copy_error(storage->default_for, storage); } } return ret; } int sieve_script_rename(struct sieve_script *script, const char *newname) { struct sieve_storage *storage = script->storage; const char *oldname = script->name; struct event_passthrough *event; int ret; i_assert(newname != NULL); /* Check script name */ if (!sieve_script_name_is_valid(newname)) { sieve_script_set_error(script, SIEVE_ERROR_BAD_PARAMS, "Invalid new Sieve script name `%s'.", str_sanitize(newname, 80)); return -1; } i_assert(script->open); // FIXME: auto-open? if (storage->default_for == NULL) { i_assert((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0); /* rename script */ i_assert(script->v.rename != NULL); ret = script->v.rename(script, newname); /* rename INBOX mailbox attribute */ if (ret >= 0 && oldname != NULL) { (void)sieve_storage_sync_script_rename(storage, oldname, newname); } } else if (sieve_storage_check_script(storage->default_for, newname, NULL) > 0) { sieve_script_set_error(script, SIEVE_ERROR_EXISTS, "A sieve script with that name already exists."); sieve_storage_copy_error(storage->default_for, storage); ret = -1; } else { ret = sieve_script_copy_from_default(script, newname); } event = event_create_passthrough(script->event)-> clear_field("script_name")-> add_str("old_script_name", script->name)-> add_str("new_script_name", newname)-> set_name("sieve_script_renamed"); if (ret >= 0) { e_debug(event->event(), "Script renamed to `%s'", newname); } else { event = event->add_str("error", storage->error); e_debug(event->event(), "Failed to rename script: %s", storage->error); } return ret; } int sieve_script_delete(struct sieve_script *script, bool ignore_active) { struct sieve_storage *storage = script->storage; bool is_active = FALSE; int ret = 0; i_assert(script->open); // FIXME: auto-open? /* Is the requested script active? */ if (sieve_script_is_active(script) > 0) { is_active = TRUE; if (!ignore_active) { sieve_script_set_error(script, SIEVE_ERROR_ACTIVE, "Cannot delete the active Sieve script."); if (storage->default_for != NULL) { sieve_storage_copy_error(storage->default_for, storage); } return -1; } } /* Trying to delete the default script? */ if (storage->is_default) { /* ignore */ return 0; } i_assert((script->storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0); /* Deactivate it explicity */ if (ignore_active && is_active) (void)sieve_storage_deactivate(storage, (time_t)-1); i_assert(script->v.delete != NULL); ret = script->v.delete(script); if (ret >= 0) { struct event_passthrough *e = event_create_passthrough(script->event)-> set_name("sieve_script_deleted"); e_debug(e->event(), "Script deleted"); /* unset INBOX mailbox attribute */ (void)sieve_storage_sync_script_delete(storage, script->name); } else { struct event_passthrough *e = event_create_passthrough(script->event)-> add_str("error", storage->error)-> set_name("sieve_script_deleted"); e_debug(e->event(), "Failed to delete script: %s", storage->error); } return ret; } int sieve_script_is_active(struct sieve_script *script) { struct sieve_storage *storage = script->storage; /* Special handling if this is a default script */ if (storage->default_for != NULL) { int ret = sieve_storage_active_script_is_default( storage->default_for); if (ret < 0) sieve_storage_copy_error(storage, storage->default_for); return ret; } if (script->v.is_active == NULL) return 0; return script->v.is_active(script); } int sieve_script_activate(struct sieve_script *script, time_t mtime) { struct sieve_storage *storage = script->storage; int ret = 0; i_assert(script->open); // FIXME: auto-open? if (storage->default_for == NULL) { i_assert((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0); i_assert(script->v.activate != NULL); ret = script->v.activate(script); if (ret >= 0) { struct event_passthrough *e = event_create_passthrough(script->event)-> set_name("sieve_script_activated"); e_debug(e->event(), "Script activated"); sieve_storage_set_modified(storage, mtime); (void)sieve_storage_sync_script_activate(storage); } else { struct event_passthrough *e = event_create_passthrough(script->event)-> add_str("error", storage->error)-> set_name("sieve_script_activated"); e_debug(e->event(), "Failed to activate script: %s", storage->error); } } else { /* Activating the default script is equal to deactivating the storage */ ret = sieve_storage_deactivate(storage->default_for, (time_t)-1); if (ret < 0) sieve_storage_copy_error(storage, storage->default_for); } return ret; } /* * Error handling */ void sieve_script_set_error(struct sieve_script *script, enum sieve_error error, const char *fmt, ...) { struct sieve_storage *storage = script->storage; va_list va; sieve_storage_clear_error(storage); if (fmt != NULL) { va_start(va, fmt); storage->error = i_strdup_vprintf(fmt, va); va_end(va); } storage->error_code = error; } void sieve_script_set_internal_error(struct sieve_script *script) { sieve_storage_set_internal_error(script->storage); } void sieve_script_set_critical(struct sieve_script *script, const char *fmt, ...) { struct sieve_storage *storage = script->storage; va_list va; if (fmt != NULL) { if ((storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0) { va_start(va, fmt); e_error(script->event, "%s", t_strdup_vprintf(fmt, va)); va_end(va); sieve_storage_set_internal_error(storage); } else { sieve_storage_clear_error(storage); /* no user is involved while synchronizing, so do it the normal way */ va_start(va, fmt); storage->error = i_strdup_vprintf(fmt, va); va_end(va); storage->error_code = SIEVE_ERROR_TEMP_FAILURE; } } } const char * sieve_script_get_last_error(struct sieve_script *script, enum sieve_error *error_r) { return sieve_storage_get_last_error(script->storage, error_r); } const char *sieve_script_get_last_error_lcase(struct sieve_script *script) { return sieve_error_from_external(script->storage->error); } /* * Script sequence */ void sieve_script_sequence_init(struct sieve_script_sequence *seq, struct sieve_storage *storage) { seq->storage = storage; sieve_storage_ref(storage); } struct sieve_script_sequence * sieve_script_sequence_create(struct sieve_instance *svinst, const char *location, enum sieve_error *error_r) { struct sieve_storage *storage; struct sieve_script_sequence *seq; enum sieve_error error; if (error_r != NULL) *error_r = SIEVE_ERROR_NONE; else error_r = &error; storage = sieve_storage_create(svinst, location, 0, error_r); if (storage == NULL) return NULL; seq = sieve_storage_get_script_sequence(storage, error_r); sieve_storage_unref(&storage); return seq; } struct sieve_script * sieve_script_sequence_next(struct sieve_script_sequence *seq, enum sieve_error *error_r) { struct sieve_storage *storage = seq->storage; i_assert(storage->v.script_sequence_next != NULL); return storage->v.script_sequence_next(seq, error_r); } void sieve_script_sequence_free(struct sieve_script_sequence **_seq) { struct sieve_script_sequence *seq = *_seq; struct sieve_storage *storage = seq->storage; if (storage->v.script_sequence_destroy != NULL) storage->v.script_sequence_destroy(seq); sieve_storage_unref(&storage); *_seq = NULL; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-address-parts.h0000644000000000000000000000653614103200126024376 0ustar00rootroot00000000000000#ifndef SIEVE_ADDRESS_PARTS_H #define SIEVE_ADDRESS_PARTS_H #include "message-address.h" #include "sieve-common.h" #include "sieve-match.h" #include "sieve-extensions.h" #include "sieve-objects.h" /* * Address part definition */ struct sieve_address_part_def { struct sieve_object_def obj_def; const char *(*extract_from) (const struct sieve_address_part *addrp, const struct smtp_address *address); }; /* * Address part instance */ struct sieve_address_part { struct sieve_object object; const struct sieve_address_part_def *def; }; #define SIEVE_ADDRESS_PART_DEFAULT(definition) \ { SIEVE_OBJECT_DEFAULT(definition), &(definition) }; #define sieve_address_part_name(addrp) \ ( (addrp)->object.def->identifier ) #define sieve_address_part_is(addrp, definition) \ ( (addrp)->def == &(definition) ) /* * Core address parts */ enum sieve_address_part_code { SIEVE_ADDRESS_PART_ALL, SIEVE_ADDRESS_PART_LOCAL, SIEVE_ADDRESS_PART_DOMAIN, SIEVE_ADDRESS_PART_CUSTOM }; extern const struct sieve_address_part_def all_address_part; extern const struct sieve_address_part_def local_address_part; extern const struct sieve_address_part_def domain_address_part; /* * Address part tagged argument */ extern const struct sieve_argument_def address_part_tag; void sieve_address_parts_link_tags (struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg, int id_code); /* * Address part registry */ void sieve_address_part_register (struct sieve_validator *valdtr, const struct sieve_extension *ext, const struct sieve_address_part_def *addrp); /* * Address part operand */ extern const struct sieve_operand_def address_part_operand; extern const struct sieve_operand_class sieve_address_part_operand_class; #define SIEVE_EXT_DEFINE_ADDRESS_PART(OP) SIEVE_EXT_DEFINE_OBJECT(OP) #define SIEVE_EXT_DEFINE_ADDRESS_PARTS(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) static inline void sieve_opr_address_part_emit (struct sieve_binary_block *sblock, const struct sieve_address_part *addrp) { sieve_opr_object_emit(sblock, addrp->object.ext, addrp->object.def); } static inline bool sieve_opr_address_part_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address) { return sieve_opr_object_dump (denv, &sieve_address_part_operand_class, address, NULL); } static inline int sieve_opr_address_part_read (const struct sieve_runtime_env *renv, sieve_size_t *address, struct sieve_address_part *addrp) { if ( !sieve_opr_object_read (renv, &sieve_address_part_operand_class, address, &addrp->object) ) return SIEVE_EXEC_BIN_CORRUPT; addrp->def = (const struct sieve_address_part_def *) addrp->object.def; return SIEVE_EXEC_OK; } /* * Address-part string list */ struct sieve_stringlist *sieve_address_part_stringlist_create (const struct sieve_runtime_env *renv, const struct sieve_address_part *addrp, struct sieve_address_list *addresses); /* * Match utility */ enum sieve_addrmatch_opt_operand { SIEVE_AM_OPT_ADDRESS_PART = SIEVE_MATCH_OPT_LAST, SIEVE_AM_OPT_LAST }; int sieve_addrmatch_opr_optional_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, signed int *opt_code); int sieve_addrmatch_opr_optional_read (const struct sieve_runtime_env *renv, sieve_size_t *address, signed int *opt_code, int *exec_status, struct sieve_address_part *addrp, struct sieve_match_type *mtch, struct sieve_comparator *cmp); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-actions.h0000644000000000000000000001706714103200126023263 0ustar00rootroot00000000000000#ifndef SIEVE_ACTIONS_H #define SIEVE_ACTIONS_H #include "lib.h" #include "mail-types.h" #include "mail-error.h" #include "sieve-common.h" #include "sieve-objects.h" #include "sieve-extensions.h" #include "sieve-execute.h" /* * Action execution environment */ struct sieve_action_exec_env { const struct sieve_execute_env *exec_env; struct sieve_result_execution *rexec; const struct sieve_action *action; struct event *event; struct sieve_result *result; struct sieve_error_handler *ehandler; struct sieve_message_context *msgctx; }; struct event_passthrough * sieve_action_create_finish_event(const struct sieve_action_exec_env *aenv); /* * Action flags */ enum sieve_action_flags { SIEVE_ACTFLAG_TRIES_DELIVER = (1 << 0), SIEVE_ACTFLAG_SENDS_RESPONSE = (1 << 1), SIEVE_ACTFLAG_MAIL_STORAGE = (1 << 2) }; /* * Action definition */ struct sieve_action_def { const char *name; unsigned int flags; bool (*equals)(const struct sieve_script_env *senv, const struct sieve_action *act1, const struct sieve_action *act2); /* Result verification */ int (*check_duplicate)(const struct sieve_runtime_env *renv, const struct sieve_action *act, const struct sieve_action *act_other); int (*check_conflict)(const struct sieve_runtime_env *renv, const struct sieve_action *act, const struct sieve_action *act_other); /* Result printing */ void (*print)(const struct sieve_action *action, const struct sieve_result_print_env *penv, bool *keep); /* Result execution */ int (*start)(const struct sieve_action_exec_env *aenv, void **tr_context); int (*execute)(const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); int (*commit)(const struct sieve_action_exec_env *aenv, void *tr_context); void (*rollback)(const struct sieve_action_exec_env *aenv, void *tr_context, bool success); void (*finish)(const struct sieve_action_exec_env *aenv, void *tr_context, int status); }; /* * Action instance */ struct sieve_action { const struct sieve_action_def *def; const struct sieve_extension *ext; struct event *event; const char *name; const char *location; unsigned int exec_seq; void *context; struct mail *mail; bool keep:1; }; #define sieve_action_is(act, definition) ((act)->def == &(definition)) #define sieve_action_name(act) ((act)->name) bool sieve_action_is_executed(const struct sieve_action *act, struct sieve_result *result); /* * Action side effects */ /* Side effect object */ struct sieve_side_effect_def { struct sieve_object_def obj_def; /* Precedence (side effects with higher value are executed first) */ unsigned int precedence; /* The action it is supposed to link to */ const struct sieve_action_def *to_action; /* Context coding */ bool (*dump_context)(const struct sieve_side_effect *seffect, const struct sieve_dumptime_env *renv, sieve_size_t *address); int (*read_context)(const struct sieve_side_effect *seffect, const struct sieve_runtime_env *renv, sieve_size_t *address, void **se_context); /* Result verification */ int (*merge)(const struct sieve_runtime_env *renv, const struct sieve_action *action, const struct sieve_side_effect *old_seffect, const struct sieve_side_effect *new_seffect, void **old_context); /* Result printing */ void (*print)(const struct sieve_side_effect *seffect, const struct sieve_action *action, const struct sieve_result_print_env *penv, bool *keep); /* Result execution */ int (*pre_execute)(const struct sieve_side_effect *seffect, const struct sieve_action_exec_env *aenv, void *tr_context, void **se_tr_context); int (*post_execute)(const struct sieve_side_effect *seffect, const struct sieve_action_exec_env *aenv, void *tr_context, void *se_tr_context, bool *keep); void (*post_commit)(const struct sieve_side_effect *seffect, const struct sieve_action_exec_env *aenv, void *tr_context, void *se_tr_context, int commit_status); void (*rollback)(const struct sieve_side_effect *seffect, const struct sieve_action_exec_env *aenv, void *tr_context, void *se_tr_context, bool success); }; struct sieve_side_effect { struct sieve_object object; const struct sieve_side_effect_def *def; void *context; }; /* * Side effect operand */ #define SIEVE_EXT_DEFINE_SIDE_EFFECT(SEF) SIEVE_EXT_DEFINE_OBJECT(SEF) #define SIEVE_EXT_DEFINE_SIDE_EFFECTS(SEFS) SIEVE_EXT_DEFINE_OBJECTS(SEFS) #define SIEVE_OPT_SIDE_EFFECT (-1) extern const struct sieve_operand_class sieve_side_effect_operand_class; static inline void sieve_opr_side_effect_emit(struct sieve_binary_block *sblock, const struct sieve_extension *ext, const struct sieve_side_effect_def *seff) { sieve_opr_object_emit(sblock, ext, &seff->obj_def); } bool sieve_opr_side_effect_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address); int sieve_opr_side_effect_read(const struct sieve_runtime_env *renv, sieve_size_t *address, struct sieve_side_effect *seffect); /* * Optional operands */ int sieve_action_opr_optional_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address, signed int *opt_code); int sieve_action_opr_optional_read(const struct sieve_runtime_env *renv, sieve_size_t *address, signed int *opt_code, int *exec_status, struct sieve_side_effects_list **list); /* * Core actions */ extern const struct sieve_action_def act_redirect; extern const struct sieve_action_def act_store; extern const struct sieve_action_def act_discard; /* * Store action */ struct act_store_context { /* Folder name represented in utf-8 */ const char *mailbox; }; struct act_store_transaction { struct act_store_context *context; struct mailbox *box; struct mailbox_transaction_context *mail_trans; const char *mailbox_name; const char *mailbox_identifier; const char *error; enum mail_error error_code; enum mail_flags flags; ARRAY_TYPE(const_string) keywords; bool flags_altered:1; bool disabled:1; bool redundant:1; }; int sieve_act_store_add_to_result(const struct sieve_runtime_env *renv, const char *name, struct sieve_side_effects_list *seffects, const char *folder); void sieve_act_store_add_flags(const struct sieve_action_exec_env *aenv, void *tr_context, const char *const *keywords, enum mail_flags flags); void sieve_act_store_get_storage_error(const struct sieve_action_exec_env *aenv, struct act_store_transaction *trans); /* * Redirect action */ struct act_redirect_context { const struct smtp_address *to_address; }; int sieve_act_redirect_add_to_result(const struct sieve_runtime_env *renv, const char *name, struct sieve_side_effects_list *seffects, const struct smtp_address *to_address); /* * Action utility functions */ /* Checking for duplicates */ bool sieve_action_duplicate_check_available( const struct sieve_script_env *senv); bool sieve_action_duplicate_check(const struct sieve_script_env *senv, const void *id, size_t id_size); void sieve_action_duplicate_mark(const struct sieve_script_env *senv, const void *id, size_t id_size, time_t time); void sieve_action_duplicate_flush(const struct sieve_script_env *senv); /* Rejecting mail */ int sieve_action_reject_mail(const struct sieve_action_exec_env *aenv, const struct smtp_address *recipient, const char *reason); /* * Mailbox */ // FIXME: move this to a more appropriate location bool sieve_mailbox_check_name(const char *mailbox, const char **error_r); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-ast.h0000644000000000000000000002603314103200126022403 0ustar00rootroot00000000000000#ifndef SIEVE_AST_H #define SIEVE_AST_H #include "lib.h" #include "str.h" #include "sieve-common.h" #include "sieve-error.h" /* Abstract Syntax Tree (AST) structure: sieve_ast (root) [*command] | +-- command: | .... +-- command: | [identifier *argument *test *command] | +-- argument: | \--> as from root | | .... | | +-- argument: V (continued below) | | [number | tag | *string] | . . *test +-- test: | .... +-- test: | [identifier *argument *test] | +-- argument: \--> as from the top . | .... of this tree +-- argument: | [number | tag | *string] . Tests and commands are defined using the same structure: sieve_ast_node. However, arguments and string-lists are described using sieve_ast_argument. */ /* IMPORTANT NOTICE: Do not decorate the AST with objects other than those * allocated on the ast's pool or static const objects. Otherwise it is possible * that pointers in the tree become dangling which is highly undesirable. */ /* * Forward declarations */ struct sieve_ast_list; struct sieve_ast_arg_list; /* * Types */ enum sieve_ast_argument_type { SAAT_NONE, SAAT_NUMBER, SAAT_STRING, SAAT_STRING_LIST, SAAT_TAG, }; enum sieve_ast_type { SAT_NONE, SAT_ROOT, SAT_COMMAND, SAT_TEST, }; /* * AST Nodes */ /* Argument node */ struct sieve_ast_argument { enum sieve_ast_argument_type type; /* Back reference to the AST object */ struct sieve_ast *ast; /* List related */ struct sieve_ast_arg_list *list; struct sieve_ast_argument *next; struct sieve_ast_argument *prev; /* Parser-assigned data */ union { string_t *str; struct sieve_ast_arg_list *strlist; const char *tag; unsigned int number; } _value; unsigned int source_line; /* Assigned during validation */ /* Argument associated with this ast element */ struct sieve_argument *argument; /* Parameters to this (tag) argument */ struct sieve_ast_argument *parameters; }; struct sieve_ast_node { enum sieve_ast_type type; /* Back reference to the AST object */ struct sieve_ast *ast; /* Back reference to this node's parent */ struct sieve_ast_node *parent; /* Linked list references */ struct sieve_ast_list *list; struct sieve_ast_node *next; struct sieve_ast_node *prev; /* Commands (NULL if not allocated) */ bool block; struct sieve_ast_list *commands; /* Tests (NULL if not allocated)*/ bool test_list; struct sieve_ast_list *tests; /* Arguments (NULL if not allocated) */ struct sieve_ast_arg_list *arguments; /* Identifier of command or test */ const char *identifier; /* The location in the file where this command was started */ unsigned int source_line; /* Assigned during validation */ /* Context */ struct sieve_command *command; }; /* * AST node lists */ struct sieve_ast_list { struct sieve_ast_node *head; struct sieve_ast_node *tail; unsigned int len; }; struct sieve_ast_arg_list { struct sieve_ast_argument *head; struct sieve_ast_argument *tail; unsigned int len; }; /* * AST object */ struct sieve_ast; struct sieve_ast *sieve_ast_create(struct sieve_script *script); void sieve_ast_ref(struct sieve_ast *ast); void sieve_ast_unref(struct sieve_ast **ast); struct sieve_ast_node *sieve_ast_root(struct sieve_ast *ast); pool_t sieve_ast_pool(struct sieve_ast *ast); struct sieve_script *sieve_ast_script(struct sieve_ast *ast); /* Extension support */ struct sieve_ast_extension { const struct sieve_extension_def *ext; void (*free)(const struct sieve_extension *ext, struct sieve_ast *ast, void *context); }; void sieve_ast_extension_link (struct sieve_ast *ast, const struct sieve_extension *ext, bool required); const struct sieve_extension * const *sieve_ast_extensions_get (struct sieve_ast *ast, unsigned int *count_r); void sieve_ast_extension_register (struct sieve_ast *ast, const struct sieve_extension *ext, const struct sieve_ast_extension *ast_ext, void *context); void sieve_ast_extension_set_context (struct sieve_ast *ast, const struct sieve_extension *ext, void *context); void *sieve_ast_extension_get_context (struct sieve_ast *ast, const struct sieve_extension *ext); bool sieve_ast_extension_is_required (struct sieve_ast *ast, const struct sieve_extension *ext); /* * AST node manipulation */ /* Command nodes */ struct sieve_ast_node *sieve_ast_test_create (struct sieve_ast_node *parent, const char *identifier, unsigned int source_line); struct sieve_ast_node *sieve_ast_command_create (struct sieve_ast_node *parent, const char *identifier, unsigned int source_line); struct sieve_ast_node *sieve_ast_node_detach (struct sieve_ast_node *first); const char *sieve_ast_type_name(enum sieve_ast_type ast_type); /* Argument nodes */ struct sieve_ast_argument *sieve_ast_argument_create (struct sieve_ast *ast, unsigned int source_line); struct sieve_ast_arg_list *sieve_ast_arg_list_create(pool_t pool); bool sieve_ast_arg_list_add (struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument); bool sieve_ast_arg_list_insert (struct sieve_ast_arg_list *list, struct sieve_ast_argument *before, struct sieve_ast_argument *argument); void sieve_ast_arg_list_substitute (struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument, struct sieve_ast_argument *replacement); struct sieve_ast_argument *sieve_ast_argument_string_create_raw (struct sieve_ast *ast, string_t *str, unsigned int source_line); struct sieve_ast_argument *sieve_ast_argument_string_create (struct sieve_ast_node *node, const string_t *str, unsigned int source_line); struct sieve_ast_argument *sieve_ast_argument_cstring_create (struct sieve_ast_node *node, const char *str, unsigned int source_line); struct sieve_ast_argument *sieve_ast_argument_tag_create (struct sieve_ast_node *node, const char *tag, unsigned int source_line); struct sieve_ast_argument *sieve_ast_argument_number_create (struct sieve_ast_node *node, unsigned int number, unsigned int source_line); void sieve_ast_argument_string_set (struct sieve_ast_argument *argument, string_t *newstr); void sieve_ast_argument_string_setc (struct sieve_ast_argument *argument, const char *newstr); void sieve_ast_argument_number_set (struct sieve_ast_argument *argument, unsigned int newnum); void sieve_ast_argument_number_substitute (struct sieve_ast_argument *argument, unsigned int number); struct sieve_ast_argument *sieve_ast_argument_tag_insert (struct sieve_ast_argument *before, const char *tag, unsigned int source_line); struct sieve_ast_argument *sieve_ast_argument_stringlist_create (struct sieve_ast_node *node, unsigned int source_line); struct sieve_ast_argument *sieve_ast_argument_stringlist_substitute (struct sieve_ast_node *node, struct sieve_ast_argument *arg); struct sieve_ast_argument *sieve_ast_arguments_detach (struct sieve_ast_argument *first, unsigned int count); bool sieve_ast_argument_attach (struct sieve_ast_node *node, struct sieve_ast_argument *argument); const char *sieve_ast_argument_type_name(enum sieve_ast_argument_type arg_type); #define sieve_ast_argument_name(argument) \ sieve_ast_argument_type_name((argument)->type) bool sieve_ast_stringlist_add (struct sieve_ast_argument *list, const string_t *str, unsigned int source_line); bool sieve_ast_stringlist_add_strc (struct sieve_ast_argument *list, const char *str, unsigned int source_line); /* * Utility */ int sieve_ast_stringlist_map (struct sieve_ast_argument **listitem, void *context, int (*map_function)(void *context, struct sieve_ast_argument *arg)); struct sieve_ast_argument *sieve_ast_stringlist_join (struct sieve_ast_argument *list, struct sieve_ast_argument *items); /* * AST access macros */ /* Generic list access macros */ #define __AST_LIST_FIRST(list) \ ((list) == NULL ? NULL : (list)->head) #define __AST_LIST_LAST(list) \ ((list) == NULL ? NULL : (list)->tail) #define __AST_LIST_COUNT(list) \ ((list) == NULL || (list)->head == NULL ? 0 : (list)->len) #define __AST_LIST_NEXT(item) ((item)->next) #define __AST_LIST_PREV(item) ((item)->prev) #define __AST_NODE_LIST_FIRST(node, list) __AST_LIST_FIRST((node)->list) #define __AST_NODE_LIST_LAST(node, list) __AST_LIST_LAST((node)->list) #define __AST_NODE_LIST_COUNT(node, list) __AST_LIST_COUNT((node)->list) /* AST macros */ /* AST node macros */ #define sieve_ast_node_pool(node) (sieve_ast_pool((node)->ast)) #define sieve_ast_node_parent(node) ((node)->parent) #define sieve_ast_node_prev(node) __AST_LIST_PREV(node) #define sieve_ast_node_next(node) __AST_LIST_NEXT(node) #define sieve_ast_node_type(node) ((node) == NULL ? SAT_NONE : (node)->type) #define sieve_ast_node_line(node) ((node) == NULL ? 0 : (node)->source_line) /* AST command node macros */ #define sieve_ast_command_first(node) __AST_NODE_LIST_FIRST(node, commands) #define sieve_ast_command_count(node) __AST_NODE_LIST_COUNT(node, commands) #define sieve_ast_command_prev(command) __AST_LIST_PREV(command) #define sieve_ast_command_next(command) __AST_LIST_NEXT(command) /* Compare the identifier of the previous command */ #define sieve_ast_prev_cmd_is(cmd, id) \ ( (cmd)->prev == NULL ? FALSE : \ strncasecmp((cmd)->prev->identifier, id, sizeof(id)-1) == 0 ) /* AST test macros */ #define sieve_ast_test_count(node) __AST_NODE_LIST_COUNT(node, tests) #define sieve_ast_test_first(node) __AST_NODE_LIST_FIRST(node, tests) #define sieve_ast_test_next(test) __AST_LIST_NEXT(test) /* AST argument macros */ #define sieve_ast_argument_pool(node) (sieve_ast_pool((node)->ast)) #define sieve_ast_argument_first(node) __AST_NODE_LIST_FIRST(node, arguments) #define sieve_ast_argument_last(node) __AST_NODE_LIST_LAST(node, arguments) #define sieve_ast_argument_count(node) __AST_NODE_LIST_COUNT(node, arguments) #define sieve_ast_argument_prev(argument) __AST_LIST_PREV(argument) #define sieve_ast_argument_next(argument) __AST_LIST_NEXT(argument) #define sieve_ast_argument_type(argument) (argument)->type #define sieve_ast_argument_line(argument) (argument)->source_line #define sieve_ast_argument_str(argument) ((argument)->_value.str) #define sieve_ast_argument_strc(argument) (str_c((argument)->_value.str)) #define sieve_ast_argument_tag(argument) ((argument)->_value.tag) #define sieve_ast_argument_number(argument) ((argument)->_value.number) /* AST string list macros */ // @UNSAFE: should check whether we are actually accessing a string list #define sieve_ast_strlist_first(list) \ __AST_NODE_LIST_FIRST(list, _value.strlist) #define sieve_ast_strlist_last(list) \ __AST_NODE_LIST_LAST(list, _value.strlist) #define sieve_ast_strlist_count(list) \ __AST_NODE_LIST_COUNT(list, _value.strlist) #define sieve_ast_strlist_next(str) __AST_LIST_NEXT(str) #define sieve_ast_strlist_prev(str) __AST_LIST_PREV(str) #define sieve_ast_strlist_str(str) sieve_ast_argument_str(str) #define sieve_ast_strlist_strc(str) sieve_ast_argument_strc(str) /* * Debug */ void sieve_ast_unparse(struct sieve_ast *ast); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/tst-header.c0000644000000000000000000001204614103200126022535 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "str-sanitize.h" #include "sieve-common.h" #include "sieve-commands.h" #include "sieve-code.h" #include "sieve-message.h" #include "sieve-comparators.h" #include "sieve-match-types.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-interpreter.h" #include "sieve-dump.h" #include "sieve-match.h" /* * Header test * * Syntax: * header [COMPARATOR] [MATCH-TYPE] * */ static bool tst_header_registered (struct sieve_validator *valdtr, const struct sieve_extension *ext, struct sieve_command_registration *cmd_reg); static bool tst_header_validate (struct sieve_validator *valdtr, struct sieve_command *tst); static bool tst_header_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *tst); const struct sieve_command_def tst_header = { .identifier = "header", .type = SCT_TEST, .positional_args = 2, .subtests = 0, .block_allowed = FALSE, .block_required = FALSE, .registered = tst_header_registered, .validate = tst_header_validate, .generate = tst_header_generate }; /* * Header operation */ static bool tst_header_operation_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address); static int tst_header_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address); const struct sieve_operation_def tst_header_operation = { .mnemonic = "HEADER", .code = SIEVE_OPERATION_HEADER, .dump = tst_header_operation_dump, .execute = tst_header_operation_execute }; /* * Test registration */ static bool tst_header_registered (struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, struct sieve_command_registration *cmd_reg) { /* The order of these is not significant */ sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); return TRUE; } /* * Validation */ static bool tst_header_validate (struct sieve_validator *valdtr, struct sieve_command *tst) { struct sieve_ast_argument *arg = tst->first_positional; struct sieve_comparator cmp_default = SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); struct sieve_match_type mcht_default = SIEVE_MATCH_TYPE_DEFAULT(is_match_type); if ( !sieve_validate_positional_argument (valdtr, tst, arg, "header names", 1, SAAT_STRING_LIST) ) { return FALSE; } if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) return FALSE; if ( !sieve_command_verify_headers_argument(valdtr, arg) ) return FALSE; arg = sieve_ast_argument_next(arg); if ( !sieve_validate_positional_argument (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) { return FALSE; } if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) return FALSE; /* Validate the key argument to a specified match type */ return sieve_match_type_validate (valdtr, tst, arg, &mcht_default, &cmp_default); } /* * Code generation */ static bool tst_header_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *tst) { sieve_operation_emit(cgenv->sblock, NULL, &tst_header_operation); /* Generate arguments */ return sieve_generate_arguments(cgenv, tst, NULL); } /* * Code dump */ static bool tst_header_operation_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address) { sieve_code_dumpf(denv, "HEADER"); sieve_code_descend(denv); /* Optional operands */ if ( sieve_message_opr_optional_dump(denv, address, NULL) != 0 ) return FALSE; return sieve_opr_stringlist_dump(denv, address, "header names") && sieve_opr_stringlist_dump(denv, address, "key list"); } /* * Code execution */ static int tst_header_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address) { struct sieve_comparator cmp = SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); struct sieve_match_type mcht = SIEVE_MATCH_TYPE_DEFAULT(is_match_type); struct sieve_stringlist *hdr_list, *key_list, *value_list; ARRAY_TYPE(sieve_message_override) svmos; int match, ret; /* * Read operands */ /* Optional operands */ i_zero(&svmos); if ( sieve_message_opr_optional_read (renv, address, NULL, &ret, NULL, &mcht, &cmp, &svmos) < 0 ) return ret; /* Read header-list */ if ( (ret=sieve_opr_stringlist_read(renv, address, "header-list", &hdr_list)) <= 0 ) return ret; /* Read key-list */ if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) <= 0 ) return ret; /* * Perform test */ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "header test"); /* Get header */ sieve_runtime_trace_descend(renv); if ( (ret=sieve_message_get_header_fields (renv, hdr_list, &svmos, TRUE, &value_list)) <= 0 ) return ret; sieve_runtime_trace_ascend(renv); /* Perform match */ if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 ) return ret; /* Set test result for subsequent conditional jump */ sieve_interpreter_set_test_result(renv->interp, match > 0); return SIEVE_EXEC_OK; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-code.c0000644000000000000000000007244614103200126022532 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "str-sanitize.h" #include "sieve-common.h" #include "sieve-limits.h" #include "sieve-extensions.h" #include "sieve-stringlist.h" #include "sieve-actions.h" #include "sieve-binary.h" #include "sieve-generator.h" #include "sieve-interpreter.h" #include "sieve-dump.h" #include "sieve-code.h" #include /* * Code stringlist */ /* Forward declarations */ static int sieve_code_stringlist_next_item (struct sieve_stringlist *_strlist, string_t **str_r); static void sieve_code_stringlist_reset (struct sieve_stringlist *_strlist); static int sieve_code_stringlist_get_length (struct sieve_stringlist *_strlist); /* Coded stringlist object */ struct sieve_code_stringlist { struct sieve_stringlist strlist; sieve_size_t start_address; sieve_size_t end_address; sieve_size_t current_offset; int length; int index; }; static struct sieve_stringlist *sieve_code_stringlist_create (const struct sieve_runtime_env *renv, sieve_size_t start_address, unsigned int length, sieve_size_t end) { struct sieve_code_stringlist *strlist; if ( end > sieve_binary_block_get_size(renv->sblock) ) return NULL; strlist = t_new(struct sieve_code_stringlist, 1); strlist->strlist.runenv = renv; strlist->strlist.exec_status = SIEVE_EXEC_OK; strlist->strlist.next_item = sieve_code_stringlist_next_item; strlist->strlist.reset = sieve_code_stringlist_reset; strlist->strlist.get_length = sieve_code_stringlist_get_length; strlist->start_address = start_address; strlist->current_offset = start_address; strlist->end_address = end; strlist->length = length; strlist->index = 0; return &strlist->strlist; } /* Stringlist implementation */ static int sieve_code_stringlist_next_item (struct sieve_stringlist *_strlist, string_t **str_r) { struct sieve_code_stringlist *strlist = (struct sieve_code_stringlist *) _strlist; sieve_size_t address; *str_r = NULL; int ret; /* Check for end of list */ if ( strlist->index >= strlist->length ) return 0; /* Read next item */ address = strlist->current_offset; if ( (ret=sieve_opr_string_read(_strlist->runenv, &address, NULL, str_r)) == SIEVE_EXEC_OK ) { strlist->index++; strlist->current_offset = address; return 1; } _strlist->exec_status = ret; return -1; } static void sieve_code_stringlist_reset (struct sieve_stringlist *_strlist) { struct sieve_code_stringlist *strlist = (struct sieve_code_stringlist *) _strlist; strlist->current_offset = strlist->start_address; strlist->index = 0; } static int sieve_code_stringlist_get_length (struct sieve_stringlist *_strlist) { struct sieve_code_stringlist *strlist = (struct sieve_code_stringlist *) _strlist; return strlist->length; } static bool sieve_code_stringlist_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, unsigned int length, sieve_size_t end, const char *field_name) { unsigned int i; if ( end > sieve_binary_block_get_size(denv->sblock) ) return FALSE; if ( field_name != NULL ) sieve_code_dumpf(denv, "%s: STRLIST [%u] (end: %08llx)", field_name, length, (unsigned long long) end); else sieve_code_dumpf(denv, "STRLIST [%u] (end: %08llx)", length, (unsigned long long) end); sieve_code_descend(denv); for ( i = 0; i < length; i++ ) { bool success = TRUE; T_BEGIN { success = sieve_opr_string_dump(denv, address, NULL); } T_END; if ( !success || *address > end ) return FALSE; } if ( *address != end ) return FALSE; sieve_code_ascend(denv); return TRUE; } /* * Core operands */ extern const struct sieve_operand_def comparator_operand; extern const struct sieve_operand_def match_type_operand; extern const struct sieve_operand_def address_part_operand; const struct sieve_operand_def *sieve_operands[] = { &omitted_operand, /* SIEVE_OPERAND_OPTIONAL */ &number_operand, &string_operand, &stringlist_operand, &comparator_operand, &match_type_operand, &address_part_operand, &catenated_string_operand }; const unsigned int sieve_operand_count = N_ELEMENTS(sieve_operands); /* * Operand functions */ sieve_size_t sieve_operand_emit (struct sieve_binary_block *sblock, const struct sieve_extension *ext, const struct sieve_operand_def *opr_def) { sieve_size_t address; if ( ext != NULL ) { address = sieve_binary_emit_extension (sblock, ext, sieve_operand_count); sieve_binary_emit_extension_object (sblock, &opr_def->ext_def->operands, opr_def->code); return address; } return sieve_binary_emit_byte(sblock, opr_def->code); } bool sieve_operand_read (struct sieve_binary_block *sblock, sieve_size_t *address, const char *field_name, struct sieve_operand *operand) { unsigned int code = sieve_operand_count; operand->address = *address; operand->field_name = field_name; operand->ext = NULL; operand->def = NULL; if ( !sieve_binary_read_extension(sblock, address, &code, &operand->ext) ) return FALSE; if ( operand->ext == NULL ) { if ( code < sieve_operand_count ) operand->def = sieve_operands[code]; return ( operand->def != NULL ); } if ( operand->ext->def == NULL ) return FALSE; operand->def = (const struct sieve_operand_def *) sieve_binary_read_extension_object(sblock, address, &operand->ext->def->operands); return ( operand->def != NULL ); } /* * Optional operand */ int sieve_opr_optional_next (struct sieve_binary_block *sblock, sieve_size_t *address, signed int *opt_code) { /* Start of optional operand block */ if ( *opt_code == 0 ) { sieve_size_t tmp_addr = *address; unsigned int op; if ( !sieve_binary_read_byte(sblock, &tmp_addr, &op) || op != SIEVE_OPERAND_OPTIONAL ) return 0; *address = tmp_addr; } /* Read optional operand code */ if ( !sieve_binary_read_code(sblock, address, opt_code) ) return -1; /* Return 0 at end of list */ return ( *opt_code != 0 ? 1 : 0 ); } /* * Operand definitions */ /* Omitted */ const struct sieve_operand_class omitted_class = { "OMITTED" }; const struct sieve_operand_def omitted_operand = { .name = "@OMITTED", .code = SIEVE_OPERAND_OPTIONAL, .class = &omitted_class }; /* Number */ static bool opr_number_dump (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, sieve_size_t *address); static int opr_number_read (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, sieve_size_t *address, sieve_number_t *number_r); const struct sieve_opr_number_interface number_interface = { opr_number_dump, opr_number_read }; const struct sieve_operand_class number_class = { "number" }; const struct sieve_operand_def number_operand = { .name = "@number", .code = SIEVE_OPERAND_NUMBER, .class = &number_class, .interface = &number_interface }; /* String */ static bool opr_string_dump (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, sieve_size_t *address); static int opr_string_read (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, sieve_size_t *address, string_t **str_r); const struct sieve_opr_string_interface string_interface ={ opr_string_dump, opr_string_read }; const struct sieve_operand_class string_class = { "string" }; const struct sieve_operand_def string_operand = { .name = "@string", .code = SIEVE_OPERAND_STRING, .class = &string_class, .interface = &string_interface }; /* String List */ static bool opr_stringlist_dump (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, sieve_size_t *address); static int opr_stringlist_read (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, sieve_size_t *address, struct sieve_stringlist **strlist_r); const struct sieve_opr_stringlist_interface stringlist_interface = { opr_stringlist_dump, opr_stringlist_read }; const struct sieve_operand_class stringlist_class = { "string-list" }; const struct sieve_operand_def stringlist_operand = { .name = "@string-list", .code = SIEVE_OPERAND_STRING_LIST, .class = &stringlist_class, .interface = &stringlist_interface }; /* Catenated String */ static bool opr_catenated_string_dump (const struct sieve_dumptime_env *denv, const struct sieve_operand *operand, sieve_size_t *address); static int opr_catenated_string_read (const struct sieve_runtime_env *renv, const struct sieve_operand *operand, sieve_size_t *address, string_t **str); const struct sieve_opr_string_interface catenated_string_interface = { opr_catenated_string_dump, opr_catenated_string_read }; const struct sieve_operand_def catenated_string_operand = { .name = "@catenated-string", .code = SIEVE_OPERAND_CATENATED_STRING, .class = &string_class, .interface = &catenated_string_interface }; /* * Operand implementations */ /* Omitted */ void sieve_opr_omitted_emit(struct sieve_binary_block *sblock) { (void) sieve_operand_emit(sblock, NULL, &omitted_operand); } /* Number */ void sieve_opr_number_emit (struct sieve_binary_block *sblock, sieve_number_t number) { (void) sieve_operand_emit(sblock, NULL, &number_operand); (void) sieve_binary_emit_integer(sblock, number); } bool sieve_opr_number_dump_data (const struct sieve_dumptime_env *denv, struct sieve_operand *oprnd, sieve_size_t *address, const char *field_name) { const struct sieve_opr_number_interface *intf; oprnd->field_name = field_name; if ( !sieve_operand_is_number(oprnd) ) return FALSE; intf = (const struct sieve_opr_number_interface *) oprnd->def->interface; if ( intf->dump == NULL ) return FALSE; return intf->dump(denv, oprnd, address); } bool sieve_opr_number_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, const char *field_name) { struct sieve_operand operand; sieve_code_mark(denv); if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) return FALSE; return sieve_opr_number_dump_data(denv, &operand, address, field_name); } int sieve_opr_number_read_data (const struct sieve_runtime_env *renv, struct sieve_operand *oprnd, sieve_size_t *address, const char *field_name, sieve_number_t *number_r) { const struct sieve_opr_number_interface *intf; oprnd->field_name = field_name; if ( !sieve_operand_is_number(oprnd) ) { sieve_runtime_trace_operand_error(renv, oprnd, "expected number operand but found %s", sieve_operand_name(oprnd)); return SIEVE_EXEC_BIN_CORRUPT; } intf = (const struct sieve_opr_number_interface *) oprnd->def->interface; if ( intf->read == NULL ) { sieve_runtime_trace_operand_error(renv, oprnd, "number operand not implemented"); return SIEVE_EXEC_FAILURE; } return intf->read(renv, oprnd, address, number_r); } int sieve_opr_number_read (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, sieve_number_t *number_r) { struct sieve_operand operand; int ret; if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) <= 0) return ret; return sieve_opr_number_read_data (renv, &operand, address, field_name, number_r); } static bool opr_number_dump (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, sieve_size_t *address) { sieve_number_t number = 0; if (sieve_binary_read_integer(denv->sblock, address, &number) ) { if ( oprnd->field_name != NULL ) sieve_code_dumpf(denv, "%s: NUM %llu", oprnd->field_name, (unsigned long long) number); else sieve_code_dumpf(denv, "NUM %llu", (unsigned long long) number); return TRUE; } return FALSE; } static int opr_number_read (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, sieve_size_t *address, sieve_number_t *number_r) { if ( !sieve_binary_read_integer(renv->sblock, address, number_r) ) { sieve_runtime_trace_operand_error(renv, oprnd, "invalid number operand"); return SIEVE_EXEC_BIN_CORRUPT; } return SIEVE_EXEC_OK; } /* String */ void sieve_opr_string_emit(struct sieve_binary_block *sblock, string_t *str) { (void) sieve_operand_emit(sblock, NULL, &string_operand); (void) sieve_binary_emit_string(sblock, str); } bool sieve_opr_string_dump_data (const struct sieve_dumptime_env *denv, struct sieve_operand *oprnd, sieve_size_t *address, const char *field_name) { const struct sieve_opr_string_interface *intf; oprnd->field_name = field_name; if ( !sieve_operand_is_string(oprnd) ) { sieve_code_dumpf(denv, "ERROR: INVALID STRING OPERAND %s", sieve_operand_name(oprnd)); return FALSE; } intf = (const struct sieve_opr_string_interface *) oprnd->def->interface; if ( intf->dump == NULL ) { sieve_code_dumpf(denv, "ERROR: DUMP STRING OPERAND"); return FALSE; } return intf->dump(denv, oprnd, address); } bool sieve_opr_string_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, const char *field_name) { struct sieve_operand operand; sieve_code_mark(denv); if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { sieve_code_dumpf(denv, "ERROR: INVALID OPERAND"); return FALSE; } return sieve_opr_string_dump_data(denv, &operand, address, field_name); } bool sieve_opr_string_dump_ex (const struct sieve_dumptime_env *denv, sieve_size_t *address, const char *field_name, const char *omitted_value) { struct sieve_operand operand; sieve_code_mark(denv); if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { sieve_code_dumpf(denv, "ERROR: INVALID OPERAND"); return FALSE; } if ( omitted_value != NULL && sieve_operand_is_omitted(&operand) ) { if ( *omitted_value != '\0' ) sieve_code_dumpf(denv, "%s: %s", field_name, omitted_value); return TRUE; } return sieve_opr_string_dump_data(denv, &operand, address, field_name); } int sieve_opr_string_read_data (const struct sieve_runtime_env *renv, struct sieve_operand *oprnd, sieve_size_t *address, const char *field_name, string_t **str_r) { const struct sieve_opr_string_interface *intf; oprnd->field_name = field_name; if ( !sieve_operand_is_string(oprnd) ) { sieve_runtime_trace_operand_error(renv, oprnd, "expected string operand but found %s", sieve_operand_name(oprnd)); return SIEVE_EXEC_BIN_CORRUPT; } intf = (const struct sieve_opr_string_interface *) oprnd->def->interface; if ( intf->read == NULL ) { sieve_runtime_trace_operand_error(renv, oprnd, "string operand not implemented"); return SIEVE_EXEC_FAILURE; } return intf->read(renv, oprnd, address, str_r); } int sieve_opr_string_read (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, string_t **str_r) { struct sieve_operand operand; int ret; if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) <= 0 ) return ret; return sieve_opr_string_read_data(renv, &operand, address, field_name, str_r); } int sieve_opr_string_read_ex (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, bool optional, string_t **str_r, bool *literal_r) { struct sieve_operand operand; int ret; if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) <= 0 ) return ret; if ( optional && sieve_operand_is_omitted(&operand) ) { *str_r = NULL; return 1; } if ( literal_r != NULL ) *literal_r = sieve_operand_is_string_literal(&operand); return sieve_opr_string_read_data(renv, &operand, address, field_name, str_r); } static void _dump_string (const struct sieve_dumptime_env *denv, string_t *str, const char *field_name) { if ( str_len(str) > 80 ) { if ( field_name != NULL ) sieve_code_dumpf(denv, "%s: STR[%ld] \"%s", field_name, (long) str_len(str), str_sanitize(str_c(str), 80)); else sieve_code_dumpf(denv, "STR[%ld] \"%s", (long) str_len(str), str_sanitize(str_c(str), 80)); } else { if ( field_name != NULL ) sieve_code_dumpf(denv, "%s: STR[%ld] \"%s\"", field_name, (long) str_len(str), str_sanitize(str_c(str), 80)); else sieve_code_dumpf(denv, "STR[%ld] \"%s\"", (long) str_len(str), str_sanitize(str_c(str), 80)); } } bool opr_string_dump (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, sieve_size_t *address) { string_t *str; if ( sieve_binary_read_string(denv->sblock, address, &str) ) { _dump_string(denv, str, oprnd->field_name); return TRUE; } return FALSE; } static int opr_string_read (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, sieve_size_t *address, string_t **str_r) { if ( !sieve_binary_read_string(renv->sblock, address, str_r) ) { sieve_runtime_trace_operand_error(renv, oprnd, "invalid string operand"); return SIEVE_EXEC_BIN_CORRUPT; } return SIEVE_EXEC_OK; } /* String list */ void sieve_opr_stringlist_emit_start (struct sieve_binary_block *sblock, unsigned int listlen, void **context) { sieve_size_t *end_offset = t_new(sieve_size_t, 1); /* Emit byte identifying the type of operand */ (void) sieve_operand_emit(sblock, NULL, &stringlist_operand); /* Give the interpreter an easy way to skip over this string list */ *end_offset = sieve_binary_emit_offset(sblock, 0); *context = (void *) end_offset; /* Emit the length of the list */ (void) sieve_binary_emit_unsigned(sblock, listlen); } void sieve_opr_stringlist_emit_item (struct sieve_binary_block *sblock, void *context ATTR_UNUSED, string_t *item) { (void) sieve_opr_string_emit(sblock, item); } void sieve_opr_stringlist_emit_end (struct sieve_binary_block *sblock, void *context) { sieve_size_t *end_offset = (sieve_size_t *) context; (void) sieve_binary_resolve_offset(sblock, *end_offset); } bool sieve_opr_stringlist_dump_data (const struct sieve_dumptime_env *denv, struct sieve_operand *oprnd, sieve_size_t *address, const char *field_name) { if ( oprnd == NULL || oprnd->def == NULL ) return FALSE; oprnd->field_name = field_name; if ( oprnd->def->class == &stringlist_class ) { const struct sieve_opr_stringlist_interface *intf = (const struct sieve_opr_stringlist_interface *) oprnd->def->interface; if ( intf->dump == NULL ) return FALSE; return intf->dump(denv, oprnd, address); } else if ( oprnd->def->class == &string_class ) { const struct sieve_opr_string_interface *intf = (const struct sieve_opr_string_interface *) oprnd->def->interface; if ( intf->dump == NULL ) return FALSE; return intf->dump(denv, oprnd, address); } return FALSE; } bool sieve_opr_stringlist_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, const char *field_name) { struct sieve_operand operand; sieve_code_mark(denv); if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { return FALSE; } return sieve_opr_stringlist_dump_data(denv, &operand, address, field_name); } bool sieve_opr_stringlist_dump_ex (const struct sieve_dumptime_env *denv, sieve_size_t *address, const char *field_name, const char *omitted_value) { struct sieve_operand operand; sieve_code_mark(denv); if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { return FALSE; } if ( omitted_value != NULL && sieve_operand_is_omitted(&operand) ) { if ( *omitted_value != '\0' ) sieve_code_dumpf(denv, "%s: %s", field_name, omitted_value); return TRUE; } return sieve_opr_stringlist_dump_data(denv, &operand, address, field_name); } int sieve_opr_stringlist_read_data (const struct sieve_runtime_env *renv, struct sieve_operand *oprnd, sieve_size_t *address, const char *field_name, struct sieve_stringlist **strlist_r) { if ( oprnd == NULL || oprnd->def == NULL ) return SIEVE_EXEC_FAILURE; oprnd->field_name = field_name; if ( oprnd->def->class == &stringlist_class ) { const struct sieve_opr_stringlist_interface *intf = (const struct sieve_opr_stringlist_interface *) oprnd->def->interface; int ret; if ( intf->read == NULL ) { sieve_runtime_trace_operand_error(renv, oprnd, "stringlist operand not implemented"); return SIEVE_EXEC_FAILURE; } if ( (ret=intf->read(renv, oprnd, address, strlist_r)) <= 0 ) return ret; return SIEVE_EXEC_OK; } else if ( oprnd->def->class == &string_class ) { /* Special case, accept single string as string list as well. */ const struct sieve_opr_string_interface *intf = (const struct sieve_opr_string_interface *) oprnd->def->interface; int ret; if ( intf->read == NULL ) { sieve_runtime_trace_operand_error(renv, oprnd, "stringlist string operand not implemented"); return SIEVE_EXEC_FAILURE; } if ( strlist_r == NULL ) { if ( (ret=intf->read(renv, oprnd, address, NULL)) <= 0 ) return ret; } else { string_t *stritem; if ( (ret=intf->read(renv, oprnd, address, &stritem)) <= 0 ) return ret; *strlist_r = sieve_single_stringlist_create (renv, stritem, FALSE); } return SIEVE_EXEC_OK; } sieve_runtime_trace_operand_error(renv, oprnd, "expected stringlist or string operand but found %s", sieve_operand_name(oprnd)); return SIEVE_EXEC_BIN_CORRUPT; } int sieve_opr_stringlist_read (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, struct sieve_stringlist **strlist_r) { struct sieve_operand operand; int ret; if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) <= 0 ) return ret; return sieve_opr_stringlist_read_data (renv, &operand, address, field_name, strlist_r); } int sieve_opr_stringlist_read_ex (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, bool optional, struct sieve_stringlist **strlist_r) { struct sieve_operand operand; int ret; if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) <= 0 ) return ret; if ( optional && sieve_operand_is_omitted(&operand) ) { *strlist_r = NULL; return 1; } return sieve_opr_stringlist_read_data (renv, &operand, address, field_name, strlist_r); } static bool opr_stringlist_dump (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, sieve_size_t *address) { sieve_size_t pc = *address; sieve_size_t end; unsigned int length = 0; sieve_offset_t end_offset; if ( !sieve_binary_read_offset(denv->sblock, address, &end_offset) ) return FALSE; end = pc + end_offset; if ( !sieve_binary_read_unsigned(denv->sblock, address, &length) ) return FALSE; return sieve_code_stringlist_dump (denv, address, length, end, oprnd->field_name); } static int opr_stringlist_read (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, sieve_size_t *address, struct sieve_stringlist **strlist_r) { sieve_size_t pc = *address; sieve_size_t end; unsigned int length = 0; sieve_offset_t end_offset; if ( !sieve_binary_read_offset(renv->sblock, address, &end_offset) ) { sieve_runtime_trace_operand_error(renv, oprnd, "stringlist corrupt: invalid end offset"); return SIEVE_EXEC_BIN_CORRUPT; } end = pc + end_offset; if ( !sieve_binary_read_unsigned(renv->sblock, address, &length) ) { sieve_runtime_trace_operand_error(renv, oprnd, "stringlist corrupt: invalid length data"); return SIEVE_EXEC_BIN_CORRUPT; } if ( strlist_r != NULL ) *strlist_r = sieve_code_stringlist_create (renv, *address, (unsigned int) length, end); /* Skip over the string list for now */ *address = end; return SIEVE_EXEC_OK; } /* Catenated String */ void sieve_opr_catenated_string_emit (struct sieve_binary_block *sblock, unsigned int elements) { (void) sieve_operand_emit(sblock, NULL, &catenated_string_operand); (void) sieve_binary_emit_unsigned(sblock, elements); } static bool opr_catenated_string_dump (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, sieve_size_t *address) { unsigned int elements = 0; unsigned int i; if ( !sieve_binary_read_unsigned(denv->sblock, address, &elements) ) return FALSE; if ( oprnd->field_name != NULL ) sieve_code_dumpf(denv, "%s: CAT-STR [%ld]:", oprnd->field_name, (long) elements); else sieve_code_dumpf(denv, "CAT-STR [%ld]:", (long) elements); sieve_code_descend(denv); for ( i = 0; i < (unsigned int) elements; i++ ) { if ( !sieve_opr_string_dump(denv, address, NULL) ) return FALSE; } sieve_code_ascend(denv); return TRUE; } static int opr_catenated_string_read (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, sieve_size_t *address, string_t **str) { unsigned int elements = 0; unsigned int i; int ret; if ( !sieve_binary_read_unsigned(renv->sblock, address, &elements) ) { sieve_runtime_trace_operand_error(renv, oprnd, "catenated string corrupt: invalid element count data"); return SIEVE_EXEC_BIN_CORRUPT; } /* Parameter str can be NULL if we are requested to only skip and not * actually read the argument. */ if ( str == NULL ) { for ( i = 0; i < (unsigned int) elements; i++ ) { if ( (ret=sieve_opr_string_read(renv, address, NULL, NULL)) <= 0 ) return ret; } } else { string_t *strelm; string_t **elm = &strelm; *str = t_str_new(128); for ( i = 0; i < (unsigned int) elements; i++ ) { if ( (ret=sieve_opr_string_read(renv, address, NULL, elm)) <= 0 ) return ret; if ( elm != NULL ) { str_append_str(*str, strelm); if ( str_len(*str) > SIEVE_MAX_STRING_LEN ) { str_truncate(*str, SIEVE_MAX_STRING_LEN); elm = NULL; } } } } return SIEVE_EXEC_OK; } /* * Core operations */ /* Forward declarations */ static bool opc_jmp_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address); static int opc_jmp_execute (const struct sieve_runtime_env *renv, sieve_size_t *address); static int opc_jmptrue_execute (const struct sieve_runtime_env *renv, sieve_size_t *address); static int opc_jmpfalse_execute (const struct sieve_runtime_env *renv, sieve_size_t *address); /* Operation objects defined in this file */ const struct sieve_operation_def sieve_jmp_operation = { .mnemonic = "JMP", .code = SIEVE_OPERATION_JMP, .dump = opc_jmp_dump, .execute = opc_jmp_execute }; const struct sieve_operation_def sieve_jmptrue_operation = { .mnemonic = "JMPTRUE", .code = SIEVE_OPERATION_JMPTRUE, .dump = opc_jmp_dump, .execute = opc_jmptrue_execute }; const struct sieve_operation_def sieve_jmpfalse_operation = { .mnemonic = "JMPFALSE", .code = SIEVE_OPERATION_JMPFALSE, .dump = opc_jmp_dump, .execute = opc_jmpfalse_execute }; /* Operation objects defined in other files */ extern const struct sieve_operation_def cmd_stop_operation; extern const struct sieve_operation_def cmd_keep_operation; extern const struct sieve_operation_def cmd_discard_operation; extern const struct sieve_operation_def cmd_redirect_operation; extern const struct sieve_operation_def tst_address_operation; extern const struct sieve_operation_def tst_header_operation; extern const struct sieve_operation_def tst_exists_operation; extern const struct sieve_operation_def tst_size_over_operation; extern const struct sieve_operation_def tst_size_under_operation; const struct sieve_operation_def *sieve_operations[] = { NULL, &sieve_jmp_operation, &sieve_jmptrue_operation, &sieve_jmpfalse_operation, &cmd_stop_operation, &cmd_keep_operation, &cmd_discard_operation, &cmd_redirect_operation, &tst_address_operation, &tst_header_operation, &tst_exists_operation, &tst_size_over_operation, &tst_size_under_operation }; const unsigned int sieve_operation_count = N_ELEMENTS(sieve_operations); /* * Operation functions */ sieve_size_t sieve_operation_emit (struct sieve_binary_block *sblock, const struct sieve_extension *ext, const struct sieve_operation_def *op_def) { sieve_size_t address; if ( ext != NULL ) { i_assert( op_def->ext_def != NULL ); address = sieve_binary_emit_extension (sblock, ext, sieve_operation_count); sieve_binary_emit_extension_object (sblock, &op_def->ext_def->operations, op_def->code); return address; } i_assert( op_def->ext_def == NULL ); return sieve_binary_emit_byte(sblock, op_def->code); } bool sieve_operation_read (struct sieve_binary_block *sblock, sieve_size_t *address, struct sieve_operation *oprtn) { unsigned int code = sieve_operation_count; oprtn->address = *address; oprtn->def = NULL; oprtn->ext = NULL; if ( !sieve_binary_read_extension(sblock, address, &code, &oprtn->ext) ) return FALSE; if ( oprtn->ext == NULL ) { if ( code < sieve_operation_count ) { oprtn->def = sieve_operations[code]; } return ( oprtn->def != NULL ); } oprtn->def = (const struct sieve_operation_def *) sieve_binary_read_extension_object(sblock, address, &oprtn->ext->def->operations); return ( oprtn->def != NULL ); } /* * Jump operations */ /* Code dump */ static bool opc_jmp_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address) { const struct sieve_operation *oprtn = denv->oprtn; unsigned int pc = *address; sieve_offset_t offset; if ( sieve_binary_read_offset(denv->sblock, address, &offset) ) sieve_code_dumpf(denv, "%s %d [%08x]", sieve_operation_mnemonic(oprtn), offset, pc + offset); else return FALSE; return TRUE; } /* Code execution */ static int opc_jmp_execute (const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) { return sieve_interpreter_program_jump(renv->interp, TRUE, FALSE); } static int opc_jmptrue_execute (const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) { bool result = sieve_interpreter_get_test_result(renv->interp); sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "jump if result is true"); sieve_runtime_trace_descend(renv); return sieve_interpreter_program_jump(renv->interp, result, FALSE); } static int opc_jmpfalse_execute (const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) { bool result = sieve_interpreter_get_test_result(renv->interp); sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "jump if result is false"); sieve_runtime_trace_descend(renv); return sieve_interpreter_program_jump(renv->interp, !result, FALSE); } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-common.h0000644000000000000000000001107314103200126023102 0ustar00rootroot00000000000000#ifndef SIEVE_COMMON_H #define SIEVE_COMMON_H #include "lib.h" #include "sieve.h" #include /* * Types */ typedef size_t sieve_size_t; typedef uint32_t sieve_offset_t; typedef uint64_t sieve_number_t; #define SIEVE_MAX_NUMBER ((sieve_number_t)-1) /* * Forward declarations */ /* sieve-error.h */ struct sieve_error_handler; /* sieve-ast.h */ enum sieve_ast_argument_type; struct sieve_ast; struct sieve_ast_node; struct sieve_ast_argument; /* sieve-commands.h */ struct sieve_argument; struct sieve_argument_def; struct sieve_command; struct sieve_command_def; struct sieve_command_context; struct sieve_command_registration; /* sieve-stringlist.h */ struct sieve_stringlist; /* sieve-code.h */ struct sieve_operation_extension; /* sieve-lexer.h */ struct sieve_lexer; /* sieve-parser.h */ struct sieve_parser; /* sieve-validator.h */ struct sieve_validator; /* sieve-generator.h */ struct sieve_jumplist; struct sieve_generator; struct sieve_codegen_env; /* sieve-runtime.h */ struct sieve_runtime_env; /* sieve-interpreter.h */ struct sieve_interpreter; /* sieve-dump.h */ struct sieve_dumptime_env; /* sieve-binary-dumper.h */ struct sieve_binary_dumper; /* sieve-code-dumper.h */ struct sieve_code_dumper; /* sieve-extension.h */ struct sieve_extension; struct sieve_extension_def; struct sieve_extension_objects; /* sieve-code.h */ struct sieve_operand; struct sieve_operand_def; struct sieve_operand_class; struct sieve_operation; struct sieve_coded_stringlist; /* sieve-binary.h */ struct sieve_binary; struct sieve_binary_block; struct sieve_binary_debug_writer; struct sieve_binary_debug_reader; /* sieve-execute.h */ struct sieve_execute; /* sieve-objects.h */ struct sieve_object_def; struct sieve_object; /* sieve-comparator.h */ struct sieve_comparator; /* sieve-match-types.h */ struct sieve_match_type; /* sieve-match.h */ struct sieve_match_context; /* sieve-address.h */ struct sieve_address_list; /* sieve-address-parts.h */ struct sieve_address_part_def; struct sieve_address_part; /* sieve-result.h */ struct sieve_result; struct sieve_side_effects_list; struct sieve_result_print_env; /* sieve-actions.h */ struct sieve_action_exec_env; struct sieve_action; struct sieve_action_def; struct sieve_side_effect; struct sieve_side_effect_def; /* sieve-script.h */ struct sieve_script; struct sieve_script_sequence; /* sieve-storage.h */ struct sieve_storage_class_registry; struct sieve_storage; /* sieve-message.h */ struct sieve_message_context; struct sieve_message_override; struct sieve_message_override_def; /* sieve-plugins.h */ struct sieve_plugin; /* sieve.c */ struct sieve_ast *sieve_parse (struct sieve_script *script, struct sieve_error_handler *ehandler, enum sieve_error *error_r); bool sieve_validate (struct sieve_ast *ast, struct sieve_error_handler *ehandler, enum sieve_compile_flags flags, enum sieve_error *error_r); /* * Parent category */ extern struct event_category event_category_sieve; /* * Sieve engine instance */ #include "sieve-address-source.h" struct sieve_instance { /* Main engine pool */ pool_t pool; /* System environment */ const char *hostname; const char *domainname; const char *base_dir; const char *temp_dir; /* User environment */ const char *username; const char *home_dir; /* Flags */ enum sieve_flag flags; /* Callbacks */ const struct sieve_callbacks *callbacks; void *context; /* Logging, events, and debug */ struct event *event; bool debug; /* Extension registry */ struct sieve_extension_registry *ext_reg; /* Storage class registry */ struct sieve_storage_class_registry *storage_reg; /* Plugin modules */ struct sieve_plugin *plugins; enum sieve_env_location env_location; enum sieve_delivery_phase delivery_phase; /* Settings */ size_t max_script_size; unsigned int max_actions; unsigned int max_redirects; unsigned int max_cpu_time_secs; unsigned int resource_usage_timeout_secs; const struct smtp_address *user_email, *user_email_implicit; struct sieve_address_source redirect_from; unsigned int redirect_duplicate_period; }; /* * Script trace log */ void sieve_trace_log_write_line (struct sieve_trace_log *trace_log, const string_t *line) ATTR_NULL(2); /* * User e-mail address */ const struct smtp_address *sieve_get_user_email (struct sieve_instance *svinst); /* * Postmaster address */ const struct message_address * sieve_get_postmaster(const struct sieve_script_env *senv); const struct smtp_address * sieve_get_postmaster_smtp(const struct sieve_script_env *senv); const char * sieve_get_postmaster_address(const struct sieve_script_env *senv); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-limits.h0000644000000000000000000000174614103200126023121 0ustar00rootroot00000000000000#ifndef SIEVE_LIMITS_H #define SIEVE_LIMITS_H /* * Scripts */ #define SIEVE_MAX_SCRIPT_NAME_LEN 256 #define SIEVE_DEFAULT_MAX_SCRIPT_SIZE (1 << 20) #define SIEVE_MAX_LOOP_DEPTH 4 /* * Lexer */ #define SIEVE_MAX_STRING_LEN (1 << 20) #define SIEVE_MAX_IDENTIFIER_LEN 32 /* * AST */ #define SIEVE_MAX_COMMAND_ARGUMENTS 32 #define SIEVE_MAX_BLOCK_NESTING 32 #define SIEVE_MAX_TEST_NESTING 32 /* * Runtime */ #define SIEVE_MAX_MATCH_VALUES 32 #define SIEVE_HIGH_CPU_TIME_MSECS 1500 #define SIEVE_DEFAULT_MAX_CPU_TIME_SECS 30 #define SIEVE_DEFAULT_RESOURCE_USAGE_TIMEOUT_SECS (60 * 60) /* * Actions */ #define SIEVE_DEFAULT_MAX_ACTIONS 32 #define SIEVE_DEFAULT_MAX_REDIRECTS 4 #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-binary.h0000644000000000000000000002067614103200126023107 0ustar00rootroot00000000000000#ifndef SIEVE_BINARY_H #define SIEVE_BINARY_H #include "lib.h" #include "sieve-common.h" /* * Config */ #define SIEVE_BINARY_VERSION_MAJOR 2 #define SIEVE_BINARY_VERSION_MINOR 0 #define SIEVE_BINARY_BASE_HEADER_SIZE 20 /* * Binary object */ struct sieve_binary; struct sieve_binary *sieve_binary_create_new(struct sieve_script *script); void sieve_binary_ref(struct sieve_binary *sbin); void sieve_binary_unref(struct sieve_binary **_sbin); void sieve_binary_close(struct sieve_binary **_sbin); /* * Resource usage */ void sieve_binary_get_resource_usage(struct sieve_binary *sbin, struct sieve_resource_usage *rusage_r); bool sieve_binary_record_resource_usage( struct sieve_binary *sbin, const struct sieve_resource_usage *rusage) ATTR_NULL(1); void sieve_binary_set_resource_usage(struct sieve_binary *sbin, const struct sieve_resource_usage *rusage); /* * Accessors */ pool_t sieve_binary_pool(struct sieve_binary *sbin); struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin); const char *sieve_binary_path(struct sieve_binary *sbin); struct sieve_script *sieve_binary_script(struct sieve_binary *sbin); time_t sieve_binary_mtime(struct sieve_binary *sbin); const struct stat *sieve_binary_stat(struct sieve_binary *sbin); const char *sieve_binary_script_name(struct sieve_binary *sbin); const char *sieve_binary_script_location(struct sieve_binary *sbin); const char *sieve_binary_source(struct sieve_binary *sbin); bool sieve_binary_loaded(struct sieve_binary *sbin); bool sieve_binary_saved(struct sieve_binary *sbin); /* * Utility */ const char *sieve_binfile_from_name(const char *name); /* * Activation after code generation */ void sieve_binary_activate(struct sieve_binary *sbin); /* * Saving the binary */ int sieve_binary_save(struct sieve_binary *sbin, const char *path, bool update, mode_t save_mode, enum sieve_error *error_r); /* * Loading the binary */ struct sieve_binary * sieve_binary_open(struct sieve_instance *svinst, const char *path, struct sieve_script *script, enum sieve_error *error_r); bool sieve_binary_up_to_date(struct sieve_binary *sbin, enum sieve_compile_flags cpflags); int sieve_binary_check_executable(struct sieve_binary *sbin, enum sieve_error *error_r, const char **client_error_r); /* * Block management */ enum sieve_binary_system_block { SBIN_SYSBLOCK_SCRIPT_DATA, SBIN_SYSBLOCK_EXTENSIONS, SBIN_SYSBLOCK_MAIN_PROGRAM, SBIN_SYSBLOCK_LAST }; struct sieve_binary_block *sieve_binary_block_create(struct sieve_binary *sbin); unsigned int sieve_binary_block_count(struct sieve_binary *sbin); struct sieve_binary_block * sieve_binary_block_get(struct sieve_binary *sbin, unsigned int id); void sieve_binary_block_clear(struct sieve_binary_block *sblock); size_t sieve_binary_block_get_size(const struct sieve_binary_block *sblock); struct sieve_binary * sieve_binary_block_get_binary(const struct sieve_binary_block *sblock); unsigned int sieve_binary_block_get_id(const struct sieve_binary_block *sblock); /* * Extension support */ struct sieve_binary_extension { const struct sieve_extension_def *extension; bool (*binary_pre_save)(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context, enum sieve_error *error_r); bool (*binary_post_save)(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context, enum sieve_error *error_r); bool (*binary_open)(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context); void (*binary_free)(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context); bool (*binary_up_to_date)(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context, enum sieve_compile_flags cpflags); }; void sieve_binary_extension_set_context(struct sieve_binary *sbin, const struct sieve_extension *ext, void *context); const void * sieve_binary_extension_get_context(struct sieve_binary *sbin, const struct sieve_extension *ext); void sieve_binary_extension_set(struct sieve_binary *sbin, const struct sieve_extension *ext, const struct sieve_binary_extension *bext, void *context); struct sieve_binary_block * sieve_binary_extension_create_block(struct sieve_binary *sbin, const struct sieve_extension *ext); struct sieve_binary_block * sieve_binary_extension_get_block(struct sieve_binary *sbin, const struct sieve_extension *ext); int sieve_binary_extension_link(struct sieve_binary *sbin, const struct sieve_extension *ext); const struct sieve_extension * sieve_binary_extension_get_by_index(struct sieve_binary *sbin, int index); int sieve_binary_extension_get_index(struct sieve_binary *sbin, const struct sieve_extension *ext); int sieve_binary_extensions_count(struct sieve_binary *sbin); /* * Code emission */ /* Low-level emission functions */ sieve_size_t sieve_binary_emit_data(struct sieve_binary_block *sblock, const void *data, sieve_size_t size); sieve_size_t sieve_binary_emit_byte(struct sieve_binary_block *sblock, uint8_t byte); void sieve_binary_update_data(struct sieve_binary_block *sblock, sieve_size_t address, const void *data, sieve_size_t size); /* Offset emission functions */ sieve_size_t sieve_binary_emit_offset(struct sieve_binary_block *sblock, sieve_offset_t offset); void sieve_binary_resolve_offset(struct sieve_binary_block *sblock, sieve_size_t address); /* Literal emission functions */ sieve_size_t sieve_binary_emit_integer(struct sieve_binary_block *sblock, sieve_number_t integer); sieve_size_t sieve_binary_emit_string(struct sieve_binary_block *sblock, const string_t *str); sieve_size_t sieve_binary_emit_cstring(struct sieve_binary_block *sblock, const char *str); static inline sieve_size_t sieve_binary_emit_unsigned(struct sieve_binary_block *sblock, unsigned int count) { return sieve_binary_emit_integer(sblock, count); } /* Extension emission functions */ sieve_size_t sieve_binary_emit_extension(struct sieve_binary_block *sblock, const struct sieve_extension *ext, unsigned int offset); void sieve_binary_emit_extension_object( struct sieve_binary_block *sblock, const struct sieve_extension_objects *objs, unsigned int code); /* * Code retrieval */ /* Literals */ bool sieve_binary_read_byte(struct sieve_binary_block *sblock, sieve_size_t *address, unsigned int *byte_r) ATTR_NULL(3); bool sieve_binary_read_code(struct sieve_binary_block *sblock, sieve_size_t *address, signed int *code_r) ATTR_NULL(3); bool sieve_binary_read_offset(struct sieve_binary_block *sblock, sieve_size_t *address, sieve_offset_t *offset_r) ATTR_NULL(3); bool sieve_binary_read_integer(struct sieve_binary_block *sblock, sieve_size_t *address, sieve_number_t *int_r) ATTR_NULL(3); bool sieve_binary_read_string(struct sieve_binary_block *sblock, sieve_size_t *address, string_t **str_r) ATTR_NULL(3); static inline bool ATTR_NULL(3) sieve_binary_read_unsigned(struct sieve_binary_block *sblock, sieve_size_t *address, unsigned int *count_r) { sieve_number_t integer = 0; if (!sieve_binary_read_integer(sblock, address, &integer)) return FALSE; if (count_r != NULL) *count_r = integer; return TRUE; } /* Extensions */ bool sieve_binary_read_extension(struct sieve_binary_block *sblock, sieve_size_t *address, unsigned int *offset_r, const struct sieve_extension **ext_r); const void * sieve_binary_read_extension_object(struct sieve_binary_block *sblock, sieve_size_t *address, const struct sieve_extension_objects *objs); /* * Debug info */ /* Writer */ struct sieve_binary_debug_writer; struct sieve_binary_debug_writer * sieve_binary_debug_writer_init(struct sieve_binary_block *sblock); void sieve_binary_debug_writer_deinit( struct sieve_binary_debug_writer **dwriter); void sieve_binary_debug_emit(struct sieve_binary_debug_writer *dwriter, sieve_size_t code_address, unsigned int code_line, unsigned int code_column); /* Reader */ struct sieve_binary_debug_reader * sieve_binary_debug_reader_init(struct sieve_binary_block *sblock); void sieve_binary_debug_reader_deinit( struct sieve_binary_debug_reader **dreader); void sieve_binary_debug_reader_reset(struct sieve_binary_debug_reader *dreader); unsigned int sieve_binary_debug_read_line(struct sieve_binary_debug_reader *dreader, sieve_size_t code_address); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-settings.c0000644000000000000000000001462414103200126023452 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "sieve-common.h" #include "sieve-limits.h" #include "sieve-error.h" #include "sieve-address.h" #include "sieve-address-source.h" #include "sieve-settings.h" #include /* * Access to settings */ bool sieve_setting_get_uint_value(struct sieve_instance *svinst, const char *setting, unsigned long long int *value_r) { const char *str_value; str_value = sieve_setting_get(svinst, setting); if (str_value == NULL || *str_value == '\0') return FALSE; if (str_to_ullong(str_value, value_r) < 0) { e_warning(svinst->event, "invalid unsigned integer value for setting '%s': " "'%s'", setting, str_value); return FALSE; } return TRUE; } bool sieve_setting_get_int_value(struct sieve_instance *svinst, const char *setting, long long int *value_r) { const char *str_value; str_value = sieve_setting_get(svinst, setting); if (str_value == NULL || *str_value == '\0') return FALSE; if (str_to_llong(str_value, value_r) < 0) { e_warning(svinst->event, "invalid integer value for setting '%s': '%s'", setting, str_value); return FALSE; } return TRUE; } bool sieve_setting_get_size_value(struct sieve_instance *svinst, const char *setting, size_t *value_r) { const char *str_value; uintmax_t value, multiply = 1; const char *endp; str_value = sieve_setting_get(svinst, setting); if (str_value == NULL || *str_value == '\0') return FALSE; if (str_parse_uintmax(str_value, &value, &endp) < 0) { e_warning(svinst->event, "invalid size value for setting '%s': '%s'", setting, str_value); return FALSE; } switch (i_toupper(*endp)) { case '\0': /* default */ case 'B': /* byte (useless) */ multiply = 1; break; case 'K': /* kilobyte */ multiply = 1024; break; case 'M': /* megabyte */ multiply = 1024*1024; break; case 'G': /* gigabyte */ multiply = 1024*1024*1024; break; case 'T': /* terabyte */ multiply = 1024ULL*1024*1024*1024; break; default: e_warning(svinst->event, "invalid size value for setting '%s': '%s'", setting, str_value); return FALSE; } if (value > SSIZE_T_MAX / multiply) { e_warning(svinst->event, "overflowing size value for setting '%s': '%s'", setting, str_value); return FALSE; } *value_r = (size_t)(value * multiply); return TRUE; } bool sieve_setting_get_bool_value(struct sieve_instance *svinst, const char *setting, bool *value_r) { const char *str_value; str_value = sieve_setting_get(svinst, setting); if (str_value == NULL) return FALSE; str_value = t_str_trim(str_value, "\t "); if (*str_value == '\0') return FALSE; if (strcasecmp(str_value, "yes") == 0) { *value_r = TRUE; return TRUE; } if (strcasecmp(str_value, "no") == 0) { *value_r = FALSE; return TRUE; } e_warning(svinst->event, "invalid boolean value for setting '%s': '%s'", setting, str_value); return FALSE; } bool sieve_setting_get_duration_value(struct sieve_instance *svinst, const char *setting, sieve_number_t *value_r) { const char *str_value; uintmax_t value, multiply = 1; const char *endp; str_value = sieve_setting_get(svinst, setting); if (str_value == NULL) return FALSE; str_value = t_str_trim(str_value, "\t "); if (*str_value == '\0') return FALSE; if (str_parse_uintmax(str_value, &value, &endp) < 0) { e_warning(svinst->event, "invalid duration value for setting '%s': '%s'", setting, str_value); return FALSE; } switch (i_tolower(*endp)) { case '\0': /* default */ case 's': /* seconds */ multiply = 1; break; case 'm': /* minutes */ multiply = 60; break; case 'h': /* hours */ multiply = 60*60; break; case 'd': /* days */ multiply = 24*60*60; break; default: e_warning(svinst->event, "invalid duration value for setting '%s': '%s'", setting, str_value); return FALSE; } if (value > SIEVE_MAX_NUMBER / multiply) { e_warning(svinst->event, "overflowing duration value for setting '%s': '%s'", setting, str_value); return FALSE; } *value_r = (unsigned int)(value * multiply); return TRUE; } /* * Main Sieve engine settings */ void sieve_settings_load(struct sieve_instance *svinst) { const char *str_setting, *error; unsigned long long int uint_setting; size_t size_setting; sieve_number_t period; svinst->max_script_size = SIEVE_DEFAULT_MAX_SCRIPT_SIZE; if (sieve_setting_get_size_value(svinst, "sieve_max_script_size", &size_setting)) svinst->max_script_size = size_setting; svinst->max_actions = SIEVE_DEFAULT_MAX_ACTIONS; if (sieve_setting_get_uint_value(svinst, "sieve_max_actions", &uint_setting)) svinst->max_actions = (unsigned int)uint_setting; svinst->max_redirects = SIEVE_DEFAULT_MAX_REDIRECTS; if (sieve_setting_get_uint_value(svinst, "sieve_max_redirects", &uint_setting)) svinst->max_redirects = (unsigned int)uint_setting; svinst->max_cpu_time_secs = SIEVE_DEFAULT_MAX_CPU_TIME_SECS; if (sieve_setting_get_duration_value(svinst, "sieve_max_cpu_time", &period)) { if (period > (UINT_MAX / 1000)) svinst->max_cpu_time_secs = (UINT_MAX / 1000); else svinst->max_cpu_time_secs = (unsigned int)period; } svinst->resource_usage_timeout_secs = SIEVE_DEFAULT_RESOURCE_USAGE_TIMEOUT_SECS; if (sieve_setting_get_duration_value( svinst, "sieve_resource_usage_timeout", &period)) { if (period > UINT_MAX) svinst->resource_usage_timeout_secs = UINT_MAX; else { svinst->resource_usage_timeout_secs = (unsigned int)period; } } (void)sieve_address_source_parse_from_setting( svinst, svinst->pool, "sieve_redirect_envelope_from", &svinst->redirect_from); svinst->redirect_duplicate_period = DEFAULT_REDIRECT_DUPLICATE_PERIOD; if (sieve_setting_get_duration_value( svinst, "sieve_redirect_duplicate_period", &period)) { if (period > UINT_MAX) svinst->redirect_duplicate_period = UINT_MAX; else { svinst->redirect_duplicate_period = (unsigned int)period; } } str_setting = sieve_setting_get(svinst, "sieve_user_email"); if (str_setting != NULL && *str_setting != '\0') { struct smtp_address *address; if (smtp_address_parse_path( svinst->pool, str_setting, SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL, &address, &error) < 0) { e_warning(svinst->event, "Invalid address value for setting " "`sieve_user_email': %s", error); } else { svinst->user_email = address; } } } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/tst-allof.c0000644000000000000000000000531114103200126022377 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "sieve-common.h" #include "sieve-commands.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-binary.h" #include "sieve-code.h" #include "sieve-binary.h" /* * Allof test * * Syntax * allof */ static bool tst_allof_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx, struct sieve_jumplist *jumps, bool jump_true); static bool tst_allof_validate_const (struct sieve_validator *valdtr, struct sieve_command *tst, int *const_current, int const_new); const struct sieve_command_def tst_allof = { .identifier = "allof", .type = SCT_TEST, .positional_args = 0, .subtests = 2, .block_allowed = FALSE, .block_required = FALSE, .validate_const = tst_allof_validate_const, .control_generate = tst_allof_generate }; /* * Code validation */ static bool tst_allof_validate_const (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *tst ATTR_UNUSED, int *const_current, int const_next) { if ( const_next == 0 ) { *const_current = 0; return FALSE; } if ( *const_current != -1 ) *const_current = const_next; return TRUE; } /* * Code generation */ static bool tst_allof_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx, struct sieve_jumplist *jumps, bool jump_true) { struct sieve_binary_block *sblock = cgenv->sblock; struct sieve_ast_node *test; struct sieve_jumplist false_jumps; if ( sieve_ast_test_count(ctx->ast_node) > 1 ) { if ( jump_true ) { /* Prepare jumplist */ sieve_jumplist_init_temp(&false_jumps, sblock); } test = sieve_ast_test_first(ctx->ast_node); while ( test != NULL ) { bool result; /* If this test list must jump on false, all sub-tests can simply add their jumps * to the caller's jump list, otherwise this test redirects all false jumps to the * end of the currently generated code. This is just after a final jump to the true * case */ if ( jump_true ) result = sieve_generate_test(cgenv, test, &false_jumps, FALSE); else result = sieve_generate_test(cgenv, test, jumps, FALSE); if ( !result ) return FALSE; test = sieve_ast_test_next(test); } if ( jump_true ) { /* All tests succeeded, jump to case TRUE */ sieve_operation_emit(cgenv->sblock, NULL, &sieve_jmp_operation); sieve_jumplist_add(jumps, sieve_binary_emit_offset(sblock, 0)); /* All false exits jump here */ sieve_jumplist_resolve(&false_jumps); } } else { /* Script author is being inefficient; we can optimize the allof test away */ test = sieve_ast_test_first(ctx->ast_node); sieve_generate_test(cgenv, test, jumps, jump_true); } return TRUE; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/ext-envelope.c0000644000000000000000000004522114103200126023111 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ /* Extension envelope * ------------------ * * Authors: Stephan Bosch * Specification: RFC 5228 * Implementation: full * Status: testing * */ #include "lib.h" #include "str-sanitize.h" #include "array.h" #include "sieve-common.h" #include "sieve-extensions.h" #include "sieve-commands.h" #include "sieve-stringlist.h" #include "sieve-code.h" #include "sieve-address.h" #include "sieve-comparators.h" #include "sieve-match-types.h" #include "sieve-address-parts.h" #include "sieve-message.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-interpreter.h" #include "sieve-dump.h" #include "sieve-match.h" /* * Forward declarations */ static const struct sieve_command_def envelope_test; const struct sieve_operation_def envelope_operation; const struct sieve_extension_def envelope_extension; /* * Extension */ static bool ext_envelope_validator_load(const struct sieve_extension *ext, struct sieve_validator *valdtr); static bool ext_envelope_interpreter_load(const struct sieve_extension *ext, const struct sieve_runtime_env *renv, sieve_size_t *address); static bool ext_envelope_validator_validate(const struct sieve_extension *ext, struct sieve_validator *valdtr, void *context, struct sieve_ast_argument *require_arg, bool required); static int ext_envelope_interpreter_run(const struct sieve_extension *this_ext, const struct sieve_runtime_env *renv, void *context, bool deferred); const struct sieve_extension_def envelope_extension = { .name = "envelope", .interpreter_load = ext_envelope_interpreter_load, .validator_load = ext_envelope_validator_load, SIEVE_EXT_DEFINE_OPERATION(envelope_operation) }; const struct sieve_validator_extension envelope_validator_extension = { .ext = &envelope_extension, .validate = ext_envelope_validator_validate }; const struct sieve_interpreter_extension envelope_interpreter_extension = { .ext_def = &envelope_extension, .run = ext_envelope_interpreter_run }; static bool ext_envelope_validator_load(const struct sieve_extension *ext, struct sieve_validator *valdtr) { /* Register new test */ sieve_validator_register_command(valdtr, ext, &envelope_test); sieve_validator_extension_register(valdtr, ext, &envelope_validator_extension, NULL); return TRUE; } static bool ext_envelope_interpreter_load(const struct sieve_extension *ext, const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) { sieve_interpreter_extension_register(renv->interp, ext, &envelope_interpreter_extension, NULL); return TRUE; } static bool ext_envelope_validator_validate(const struct sieve_extension *ext, struct sieve_validator *valdtr, void *context ATTR_UNUSED, struct sieve_ast_argument *require_arg, bool required) { if (required) { enum sieve_compile_flags flags = sieve_validator_compile_flags(valdtr); if ((flags & SIEVE_COMPILE_FLAG_NO_ENVELOPE) != 0) { sieve_argument_validate_error( valdtr, require_arg, "the %s extension cannot be used in this context " "(needs access to message envelope)", sieve_extension_name(ext)); return FALSE; } } return TRUE; } static int ext_envelope_interpreter_run(const struct sieve_extension *ext, const struct sieve_runtime_env *renv, void *context ATTR_UNUSED, bool deferred) { const struct sieve_execute_env *eenv = renv->exec_env; if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) { if (!deferred) { sieve_runtime_error( renv, NULL, "the %s extension cannot be used in this context " "(needs access to message envelope)", sieve_extension_name(ext)); } return SIEVE_EXEC_FAILURE; } return SIEVE_EXEC_OK; } /* * Envelope test * * Syntax * envelope [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE] * */ static bool tst_envelope_registered(struct sieve_validator *valdtr, const struct sieve_extension *ext, struct sieve_command_registration *cmd_reg); static bool tst_envelope_validate(struct sieve_validator *valdtr, struct sieve_command *tst); static bool tst_envelope_generate(const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); static const struct sieve_command_def envelope_test = { .identifier = "envelope", .type = SCT_TEST, .positional_args= 2, .subtests = 0, .block_allowed = FALSE, .block_required = FALSE, .registered = tst_envelope_registered, .validate = tst_envelope_validate, .generate = tst_envelope_generate }; /* * Envelope operation */ static bool ext_envelope_operation_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address); static int ext_envelope_operation_execute(const struct sieve_runtime_env *renv, sieve_size_t *address); const struct sieve_operation_def envelope_operation = { .mnemonic = "ENVELOPE", .ext_def = &envelope_extension, .dump = ext_envelope_operation_dump, .execute = ext_envelope_operation_execute }; /* * Envelope parts * * FIXME: not available to extensions */ struct sieve_envelope_part { const char *identifier; const struct smtp_address *const *(*get_addresses) (const struct sieve_runtime_env *renv); const char * const *(*get_values) (const struct sieve_runtime_env *renv); }; static const struct smtp_address *const * _from_part_get_addresses(const struct sieve_runtime_env *renv); static const char *const * _from_part_get_values(const struct sieve_runtime_env *renv); static const struct smtp_address *const * _to_part_get_addresses(const struct sieve_runtime_env *renv); static const char *const * _to_part_get_values(const struct sieve_runtime_env *renv); static const char *const * _auth_part_get_values(const struct sieve_runtime_env *renv); static const struct sieve_envelope_part _from_part = { "from", _from_part_get_addresses, _from_part_get_values, }; static const struct sieve_envelope_part _to_part = { "to", _to_part_get_addresses, _to_part_get_values, }; static const struct sieve_envelope_part _auth_part = { "auth", NULL, _auth_part_get_values, }; static const struct sieve_envelope_part *_envelope_parts[] = { /* Required */ &_from_part, &_to_part, /* Non-standard */ &_auth_part }; static unsigned int _envelope_part_count = N_ELEMENTS(_envelope_parts); static const struct sieve_envelope_part * _envelope_part_find(const char *identifier) { unsigned int i; for (i = 0; i < _envelope_part_count; i++) { if (strcasecmp(_envelope_parts[i]->identifier, identifier) == 0) return _envelope_parts[i]; } return NULL; } /* Envelope parts implementation */ static const struct smtp_address *const * _from_part_get_addresses(const struct sieve_runtime_env *renv) { ARRAY(const struct smtp_address *) envelope_values; const struct smtp_address *address = sieve_message_get_sender(renv->msgctx); t_array_init(&envelope_values, 2); if (address == NULL) address = smtp_address_create_temp(NULL, NULL); array_append(&envelope_values, &address, 1); (void)array_append_space(&envelope_values); return array_idx(&envelope_values, 0); } static const char *const * _from_part_get_values(const struct sieve_runtime_env *renv) { ARRAY(const char *)envelope_values; const struct smtp_address *address = sieve_message_get_sender(renv->msgctx); const char *value; t_array_init(&envelope_values, 2); value = ""; if (!smtp_address_isnull(address)) value = smtp_address_encode(address); array_append(&envelope_values, &value, 1); (void)array_append_space(&envelope_values); return array_idx(&envelope_values, 0); } static const struct smtp_address *const * _to_part_get_addresses(const struct sieve_runtime_env *renv) { ARRAY(const struct smtp_address *) envelope_values; const struct smtp_address *address = sieve_message_get_orig_recipient(renv->msgctx); if (address != NULL && address->localpart != NULL) { t_array_init(&envelope_values, 2); array_append(&envelope_values, &address, 1); (void)array_append_space(&envelope_values); return array_idx(&envelope_values, 0); } return NULL; } static const char *const * _to_part_get_values(const struct sieve_runtime_env *renv) { ARRAY(const char *) envelope_values; const struct smtp_address *address = sieve_message_get_orig_recipient(renv->msgctx); t_array_init(&envelope_values, 2); if (address != NULL && address->localpart != NULL) { const char *value = smtp_address_encode(address); array_append(&envelope_values, &value, 1); } (void)array_append_space(&envelope_values); return array_idx(&envelope_values, 0); } static const char *const * _auth_part_get_values(const struct sieve_runtime_env *renv) { const struct sieve_execute_env *eenv = renv->exec_env; ARRAY(const char *) envelope_values; t_array_init(&envelope_values, 2); if (eenv->msgdata->auth_user != NULL) array_append(&envelope_values, &eenv->msgdata->auth_user, 1); (void)array_append_space(&envelope_values); return array_idx(&envelope_values, 0); } /* * Envelope address list */ /* Forward declarations */ static int sieve_envelope_address_list_next_string_item(struct sieve_stringlist *_strlist, string_t **str_r); static int sieve_envelope_address_list_next_item(struct sieve_address_list *_addrlist, struct smtp_address *addr_r, string_t **unparsed_r); static void sieve_envelope_address_list_reset(struct sieve_stringlist *_strlist); /* Stringlist object */ struct sieve_envelope_address_list { struct sieve_address_list addrlist; struct sieve_stringlist *env_parts; const struct smtp_address *const *cur_addresses; const char * const *cur_values; int value_index; }; static struct sieve_address_list * sieve_envelope_address_list_create(const struct sieve_runtime_env *renv, struct sieve_stringlist *env_parts) { struct sieve_envelope_address_list *addrlist; addrlist = t_new(struct sieve_envelope_address_list, 1); addrlist->addrlist.strlist.runenv = renv; addrlist->addrlist.strlist.exec_status = SIEVE_EXEC_OK; addrlist->addrlist.strlist.next_item = sieve_envelope_address_list_next_string_item; addrlist->addrlist.strlist.reset = sieve_envelope_address_list_reset; addrlist->addrlist.next_item = sieve_envelope_address_list_next_item; addrlist->env_parts = env_parts; return &addrlist->addrlist; } static int sieve_envelope_address_list_next_item(struct sieve_address_list *_addrlist, struct smtp_address *addr_r, string_t **unparsed_r) { struct sieve_envelope_address_list *addrlist = (struct sieve_envelope_address_list *)_addrlist; const struct sieve_runtime_env *renv = _addrlist->strlist.runenv; if (addr_r != NULL) smtp_address_init(addr_r, NULL, NULL); if (unparsed_r != NULL) *unparsed_r = NULL; while (addrlist->cur_addresses == NULL && addrlist->cur_values == NULL) { const struct sieve_envelope_part *epart; string_t *envp_item = NULL; int ret; /* Read next header value from source list */ if ((ret = sieve_stringlist_next_item(addrlist->env_parts, &envp_item)) <= 0) return ret; if (_addrlist->strlist.trace) { sieve_runtime_trace( _addrlist->strlist.runenv, 0, "getting `%s' part from message envelope", str_sanitize(str_c(envp_item), 80)); } if ((epart=_envelope_part_find(str_c(envp_item))) != NULL) { addrlist->value_index = 0; if (epart->get_addresses != NULL) { /* Field contains addresses */ addrlist->cur_addresses = epart->get_addresses(renv); /* Drop empty list */ if (addrlist->cur_addresses != NULL && addrlist->cur_addresses[0] == NULL) addrlist->cur_addresses = NULL; } if (addrlist->cur_addresses == NULL && epart->get_values != NULL) { /* Field contains something else */ addrlist->cur_values = epart->get_values(renv); /* Drop empty list */ if (addrlist->cur_values != NULL && addrlist->cur_values[0] == NULL) addrlist->cur_values = NULL; } } } /* Return next item */ if (addrlist->cur_addresses != NULL) { const struct smtp_address *addr = addrlist->cur_addresses[addrlist->value_index]; if (addr->localpart == NULL) { /* Null path <> */ if (unparsed_r != NULL) *unparsed_r = t_str_new_const("", 0); } else { if (addr_r != NULL) *addr_r = *addr; } /* Advance to next value */ addrlist->value_index++; if (addrlist->cur_addresses[addrlist->value_index] == NULL) { addrlist->cur_addresses = NULL; addrlist->value_index = 0; } } else { if (unparsed_r != NULL) { const char *value = addrlist->cur_values[addrlist->value_index]; *unparsed_r = t_str_new_const(value, strlen(value)); } /* Advance to next value */ addrlist->value_index++; if (addrlist->cur_values[addrlist->value_index] == NULL) { addrlist->cur_values = NULL; addrlist->value_index = 0; } } return 1; } static int sieve_envelope_address_list_next_string_item(struct sieve_stringlist *_strlist, string_t **str_r) { struct sieve_address_list *addrlist = (struct sieve_address_list *)_strlist; struct smtp_address addr; int ret; if ((ret=sieve_envelope_address_list_next_item(addrlist, &addr, str_r)) <= 0) return ret; if (addr.localpart != NULL) { const char *addr_str = smtp_address_encode(&addr); if (str_r != NULL) *str_r = t_str_new_const(addr_str, strlen(addr_str)); } return 1; } static void sieve_envelope_address_list_reset(struct sieve_stringlist *_strlist) { struct sieve_envelope_address_list *addrlist = (struct sieve_envelope_address_list *)_strlist; sieve_stringlist_reset(addrlist->env_parts); addrlist->cur_addresses = NULL; addrlist->cur_values = NULL; addrlist->value_index = 0; } /* * Command Registration */ static bool tst_envelope_registered(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, struct sieve_command_registration *cmd_reg) { /* The order of these is not significant */ sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); sieve_address_parts_link_tags(valdtr, cmd_reg, SIEVE_AM_OPT_ADDRESS_PART); return TRUE; } /* * Validation */ static int _envelope_part_is_supported(void *context, struct sieve_ast_argument *arg) { const struct sieve_envelope_part **not_address = (const struct sieve_envelope_part **) context; if (sieve_argument_is_string_literal(arg)) { const struct sieve_envelope_part *epart; if ((epart=_envelope_part_find( sieve_ast_strlist_strc(arg))) != NULL) { if (epart->get_addresses == NULL) { if (*not_address == NULL) *not_address = epart; } return 1; } return 0; } return 1; /* Can't check at compile time */ } static bool tst_envelope_validate(struct sieve_validator *valdtr, struct sieve_command *tst) { struct sieve_ast_argument *arg = tst->first_positional; struct sieve_ast_argument *epart; struct sieve_comparator cmp_default = SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); struct sieve_match_type mcht_default = SIEVE_MATCH_TYPE_DEFAULT(is_match_type); const struct sieve_envelope_part *not_address = NULL; if (!sieve_validate_positional_argument(valdtr, tst, arg, "envelope part", 1, SAAT_STRING_LIST)) { return FALSE; } if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) return FALSE; /* Check whether supplied envelope parts are supported * FIXME: verify dynamic envelope parts at runtime */ epart = arg; if (sieve_ast_stringlist_map(&epart, (void *) ¬_address, _envelope_part_is_supported) <= 0) { i_assert(epart != NULL); sieve_argument_validate_error( valdtr, epart, "specified envelope part '%s' is not supported by the envelope test", str_sanitize(sieve_ast_strlist_strc(epart), 64)); return FALSE; } if (not_address != NULL) { struct sieve_ast_argument *addrp_arg = sieve_command_find_argument(tst, &address_part_tag); if (addrp_arg != NULL) { sieve_argument_validate_error( valdtr, addrp_arg, "address part ':%s' specified while non-address envelope part '%s' " "is tested with the envelope test", sieve_ast_argument_tag(addrp_arg), not_address->identifier); return FALSE; } } arg = sieve_ast_argument_next(arg); if (!sieve_validate_positional_argument(valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST)) return FALSE; if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) return FALSE; /* Validate the key argument to a specified match type */ return sieve_match_type_validate(valdtr, tst, arg, &mcht_default, &cmp_default); } /* * Code generation */ static bool tst_envelope_generate(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) { (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &envelope_operation); /* Generate arguments */ if (!sieve_generate_arguments(cgenv, cmd, NULL)) return FALSE; return TRUE; } /* * Code dump */ static bool ext_envelope_operation_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address) { sieve_code_dumpf(denv, "ENVELOPE"); sieve_code_descend(denv); /* Handle any optional arguments */ if (sieve_addrmatch_opr_optional_dump(denv, address, NULL) != 0) return FALSE; return (sieve_opr_stringlist_dump(denv, address, "envelope part") && sieve_opr_stringlist_dump(denv, address, "key list")); } /* * Interpretation */ static int ext_envelope_operation_execute(const struct sieve_runtime_env *renv, sieve_size_t *address) { struct sieve_comparator cmp = SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); struct sieve_match_type mcht = SIEVE_MATCH_TYPE_DEFAULT(is_match_type); struct sieve_address_part addrp = SIEVE_ADDRESS_PART_DEFAULT(all_address_part); struct sieve_stringlist *env_part_list, *value_list, *key_list; struct sieve_address_list *addr_list; int match, ret; /* * Read operands */ /* Read optional operands */ if (sieve_addrmatch_opr_optional_read(renv, address, NULL, &ret, &addrp, &mcht, &cmp) < 0) return ret; /* Read envelope-part */ if ((ret = sieve_opr_stringlist_read(renv, address, "envelope-part", &env_part_list)) <= 0) return ret; /* Read key-list */ if ((ret = sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) <= 0) return ret; /* * Perform test */ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "envelope test"); /* Create value stringlist */ addr_list = sieve_envelope_address_list_create(renv, env_part_list); value_list = sieve_address_part_stringlist_create(renv, &addrp, addr_list); /* Perform match */ if ((match = sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0) return ret; /* Set test result for subsequent conditional jump */ sieve_interpreter_set_test_result(renv->interp, match > 0); return SIEVE_EXEC_OK; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/tst-address.c0000644000000000000000000001730014103200126022730 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "str-sanitize.h" #include "sieve-common.h" #include "sieve-commands.h" #include "sieve-stringlist.h" #include "sieve-code.h" #include "sieve-comparators.h" #include "sieve-match-types.h" #include "sieve-message.h" #include "sieve-address.h" #include "sieve-address-parts.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-interpreter.h" #include "sieve-dump.h" #include "sieve-match.h" #include /* * Address test * * Syntax: * address [ADDRESS-PART] [COMPARATOR] [MATCH-TYPE] * */ static bool tst_address_registered (struct sieve_validator *valdtr, const struct sieve_extension *ext, struct sieve_command_registration *cmd_reg); static bool tst_address_validate (struct sieve_validator *valdtr, struct sieve_command *tst); static bool tst_address_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); const struct sieve_command_def tst_address = { .identifier = "address", .type = SCT_TEST, .positional_args = 2, .subtests = 0, .block_allowed = FALSE, .block_required = FALSE, .registered = tst_address_registered, .validate = tst_address_validate, .generate = tst_address_generate }; /* * Address operation */ static bool tst_address_operation_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address); static int tst_address_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address); const struct sieve_operation_def tst_address_operation = { .mnemonic = "ADDRESS", .code = SIEVE_OPERATION_ADDRESS, .dump = tst_address_operation_dump, .execute = tst_address_operation_execute }; /* * Test registration */ static bool tst_address_registered (struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, struct sieve_command_registration *cmd_reg) { /* The order of these is not significant */ sieve_comparators_link_tag (valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR ); sieve_match_types_link_tags (valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); sieve_address_parts_link_tags (valdtr, cmd_reg, SIEVE_AM_OPT_ADDRESS_PART); return TRUE; } /* * Validation */ /* List of valid headers: * Implementations MUST restrict the address test to headers that * contain addresses, but MUST include at least From, To, Cc, Bcc, * Sender, Resent-From, and Resent-To, and it SHOULD include any other * header that utilizes an "address-list" structured header body. * * This list explicitly does not contain the envelope-to and return-path * headers. The envelope test must be used to test against these addresses. * * FIXME: this restriction is somewhat odd. Sieve list advises to allow * any other header as long as its content matches the address-list * grammar. */ static const char * const _allowed_headers[] = { /* Required */ "from", "to", "cc", "bcc", "sender", "resent-from", "resent-to", /* Additional (RFC 822 / RFC 2822) */ "reply-to", "resent-reply-to", "resent-sender", "resent-cc", "resent-bcc", /* Non-standard (RFC 2076, draft-palme-mailext-headers-08.txt) */ "for-approval", "for-handling", "for-comment", "apparently-to", "errors-to", "delivered-to", "return-receipt-to", "x-admin", "read-receipt-to", "x-confirm-reading-to", "return-receipt-requested", "registered-mail-reply-requested-by", "mail-followup-to", "mail-reply-to", "abuse-reports-to", "x-complaints-to", "x-report-abuse-to", /* Undocumented */ "x-beenthere", "x-original-to", NULL }; static int _header_is_allowed (void *context ATTR_UNUSED, struct sieve_ast_argument *arg) { if ( sieve_argument_is_string_literal(arg) ) { const char *header = sieve_ast_strlist_strc(arg); const char * const *hdsp = _allowed_headers; while ( *hdsp != NULL ) { if ( strcasecmp( *hdsp, header ) == 0 ) return 1; hdsp++; } return 0; } return 1; } static bool tst_address_validate (struct sieve_validator *valdtr, struct sieve_command *tst) { struct sieve_ast_argument *arg = tst->first_positional; struct sieve_ast_argument *header; struct sieve_comparator cmp_default = SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); struct sieve_match_type mcht_default = SIEVE_MATCH_TYPE_DEFAULT(is_match_type); if ( !sieve_validate_positional_argument (valdtr, tst, arg, "header list", 1, SAAT_STRING_LIST) ) { return FALSE; } if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) return FALSE; if ( !sieve_command_verify_headers_argument(valdtr, arg) ) return FALSE; /* Check if supplied header names are allowed * FIXME: verify dynamic header names at runtime */ header = arg; if ( sieve_ast_stringlist_map (&header, NULL, _header_is_allowed) <= 0 ) { i_assert(header != NULL); sieve_argument_validate_error(valdtr, header, "specified header '%s' is not allowed for the address test", str_sanitize(sieve_ast_strlist_strc(header), 64)); return FALSE; } /* Check key list */ arg = sieve_ast_argument_next(arg); if ( !sieve_validate_positional_argument (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) { return FALSE; } if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) return FALSE; /* Validate the key argument to a specified match type */ return sieve_match_type_validate (valdtr, tst, arg, &mcht_default, &cmp_default); } /* * Code generation */ static bool tst_address_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *tst) { sieve_operation_emit(cgenv->sblock, NULL, &tst_address_operation); /* Generate arguments */ return sieve_generate_arguments(cgenv, tst, NULL); } /* * Code dump */ static bool tst_address_operation_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address) { sieve_code_dumpf(denv, "ADDRESS"); sieve_code_descend(denv); /* Handle any optional arguments */ if ( sieve_message_opr_optional_dump(denv, address, NULL) != 0 ) return FALSE; return sieve_opr_stringlist_dump(denv, address, "header list") && sieve_opr_stringlist_dump(denv, address, "key list"); } /* * Code execution */ static int tst_address_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address) { struct sieve_comparator cmp = SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); struct sieve_match_type mcht = SIEVE_MATCH_TYPE_DEFAULT(is_match_type); struct sieve_address_part addrp = SIEVE_ADDRESS_PART_DEFAULT(all_address_part); struct sieve_stringlist *hdr_list, *hdr_value_list, *value_list, *key_list; struct sieve_address_list *addr_list; ARRAY_TYPE(sieve_message_override) svmos; int match, ret; /* Read optional operands */ i_zero(&svmos); if ( sieve_message_opr_optional_read (renv, address, NULL, &ret, &addrp, &mcht, &cmp, &svmos) < 0 ) return ret; /* Read header-list */ if ( (ret=sieve_opr_stringlist_read(renv, address, "header-list", &hdr_list)) <= 0 ) return ret; /* Read key-list */ if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) <= 0 ) return ret; sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "address test"); /* Get header */ sieve_runtime_trace_descend(renv); if ( (ret=sieve_message_get_header_fields (renv, hdr_list, &svmos, FALSE, &hdr_value_list)) <= 0 ) return ret; sieve_runtime_trace_ascend(renv); /* Create value stringlist */ addr_list = sieve_header_address_list_create(renv, hdr_value_list); value_list = sieve_address_part_stringlist_create(renv, &addrp, addr_list); /* Perform match */ if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 ) return ret; /* Set test result for subsequent conditional jump */ sieve_interpreter_set_test_result(renv->interp, match > 0); return SIEVE_EXEC_OK; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/tst-exists.c0000644000000000000000000001027714103200126022630 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "str-sanitize.h" #include "sieve-common.h" #include "sieve-commands.h" #include "sieve-stringlist.h" #include "sieve-code.h" #include "sieve-message.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-interpreter.h" #include "sieve-code-dumper.h" /* * Exists test * * Syntax: * exists */ static bool tst_exists_validate (struct sieve_validator *valdtr, struct sieve_command *tst); static bool tst_exists_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *tst); const struct sieve_command_def tst_exists = { .identifier = "exists", .type = SCT_TEST, .positional_args = 1, .subtests = 0, .block_allowed = FALSE, .block_required = FALSE, .validate = tst_exists_validate, .generate = tst_exists_generate }; /* * Exists operation */ static bool tst_exists_operation_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address); static int tst_exists_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address); const struct sieve_operation_def tst_exists_operation = { .mnemonic = "EXISTS", .code = SIEVE_OPERATION_EXISTS, .dump = tst_exists_operation_dump, .execute = tst_exists_operation_execute }; /* * Validation */ static bool tst_exists_validate (struct sieve_validator *valdtr, struct sieve_command *tst) { struct sieve_ast_argument *arg = tst->first_positional; if ( !sieve_validate_positional_argument (valdtr, tst, arg, "header names", 1, SAAT_STRING_LIST) ) { return FALSE; } if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) return FALSE; return sieve_command_verify_headers_argument(valdtr, arg); } /* * Code generation */ static bool tst_exists_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *tst) { sieve_operation_emit(cgenv->sblock, NULL, &tst_exists_operation); /* Generate arguments */ return sieve_generate_arguments(cgenv, tst, NULL); } /* * Code dump */ static bool tst_exists_operation_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address) { sieve_code_dumpf(denv, "EXISTS"); sieve_code_descend(denv); /* Optional operands */ if ( sieve_message_opr_optional_dump(denv, address, NULL) != 0 ) return FALSE; return sieve_opr_stringlist_dump(denv, address, "header names"); } /* * Code execution */ static int tst_exists_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address) { struct sieve_stringlist *hdr_list; ARRAY_TYPE(sieve_message_override) svmos; string_t *hdr_item; bool matched; int ret; /* * Read operands */ /* Optional operands */ i_zero(&svmos); if ( sieve_message_opr_optional_read (renv, address, NULL, &ret, NULL, NULL, NULL, &svmos) < 0 ) return ret; /* Read header-list */ if ( (ret=sieve_opr_stringlist_read(renv, address, "header-list", &hdr_list)) <= 0 ) return ret; /* * Perfrom test */ sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "exists test"); sieve_runtime_trace_descend(renv); /* Iterate through all requested headers to match (must find all specified) */ hdr_item = NULL; matched = TRUE; while ( matched && (ret=sieve_stringlist_next_item(hdr_list, &hdr_item)) > 0 ) { struct sieve_stringlist *value_list; string_t *dummy; /* Get header */ if ( (ret=sieve_message_get_header_fields (renv, sieve_single_stringlist_create(renv, hdr_item, FALSE), &svmos, FALSE, &value_list)) <= 0 ) return ret; if ( (ret=sieve_stringlist_next_item(value_list, &dummy)) < 0) return value_list->exec_status; if ( ret == 0 ) matched = FALSE; sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, "header `%s' %s", str_sanitize(str_c(hdr_item), 80), ( matched ? "exists" : "is missing" )); } if ( matched ) sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, "all headers exist"); else sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, "headers are missing"); /* Set test result for subsequent conditional jump */ if ( ret >= 0 ) { sieve_interpreter_set_test_result(renv->interp, matched); return SIEVE_EXEC_OK; } sieve_runtime_trace_error(renv, "invalid header-list item"); return SIEVE_EXEC_BIN_CORRUPT; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-match-types.c0000644000000000000000000003434114103200126024046 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "compat.h" #include "mempool.h" #include "hash.h" #include "array.h" #include "sieve-common.h" #include "sieve-limits.h" #include "sieve-extensions.h" #include "sieve-commands.h" #include "sieve-code.h" #include "sieve-binary.h" #include "sieve-comparators.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-interpreter.h" #include "sieve-dump.h" #include "sieve-match-types.h" #include /* * Types */ struct sieve_match_values { pool_t pool; ARRAY(string_t *) values; unsigned count; }; /* * Default match types */ const struct sieve_match_type_def *sieve_core_match_types[] = { &is_match_type, &contains_match_type, &matches_match_type }; const unsigned int sieve_core_match_types_count = N_ELEMENTS(sieve_core_match_types); /* * Match-type 'extension' */ static bool mtch_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def match_type_extension = { .name = "@match-types", .validator_load = mtch_validator_load }; /* * Validator context: * name-based match-type registry. */ static struct sieve_validator_object_registry *_get_object_registry (struct sieve_validator *valdtr) { struct sieve_instance *svinst; const struct sieve_extension *mcht_ext; svinst = sieve_validator_svinst(valdtr); mcht_ext = sieve_get_match_type_extension(svinst); return sieve_validator_object_registry_get(valdtr, mcht_ext); } void sieve_match_type_register (struct sieve_validator *valdtr, const struct sieve_extension *ext, const struct sieve_match_type_def *mcht_def) { struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); sieve_validator_object_registry_add(regs, ext, &mcht_def->obj_def); } static bool sieve_match_type_exists (struct sieve_validator *valdtr, const char *identifier) { struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); return sieve_validator_object_registry_find(regs, identifier, NULL); } static const struct sieve_match_type *sieve_match_type_create_instance (struct sieve_validator *valdtr, struct sieve_command *cmd, const char *identifier) { struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); struct sieve_object object; struct sieve_match_type *mcht; if ( !sieve_validator_object_registry_find(regs, identifier, &object) ) return NULL; mcht = p_new(sieve_command_pool(cmd), struct sieve_match_type, 1); mcht->object = object; mcht->def = (const struct sieve_match_type_def *) object.def; return mcht; } bool mtch_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr) { struct sieve_validator_object_registry *regs = sieve_validator_object_registry_init(valdtr, ext); unsigned int i; /* Register core match-types */ for ( i = 0; i < sieve_core_match_types_count; i++ ) { sieve_validator_object_registry_add (regs, NULL, &(sieve_core_match_types[i]->obj_def)); } return TRUE; } /* * Interpreter context */ struct mtch_interpreter_context { struct sieve_match_values *match_values; bool match_values_enabled; }; static void mtch_interpreter_free (const struct sieve_extension *ext ATTR_UNUSED, struct sieve_interpreter *interp ATTR_UNUSED, void *context) { struct mtch_interpreter_context *mctx = (struct mtch_interpreter_context *) context; if ( mctx->match_values != NULL ) { pool_unref(&mctx->match_values->pool); } } struct sieve_interpreter_extension mtch_interpreter_extension = { .ext_def = &match_type_extension, .free = mtch_interpreter_free }; static inline struct mtch_interpreter_context *get_interpreter_context (struct sieve_interpreter *interp, bool create) { struct sieve_instance *svinst; const struct sieve_extension *mcht_ext; struct mtch_interpreter_context *ctx; svinst = sieve_interpreter_svinst(interp); mcht_ext = sieve_get_match_type_extension(svinst); ctx = (struct mtch_interpreter_context *) sieve_interpreter_extension_get_context(interp, mcht_ext); if ( ctx == NULL && create ) { pool_t pool = sieve_interpreter_pool(interp); ctx = p_new(pool, struct mtch_interpreter_context, 1); sieve_interpreter_extension_register (interp, mcht_ext, &mtch_interpreter_extension, (void *) ctx); } return ctx; } /* * Match values */ bool sieve_match_values_set_enabled (const struct sieve_runtime_env *renv, bool enable) { struct mtch_interpreter_context *ctx = get_interpreter_context(renv->interp, enable); if ( ctx != NULL ) { bool previous = ctx->match_values_enabled; ctx->match_values_enabled = enable; return previous; } return FALSE; } bool sieve_match_values_are_enabled (const struct sieve_runtime_env *renv) { struct mtch_interpreter_context *ctx = get_interpreter_context(renv->interp, FALSE); return ( ctx == NULL ? FALSE : ctx->match_values_enabled ); } struct sieve_match_values *sieve_match_values_start (const struct sieve_runtime_env *renv) { struct mtch_interpreter_context *ctx = get_interpreter_context(renv->interp, FALSE); struct sieve_match_values *match_values; if ( ctx == NULL || !ctx->match_values_enabled ) return NULL; pool_t pool = pool_alloconly_create("sieve_match_values", 1024); match_values = p_new(pool, struct sieve_match_values, 1); match_values->pool = pool; match_values->count = 0; p_array_init(&match_values->values, pool, 4); return match_values; } static string_t *sieve_match_values_add_entry (struct sieve_match_values *mvalues) { string_t *entry; if ( mvalues == NULL ) return NULL; if ( mvalues->count >= SIEVE_MAX_MATCH_VALUES ) return NULL; if ( mvalues->count >= array_count(&mvalues->values) ) { entry = str_new(mvalues->pool, 64); array_append(&mvalues->values, &entry, 1); } else { string_t * const *ep = array_idx(&mvalues->values, mvalues->count); entry = *ep; str_truncate(entry, 0); } mvalues->count++; return entry; } void sieve_match_values_set (struct sieve_match_values *mvalues, unsigned int index, string_t *value) { if ( mvalues != NULL && index < array_count(&mvalues->values) ) { string_t * const *ep = array_idx(&mvalues->values, index); string_t *entry = *ep; if ( entry != NULL && value != NULL ) { str_truncate(entry, 0); str_append_str(entry, value); } } } void sieve_match_values_add (struct sieve_match_values *mvalues, string_t *value) { string_t *entry = sieve_match_values_add_entry(mvalues); if ( entry != NULL && value != NULL ) str_append_str(entry, value); } void sieve_match_values_add_char (struct sieve_match_values *mvalues, char c) { string_t *entry = sieve_match_values_add_entry(mvalues); if ( entry != NULL ) str_append_c(entry, c); } void sieve_match_values_skip (struct sieve_match_values *mvalues, int num) { int i; for ( i = 0; i < num; i++ ) (void) sieve_match_values_add_entry(mvalues); } void sieve_match_values_commit (const struct sieve_runtime_env *renv, struct sieve_match_values **mvalues) { struct mtch_interpreter_context *ctx; if ( (*mvalues) == NULL ) return; ctx = get_interpreter_context(renv->interp, FALSE); if ( ctx == NULL || !ctx->match_values_enabled ) return; if ( ctx->match_values != NULL ) { pool_unref(&ctx->match_values->pool); ctx->match_values = NULL; } ctx->match_values = *mvalues; *mvalues = NULL; } void sieve_match_values_abort (struct sieve_match_values **mvalues) { if ( (*mvalues) == NULL ) return; pool_unref(&(*mvalues)->pool); *mvalues = NULL; } void sieve_match_values_get (const struct sieve_runtime_env *renv, unsigned int index, string_t **value_r) { struct mtch_interpreter_context *ctx = get_interpreter_context(renv->interp, FALSE); struct sieve_match_values *mvalues; if ( ctx == NULL || ctx->match_values == NULL ) { *value_r = NULL; return; } mvalues = ctx->match_values; if ( index < array_count(&mvalues->values) && index < mvalues->count ) { string_t * const *entry = array_idx(&mvalues->values, index); *value_r = *entry; return; } *value_r = NULL; } /* * Match-type tagged argument */ /* Forward declarations */ static bool tag_match_type_is_instance_of (struct sieve_validator *valdtr, struct sieve_command *cmd, const struct sieve_extension *ext, const char *identifier, void **data); static bool tag_match_type_validate (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *cmd); static bool tag_match_type_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd); /* Argument object */ const struct sieve_argument_def match_type_tag = { .identifier = "MATCH-TYPE", .is_instance_of = tag_match_type_is_instance_of, .validate = tag_match_type_validate, .generate = tag_match_type_generate }; /* Argument implementation */ static bool tag_match_type_is_instance_of (struct sieve_validator *valdtr, struct sieve_command *cmd, const struct sieve_extension *ext ATTR_UNUSED, const char *identifier, void **data) { const struct sieve_match_type *mcht; if ( data == NULL ) return sieve_match_type_exists(valdtr, identifier); if ( (mcht=sieve_match_type_create_instance (valdtr, cmd, identifier)) == NULL ) return FALSE; *data = (void *) mcht; return TRUE; } static bool tag_match_type_validate (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *cmd ATTR_UNUSED) { const struct sieve_match_type *mcht = (const struct sieve_match_type *) (*arg)->argument->data; struct sieve_match_type_context *mtctx; mtctx = p_new(sieve_command_pool(cmd), struct sieve_match_type_context, 1); mtctx->match_type = mcht; mtctx->argument = *arg; mtctx->comparator = NULL; /* Can be filled in later */ (*arg)->argument->data = mtctx; /* Syntax: * ":is" / ":contains" / ":matches" (subject to extension) */ /* Skip tag */ *arg = sieve_ast_argument_next(*arg); /* Check whether this match type requires additional validation. * Additional validation can override the match type recorded in the context * for later code generation. */ if ( mcht->def != NULL && mcht->def->validate != NULL ) { return mcht->def->validate(valdtr, arg, mtctx); } return TRUE; } static bool tag_match_type_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd ATTR_UNUSED) { struct sieve_match_type_context *mtctx = (struct sieve_match_type_context *) arg->argument->data; (void) sieve_opr_match_type_emit(cgenv->sblock, mtctx->match_type); return TRUE; } void sieve_match_types_link_tags (struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg, int id_code) { struct sieve_instance *svinst; const struct sieve_extension *mcht_ext; svinst = sieve_validator_svinst(valdtr); mcht_ext = sieve_get_comparator_extension(svinst); sieve_validator_register_tag (valdtr, cmd_reg, mcht_ext, &match_type_tag, id_code); } /* * Validation */ bool sieve_match_type_validate (struct sieve_validator *valdtr, struct sieve_command *cmd, struct sieve_ast_argument *key_arg, const struct sieve_match_type *mcht_default, const struct sieve_comparator *cmp_default) { struct sieve_ast_argument *arg = sieve_command_first_argument(cmd); struct sieve_ast_argument *mt_arg = NULL; struct sieve_match_type_context *mtctx; const struct sieve_match_type *mcht = NULL; const struct sieve_comparator *cmp = NULL; /* Find match type and comparator among the arguments */ while ( arg != NULL && arg != cmd->first_positional ) { if ( sieve_argument_is_comparator(arg) ) { cmp = sieve_comparator_tag_get(arg); if ( mt_arg != NULL ) break; } if ( sieve_argument_is_match_type(arg) ) { mt_arg = arg; if ( cmp != NULL ) break; } arg = sieve_ast_argument_next(arg); } /* Verify using the default comparator if none is specified explicitly */ if ( cmp == NULL ) { cmp = sieve_comparator_copy(sieve_command_pool(cmd), cmp_default); } /* Verify the default match type if none is specified explicitly */ if ( mt_arg == NULL || mt_arg->argument == NULL || mt_arg->argument->data == NULL ) { mcht = sieve_match_type_copy(sieve_command_pool(cmd), mcht_default); mtctx = t_new(struct sieve_match_type_context, 1); mtctx->command = cmd; mtctx->match_type = mcht; } else { mtctx = (struct sieve_match_type_context *) mt_arg->argument->data; mcht = mtctx->match_type; } mtctx->comparator = cmp; /* Check whether this match type requires additional validation. * Additional validation can override the match type recorded in the context * for later code generation. */ if ( mcht != NULL && mcht->def != NULL && mcht->def->validate_context != NULL ) { return mcht->def->validate_context(valdtr, mt_arg, mtctx, key_arg); } return TRUE; } void sieve_match_type_arguments_remove (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd) { struct sieve_ast_argument *arg = sieve_command_first_argument(cmd); /* Remove any comparator and match type arguments */ while ( arg != NULL && arg != cmd->first_positional ) { if ( sieve_argument_is_comparator(arg) ) { arg = sieve_ast_arguments_detach(arg, 1); continue; } if ( sieve_argument_is_match_type(arg) ) { arg = sieve_ast_arguments_detach(arg, 1); continue; } arg = sieve_ast_argument_next(arg); } } /* * Match-type operand */ const struct sieve_operand_class sieve_match_type_operand_class = { "match type" }; static const struct sieve_extension_objects core_match_types = SIEVE_EXT_DEFINE_MATCH_TYPES(sieve_core_match_types); const struct sieve_operand_def match_type_operand = { .name = "match-type", .code = SIEVE_OPERAND_MATCH_TYPE, .class = &sieve_match_type_operand_class, .interface = &core_match_types }; /* * Common validation implementation */ bool sieve_match_substring_validate_context (struct sieve_validator *valdtr, struct sieve_ast_argument *arg, struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg ATTR_UNUSED) { const struct sieve_comparator *cmp = ctx->comparator; if ( cmp == NULL || cmp->def == NULL ) return TRUE; if ( (cmp->def->flags & SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH) == 0 ) { sieve_argument_validate_error(valdtr, arg, "the specified %s comparator does not support " "sub-string matching as required by the :%s match type", cmp->object.def->identifier, ctx->match_type->object.def->identifier ); return FALSE; } return TRUE; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-address.h0000644000000000000000000000306014103200126023234 0ustar00rootroot00000000000000#ifndef SIEVE_ADDRESS_H #define SIEVE_ADDRESS_H #include "lib.h" #include "strfuncs.h" #include "smtp-address.h" #include "sieve-common.h" #include "sieve-stringlist.h" /* * Address list API */ struct sieve_address_list { struct sieve_stringlist strlist; int (*next_item)(struct sieve_address_list *_addrlist, struct smtp_address *addr_r, string_t **unparsed_r); }; static inline int sieve_address_list_next_item(struct sieve_address_list *addrlist, struct smtp_address *addr_r, string_t **unparsed_r) { return addrlist->next_item(addrlist, addr_r, unparsed_r); } static inline void sieve_address_list_reset(struct sieve_address_list *addrlist) { sieve_stringlist_reset(&addrlist->strlist); } static inline int sieve_address_list_get_length(struct sieve_address_list *addrlist) { return sieve_stringlist_get_length(&addrlist->strlist); } static inline void sieve_address_list_set_trace(struct sieve_address_list *addrlist, bool trace) { sieve_stringlist_set_trace(&addrlist->strlist, trace); } /* * Header address list */ struct sieve_address_list * sieve_header_address_list_create(const struct sieve_runtime_env *renv, struct sieve_stringlist *field_values); /* * Sieve address parsing/validatin */ const struct smtp_address * sieve_address_parse(const char *address, const char **error_r); const struct smtp_address * sieve_address_parse_str(string_t *address, const char **error_r); bool sieve_address_validate(const char *address, const char **error_r); bool sieve_address_validate_str(string_t *address, const char **error_r); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-extensions.c0000644000000000000000000005746214103200126024020 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "mempool.h" #include "hash.h" #include "array.h" #include "sieve-common.h" #include "sieve-error.h" #include "sieve-settings.h" #include "sieve-extensions.h" /* * Forward declarations */ static void sieve_extension_registry_init(struct sieve_instance *svinst); static void sieve_extension_registry_deinit(struct sieve_instance *svinst); static void sieve_capability_registry_init(struct sieve_instance *svinst); static void sieve_capability_registry_deinit(struct sieve_instance *svinst); static struct sieve_extension *_sieve_extension_register (struct sieve_instance *svinst, const struct sieve_extension_def *extdef, bool load, bool required); /* * Instance global context */ struct sieve_extension_registry { ARRAY(struct sieve_extension *) extensions; HASH_TABLE(const char *, struct sieve_extension *) extension_index; HASH_TABLE(const char *, struct sieve_capability_registration *) capabilities_index; /* Core language 'extensions' */ const struct sieve_extension *comparator_extension; const struct sieve_extension *match_type_extension; const struct sieve_extension *address_part_extension; /* Preloaded extensions */ ARRAY(const struct sieve_extension *) preloaded_extensions; }; /* * Pre-loaded 'extensions' */ extern const struct sieve_extension_def comparator_extension; extern const struct sieve_extension_def match_type_extension; extern const struct sieve_extension_def address_part_extension; /* * Dummy extensions */ /* FIXME: This is stupid. Define a comparator-* extension and be done with it */ const struct sieve_extension_def comparator_i_octet_extension = { .name = "comparator-i;octet", }; const struct sieve_extension_def comparator_i_ascii_casemap_extension = { .name = "comparator-i;ascii-casemap", }; /* * List of native extensions */ /* Dummy extensions */ extern const struct sieve_extension_def comparator_i_octet_extension; extern const struct sieve_extension_def comparator_i_ascii_casemap_extension; const struct sieve_extension_def *sieve_dummy_extensions[] = { &comparator_i_octet_extension, &comparator_i_ascii_casemap_extension }; const unsigned int sieve_dummy_extensions_count = N_ELEMENTS(sieve_dummy_extensions); /* Core */ extern const struct sieve_extension_def fileinto_extension; extern const struct sieve_extension_def reject_extension; extern const struct sieve_extension_def envelope_extension; extern const struct sieve_extension_def encoded_character_extension; extern const struct sieve_extension_def vacation_extension; extern const struct sieve_extension_def subaddress_extension; extern const struct sieve_extension_def comparator_i_ascii_numeric_extension; extern const struct sieve_extension_def relational_extension; extern const struct sieve_extension_def regex_extension; extern const struct sieve_extension_def imap4flags_extension; extern const struct sieve_extension_def copy_extension; extern const struct sieve_extension_def include_extension; extern const struct sieve_extension_def body_extension; extern const struct sieve_extension_def variables_extension; extern const struct sieve_extension_def enotify_extension; extern const struct sieve_extension_def environment_extension; extern const struct sieve_extension_def mailbox_extension; extern const struct sieve_extension_def date_extension; extern const struct sieve_extension_def index_extension; extern const struct sieve_extension_def ihave_extension; extern const struct sieve_extension_def duplicate_extension; extern const struct sieve_extension_def mime_extension; extern const struct sieve_extension_def foreverypart_extension; extern const struct sieve_extension_def extracttext_extension; extern const struct sieve_extension_def mboxmetadata_extension; extern const struct sieve_extension_def servermetadata_extension; const struct sieve_extension_def *sieve_core_extensions[] = { /* Core extensions */ &fileinto_extension, &reject_extension, &envelope_extension, &encoded_character_extension, /* 'Plugins' */ &vacation_extension, &subaddress_extension, &comparator_i_ascii_numeric_extension, &relational_extension, ®ex_extension, &imap4flags_extension, ©_extension, &include_extension, &body_extension, &variables_extension, &enotify_extension, &environment_extension, &mailbox_extension, &date_extension, &index_extension, &ihave_extension, &duplicate_extension, &mime_extension, &foreverypart_extension, &extracttext_extension }; const unsigned int sieve_core_extensions_count = N_ELEMENTS(sieve_core_extensions); /* Extra; * These are not enabled by default, e.g. because explicit configuration is * necessary to make these useful. */ extern const struct sieve_extension_def vacation_seconds_extension; extern const struct sieve_extension_def spamtest_extension; extern const struct sieve_extension_def spamtestplus_extension; extern const struct sieve_extension_def virustest_extension; extern const struct sieve_extension_def editheader_extension; extern const struct sieve_extension_def special_use_extension; extern const struct sieve_extension_def vnd_debug_extension; extern const struct sieve_extension_def vnd_environment_extension; extern const struct sieve_extension_def vnd_report_extension; const struct sieve_extension_def *sieve_extra_extensions[] = { &vacation_seconds_extension, &spamtest_extension, &spamtestplus_extension, &virustest_extension, &editheader_extension, &mboxmetadata_extension, &servermetadata_extension, &special_use_extension, /* vnd.dovecot. */ &vnd_debug_extension, &vnd_environment_extension, &vnd_report_extension }; const unsigned int sieve_extra_extensions_count = N_ELEMENTS(sieve_extra_extensions); /* * Deprecated extensions */ extern const struct sieve_extension_def imapflags_extension; extern const struct sieve_extension_def notify_extension; extern const struct sieve_extension_def vnd_duplicate_extension; const struct sieve_extension_def *sieve_deprecated_extensions[] = { &imapflags_extension, ¬ify_extension, &vnd_duplicate_extension }; const unsigned int sieve_deprecated_extensions_count = N_ELEMENTS(sieve_deprecated_extensions); /* * Unfinished extensions */ #ifdef HAVE_SIEVE_UNFINISHED extern const struct sieve_extension_def ereject_extension; const struct sieve_extension_def *sieve_unfinished_extensions[] = { &ereject_extension }; const unsigned int sieve_unfinished_extensions_count = N_ELEMENTS(sieve_unfinished_extensions); #endif /* HAVE_SIEVE_UNFINISHED */ /* * Extensions init/deinit */ bool sieve_extensions_init(struct sieve_instance *svinst) { unsigned int i; struct sieve_extension_registry *ext_reg = p_new(svinst->pool, struct sieve_extension_registry, 1); struct sieve_extension *ext; svinst->ext_reg = ext_reg; sieve_extension_registry_init(svinst); sieve_capability_registry_init(svinst); /* Preloaded 'extensions' */ ext_reg->comparator_extension = sieve_extension_register(svinst, &comparator_extension, TRUE); ext_reg->match_type_extension = sieve_extension_register(svinst, &match_type_extension, TRUE); ext_reg->address_part_extension = sieve_extension_register(svinst, &address_part_extension, TRUE); p_array_init(&ext_reg->preloaded_extensions, svinst->pool, 5); array_append(&ext_reg->preloaded_extensions, &ext_reg->comparator_extension, 1); array_append(&ext_reg->preloaded_extensions, &ext_reg->match_type_extension, 1); array_append(&ext_reg->preloaded_extensions, &ext_reg->address_part_extension, 1); /* Pre-load dummy extensions */ for ( i = 0; i < sieve_dummy_extensions_count; i++ ) { if ( (ext=_sieve_extension_register (svinst, sieve_dummy_extensions[i], TRUE, FALSE)) == NULL ) return FALSE; ext->dummy = TRUE; } /* Pre-load core extensions */ for ( i = 0; i < sieve_core_extensions_count; i++ ) { if ( sieve_extension_register (svinst, sieve_core_extensions[i], TRUE) == NULL ) return FALSE; } /* Pre-load extra extensions */ for ( i = 0; i < sieve_extra_extensions_count; i++ ) { if ( sieve_extension_register (svinst, sieve_extra_extensions[i], FALSE) == NULL ) return FALSE; } /* Register deprecated extensions */ for ( i = 0; i < sieve_deprecated_extensions_count; i++ ) { if ( sieve_extension_register (svinst, sieve_deprecated_extensions[i], FALSE) == NULL ) return FALSE; } #ifdef HAVE_SIEVE_UNFINISHED /* Register unfinished extensions */ for ( i = 0; i < sieve_unfinished_extensions_count; i++ ) { if ( sieve_extension_register (svinst, sieve_unfinished_extensions[i], FALSE) == NULL ) return FALSE; } #endif /* More extensions can be added through plugins */ return TRUE; } void sieve_extensions_configure(struct sieve_instance *svinst) { const char *extensions; /* Apply sieve_extensions configuration */ if ( (extensions=sieve_setting_get (svinst, "sieve_extensions")) != NULL ) sieve_extensions_set_string(svinst, extensions, FALSE, FALSE); /* Apply sieve_global_extensions configuration */ if ( (extensions=sieve_setting_get (svinst, "sieve_global_extensions")) != NULL ) sieve_extensions_set_string(svinst, extensions, TRUE, FALSE); /* Apply sieve_implicit_extensions configuration */ if ( (extensions=sieve_setting_get (svinst, "sieve_implicit_extensions")) != NULL ) sieve_extensions_set_string(svinst, extensions, FALSE, TRUE); } void sieve_extensions_deinit(struct sieve_instance *svinst) { sieve_extension_registry_deinit(svinst); sieve_capability_registry_deinit(svinst); } /* * Pre-loaded extensions */ const struct sieve_extension *const *sieve_extensions_get_preloaded (struct sieve_instance *svinst, unsigned int *count_r) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; return array_get(&ext_reg->preloaded_extensions, count_r); } /* * Extension registry */ static bool _sieve_extension_load(struct sieve_extension *ext) { /* Call load handler */ if ( ext->def != NULL && ext->def->load != NULL && !ext->def->load(ext, &ext->context) ) { e_error(ext->svinst->event, "failed to load '%s' extension support.", ext->def->name); return FALSE; } return TRUE; } static void _sieve_extension_unload(struct sieve_extension *ext) { /* Call unload handler */ if ( ext->def != NULL && ext->def->unload != NULL ) ext->def->unload(ext); ext->context = NULL; } static void sieve_extension_registry_init(struct sieve_instance *svinst) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; p_array_init(&ext_reg->extensions, svinst->pool, 50); hash_table_create (&ext_reg->extension_index, default_pool, 0, str_hash, strcmp); } static void sieve_extension_registry_deinit(struct sieve_instance *svinst) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; struct sieve_extension * const *exts; unsigned int i, ext_count; if ( !hash_table_is_created(ext_reg->extension_index) ) return; exts = array_get_modifiable(&ext_reg->extensions, &ext_count); for ( i = 0; i < ext_count; i++ ) { _sieve_extension_unload(exts[i]); } hash_table_destroy(&ext_reg->extension_index); } bool sieve_extension_reload(const struct sieve_extension *ext) { struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; struct sieve_extension * const *mod_ext; int ext_id = ext->id; /* Let's not just cast the 'const' away */ if ( ext_id >= 0 && ext_id < (int) array_count(&ext_reg->extensions) ) { mod_ext = array_idx(&ext_reg->extensions, ext_id); return _sieve_extension_load(*mod_ext); } return FALSE; } static struct sieve_extension *sieve_extension_lookup (struct sieve_instance *svinst, const char *name) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; return hash_table_lookup(ext_reg->extension_index, name); } static struct sieve_extension *sieve_extension_alloc (struct sieve_instance *svinst, const struct sieve_extension_def *extdef) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; struct sieve_extension *ext, **extr; int ext_id; ext_id = (int)array_count(&ext_reg->extensions); /* Add extension to the registry */ extr = array_append_space(&ext_reg->extensions); *extr = ext = p_new(svinst->pool, struct sieve_extension, 1); ext->id = ext_id; ext->def = extdef; ext->svinst = svinst; return ext; } static struct sieve_extension *_sieve_extension_register (struct sieve_instance *svinst, const struct sieve_extension_def *extdef, bool load, bool required) { struct sieve_extension *ext; ext = sieve_extension_lookup(svinst, extdef->name); /* Register extension if it is not registered already */ if ( ext == NULL ) { ext = sieve_extension_alloc(svinst, extdef); hash_table_insert (svinst->ext_reg->extension_index, extdef->name, ext); } else if ( ext->overridden ) { /* Create a dummy */ ext = sieve_extension_alloc(svinst, extdef); } else { /* Re-register it if it were previously unregistered * (not going to happen) */ i_assert( ext->def == NULL || ext->def == extdef ); ext->def = extdef; } /* Enable extension */ if ( load || required ) { ext->enabled = ( ext->enabled || load ); /* Call load handler if extension was not loaded already */ if ( !ext->loaded ) { if ( !_sieve_extension_load(ext) ) return NULL; } ext->loaded = TRUE; } ext->required = ( ext->required || required ); return ext; } const struct sieve_extension *sieve_extension_register (struct sieve_instance *svinst, const struct sieve_extension_def *extdef, bool load) { return _sieve_extension_register(svinst, extdef, load, FALSE); } void sieve_extension_unregister(const struct sieve_extension *ext) { struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; struct sieve_extension * const *mod_ext; int ext_id = ext->id; if ( ext_id >= 0 && ext_id < (int) array_count(&ext_reg->extensions) ) { mod_ext = array_idx(&ext_reg->extensions, ext_id); sieve_extension_capabilities_unregister(*mod_ext); _sieve_extension_unload(*mod_ext); (*mod_ext)->loaded = FALSE; (*mod_ext)->enabled = FALSE; (*mod_ext)->def = NULL; } } const struct sieve_extension *sieve_extension_replace (struct sieve_instance *svinst, const struct sieve_extension_def *extdef, bool load) { struct sieve_extension *ext; ext = sieve_extension_lookup(svinst, extdef->name); if (ext != NULL) sieve_extension_unregister(ext); return sieve_extension_register(svinst, extdef, load); } const struct sieve_extension *sieve_extension_require (struct sieve_instance *svinst, const struct sieve_extension_def *extdef, bool load) { return _sieve_extension_register(svinst, extdef, load, TRUE); } void sieve_extension_override (struct sieve_instance *svinst, const char *name, const struct sieve_extension *ext) { struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; struct sieve_extension * const *mod_ext; struct sieve_extension *old_ext; old_ext = sieve_extension_lookup(svinst, name); if (old_ext == ext) return; i_assert( old_ext == NULL || !old_ext->overridden ); i_assert( ext->id >= 0 && ext->id < (int) array_count(&ext_reg->extensions) ); mod_ext = array_idx(&ext_reg->extensions, ext->id); hash_table_update (ext_reg->extension_index, name, *mod_ext); if ( old_ext != NULL ) old_ext->overridden = TRUE; } unsigned int sieve_extensions_get_count(struct sieve_instance *svinst) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; return array_count(&ext_reg->extensions); } const struct sieve_extension *const * sieve_extensions_get_all(struct sieve_instance *svinst, unsigned int *count_r) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; return (const struct sieve_extension *const *) array_get(&ext_reg->extensions, count_r); } const struct sieve_extension *sieve_extension_get_by_id (struct sieve_instance *svinst, unsigned int ext_id) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; struct sieve_extension * const *ext; if ( ext_id < array_count(&ext_reg->extensions) ) { ext = array_idx(&ext_reg->extensions, ext_id); if ( (*ext)->def != NULL && ((*ext)->enabled || (*ext)->required) ) return *ext; } return NULL; } const struct sieve_extension *sieve_extension_get_by_name (struct sieve_instance *svinst, const char *name) { const struct sieve_extension *ext; if ( *name == '@' ) return NULL; if ( strlen(name) > 128 ) return NULL; ext = sieve_extension_lookup(svinst, name); if ( ext == NULL || ext->def == NULL || (!ext->enabled && !ext->required)) return NULL; return ext; } static inline bool _sieve_extension_listable(const struct sieve_extension *ext) { return ( ext->enabled && ext->def != NULL && *(ext->def->name) != '@' && !ext->dummy && !ext->global && !ext->overridden); } const char *sieve_extensions_get_string(struct sieve_instance *svinst) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; string_t *extstr = t_str_new(256); struct sieve_extension * const *exts; unsigned int i, ext_count; exts = array_get(&ext_reg->extensions, &ext_count); if ( ext_count > 0 ) { i = 0; /* Find first listable extension */ while ( i < ext_count && !_sieve_extension_listable(exts[i]) ) i++; if ( i < ext_count ) { /* Add first to string */ str_append(extstr, exts[i]->def->name); i++; /* Add others */ for ( ; i < ext_count; i++ ) { if ( _sieve_extension_listable(exts[i]) ) { str_append_c(extstr, ' '); str_append(extstr, exts[i]->def->name); } } } } return str_c(extstr); } static void sieve_extension_set_enabled (struct sieve_extension *ext, bool enabled) { if ( enabled ) { ext->enabled = TRUE; if ( !ext->loaded ) { (void)_sieve_extension_load(ext); } ext->loaded = TRUE; } else { ext->enabled = FALSE; } } static void sieve_extension_set_global (struct sieve_extension *ext, bool enabled) { if ( enabled ) { sieve_extension_set_enabled(ext, TRUE); ext->global = TRUE; } else { ext->global = FALSE; } } static void sieve_extension_set_implicit (struct sieve_extension *ext, bool enabled) { if ( enabled ) { sieve_extension_set_enabled(ext, TRUE); ext->implicit = TRUE; } else { ext->implicit = FALSE; } } void sieve_extensions_set_string (struct sieve_instance *svinst, const char *ext_string, bool global, bool implicit) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; ARRAY(const struct sieve_extension *) enabled_extensions; ARRAY(const struct sieve_extension *) disabled_extensions; const struct sieve_extension *const *ext_enabled; const struct sieve_extension *const *ext_disabled; struct sieve_extension **exts; const char **ext_names; unsigned int i, ext_count, ena_count, dis_count; bool relative = FALSE; if ( ext_string == NULL ) { if ( global || implicit ) return; /* Enable all */ exts = array_get_modifiable(&ext_reg->extensions, &ext_count); for ( i = 0; i < ext_count; i++ ) sieve_extension_set_enabled(exts[i], TRUE); return; } T_BEGIN { t_array_init(&enabled_extensions, array_count(&ext_reg->extensions)); t_array_init(&disabled_extensions, array_count(&ext_reg->extensions)); ext_names = t_strsplit_spaces(ext_string, " \t"); while ( *ext_names != NULL ) { const char *name = *ext_names; ext_names++; if ( *name != '\0' ) { const struct sieve_extension *ext; char op = '\0'; /* No add/remove operation */ if ( *name == '+' /* Add to existing config */ || *name == '-' ) { /* Remove from existing config */ op = *name++; relative = TRUE; } if ( *name == '@' ) ext = NULL; else ext = hash_table_lookup(ext_reg->extension_index, name); if ( ext == NULL || ext->def == NULL ) { e_warning(svinst->event, "ignored unknown extension '%s' while configuring " "available extensions", name); continue; } if ( op == '-' ) array_append(&disabled_extensions, &ext, 1); else array_append(&enabled_extensions, &ext, 1); } } exts = array_get_modifiable(&ext_reg->extensions, &ext_count); ext_enabled = array_get(&enabled_extensions, &ena_count); ext_disabled = array_get(&disabled_extensions, &dis_count); /* Set new extension status */ for ( i = 0; i < ext_count; i++ ) { unsigned int j; bool enabled = FALSE; if ( exts[i]->id < 0 || exts[i]->def == NULL || *(exts[i]->def->name) == '@' ) { continue; } /* If extensions are specified relative to the default set, * we first need to check which ones are disabled */ if ( relative ) { if ( global ) enabled = exts[i]->global; else if ( implicit ) enabled = exts[i]->implicit; else enabled = exts[i]->enabled; if ( enabled ) { /* Disable if explicitly disabled */ for ( j = 0; j < dis_count; j++ ) { if ( ext_disabled[j]->def == exts[i]->def ) { enabled = FALSE; break; } } } } /* Enable if listed with '+' or no prefix */ for ( j = 0; j < ena_count; j++ ) { if ( ext_enabled[j]->def == exts[i]->def ) { enabled = TRUE; break; } } /* Perform actual activation/deactivation */ if ( global ) { sieve_extension_set_global(exts[i], enabled); } else if ( implicit ) { sieve_extension_set_implicit(exts[i], enabled); } else { sieve_extension_set_enabled(exts[i], enabled); } } } T_END; } const struct sieve_extension *sieve_get_match_type_extension (struct sieve_instance *svinst) { return svinst->ext_reg->match_type_extension; } const struct sieve_extension *sieve_get_comparator_extension (struct sieve_instance *svinst) { return svinst->ext_reg->comparator_extension; } const struct sieve_extension *sieve_get_address_part_extension (struct sieve_instance *svinst) { return svinst->ext_reg->address_part_extension; } void sieve_enable_debug_extension(struct sieve_instance *svinst) { (void) sieve_extension_register(svinst, &vnd_debug_extension, TRUE); } /* * Extension capabilities */ struct sieve_capability_registration { const struct sieve_extension *ext; const struct sieve_extension_capabilities *capabilities; }; void sieve_capability_registry_init(struct sieve_instance *svinst) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; hash_table_create (&ext_reg->capabilities_index, default_pool, 0, str_hash, strcmp); } void sieve_capability_registry_deinit(struct sieve_instance *svinst) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; if ( !hash_table_is_created(ext_reg->capabilities_index) ) return; hash_table_destroy(&svinst->ext_reg->capabilities_index); } void sieve_extension_capabilities_register (const struct sieve_extension *ext, const struct sieve_extension_capabilities *cap) { struct sieve_instance *svinst = ext->svinst; struct sieve_extension_registry *ext_reg = svinst->ext_reg; struct sieve_capability_registration *reg; reg = hash_table_lookup(ext_reg->capabilities_index, cap->name); if (reg != NULL) { /* Already registered */ return; } reg = p_new(svinst->pool, struct sieve_capability_registration, 1); reg->ext = ext; reg->capabilities = cap; hash_table_insert(ext_reg->capabilities_index, cap->name, reg); } void sieve_extension_capabilities_unregister (const struct sieve_extension *ext) { struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; struct hash_iterate_context *hictx; const char *name; struct sieve_capability_registration *reg; hictx = hash_table_iterate_init(ext_reg->capabilities_index); while ( hash_table_iterate(hictx, ext_reg->capabilities_index, &name, ®) ) { if ( reg->ext == ext ) hash_table_remove(ext_reg->capabilities_index, name); } hash_table_iterate_deinit(&hictx); } const char *sieve_extension_capabilities_get_string (struct sieve_instance *svinst, const char *cap_name) { struct sieve_extension_registry *ext_reg = svinst->ext_reg; const struct sieve_capability_registration *cap_reg = hash_table_lookup(ext_reg->capabilities_index, cap_name); const struct sieve_extension_capabilities *cap; if ( cap_reg == NULL || cap_reg->capabilities == NULL ) return NULL; cap = cap_reg->capabilities; if ( cap->get_string == NULL || !cap_reg->ext->enabled ) return NULL; return cap->get_string(cap_reg->ext); } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-address-parts.c0000644000000000000000000003155214103200126024365 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "compat.h" #include "mempool.h" #include "hash.h" #include "array.h" #include "str-sanitize.h" #include "sieve-extensions.h" #include "sieve-code.h" #include "sieve-address.h" #include "sieve-commands.h" #include "sieve-binary.h" #include "sieve-comparators.h" #include "sieve-match-types.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-interpreter.h" #include "sieve-dump.h" #include "sieve-match.h" #include "sieve-address-parts.h" #include /* * Default address parts */ const struct sieve_address_part_def *sieve_core_address_parts[] = { &all_address_part, &local_address_part, &domain_address_part }; const unsigned int sieve_core_address_parts_count = N_ELEMENTS(sieve_core_address_parts); /* * Address-part 'extension' */ static bool addrp_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def address_part_extension = { .name = "@address-parts", .validator_load = addrp_validator_load }; /* * Validator context: * name-based address-part registry. */ static struct sieve_validator_object_registry *_get_object_registry (struct sieve_validator *valdtr) { struct sieve_instance *svinst; const struct sieve_extension *adrp_ext; svinst = sieve_validator_svinst(valdtr); adrp_ext = sieve_get_address_part_extension(svinst); return sieve_validator_object_registry_get(valdtr, adrp_ext); } void sieve_address_part_register (struct sieve_validator *valdtr, const struct sieve_extension *ext, const struct sieve_address_part_def *addrp_def) { struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); sieve_validator_object_registry_add(regs, ext, &addrp_def->obj_def); } static bool sieve_address_part_exists (struct sieve_validator *valdtr, const char *identifier) { struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); return sieve_validator_object_registry_find(regs, identifier, NULL); } static const struct sieve_address_part *sieve_address_part_create_instance (struct sieve_validator *valdtr, struct sieve_command *cmd, const char *identifier) { struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); struct sieve_object object; struct sieve_address_part *addrp; if ( !sieve_validator_object_registry_find(regs, identifier, &object) ) return NULL; addrp = p_new(sieve_command_pool(cmd), struct sieve_address_part, 1); addrp->object = object; addrp->def = (const struct sieve_address_part_def *) object.def; return addrp; } static bool addrp_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr) { struct sieve_validator_object_registry *regs = sieve_validator_object_registry_init(valdtr, ext); unsigned int i; /* Register core address-parts */ for ( i = 0; i < sieve_core_address_parts_count; i++ ) { sieve_validator_object_registry_add (regs, NULL, &(sieve_core_address_parts[i]->obj_def)); } return TRUE; } void sieve_address_parts_link_tags (struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg, int id_code) { struct sieve_instance *svinst; const struct sieve_extension *adrp_ext; svinst = sieve_validator_svinst(valdtr); adrp_ext = sieve_get_address_part_extension(svinst); sieve_validator_register_tag (valdtr, cmd_reg, adrp_ext, &address_part_tag, id_code); } /* * Address-part tagged argument */ /* Forward declarations */ static bool tag_address_part_is_instance_of (struct sieve_validator *valdtr, struct sieve_command *cmd, const struct sieve_extension *ext, const char *identifier, void **data); static bool tag_address_part_validate (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *cmd); static bool tag_address_part_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd); /* Argument object */ const struct sieve_argument_def address_part_tag = { .identifier = "ADDRESS-PART", .is_instance_of = tag_address_part_is_instance_of, .validate = tag_address_part_validate, .generate = tag_address_part_generate }; /* Argument implementation */ static bool tag_address_part_is_instance_of (struct sieve_validator *valdtr, struct sieve_command *cmd, const struct sieve_extension *ext ATTR_UNUSED, const char *identifier, void **data) { const struct sieve_address_part *addrp; if ( data == NULL ) return sieve_address_part_exists(valdtr, identifier); if ( (addrp=sieve_address_part_create_instance (valdtr, cmd, identifier)) == NULL ) return FALSE; *data = (void *) addrp; return TRUE; } static bool tag_address_part_validate (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg, struct sieve_command *cmd ATTR_UNUSED) { /* NOTE: Currenly trivial, but might need to allow for further validation for * future extensions. */ /* Syntax: * ":localpart" / ":domain" / ":all" (subject to extension) */ /* Skip tag */ *arg = sieve_ast_argument_next(*arg); return TRUE; } static bool tag_address_part_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd ATTR_UNUSED) { struct sieve_address_part *addrp = (struct sieve_address_part *) arg->argument->data; sieve_opr_address_part_emit(cgenv->sblock, addrp); return TRUE; } /* * Address-part operand */ const struct sieve_operand_class sieve_address_part_operand_class = { "address part" }; static const struct sieve_extension_objects core_address_parts = SIEVE_EXT_DEFINE_MATCH_TYPES(sieve_core_address_parts); const struct sieve_operand_def address_part_operand = { .name = "address-part", .code = SIEVE_OPERAND_ADDRESS_PART, .class = &sieve_address_part_operand_class, .interface = &core_address_parts }; /* * Address-part string list */ static int sieve_address_part_stringlist_next_item (struct sieve_stringlist *_strlist, string_t **str_r); static void sieve_address_part_stringlist_reset (struct sieve_stringlist *_strlist); static int sieve_address_part_stringlist_get_length (struct sieve_stringlist *_strlist); static void sieve_address_part_stringlist_set_trace (struct sieve_stringlist *_strlist, bool trace); struct sieve_address_part_stringlist { struct sieve_stringlist strlist; const struct sieve_address_part *addrp; struct sieve_address_list *addresses; }; struct sieve_stringlist *sieve_address_part_stringlist_create (const struct sieve_runtime_env *renv, const struct sieve_address_part *addrp, struct sieve_address_list *addresses) { struct sieve_address_part_stringlist *strlist; strlist = t_new(struct sieve_address_part_stringlist, 1); strlist->strlist.runenv = renv; strlist->strlist.next_item = sieve_address_part_stringlist_next_item; strlist->strlist.reset = sieve_address_part_stringlist_reset; strlist->strlist.get_length = sieve_address_part_stringlist_get_length; strlist->strlist.set_trace = sieve_address_part_stringlist_set_trace; strlist->addrp = addrp; strlist->addresses = addresses; return &strlist->strlist; } static int sieve_address_part_stringlist_next_item (struct sieve_stringlist *_strlist, string_t **str_r) { struct sieve_address_part_stringlist *strlist = (struct sieve_address_part_stringlist *)_strlist; struct smtp_address item; string_t *item_unparsed; int ret; *str_r = NULL; while ( *str_r == NULL ) { if ( (ret=sieve_address_list_next_item (strlist->addresses, &item, &item_unparsed)) <= 0 ) return ret; if ( item.localpart == NULL ) { if ( item_unparsed != NULL ) { if ( _strlist->trace ) { sieve_runtime_trace(_strlist->runenv, 0, "extracting `%s' part from non-address value `%s'", sieve_address_part_name(strlist->addrp), str_sanitize(str_c(item_unparsed), 80)); } if ( str_len(item_unparsed) == 0 || sieve_address_part_is(strlist->addrp, all_address_part) ) *str_r = item_unparsed; } } else { const struct sieve_address_part *addrp = strlist->addrp; const char *part = NULL; if ( _strlist->trace ) { sieve_runtime_trace(_strlist->runenv, 0, "extracting `%s' part from address %s", sieve_address_part_name(strlist->addrp), smtp_address_encode_path(&item)); } if ( addrp->def != NULL && addrp->def->extract_from != NULL ) part = addrp->def->extract_from(addrp, &item); if ( part != NULL ) *str_r = t_str_new_const(part, strlen(part)); } } return 1; } static void sieve_address_part_stringlist_reset (struct sieve_stringlist *_strlist) { struct sieve_address_part_stringlist *strlist = (struct sieve_address_part_stringlist *)_strlist; sieve_address_list_reset(strlist->addresses); } static int sieve_address_part_stringlist_get_length (struct sieve_stringlist *_strlist) { struct sieve_address_part_stringlist *strlist = (struct sieve_address_part_stringlist *)_strlist; return sieve_address_list_get_length(strlist->addresses); } static void sieve_address_part_stringlist_set_trace (struct sieve_stringlist *_strlist, bool trace) { struct sieve_address_part_stringlist *strlist = (struct sieve_address_part_stringlist *)_strlist; sieve_address_list_set_trace(strlist->addresses, trace); } /* * Default ADDRESS-PART, MATCH-TYPE, COMPARATOR access */ int sieve_addrmatch_opr_optional_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, signed int *opt_code) { signed int _opt_code = 0; bool final = FALSE, opok = TRUE; if ( opt_code == NULL ) { opt_code = &_opt_code; final = TRUE; } while ( opok ) { int opt; if ( (opt=sieve_opr_optional_dump(denv, address, opt_code)) <= 0 ) return opt; switch ( *opt_code ) { case SIEVE_MATCH_OPT_COMPARATOR: opok = sieve_opr_comparator_dump(denv, address); break; case SIEVE_MATCH_OPT_MATCH_TYPE: opok = sieve_opr_match_type_dump(denv, address); break; case SIEVE_AM_OPT_ADDRESS_PART: opok = sieve_opr_address_part_dump(denv, address); break; default: return ( final ? -1 : 1 ); } } return -1; } int sieve_addrmatch_opr_optional_read (const struct sieve_runtime_env *renv, sieve_size_t *address, signed int *opt_code, int *exec_status, struct sieve_address_part *addrp, struct sieve_match_type *mtch, struct sieve_comparator *cmp) { signed int _opt_code = 0; bool final = FALSE; int status = SIEVE_EXEC_OK; if ( opt_code == NULL ) { opt_code = &_opt_code; final = TRUE; } if ( exec_status != NULL ) *exec_status = SIEVE_EXEC_OK; while ( status == SIEVE_EXEC_OK ) { int opt; if ( (opt=sieve_opr_optional_read(renv, address, opt_code)) <= 0 ){ if ( opt < 0 && exec_status != NULL ) *exec_status = SIEVE_EXEC_BIN_CORRUPT; return opt; } switch ( *opt_code ) { case SIEVE_MATCH_OPT_COMPARATOR: if (cmp == NULL) { sieve_runtime_trace_error(renv, "unexpected comparator operand"); if ( exec_status != NULL ) *exec_status = SIEVE_EXEC_BIN_CORRUPT; return -1; } status = sieve_opr_comparator_read(renv, address, cmp); break; case SIEVE_MATCH_OPT_MATCH_TYPE: if (mtch == NULL) { sieve_runtime_trace_error(renv, "unexpected match-type operand"); if ( exec_status != NULL ) *exec_status = SIEVE_EXEC_BIN_CORRUPT; return -1; } status = sieve_opr_match_type_read(renv, address, mtch); break; case SIEVE_AM_OPT_ADDRESS_PART: if (addrp == NULL) { sieve_runtime_trace_error(renv, "unexpected address-part operand"); if ( exec_status != NULL ) *exec_status = SIEVE_EXEC_BIN_CORRUPT; return -1; } status = sieve_opr_address_part_read(renv, address, addrp); break; default: if ( final ) { sieve_runtime_trace_error(renv, "invalid optional operand"); if ( exec_status != NULL ) *exec_status = SIEVE_EXEC_BIN_CORRUPT; return -1; } return 1; } } if ( exec_status != NULL ) *exec_status = status; return -1; } /* * Core address-part modifiers */ static const char *addrp_all_extract_from (const struct sieve_address_part *addrp ATTR_UNUSED, const struct smtp_address *address) { if ( address->localpart == NULL ) return NULL; return smtp_address_encode(address); } static const char *addrp_domain_extract_from (const struct sieve_address_part *addrp ATTR_UNUSED, const struct smtp_address *address) { return address->domain; } static const char *addrp_localpart_extract_from (const struct sieve_address_part *addrp ATTR_UNUSED, const struct smtp_address *address) { return address->localpart; } const struct sieve_address_part_def all_address_part = { SIEVE_OBJECT("all", &address_part_operand, SIEVE_ADDRESS_PART_ALL), .extract_from = addrp_all_extract_from }; const struct sieve_address_part_def local_address_part = { SIEVE_OBJECT("localpart", &address_part_operand, SIEVE_ADDRESS_PART_LOCAL), .extract_from = addrp_localpart_extract_from }; const struct sieve_address_part_def domain_address_part = { SIEVE_OBJECT("domain", &address_part_operand, SIEVE_ADDRESS_PART_DOMAIN), .extract_from = addrp_domain_extract_from }; dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-match-types.h0000644000000000000000000001364514103200126024057 0ustar00rootroot00000000000000#ifndef SIEVE_MATCH_TYPES_H #define SIEVE_MATCH_TYPES_H #include "sieve-common.h" #include "sieve-extensions.h" #include "sieve-commands.h" #include "sieve-code.h" #include "sieve-objects.h" /* * Types */ struct sieve_match_type_context; /* * Core match types */ enum sieve_match_type_code { SIEVE_MATCH_TYPE_IS, SIEVE_MATCH_TYPE_CONTAINS, SIEVE_MATCH_TYPE_MATCHES, SIEVE_MATCH_TYPE_CUSTOM }; extern const struct sieve_match_type_def is_match_type; extern const struct sieve_match_type_def contains_match_type; extern const struct sieve_match_type_def matches_match_type; /* * Match type definition */ struct sieve_match_type_def { struct sieve_object_def obj_def; bool (*validate) (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_match_type_context *ctx); bool (*validate_context) (struct sieve_validator *valdtr, struct sieve_ast_argument *arg, struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg); /* * Matching */ /* Custom implementation */ int (*match) (struct sieve_match_context *mctx, struct sieve_stringlist *value_list, struct sieve_stringlist *key_list); /* Default match loop */ void (*match_init)(struct sieve_match_context *mctx); int (*match_keys) (struct sieve_match_context *mctx, const char *val, size_t val_size, struct sieve_stringlist *key_list); int (*match_key) (struct sieve_match_context *mctx, const char *val, size_t val_size, const char *key, size_t key_size); void (*match_deinit)(struct sieve_match_context *mctx); }; /* * Match type instance */ struct sieve_match_type { struct sieve_object object; const struct sieve_match_type_def *def; }; #define SIEVE_MATCH_TYPE_DEFAULT(definition) \ { SIEVE_OBJECT_DEFAULT(definition), &(definition) } #define sieve_match_type_name(mcht) \ ( (mcht)->object.def->identifier ) #define sieve_match_type_is(mcht, definition) \ ( (mcht)->def == &(definition) ) static inline const struct sieve_match_type *sieve_match_type_copy (pool_t pool, const struct sieve_match_type *cmp_orig) { struct sieve_match_type *cmp = p_new(pool, struct sieve_match_type, 1); *cmp = *cmp_orig; return cmp; } /* * Match type context */ struct sieve_match_type_context { struct sieve_command *command; struct sieve_ast_argument *argument; const struct sieve_match_type *match_type; /* Only filled in when match_type->validate_context() is called */ const struct sieve_comparator *comparator; /* Context data could be used in the future to pass data between validator and * generator in match types that use extra parameters. Currently not * necessary, not even for the relational extension. */ void *ctx_data; }; /* * Match type registration */ void sieve_match_type_register (struct sieve_validator *valdtr, const struct sieve_extension *ext, const struct sieve_match_type_def *mcht); /* * Match values */ struct sieve_match_values; bool sieve_match_values_set_enabled (const struct sieve_runtime_env *renv, bool enable); bool sieve_match_values_are_enabled (const struct sieve_runtime_env *renv); struct sieve_match_values *sieve_match_values_start (const struct sieve_runtime_env *renv); void sieve_match_values_set (struct sieve_match_values *mvalues, unsigned int index, string_t *value); void sieve_match_values_add (struct sieve_match_values *mvalues, string_t *value); void sieve_match_values_add_char (struct sieve_match_values *mvalues, char c); void sieve_match_values_skip (struct sieve_match_values *mvalues, int num); void sieve_match_values_commit (const struct sieve_runtime_env *renv, struct sieve_match_values **mvalues); void sieve_match_values_abort (struct sieve_match_values **mvalues); void sieve_match_values_get (const struct sieve_runtime_env *renv, unsigned int index, string_t **value_r); /* * Match type tagged argument */ extern const struct sieve_argument_def match_type_tag; static inline bool sieve_argument_is_match_type (struct sieve_ast_argument *arg) { return ( arg->argument->def == &match_type_tag ); } void sieve_match_types_link_tags (struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg, int id_code); /* * Validation */ bool sieve_match_type_validate (struct sieve_validator *valdtr, struct sieve_command *cmd, struct sieve_ast_argument *key_arg, const struct sieve_match_type *mcht_default, const struct sieve_comparator *cmp_default); void sieve_match_type_arguments_remove (struct sieve_validator *valdtr, struct sieve_command *cmd); /* * Match type operand */ extern const struct sieve_operand_def match_type_operand; extern const struct sieve_operand_class sieve_match_type_operand_class; #define SIEVE_EXT_DEFINE_MATCH_TYPE(OP) SIEVE_EXT_DEFINE_OBJECT(OP) #define SIEVE_EXT_DEFINE_MATCH_TYPES(OPS) SIEVE_EXT_DEFINE_OBJECTS(OPS) static inline bool sieve_operand_is_match_type (const struct sieve_operand *operand) { return ( operand != NULL && operand->def != NULL && operand->def->class == &sieve_match_type_operand_class ); } static inline void sieve_opr_match_type_emit (struct sieve_binary_block *sblock, const struct sieve_match_type *mcht) { sieve_opr_object_emit(sblock, mcht->object.ext, mcht->object.def); } static inline bool sieve_opr_match_type_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address) { return sieve_opr_object_dump (denv, &sieve_match_type_operand_class, address, NULL); } static inline int sieve_opr_match_type_read (const struct sieve_runtime_env *renv, sieve_size_t *address, struct sieve_match_type *mcht) { if ( !sieve_opr_object_read (renv, &sieve_match_type_operand_class, address, &mcht->object) ) return SIEVE_EXEC_BIN_CORRUPT; mcht->def = (const struct sieve_match_type_def *) mcht->object.def; return SIEVE_EXEC_OK; } /* Common validation implementation */ bool sieve_match_substring_validate_context (struct sieve_validator *valdtr, struct sieve_ast_argument *arg, struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/cmp-i-octet.c0000644000000000000000000000372614103200126022623 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ /* Comparator 'i;octet': * */ #include "lib.h" #include "sieve-common.h" #include "sieve-comparators.h" #include #include /* * Forward declarations */ static int cmp_i_octet_compare (const struct sieve_comparator *cmp, const char *val1, size_t val1_size, const char *val2, size_t val2_size); static bool cmp_i_octet_char_match (const struct sieve_comparator *cmp, const char **val1, const char *val1_end, const char **val2, const char *val2_end); /* * Comparator object */ const struct sieve_comparator_def i_octet_comparator = { SIEVE_OBJECT("i;octet", &comparator_operand, SIEVE_COMPARATOR_I_OCTET), .flags = SIEVE_COMPARATOR_FLAG_ORDERING | SIEVE_COMPARATOR_FLAG_EQUALITY | SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH | SIEVE_COMPARATOR_FLAG_PREFIX_MATCH, .compare = cmp_i_octet_compare, .char_match = cmp_i_octet_char_match, .char_skip = sieve_comparator_octet_skip }; /* * Comparator implementation */ static int cmp_i_octet_compare( const struct sieve_comparator *cmp ATTR_UNUSED, const char *val1, size_t val1_size, const char *val2, size_t val2_size) { int result; if ( val1_size == val2_size ) { return memcmp((void *) val1, (void *) val2, val1_size); } if ( val1_size > val2_size ) { result = memcmp((void *) val1, (void *) val2, val2_size); if ( result == 0 ) return 1; return result; } result = memcmp((void *) val1, (void *) val2, val1_size); if ( result == 0 ) return -1; return result; } static bool cmp_i_octet_char_match (const struct sieve_comparator *cmp ATTR_UNUSED, const char **val, const char *val_end, const char **key, const char *key_end) { const char *val_begin = *val; const char *key_begin = *key; while ( **val == **key && *val < val_end && *key < key_end ) { (*val)++; (*key)++; } if ( *key < key_end ) { /* Reset */ *val = val_begin; *key = key_begin; return FALSE; } return TRUE; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-extensions.h0000644000000000000000000001220014103200126024002 0ustar00rootroot00000000000000#ifndef SIEVE_EXTENSIONS_H #define SIEVE_EXTENSIONS_H #include "lib.h" #include "sieve-common.h" /* * Per-extension object registry */ struct sieve_extension_objects { const void *objects; unsigned int count; }; /* * Extension definition */ struct sieve_extension_def { const char *name; /* Version */ unsigned int version; /* Registration */ bool (*load)(const struct sieve_extension *ext, void **context); void (*unload)(const struct sieve_extension *ext); /* Compilation */ bool (*validator_load) (const struct sieve_extension *ext, struct sieve_validator *validator); bool (*generator_load) (const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv); bool (*interpreter_load) (const struct sieve_extension *ext, const struct sieve_runtime_env *renv, sieve_size_t *address); bool (*binary_load) (const struct sieve_extension *ext, struct sieve_binary *binary); /* Code dump */ bool (*binary_dump) (const struct sieve_extension *ext, struct sieve_dumptime_env *denv); bool (*code_dump) (const struct sieve_extension *ext, const struct sieve_dumptime_env *denv, sieve_size_t *address); /* Objects */ struct sieve_extension_objects operations; struct sieve_extension_objects operands; }; /* Defining opcodes and operands */ #define SIEVE_EXT_DEFINE_NO_OBJECTS \ { NULL, 0 } #define SIEVE_EXT_DEFINE_OBJECT(OBJ) \ { &OBJ, 1 } #define SIEVE_EXT_DEFINE_OBJECTS(OBJS) \ { OBJS, N_ELEMENTS(OBJS) } #define SIEVE_EXT_GET_OBJECTS_COUNT(ext, field) \ ext->field->count; #define SIEVE_EXT_DEFINE_NO_OPERATIONS \ .operations = SIEVE_EXT_DEFINE_NO_OBJECTS #define SIEVE_EXT_DEFINE_OPERATION(OP) \ .operations = SIEVE_EXT_DEFINE_OBJECT(OP) #define SIEVE_EXT_DEFINE_OPERATIONS(OPS) \ .operations = SIEVE_EXT_DEFINE_OBJECTS(OPS) #define SIEVE_EXT_DEFINE_NO_OPERANDS \ .operands = SIEVE_EXT_DEFINE_NO_OBJECTS #define SIEVE_EXT_DEFINE_OPERAND(OP) \ .operands = SIEVE_EXT_DEFINE_OBJECT(OP) #define SIEVE_EXT_DEFINE_OPERANDS(OPS) \ .operands = SIEVE_EXT_DEFINE_OBJECTS(OPS) /* * Extension instance */ struct sieve_extension { const struct sieve_extension_def *def; int id; struct sieve_instance *svinst; void *context; bool required:1; bool loaded:1; bool enabled:1; bool dummy:1; bool global:1; bool implicit:1; bool overridden:1; }; #define sieve_extension_is(ext, definition) \ ( (ext)->def == &(definition) ) #define sieve_extension_name(ext) \ ((ext)->def->name) #define sieve_extension_name_is(ext, _name) \ ( strcmp((ext)->def->name, (_name)) == 0 ) #define sieve_extension_version(ext) \ ((ext)->def->version) #define sieve_extension_version_is(ext, _version) \ ((ext)->def->version == (_version)) /* * Extensions init/deinit */ bool sieve_extensions_init(struct sieve_instance *svinst); void sieve_extensions_configure(struct sieve_instance *svinst); void sieve_extensions_deinit(struct sieve_instance *svinst); /* * Pre-loaded extensions */ const struct sieve_extension *const *sieve_extensions_get_preloaded (struct sieve_instance *svinst, unsigned int *count_r); /* * Extension registry */ const struct sieve_extension *sieve_extension_register (struct sieve_instance *svinst, const struct sieve_extension_def *extension, bool load); const struct sieve_extension *sieve_extension_require (struct sieve_instance *svinst, const struct sieve_extension_def *extension, bool load); bool sieve_extension_reload(const struct sieve_extension *ext); void sieve_extension_unregister(const struct sieve_extension *ext); const struct sieve_extension *sieve_extension_replace (struct sieve_instance *svinst, const struct sieve_extension_def *extdef, bool load); void sieve_extension_override (struct sieve_instance *svinst, const char *name, const struct sieve_extension *ext); unsigned int sieve_extensions_get_count(struct sieve_instance *svinst); const struct sieve_extension *const * sieve_extensions_get_all(struct sieve_instance *svinst, unsigned int *count_r); const struct sieve_extension *sieve_extension_get_by_id (struct sieve_instance *svinst, unsigned int ext_id); const struct sieve_extension *sieve_extension_get_by_name (struct sieve_instance *svinst, const char *name); const char *sieve_extensions_get_string (struct sieve_instance *svinst); void sieve_extensions_set_string (struct sieve_instance *svinst, const char *ext_string, bool global, bool implicit); const struct sieve_extension *sieve_get_match_type_extension (struct sieve_instance *svinst); const struct sieve_extension *sieve_get_comparator_extension (struct sieve_instance *svinst); const struct sieve_extension *sieve_get_address_part_extension (struct sieve_instance *svinst); void sieve_enable_debug_extension(struct sieve_instance *svinst); /* * Capability registries */ struct sieve_extension_capabilities { const char *name; const char *(*get_string)(const struct sieve_extension *ext); }; void sieve_extension_capabilities_register (const struct sieve_extension *ext, const struct sieve_extension_capabilities *cap); void sieve_extension_capabilities_unregister (const struct sieve_extension *ext); const char *sieve_extension_capabilities_get_string (struct sieve_instance *svinst, const char *cap_name); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-code.h0000644000000000000000000002344614103200126022533 0ustar00rootroot00000000000000#ifndef SIEVE_CODE_H #define SIEVE_CODE_H #include "lib.h" #include "buffer.h" #include "mempool.h" #include "array.h" #include "sieve-common.h" #include "sieve-runtime.h" #include "sieve-runtime-trace.h" #include "sieve-dump.h" /* * Operand object */ struct sieve_operand_class { const char *name; }; struct sieve_operand_def { const char *name; const struct sieve_extension_def *ext_def; unsigned int code; const struct sieve_operand_class *class; const void *interface; }; struct sieve_operand { const struct sieve_operand_def *def; const struct sieve_extension *ext; sieve_size_t address; const char *field_name; }; #define sieve_operand_name(opr) \ ( (opr)->def == NULL ? "(NULL)" : (opr)->def->name ) #define sieve_operand_is(opr, definition) \ ( (opr)->def == &(definition) ) sieve_size_t sieve_operand_emit (struct sieve_binary_block *sblock, const struct sieve_extension *ext, const struct sieve_operand_def *oprnd); bool sieve_operand_read (struct sieve_binary_block *sblock, sieve_size_t *address, const char *field_name, struct sieve_operand *oprnd); static inline int sieve_operand_runtime_read (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, struct sieve_operand *operand) { if ( !sieve_operand_read(renv->sblock, address, field_name, operand) ) { sieve_runtime_trace_operand_error(renv, operand, "invalid operand"); return SIEVE_EXEC_BIN_CORRUPT; } return SIEVE_EXEC_OK; } /* * Optional operands */ int sieve_opr_optional_next (struct sieve_binary_block *sblock, sieve_size_t *address, signed int *opt_code); static inline int sieve_opr_optional_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, signed int *opt_code) { sieve_size_t pc = *address; int ret; if ( (ret=sieve_opr_optional_next(denv->sblock, address, opt_code)) <= 0 ) return ret; sieve_code_mark_specific(denv, pc); return ret; } static inline int sieve_opr_optional_read (const struct sieve_runtime_env *renv, sieve_size_t *address, signed int *opt_code) { int ret; if ( (ret=sieve_opr_optional_next(renv->sblock, address, opt_code)) < 0 ) sieve_runtime_trace_error(renv, "invalid optional operand code"); return ret; } /* * Core operands */ /* Operand codes */ enum sieve_core_operand { SIEVE_OPERAND_OPTIONAL = 0x00, SIEVE_OPERAND_NUMBER, SIEVE_OPERAND_STRING, SIEVE_OPERAND_STRING_LIST, SIEVE_OPERAND_COMPARATOR, SIEVE_OPERAND_MATCH_TYPE, SIEVE_OPERAND_ADDRESS_PART, SIEVE_OPERAND_CATENATED_STRING, SIEVE_OPERAND_CUSTOM }; /* Operand classes */ extern const struct sieve_operand_class number_class; extern const struct sieve_operand_class string_class; extern const struct sieve_operand_class stringlist_class; /* Operand objects */ extern const struct sieve_operand_def omitted_operand; extern const struct sieve_operand_def number_operand; extern const struct sieve_operand_def string_operand; extern const struct sieve_operand_def stringlist_operand; extern const struct sieve_operand_def catenated_string_operand; extern const struct sieve_operand_def *sieve_operands[]; extern const unsigned int sieve_operand_count; /* Operand object interfaces */ struct sieve_opr_number_interface { bool (*dump) (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, sieve_size_t *address); int (*read) (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, sieve_size_t *address, sieve_number_t *number_r); }; struct sieve_opr_string_interface { bool (*dump) (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, sieve_size_t *address); int (*read) (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, sieve_size_t *address, string_t **str_r); }; struct sieve_opr_stringlist_interface { bool (*dump) (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, sieve_size_t *address); int (*read) (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, sieve_size_t *address, struct sieve_stringlist **strlist_r); }; /* * Core operand functions */ /* Omitted */ void sieve_opr_omitted_emit(struct sieve_binary_block *sblock); static inline bool sieve_operand_is_omitted (const struct sieve_operand *operand) { return ( operand != NULL && operand->def != NULL && operand->def == &omitted_operand ); } /* Number */ void sieve_opr_number_emit (struct sieve_binary_block *sblock, sieve_number_t number); bool sieve_opr_number_dump_data (const struct sieve_dumptime_env *denv, struct sieve_operand *operand, sieve_size_t *address, const char *field_name); bool sieve_opr_number_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, const char *field_name); int sieve_opr_number_read_data (const struct sieve_runtime_env *renv, struct sieve_operand *operand, sieve_size_t *address, const char *field_name, sieve_number_t *number_r); int sieve_opr_number_read (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, sieve_number_t *number_r); static inline bool sieve_operand_is_number (const struct sieve_operand *operand) { return ( operand != NULL && operand->def != NULL && operand->def->class == &number_class ); } /* String */ void sieve_opr_string_emit (struct sieve_binary_block *sblock, string_t *str); bool sieve_opr_string_dump_data (const struct sieve_dumptime_env *denv, struct sieve_operand *operand, sieve_size_t *address, const char *field_name); bool sieve_opr_string_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, const char *field_name); bool sieve_opr_string_dump_ex (const struct sieve_dumptime_env *denv, sieve_size_t *address, const char *field_name, const char *omitted_value); int sieve_opr_string_read_data (const struct sieve_runtime_env *renv, struct sieve_operand *operand, sieve_size_t *address, const char *field_name, string_t **str_r); int sieve_opr_string_read (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, string_t **str_r); int sieve_opr_string_read_ex (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, bool optional, string_t **str_r, bool *literal_r); static inline bool sieve_operand_is_string (const struct sieve_operand *operand) { return ( operand != NULL && operand->def != NULL && operand->def->class == &string_class ); } static inline bool sieve_operand_is_string_literal (const struct sieve_operand *operand) { return ( operand != NULL && sieve_operand_is(operand, string_operand) ); } /* String list */ void sieve_opr_stringlist_emit_start (struct sieve_binary_block *sblock, unsigned int listlen, void **context); void sieve_opr_stringlist_emit_item (struct sieve_binary_block *sblock, void *context ATTR_UNUSED, string_t *item); void sieve_opr_stringlist_emit_end (struct sieve_binary_block *sblock, void *context); bool sieve_opr_stringlist_dump_data (const struct sieve_dumptime_env *denv, struct sieve_operand *operand, sieve_size_t *address, const char *field_name); bool sieve_opr_stringlist_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, const char *field_name); bool sieve_opr_stringlist_dump_ex (const struct sieve_dumptime_env *denv, sieve_size_t *address, const char *field_name, const char *omitted_value); int sieve_opr_stringlist_read_data (const struct sieve_runtime_env *renv, struct sieve_operand *operand, sieve_size_t *address, const char *field_name, struct sieve_stringlist **strlist_r); int sieve_opr_stringlist_read (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, struct sieve_stringlist **strlist_r); int sieve_opr_stringlist_read_ex (const struct sieve_runtime_env *renv, sieve_size_t *address, const char *field_name, bool optional, struct sieve_stringlist **strlist_r); static inline bool sieve_operand_is_stringlist (const struct sieve_operand *operand) { return ( operand != NULL && operand->def != NULL && (operand->def->class == &stringlist_class || operand->def->class == &string_class) ); } /* Catenated string */ void sieve_opr_catenated_string_emit (struct sieve_binary_block *sblock, unsigned int elements); /* * Operation object */ struct sieve_operation_def { const char *mnemonic; const struct sieve_extension_def *ext_def; unsigned int code; bool (*dump) (const struct sieve_dumptime_env *denv, sieve_size_t *address); int (*execute) (const struct sieve_runtime_env *renv, sieve_size_t *address); }; struct sieve_operation { const struct sieve_operation_def *def; const struct sieve_extension *ext; sieve_size_t address; }; #define sieve_operation_is(oprtn, definition) \ ( (oprtn)->def == &(definition) ) #define sieve_operation_mnemonic(oprtn) \ ( (oprtn)->def == NULL ? "(NULL)" : (oprtn)->def->mnemonic ) sieve_size_t sieve_operation_emit (struct sieve_binary_block *sblock, const struct sieve_extension *ext, const struct sieve_operation_def *op_def); bool sieve_operation_read (struct sieve_binary_block *sblock, sieve_size_t *address, struct sieve_operation *oprtn); const char *sieve_operation_read_string (struct sieve_binary_block *sblock, sieve_size_t *address); /* * Core operations */ /* Opcodes */ enum sieve_operation_code { SIEVE_OPERATION_INVALID, SIEVE_OPERATION_JMP, SIEVE_OPERATION_JMPTRUE, SIEVE_OPERATION_JMPFALSE, SIEVE_OPERATION_STOP, SIEVE_OPERATION_KEEP, SIEVE_OPERATION_DISCARD, SIEVE_OPERATION_REDIRECT, SIEVE_OPERATION_ADDRESS, SIEVE_OPERATION_HEADER, SIEVE_OPERATION_EXISTS, SIEVE_OPERATION_SIZE_OVER, SIEVE_OPERATION_SIZE_UNDER, SIEVE_OPERATION_CUSTOM }; /* Operation objects */ extern const struct sieve_operation_def sieve_jmp_operation; extern const struct sieve_operation_def sieve_jmptrue_operation; extern const struct sieve_operation_def sieve_jmpfalse_operation; extern const struct sieve_operation_def *sieve_operations[]; extern const unsigned int sieve_operations_count; #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/cmd-if.c0000644000000000000000000001527114103200126021637 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "sieve-common.h" #include "sieve-commands.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-code.h" #include "sieve-binary.h" /* * Commands */ static bool cmd_if_validate (struct sieve_validator *valdtr, struct sieve_command *cmd); static bool cmd_elsif_validate (struct sieve_validator *valdtr, struct sieve_command *cmd); static bool cmd_if_validate_const (struct sieve_validator *valdtr, struct sieve_command *cmd, int *const_current, int const_next); static bool cmd_if_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); static bool cmd_else_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); /* If command * * Syntax: * if */ const struct sieve_command_def cmd_if = { .identifier = "if", .type = SCT_COMMAND, .positional_args = 0, .subtests = 1, .block_allowed = TRUE, .block_required = TRUE, .validate = cmd_if_validate, .validate_const = cmd_if_validate_const, .generate = cmd_if_generate }; /* ElsIf command * * Santax: * elsif */ const struct sieve_command_def cmd_elsif = { .identifier = "elsif", .type = SCT_COMMAND, .positional_args = 0, .subtests = 1, .block_allowed = TRUE, .block_required = TRUE, .validate = cmd_elsif_validate, .validate_const = cmd_if_validate_const, .generate = cmd_if_generate }; /* Else command * * Syntax: * else */ const struct sieve_command_def cmd_else = { .identifier = "else", .type = SCT_COMMAND, .positional_args = 0, .subtests = 0, .block_allowed = TRUE, .block_required = TRUE, .validate = cmd_elsif_validate, .validate_const = cmd_if_validate_const, .generate = cmd_else_generate }; /* * Context management */ struct cmd_if_context_data { struct cmd_if_context_data *previous; struct cmd_if_context_data *next; int const_condition; bool jump_generated; sieve_size_t exit_jump; }; static void cmd_if_initialize_context_data (struct sieve_command *cmd, struct cmd_if_context_data *previous) { struct cmd_if_context_data *cmd_data; /* Assign context */ cmd_data = p_new(sieve_command_pool(cmd), struct cmd_if_context_data, 1); cmd_data->exit_jump = 0; cmd_data->jump_generated = FALSE; /* Update linked list of contexts */ cmd_data->previous = previous; cmd_data->next = NULL; if ( previous != NULL ) previous->next = cmd_data; /* Check const status */ cmd_data->const_condition = -1; while ( previous != NULL ) { if ( previous->const_condition > 0 ) { cmd_data->const_condition = 0; break; } previous = previous->previous; } /* Assign to command context */ cmd->data = cmd_data; } /* * Validation */ static bool cmd_if_validate (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd) { /* Start if-command structure */ cmd_if_initialize_context_data(cmd, NULL); return TRUE; } static bool cmd_elsif_validate (struct sieve_validator *valdtr, struct sieve_command *cmd) { struct sieve_command *prev; i_assert(cmd != NULL); prev = sieve_command_prev(cmd); /* Check valid command placement */ if ( prev == NULL || ( !sieve_command_is(prev, cmd_if) && !sieve_command_is(prev, cmd_elsif) ) ) { sieve_command_validate_error(valdtr, cmd, "the %s command must follow an if or elseif command", sieve_command_identifier(cmd)); return FALSE; } /* Previous command in this block is 'if' or 'elsif', so we can safely refer * to its context data */ cmd_if_initialize_context_data(cmd, prev->data); return TRUE; } static bool cmd_if_validate_const (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd, int *const_current, int const_next) { struct cmd_if_context_data *cmd_data = (struct cmd_if_context_data *) cmd->data; if ( cmd_data != NULL ) { if ( cmd_data->const_condition == 0 ) { *const_current = cmd_data->const_condition; return FALSE; } cmd_data->const_condition = const_next; } *const_current = const_next; return ( const_next < 0 ); } /* * Code generation */ /* The if command does not generate specific IF-ELSIF-ELSE opcodes, but only uses * JMP instructions. This is why the implementation of the if command does not * include an opcode implementation. */ static void cmd_if_resolve_exit_jumps (struct sieve_binary_block *sblock, struct cmd_if_context_data *cmd_data) { struct cmd_if_context_data *if_ctx = cmd_data->previous; /* Iterate backwards through all if-command contexts and resolve the * exit jumps to the current code position. */ while ( if_ctx != NULL ) { if ( if_ctx->jump_generated ) sieve_binary_resolve_offset(sblock, if_ctx->exit_jump); if_ctx = if_ctx->previous; } } static bool cmd_if_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) { struct sieve_binary_block *sblock = cgenv->sblock; struct cmd_if_context_data *cmd_data = (struct cmd_if_context_data *) cmd->data; struct sieve_ast_node *test; struct sieve_jumplist jmplist; /* Generate test condition */ if ( cmd_data->const_condition < 0 ) { /* Prepare jumplist */ sieve_jumplist_init_temp(&jmplist, sblock); test = sieve_ast_test_first(cmd->ast_node); if ( !sieve_generate_test(cgenv, test, &jmplist, FALSE) ) return FALSE; } /* Case true { */ if ( cmd_data->const_condition != 0 ) { if ( !sieve_generate_block(cgenv, cmd->ast_node) ) return FALSE; } /* Are we the final command in this if-elsif-else structure? */ if ( cmd_data->next == NULL || cmd_data->const_condition == 1 ) { /* Yes, Resolve previous exit jumps to this point */ cmd_if_resolve_exit_jumps(sblock, cmd_data); } else if ( cmd_data->const_condition < 0 ) { /* No, generate jump to end of if-elsif-else structure (resolved later) * This of course is not necessary if the {} block contains a command * like stop at top level that unconditionally exits the block already * anyway. */ if ( !sieve_command_block_exits_unconditionally(cmd) ) { sieve_operation_emit(sblock, NULL, &sieve_jmp_operation); cmd_data->exit_jump = sieve_binary_emit_offset(sblock, 0); cmd_data->jump_generated = TRUE; } } if ( cmd_data->const_condition < 0 ) { /* Case false ... (subsequent elsif/else commands might generate more) */ sieve_jumplist_resolve(&jmplist); } return TRUE; } static bool cmd_else_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) { struct cmd_if_context_data *cmd_data = (struct cmd_if_context_data *) cmd->data; /* Else { */ if ( cmd_data->const_condition != 0 ) { if ( !sieve_generate_block(cgenv, cmd->ast_node) ) return FALSE; /* } End: resolve all exit blocks */ cmd_if_resolve_exit_jumps(cgenv->sblock, cmd_data); } return TRUE; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-code-dumper.h0000644000000000000000000000306214103200126024015 0ustar00rootroot00000000000000#ifndef SIEVE_CODE_DUMPER_H #define SIEVE_CODE_DUMPER_H #include "sieve-common.h" struct sieve_code_dumper; struct sieve_code_dumper *sieve_code_dumper_create (struct sieve_dumptime_env *denv); void sieve_code_dumper_free (struct sieve_code_dumper **_dumper); pool_t sieve_code_dumper_pool (struct sieve_code_dumper *dumper); /* * Extension support */ struct sieve_code_dumper_extension { const struct sieve_extension_def *ext; void (*free)(struct sieve_code_dumper *dumper, void *context); }; void sieve_dump_extension_register (struct sieve_code_dumper *dumper, const struct sieve_extension *ext, const struct sieve_code_dumper_extension *dump_ext, void *context); void sieve_dump_extension_set_context (struct sieve_code_dumper *dumper, const struct sieve_extension *ext, void *context); void *sieve_dump_extension_get_context (struct sieve_code_dumper *dumper, const struct sieve_extension *ext); /* Dump functions */ void sieve_code_dumpf (const struct sieve_dumptime_env *denv, const char *fmt, ...) ATTR_FORMAT(2, 3); void sieve_code_mark(const struct sieve_dumptime_env *denv); void sieve_code_mark_specific (const struct sieve_dumptime_env *denv, sieve_size_t location); void sieve_code_descend(const struct sieve_dumptime_env *denv); void sieve_code_ascend(const struct sieve_dumptime_env *denv); /* Operations and operands */ bool sieve_code_dumper_print_optional_operands (const struct sieve_dumptime_env *denv, sieve_size_t *address); /* Code dump (debugging purposes) */ void sieve_code_dumper_run(struct sieve_code_dumper *dumper); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-config.h0000644000000000000000000000057114103200126023060 0ustar00rootroot00000000000000#ifndef SIEVE_CONFIG_H #define SIEVE_CONFIG_H #include "pigeonhole-config.h" #include "pigeonhole-version.h" #define SIEVE_IMPLEMENTATION PIGEONHOLE_NAME " Sieve " PIGEONHOLE_VERSION_FULL #define SIEVE_SCRIPT_FILEEXT "sieve" #define SIEVE_BINARY_FILEEXT "svbin" #define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON" #define DEFAULT_REDIRECT_DUPLICATE_PERIOD (3600 * 12) #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/cmd-require.c0000644000000000000000000000423314103200126022711 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "sieve-common.h" #include "sieve-commands.h" #include "sieve-extensions.h" #include "sieve-validator.h" #include "sieve-generator.h" /* * Require command * * Syntax * Syntax: require */ static bool cmd_require_validate (struct sieve_validator *valdtr, struct sieve_command *cmd); const struct sieve_command_def cmd_require = { .identifier = "require", .type = SCT_COMMAND, .positional_args = 1, .subtests = 0, .block_allowed = FALSE, .block_required = FALSE, .validate = cmd_require_validate }; /* * Validation */ static bool cmd_require_validate (struct sieve_validator *valdtr, struct sieve_command *cmd) { bool result = TRUE; struct sieve_ast_argument *arg; struct sieve_command *prev = sieve_command_prev(cmd); /* Check valid command placement */ if ( !sieve_command_is_toplevel(cmd) || ( !sieve_command_is_first(cmd) && prev != NULL && !sieve_command_is(prev, cmd_require) ) ) { sieve_command_validate_error(valdtr, cmd, "require commands can only be placed at top level " "at the beginning of the file"); return FALSE; } /* Check argument and load specified extension(s) */ arg = cmd->first_positional; if ( sieve_ast_argument_type(arg) == SAAT_STRING ) { /* Single string */ const struct sieve_extension *ext = sieve_validator_extension_load_by_name (valdtr, cmd, arg, sieve_ast_argument_strc(arg)); if ( ext == NULL ) result = FALSE; } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) { /* String list */ struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg); while ( stritem != NULL ) { const struct sieve_extension *ext = sieve_validator_extension_load_by_name (valdtr, cmd, stritem, sieve_ast_strlist_strc(stritem)); if ( ext == NULL ) result = FALSE; stritem = sieve_ast_strlist_next(stritem); } } else { /* Something else */ sieve_argument_validate_error(valdtr, arg, "the require command accepts a single string or string list argument, " "but %s was found", sieve_ast_argument_name(arg)); return FALSE; } return result; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-message.c0000644000000000000000000013472014103200126023236 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "mempool.h" #include "array.h" #include "str.h" #include "str-sanitize.h" #include "istream.h" #include "time-util.h" #include "rfc822-parser.h" #include "message-date.h" #include "message-parser.h" #include "message-decoder.h" #include "message-header-decode.h" #include "mail-html2text.h" #include "mail-storage.h" #include "mail-user.h" #include "smtp-params.h" #include "master-service.h" #include "master-service-settings.h" #include "raw-storage.h" #include "edit-mail.h" #include "sieve-common.h" #include "sieve-stringlist.h" #include "sieve-error.h" #include "sieve-extensions.h" #include "sieve-code.h" #include "sieve-address.h" #include "sieve-address-parts.h" #include "sieve-runtime.h" #include "sieve-runtime-trace.h" #include "sieve-match.h" #include "sieve-interpreter.h" #include "sieve-message.h" /* * Message transmission */ const char *sieve_message_get_new_id(const struct sieve_instance *svinst) { static int count = 0; return t_strdup_printf("", dec2str(ioloop_timeval.tv_sec), dec2str(ioloop_timeval.tv_usec), count++, svinst->hostname); } /* * Message context */ struct sieve_message_header { const char *name; const unsigned char *value, *utf8_value; size_t value_len, utf8_value_len; }; struct sieve_message_part { struct sieve_message_part *parent, *next, *children; ARRAY(struct sieve_message_header) headers; const char *content_type; const char *content_disposition; const char *decoded_body; const char *text_body; size_t decoded_body_size; size_t text_body_size; bool have_body:1; /* there's the empty end-of-headers line */ bool epilogue:1; /* this is a multipart epilogue */ }; struct sieve_message_version { struct mail *mail; struct mailbox *box; struct mailbox_transaction_context *trans; struct edit_mail *edit_mail; }; struct sieve_message_context { pool_t pool; pool_t context_pool; int refcount; struct sieve_instance *svinst; struct timeval time; struct mail_user *mail_user; const struct sieve_message_data *msgdata; /* Message versioning */ struct mail_user *raw_mail_user; ARRAY(struct sieve_message_version) versions; /* Context data for extensions */ ARRAY(void *) ext_contexts; /* Body */ ARRAY(struct sieve_message_part *) cached_body_parts; ARRAY(struct sieve_message_part_data) return_body_parts; buffer_t *raw_body; bool edit_snapshot:1; bool substitute_snapshot:1; }; /* * Message versions */ static inline struct sieve_message_version *sieve_message_version_new (struct sieve_message_context *msgctx) { return array_append_space(&msgctx->versions); } static inline struct sieve_message_version *sieve_message_version_get (struct sieve_message_context *msgctx) { struct sieve_message_version *versions; unsigned int count; versions = array_get_modifiable(&msgctx->versions, &count); if ( count == 0 ) return array_append_space(&msgctx->versions); return &versions[count-1]; } static inline void sieve_message_version_free (struct sieve_message_version *version) { if ( version->edit_mail != NULL ) { edit_mail_unwrap(&version->edit_mail); version->edit_mail = NULL; } if ( version->mail != NULL ) { mail_free(&version->mail); mailbox_transaction_rollback(&version->trans); mailbox_free(&version->box); version->mail = NULL; } } /* * Message context object */ struct sieve_message_context *sieve_message_context_create (struct sieve_instance *svinst, struct mail_user *mail_user, const struct sieve_message_data *msgdata) { struct sieve_message_context *msgctx; msgctx = i_new(struct sieve_message_context, 1); msgctx->refcount = 1; msgctx->svinst = svinst; msgctx->mail_user = mail_user; msgctx->msgdata = msgdata; i_gettimeofday(&msgctx->time); sieve_message_context_reset(msgctx); return msgctx; } void sieve_message_context_ref(struct sieve_message_context *msgctx) { msgctx->refcount++; } static void sieve_message_context_clear(struct sieve_message_context *msgctx) { struct sieve_message_version *versions; unsigned int count, i; if ( msgctx->pool != NULL ) { versions = array_get_modifiable(&msgctx->versions, &count); for ( i = 0; i < count; i++ ) { sieve_message_version_free(&versions[i]); } pool_unref(&(msgctx->pool)); } } void sieve_message_context_unref(struct sieve_message_context **msgctx) { i_assert((*msgctx)->refcount > 0); if (--(*msgctx)->refcount != 0) return; if ( (*msgctx)->raw_mail_user != NULL ) mail_user_unref(&(*msgctx)->raw_mail_user); sieve_message_context_clear(*msgctx); if ( (*msgctx)->context_pool != NULL ) pool_unref(&((*msgctx)->context_pool)); i_free(*msgctx); *msgctx = NULL; } static void sieve_message_context_flush(struct sieve_message_context *msgctx) { pool_t pool; if ( msgctx->context_pool != NULL ) pool_unref(&(msgctx->context_pool)); msgctx->context_pool = pool = pool_alloconly_create("sieve_message_context_data", 2048); p_array_init(&msgctx->ext_contexts, pool, sieve_extensions_get_count(msgctx->svinst)); p_array_init(&msgctx->cached_body_parts, pool, 8); p_array_init(&msgctx->return_body_parts, pool, 8); msgctx->raw_body = NULL; } void sieve_message_context_reset(struct sieve_message_context *msgctx) { sieve_message_context_clear(msgctx); msgctx->pool = pool_alloconly_create("sieve_message_context", 1024); p_array_init(&msgctx->versions, msgctx->pool, 4); sieve_message_context_flush(msgctx); } pool_t sieve_message_context_pool(struct sieve_message_context *msgctx) { return msgctx->context_pool; } void sieve_message_context_time(struct sieve_message_context *msgctx, struct timeval *time) { *time = msgctx->time; } /* Extension support */ void sieve_message_context_extension_set (struct sieve_message_context *msgctx, const struct sieve_extension *ext, void *context) { if ( ext->id < 0 ) return; array_idx_set(&msgctx->ext_contexts, (unsigned int) ext->id, &context); } const void *sieve_message_context_extension_get (struct sieve_message_context *msgctx, const struct sieve_extension *ext) { void * const *ctx; if ( ext->id < 0 || ext->id >= (int) array_count(&msgctx->ext_contexts) ) return NULL; ctx = array_idx(&msgctx->ext_contexts, (unsigned int) ext->id); return *ctx; } /* Envelope */ const struct smtp_address *sieve_message_get_orig_recipient (struct sieve_message_context *msgctx) { const struct sieve_message_data *msgdata = msgctx->msgdata; const struct smtp_address *orcpt_to = NULL; if ( msgdata->envelope.rcpt_params != NULL ) { orcpt_to = msgdata->envelope.rcpt_params->orcpt.addr; if ( !smtp_address_isnull(orcpt_to) ) return orcpt_to; } orcpt_to = msgdata->envelope.rcpt_to; return ( !smtp_address_isnull(orcpt_to) ? orcpt_to : NULL ); } const struct smtp_address *sieve_message_get_final_recipient (struct sieve_message_context *msgctx) { const struct sieve_message_data *msgdata = msgctx->msgdata; const struct smtp_address *rcpt_to = msgdata->envelope.rcpt_to; return ( !smtp_address_isnull(rcpt_to) ? rcpt_to : NULL); } const struct smtp_address *sieve_message_get_sender (struct sieve_message_context *msgctx) { const struct sieve_message_data *msgdata = msgctx->msgdata; const struct smtp_address *mail_from = msgdata->envelope.mail_from; return ( !smtp_address_isnull(mail_from) ? mail_from : NULL); } /* * Mail */ int sieve_message_substitute (struct sieve_message_context *msgctx, struct istream *input) { static const char *wanted_headers[] = { "From", "Message-ID", "Subject", "Return-Path", NULL }; static const struct smtp_address default_sender = { .localpart = DEFAULT_ENVELOPE_SENDER, .domain = NULL, }; struct mail_user *mail_user = msgctx->mail_user; struct sieve_message_version *version; struct mailbox_header_lookup_ctx *headers_ctx; struct mailbox *box = NULL; const struct smtp_address *sender; int ret; i_assert(input->blocking); if ( msgctx->raw_mail_user == NULL ) { void **sets = master_service_settings_get_others(master_service); msgctx->raw_mail_user = raw_storage_create_from_set(mail_user->set_info, sets[0]); } i_stream_seek(input, 0); sender = sieve_message_get_sender(msgctx); sender = (sender == NULL ? &default_sender : sender); ret = raw_mailbox_alloc_stream(msgctx->raw_mail_user, input, (time_t)-1, smtp_address_encode(sender), &box); if ( ret < 0 ) { e_error(msgctx->svinst->event, "can't open substituted mail as raw: %s", mailbox_get_last_internal_error(box, NULL)); return -1; } if ( msgctx->substitute_snapshot ) { version = sieve_message_version_new(msgctx); } else { version = sieve_message_version_get(msgctx); sieve_message_version_free(version); } version->box = box; version->trans = mailbox_transaction_begin(box, 0, __func__); headers_ctx = mailbox_header_lookup_init(box, wanted_headers); version->mail = mail_alloc(version->trans, 0, headers_ctx); mailbox_header_lookup_unref(&headers_ctx); mail_set_seq(version->mail, 1); sieve_message_context_flush(msgctx); msgctx->substitute_snapshot = FALSE; msgctx->edit_snapshot = FALSE; return 1; } struct mail *sieve_message_get_mail (struct sieve_message_context *msgctx) { const struct sieve_message_version *versions; unsigned int count; versions = array_get(&msgctx->versions, &count); if ( count == 0 ) return msgctx->msgdata->mail; if ( versions[count-1].edit_mail != NULL ) return edit_mail_get_mail(versions[count-1].edit_mail); return versions[count-1].mail; } struct edit_mail *sieve_message_edit (struct sieve_message_context *msgctx) { struct sieve_message_version *version; version = sieve_message_version_get(msgctx); if ( version->edit_mail == NULL ) { version->edit_mail = edit_mail_wrap (( version->mail == NULL ? msgctx->msgdata->mail : version->mail )); } else if ( msgctx->edit_snapshot ) { version->edit_mail = edit_mail_snapshot(version->edit_mail); } msgctx->edit_snapshot = FALSE; return version->edit_mail; } void sieve_message_snapshot (struct sieve_message_context *msgctx) { msgctx->edit_snapshot = TRUE; msgctx->substitute_snapshot = TRUE; } /* * Message header list */ /* Forward declarations */ static int sieve_message_header_list_next_item (struct sieve_header_list *_hdrlist, const char **name_r, string_t **value_r); static int sieve_message_header_list_next_value (struct sieve_stringlist *_strlist, string_t **value_r); static void sieve_message_header_list_reset (struct sieve_stringlist *_strlist); /* String list object */ struct sieve_message_header_list { struct sieve_header_list hdrlist; struct sieve_stringlist *field_names; const char *header_name; const char *const *headers; int headers_index; bool mime_decode:1; }; struct sieve_header_list *sieve_message_header_list_create (const struct sieve_runtime_env *renv, struct sieve_stringlist *field_names, bool mime_decode) { struct sieve_message_header_list *hdrlist; hdrlist = t_new(struct sieve_message_header_list, 1); hdrlist->hdrlist.strlist.runenv = renv; hdrlist->hdrlist.strlist.exec_status = SIEVE_EXEC_OK; hdrlist->hdrlist.strlist.next_item = sieve_message_header_list_next_value; hdrlist->hdrlist.strlist.reset = sieve_message_header_list_reset; hdrlist->hdrlist.next_item = sieve_message_header_list_next_item; hdrlist->field_names = field_names; hdrlist->mime_decode = mime_decode; return &hdrlist->hdrlist; } // NOTE: get rid of this once we have a proper Sieve string type static inline string_t *_header_right_trim(const char *raw) { string_t *result; const char *p, *pend; pend = raw + strlen(raw); if (raw == pend) { result = t_str_new(1); } else { for ( p = pend-1; p >= raw; p-- ) { if ( *p != ' ' && *p != '\t' ) break; } result = t_str_new(p - raw + 1); str_append_data(result, raw, p - raw + 1); } return result; } /* String list implementation */ static int sieve_message_header_list_next_item (struct sieve_header_list *_hdrlist, const char **name_r, string_t **value_r) { struct sieve_message_header_list *hdrlist = (struct sieve_message_header_list *) _hdrlist; const struct sieve_runtime_env *renv = _hdrlist->strlist.runenv; struct mail *mail = sieve_message_get_mail(renv->msgctx); if ( name_r != NULL ) *name_r = NULL; *value_r = NULL; /* Check for end of current header list */ if ( hdrlist->headers == NULL ) { hdrlist->headers_index = 0; } else if ( hdrlist->headers[hdrlist->headers_index] == NULL ) { hdrlist->headers = NULL; hdrlist->headers_index = 0; } /* Fetch next header */ while ( hdrlist->headers == NULL ) { string_t *hdr_item = NULL; int ret; /* Read next header name from source list */ if ( (ret=sieve_stringlist_next_item (hdrlist->field_names, &hdr_item)) <= 0 ) return ret; hdrlist->header_name = str_c(hdr_item); if ( _hdrlist->strlist.trace ) { sieve_runtime_trace(renv, 0, "extracting `%s' headers from message", str_sanitize(str_c(hdr_item), 80)); } /* Fetch all matching headers from the e-mail */ if ( hdrlist->mime_decode ) { ret = mail_get_headers_utf8(mail, str_c(hdr_item), &hdrlist->headers); } else { ret = mail_get_headers(mail, str_c(hdr_item), &hdrlist->headers); } if (ret < 0) { _hdrlist->strlist.exec_status = sieve_runtime_mail_error(renv, mail, "failed to read header field `%s'", str_c(hdr_item)); return -1; } if ( ret == 0 || hdrlist->headers[0] == NULL ) { /* Try next item when no headers found */ hdrlist->headers = NULL; } } /* Return next item */ if ( name_r != NULL ) *name_r = hdrlist->header_name; *value_r = _header_right_trim(hdrlist->headers[hdrlist->headers_index++]); return 1; } static int sieve_message_header_list_next_value (struct sieve_stringlist *_strlist, string_t **value_r) { struct sieve_header_list *hdrlist = (struct sieve_header_list *) _strlist; return sieve_message_header_list_next_item (hdrlist, NULL, value_r); } static void sieve_message_header_list_reset (struct sieve_stringlist *strlist) { struct sieve_message_header_list *hdrlist = (struct sieve_message_header_list *) strlist; hdrlist->headers = NULL; hdrlist->headers_index = 0; sieve_stringlist_reset(hdrlist->field_names); } /* * Header override operand */ const struct sieve_operand_class sieve_message_override_operand_class = { "header-override" }; bool sieve_opr_message_override_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address) { struct sieve_message_override svmo; const struct sieve_message_override_def *hodef; if ( !sieve_opr_object_dump (denv, &sieve_message_override_operand_class, address, &svmo.object) ) return FALSE; hodef = svmo.def = (const struct sieve_message_override_def *) svmo.object.def; if ( hodef->dump_context != NULL ) { sieve_code_descend(denv); if ( !hodef->dump_context(&svmo, denv, address) ) { return FALSE; } sieve_code_ascend(denv); } return TRUE; } int sieve_opr_message_override_read (const struct sieve_runtime_env *renv, sieve_size_t *address, struct sieve_message_override *svmo) { const struct sieve_message_override_def *hodef; int ret; svmo->context = NULL; if ( !sieve_opr_object_read (renv, &sieve_message_override_operand_class, address, &svmo->object) ) return SIEVE_EXEC_BIN_CORRUPT; hodef = svmo->def = (const struct sieve_message_override_def *) svmo->object.def; if ( hodef->read_context != NULL && (ret=hodef->read_context(svmo, renv, address, &svmo->context)) <= 0 ) return ret; return SIEVE_EXEC_OK; } /* * Optional operands */ int sieve_message_opr_optional_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, signed int *opt_code) { signed int _opt_code = 0; bool final = FALSE, opok = TRUE; if ( opt_code == NULL ) { opt_code = &_opt_code; final = TRUE; } while ( opok ) { int opt; if ( (opt=sieve_addrmatch_opr_optional_dump (denv, address, opt_code)) <= 0 ) return opt; if ( *opt_code == SIEVE_OPT_MESSAGE_OVERRIDE ) { opok = sieve_opr_message_override_dump(denv, address); } else { return ( final ? -1 : 1 ); } } return -1; } int sieve_message_opr_optional_read (const struct sieve_runtime_env *renv, sieve_size_t *address, signed int *opt_code, int *exec_status, struct sieve_address_part *addrp, struct sieve_match_type *mcht, struct sieve_comparator *cmp, ARRAY_TYPE(sieve_message_override) *svmos) { signed int _opt_code = 0; bool final = FALSE; int ret; if ( opt_code == NULL ) { opt_code = &_opt_code; final = TRUE; } if ( exec_status != NULL ) *exec_status = SIEVE_EXEC_OK; for ( ;; ) { int opt; if ( (opt=sieve_addrmatch_opr_optional_read (renv, address, opt_code, exec_status, addrp, mcht, cmp)) <= 0 ) return opt; if ( *opt_code == SIEVE_OPT_MESSAGE_OVERRIDE ) { struct sieve_message_override svmo; const struct sieve_message_override *svmo_idx; unsigned int count, i; if ( (ret=sieve_opr_message_override_read (renv, address, &svmo)) <= 0 ) { if ( exec_status != NULL ) *exec_status = ret; return -1; } if ( !array_is_created(svmos) ) t_array_init(svmos, 8); /* insert in sorted sequence */ svmo_idx = array_get(svmos, &count); for (i = 0; i < count; i++) { if (svmo.def->sequence < svmo_idx[i].def->sequence) { array_insert(svmos, i, &svmo, 1); break; } } if (count == i) array_append(svmos, &svmo, 1); } else { if ( final ) { sieve_runtime_trace_error(renv, "invalid optional operand"); if ( exec_status != NULL ) *exec_status = SIEVE_EXEC_BIN_CORRUPT; return -1; } return 1; } } i_unreached(); return -1; } /* * Message header */ int sieve_message_get_header_fields (const struct sieve_runtime_env *renv, struct sieve_stringlist *field_names, ARRAY_TYPE(sieve_message_override) *svmos, bool mime_decode, struct sieve_stringlist **fields_r) { const struct sieve_message_override *svmo; unsigned int count, i; int ret; if ( svmos == NULL || !array_is_created(svmos) || array_count(svmos) == 0 ) { struct sieve_header_list *headers; headers = sieve_message_header_list_create (renv, field_names, mime_decode); *fields_r = &headers->strlist; return SIEVE_EXEC_OK; } svmo = array_get(svmos, &count); if ( svmo[0].def->sequence == 0 && svmo[0].def->header_override != NULL ) { *fields_r = field_names; } else { struct sieve_header_list *headers; headers = sieve_message_header_list_create (renv, field_names, mime_decode); *fields_r = &headers->strlist; } for ( i = 0; i < count; i++ ) { if ( svmo[i].def->header_override != NULL && (ret=svmo[i].def->header_override (&svmo[i], renv, mime_decode, fields_r)) <= 0 ) return ret; } return SIEVE_EXEC_OK; } /* * Message part */ struct sieve_message_part *sieve_message_part_parent (struct sieve_message_part *mpart) { return mpart->parent; } struct sieve_message_part *sieve_message_part_next (struct sieve_message_part *mpart) { return mpart->next; } struct sieve_message_part *sieve_message_part_children (struct sieve_message_part *mpart) { return mpart->children; } const char *sieve_message_part_content_type (struct sieve_message_part *mpart) { return mpart->content_type; } const char *sieve_message_part_content_disposition (struct sieve_message_part *mpart) { return mpart->content_disposition; } int sieve_message_part_get_first_header (struct sieve_message_part *mpart, const char *field, const char **value_r) { const struct sieve_message_header *headers; unsigned int i, count; headers = array_get(&mpart->headers, &count); for ( i = 0; i < count; i++ ) { if ( strcasecmp( headers[i].name, field) == 0 ) { i_assert( headers[i].value[headers[i].value_len] == '\0' ); *value_r = (const char *)headers[i].value; return 1; } } *value_r = NULL; return 0; } void sieve_message_part_get_data (struct sieve_message_part *mpart, struct sieve_message_part_data *data, bool text) { i_zero(data); data->content_type = mpart->content_type; data->content_disposition = mpart->content_disposition; if ( !text ) { data->content = mpart->decoded_body; data->size = mpart->decoded_body_size; } else if ( mpart->children != NULL ) { data->content = ""; data->size = 0; } else { data->content = mpart->text_body; data->size = mpart->text_body_size; } } /* * Message body */ static void str_replace_nuls(string_t *str) { char *data = str_c_modifiable(str); unsigned int i, len = str_len(str); for (i = 0; i < len; i++) { if (data[i] == '\0') data[i] = ' '; } } static bool _is_wanted_content_type (const char * const *wanted_types, const char *content_type) ATTR_NULL(1) { const char *subtype; size_t type_len; if ( wanted_types == NULL ) return TRUE; subtype = strchr(content_type, '/'); type_len = ( subtype == NULL ? strlen(content_type) : (size_t)(subtype - content_type) ); i_assert( wanted_types != NULL ); for (; *wanted_types != NULL; wanted_types++) { const char *wanted_subtype; if (**wanted_types == '\0') { /* empty string matches everything */ return TRUE; } wanted_subtype = strchr(*wanted_types, '/'); if (wanted_subtype == NULL) { /* match only main type */ if (strlen(*wanted_types) == type_len && strncasecmp(*wanted_types, content_type, type_len) == 0) return TRUE; } else { /* match whole type/subtype */ if (strcasecmp(*wanted_types, content_type) == 0) return TRUE; } } return FALSE; } static bool sieve_message_body_get_return_parts (const struct sieve_runtime_env *renv, const char * const *wanted_types, bool extract_text) { struct sieve_message_context *msgctx = renv->msgctx; struct sieve_message_part *const *body_parts; unsigned int i, count; struct sieve_message_part_data *return_part; /* Check whether any body parts are cached already */ body_parts = array_get(&msgctx->cached_body_parts, &count); if ( count == 0 ) return FALSE; /* Clear result array */ array_clear(&msgctx->return_body_parts); /* Fill result array with requested content_types */ for (i = 0; i < count; i++) { if (!body_parts[i]->have_body) { /* Part has no body; according to RFC this MUST not match to anything and * therefore it is not included in the result. */ continue; } /* Skip content types that are not requested */ if (!_is_wanted_content_type (wanted_types, body_parts[i]->content_type)) continue; /* Add new item to the result */ return_part = array_append_space(&msgctx->return_body_parts); return_part->content_type = body_parts[i]->content_type; return_part->content_disposition = body_parts[i]->content_disposition; /* Depending on whether a decoded body part is requested, the appropriate * cache item is read. If it is missing, this function fails and the cache * needs to be completed by sieve_message_parts_add_missing(). */ if (extract_text) { if (body_parts[i]->text_body == NULL) return FALSE; return_part->content = body_parts[i]->text_body; return_part->size = body_parts[i]->text_body_size; } else { if (body_parts[i]->decoded_body == NULL) return FALSE; return_part->content = body_parts[i]->decoded_body; return_part->size = body_parts[i]->decoded_body_size; } } return TRUE; } static void sieve_message_part_save (const struct sieve_runtime_env *renv, buffer_t *buf, struct sieve_message_part *body_part, bool extract_text) { struct sieve_message_context *msgctx = renv->msgctx; pool_t pool = msgctx->context_pool; buffer_t *result_buf, *text_buf = NULL; char *part_data; size_t part_size; /* Extract text if requested */ result_buf = buf; if ( extract_text && body_part->children == NULL && !body_part->epilogue ) { if ( buf->used > 0 && mail_html2text_content_type_match (body_part->content_type) ) { struct mail_html2text *html2text; text_buf = buffer_create_dynamic(default_pool, 4096); /* Remove HTML markup */ html2text = mail_html2text_init(0); mail_html2text_more(html2text, buf->data, buf->used, text_buf); mail_html2text_deinit(&html2text); result_buf = text_buf; } } /* Add terminating NUL to the body part buffer */ buffer_append_c(result_buf, '\0'); /* Make copy of the buffer */ part_data = p_malloc(pool, result_buf->used); memcpy(part_data, result_buf->data, result_buf->used); part_size = result_buf->used - 1; /* Free text buffer if used */ if ( text_buf != NULL) buffer_free(&text_buf); /* Depending on whether the part is processed into text, store message * body in the appropriate cache location. */ if ( !extract_text ) { body_part->decoded_body = part_data; body_part->decoded_body_size = part_size; } else { body_part->text_body = part_data; body_part->text_body_size = part_size; } /* Clear buffer */ buffer_set_used_size(buf, 0); } static const char * _parse_content_type(const struct message_header_line *hdr) { struct rfc822_parser_context parser; string_t *content_type; /* Initialize parsing */ rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); (void)rfc822_skip_lwsp(&parser); /* Parse content type */ content_type = t_str_new(64); if (rfc822_parse_content_type(&parser, content_type) < 0) return ""; /* Content-type value must end here, otherwise it is invalid after all */ (void)rfc822_skip_lwsp(&parser); if ( parser.data != parser.end && *parser.data != ';' ) return ""; /* Success */ return str_c(content_type); } static const char * _parse_content_disposition(const struct message_header_line *hdr) { struct rfc822_parser_context parser; string_t *content_disp; /* Initialize parsing */ rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); (void)rfc822_skip_lwsp(&parser); /* Parse content type */ content_disp = t_str_new(64); if (rfc822_parse_mime_token(&parser, content_disp) < 0) return ""; /* Content-type value must end here, otherwise it is invalid after all */ (void)rfc822_skip_lwsp(&parser); if ( parser.data != parser.end && *parser.data != ';' ) return ""; /* Success */ return str_c(content_disp); } /* sieve_message_parts_add_missing(): * Add requested message body parts to the cache that are missing. */ static int sieve_message_parts_add_missing (const struct sieve_runtime_env *renv, const char *const *content_types, bool extract_text, bool iter_all) ATTR_NULL(2) { struct sieve_message_context *msgctx = renv->msgctx; pool_t pool = msgctx->context_pool; struct mail *mail = sieve_message_get_mail(renv->msgctx); struct message_parser_settings mparser_set = { .hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP, .flags = MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS, }; ARRAY(struct sieve_message_header) headers; struct sieve_message_part *body_part, *header_part, *last_part; struct message_parser_ctx *parser; struct message_decoder_context *decoder; struct message_block block, decoded; struct message_part *mparts, *prev_mpart = NULL; buffer_t *buf; struct istream *input; unsigned int idx = 0; bool save_body = FALSE, have_all; string_t *hdr_content = NULL; /* First check whether any are missing */ if ( !iter_all && sieve_message_body_get_return_parts (renv, content_types, extract_text) ) { /* Cache hit; all are present */ return SIEVE_EXEC_OK; } /* Get the message stream */ if ( mail_get_stream(mail, NULL, NULL, &input) < 0 ) { return sieve_runtime_mail_error(renv, mail, "failed to open input message"); } if (mail_get_parts(mail, &mparts) < 0) { return sieve_runtime_mail_error(renv, mail, "failed to parse input message parts"); } buf = buffer_create_dynamic(default_pool, 4096); body_part = header_part = last_part = NULL; if (iter_all) { t_array_init(&headers, 64); hdr_content = t_str_new(512); mparser_set.hdr_flags |= MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE; } else { i_zero(&headers); } /* Initialize body decoder */ decoder = message_decoder_init(NULL, 0); // FIXME: currently not tested with edit-mail. //parser = message_parser_init_from_parts(parts, input, // hparser_flags, mparser_flags); parser = message_parser_init(pool_datastack_create(), input, &mparser_set); while ( message_parser_parse_next_block(parser, &block) > 0 ) { struct sieve_message_part **body_part_idx; struct message_header_line *hdr = block.hdr; struct sieve_message_header *header; unsigned char *data; if ( block.part != prev_mpart ) { bool message_rfc822 = FALSE; /* Save previous body part */ if ( body_part != NULL ) { /* Treat message/rfc822 separately; headers become content */ if ( block.part->parent == prev_mpart && strcmp(body_part->content_type, "message/rfc822") == 0 ) { message_rfc822 = TRUE; } else { if ( save_body ) { sieve_message_part_save (renv, buf, body_part, extract_text); } } if ( iter_all && !array_is_created(&body_part->headers) && array_count(&headers) > 0 ) { p_array_init(&body_part->headers, pool, array_count(&headers)); array_copy(&body_part->headers.arr, 0, &headers.arr, 0, array_count(&headers)); } } /* Start processing next part */ body_part_idx = array_idx_get_space (&msgctx->cached_body_parts, idx); if ( *body_part_idx == NULL ) *body_part_idx = p_new(pool, struct sieve_message_part, 1); body_part = *body_part_idx; body_part->content_type = "text/plain"; if ( iter_all ) array_clear(&headers); /* Copy tree structure */ if ( block.part->context != NULL ) { struct sieve_message_part *epipart = (struct sieve_message_part *)block.part->context; i_assert(epipart != NULL); /* multipart epilogue */ body_part->content_type = epipart->content_type; body_part->have_body = TRUE; body_part->epilogue = TRUE; save_body = iter_all || _is_wanted_content_type (content_types, body_part->content_type); } else { struct sieve_message_part *parent = NULL; if ( block.part->parent != NULL ) { body_part->parent = parent = (struct sieve_message_part *) block.part->parent->context; } /* new part */ block.part->context = (void*)body_part; if ( last_part != NULL ) { i_assert( parent != NULL ); if ( last_part->parent == parent ) { last_part->next = body_part; } else if (parent->children == NULL) { parent->children = body_part; } else { struct sieve_message_part *child = parent->children; while (child->next != NULL && child != body_part) child = child->next; if (child != body_part) child->next = body_part; } } } last_part = body_part; /* If this is message/rfc822 content, retain the enveloping part for * storing headers as content. */ if ( message_rfc822 ) { i_assert(idx > 0); body_part_idx = array_idx_modifiable (&msgctx->cached_body_parts, idx-1); header_part = *body_part_idx; } else { header_part = NULL; } prev_mpart = block.part; idx++; } if ( hdr != NULL || block.size == 0 ) { enum { _HDR_CONTENT_TYPE, _HDR_CONTENT_DISPOSITION, _HDR_OTHER } hdr_field; /* Reading headers */ i_assert( body_part != NULL ); /* Decode block */ (void)message_decoder_decode_next_block (decoder, &block, &decoded); /* Check for end of headers */ if ( hdr == NULL ) { /* Save headers for message/rfc822 part */ if ( header_part != NULL ) { sieve_message_part_save (renv, buf, header_part, FALSE); header_part = NULL; } /* Save bodies only if we have a wanted content-type */ save_body = iter_all || _is_wanted_content_type (content_types, body_part->content_type); continue; } /* Encountered the empty line that indicates the end of the headers and * the start of the body */ if ( hdr->eoh ) { body_part->have_body = TRUE; continue; } else if ( header_part != NULL ) { /* Save message/rfc822 header as part content */ if ( hdr->continued ) { buffer_append(buf, hdr->value, hdr->value_len); } else { buffer_append(buf, hdr->name, hdr->name_len); buffer_append(buf, hdr->middle, hdr->middle_len); buffer_append(buf, hdr->value, hdr->value_len); } if ( !hdr->no_newline ) { buffer_append(buf, "\r\n", 2); } } if ( strcasecmp(hdr->name, "Content-Type" ) == 0 ) hdr_field = _HDR_CONTENT_TYPE; else if ( strcasecmp(hdr->name, "Content-Disposition" ) == 0 ) hdr_field = _HDR_CONTENT_DISPOSITION; else if ( iter_all && !array_is_created(&body_part->headers) ) hdr_field = _HDR_OTHER; else { /* Not interested in this header */ continue; } /* Header can have folding whitespace. Acquire the full value before * continuing */ if ( hdr->continues ) { hdr->use_full_value = TRUE; continue; } if ( iter_all && !array_is_created(&body_part->headers) ) { const unsigned char *value, *vp; size_t vlen; /* Add header */ header = array_append_space(&headers); header->name = p_strdup(pool, hdr->name); /* Trim end of field value (not done by parser) */ value = hdr->full_value; vp = value + hdr->full_value_len; while ( vp > value && (vp[-1] == '\t' || vp[-1] == ' ') ) vp--; vlen = (size_t)(vp - value); /* Decode MIME encoded-words. */ str_truncate(hdr_content, 0); message_header_decode_utf8 (value, vlen, hdr_content, NULL); if ( vlen != str_len(hdr_content) || strncmp(str_c(hdr_content), (const char *)value, vlen) != 0 ) { if ( strlen(str_c(hdr_content)) != str_len(hdr_content) ) { /* replace NULs with spaces */ str_replace_nuls(hdr_content); } /* store raw */ data = p_malloc(pool, vlen + 1); data[vlen] = '\0'; header->value = memcpy(data, value, vlen); header->value_len = vlen; /* store decoded */ data = p_malloc(pool, str_len(hdr_content) + 1); data[str_len(hdr_content)] = '\0'; header->utf8_value = memcpy(data, str_data(hdr_content), str_len(hdr_content)); header->utf8_value_len = str_len(hdr_content); } else { /* raw == decoded */ data = p_malloc(pool, vlen + 1); data[vlen] = '\0'; header->value = header->utf8_value = memcpy(data, value, vlen); header->value_len = header->utf8_value_len = vlen; } if ( hdr_field == _HDR_OTHER ) continue; } /* Parse the content type from the Content-type header */ T_BEGIN { switch ( hdr_field ) { case _HDR_CONTENT_TYPE: body_part->content_type = p_strdup(pool, _parse_content_type(block.hdr)); break; case _HDR_CONTENT_DISPOSITION: body_part->content_disposition = p_strdup(pool, _parse_content_disposition(block.hdr)); break; default: i_unreached(); } } T_END; continue; } /* Reading body */ if ( save_body ) { (void)message_decoder_decode_next_block (decoder, &block, &decoded); buffer_append(buf, decoded.data, decoded.size); } } /* even with an empty message there was at least the "end of headers" block, which set the body_part. */ i_assert( body_part != NULL ); /* Save last body part if necessary */ if ( header_part != NULL ) { sieve_message_part_save (renv, buf, header_part, FALSE); } else if ( save_body ) { sieve_message_part_save (renv, buf, body_part, extract_text); } if ( iter_all && !array_is_created(&body_part->headers) && array_count(&headers) > 0 ) { p_array_init(&body_part->headers, pool, array_count(&headers)); array_copy(&body_part->headers.arr, 0, &headers.arr, 0, array_count(&headers)); } /* Try to fill the return_body_parts array once more */ have_all = iter_all || sieve_message_body_get_return_parts (renv, content_types, extract_text); /* This time, failure is a bug */ i_assert(have_all); /* Cleanup */ (void)message_parser_deinit(&parser, &mparts); message_decoder_deinit(&decoder); buffer_free(&buf); /* Return status */ if ( input->stream_errno != 0 ) { sieve_runtime_critical(renv, NULL, "failed to read input message", "read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); return SIEVE_EXEC_TEMP_FAILURE; } return SIEVE_EXEC_OK; } int sieve_message_body_get_content (const struct sieve_runtime_env *renv, const char * const *content_types, struct sieve_message_part_data **parts_r) { struct sieve_message_context *msgctx = renv->msgctx; int status; T_BEGIN { /* Fill the return_body_parts array */ status = sieve_message_parts_add_missing (renv, content_types, FALSE, FALSE); } T_END; /* Check status */ if ( status <= 0 ) return status; /* Return the array of body items */ (void) array_append_space(&msgctx->return_body_parts); /* NULL-terminate */ *parts_r = array_idx_modifiable(&msgctx->return_body_parts, 0); return status; } int sieve_message_body_get_text (const struct sieve_runtime_env *renv, struct sieve_message_part_data **parts_r) { static const char * const _text_content_types[] = { "application/xhtml+xml", "text", NULL }; struct sieve_message_context *msgctx = renv->msgctx; int status; /* We currently only support extracting plain text from: - text/html -> HTML - application/xhtml+xml -> XHTML Other text types are read as is. Any non-text types are skipped. */ T_BEGIN { /* Fill the return_body_parts array */ status = sieve_message_parts_add_missing (renv, _text_content_types, TRUE, FALSE); } T_END; /* Check status */ if ( status <= 0 ) return status; /* Return the array of body items */ (void) array_append_space(&msgctx->return_body_parts); /* NULL-terminate */ *parts_r = array_idx_modifiable(&msgctx->return_body_parts, 0); return status; } int sieve_message_body_get_raw (const struct sieve_runtime_env *renv, struct sieve_message_part_data **parts_r) { struct sieve_message_context *msgctx = renv->msgctx; struct sieve_message_part_data *return_part; buffer_t *buf; if ( msgctx->raw_body == NULL ) { struct mail *mail = sieve_message_get_mail(renv->msgctx); struct istream *input; struct message_size hdr_size, body_size; const unsigned char *data; size_t size; int ret; msgctx->raw_body = buf = buffer_create_dynamic (msgctx->context_pool, 1024*64); /* Get stream for message */ if ( mail_get_stream(mail, &hdr_size, &body_size, &input) < 0 ) { return sieve_runtime_mail_error(renv, mail, "failed to open input message"); } /* Skip stream to beginning of body */ i_stream_skip(input, hdr_size.physical_size); /* Read raw message body */ while ( (ret=i_stream_read_more(input, &data, &size)) > 0 ) { buffer_append(buf, data, size); i_stream_skip(input, size); } if ( ret < 0 && input->stream_errno != 0 ) { sieve_runtime_critical(renv, NULL, "failed to read input message", "read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); return SIEVE_EXEC_TEMP_FAILURE; } /* Add terminating NUL to the body part buffer */ buffer_append_c(buf, '\0'); } else { buf = msgctx->raw_body; } /* Clear result array */ array_clear(&msgctx->return_body_parts); if ( buf->used > 1 ) { const char *data = (const char *)buf->data; size_t size = buf->used - 1; i_assert( data[size] == '\0' ); /* Add single item to the result */ return_part = array_append_space(&msgctx->return_body_parts); return_part->content = data; return_part->size = size; } /* Return the array of body items */ (void) array_append_space(&msgctx->return_body_parts); /* NULL-terminate */ *parts_r = array_idx_modifiable(&msgctx->return_body_parts, 0); return SIEVE_EXEC_OK; } /* * Message part iterator */ int sieve_message_part_iter_init (struct sieve_message_part_iter *iter, const struct sieve_runtime_env *renv) { struct sieve_message_context *msgctx = renv->msgctx; struct sieve_message_part *const *parts; unsigned int count; int status; T_BEGIN { /* Fill the return_body_parts array */ status = sieve_message_parts_add_missing (renv, NULL, TRUE, TRUE); } T_END; /* Check status */ if ( status <= 0 ) return status; i_zero(iter); iter->renv = renv; iter->index = 0; iter->offset = 0; parts = array_get(&msgctx->cached_body_parts, &count); if (count == 0) iter->root = NULL; else iter->root = parts[0]; return SIEVE_EXEC_OK; } void sieve_message_part_iter_subtree(struct sieve_message_part_iter *iter, struct sieve_message_part_iter *subtree) { const struct sieve_runtime_env *renv = iter->renv; struct sieve_message_context *msgctx = renv->msgctx; struct sieve_message_part *const *parts; unsigned int count; *subtree = *iter; parts = array_get(&msgctx->cached_body_parts, &count); if ( subtree->index >= count) subtree->root = NULL; else subtree->root = parts[subtree->index]; subtree->offset = subtree->index; } void sieve_message_part_iter_children(struct sieve_message_part_iter *iter, struct sieve_message_part_iter *child) { const struct sieve_runtime_env *renv = iter->renv; struct sieve_message_context *msgctx = renv->msgctx; struct sieve_message_part *const *parts; unsigned int count; *child = *iter; parts = array_get(&msgctx->cached_body_parts, &count); if ( (child->index+1) >= count || parts[child->index]->children == NULL) child->root = NULL; else child->root = parts[child->index++]; child->offset = child->index; } struct sieve_message_part *sieve_message_part_iter_current (struct sieve_message_part_iter *iter) { const struct sieve_runtime_env *renv = iter->renv; struct sieve_message_context *msgctx = renv->msgctx; struct sieve_message_part *const *parts; unsigned int count; if ( iter->root == NULL ) return NULL; parts = array_get(&msgctx->cached_body_parts, &count); if ( iter->index >= count ) return NULL; do { if ( parts[iter->index] == iter->root->next ) return NULL; if ( parts[iter->index] == iter->root->parent ) return NULL; } while ( parts[iter->index]->epilogue && ++iter->index < count ); if ( iter->index >= count ) return NULL; return parts[iter->index]; } struct sieve_message_part *sieve_message_part_iter_next (struct sieve_message_part_iter *iter) { const struct sieve_runtime_env *renv = iter->renv; struct sieve_message_context *msgctx = renv->msgctx; if ( iter->index >= array_count(&msgctx->cached_body_parts) ) return NULL; iter->index++; return sieve_message_part_iter_current(iter); } void sieve_message_part_iter_reset (struct sieve_message_part_iter *iter) { iter->index = iter->offset; } /* * MIME header list */ /* Forward declarations */ static int sieve_mime_header_list_next_item (struct sieve_header_list *_hdrlist, const char **name_r, string_t **value_r); static int sieve_mime_header_list_next_value (struct sieve_stringlist *_strlist, string_t **value_r); static void sieve_mime_header_list_reset (struct sieve_stringlist *_strlist); /* Header list object */ struct sieve_mime_header_list { struct sieve_header_list hdrlist; struct sieve_stringlist *field_names; struct sieve_message_part_iter part_iter; const char *header_name; const struct sieve_message_header *headers; unsigned int headers_index, headers_count; bool mime_decode:1; bool children:1; }; struct sieve_header_list *sieve_mime_header_list_create (const struct sieve_runtime_env *renv, struct sieve_stringlist *field_names, struct sieve_message_part_iter *part_iter, bool mime_decode, bool children) { struct sieve_mime_header_list *hdrlist; hdrlist = t_new(struct sieve_mime_header_list, 1); hdrlist->hdrlist.strlist.runenv = renv; hdrlist->hdrlist.strlist.exec_status = SIEVE_EXEC_OK; hdrlist->hdrlist.strlist.next_item = sieve_mime_header_list_next_value; hdrlist->hdrlist.strlist.reset = sieve_mime_header_list_reset; hdrlist->hdrlist.next_item = sieve_mime_header_list_next_item; hdrlist->field_names = field_names; hdrlist->mime_decode = mime_decode; hdrlist->children = children; sieve_message_part_iter_subtree(part_iter, &hdrlist->part_iter); return &hdrlist->hdrlist; } /* MIME list implementation */ static void sieve_mime_header_list_next_name (struct sieve_mime_header_list *hdrlist) { struct sieve_message_part *mpart; sieve_message_part_iter_reset(&hdrlist->part_iter); mpart = sieve_message_part_iter_current(&hdrlist->part_iter); if ( mpart != NULL && array_is_created(&mpart->headers) ) { hdrlist->headers = array_get (&mpart->headers, &hdrlist->headers_count); hdrlist->headers_index = 0; } } static int sieve_mime_header_list_next_item (struct sieve_header_list *_hdrlist, const char **name_r, string_t **value_r) { struct sieve_mime_header_list *hdrlist = (struct sieve_mime_header_list *) _hdrlist; const struct sieve_runtime_env *renv = _hdrlist->strlist.runenv; if ( name_r != NULL ) *name_r = NULL; *value_r = NULL; for (;;) { /* Check for end of current header list */ if ( hdrlist->headers_count == 0 || hdrlist->headers_index >= hdrlist->headers_count ) { hdrlist->headers_count = 0; hdrlist->headers_index = 0; hdrlist->headers = NULL; } /* Fetch more headers */ while ( hdrlist->headers_count == 0 ) { string_t *hdr_item = NULL; int ret; if ( hdrlist->header_name != NULL && hdrlist->children ) { struct sieve_message_part *mpart; mpart = sieve_message_part_iter_next(&hdrlist->part_iter); if ( mpart != NULL && array_is_created(&mpart->headers) ) { hdrlist->headers = array_get (&mpart->headers, &hdrlist->headers_count); hdrlist->headers_index = 0; } if ( hdrlist->headers_count > 0 ) { if ( _hdrlist->strlist.trace ) { sieve_runtime_trace(renv, 0, "moving to next message part"); } break; } } /* Read next header name from source list */ if ( (ret=sieve_stringlist_next_item (hdrlist->field_names, &hdr_item)) <= 0 ) return ret; hdrlist->header_name = str_c(hdr_item); if ( _hdrlist->strlist.trace ) { sieve_runtime_trace(renv, 0, "extracting `%s' headers from message part", str_sanitize(str_c(hdr_item), 80)); } sieve_mime_header_list_next_name(hdrlist); } for ( ; hdrlist->headers_index < hdrlist->headers_count; hdrlist->headers_index++ ) { const struct sieve_message_header *header = &hdrlist->headers[hdrlist->headers_index]; if ( strcasecmp(header->name, hdrlist->header_name) == 0 ) { if ( name_r != NULL ) *name_r = hdrlist->header_name; if ( hdrlist->mime_decode ) { *value_r = t_str_new_const ((const char *)header->utf8_value, header->utf8_value_len); } else { *value_r = t_str_new_const ((const char *)header->value, header->value_len); } hdrlist->headers_index++; return 1; } } } i_unreached(); return -1; } static int sieve_mime_header_list_next_value (struct sieve_stringlist *_strlist, string_t **value_r) { struct sieve_header_list *hdrlist = (struct sieve_header_list *) _strlist; return sieve_mime_header_list_next_item (hdrlist, NULL, value_r); } static void sieve_mime_header_list_reset (struct sieve_stringlist *strlist) { struct sieve_mime_header_list *hdrlist = (struct sieve_mime_header_list *) strlist; sieve_stringlist_reset(hdrlist->field_names); hdrlist->header_name = NULL; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-lexer.c0000644000000000000000000005276014103200126022734 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "compat.h" #include "str.h" #include "str-sanitize.h" #include "istream.h" #include "sieve-common.h" #include "sieve-limits.h" #include "sieve-error.h" #include "sieve-script.h" #include "sieve-lexer.h" #include #include #include #include #include #include /* * Useful macros */ #define DIGIT_VAL(c) (c - '0') /* * Lexer object */ struct sieve_lexical_scanner { pool_t pool; struct sieve_instance *svinst; struct sieve_script *script; struct istream *input; struct sieve_error_handler *ehandler; /* Currently scanned data */ const unsigned char *buffer; size_t buffer_size; size_t buffer_pos; struct sieve_lexer lexer; int current_line; }; const struct sieve_lexer * sieve_lexer_create(struct sieve_script *script, struct sieve_error_handler *ehandler, enum sieve_error *error_r) { struct sieve_lexical_scanner *scanner; struct sieve_instance *svinst = sieve_script_svinst(script); struct istream *stream; const struct stat *st; /* Open script as stream */ if (sieve_script_get_stream(script, &stream, error_r) < 0) return NULL; /* Check script size */ if (i_stream_stat(stream, TRUE, &st) >= 0 && st->st_size > 0 && svinst->max_script_size > 0 && (uoff_t)st->st_size > svinst->max_script_size) { sieve_error(ehandler, sieve_script_name(script), "sieve script is too large (max %zu bytes)", svinst->max_script_size); if (error_r != NULL) *error_r = SIEVE_ERROR_NOT_POSSIBLE; return NULL; } scanner = i_new(struct sieve_lexical_scanner, 1); scanner->lexer.scanner = scanner; scanner->ehandler = ehandler; sieve_error_handler_ref(ehandler); scanner->input = stream; i_stream_ref(scanner->input); scanner->script = script; sieve_script_ref(script); scanner->buffer = NULL; scanner->buffer_size = 0; scanner->buffer_pos = 0; scanner->lexer.token_type = STT_NONE; scanner->lexer.token_str_value = str_new(default_pool, 256); scanner->lexer.token_int_value = 0; scanner->lexer.token_line = 1; scanner->current_line = 1; return &scanner->lexer; } void sieve_lexer_free(const struct sieve_lexer **_lexer) { const struct sieve_lexer *lexer = *_lexer; struct sieve_lexical_scanner *scanner = lexer->scanner; i_stream_unref(&scanner->input); sieve_script_unref(&scanner->script); sieve_error_handler_unref(&scanner->ehandler); str_free(&scanner->lexer.token_str_value); i_free(scanner); *_lexer = NULL; } /* * Internal error handling */ inline static void ATTR_FORMAT(4, 5) sieve_lexer_error(const struct sieve_lexer *lexer, const char *csrc_filename, unsigned int csrc_linenum, const char *fmt, ...) { struct sieve_lexical_scanner *scanner = lexer->scanner; struct sieve_error_params params = { .log_type = LOG_TYPE_ERROR, .csrc = { .filename = csrc_filename, .linenum = csrc_linenum, }, }; va_list args; va_start(args, fmt); T_BEGIN { params.location = sieve_error_script_location(scanner->script, scanner->current_line); sieve_logv(scanner->ehandler, ¶ms, fmt, args); } T_END; va_end(args); } #define sieve_lexer_error(lexer, ...) \ sieve_lexer_error(lexer, __FILE__, __LINE__, __VA_ARGS__) inline static void ATTR_FORMAT(4, 5) sieve_lexer_warning(const struct sieve_lexer *lexer, const char *csrc_filename, unsigned int csrc_linenum, const char *fmt, ...) { struct sieve_lexical_scanner *scanner = lexer->scanner; struct sieve_error_params params = { .log_type = LOG_TYPE_WARNING, .csrc = { .filename = csrc_filename, .linenum = csrc_linenum, }, }; va_list args; va_start(args, fmt); T_BEGIN { params.location = sieve_error_script_location(scanner->script, scanner->current_line); sieve_logv(scanner->ehandler, ¶ms, fmt, args); } T_END; va_end(args); } #define sieve_lexer_warning(lexer, ...) \ sieve_lexer_warning(lexer, __FILE__, __LINE__, __VA_ARGS__) const char *sieve_lexer_token_description(const struct sieve_lexer *lexer) { switch (lexer->token_type) { case STT_NONE: return "no token (bug)"; case STT_WHITESPACE: return "whitespace (bug)"; case STT_EOF: return "end of file"; case STT_NUMBER: return "number"; case STT_IDENTIFIER: return "identifier"; case STT_TAG: return "tag"; case STT_STRING: return "string"; case STT_RBRACKET: return "')'"; case STT_LBRACKET: return "'('"; case STT_RCURLY: return "'}'"; case STT_LCURLY: return "'{'"; case STT_RSQUARE: return "']'"; case STT_LSQUARE: return "'['"; case STT_SEMICOLON: return "';'"; case STT_COMMA: return "','"; case STT_SLASH: return "'/'"; case STT_COLON: return "':'"; case STT_GARBAGE: return "unknown characters"; case STT_ERROR: return "error token (bug)"; } return "unknown token (bug)"; } /* * Debug */ void sieve_lexer_token_print(const struct sieve_lexer *lexer) { switch (lexer->token_type) { case STT_NONE: printf("??NONE?? "); break; case STT_WHITESPACE: printf("??WHITESPACE?? "); break; case STT_EOF: printf("EOF\n"); break; case STT_NUMBER: printf("NUMBER "); break; case STT_IDENTIFIER: printf("IDENTIFIER "); break; case STT_TAG: printf("TAG "); break; case STT_STRING: printf("STRING "); break; case STT_RBRACKET: printf(") "); break; case STT_LBRACKET: printf("( "); break; case STT_RCURLY: printf("}\n"); break; case STT_LCURLY: printf("{\n"); break; case STT_RSQUARE: printf("] "); break; case STT_LSQUARE: printf("[ "); break; case STT_SEMICOLON: printf(";\n"); break; case STT_COMMA: printf(", "); break; case STT_SLASH: printf("/ "); break; case STT_COLON: printf(": "); break; case STT_GARBAGE: printf(">>GARBAGE<<"); break; case STT_ERROR: printf(">>ERROR<<"); break; default: printf("UNKNOWN "); break; } } /* * Lexical scanning */ static void sieve_lexer_shift(struct sieve_lexical_scanner *scanner) { if (scanner->buffer_size > 0 && scanner->buffer[scanner->buffer_pos] == '\n') scanner->current_line++; if (scanner->buffer_size > 0 && scanner->buffer_pos + 1 < scanner->buffer_size) scanner->buffer_pos++; else { if (scanner->buffer_size > 0) i_stream_skip(scanner->input, scanner->buffer_size); scanner->buffer = i_stream_get_data(scanner->input, &scanner->buffer_size); if (scanner->buffer_size == 0 && i_stream_read(scanner->input) > 0) { scanner->buffer = i_stream_get_data( scanner->input, &scanner->buffer_size); } scanner->buffer_pos = 0; } } static inline int sieve_lexer_curchar(struct sieve_lexical_scanner *scanner) { if (scanner->buffer_size == 0) return -1; return scanner->buffer[scanner->buffer_pos]; } static inline const char *_char_sanitize(int ch) { if (ch > 31 && ch < 127) return t_strdup_printf("'%c'", ch); return t_strdup_printf("0x%02x", ch); } static bool sieve_lexer_scan_number(struct sieve_lexical_scanner *scanner) { struct sieve_lexer *lexer = &scanner->lexer; uintmax_t value; string_t *str; bool overflow = FALSE; str_truncate(lexer->token_str_value,0); str = lexer->token_str_value; while (i_isdigit(sieve_lexer_curchar(scanner))) { str_append_c(str, sieve_lexer_curchar(scanner)); sieve_lexer_shift(scanner); } if (str_to_uintmax(str_c(str), &value) < 0 || value > (sieve_number_t)-1) { overflow = TRUE; } else { switch (sieve_lexer_curchar(scanner)) { case 'k': case 'K': /* Kilo */ if (value > (SIEVE_MAX_NUMBER >> 10)) overflow = TRUE; else value = value << 10; sieve_lexer_shift(scanner); break; case 'm': case 'M': /* Mega */ if (value > (SIEVE_MAX_NUMBER >> 20)) overflow = TRUE; else value = value << 20; sieve_lexer_shift(scanner); break; case 'g': case 'G': /* Giga */ if (value > (SIEVE_MAX_NUMBER >> 30)) overflow = TRUE; else value = value << 30; sieve_lexer_shift(scanner); break; default: /* Next token */ break; } } /* Check for integer overflow */ if (overflow) { sieve_lexer_error(lexer, "number exceeds integer limits (max %llu)", (long long) SIEVE_MAX_NUMBER); lexer->token_type = STT_ERROR; return FALSE; } lexer->token_type = STT_NUMBER; lexer->token_int_value = (sieve_number_t)value; return TRUE; } static bool sieve_lexer_scan_hash_comment(struct sieve_lexical_scanner *scanner) { struct sieve_lexer *lexer = &scanner->lexer; while (sieve_lexer_curchar(scanner) != '\n') { switch(sieve_lexer_curchar(scanner)) { case -1: if (!scanner->input->eof) { lexer->token_type = STT_ERROR; return FALSE; } sieve_lexer_warning(lexer, "no newline (CRLF) at end of hash comment at end of file"); lexer->token_type = STT_WHITESPACE; return TRUE; case '\0': sieve_lexer_error(lexer, "encountered NUL character in hash comment"); lexer->token_type = STT_ERROR; return FALSE; default: break; } /* Stray CR is ignored */ sieve_lexer_shift(scanner); } sieve_lexer_shift(scanner); lexer->token_type = STT_WHITESPACE; return TRUE; } /* sieve_lexer_scan_raw_token: * Scans valid tokens and whitespace */ static bool sieve_lexer_scan_raw_token(struct sieve_lexical_scanner *scanner) { struct sieve_lexer *lexer = &scanner->lexer; string_t *str; int ret; /* Read first character */ if (lexer->token_type == STT_NONE) { if ((ret = i_stream_read(scanner->input)) < 0) { i_assert(ret != -2); if (!scanner->input->eof) { lexer->token_type = STT_ERROR; return FALSE; } } sieve_lexer_shift(scanner); } lexer->token_line = scanner->current_line; switch (sieve_lexer_curchar(scanner)) { /* whitespace */ // hash-comment = ( "#" *CHAR-NOT-CRLF CRLF ) case '#': sieve_lexer_shift(scanner); return sieve_lexer_scan_hash_comment(scanner); // bracket-comment = "/*" *(CHAR-NOT-STAR / ("*" CHAR-NOT-SLASH)) "*/" // ;; No */ allowed inside a comment. // ;; (No * is allowed unless it is the last character, // ;; or unless it is followed by a character that isn't a // ;; slash.) case '/': sieve_lexer_shift(scanner); if (sieve_lexer_curchar(scanner) == '*') { sieve_lexer_shift(scanner); while (TRUE) { switch (sieve_lexer_curchar(scanner)) { case -1: if (scanner->input->eof) { sieve_lexer_error(lexer, "end of file before end of bracket comment " "('/* ... */') " "started at line %d", lexer->token_line); } lexer->token_type = STT_ERROR; return FALSE; case '*': sieve_lexer_shift(scanner); if (sieve_lexer_curchar(scanner) == '/') { sieve_lexer_shift(scanner); lexer->token_type = STT_WHITESPACE; return TRUE; } else if (sieve_lexer_curchar(scanner) == -1) { sieve_lexer_error(lexer, "end of file before end of bracket comment " "('/* ... */') " "started at line %d", lexer->token_line); lexer->token_type = STT_ERROR; return FALSE; } break; case '\0': sieve_lexer_error(lexer, "encountered NUL character in bracket comment"); lexer->token_type = STT_ERROR; return FALSE; default: sieve_lexer_shift(scanner); } } i_unreached(); return FALSE; } lexer->token_type = STT_SLASH; return TRUE; // comment = bracket-comment / hash-comment // white-space = 1*(SP / CRLF / HTAB) / comment case '\t': case '\r': case '\n': case ' ': sieve_lexer_shift(scanner); while (sieve_lexer_curchar(scanner) == '\t' || sieve_lexer_curchar(scanner) == '\r' || sieve_lexer_curchar(scanner) == '\n' || sieve_lexer_curchar(scanner) == ' ') { sieve_lexer_shift(scanner); } lexer->token_type = STT_WHITESPACE; return TRUE; /* quoted-string */ case '"': sieve_lexer_shift(scanner); str_truncate(lexer->token_str_value, 0); str = lexer->token_str_value; while (sieve_lexer_curchar(scanner) != '"') { if (sieve_lexer_curchar(scanner) == '\\') sieve_lexer_shift(scanner); switch (sieve_lexer_curchar(scanner)) { /* End of file */ case -1: if (scanner->input->eof) { sieve_lexer_error(lexer, "end of file before end of quoted string " "started at line %d", lexer->token_line); } lexer->token_type = STT_ERROR; return FALSE; /* NUL character */ case '\0': sieve_lexer_error(lexer, "encountered NUL character in quoted string " "started at line %d", lexer->token_line); lexer->token_type = STT_ERROR; return FALSE; /* CR .. check for LF */ case '\r': sieve_lexer_shift(scanner); if (sieve_lexer_curchar(scanner) != '\n') { sieve_lexer_error(lexer, "found stray carriage-return (CR) character " "in quoted string started at line %d", lexer->token_line); lexer->token_type = STT_ERROR; return FALSE; } if (str_len(str) <= SIEVE_MAX_STRING_LEN) str_append(str, "\r\n"); break; /* Loose LF is allowed (non-standard) and converted to CRLF */ case '\n': if (str_len(str) <= SIEVE_MAX_STRING_LEN) str_append(str, "\r\n"); break; /* Other characters */ default: if (str_len(str) <= SIEVE_MAX_STRING_LEN) str_append_c(str, sieve_lexer_curchar(scanner)); } sieve_lexer_shift(scanner); } sieve_lexer_shift(scanner); if (str_len(str) > SIEVE_MAX_STRING_LEN) { sieve_lexer_error(lexer, "quoted string started at line %d is too long " "(longer than %llu bytes)", lexer->token_line, (long long) SIEVE_MAX_STRING_LEN); lexer->token_type = STT_ERROR; return FALSE; } lexer->token_type = STT_STRING; return TRUE; /* single character tokens */ case ']': sieve_lexer_shift(scanner); lexer->token_type = STT_RSQUARE; return TRUE; case '[': sieve_lexer_shift(scanner); lexer->token_type = STT_LSQUARE; return TRUE; case '}': sieve_lexer_shift(scanner); lexer->token_type = STT_RCURLY; return TRUE; case '{': sieve_lexer_shift(scanner); lexer->token_type = STT_LCURLY; return TRUE; case ')': sieve_lexer_shift(scanner); lexer->token_type = STT_RBRACKET; return TRUE; case '(': sieve_lexer_shift(scanner); lexer->token_type = STT_LBRACKET; return TRUE; case ';': sieve_lexer_shift(scanner); lexer->token_type = STT_SEMICOLON; return TRUE; case ',': sieve_lexer_shift(scanner); lexer->token_type = STT_COMMA; return TRUE; /* EOF */ case -1: if (!scanner->input->eof) { lexer->token_type = STT_ERROR; return FALSE; } lexer->token_type = STT_EOF; return TRUE; default: /* number */ if (i_isdigit(sieve_lexer_curchar(scanner))) { return sieve_lexer_scan_number(scanner); /* identifier / tag */ } else if (i_isalpha(sieve_lexer_curchar(scanner)) || sieve_lexer_curchar(scanner) == '_' || sieve_lexer_curchar(scanner) == ':') { enum sieve_token_type type = STT_IDENTIFIER; str_truncate(lexer->token_str_value,0); str = lexer->token_str_value; /* If it starts with a ':' it is a tag and not an identifier */ if (sieve_lexer_curchar(scanner) == ':') { sieve_lexer_shift(scanner); // discard colon type = STT_TAG; /* First character still can't be a DIGIT */ if (i_isalpha(sieve_lexer_curchar(scanner)) || sieve_lexer_curchar(scanner) == '_') { str_append_c(str, sieve_lexer_curchar(scanner)); sieve_lexer_shift(scanner); } else { /* Hmm, otherwise it is just a spurious colon */ lexer->token_type = STT_COLON; return TRUE; } } else { str_append_c(str, sieve_lexer_curchar(scanner)); sieve_lexer_shift(scanner); } /* Scan the rest of the identifier */ while (i_isalnum(sieve_lexer_curchar(scanner)) || sieve_lexer_curchar(scanner) == '_') { if (str_len(str) <= SIEVE_MAX_IDENTIFIER_LEN) { str_append_c(str, sieve_lexer_curchar(scanner)); } sieve_lexer_shift(scanner); } /* Is this in fact a multiline text string ? */ if (sieve_lexer_curchar(scanner) == ':' && type == STT_IDENTIFIER && str_len(str) == 4 && strncasecmp(str_c(str), "text", 4) == 0) { sieve_lexer_shift(scanner); // discard colon /* Discard SP and HTAB whitespace */ while (sieve_lexer_curchar(scanner) == ' ' || sieve_lexer_curchar(scanner) == '\t') sieve_lexer_shift(scanner); /* Discard hash comment or handle single CRLF */ if (sieve_lexer_curchar(scanner) == '\r') sieve_lexer_shift(scanner); switch (sieve_lexer_curchar(scanner)) { case '#': if (!sieve_lexer_scan_hash_comment(scanner)) return FALSE; if (scanner->input->eof) { sieve_lexer_error(lexer, "end of file before end of multi-line string"); lexer->token_type = STT_ERROR; return FALSE; } else if (scanner->input->stream_errno != 0) { lexer->token_type = STT_ERROR; return FALSE; } break; case '\n': sieve_lexer_shift(scanner); break; case -1: if (scanner->input->eof) { sieve_lexer_error(lexer, "end of file before end of multi-line string"); } lexer->token_type = STT_ERROR; return FALSE; default: sieve_lexer_error(lexer, "invalid character %s after 'text:' in multiline string", _char_sanitize(sieve_lexer_curchar(scanner))); lexer->token_type = STT_ERROR; return FALSE; } /* Start over */ str_truncate(str, 0); /* Parse literal lines */ while (TRUE) { bool cr_shifted = FALSE; /* Remove dot-stuffing or detect end of text */ if (sieve_lexer_curchar(scanner) == '.') { sieve_lexer_shift(scanner); /* Check for CR.. */ if (sieve_lexer_curchar(scanner) == '\r') { sieve_lexer_shift(scanner); cr_shifted = TRUE; } /* ..LF */ if (sieve_lexer_curchar(scanner) == '\n') { sieve_lexer_shift(scanner); /* End of multi-line string */ /* Check whether length limit was violated */ if (str_len(str) > SIEVE_MAX_STRING_LEN) { sieve_lexer_error(lexer, "multi-line string started at line %d is too long " "(longer than %llu bytes)", lexer->token_line, (long long) SIEVE_MAX_STRING_LEN); lexer->token_type = STT_ERROR; return FALSE; } lexer->token_type = STT_STRING; return TRUE; } else if (cr_shifted) { /* Seen CR, but no LF */ if (sieve_lexer_curchar(scanner) != -1 || !scanner->input->eof) { sieve_lexer_error(lexer, "found stray carriage-return (CR) character " "in multi-line string started at line %d", lexer->token_line); } lexer->token_type = STT_ERROR; return FALSE; } /* Handle dot-stuffing */ if (str_len(str) <= SIEVE_MAX_STRING_LEN) str_append_c(str, '.'); if (sieve_lexer_curchar(scanner) == '.') sieve_lexer_shift(scanner); } /* Scan the rest of the line */ while (sieve_lexer_curchar(scanner) != '\n' && sieve_lexer_curchar(scanner) != '\r') { switch (sieve_lexer_curchar(scanner)) { case -1: if (scanner->input->eof) { sieve_lexer_error(lexer, "end of file before end of multi-line string"); } lexer->token_type = STT_ERROR; return FALSE; case '\0': sieve_lexer_error(lexer, "encountered NUL character in quoted string " "started at line %d", lexer->token_line); lexer->token_type = STT_ERROR; return FALSE; default: if (str_len(str) <= SIEVE_MAX_STRING_LEN) str_append_c(str, sieve_lexer_curchar(scanner)); } sieve_lexer_shift(scanner); } /* If exited loop due to CR, skip it */ if (sieve_lexer_curchar(scanner) == '\r') sieve_lexer_shift(scanner); /* Now we must see an LF */ if (sieve_lexer_curchar(scanner) != '\n') { if (sieve_lexer_curchar(scanner) != -1 || !scanner->input->eof) { sieve_lexer_error(lexer, "found stray carriage-return (CR) character " "in multi-line string started at line %d", lexer->token_line); } lexer->token_type = STT_ERROR; return FALSE; } if (str_len(str) <= SIEVE_MAX_STRING_LEN) str_append(str, "\r\n"); sieve_lexer_shift(scanner); } i_unreached(); lexer->token_type = STT_ERROR; return FALSE; } if (str_len(str) > SIEVE_MAX_IDENTIFIER_LEN) { sieve_lexer_error(lexer, "encountered impossibly long %s%s'", (type == STT_TAG ? "tag identifier ':" : "identifier '"), str_sanitize(str_c(str), SIEVE_MAX_IDENTIFIER_LEN)); lexer->token_type = STT_ERROR; return FALSE; } lexer->token_type = type; return TRUE; } /* Error (unknown character and EOF handled already) */ if (lexer->token_type != STT_GARBAGE) { sieve_lexer_error(lexer, "unexpected character(s) starting with %s", _char_sanitize(sieve_lexer_curchar(scanner))); } sieve_lexer_shift(scanner); lexer->token_type = STT_GARBAGE; return FALSE; } } void sieve_lexer_skip_token(const struct sieve_lexer *lexer) { /* Scan token while skipping whitespace */ do { struct sieve_lexical_scanner *scanner = lexer->scanner; if (!sieve_lexer_scan_raw_token(scanner)) { if (!scanner->input->eof && scanner->input->stream_errno != 0) { sieve_critical(scanner->svinst, scanner->ehandler, sieve_error_script_location(scanner->script, scanner->current_line), "error reading script", "error reading script during lexical analysis: %s", i_stream_get_error(scanner->input)); } return; } } while (lexer->token_type == STT_WHITESPACE); } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/cmd-keep.c0000644000000000000000000000442314103200126022162 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "sieve-common.h" #include "sieve-commands.h" #include "sieve-code.h" #include "sieve-dump.h" #include "sieve-message.h" #include "sieve-actions.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-interpreter.h" #include "sieve-result.h" /* * Keep command * * Syntax: * keep */ static bool cmd_keep_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); const struct sieve_command_def cmd_keep = { .identifier = "keep", .type = SCT_COMMAND, .positional_args = 0, .subtests = 0, .block_allowed = FALSE, .block_required = FALSE, .generate = cmd_keep_generate }; /* * Keep operation */ static bool cmd_keep_operation_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address); static int cmd_keep_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address); const struct sieve_operation_def cmd_keep_operation = { .mnemonic = "KEEP", .code = SIEVE_OPERATION_KEEP, .dump = cmd_keep_operation_dump, .execute = cmd_keep_operation_execute }; /* * Code generation */ static bool cmd_keep_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) { /* Emit opcode */ sieve_operation_emit(cgenv->sblock, NULL, &cmd_keep_operation); /* Generate arguments */ return sieve_generate_arguments(cgenv, cmd, NULL); } /* * Code dump */ static bool cmd_keep_operation_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address) { sieve_code_dumpf(denv, "KEEP"); sieve_code_descend(denv); return ( sieve_action_opr_optional_dump(denv, address, NULL) == 0 ); } /* * Interpretation */ static int cmd_keep_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address) { struct sieve_side_effects_list *slist = NULL; int ret = 0; /* * Read data */ /* Optional operands (side effects only) */ if ( sieve_action_opr_optional_read(renv, address, NULL, &ret, &slist) != 0 ) return ret; /* * Perform operation */ sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "keep action; store message in default mailbox"); /* Add keep action to result. */ if ( sieve_result_add_keep(renv, slist) < 0 ) return SIEVE_EXEC_FAILURE; return SIEVE_EXEC_OK; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-types.h0000644000000000000000000001577414103200126022772 0ustar00rootroot00000000000000#ifndef SIEVE_TYPES_H #define SIEVE_TYPES_H #include "lib.h" #include "smtp-address.h" #include /* * Forward declarations */ struct smtp_params_mail; struct smtp_params_rcpt; struct sieve_instance; struct sieve_callbacks; struct sieve_script; struct sieve_binary; struct sieve_message_data; struct sieve_script_env; struct sieve_exec_status; struct sieve_trace_log; /* * System environment */ enum sieve_flag { /* Relative paths are resolved to HOME */ SIEVE_FLAG_HOME_RELATIVE = (1 << 0) }; /* Sieve evaluation can be performed at various different points as messages are processed. */ enum sieve_env_location { /* Unknown */ SIEVE_ENV_LOCATION_UNKNOWN = 0, /* "MDA" - evaluation is being performed by a Mail Delivery Agent */ SIEVE_ENV_LOCATION_MDA, /* "MTA" - the Sieve script is being evaluated by a Message Transfer Agent */ SIEVE_ENV_LOCATION_MTA, /* "MS" - evaluation is being performed by a Message Store */ SIEVE_ENV_LOCATION_MS }; /* The point relative to final delivery where the Sieve script is being evaluated. */ enum sieve_delivery_phase { SIEVE_DELIVERY_PHASE_UNKNOWN = 0, SIEVE_DELIVERY_PHASE_PRE, SIEVE_DELIVERY_PHASE_DURING, SIEVE_DELIVERY_PHASE_POST, }; struct sieve_environment { const char *hostname; const char *domainname; const char *base_dir; const char *username; const char *home_dir; const char *temp_dir; struct event *event_parent; enum sieve_flag flags; enum sieve_env_location location; enum sieve_delivery_phase delivery_phase; }; /* * Callbacks */ struct sieve_callbacks { const char *(*get_homedir)(void *context); const char *(*get_setting)(void *context, const char *identifier); }; /* * Errors */ enum sieve_error { SIEVE_ERROR_NONE = 0, /* Temporary internal error */ SIEVE_ERROR_TEMP_FAILURE, /* It's not possible to do the wanted operation */ SIEVE_ERROR_NOT_POSSIBLE, /* Invalid parameters (eg. script name not valid) */ SIEVE_ERROR_BAD_PARAMS, /* No permission to do the request */ SIEVE_ERROR_NO_PERMISSION, /* Out of disk space */ SIEVE_ERROR_NO_QUOTA, /* Item (e.g. script or binary) cannot be found */ SIEVE_ERROR_NOT_FOUND, /* Item (e.g. script or binary) already exists */ SIEVE_ERROR_EXISTS, /* Referenced item (e.g. script or binary) is not valid or currupt */ SIEVE_ERROR_NOT_VALID, /* Not allowed to perform the operation because the item is in active use */ SIEVE_ERROR_ACTIVE, /* Operation exceeds resource limit */ SIEVE_ERROR_RESOURCE_LIMIT, }; /* * Compile flags */ enum sieve_compile_flags { /* No global extensions are allowed * (as marked by sieve_global_extensions setting) */ SIEVE_COMPILE_FLAG_NOGLOBAL = (1<<0), /* Script is being uploaded (usually through ManageSieve) */ SIEVE_COMPILE_FLAG_UPLOADED = (1<<1), /* Script is being activated (usually through ManageSieve) */ SIEVE_COMPILE_FLAG_ACTIVATED = (1<<2), /* Compiled for environment with no access to envelope */ SIEVE_COMPILE_FLAG_NO_ENVELOPE = (1<<3) }; /* * Message data * * - The mail message + envelope data */ struct sieve_message_data { struct mail *mail; const char *auth_user; const char *id; struct { const struct smtp_address *mail_from; const struct smtp_params_mail *mail_params; const struct smtp_address *rcpt_to; const struct smtp_params_rcpt *rcpt_params; } envelope; }; /* * Runtime flags */ enum sieve_execute_flags { /* No global extensions are allowed * (as marked by sieve_global_extensions setting) */ SIEVE_EXECUTE_FLAG_NOGLOBAL = (1<<0), /* Do not execute (implicit keep) at the end */ SIEVE_EXECUTE_FLAG_DEFER_KEEP = (1<<1), /* There is no envelope */ SIEVE_EXECUTE_FLAG_NO_ENVELOPE = (1<<2), /* Skip sending responses */ SIEVE_EXECUTE_FLAG_SKIP_RESPONSES = (1<<3), /* Log result as info (when absent, only debug logging is performed) */ SIEVE_EXECUTE_FLAG_LOG_RESULT = (1<<4), }; /* * Runtime trace settings */ typedef enum { SIEVE_TRLVL_NONE = 0, SIEVE_TRLVL_ACTIONS, SIEVE_TRLVL_COMMANDS, SIEVE_TRLVL_TESTS, SIEVE_TRLVL_MATCHING } sieve_trace_level_t; enum { SIEVE_TRFLG_DEBUG = (1 << 0), SIEVE_TRFLG_ADDRESSES = (1 << 1) }; struct sieve_trace_config { sieve_trace_level_t level; unsigned int flags; }; /* * Script environment * * - Environment for currently executing script */ struct sieve_script_env { /* Mail-related */ struct mail_user *user; const struct message_address *postmaster_address; const char *default_mailbox; bool mailbox_autocreate; bool mailbox_autosubscribe; /* External context data */ void *script_context; /* Callbacks */ /* Interface for sending mail */ void *(*smtp_start) (const struct sieve_script_env *senv, const struct smtp_address *mail_from); /* Add a new recipient */ void (*smtp_add_rcpt) (const struct sieve_script_env *senv, void *handle, const struct smtp_address *rcpt_to); /* Get an output stream where the message can be written to. The recipients must already be added before calling this. */ struct ostream *(*smtp_send) (const struct sieve_script_env *senv, void *handle); /* Abort the SMTP transaction after smtp_send() is already issued */ void (*smtp_abort) (const struct sieve_script_env *senv, void *handle); /* Returns 1 on success, 0 on permanent failure, -1 on temporary failure. */ int (*smtp_finish) (const struct sieve_script_env *senv, void *handle, const char **error_r); /* Interface for marking and checking duplicates */ bool (*duplicate_check) (const struct sieve_script_env *senv, const void *id, size_t id_size); void (*duplicate_mark) (const struct sieve_script_env *senv, const void *id, size_t id_size, time_t time); void (*duplicate_flush) (const struct sieve_script_env *senv); /* Interface for rejecting mail */ int (*reject_mail)(const struct sieve_script_env *senv, const struct smtp_address *recipient, const char *reason); /* Interface for amending result messages */ const char * (*result_amend_log_message)(const struct sieve_script_env *senv, enum log_type log_type, const char *message); /* Execution status record */ struct sieve_exec_status *exec_status; /* Runtime trace*/ struct sieve_trace_log *trace_log; struct sieve_trace_config trace_config; }; #define SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) \ (senv->default_mailbox == NULL ? "INBOX" : senv->default_mailbox ) /* * Resource usage */ struct sieve_resource_usage { /* The total amount of system + user CPU time consumed while executing the Sieve script. */ unsigned int cpu_time_msecs; }; /* * Script execution status */ struct sieve_exec_status { struct mail_storage *last_storage; struct sieve_resource_usage resource_usage; bool message_saved:1; bool message_forwarded:1; bool tried_default_save:1; bool keep_original:1; bool store_failed:1; bool significant_action_executed:1; }; /* * Execution exit codes */ enum sieve_execution_exitcode { SIEVE_EXEC_OK = 1, SIEVE_EXEC_FAILURE = 0, SIEVE_EXEC_TEMP_FAILURE = -1, SIEVE_EXEC_BIN_CORRUPT = -2, SIEVE_EXEC_KEEP_FAILED = -3, SIEVE_EXEC_RESOURCE_LIMIT = -4, }; #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/Makefile.in0000644000000000000000000013612414103200135022402 0ustar00rootroot00000000000000# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/lib-sieve ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/dovecot.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/dummy-config.h \ $(top_builddir)/pigeonhole-config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dovecot_pkglibdir)" \ "$(DESTDIR)$(pkginc_libdir)" LTLIBRARIES = $(dovecot_pkglib_LTLIBRARIES) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = $(strgdir)/data/libsieve_storage_data.la \ $(strgdir)/file/libsieve_storage_file.la \ $(strgdir)/dict/libsieve_storage_dict.la \ $(strgdir)/ldap/libsieve_storage_ldap.la $(am__DEPENDENCIES_1) am__DEPENDENCIES_3 = $(extdir)/vacation/libsieve_ext_vacation.la \ $(extdir)/subaddress/libsieve_ext_subaddress.la \ $(extdir)/comparator-i-ascii-numeric/libsieve_ext_comparator-i-ascii-numeric.la \ $(extdir)/relational/libsieve_ext_relational.la \ $(extdir)/regex/libsieve_ext_regex.la \ $(extdir)/copy/libsieve_ext_copy.la \ $(extdir)/imap4flags/libsieve_ext_imap4flags.la \ $(extdir)/include/libsieve_ext_include.la \ $(extdir)/body/libsieve_ext_body.la \ $(extdir)/variables/libsieve_ext_variables.la \ $(extdir)/enotify/libsieve_ext_enotify.la \ $(extdir)/notify/libsieve_ext_notify.la \ $(extdir)/environment/libsieve_ext_environment.la \ $(extdir)/mailbox/libsieve_ext_mailbox.la \ $(extdir)/date/libsieve_ext_date.la \ $(extdir)/spamvirustest/libsieve_ext_spamvirustest.la \ $(extdir)/ihave/libsieve_ext_ihave.la \ $(extdir)/editheader/libsieve_ext_editheader.la \ $(extdir)/duplicate/libsieve_ext_duplicate.la \ $(extdir)/index/libsieve_ext_index.la \ $(extdir)/metadata/libsieve_ext_metadata.la \ $(extdir)/mime/libsieve_ext_mime.la \ $(extdir)/special-use/libsieve_ext_special_use.la \ $(extdir)/vnd.dovecot/debug/libsieve_ext_debug.la \ $(extdir)/vnd.dovecot/environment/libsieve_ext_vnd_environment.la \ $(extdir)/vnd.dovecot/report/libsieve_ext_vnd_report.la \ $(am__DEPENDENCIES_1) am__objects_1 = cmp-i-octet.lo cmp-i-ascii-casemap.lo am__objects_2 = mcht-is.lo mcht-contains.lo mcht-matches.lo am__objects_3 = tst-truefalse.lo tst-not.lo tst-anyof.lo tst-allof.lo \ tst-address.lo tst-header.lo tst-exists.lo tst-size.lo am__objects_4 = cmd-require.lo cmd-stop.lo cmd-if.lo cmd-keep.lo \ cmd-redirect.lo cmd-discard.lo am__objects_5 = ext-fileinto.lo ext-reject.lo ext-envelope.lo \ ext-encoded-character.lo am_libdovecot_sieve_la_OBJECTS = sieve-settings.lo sieve-message.lo \ sieve-smtp.lo sieve-lexer.lo sieve-script.lo sieve-storage.lo \ sieve-storage-sync.lo sieve-ast.lo sieve-binary.lo \ sieve-binary-file.lo sieve-binary-code.lo \ sieve-binary-debug.lo sieve-parser.lo sieve-address.lo \ sieve-validator.lo sieve-generator.lo sieve-execute.lo \ sieve-interpreter.lo sieve-runtime-trace.lo \ sieve-code-dumper.lo sieve-binary-dumper.lo sieve-result.lo \ sieve-error.lo sieve-objects.lo sieve-stringlist.lo \ sieve-comparators.lo sieve-match-types.lo \ sieve-address-parts.lo sieve-address-source.lo sieve-match.lo \ sieve-commands.lo sieve-code.lo sieve-actions.lo \ sieve-extensions.lo sieve-plugins.lo $(am__objects_1) \ $(am__objects_2) $(am__objects_3) $(am__objects_4) \ $(am__objects_5) sieve.lo libdovecot_sieve_la_OBJECTS = $(am_libdovecot_sieve_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/cmd-discard.Plo \ ./$(DEPDIR)/cmd-if.Plo ./$(DEPDIR)/cmd-keep.Plo \ ./$(DEPDIR)/cmd-redirect.Plo ./$(DEPDIR)/cmd-require.Plo \ ./$(DEPDIR)/cmd-stop.Plo ./$(DEPDIR)/cmp-i-ascii-casemap.Plo \ ./$(DEPDIR)/cmp-i-octet.Plo \ ./$(DEPDIR)/ext-encoded-character.Plo \ ./$(DEPDIR)/ext-envelope.Plo ./$(DEPDIR)/ext-fileinto.Plo \ ./$(DEPDIR)/ext-reject.Plo ./$(DEPDIR)/mcht-contains.Plo \ ./$(DEPDIR)/mcht-is.Plo ./$(DEPDIR)/mcht-matches.Plo \ ./$(DEPDIR)/sieve-actions.Plo \ ./$(DEPDIR)/sieve-address-parts.Plo \ ./$(DEPDIR)/sieve-address-source.Plo \ ./$(DEPDIR)/sieve-address.Plo ./$(DEPDIR)/sieve-ast.Plo \ ./$(DEPDIR)/sieve-binary-code.Plo \ ./$(DEPDIR)/sieve-binary-debug.Plo \ ./$(DEPDIR)/sieve-binary-dumper.Plo \ ./$(DEPDIR)/sieve-binary-file.Plo ./$(DEPDIR)/sieve-binary.Plo \ ./$(DEPDIR)/sieve-code-dumper.Plo ./$(DEPDIR)/sieve-code.Plo \ ./$(DEPDIR)/sieve-commands.Plo \ ./$(DEPDIR)/sieve-comparators.Plo ./$(DEPDIR)/sieve-error.Plo \ ./$(DEPDIR)/sieve-execute.Plo ./$(DEPDIR)/sieve-extensions.Plo \ ./$(DEPDIR)/sieve-generator.Plo \ ./$(DEPDIR)/sieve-interpreter.Plo ./$(DEPDIR)/sieve-lexer.Plo \ ./$(DEPDIR)/sieve-match-types.Plo ./$(DEPDIR)/sieve-match.Plo \ ./$(DEPDIR)/sieve-message.Plo ./$(DEPDIR)/sieve-objects.Plo \ ./$(DEPDIR)/sieve-parser.Plo ./$(DEPDIR)/sieve-plugins.Plo \ ./$(DEPDIR)/sieve-result.Plo \ ./$(DEPDIR)/sieve-runtime-trace.Plo \ ./$(DEPDIR)/sieve-script.Plo ./$(DEPDIR)/sieve-settings.Plo \ ./$(DEPDIR)/sieve-smtp.Plo ./$(DEPDIR)/sieve-storage-sync.Plo \ ./$(DEPDIR)/sieve-storage.Plo ./$(DEPDIR)/sieve-stringlist.Plo \ ./$(DEPDIR)/sieve-validator.Plo ./$(DEPDIR)/sieve.Plo \ ./$(DEPDIR)/tst-address.Plo ./$(DEPDIR)/tst-allof.Plo \ ./$(DEPDIR)/tst-anyof.Plo ./$(DEPDIR)/tst-exists.Plo \ ./$(DEPDIR)/tst-header.Plo ./$(DEPDIR)/tst-not.Plo \ ./$(DEPDIR)/tst-size.Plo ./$(DEPDIR)/tst-truefalse.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdovecot_sieve_la_SOURCES) DIST_SOURCES = $(libdovecot_sieve_la_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(pkginc_lib_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINARY_CFLAGS = @BINARY_CFLAGS@ BINARY_LDFLAGS = @BINARY_LDFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@ DLLTOOL = @DLLTOOL@ DOVECOT_BINARY_CFLAGS = @DOVECOT_BINARY_CFLAGS@ DOVECOT_BINARY_LDFLAGS = @DOVECOT_BINARY_LDFLAGS@ DOVECOT_CFLAGS = @DOVECOT_CFLAGS@ DOVECOT_COMPRESS_LIBS = @DOVECOT_COMPRESS_LIBS@ DOVECOT_INSTALLED = @DOVECOT_INSTALLED@ DOVECOT_LIBS = @DOVECOT_LIBS@ DOVECOT_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_NOTIFY_INCLUDE = @LIBDOVECOT_NOTIFY_INCLUDE@ LIBDOVECOT_POP3_INCLUDE = @LIBDOVECOT_POP3_INCLUDE@ LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE = @LIBDOVECOT_PUSH_NOTIFICATION_INCLUDE@ LIBDOVECOT_SERVICE_INCLUDE = @LIBDOVECOT_SERVICE_INCLUDE@ LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ LIBDOVECOT_SQL_DEPS = @LIBDOVECOT_SQL_DEPS@ LIBDOVECOT_SQL_INCLUDE = @LIBDOVECOT_SQL_INCLUDE@ LIBDOVECOT_SSL = @LIBDOVECOT_SSL@ LIBDOVECOT_SSL_DEPS = @LIBDOVECOT_SSL_DEPS@ LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ LIBDOVECOT_STORAGE_INCLUDE = @LIBDOVECOT_STORAGE_INCLUDE@ LIBDOVECOT_SUBMISSION_INCLUDE = @LIBDOVECOT_SUBMISSION_INCLUDE@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIE_CFLAGS = @PIE_CFLAGS@ PIE_LDFLAGS = @PIE_LDFLAGS@ RANLIB = @RANLIB@ RELRO_LDFLAGS = @RELRO_LDFLAGS@ RUN_TEST = @RUN_TEST@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dovecot_docdir = @dovecot_docdir@ dovecot_installed_moduledir = @dovecot_installed_moduledir@ dovecot_moduledir = @dovecot_moduledir@ dovecot_pkgincludedir = @dovecot_pkgincludedir@ dovecot_pkglibdir = @dovecot_pkglibdir@ dovecot_pkglibexecdir = @dovecot_pkglibexecdir@ dovecot_statedir = @dovecot_statedir@ dovecotdir = @dovecotdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ moduledir = @moduledir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sieve_docdir = @sieve_docdir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = util storage plugins dovecot_pkglib_LTLIBRARIES = libdovecot-sieve.la AM_CPPFLAGS = \ $(LIBDOVECOT_INCLUDE) \ $(LIBDOVECOT_SERVICE_INCLUDE) \ -I$(top_srcdir)/src/lib-sieve/util \ -DMODULEDIR=\""$(dovecot_moduledir)"\" tests = \ tst-truefalse.c \ tst-not.c \ tst-anyof.c \ tst-allof.c \ tst-address.c \ tst-header.c \ tst-exists.c \ tst-size.c commands = \ cmd-require.c \ cmd-stop.c \ cmd-if.c \ cmd-keep.c \ cmd-redirect.c \ cmd-discard.c extensions = \ ext-fileinto.c \ ext-reject.c \ ext-envelope.c \ ext-encoded-character.c match_types = \ mcht-is.c \ mcht-contains.c \ mcht-matches.c comparators = \ cmp-i-octet.c \ cmp-i-ascii-casemap.c @BUILD_UNFINISHED_TRUE@unfinished_storages = @BUILD_UNFINISHED_TRUE@unfinished_plugins = strgdir = $(top_builddir)/src/lib-sieve/storage storages = \ $(strgdir)/data/libsieve_storage_data.la \ $(strgdir)/file/libsieve_storage_file.la \ $(strgdir)/dict/libsieve_storage_dict.la \ $(strgdir)/ldap/libsieve_storage_ldap.la \ $(unfinished_storages) extdir = $(top_builddir)/src/lib-sieve/plugins plugins = \ $(extdir)/vacation/libsieve_ext_vacation.la \ $(extdir)/subaddress/libsieve_ext_subaddress.la \ $(extdir)/comparator-i-ascii-numeric/libsieve_ext_comparator-i-ascii-numeric.la \ $(extdir)/relational/libsieve_ext_relational.la \ $(extdir)/regex/libsieve_ext_regex.la \ $(extdir)/copy/libsieve_ext_copy.la \ $(extdir)/imap4flags/libsieve_ext_imap4flags.la \ $(extdir)/include/libsieve_ext_include.la \ $(extdir)/body/libsieve_ext_body.la \ $(extdir)/variables/libsieve_ext_variables.la \ $(extdir)/enotify/libsieve_ext_enotify.la \ $(extdir)/notify/libsieve_ext_notify.la \ $(extdir)/environment/libsieve_ext_environment.la \ $(extdir)/mailbox/libsieve_ext_mailbox.la \ $(extdir)/date/libsieve_ext_date.la \ $(extdir)/spamvirustest/libsieve_ext_spamvirustest.la \ $(extdir)/ihave/libsieve_ext_ihave.la \ $(extdir)/editheader/libsieve_ext_editheader.la \ $(extdir)/duplicate/libsieve_ext_duplicate.la \ $(extdir)/index/libsieve_ext_index.la \ $(extdir)/metadata/libsieve_ext_metadata.la \ $(extdir)/mime/libsieve_ext_mime.la \ $(extdir)/special-use/libsieve_ext_special_use.la \ $(extdir)/vnd.dovecot/debug/libsieve_ext_debug.la \ $(extdir)/vnd.dovecot/environment/libsieve_ext_vnd_environment.la \ $(extdir)/vnd.dovecot/report/libsieve_ext_vnd_report.la \ $(unfinished_plugins) libdovecot_sieve_la_DEPENDENCIES = \ $(storages) \ $(plugins) \ $(top_builddir)/src/lib-sieve/util/libsieve_util.la \ $(LIBDOVECOT_STORAGE_DEPS) \ $(LIBDOVECOT_DEPS) libdovecot_sieve_la_LIBADD = \ $(storages) \ $(plugins) \ $(top_builddir)/src/lib-sieve/util/libsieve_util.la \ $(LIBDOVECOT_STORAGE) \ $(LIBDOVECOT) libdovecot_sieve_la_SOURCES = \ sieve-settings.c \ sieve-message.c \ sieve-smtp.c \ sieve-lexer.c \ sieve-script.c \ sieve-storage.c \ sieve-storage-sync.c \ sieve-ast.c \ sieve-binary.c \ sieve-binary-file.c \ sieve-binary-code.c \ sieve-binary-debug.c \ sieve-parser.c \ sieve-address.c \ sieve-validator.c \ sieve-generator.c \ sieve-execute.c \ sieve-interpreter.c \ sieve-runtime-trace.c \ sieve-code-dumper.c \ sieve-binary-dumper.c \ sieve-result.c \ sieve-error.c \ sieve-objects.c \ sieve-stringlist.c \ sieve-comparators.c \ sieve-match-types.c \ sieve-address-parts.c \ sieve-address-source.c \ sieve-match.c \ sieve-commands.c \ sieve-code.c \ sieve-actions.c \ sieve-extensions.c \ sieve-plugins.c \ $(comparators) \ $(match_types) \ $(tests) \ $(commands) \ $(extensions) \ sieve.c headers = \ sieve-config.h \ sieve-types.h \ sieve-common.h \ sieve-limits.h \ sieve-settings.h \ sieve-message.h \ sieve-smtp.h \ sieve-lexer.h \ sieve-script.h \ sieve-script-private.h \ sieve-storage.h \ sieve-storage-private.h \ sieve-ast.h \ sieve-binary.h \ sieve-binary-private.h \ sieve-parser.h \ sieve-address.h \ sieve-validator.h \ sieve-generator.h \ sieve-execute.h \ sieve-interpreter.h \ sieve-runtime-trace.h \ sieve-runtime.h \ sieve-code-dumper.h \ sieve-binary-dumper.h \ sieve-dump.h \ sieve-result.h \ sieve-error.h \ sieve-error-private.h \ sieve-objects.h \ sieve-stringlist.h \ sieve-match.h \ sieve-comparators.h \ sieve-match-types.h \ sieve-address-parts.h \ sieve-address-source.h \ sieve-commands.h \ sieve-code.h \ sieve-actions.h \ sieve-extensions.h \ sieve-plugins.h \ sieve.h pkginc_libdir = $(dovecot_pkgincludedir)/sieve pkginc_lib_HEADERS = $(headers) all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib-sieve/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/lib-sieve/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dovecot_pkglibLTLIBRARIES: $(dovecot_pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dovecot_pkglib_LTLIBRARIES)'; test -n "$(dovecot_pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dovecot_pkglibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dovecot_pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dovecot_pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dovecot_pkglibdir)"; \ } uninstall-dovecot_pkglibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dovecot_pkglib_LTLIBRARIES)'; test -n "$(dovecot_pkglibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dovecot_pkglibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dovecot_pkglibdir)/$$f"; \ done clean-dovecot_pkglibLTLIBRARIES: -test -z "$(dovecot_pkglib_LTLIBRARIES)" || rm -f $(dovecot_pkglib_LTLIBRARIES) @list='$(dovecot_pkglib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libdovecot-sieve.la: $(libdovecot_sieve_la_OBJECTS) $(libdovecot_sieve_la_DEPENDENCIES) $(EXTRA_libdovecot_sieve_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) -rpath $(dovecot_pkglibdir) $(libdovecot_sieve_la_OBJECTS) $(libdovecot_sieve_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-discard.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-if.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-keep.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-redirect.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-require.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-stop.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmp-i-ascii-casemap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmp-i-octet.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-encoded-character.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-envelope.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-fileinto.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext-reject.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-contains.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-is.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcht-matches.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-actions.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-address-parts.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-address-source.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-address.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-ast.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-binary-code.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-binary-debug.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-binary-dumper.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-binary-file.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-binary.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-code-dumper.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-code.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-commands.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-comparators.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-error.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-execute.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-extensions.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-generator.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-interpreter.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-lexer.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-match-types.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-match.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-message.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-objects.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-parser.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-plugins.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-result.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-runtime-trace.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-script.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-settings.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-smtp.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-storage-sync.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-storage.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-stringlist.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve-validator.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sieve.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-address.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-allof.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-anyof.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-exists.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-header.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-not.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-size.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tst-truefalse.Plo@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) @$(NORMAL_INSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ done uninstall-pkginc_libHEADERS: @$(NORMAL_UNINSTALL) @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(dovecot_pkglibdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-dovecot_pkglibLTLIBRARIES clean-generic clean-libtool \ mostlyclean-am distclean: distclean-recursive -rm -f ./$(DEPDIR)/cmd-discard.Plo -rm -f ./$(DEPDIR)/cmd-if.Plo -rm -f ./$(DEPDIR)/cmd-keep.Plo -rm -f ./$(DEPDIR)/cmd-redirect.Plo -rm -f ./$(DEPDIR)/cmd-require.Plo -rm -f ./$(DEPDIR)/cmd-stop.Plo -rm -f ./$(DEPDIR)/cmp-i-ascii-casemap.Plo -rm -f ./$(DEPDIR)/cmp-i-octet.Plo -rm -f ./$(DEPDIR)/ext-encoded-character.Plo -rm -f ./$(DEPDIR)/ext-envelope.Plo -rm -f ./$(DEPDIR)/ext-fileinto.Plo -rm -f ./$(DEPDIR)/ext-reject.Plo -rm -f ./$(DEPDIR)/mcht-contains.Plo -rm -f ./$(DEPDIR)/mcht-is.Plo -rm -f ./$(DEPDIR)/mcht-matches.Plo -rm -f ./$(DEPDIR)/sieve-actions.Plo -rm -f ./$(DEPDIR)/sieve-address-parts.Plo -rm -f ./$(DEPDIR)/sieve-address-source.Plo -rm -f ./$(DEPDIR)/sieve-address.Plo -rm -f ./$(DEPDIR)/sieve-ast.Plo -rm -f ./$(DEPDIR)/sieve-binary-code.Plo -rm -f ./$(DEPDIR)/sieve-binary-debug.Plo -rm -f ./$(DEPDIR)/sieve-binary-dumper.Plo -rm -f ./$(DEPDIR)/sieve-binary-file.Plo -rm -f ./$(DEPDIR)/sieve-binary.Plo -rm -f ./$(DEPDIR)/sieve-code-dumper.Plo -rm -f ./$(DEPDIR)/sieve-code.Plo -rm -f ./$(DEPDIR)/sieve-commands.Plo -rm -f ./$(DEPDIR)/sieve-comparators.Plo -rm -f ./$(DEPDIR)/sieve-error.Plo -rm -f ./$(DEPDIR)/sieve-execute.Plo -rm -f ./$(DEPDIR)/sieve-extensions.Plo -rm -f ./$(DEPDIR)/sieve-generator.Plo -rm -f ./$(DEPDIR)/sieve-interpreter.Plo -rm -f ./$(DEPDIR)/sieve-lexer.Plo -rm -f ./$(DEPDIR)/sieve-match-types.Plo -rm -f ./$(DEPDIR)/sieve-match.Plo -rm -f ./$(DEPDIR)/sieve-message.Plo -rm -f ./$(DEPDIR)/sieve-objects.Plo -rm -f ./$(DEPDIR)/sieve-parser.Plo -rm -f ./$(DEPDIR)/sieve-plugins.Plo -rm -f ./$(DEPDIR)/sieve-result.Plo -rm -f ./$(DEPDIR)/sieve-runtime-trace.Plo -rm -f ./$(DEPDIR)/sieve-script.Plo -rm -f ./$(DEPDIR)/sieve-settings.Plo -rm -f ./$(DEPDIR)/sieve-smtp.Plo -rm -f ./$(DEPDIR)/sieve-storage-sync.Plo -rm -f ./$(DEPDIR)/sieve-storage.Plo -rm -f ./$(DEPDIR)/sieve-stringlist.Plo -rm -f ./$(DEPDIR)/sieve-validator.Plo -rm -f ./$(DEPDIR)/sieve.Plo -rm -f ./$(DEPDIR)/tst-address.Plo -rm -f ./$(DEPDIR)/tst-allof.Plo -rm -f ./$(DEPDIR)/tst-anyof.Plo -rm -f ./$(DEPDIR)/tst-exists.Plo -rm -f ./$(DEPDIR)/tst-header.Plo -rm -f ./$(DEPDIR)/tst-not.Plo -rm -f ./$(DEPDIR)/tst-size.Plo -rm -f ./$(DEPDIR)/tst-truefalse.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dovecot_pkglibLTLIBRARIES \ install-pkginc_libHEADERS install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/cmd-discard.Plo -rm -f ./$(DEPDIR)/cmd-if.Plo -rm -f ./$(DEPDIR)/cmd-keep.Plo -rm -f ./$(DEPDIR)/cmd-redirect.Plo -rm -f ./$(DEPDIR)/cmd-require.Plo -rm -f ./$(DEPDIR)/cmd-stop.Plo -rm -f ./$(DEPDIR)/cmp-i-ascii-casemap.Plo -rm -f ./$(DEPDIR)/cmp-i-octet.Plo -rm -f ./$(DEPDIR)/ext-encoded-character.Plo -rm -f ./$(DEPDIR)/ext-envelope.Plo -rm -f ./$(DEPDIR)/ext-fileinto.Plo -rm -f ./$(DEPDIR)/ext-reject.Plo -rm -f ./$(DEPDIR)/mcht-contains.Plo -rm -f ./$(DEPDIR)/mcht-is.Plo -rm -f ./$(DEPDIR)/mcht-matches.Plo -rm -f ./$(DEPDIR)/sieve-actions.Plo -rm -f ./$(DEPDIR)/sieve-address-parts.Plo -rm -f ./$(DEPDIR)/sieve-address-source.Plo -rm -f ./$(DEPDIR)/sieve-address.Plo -rm -f ./$(DEPDIR)/sieve-ast.Plo -rm -f ./$(DEPDIR)/sieve-binary-code.Plo -rm -f ./$(DEPDIR)/sieve-binary-debug.Plo -rm -f ./$(DEPDIR)/sieve-binary-dumper.Plo -rm -f ./$(DEPDIR)/sieve-binary-file.Plo -rm -f ./$(DEPDIR)/sieve-binary.Plo -rm -f ./$(DEPDIR)/sieve-code-dumper.Plo -rm -f ./$(DEPDIR)/sieve-code.Plo -rm -f ./$(DEPDIR)/sieve-commands.Plo -rm -f ./$(DEPDIR)/sieve-comparators.Plo -rm -f ./$(DEPDIR)/sieve-error.Plo -rm -f ./$(DEPDIR)/sieve-execute.Plo -rm -f ./$(DEPDIR)/sieve-extensions.Plo -rm -f ./$(DEPDIR)/sieve-generator.Plo -rm -f ./$(DEPDIR)/sieve-interpreter.Plo -rm -f ./$(DEPDIR)/sieve-lexer.Plo -rm -f ./$(DEPDIR)/sieve-match-types.Plo -rm -f ./$(DEPDIR)/sieve-match.Plo -rm -f ./$(DEPDIR)/sieve-message.Plo -rm -f ./$(DEPDIR)/sieve-objects.Plo -rm -f ./$(DEPDIR)/sieve-parser.Plo -rm -f ./$(DEPDIR)/sieve-plugins.Plo -rm -f ./$(DEPDIR)/sieve-result.Plo -rm -f ./$(DEPDIR)/sieve-runtime-trace.Plo -rm -f ./$(DEPDIR)/sieve-script.Plo -rm -f ./$(DEPDIR)/sieve-settings.Plo -rm -f ./$(DEPDIR)/sieve-smtp.Plo -rm -f ./$(DEPDIR)/sieve-storage-sync.Plo -rm -f ./$(DEPDIR)/sieve-storage.Plo -rm -f ./$(DEPDIR)/sieve-stringlist.Plo -rm -f ./$(DEPDIR)/sieve-validator.Plo -rm -f ./$(DEPDIR)/sieve.Plo -rm -f ./$(DEPDIR)/tst-address.Plo -rm -f ./$(DEPDIR)/tst-allof.Plo -rm -f ./$(DEPDIR)/tst-anyof.Plo -rm -f ./$(DEPDIR)/tst-exists.Plo -rm -f ./$(DEPDIR)/tst-header.Plo -rm -f ./$(DEPDIR)/tst-not.Plo -rm -f ./$(DEPDIR)/tst-size.Plo -rm -f ./$(DEPDIR)/tst-truefalse.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-dovecot_pkglibLTLIBRARIES \ uninstall-pkginc_libHEADERS .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--depfiles check check-am clean \ clean-dovecot_pkglibLTLIBRARIES clean-generic clean-libtool \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dovecot_pkglibLTLIBRARIES \ install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am \ install-pkginc_libHEADERS install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-dovecot_pkglibLTLIBRARIES \ uninstall-pkginc_libHEADERS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-smtp.c0000644000000000000000000000402114103200126022563 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "smtp-address.h" #include "sieve-common.h" #include "sieve-smtp.h" struct sieve_smtp_context { const struct sieve_script_env *senv; void *handle; bool sent:1; }; bool sieve_smtp_available (const struct sieve_script_env *senv) { return ( senv->smtp_start != NULL && senv->smtp_add_rcpt != NULL && senv->smtp_send != NULL && senv->smtp_finish != NULL ); } struct sieve_smtp_context *sieve_smtp_start (const struct sieve_script_env *senv, const struct smtp_address *mail_from) { struct sieve_smtp_context *sctx; void *handle; if ( !sieve_smtp_available(senv) ) return NULL; handle = senv->smtp_start(senv, mail_from); i_assert( handle != NULL ); sctx = i_new(struct sieve_smtp_context, 1); sctx->senv = senv; sctx->handle = handle; return sctx; } void sieve_smtp_add_rcpt (struct sieve_smtp_context *sctx, const struct smtp_address *rcpt_to) { i_assert(!sctx->sent); sctx->senv->smtp_add_rcpt(sctx->senv, sctx->handle, rcpt_to); } struct ostream *sieve_smtp_send (struct sieve_smtp_context *sctx) { i_assert(!sctx->sent); sctx->sent = TRUE; return sctx->senv->smtp_send(sctx->senv, sctx->handle); } struct sieve_smtp_context *sieve_smtp_start_single (const struct sieve_script_env *senv, const struct smtp_address *rcpt_to, const struct smtp_address *mail_from, struct ostream **output_r) { struct sieve_smtp_context *sctx; sctx = sieve_smtp_start(senv, mail_from); sieve_smtp_add_rcpt(sctx, rcpt_to); *output_r = sieve_smtp_send(sctx); return sctx; } void sieve_smtp_abort (struct sieve_smtp_context *sctx) { const struct sieve_script_env *senv = sctx->senv; void *handle = sctx->handle; i_free(sctx); i_assert(senv->smtp_abort != NULL); senv->smtp_abort(senv, handle); } int sieve_smtp_finish (struct sieve_smtp_context *sctx, const char **error_r) { const struct sieve_script_env *senv = sctx->senv; void *handle = sctx->handle; i_free(sctx); return senv->smtp_finish(senv, handle, error_r); } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-script.h0000644000000000000000000001075314103200126023122 0ustar00rootroot00000000000000#ifndef SIEVE_SCRIPT_H #define SIEVE_SCRIPT_H #include "sieve-common.h" #include /* * Sieve script name */ bool sieve_script_name_is_valid(const char *scriptname); /* * Sieve script file */ bool sieve_script_file_has_extension(const char *filename); /* * Sieve script class */ void sieve_script_class_register(struct sieve_instance *svinst, const struct sieve_script *script_class); void sieve_script_class_unregister(struct sieve_instance *svinst, const struct sieve_script *script_class); /* * Sieve script instance */ struct sieve_script; ARRAY_DEFINE_TYPE(sieve_script, struct sieve_script *); struct sieve_script * sieve_script_create(struct sieve_instance *svinst, const char *location, const char *name, enum sieve_error *error_r) ATTR_NULL(3,4); void sieve_script_ref(struct sieve_script *script); void sieve_script_unref(struct sieve_script **script); int sieve_script_open(struct sieve_script *script, enum sieve_error *error_r) ATTR_NULL(2); int sieve_script_open_as(struct sieve_script *script, const char *name, enum sieve_error *error_r) ATTR_NULL(3); struct sieve_script * sieve_script_create_open(struct sieve_instance *svinst, const char *location, const char *name, enum sieve_error *error_r) ATTR_NULL(3, 4); int sieve_script_check(struct sieve_instance *svinst, const char *location, const char *name, enum sieve_error *error_r) ATTR_NULL(3, 4); /* * Data script */ struct sieve_script * sieve_data_script_create_from_input(struct sieve_instance *svinst, const char *name, struct istream *input); /* * Binary */ int sieve_script_binary_read_metadata(struct sieve_script *script, struct sieve_binary_block *sblock, sieve_size_t *offset); void sieve_script_binary_write_metadata(struct sieve_script *script, struct sieve_binary_block *sblock); bool sieve_script_binary_dump_metadata(struct sieve_script *script, struct sieve_dumptime_env *denv, struct sieve_binary_block *sblock, sieve_size_t *offset) ATTR_NULL(1); struct sieve_binary * sieve_script_binary_load(struct sieve_script *script, enum sieve_error *error_r); int sieve_script_binary_save(struct sieve_script *script, struct sieve_binary *sbin, bool update, enum sieve_error *error_r) ATTR_NULL(4); const char *sieve_script_binary_get_prefix(struct sieve_script *script); /* * Stream management */ int sieve_script_get_stream(struct sieve_script *script, struct istream **stream_r, enum sieve_error *error_r) ATTR_NULL(3); /* * Management */ // FIXME: check read/write flag! int sieve_script_rename(struct sieve_script *script, const char *newname); int sieve_script_is_active(struct sieve_script *script); int sieve_script_activate(struct sieve_script *script, time_t mtime); int sieve_script_delete(struct sieve_script *script, bool ignore_active); /* * Properties */ const char *sieve_script_name(const struct sieve_script *script) ATTR_PURE; const char *sieve_script_location(const struct sieve_script *script) ATTR_PURE; struct sieve_instance * sieve_script_svinst(const struct sieve_script *script) ATTR_PURE; int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r); bool sieve_script_is_open(const struct sieve_script *script) ATTR_PURE; bool sieve_script_is_default(const struct sieve_script *script) ATTR_PURE; const char * sieve_file_script_get_dirpath(const struct sieve_script *script) ATTR_PURE; const char * sieve_file_script_get_path(const struct sieve_script *script) ATTR_PURE; /* * Comparison */ bool sieve_script_equals(const struct sieve_script *script, const struct sieve_script *other); unsigned int sieve_script_hash(const struct sieve_script *script); static inline int sieve_script_cmp(const struct sieve_script *script, const struct sieve_script *other) { return ( sieve_script_equals(script, other) ? 0 : -1 ); } /* * Error handling */ const char *sieve_script_get_last_error(struct sieve_script *script, enum sieve_error *error_r) ATTR_NULL(2); const char *sieve_script_get_last_error_lcase(struct sieve_script *script); /* * Script sequence */ struct sieve_script_sequence; struct sieve_script_sequence * sieve_script_sequence_create(struct sieve_instance *svinst, const char *location, enum sieve_error *error_r); struct sieve_script * sieve_script_sequence_next(struct sieve_script_sequence *seq, enum sieve_error *error_r); void sieve_script_sequence_free(struct sieve_script_sequence **_seq); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/cmd-discard.c0000644000000000000000000000771114103200126022652 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "str-sanitize.h" #include "sieve-common.h" #include "sieve-commands.h" #include "sieve-code.h" #include "sieve-dump.h" #include "sieve-actions.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-interpreter.h" #include "sieve-result.h" /* * Discard command * * Syntax * discard */ static bool cmd_discard_generate(const struct sieve_codegen_env *cgenv, struct sieve_command *ctx ATTR_UNUSED); const struct sieve_command_def cmd_discard = { .identifier = "discard", .type = SCT_COMMAND, .positional_args = 0, .subtests = 0, .block_allowed = FALSE, .block_required = FALSE, .generate = cmd_discard_generate }; /* * Discard operation */ static bool cmd_discard_operation_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address); static int cmd_discard_operation_execute(const struct sieve_runtime_env *renv, sieve_size_t *address); const struct sieve_operation_def cmd_discard_operation = { .mnemonic = "DISCARD", .code = SIEVE_OPERATION_DISCARD, .dump = cmd_discard_operation_dump, .execute = cmd_discard_operation_execute }; /* * Discard actions */ static bool act_discard_equals(const struct sieve_script_env *senv, const struct sieve_action *act1, const struct sieve_action *act2); static int act_discard_check_duplicate(const struct sieve_runtime_env *renv, const struct sieve_action *act, const struct sieve_action *act_other); static void act_discard_print(const struct sieve_action *action, const struct sieve_result_print_env *rpenv, bool *keep); static int act_discard_execute(const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep); const struct sieve_action_def act_discard = { .name = "discard", .equals = act_discard_equals, .check_duplicate = act_discard_check_duplicate, .print = act_discard_print, .execute = act_discard_execute, }; /* * Code generation */ static bool cmd_discard_generate(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd ATTR_UNUSED) { sieve_operation_emit(cgenv->sblock, NULL, &cmd_discard_operation); return TRUE; } /* * Code dump */ static bool cmd_discard_operation_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address) { sieve_code_dumpf(denv, "DISCARD"); sieve_code_descend(denv); return (sieve_action_opr_optional_dump(denv, address, NULL) == 0); } /* * Interpretation */ static int cmd_discard_operation_execute(const struct sieve_runtime_env *renv ATTR_UNUSED, sieve_size_t *address ATTR_UNUSED) { sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "discard action; cancel implicit keep"); if (sieve_result_add_action(renv, NULL, "discard", &act_discard, NULL, NULL, 0, FALSE) < 0) return SIEVE_EXEC_FAILURE; return SIEVE_EXEC_OK; } /* * Action implementation */ static bool act_discard_equals(const struct sieve_script_env *senv ATTR_UNUSED, const struct sieve_action *act1 ATTR_UNUSED, const struct sieve_action *act2 ATTR_UNUSED) { return TRUE; } static int act_discard_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED, const struct sieve_action *act ATTR_UNUSED, const struct sieve_action *act_other ATTR_UNUSED) { return 1; } static void act_discard_print(const struct sieve_action *action ATTR_UNUSED, const struct sieve_result_print_env *rpenv, bool *keep) { sieve_result_action_printf(rpenv, "discard"); *keep = FALSE; } static int act_discard_execute(const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED, bool *keep) { const struct sieve_execute_env *eenv = aenv->exec_env; eenv->exec_status->significant_action_executed = TRUE; struct event_passthrough *e = sieve_action_create_finish_event(aenv); sieve_result_event_log(aenv, e->event(), "Marked message to be discarded if not explicitly delivered " "(discard action)"); *keep = FALSE; return SIEVE_EXEC_OK; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-generator.h0000644000000000000000000000714114103200126023601 0ustar00rootroot00000000000000#ifndef SIEVE_GENERATOR_H #define SIEVE_GENERATOR_H #include "sieve-common.h" /* * Code generator */ struct sieve_generator; struct sieve_codegen_env { struct sieve_generator *gentr; struct sieve_instance *svinst; enum sieve_compile_flags flags; struct sieve_script *script; struct sieve_ast *ast; struct sieve_binary *sbin; struct sieve_binary_block *sblock; }; struct sieve_generator * sieve_generator_create(struct sieve_ast *ast, struct sieve_error_handler *ehandler, enum sieve_compile_flags flags); void sieve_generator_free(struct sieve_generator **generator); /* * Accessors */ struct sieve_error_handler * sieve_generator_error_handler(struct sieve_generator *gentr); pool_t sieve_generator_pool(struct sieve_generator *gentr); struct sieve_script *sieve_generator_script(struct sieve_generator *gentr); struct sieve_binary *sieve_generator_get_binary(struct sieve_generator *gentr); struct sieve_binary_block * sieve_generator_get_block(struct sieve_generator *gentr); /* * Extension support */ void sieve_generator_extension_set_context(struct sieve_generator *gentr, const struct sieve_extension *ext, void *context); const void * sieve_generator_extension_get_context(struct sieve_generator *gentr, const struct sieve_extension *ext); /* * Jump list */ struct sieve_jumplist { pool_t pool; struct sieve_binary_block *block; ARRAY(sieve_size_t) jumps; }; struct sieve_jumplist * sieve_jumplist_create(pool_t pool, struct sieve_binary_block *sblock); void sieve_jumplist_init_temp(struct sieve_jumplist *jlist, struct sieve_binary_block *sblock); void sieve_jumplist_reset(struct sieve_jumplist *jlist); void sieve_jumplist_add(struct sieve_jumplist *jlist, sieve_size_t jump); void sieve_jumplist_resolve(struct sieve_jumplist *jlist); /* * Code generation API */ bool sieve_generate_argument(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd); bool sieve_generate_arguments(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, struct sieve_ast_argument **last_arg_r); bool sieve_generate_argument_parameters(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, struct sieve_ast_argument *arg); bool sieve_generate_block(const struct sieve_codegen_env *cgenv, struct sieve_ast_node *block); bool sieve_generate_test(const struct sieve_codegen_env *cgenv, struct sieve_ast_node *tst_node, struct sieve_jumplist *jlist, bool jump_true); struct sieve_binary * sieve_generator_run(struct sieve_generator *gentr, struct sieve_binary_block **sblock_r); /* * Error handling */ void sieve_generator_error(struct sieve_generator *gentr, const char *csrc_filename, unsigned int csrc_linenum, unsigned int source_line, const char *fmt, ...) ATTR_FORMAT(5, 6); #define sieve_generator_error(gentr, ...) \ sieve_generator_error(gentr, __FILE__, __LINE__, __VA_ARGS__) void sieve_generator_warning(struct sieve_generator *gentr, const char *csrc_filename, unsigned int csrc_linenum, unsigned int source_line, const char *fmt, ...) ATTR_FORMAT(5, 6); #define sieve_generator_warning(gentr, ...) \ sieve_generator_warning(gentr, __FILE__, __LINE__, __VA_ARGS__) void sieve_generator_critical(struct sieve_generator *gentr, const char *csrc_filename, unsigned int csrc_linenum, unsigned int source_line, const char *fmt, ...) ATTR_FORMAT(5, 6); #define sieve_generator_critical(gentr, ...) \ sieve_generator_critical(gentr, __FILE__, __LINE__, __VA_ARGS__) #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/ext-encoded-character.c0000644000000000000000000001340614103200126024627 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ /* Extension encoded-character * --------------------------- * * Authors: Stephan Bosch * Specification: RFC 5228 * Implementation: full * Status: testing * */ #include "lib.h" #include "unichar.h" #include "sieve-extensions.h" #include "sieve-commands.h" #include "sieve-validator.h" #include /* * Extension */ static bool ext_encoded_character_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr); const struct sieve_extension_def encoded_character_extension = { .name = "encoded-character", .validator_load = ext_encoded_character_validator_load, }; /* * Encoded string argument */ bool arg_encoded_string_validate (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *context); const struct sieve_argument_def encoded_string_argument = { .identifier = "@encoded-string", .validate = arg_encoded_string_validate }; /* Parsing */ static bool _skip_whitespace (const char **in, const char *inend) { while ( *in < inend ) { if ( **in == '\r' ) { (*in)++; if ( **in != '\n' ) return FALSE; continue; } /* (Loose LF is non-standard) */ if ( **in != ' ' && **in != '\n' && **in != '\t' ) break; (*in)++; } return TRUE; } static bool _parse_hexint (const char **in, const char *inend, int max_digits, unsigned int *result) { int digit = 0; *result = 0; while ( *in < inend && (max_digits == 0 || digit < max_digits) ) { if ( (**in) >= '0' && (**in) <= '9' ) *result = ((*result) << 4) + (**in) - ((unsigned int) '0'); else if ( (**in) >= 'a' && (**in) <= 'f' ) *result = ((*result) << 4) + (**in) - ((unsigned int) 'a') + 0x0a; else if ( (**in) >= 'A' && (**in) <= 'F' ) *result = ((*result) << 4) + (**in) - ((unsigned int) 'A') + 0x0a; else return ( digit > 0 ); (*in)++; digit++; } if ( digit == max_digits ) { /* Hex digit _MUST_ end here */ if ( (**in >= '0' && **in <= '9') || (**in >= 'a' && **in <= 'f') || (**in >= 'A' && **in <= 'F') ) return FALSE; return TRUE; } return ( digit > 0 ); } static bool _decode_hex (const char **in, const char *inend, string_t *result) { int values = 0; while ( *in < inend ) { unsigned int hexpair; if ( !_skip_whitespace(in, inend) ) return FALSE; if ( !_parse_hexint(in, inend, 2, &hexpair) ) break; str_append_c(result, (unsigned char) hexpair); values++; } return ( values > 0 ); } static bool _decode_unicode (const char **in, const char *inend, string_t *result, unsigned int *error_hex) { int values = 0; bool valid = TRUE; while ( *in < inend ) { unsigned int unicode_hex; if ( !_skip_whitespace(in, inend) ) return FALSE; if ( !_parse_hexint(in, inend, 0, &unicode_hex) ) break; if ( uni_is_valid_ucs4((unichar_t) unicode_hex) ) uni_ucs4_to_utf8_c((unichar_t) unicode_hex, result); else { if ( valid ) *error_hex = unicode_hex; valid = FALSE; } values++; } return ( values > 0 ); } bool arg_encoded_string_validate (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *cmd) { bool result = TRUE; enum { ST_NONE, ST_OPEN, ST_TYPE, ST_CLOSE } state = ST_NONE; string_t *str = sieve_ast_argument_str(*arg); string_t *tmpstr, *newstr = NULL; const char *p, *mark, *strstart, *substart = NULL; const char *strval = (const char *) str_data(str); const char *strend = strval + str_len(str); unsigned int error_hex = 0; T_BEGIN { tmpstr = t_str_new(32); p = strval; strstart = p; while ( result && p < strend ) { switch ( state ) { /* Normal string */ case ST_NONE: if ( *p == '$' ) { substart = p; state = ST_OPEN; } p++; break; /* Parsed '$' */ case ST_OPEN: if ( *p == '{' ) { state = ST_TYPE; p++; } else state = ST_NONE; break; /* Parsed '${' */ case ST_TYPE: mark = p; /* Scan for 'hex' or 'unicode' */ while ( p < strend && i_isalpha(*p) ) p++; if ( *p != ':' ) { state = ST_NONE; break; } state = ST_CLOSE; str_truncate(tmpstr, 0); if ( strncasecmp(mark, "hex", p - mark) == 0 ) { /* Hexadecimal */ p++; if ( !_decode_hex(&p, strend, tmpstr) ) state = ST_NONE; } else if ( strncasecmp(mark, "unicode", p - mark) == 0 ) { /* Unicode */ p++; if ( !_decode_unicode(&p, strend, tmpstr, &error_hex) ) state = ST_NONE; } else { /* Invalid encoding */ p++; state = ST_NONE; } break; case ST_CLOSE: if ( *p == '}' ) { /* We now know that the substitution is valid */ if ( error_hex != 0 ) { sieve_argument_validate_error(valdtr, *arg, "invalid unicode character 0x%08x in encoded character substitution", error_hex); result = FALSE; break; } if ( newstr == NULL ) { newstr = str_new(sieve_ast_pool((*arg)->ast), str_len(str)*2); } str_append_data(newstr, strstart, substart-strstart); str_append_str(newstr, tmpstr); strstart = p + 1; substart = strstart; p++; } state = ST_NONE; } } } T_END; if ( !result ) return FALSE; if ( newstr != NULL ) { if ( strstart != strend ) str_append_data(newstr, strstart, strend-strstart); sieve_ast_argument_string_set(*arg, newstr); } /* Pass the processed string to a (possible) next layer of processing */ return sieve_validator_argument_activate_super (valdtr, cmd, *arg, TRUE); } /* * Extension implementation */ static bool ext_encoded_character_validator_load (const struct sieve_extension *ext, struct sieve_validator *valdtr) { /* Override the constant string argument with our own */ sieve_validator_argument_override (valdtr, SAT_CONST_STRING, ext, &encoded_string_argument); return TRUE; } dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-match.h0000644000000000000000000000305114103200126022703 0ustar00rootroot00000000000000#ifndef SIEVE_MATCH_H #define SIEVE_MATCH_H #include "sieve-common.h" /* * Matching context */ struct sieve_match_context { pool_t pool; const struct sieve_runtime_env *runenv; const struct sieve_match_type *match_type; const struct sieve_comparator *comparator; void *data; int match_status; int exec_status; bool trace:1; }; /* * Matching implementation */ /* Manual value iteration (for when multiple matches are allowed) */ struct sieve_match_context *sieve_match_begin (const struct sieve_runtime_env *renv, const struct sieve_match_type *mcht, const struct sieve_comparator *cmp); int sieve_match_value (struct sieve_match_context *mctx, const char *value, size_t value_size, struct sieve_stringlist *key_list); int sieve_match_end(struct sieve_match_context **mctx, int *exec_status); /* Default matching operation */ int sieve_match (const struct sieve_runtime_env *renv, const struct sieve_match_type *mcht, const struct sieve_comparator *cmp, struct sieve_stringlist *value_list, struct sieve_stringlist *key_list, int *exec_status); /* * Read matching operands */ enum sieve_match_opt_operand { SIEVE_MATCH_OPT_END, SIEVE_MATCH_OPT_COMPARATOR, SIEVE_MATCH_OPT_MATCH_TYPE, SIEVE_MATCH_OPT_LAST }; int sieve_match_opr_optional_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address, int *opt_code); int sieve_match_opr_optional_read (const struct sieve_runtime_env *renv, sieve_size_t *address, int *opt_code, int *exec_status, struct sieve_comparator *cmp, struct sieve_match_type *mcht); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-storage.h0000644000000000000000000001234614103200126023262 0ustar00rootroot00000000000000#ifndef SIEVE_STORAGE_H #define SIEVE_STORAGE_H #define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \ MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/" #define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \ MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/" #define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \ MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default" #define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L' #define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S' /* * Storage object */ enum sieve_storage_flags { /* Storage is opened for read/write access (e.g. ManageSieve) */ SIEVE_STORAGE_FLAG_READWRITE = 0x01, /* This storage is used for synchronization (and not normal ManageSieve) */ SIEVE_STORAGE_FLAG_SYNCHRONIZING = 0x02 }; struct sieve_storage; struct sieve_storage * sieve_storage_create(struct sieve_instance *svinst, const char *location, enum sieve_storage_flags flags, enum sieve_error *error_r) ATTR_NULL(4); struct sieve_storage * sieve_storage_create_main(struct sieve_instance *svinst, struct mail_user *user, enum sieve_storage_flags flags, enum sieve_error *error_r) ATTR_NULL(4); void sieve_storage_ref(struct sieve_storage *storage); void sieve_storage_unref(struct sieve_storage **_storage); /* * Script access */ struct sieve_script * sieve_storage_get_script(struct sieve_storage *storage, const char *name, enum sieve_error *error_r) ATTR_NULL(3); struct sieve_script * sieve_storage_open_script(struct sieve_storage *storage, const char *name, enum sieve_error *error_r) ATTR_NULL(3); int sieve_storage_check_script(struct sieve_storage *storage, const char *name, enum sieve_error *error_r) ATTR_NULL(3); /* * Script sequence */ struct sieve_script_sequence * sieve_storage_get_script_sequence(struct sieve_storage *storage, enum sieve_error *error_r); /* * Active script */ int sieve_storage_active_script_get_name(struct sieve_storage *storage, const char **name_r); struct sieve_script * sieve_storage_active_script_open(struct sieve_storage *storage, enum sieve_error *error_r) ATTR_NULL(2); int sieve_storage_active_script_get_last_change(struct sieve_storage *storage, time_t *last_change_r); /* * Listing scripts in storage */ struct sieve_storage_list_context; /* Create a context for listing the scripts in the storage */ struct sieve_storage_list_context * sieve_storage_list_init(struct sieve_storage *storage); /* Get the next script in the storage. */ const char *sieve_storage_list_next(struct sieve_storage_list_context *lctx, bool *active_r) ATTR_NULL(2); /* Destroy the listing context */ int sieve_storage_list_deinit(struct sieve_storage_list_context **lctx); /* * Saving scripts in storage */ struct sieve_storage_save_context; struct sieve_storage_save_context * sieve_storage_save_init(struct sieve_storage *storage, const char *scriptname, struct istream *input); int sieve_storage_save_continue(struct sieve_storage_save_context *sctx); int sieve_storage_save_finish(struct sieve_storage_save_context *sctx); struct sieve_script * sieve_storage_save_get_tempscript(struct sieve_storage_save_context *sctx); bool sieve_storage_save_will_activate(struct sieve_storage_save_context *sctx); void sieve_storage_save_set_mtime(struct sieve_storage_save_context *sctx, time_t mtime); void sieve_storage_save_cancel(struct sieve_storage_save_context **sctx); int sieve_storage_save_commit(struct sieve_storage_save_context **sctx); int sieve_storage_save_as(struct sieve_storage *storage, struct istream *input, const char *name); /* Saves input directly as a regular file at the active script path. * This is needed for the doveadm-sieve plugin. */ int sieve_storage_save_as_active(struct sieve_storage *storage, struct istream *input, time_t mtime); /* * Management */ int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime); /* * Storage quota */ enum sieve_storage_quota { SIEVE_STORAGE_QUOTA_NONE, SIEVE_STORAGE_QUOTA_MAXSIZE, SIEVE_STORAGE_QUOTA_MAXSCRIPTS, SIEVE_STORAGE_QUOTA_MAXSTORAGE }; bool sieve_storage_quota_validsize(struct sieve_storage *storage, size_t size, uint64_t *limit_r); uint64_t sieve_storage_quota_max_script_size(struct sieve_storage *storage); int sieve_storage_quota_havespace(struct sieve_storage *storage, const char *scriptname, size_t size, enum sieve_storage_quota *quota_r, uint64_t *limit_r); /* * Properties */ const char *sieve_storage_location(const struct sieve_storage *storage) ATTR_PURE; bool sieve_storage_is_default(const struct sieve_storage *storage) ATTR_PURE; int sieve_storage_is_singular(struct sieve_storage *storage); /* * Error handling */ void sieve_storage_clear_error(struct sieve_storage *storage); void sieve_storage_set_error(struct sieve_storage *storage, enum sieve_error error, const char *fmt, ...) ATTR_FORMAT(3, 4); void sieve_storage_set_critical(struct sieve_storage *storage, const char *fmt, ...) ATTR_FORMAT(2, 3); const char *sieve_storage_get_last_error(struct sieve_storage *storage, enum sieve_error *error_r) ATTR_NULL(2); /* * */ int sieve_storage_get_last_change(struct sieve_storage *storage, time_t *last_change_r); void sieve_storage_set_modified(struct sieve_storage *storage, time_t mtime); #endif dovecot-2.3-pigeonhole-0.5.16/src/lib-sieve/sieve-storage.c0000644000000000000000000011614014103200126023252 0ustar00rootroot00000000000000/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str-sanitize.h" #include "home-expand.h" #include "eacces-error.h" #include "mkdir-parents.h" #include "ioloop.h" #include "sieve-common.h" #include "sieve-settings.h" #include "sieve-error-private.h" #include "sieve-script-private.h" #include "sieve-storage-private.h" #include #include #include #include #include #define CRITICAL_MSG \ "Internal error occurred. Refer to server log for more information." #define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]" struct event_category event_category_sieve_storage = { .parent = &event_category_sieve, .name = "sieve-storage", }; /* * Storage class */ struct sieve_storage_class_registry { ARRAY_TYPE(sieve_storage_class) storage_classes; }; void sieve_storages_init(struct sieve_instance *svinst) { svinst->storage_reg = p_new(svinst->pool, struct sieve_storage_class_registry, 1); p_array_init(&svinst->storage_reg->storage_classes, svinst->pool, 8); sieve_storage_class_register(svinst, &sieve_file_storage); sieve_storage_class_register(svinst, &sieve_dict_storage); sieve_storage_class_register(svinst, &sieve_ldap_storage); } void sieve_storages_deinit(struct sieve_instance *svinst ATTR_UNUSED) { /* nothing yet */ } void sieve_storage_class_register(struct sieve_instance *svinst, const struct sieve_storage *storage_class) { struct sieve_storage_class_registry *reg = svinst->storage_reg; const struct sieve_storage *old_class; old_class = sieve_storage_find_class(svinst, storage_class->driver_name); if (old_class != NULL) { if (old_class->v.alloc == NULL) { /* replacing a "support not compiled in" storage class */ sieve_storage_class_unregister(svinst, old_class); } else { i_panic("sieve_storage_class_register(%s): " "Already registered", storage_class->driver_name); } } array_append(®->storage_classes, &storage_class, 1); } void sieve_storage_class_unregister(struct sieve_instance *svinst, const struct sieve_storage *storage_class) { struct sieve_storage_class_registry *reg = svinst->storage_reg; const struct sieve_storage *const *classes; unsigned int i, count; classes = array_get(®->storage_classes, &count); for (i = 0; i < count; i++) { if (classes[i] == storage_class) { array_delete(®->storage_classes, i, 1); break; } } } const struct sieve_storage * sieve_storage_find_class(struct sieve_instance *svinst, const char *name) { struct sieve_storage_class_registry *reg = svinst->storage_reg; const struct sieve_storage *const *classes; unsigned int i, count; i_assert(name != NULL); classes = array_get(®->storage_classes, &count); for (i = 0; i < count; i++) { if (strcasecmp(classes[i]->driver_name, name) == 0) return classes[i]; } return NULL; } /* * Storage instance */ static const char *split_next_arg(const char *const **_args) { const char *const *args = *_args; const char *str = args[0]; /* join arguments for escaped ";" separator */ args++; while (*args != NULL && **args == '\0') { args++; if (*args == NULL) { /* string ends with ";", just ignore it. */ break; } str = t_strconcat(str, ";", *args, NULL); args++; } *_args = args; return str; } static int sieve_storage_driver_parse(struct sieve_instance *svinst, const char **data, const struct sieve_storage **driver_r) { const struct sieve_storage *storage_class = NULL; const char *p; p = strchr(*data, ':'); if (p == NULL) return 0; /* Lookup storage driver */ T_BEGIN { const char *driver; driver = t_strdup_until(*data, p); *data = p+1; storage_class = sieve_storage_find_class(svinst, driver); if (storage_class == NULL) { e_error(svinst->event, "Unknown storage driver module `%s'", driver); } else if (storage_class->v.alloc == NULL) { e_error(svinst->event, "Support not compiled in for storage driver `%s'", driver); storage_class = NULL; } } T_END; *driver_r = storage_class; return (storage_class == NULL ? -1 : 1); } static int sieve_storage_data_parse(struct sieve_storage *storage, const char *data, const char **location_r, const char *const **options_r) { ARRAY_TYPE(const_string) options; const char *const *tmp; if (*data == '\0') { *options_r = NULL; *location_r = data; return 0; } /* */ tmp = t_strsplit(data, ";"); *location_r = split_next_arg(&tmp); if (options_r != NULL) { t_array_init(&options, 8); /* [