/* bytecode.h -- bytecode definition
 *
 * Copyright (c) 1994-2018 Carnegie Mellon University.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The name "Carnegie Mellon University" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For permission or any legal
 *    details, please contact
 *      Carnegie Mellon University
 *      Center for Technology Transfer and Enterprise Creation
 *      4615 Forbes Avenue
 *      Suite 302
 *      Pittsburgh, PA  15213
 *      (412) 268-7393, fax: (412) 268-7395
 *      innovation@andrew.cmu.edu
 *
 * 4. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by Computing Services
 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
 *
 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef SIEVE_BYTECODE_H
#define SIEVE_BYTECODE_H

#include <stddef.h>

/* for debugging*/
#define DUMPCODE 0
#define VERBOSE 0

/*for finding correctly aligned bytes on strings*/
/* bump to the next multiple of 4 bytes */
#define ROUNDUP(num) (((num) + 3) & 0xFFFFFFFC)


/* yes, lots of these are superfluous, it's for clarity */
typedef struct
{
    int type; /* bytecode data type */

    union {
        int op; /* OPTYPE */
        int value;

        int jump;

        int listlen;
        const char *str;
    } u;

} bytecode_t;

enum bytecode_data_type {
    BT_OPCODE,
    BT_JUMP,
    BT_VALUE,
    BT_VALLISTLEN,
    BT_STRLISTLEN,
    BT_STR
};

struct bytecode_info
{
    bytecode_t *data;/* pointer to almost-flat bytecode */
    size_t scriptend; /* used by emit code to know final length of bytecode */
    size_t reallen; /* allocated length of 'data' */
};

/* For sanity during input on 64-bit platforms.
 * str should only be accessed as (char *)&str, but given the use of
 * unwrap_string, this should be OK */
typedef union
{
    int op; /* OPTYPE */
    int value;

    int jump;

    int listlen;

    /* store strings (need 2 consecutive bytecodes) */
    int len;
    int str;
} bytecode_input_t;


/* version 0x01 scripts were written in host byte order.
 * we don't want to use this version number again and cause a mess
 * this isn't a huge concern, since this is version ntohl(1), or 16777216
 *
 * version 0x02 scripts started using network byte order (recompile)
 * version 0x03 scripts implemented short-circuiting of testlists (recompile)
 * version 0x04 scripts implemented BODY, INCLUDE and COPY extensions
 * version 0x05 scripts implemented updated VACATION (:from and :handle)
 * version 0x06 scripts implemented updated VACATION (:seconds)
 * version 0x07 scripts implemented updated INCLUDE (:once and :optional)
 * version 0x08 scripts implemented DATE and INDEX extensions
 * version 0x09 scripts implemented IMAP4FLAGS extension
 * version 0x0a-0x0f skipped by someone who didn't read hex
 * version 0x10 scripts implemented Mailbox and Metadata (RFC 5490)
 * version 0x11 scripts implemented Variables (RFC 5229)
 * version 0x12 scripts implemented Editheader (RFC 5293),
 *                                  eReject (RFC 5429),
 *                                  External Lists (RFC 6134),
 *                                  eNotify (RFC 5435),
 *                                  Duplicate (RFC 7352),
 *                                  Ihave (RFC 5463),
 *                                  updated KEEP (remove :copy)
 *                                  updated VACATION (:fcc) (RFC 8580)
 *                                  Special-Use (RFC 8579)
 * version 0x13 scripts forced an upgrade for broken bytecode generated by a buggy commit
 * version 0x14 scripts implemented Environment (RFC 5183)
 * version 0x15 scripts implemented Redirect-DeliverBy, Redirect-DSN (RFC 6009)
 * version 0x16 scripts implemented draft-gondwana-sieve-mailboxid-01
 * version 0x17 scripts implemented vnd.cyrus.log (aka x-cyrus-log)
 * version 0x18 scripts implemented vnd.cyrus.jmapquery (aka x-cyrus-jmapquery)
 * version 0x19 scripts implemented vnd.cyrus.snooze (aka x-cyrus-snooze)
 * version 0x1A scripts store fileinto mailbox names in UTF8 rather than mUTF7
 * version 0x1B scripts re-implemented vnd.cyrus.snooze times as HH:MM:SS
 *                                                      AND :weekdays as [0, 6]
 * version 0x1C scripts added :tzid argument to vnd.cyrus.snooze
 * version 0x1D scripts re-implemented Snooze per draft-ietf-extra-sieve-snooze-00
 *                      and Fcc per draft-ietf-extra-sieve-mailboxid-01
 * version 0x1E scripts store [current]date :zone argument as a string
 */
#define BYTECODE_VERSION 0x1E
#define BYTECODE_MIN_VERSION 0x03 /* minimum supported version */
#define BYTECODE_MAGIC "CyrSBytecode"
#define BYTECODE_MAGIC_LEN 12 /* Should be multiple of 4 */

/* IMPORTANT: To maintain forward compatibility of bytecode, please only add
 * new instructions to the end of these enums.  (The reason these values
 * are all duplicated here is to avoid silliness if this caveat is forgotten
 * about in the other tables.)
 *
 * Comments below show which extensions are supported
 * and the order of the arguments as they are written out in bytecode.
 *
 * In all cases below, COMPARATOR expands to:
 * <match-type: int> <relational-match: int> <collation: int>
 */

enum bytecode {
    B_STOP,
    B_KEEP_ORIG,                /* legacy keep w/o support for :flags          */
    B_DISCARD,
    B_REJECT,                   /* require "reject"

                                   <reason: string>                            */

    B_FILEINTO_ORIG,            /* legacy fileinto w/o support for :copy

                                   require "fileinto"

                                   <mailbox: string>  */

    B_REDIRECT_ORIG,            /* legacy redirect w/o support for :copy

                                   <address: string>                           */

    B_IF,                       /* <jump-begin-then>: int>
                                   <jump-begin-else: int>
                                   <jump-end-else: int>
                                   <test: test>
                                   <then-block: command-list>
                                   <else-block: command-list> (optional)       */

    B_MARK,                     /* require "imapflags"                         */
    B_UNMARK,                   /* require "imapflags"                         */

    B_ADDFLAG_ORIG,	        /* legacy addflag w/o support for variables

                                   require "imap4flags"

                                   <flag-list: string-list>                    */

    B_SETFLAG_ORIG,	        /* legacy setflag w/o support for variables

                                   require "imap4flags"

                                   <flag-list: string-list>                    */

    B_REMOVEFLAG_ORIG,	        /* legacy removeflag w/o support for variables

                                   require "imap4flags"

                                   <flag-list: string-list>                    */

    B_NOTIFY,                   /* require "notify"

                                   <method: string> <id: string>
                                   <options: string-list> <priority: int>
                                   <message: string>                           */

    B_DENOTIFY,                 /* require "notify"
                                   <priority: int>
                                   <match-type: int> <relational-match: int>
                                   <pattern: string>                           */

    B_VACATION_ORIG,            /* legacy vacation w/o support for :seconds

                                   require "vacation"

                                   <addresses: string-list> <subject: string>
                                   <message: string> <days: int> <mime: int>
                                   <from: string> <handle: string>             */

    B_NULL,
    B_JUMP,                     /* <byte-offset: int>                          */

    B_INCLUDE,                  /* require "include"

                                   <parameters-bitmask: int>
                                   <script-name: string>                       */

    B_RETURN,                   /* require "include"                           */

    B_FILEINTO_COPY,            /* legacy fileinto w/o support for :flags

                                   require ["fileinto", "copy"]

                                   <copy: int> <mailbox: string>               */

    B_REDIRECT_COPY,            /* legacy redirect w/o support for :list */

    B_VACATION_SEC,             /* legacy vacation w/o support for :fcc

                                   require ["vacation", "vacation-seconds"]

                                   <addresses: string-list> <subject: string>
                                   <message: string> <seconds: int> <mime: int>
                                   <from: string> <handle: string>             */

    B_KEEP_COPY,                /* legacy keep with bogus support for :copy */

    B_FILEINTO_FLAGS,           /* legacy fileinto w/o support for :create

                                   require ["fileinto", "copy", "imap4flags"]

                                   <flag-list: string-list>
                                   <copy: int> <mailbox: string>               */

    B_FILEINTO_CREATE,          /* legacy fileinto w/o support for :specialuse

                                   require ["fileinto", "copy", "imap4flags",
                                            "mailbox"]

                                   <create: int> <flag-list: string-list>
                                   <copy: int> <mailbox: string>               */

    B_SET,                      /* require "variables"

                                   <modifiers-bitmask: int>
                                   <variable-name: string> <value: string>     */

    B_ADDFLAG,                  /* require "imap4flags"

                                   <variable-name: string>
                                   <flag-list: string-list>                    */

    B_SETFLAG,                  /* require "imap4flags"

                                   <variable-name: string>
                                   <flag-list: string-list>                    */

    B_REMOVEFLAG,               /* require "imap4flags"

                                   <variable-name: string>
                                   <flag-list: string-list>                    */

    B_ADDHEADER,                /* require "editheader"

                                   <index: int>
                                   <header-name: string> <value: string>       */

    B_DELETEHEADER,             /* require "editheader"

                                   <index: int> <COMPARATOR>
                                   <header-name: string> <patterns: string-list> */

    B_EREJECT,                  /* require "ereject"

                                   <reason: string>                            */

    B_REDIRECT_LIST,            /* legacy redirect w/o support for dsn/deliverby

                                   require ["copy", "list"]

                                   <list: int> <copy: int> <address: string>   */

    B_ENOTIFY,                  /* require "enotify"

                                   <method: string> <from: string>
                                   <options: string-list> <priority: int>
                                   <message: string>                           */

    B_ERROR,                    /* require ihave */

    B_KEEP,                     /* require "imap4flags"

                                   <flag-list: string-list>                    */

    B_VACATION_FCC_ORIG,        /* legacy vacation w/o support for :specialuse
                                   with :fcc

                                   require ["vacation", "vacation-seconds,
                                            "fcc", "mailbox"]

                                   <addresses: string-list> <subject: string>
                                   <message: string> <seconds: int> <mime: int>
                                   <from: string> <handle: string>
                                   <fcc-mailbox: string>
                                   IF (fcc-mailbox != NIL)
                                     <create: int> <flag-list: string-list>    */

    B_VACATION_FCC_SPLUSE,      /* legacy vacation w/o support for :mailboxid
                                   with :fcc

                                   require ["vacation", "vacation-seconds,
                                            "fcc", "mailbox", "special-use"]

                                   <addresses: string-list> <subject: string>
                                   <message: string> <seconds: int> <mime: int>
                                   <from: string> <handle: string>
                                   <fcc-mailbox: string>
                                   IF (fcc-mailbox != NIL):
                                     <create: int> <flag-list: string-list>
                                     <special-use: string>                     */

    B_FILEINTO_SPECIALUSE,      /* legacy fileinto w/o support for :mailboxid

                                   require ["fileinto", "copy", "imap4flags",
                                            "mailbox", "specialuse"]

                                   <special-use: string>
                                   <create: int> <flag-list: string-list>
                                   <copy: int> <mailbox: string>               */

    B_REDIRECT,                 /* require ["copy", "list", "redirect-dsn",
                                            "redirect-deliveryby"]

                                   <bytime: string> <bymode: string>
                                   <bytrace: int>
                                   <notify: string> <return: string>
                                   <list: int> <copy: int> <address: string>   */

    B_FILEINTO,                 /* require ["fileinto", "copy", "imap4flags",
                                            "mailbox", "specialuse", "mailboxid"]

                                   <mailbox-id: string> <special-use: string>
                                   <create: int> <flag-list: string-list>
                                   <copy: int> <mailbox: string>               */

    B_LOG,                      /* require "vnd.cyrus.log"

                                   <message: string>                           */

    B_SNOOZE_ORIG,              /* legacy snooze w/o support for :tzid

                                   require "vnd.cyrus.snooze"

                                   <mailbox-name/id: string>
                                   <addflags: string-list>
                                   <removeflags: string-list>
                                   <weekdays/is_id-bitmask: int>
                                   <times: value-list>                         */

    B_SNOOZE_TZID,              /* legacy snooze w/o support for :specialuse

                                   require "vnd.cyrus.snooze"

                                   <tzid: string>
                                   <mailbox-name/id: string>
                                   <addflags: string-list>
                                   <removeflags: string-list>
                                   <weekdays/is_id-bitmask: int>
                                   <times: value-list>                         */


    B_SNOOZE,                   /* require ["vnd.cyrus.snooze", "imap4flags"
                                            "mailbox", "specialuse", "mailboxid"]

                                   <mailbox-name: string>
                                   <mailbox-id: string>
                                   <special-use: string>
                                   <create: int>
                                   <addflags: string-list>
                                   <removeflags: string-list>
                                   <weekdays-bitmask: int>
                                   <tzid: string>
                                   <times: value-list>                         */

    B_VACATION,                 /* require ["vacation", "vacation-seconds,
                                            "fcc", "mailbox", "special-use",
                                            "mailboxid"]

                                   <addresses: string-list> <subject: string>
                                   <message: string> <seconds: int> <mime: int>
                                   <from: string> <handle: string>
                                   <fcc-mailbox: string>
                                   IF (fcc-mailbox != NIL):
                                     <create: int> <flag-list: string-list>
                                     <special-use: string> <mailboxid: string> */

    /*****  insert new actions above this line  *****/
    B_ILLEGAL_VALUE             /* any value >= this code is illegal */
};

enum bytecode_comps {
    BC_FALSE,
    BC_TRUE,
    BC_NOT,                     /* <subtest: test>                             */

    BC_EXISTS,                  /* <header-names: string-list>                 */

    BC_SIZE,                    /* <over/under: int> <size: int>               */

    BC_ANYOF,                   /* <subtests: test-list>                       */

    BC_ALLOF,                   /* <subtests: test-list>                       */

    BC_ADDRESS_PRE_INDEX,       /* <COMPARATOR> <address-part: int>
                                   <header-names: string-list>
                                   <patterns: string-list>                     */

    BC_ENVELOPE,                /* require "envelope"
                                   <COMPARATOR> <address-part: int>
                                   <envelope-parts: string-list>
                                   <patterns: string-list>                     */

    BC_HEADER_PRE_INDEX,        /* <COMPARATOR> <header-names: string-list>
                                   <patterns: string-list>                     */

    BC_BODY,                    /* require "body"

                                   <COMPARATOR>
                                   <transform: int> <offset: int>
                                   <content-types: string-list>
                                   <patterns: string-list>                     */

    BC_DATE_ORIG,               /* require ["date", "index"]

                                   <index: int> <zonetag: int> <tzoffset: int>
                                   <COMPARATOR> <date-part: int>
                                   <header-name: string> <patterns: string-list> */

    BC_CURRENTDATE_ORIG,        /* require "date"

                                   <zonetag: int> <tzoffset: int>
                                   <COMPARATOR> <date-part: int>
                                   <patterns: string-list>                     */

    BC_ADDRESS,                 /* <index: int>
                                   <COMPARATOR> <address-part: int>
                                   <header-names: string-list>
                                   <patterns: string-list>                     */

    BC_HEADER,                  /* <index: int>
                                   <COMPARATOR> <header-names: string-list>
                                   <patterns: string-list>                     */

    BC_HASFLAG,                 /* require "imap4flags"
                                   <COMPARATOR> <variable-list: string-list>
                                   <flag-list: string-list>                    */

    BC_MAILBOXEXISTS,           /* require "mailbox"

                                   <mailbox-names: string-list>                */

    BC_METADATA,                /* require "mboxmetadata"
                                   <COMPARATOR>
                                   <mailbox: string>
                                   <annotation-name: string>
                                   <patterns: string-list>                     */

    BC_METADATAEXISTS,          /* require "mboxmetadata"
                                   <mailbox: string>
                                   <annotation-names: string-list>             */

    BC_SERVERMETADATA,          /* require "servermetadata"
                                   <COMPARATOR>
                                   <annotation-name: string>
                                   <patterns: string-list>                     */

    BC_SERVERMETADATAEXISTS,    /* require "servermetadata"

                                   <annotation-names: string-list>             */

    BC_STRING,                  /* require "variables"
                                   <COMPARATOR> <strings: string-list>
                                   <patterns: string-list>                     */

    BC_VALIDEXTLIST,            /* require "extlists"

                                   <ext-list-names: string-list>               */

    BC_DUPLICATE,               /* require "duplicate" */

    BC_IHAVE,                   /* require "ihave"

                                   <capabilities: string-list>                 */

    BC_SPECIALUSEEXISTS,        /* require "special-use"

                                   <mailbox: string>
                                   <special-use-attrs: string-list>            */

    BC_ENVIRONMENT,             /* require "environment"
                                   <COMPARATOR>
                                   <name: string> <patterns: string-list>      */

    BC_VALIDNOTIFYMETHOD,       /* require "enotify"

                                   <notification-uris: string-list>            */

    BC_NOTIFYMETHODCAPABILITY,  /* require "enotify"

                                   <COMPARATOR>
                                   <notification-uri: string>
                                   <notification-capability: string>
                                   <patterns: string-list>                     */

    BC_MAILBOXIDEXISTS,         /* require "mailboxid"

                                   <patterns: string-list>                     */

    BC_JMAPQUERY,               /* require "vnd.cyrus.jmapquery"

                                   <query: string>                             */

    BC_DATE,                    /* require ["date", "index"]

                                   <index: int> <zonetag: int> <tzoffset: string>
                                   <COMPARATOR> <date-part: int>
                                   <header-name: string> <patterns: string-list> */

    BC_CURRENTDATE,             /* require "date"

                                   <zonetag: int> <tzoffset: string>
                                   <COMPARATOR> <date-part: int>
                                   <patterns: string-list>                     */

    /*****  insert new tests above this line  *****/
    BC_ILLEGAL_VALUE    /* any value >= this code is illegal */
};

/* currently one enum so as to help determine where values are being misused.
 * we have left placeholders incase we need to add more later to the middle */
enum bytecode_tags {
    /* Size Tests (0-3) */
    B_OVER,
    B_UNDER,

    B_SIZE_PLACEHOLDER_1,
    B_SIZE_PLACEHOLDER_2,

    /* Relational Match Types (4-11) */
    B_GT,               /* require relational */
    B_GE,               /* require relational */
    B_LT,               /* require relational */
    B_LE,               /* require relational */
    B_EQ,               /* require relational */
    B_NE,               /* require relational */

    B_RELATIONAL_PLACEHOLDER_1,
    B_RELATIONAL_PLACEHOLDER_2,

    /* Priorities (12-19) */
    B_LOW,              /* require notify */
    B_NORMAL,           /* require notify */
    B_HIGH,             /* require notify */
    B_ANY,              /* require notify */

    B_PRIORITY_PLACEHOLDER_1,
    B_PRIORITY_PLACEHOLDER_2,
    B_PRIORITY_PLACEHOLDER_3,
    B_PRIORITY_PLACEHOLDER_4,

    /* Address Parts (20-28) */
    B_ALL,
    B_LOCALPART,
    B_DOMAIN,
    B_USER,             /* require subaddress */
    B_DETAIL,           /* require subaddress */

    B_ADDRESS_PLACEHOLDER_1,
    B_ADDRESS_PLACEHOLDER_2,
    B_ADDRESS_PLACEHOLDER_3,
    B_ADDRESS_PLACEHOLDER_4,

    /* Comparators (29-35) */
    B_ASCIICASEMAP,
    B_OCTET,
    B_ASCIINUMERIC,     /* require comparator-i;ascii-numeric */

    B_COMPARATOR_PLACEHOLDER_1,
    B_COMPARATOR_PLACEHOLDER_2,
    B_COMPARATOR_PLACEHOLDER_3,
    B_COMPARATOR_PLACEHOLDER_4,

    /* Match Types (36-45) */
    B_IS,
    B_CONTAINS,
    B_MATCHES,
    B_REGEX,            /* require regex */
    B_COUNT,            /* require relational */
    B_VALUE,            /* require relational */
    B_LIST,             /* require extlists */

    B_MATCH_PLACEHOLDER_1,
    B_MATCH_PLACEHOLDER_2,
    B_MATCH_PLACEHOLDER_3,

    /* Body Transforms (46-53) */
    B_RAW,              /* require body */
    B_TEXT,             /* require body */
    B_CONTENT,          /* require body */

    B_TRANSFORM_PLACEHOLDER_1,
    B_TRANSFORM_PLACEHOLDER_2,
    B_TRANSFORM_PLACEHOLDER_3,
    B_TRANSFORM_PLACEHOLDER_4,
    B_TRANSFORM_PLACEHOLDER_5,

    /* Script locations (54-59) */
    B_PERSONAL,         /* require include */
    B_GLOBAL,           /* require include */

    B_LOCATION_PLACEHOLDER_1,
    B_LOCATION_PLACEHOLDER_2,
    B_LOCATION_PLACEHOLDER_3,
    B_LOCATION_PLACEHOLDER_4,

    /* Zones (60-63) */
    B_TIMEZONE,
    B_ORIGINALZONE,

    B_ZONE_PLACEHOLDER_1,
    B_ZONE_PLACEHOLDER_2,

    /* Date Parts (64-80) */
    B_YEAR,
    B_MONTH,
    B_DAY,
    B_DATE,
    B_JULIAN,
    B_HOUR,
    B_MINUTE,
    B_SECOND,
    B_TIME,
    B_ISO8601,
    B_STD11,
    B_ZONE,
    B_WEEKDAY,

    B_DATEPART_PLACEHOLDER_1,
    B_DATEPART_PLACEHOLDER_2,
    B_DATEPART_PLACEHOLDER_3,
    B_DATEPART_PLACEHOLDER_4,

    /* Duplicate ID Types (81-84) */
    B_HEADER,
    B_UNIQUEID,

    B_IDTYPE_PLACEHOLDER_1,
    B_IDTYPE_PLACEHOLDER_2
};

#define INC_LOCATION_MASK 0x3F
#define INC_ONCE_MASK     0x40
#define INC_OPTIONAL_MASK 0x80

#define SNOOZE_WDAYS_MASK 0x7F
#define SNOOZE_IS_ID_MASK 0x80

enum bytecode_variables_bitflags {
    BFV_LOWER	        = 1<<0,
    BFV_UPPER	        = 1<<1,
    BFV_LOWERFIRST      = 1<<2,
    BFV_UPPERFIRST      = 1<<3,
    BFV_QUOTEWILDCARD   = 1<<4,
    BFV_ENCODEURL	= 1<<5,
    BFV_LENGTH		= 1<<6,
    BFV_QUOTEREGEX      = 1<<7
};

#define BFV_MOD40_MASK (BFV_LOWER         | BFV_UPPER)
#define BFV_MOD30_MASK (BFV_LOWERFIRST    | BFV_UPPERFIRST)
#define BFV_MOD20_MASK (BFV_QUOTEWILDCARD | BFV_QUOTEREGEX)
#define BFV_MOD15_MASK (BFV_ENCODEURL)
#define BFV_MOD10_MASK (BFV_LENGTH)

enum bytecode_required_extensions {
    BFE_VARIABLES       = 1<<0
};

#endif
