test.sieve

    # 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!";
    }