# Sieve syntax highlighting test
# see RFC 5228

# comments §2.3.

# Single line with alerts TODO ###
/*
 Multiline comment with alerts FIXME
 this also should be folding region
 */

# literal data §2.4.
# numbers §2.4.1.
42
100K
10M
3G

# strings §2.4.2.
"string"
"string\nwith\"special chars"
# not a string
"multi \
 line \
 #with\a \
 continuation"
# not a string
text:
Multiline
string\" "with quotes"
or # comments
this should also be a folding region
.dot
..dot
dotstuffed dot:
..
ending with a single .
.

# encoded characters §2.4.2.4.
# valid
"$${hex:24 24}"
"${unicode:40}"
"$${hex:40}"
"${hex: da }"
"${hex:FE }"
"${HEX: 40}"
"${unicode:40}"
"${UNICODE:40}"
"${UnICoDE:0000040}"
"${Unicode:ff}"
#invalid
"${hex:40"
"${hex:4${hex:30}}"
"${ unicode:40}"
"${Unicode:Cool}"
# semantic errors, syntactically correct
"${hex:400}"
"${unicode:200000}"
"${Unicode:DF01}"

# test lists §2.5.1
if anyof (not exists ["From", "Date"],
          header :contains "from" "fool@example.com") {
    discard;
}
if allof (not exists ["From", "Date"],
          header :contains "from" "fool@example.com") {
    discard;
}

# match types §2.7.1.
:contains
:matches
:is

# comparators §2.7.3.
if header :contains :comparator "i;octet" "Subject" "MAKE MONEY FAST" {
    discard;
}

# comparison against addresses §2.7.4.
:localpart
:domain
:all

# commands §2.9.
keep;
fileinto "inbox.bla";
redirect "test@kde.org";
discard;

# control if §3.1.
# this also should create folding regions
if header :contains "from" "foo" {
    discard;
} elsif header :contains ["subject"] ["$$$"] {
    discard;
} else {
    fileinto "INBOX";
}

# control require §3.2.
require "fileinto";
require ["vacation", "imapflags"];

# control stop §3.3.
stop;

# test commands §5.
address
allof
anyof
exists
false
header
not
size
true

# test address §5.1.
if address :is :all "from" "kde@example.com" {
    discard;
} elsif address :domain :is ["From", "To"] "example.com" { # comment
    keep; # comment
}

# test envelope §5.4.
if envelope :all :is "from" "kde@example.com" {
    discard;
}

# test exists §5.5.
if not exists ["From","Date"] {
    discard;
}

# test header §5.7.
not header :matches "Cc" "?*"

# test size §5.9.
if size :over 500K { discard; }
if size :under 1M { keep; } else { discard; }


/*
 * Extensions
 * see https://www.iana.org/assignments/sieve-extensions/sieve-extensions.xhtml
 */

# Body RFC5173
:raw
:content
:text
if body :raw :contains "MAKE MONEY FAST" {
    discard;
}
if body :content "text" :contains ["bla", "blub"] {
    fileinto "inbox.foo";
}

# Convert RFC6558
require ["convert"];
convert "image/tiff" "image/jpeg" ["pix-x=320","pix-y=240"];

# Copy RFC3894
require ["copy", "fileinto"];
fileinto :copy "incoming";

# Date RFC5260
require ["date", "relational", "fileinto"];
if allof(header :is "from" "boss@example.com",
         date :value "ge" :originalzone "date" "hour" "09",
         date :value "lt" :originalzone "date" "hour" "17")
    { fileinto "urgent"; }
if anyof(date :is "received" "weekday" "0",
         date :is "received" "weekday" "6")
{ fileinto "weekend"; }
if anyof(currentdate :is "weekday" "0",
         currentdate :is "weekday" "6",
         currentdate :value "lt" "hour" "09",
         currentdate :value "ge" "hour" "17")
{ redirect "pager@example.com"; }
if allof(currentdate :value "ge" "date" "2007-06-30",
         currentdate :value "le" "date" "2007-07-07")
{ vacation :days 7  "I'm away during the first week in July."; }

require ["date", "variables", "fileinto"];
if currentdate :matches "month" "*" { set "month" "${1}"; }
if currentdate :matches "year"  "*" { set "year"  "${1}"; }
fileinto "${month}-${year}";

require ["variables", "date", "editheader"];
if currentdate :matches "std11" "*" {addheader "Processing-date" "${0}";}

require ["date", "relational", "index"];
if date :value "gt" :index 2 :zone "-0500" "received" "iso8601" "2007-02-26T09:00:00-05:00"
{ redirect "aftercutoff@example.org"; }

if header :index 1 :matches "received" "*(* [*.*.*.*])*" {}

# Duplicate RFC7352
require ["duplicate", "variables"];
if duplicate { discard; }
if duplicate :header "message-id" { discard; }
if header :matches "message-id" "*" {
    if duplicate :uniqueid "${0}" { discard; }
}

# Editheader RFC5293
addheader "X-Hello" "World";
deleteheader :index 1 "X-Hello";

# Enclose RFC5703
:mime
:anychild
:type
:subtype
:contenttype
:param
replace
enclose
extracttext
if header :mime :type "Content-Type" "image" {}
replace "Executable attachment removed by user filter";
foreverypart {
    if header :mime :param "filename" :matches ["Content-Type", "Content-Disposition"]
        ["*.com", "*.exe", "*.vbs", "*.scr", "*.pif", "*.hta", "*.bat", "*.zip" ] {
        # these attachment types are executable
        enclose :subject "Warning" text:
WARNING! The enclosed message contains executable attachments.
These attachment types may contain a computer virus program
that can infect your computer and potentially damage your data.
.
;
        break;
    }
}
extracttext :first 100 "msgcontent";

# Notify RFC5435
notify :importance "1" :message "This is probably very important" "mailto:kde@example.com";
notify :message "${from_addr}${env_from}: ${subject}" "mailto:kde@example.com";
if not valid_notify_method ["mailto:", "http://gw.example.net/notify?test"] { stop; }
if notify_method_capability "xmpp:tim@example.com?message;subject=SIEVE" "Online" "yes" {}
set :encodeurl "body_param" "stuff";

# Envelope DSN RFC6009
if envelope "notify" "SUCCESS" {}
if allof (envelope "notify" "FAILURE", envelope :comparator "i;ascii-numeric" :count "eq" "notify" "1") {}
if envelope :matches "orcpt" "rfc822;*@example.com" {}
if anyof (envelope :contains "bytimerelative" "-", envelope :value "eq" :comparator "i;ascii-numeric" "bytimerelative" "0") {}
redirect :copy :notify "NEVER" "elsewhere@example.com";
redirect :copy :bytimerelative 600 "cellphone@example.com";
redirect :copy :bytimeabsolute "${date}T20:00:00${zone}" :bymode "return" "cellphone@example.com";

# Environment RFC5183
if environment :contains "item" "" {}

# Reject RFC5429
ereject "I no longer accept mail from this address";
reject text:
Your message is too big.  If you want to send me a big attachment,
put it on a public web site and send me a URL.
.
;

# External Lists RFC6134
if envelope :list "from" ":addrbook:default" {}
if currentdate :list "date" "tag:example.com,2011-01-01:localHolidays" {}
if allof (envelope :detail "to" "mylist", header :list "from" "tag:example.com,2010-05-28:mylist") {
    redirect :list "tag:example.com,2010-05-28:mylist";
}
if string :list "${ip}" "tag:example.com,2011-04-10:DisallowedIPs" {}
if header :mime :param "filename" :list ["Content-Type", "Content-Disposition"] "tag:example.com,2011-04-10:BadFileNameExts" {}

# ihave RFC5463
if ihave "fileinto" {}
error "failed!";

# IMAP Sieve RFC6785
if anyof (environment :is "imap.cause" "APPEND", environment :is "imap.cause" "COPY")  {
    if environment :is "imap.mailbox" "ActionItems" {
        redirect :copy "actionitems@example.com";
    }
}

# IMAP4 Flags RFC5232
setflag "\\Deleted";
setflag "flagvar" "\\Flagged";
addflag "flagvar" "\\Deleted";
addflag "flagvar" ["\\Deleted", "\\Answered"];
removeflag "flagvar" "$MDNRequired";
hasflag :contains "MyVar" "Junk"
hasflag :contains "${MyVar}" ["label", "forward"]
hasflag :count "ge" :comparator "i;ascii-numeric" "MyFlags" 2
fileinto :flags "\\Deleted" "INBOX.bla";

# Include RFC6609
include :personal "always_allow";
include :global "spam_tests";
return;
global "i_am_on_vacation";
set "global.i_am_on_vacation" "1";

# Mailbox RFC5490
if mailboxexists "bla" {}
fileinto :create "inbox.bla";
if metadata :is "INBOX" "/private/vendor/vendor.isode/auto-replies" "on" {}
if metadataexists "INBOX" "/private/vendor/foo" {}
if servermetadata :matches "/private/vendor/vendor.isode/notification-uri" "*" {}
if servermetadataexists "/private/vendor/foo" {}

# Regexp (draft)
if not address :regex ["to", "cc", "bcc"] "me(\\+.*)?@company\\.com" {}

# Relational RFC5231
if address :count "ge" :comparator "i;ascii-numeric" ["to", "cc"] ["3"] {}
if header :value "lt" :comparator "i;ascii-numeric" ["x-priority"] ["3"] {}

# Spamtest RFC5235
if spamtest :value "eq" :comparator "i;ascii-numeric" "0" {}
elsif spamtest :value "ge" :comparator "i;ascii-numeric" "3" {}
elsif spamtest :percent :value "lt" :comparator "i;ascii-numeric" "37" {}
if virustest :value "eq" :comparator "i;ascii-numeric" "4" {}

# Subaddress RFC5233
if envelope :user "to" "postmaster" {}
if envelope :detail "to" "mta-filters" {}

# Vacation RFC5230
vacation "I'm out";
vacation :subject "Automatic response to: ${1}"
                  "I'm away -- send mail to foo in my absence";
vacation :handle "ran-away" "I'm out";
vacation :mime text:
   Content-Type: multipart/alternative; boundary=foo

   --foo

   Hello ${sender}, I'm at the beach relaxing.

   --foo
   Content-Type: text/html; charset=us-ascii

   <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"
    "http://www.w3.org/TR/REC-html40/strict.dtd">
   <HTML><HEAD><TITLE>How to relax</TITLE>
   <BASE HREF="http://home.example.com/pictures/"></HEAD>
   <BODY><P>I'm at the <A HREF="beach.gif">beach</A> relaxing.
   </BODY></HTML>

   --foo--
.
;
vacation :days 23 :addresses ["kde@example.edu"] "I'm away until October 19.";

# Vacation Seconds RFC6131
vacation :addresses ["kde@example.edu"] :seconds 1800
    "I am in a meeting, and do not have access to email.";

# Variables RFC5229
"&%${}!" # invalid
"${doh!}" #invalid
"${company}"
"bla ${var42} blub"
"${1}"
"${BAD${Company}" # second one is the variable
"${President, ${Company} Inc.}" # inner one is the variable
set "var" "value";
set "var2" "${var}";

set "a" "juMBlEd lETteRS";
set :length "b" "${a}";
set :lower "b" "${a}";
set :upperfirst "b" "${a}";
set :upperfirst :lower "b" "${a}";
set :quotewildcard "b" "Rock*";
set :lowerfirst "b" "${a}";

if string :matches " ${state} " "* pending *" {}

# Dovecot debug extension
require "vnd.dovecot.debug";
if header :contains "subject" "hello" {
    debug_log "Subject header contains hello!";
}