Projects
Kolab:3.4
cyrus-imapd
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 77
View file
cyrus-imapd.spec
Changed
@@ -68,7 +68,6 @@ Patch0001: cyrus-imapd-2.5-default-twoskip.patch Patch0002: cyrus-imapd-2.5-ctl_mboxlist-mbtype.patch -Patch0003: cyrus-imapd-2.5-sieve-date-extension.patch BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) @@ -254,7 +253,6 @@ #%patch0001 -p1 %patch0002 -p1 -%patch0003 -p1 # only to update config.* files aclocal -I cmulocal
View file
cyrus-imapd-2.5-sieve-date-extension.patch
Deleted
@@ -1,1323 +0,0 @@ -From d54c1fb7cd90610d60a5263b6372b8d0cb1bbdb8 Mon Sep 17 00:00:00 2001 -From: "Guillermo A. Amaral" <gamaral@kdab.com> -Date: Thu, 31 Jul 2014 18:17:34 -0700 -Subject: [PATCH] Sieve Date Extension - ---- - cunit/sieve.testc | 317 ++++++++++++++++++++- - lib/imapoptions | 2 +- - sieve/bc_emit.c | 70 +++++ - sieve/bc_eval.c | 189 ++++++++++++ - sieve/bc_generate.c | 111 ++++++++ - sieve/bytecode.h | 34 ++- - sieve/script.c | 4 + - sieve/script.h | 1 + - sieve/sieve-lex.l | 4 + - sieve/sieve.y | 212 ++++++++++++++ - sieve/sieved.c | 59 ++++ - .../tests/testExtension/uberExtensionTestScript.s | 26 +- - sieve/tree.c | 7 + - sieve/tree.h | 10 + - 14 files changed, 1040 insertions(+), 6 deletions(-) - -diff --git a/cunit/sieve.testc b/cunit/sieve.testc -index 47b6070..e5390b1 100644 ---- a/cunit/sieve.testc -+++ b/cunit/sieve.testc -@@ -502,7 +502,7 @@ static int set_up(void) - "partition-"PARTITION": "DBDIR"/data\n" - "sievenotifier: mailto\n" - "sieve_extensions: fileinto reject vacation imapflags notify" \ -- " envelope body relational regex subaddress copy\n" -+ " envelope body relational regex subaddress copy date\n" - ); - libcyrus_init(); - return 0; -@@ -946,6 +946,321 @@ static void test_address_localpart(void) - context_cleanup(&ctx); - } - -+static void test_date_year(void) -+{ -+ static const char SCRIPT[] = -+ "if date :is \"received\" \"year\" [ \"1983\", \"1993\", \"2003\", \"2013\" ]\n" -+ "{redirect \"me@blah.com\";}\n" -+ ; -+ static const char MSG_TRUE[] = -+ "Date: Sat, 16 Nov 2013 12:46:49 +1100\r\n" -+ "Received: from localhost (localhost [127.0.0.1])\r\n" -+ " by mail.com (Cyrus v2.3.16) with LMTPA;\r\n" -+ " Tue, 16 Nov 2013 12:50:12 +1100\r\n" -+ "From: zme@true.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ static const char MSG_FALSE[] = -+ "Date: Tue, 16 Nov 2010 12:46:49 +1100\r\n" -+ "Received: from localhost (localhost [127.0.0.1])\r\n" -+ " by mail.com (Cyrus v2.3.16) with LMTPA;\r\n" -+ " Tue, 16 Nov 2010 12:50:12 +1100\r\n" -+ "From: yme@false.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ sieve_test_context_t ctx; -+ -+ context_setup(&ctx, SCRIPT); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ -+ run_message(&ctx, MSG_TRUE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 1); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 0); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ run_message(&ctx, MSG_FALSE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 2); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 1); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ context_cleanup(&ctx); -+} -+ -+static void test_date_zone_month(void) -+{ -+ static const char SCRIPT[] = -+ "if date :is :zone \"-0800\" \"date\" \"month\" \"11\"\n" -+ "{redirect \"me@blah.com\";}\n" -+ ; -+ static const char MSG_TRUE[] = -+ "Date: Fri, 1 Nov 2013 19:46:49 +1100\r\n" -+ "From: zme@true.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ static const char MSG_FALSE[] = -+ "Date: Fri, 1 Nov 2013 11:46:49 +1100\r\n" -+ "From: yme@false.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ sieve_test_context_t ctx; -+ -+ context_setup(&ctx, SCRIPT); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ -+ run_message(&ctx, MSG_TRUE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 1); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 0); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ run_message(&ctx, MSG_FALSE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 2); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 1); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ context_cleanup(&ctx); -+} -+ -+static void test_date_date(void) -+{ -+ static const char SCRIPT[] = -+ "if date :is :originalzone \"date\" \"date\" \"2013-11-02\"\n" -+ "{redirect \"me@blah.com\";}\n" -+ ; -+ static const char MSG_TRUE[] = -+ "Date: Sat, 2 Nov 2013 19:46:49 +1100\r\n" -+ "From: zme@true.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ static const char MSG_FALSE[] = -+ "Date: Fri, 1 Nov 2013 19:45:49 +1100\r\n" -+ "From: yme@false.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ sieve_test_context_t ctx; -+ -+ context_setup(&ctx, SCRIPT); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ -+ run_message(&ctx, MSG_TRUE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 1); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 0); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ run_message(&ctx, MSG_FALSE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 2); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 1); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ context_cleanup(&ctx); -+} -+ -+static void test_date_time(void) -+{ -+ static const char SCRIPT[] = -+ "if date :is :originalzone \"date\" \"time\" \"19:46:49\"\n" -+ "{redirect \"me@blah.com\";}\n" -+ ; -+ static const char MSG_TRUE[] = -+ "Date: Sat, 2 Nov 2013 19:46:49 +1100\r\n" -+ "From: zme@true.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ static const char MSG_FALSE[] = -+ "Date: Sat, 2 Nov 2013 19:45:49 +1100\r\n" -+ "From: yme@false.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ sieve_test_context_t ctx; -+ -+ context_setup(&ctx, SCRIPT); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ -+ run_message(&ctx, MSG_TRUE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 1); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 0); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ run_message(&ctx, MSG_FALSE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 2); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 1); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ context_cleanup(&ctx); -+} -+ -+static void test_date_originalzone_day(void) -+{ -+ static const char SCRIPT[] = -+ "if date :is :originalzone \"date\" \"day\" \"16\"\n" -+ "{redirect \"me@blah.com\";}\n" -+ ; -+ static const char MSG_TRUE[] = -+ "Date: Sat, 16 Nov 2013 12:46:49 +1100\r\n" -+ "From: zme@true.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ static const char MSG_FALSE[] = -+ "Date: Fri, 15 Nov 2013 12:46:49 +1100\r\n" -+ "From: yme@false.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ sieve_test_context_t ctx; -+ -+ context_setup(&ctx, SCRIPT); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ -+ run_message(&ctx, MSG_TRUE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 1); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 0); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ run_message(&ctx, MSG_FALSE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 2); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 1); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ context_cleanup(&ctx); -+} -+ -+static void test_date_weekend_weekday(void) -+{ -+ static const char SCRIPT[] = -+ "if anyof(date :is :zone \"-0800\" \"date\" \"weekday\" \"0\",\n" -+ " date :is :zone \"-0800\" \"date\" \"weekday\" \"6\")\n" -+ "{redirect \"me@blah.com\";}\n" -+ ; -+ static const char MSG_TRUE[] = -+ "Date: Sat, 2 Nov 2013 19:46:49 +1100\r\n" -+ "From: zme@true.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ static const char MSG_FALSE[] = -+ "Date: Fri, 1 Nov 2013 11:46:49 +1100\r\n" -+ "From: yme@false.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ sieve_test_context_t ctx; -+ -+ context_setup(&ctx, SCRIPT); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ -+ run_message(&ctx, MSG_TRUE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 1); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 0); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ run_message(&ctx, MSG_FALSE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 2); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 1); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ context_cleanup(&ctx); -+} -+ -+static void test_date_zone(void) -+{ -+ static const char SCRIPT[] = -+ "if date :is :originalzone \"date\" \"zone\" \"+1100\"\n" -+ "{redirect \"me@blah.com\";}\n" -+ ; -+ static const char MSG_TRUE[] = -+ "Date: Sat, 2 Nov 2013 19:46:49 +1100\r\n" -+ "From: zme@true.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ static const char MSG_FALSE[] = -+ "Date: Fri, 1 Nov 2013 11:46:49 -0700\r\n" -+ "From: yme@false.com\r\n" -+ "To: you\r\n" -+ "Subject: simple address test\r\n" -+ "\r\n" -+ "blah\n" -+ ; -+ sieve_test_context_t ctx; -+ -+ context_setup(&ctx, SCRIPT); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ -+ run_message(&ctx, MSG_TRUE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 1); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 0); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ run_message(&ctx, MSG_FALSE); -+ CU_ASSERT_EQUAL(ctx.stats.errors, 0); -+ CU_ASSERT_EQUAL(ctx.stats.actions, 2); -+ CU_ASSERT_EQUAL(ctx.stats.redirects, 1); -+ CU_ASSERT_EQUAL(ctx.stats.keeps, 1); -+ CU_ASSERT_STRING_EQUAL(ctx.redirected_to, "me@blah.com"); -+ -+ context_cleanup(&ctx); -+} -+ - // TODO: test - // if size :over 10K { redirect "me@blah.com"; } - // TODO: test -diff --git a/lib/imapoptions b/lib/imapoptions -index 2c477a6..ee5db48 100644 ---- a/lib/imapoptions -+++ b/lib/imapoptions -@@ -1311,7 +1311,7 @@ product version in the capabilities */ - user's scripts reside on a remote server (in a Murder). - Otherwise, timsieved will proxy traffic to the remote server. */ - --{ "sieve_extensions", "fileinto reject vacation vacation-seconds imapflags notify envelope relational regex subaddress copy", BITFIELD("fileinto", "reject", "vacation", "vacation-seconds", "imapflags", "notify", "include", "envelope", "body", "relational", "regex", "subaddress", "copy") } -+{ "sieve_extensions", "fileinto reject vacation vacation-seconds imapflags notify envelope relational regex subaddress copy date", BITFIELD("fileinto", "reject", "vacation", "vacation-seconds", "imapflags", "notify", "include", "envelope", "body", "relational", "regex", "subaddress", "copy", "date") } - /* Space-separated list of Sieve extensions allowed to be used in - sieve scripts, enforced at submission by timsieved(8). Any - previously installed script will be unaffected by this option and -diff --git a/sieve/bc_emit.c b/sieve/bc_emit.c -index f010d61..f4d48c9 100644 ---- a/sieve/bc_emit.c -+++ b/sieve/bc_emit.c -@@ -347,6 +347,76 @@ static int bc_test_emit(int fd, int *codep, bytecode_info_t *bc) - break; - } - -+ case BC_DATE: -+ case BC_CURRENTDATE: -+ { -+ int ret; -+ int datalen; -+ int tmp; -+ -+ /* drop zone tag */ -+ tmp = bc->data[(*codep)].value; -+ if(write_int(fd, bc->data[(*codep)].value) == -1) -+ return -1; -+ wrote += sizeof(int); -+ (*codep)++; -+ -+ /* drop timezone offset */ -+ if (tmp == B_TIMEZONE) { -+ if(write_int(fd, bc->data[(*codep)].value) == -1) -+ return -1; -+ wrote += sizeof(int); -+ (*codep)++; -+ } -+ -+ /* drop match type */ -+ if(write_int(fd, bc->data[(*codep)].value) == -1) -+ return -1; -+ wrote += sizeof(int); -+ (*codep)++; -+ -+ /* drop relation */ -+ if(write_int(fd, bc->data[(*codep)].value) == -1) -+ return -1; -+ wrote += sizeof(int); -+ (*codep)++; -+ -+ /* drop comparator */ -+ if(write_int(fd, bc->data[(*codep)].value) == -1) -+ return -1; -+ wrote += sizeof(int); -+ (*codep)++; -+ -+ /* drop date-part */ -+ if(write_int(fd, bc->data[(*codep)].value) == -1) -+ return -1; -+ wrote += sizeof(int); -+ (*codep)++; -+ -+ /* drop header-name */ -+ { -+ datalen = bc->data[(*codep)++].len; -+ -+ if(write_int(fd, datalen) == -1) return -1; -+ wrote += sizeof(int); -+ -+ if(write(fd, bc->data[(*codep)++].str, datalen) == -1) return -1; -+ wrote += datalen; -+ -+ ret = align_string(fd,datalen); -+ if(ret == -1) return -1; -+ -+ wrote+=ret; -+ } -+ -+ /* drop keywords */ -+ ret = bc_stringlist_emit(fd, codep, bc); -+ if(ret < 0) return -1; -+ wrote+=ret; -+ -+ break; -+ } -+ - default: - /* Unknown testcode? */ - return -1; -diff --git a/sieve/bc_eval.c b/sieve/bc_eval.c -index 94c30ff..104748f 100644 ---- a/sieve/bc_eval.c -+++ b/sieve/bc_eval.c -@@ -883,6 +883,195 @@ envelope_err: - - break; - } -+ case BC_DATE:/*11*/ -+ case BC_CURRENTDATE:/*12*/ -+ { -+ char buffer[64]; -+ const char **headers = NULL; -+ const char **key; -+ const char **keylist = NULL; -+ const char *header = NULL; -+ const char *header_data; -+ const char *header_name = NULL; -+ int comparator; -+ int date_part; -+ int index; -+ int match; -+ int relation; -+ int timezone_offset = 0; -+ int zone; -+ struct tm *tm; -+ time_t t; -+ -+ ++i; /* BC_DATE */ -+ -+ /* zone tag */ -+ zone = ntohl(bc[i++].value); -+ -+ /* timezone offset */ -+ if (zone == B_TIMEZONE) { -+ timezone_offset = ntohl(bc[i++].value); -+ } -+ -+ /* comparator */ -+ match = ntohl(bc[i++].value); -+ relation = ntohl(bc[i++].value); -+ comparator = ntohl(bc[i++].value); -+ -+ /* find comparator function */ -+ comp = lookup_comp(comparator, match, relation, &comprock); -+ if(!comp) { -+ res = SIEVE_RUN_ERROR; -+ break; -+ } -+ -+ /* date-part */ -+ date_part = ntohl(bc[i++].value); -+ -+ /* header name */ -+ i = unwrap_string(bc, i, &header_name, NULL); -+ -+ -+ /* -+ * Process header -+ */ -+ -+ /* TODO: implement index extension */ -+ index = 0; -+ -+ if (interp->getheader(m, header_name, &headers) != SIEVE_OK -+ || headers[index] == NULL) { -+ res = SIEVE_FAIL; -+ goto alldone; -+ } -+ header = headers[index]; -+ -+ if (BC_CURRENTDATE == op) { -+ t = time(NULL); -+ } -+ else { -+ /* look for separator */ -+ header_data = strrchr(header, ';'); -+ if (header_data) { -+ /* separator found, skip character and continue */ -+ ++header_data; -+ } -+ else { -+ /* separator not found, use full header */ -+ header_data = header; -+ } -+ -+ if (-1 == time_from_rfc822(header_data, &t)) { -+ res = SIEVE_FAIL; -+ goto alldone; -+ } -+ } -+ -+ /* timezone offset */ -+ if (zone == B_ORIGINALZONE) { -+ char *zone; -+ char sign; -+ int hours; -+ int minutes; -+ -+ zone = strrchr(header, ' '); -+ if (!zone || -+ 3 != sscanf(zone + 1, "%c%02d%02d", &sign, &hours, &minutes)) { -+ res = SIEVE_FAIL; -+ goto alldone; -+ } -+ -+ timezone_offset = (sign == '-' ? -1 : 1) * ((hours * 60) + (minutes)); -+ } -+ -+ /* apply timezone_offset (if any) */ -+ t += timezone_offset * 60; -+ -+ /* get tm struct */ -+ tm = gmtime(&t); -+ -+ -+ /* -+ * Tests -+ */ -+ -+ if (match == B_COUNT) { -+ res = SIEVE_OK; -+ goto alldone; -+ } -+ -+ keylist = bc_makeArray(bc, &i); -+ for (key = keylist; *key; ++key) { -+ switch (date_part) { -+ case B_YEAR: -+ snprintf(buffer, sizeof(buffer), "%04d", 1900 + tm->tm_year); -+ break; -+ case B_MONTH: -+ snprintf(buffer, sizeof(buffer), "%02d", 1 + tm->tm_mon); -+ break; -+ case B_DAY: -+ snprintf(buffer, sizeof(buffer), "%02d", tm->tm_mday); -+ break; -+ case B_DATE: -+ snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d", -+ 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday); -+ break; -+ case B_JULIAN: { -+ int month, year; -+ int c, ya; -+ -+ month = 1 + tm->tm_mon; -+ year = 1900 + tm->tm_year; -+ -+ if (month > 2) { -+ month -= 3; -+ } -+ else { -+ month += 9; -+ --year; -+ } -+ c = year / 100; -+ ya = year - c * 100; -+ -+ snprintf(buffer, sizeof(buffer), "%d", -+ (c * 146097 / 4 + ya * 1461 / 4 + -+ (month * 153 + 2) / 5 + tm->tm_mday + 1721119)); -+ } break; -+ case B_HOUR: -+ snprintf(buffer, sizeof(buffer), "%02d", tm->tm_hour); -+ break; -+ case B_MINUTE: -+ snprintf(buffer, sizeof(buffer), "%02d", tm->tm_min); -+ break; -+ case B_SECOND: -+ snprintf(buffer, sizeof(buffer), "%02d", tm->tm_sec); -+ break; -+ case B_TIME: -+ snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d", -+ tm->tm_hour, tm->tm_min, tm->tm_sec); -+ break; -+ case B_ISO8601: -+ time_to_iso8601(t, buffer, sizeof(buffer)); -+ break; -+ case B_STD11: -+ time_to_rfc822(t, buffer, sizeof(buffer)); -+ break; -+ case B_ZONE: -+ snprintf(buffer, sizeof(buffer), "%c%02d%02d", -+ timezone_offset >= 0 ? '+' : '-', -+ abs(timezone_offset) / 60, -+ abs(timezone_offset) % 60); -+ break; -+ case B_WEEKDAY: -+ snprintf(buffer, sizeof(buffer), "%1d", tm->tm_wday); -+ break; -+ } -+ -+ res |= comp(buffer, strlen(buffer), *key, comprock); -+ } -+ free(keylist); -+ break; -+ } - default: - #if VERBOSE - printf("WERT, can't evaluate if statement. %d is not a valid command", -diff --git a/sieve/bc_generate.c b/sieve/bc_generate.c -index bb80a2b..8185fed 100644 ---- a/sieve/bc_generate.c -+++ b/sieve/bc_generate.c -@@ -249,6 +249,40 @@ static int bc_comparator_generate(int codep, bytecode_info_t *retval, - return codep; - } - -+static int bc_zone_generate(int codep, bytecode_info_t *retval, -+ int zonetag, const char *zone) -+{ -+ unsigned hours; -+ unsigned minutes; -+ char sign; -+ -+ assert(retval != NULL); -+ -+ /* zonetag */ -+ if (!atleast(retval, codep + 1)) return -1; -+ -+ switch (zonetag) { -+ case ZONE: -+ retval->data[codep++].value = B_TIMEZONE; -+ -+ /* time-zone offset in minutes */ -+ if (!atleast(retval, codep + 1) || -+ sscanf(zone, "%c%02u%02u", &sign, &hours, &minutes) != 3) -+ return -1; -+ -+ retval->data[codep++].value = (sign == '-' ? -1 : 1) * (hours * 60) + minutes; -+ break; -+ case ORIGINALZONE: -+ retval->data[codep++].value = B_ORIGINALZONE; -+ break; -+ default: -+ return -1; -+ } -+ -+ return codep; -+} -+ -+ - - - /* writes a single test into almost-flat form starting at codep. -@@ -410,6 +444,83 @@ static int bc_test_generate(int codep, bytecode_info_t *retval, test_t *t) - if (codep == -1) return -1; - - break; -+ case DATE: -+ case CURRENTDATE: -+ /* BC_DATE { time-zone: string} { c: comparator } -+ * { header-name : string } { date-part: string } -+ * { key-list : string list } -+ */ -+ -+ if(!atleast(retval,codep + 1)) return -1; -+ retval->data[codep++].op = (DATE == t->type) ? BC_DATE : BC_CURRENTDATE; -+ -+ /* zone */ -+ codep = bc_zone_generate(codep, retval, -+ t->u.dt.zonetag, -+ t->u.dt.zone); -+ if (codep == -1) return -1; -+ -+ /* comparator */ -+ codep = bc_comparator_generate(codep, retval, -+ t->u.dt.comptag, -+ t->u.dt.relation, -+ t->u.dt.comparator); -+ if (codep == -1) return -1; -+ -+ /* date-part */ -+ if(!atleast(retval,codep + 1)) return -1; -+ switch (t->u.dt.date_part) { -+ case YEAR: -+ retval->data[codep++].value = B_YEAR; -+ break; -+ case MONTH: -+ retval->data[codep++].value = B_MONTH; -+ break; -+ case DAY: -+ retval->data[codep++].value = B_DAY; -+ break; -+ case DATE: -+ retval->data[codep++].value = B_DATE; -+ break; -+ case JULIAN: -+ retval->data[codep++].value = B_JULIAN; -+ break; -+ case HOUR: -+ retval->data[codep++].value = B_HOUR; -+ break; -+ case MINUTE: -+ retval->data[codep++].value = B_MINUTE; -+ break; -+ case SECOND: -+ retval->data[codep++].value = B_SECOND; -+ break; -+ case TIME: -+ retval->data[codep++].value = B_TIME; -+ break; -+ case ISO8601: -+ retval->data[codep++].value = B_ISO8601; -+ break; -+ case STD11: -+ retval->data[codep++].value = B_STD11; -+ break; -+ case ZONE: -+ retval->data[codep++].value = B_ZONE; -+ break; -+ case WEEKDAY: -+ retval->data[codep++].value = B_WEEKDAY; -+ break; -+ } -+ -+ /* header-name */ -+ if(!atleast(retval,codep + 2)) return -1; -+ retval->data[codep++].len = strlen(t->u.dt.header_name); -+ retval->data[codep++].str = t->u.dt.header_name; -+ -+ /* keywords */ -+ codep = bc_stringlist_generate(codep, retval, t->u.dt.kl); -+ if (codep == -1) return -1; -+ -+ break; - default: - return -1; - -diff --git a/sieve/bytecode.h b/sieve/bytecode.h -index acb3755..e8998b4 100644 ---- a/sieve/bytecode.h -+++ b/sieve/bytecode.h -@@ -158,7 +158,9 @@ enum bytecode_comps { - BC_ADDRESS, - BC_ENVELOPE, /* require envelope */ - BC_HEADER, -- BC_BODY /* require body */ -+ BC_BODY, /* require body */ -+ BC_DATE, /* require date */ -+ BC_CURRENTDATE /* require date */ - }; - - /* currently one enum so as to help determine where values are being misused. -@@ -246,8 +248,34 @@ enum bytecode_tags { - B_LOCATION_PLACEHOLDER_1, - B_LOCATION_PLACEHOLDER_2, - B_LOCATION_PLACEHOLDER_3, -- B_LOCATION_PLACEHOLDER_4 -- -+ B_LOCATION_PLACEHOLDER_4, -+ -+ /* Zones */ -+ B_TIMEZONE, -+ B_ORIGINALZONE, -+ -+ B_ZONE_PLACEHOLDER_1, -+ B_ZONE_PLACEHOLDER_2, -+ -+ /* Date Parts */ -+ 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 - }; - - #endif -diff --git a/sieve/script.c b/sieve/script.c -index e47917c..043dba0 100644 ---- a/sieve/script.c -+++ b/sieve/script.c -@@ -180,6 +180,10 @@ int script_require(sieve_script_t *s, char *req) - (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_COPY)) { - s->support.copy = 1; - return 1; -+ } else if (!strcmp("date", req) && -+ (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_DATE)) { -+ s->support.date = 1; -+ return 1; - } - return 0; - } -diff --git a/sieve/script.h b/sieve/script.h -index fefa4af..490cf45 100644 ---- a/sieve/script.h -+++ b/sieve/script.h -@@ -68,6 +68,7 @@ struct sieve_script { - int i_ascii_numeric: 1; - int include : 1; - int copy : 1; -+ int date : 1; - int vacation_seconds: 1; - } support; - -diff --git a/sieve/sieve-lex.l b/sieve/sieve-lex.l -index 00c82fe..876f4c9 100644 ---- a/sieve/sieve-lex.l -+++ b/sieve/sieve-lex.l -@@ -171,6 +171,10 @@ CRLF (\r\n|\r|\n) - <INITIAL>:once return ONCE; - <INITIAL>return return RETURN; - <INITIAL>:copy return COPY; -+<INITIAL>date return DATE; -+<INITIAL>currentdate return CURRENTDATE; -+<INITIAL>:zone return ZONE; -+<INITIAL>:originalzone return ORIGINALZONE; - <INITIAL>[ \t\n\r] ; /* ignore whitespace */ - <INITIAL>#.* ; /* ignore hash comments */ - <INITIAL>"/*"([^\*]|\*[^\/])*\*?"*/" ; /* ignore bracket comments */ -diff --git a/sieve/sieve.y b/sieve/sieve.y -index f69ddc4..310aa3c 100644 ---- a/sieve/sieve.y -+++ b/sieve/sieve.y -@@ -123,12 +123,22 @@ struct itags { - int optional; - }; - -+struct dttags { -+ int zonetag; -+ char *zone; -+ int comptag; -+ int relation; -+ char *comparator; -+ int date_part; -+}; -+ - static char *check_reqs(sieve_script_t *script, strarray_t *sl); - static test_t *build_address(int t, struct aetags *ae, - strarray_t *sl, strarray_t *pl); - static test_t *build_header(int t, struct htags *h, - strarray_t *sl, strarray_t *pl); - static test_t *build_body(int t, struct btags *b, strarray_t *pl); -+static test_t *build_date(int t, struct dttags *dt, char *hn, strarray_t *kl); - static commandlist_t *build_vacation(int t, struct vtags *h, char *s); - static commandlist_t *build_notify(int t, struct ntags *n); - static commandlist_t *build_denotify(int t, struct dtags *n); -@@ -154,6 +164,9 @@ static struct dtags *new_dtags(void); - static struct dtags *canon_dtags(struct dtags *d); - static void free_dtags(struct dtags *d); - static struct itags *new_itags(void); -+static struct dttags *new_dttags(void); -+static struct dttags *canon_dttags(struct dttags *dt); -+static void free_dttags(struct dttags *b); - - static int verify_stringlist(sieve_script_t*, strarray_t *sl, int (*verify)(sieve_script_t*, char *)); - static int verify_mailbox(sieve_script_t*, char *s); -@@ -163,6 +176,8 @@ static int verify_addrheader(sieve_script_t*, char *s); - static int verify_envelope(sieve_script_t*, char *s); - static int verify_flag(sieve_script_t*, char *s); - static int verify_relat(sieve_script_t*, char *s); -+static int verify_zone(sieve_script_t*, char *s); -+static int verify_date_part(sieve_script_t *parse_script, char *dp); - #ifdef ENABLE_REGEX - static int verify_regex(sieve_script_t*, char *s, int cflags); - static int verify_regexs(sieve_script_t*,const strarray_t *sl, char *comp); -@@ -194,6 +209,7 @@ extern void sieverestart(FILE *f); - struct ntags *ntag; - struct dtags *dtag; - struct itags *itag; -+ struct dttags *dttag; - } - - %token <nval> NUMBER -@@ -211,6 +227,8 @@ extern void sieverestart(FILE *f); - %token METHOD ID OPTIONS LOW NORMAL HIGH ANY MESSAGE - %token INCLUDE PERSONAL GLOBAL RETURN OPTIONAL ONCE - %token COPY -+%token DATE CURRENTDATE ZONE ORIGINALZONE -+%token YEAR MONTH DAY JULIAN HOUR MINUTE SECOND TIME ISO8601 STD11 ZONE WEEKDAY - - %type <cl> commands command action elsif block - %type <sl> stringlist strings -@@ -224,6 +242,7 @@ extern void sieverestart(FILE *f); - %type <ntag> ntags - %type <dtag> dtags - %type <itag> itags -+%type <dttag> dttags - %type <nval> priority - - %name-prefix="sieve" -@@ -585,6 +604,32 @@ test: ANYOF testlist { $$ = new_test(ANYOF); $$->u.tl = $2; } - | NOT test { $$ = new_test(NOT); $$->u.t = $2; } - | SIZE sizetag NUMBER { $$ = new_test(SIZE); $$->u.sz.t = $2; - $$->u.sz.n = $3; } -+ | DATE dttags STRING STRING stringlist -+ { -+ $2->date_part = verify_date_part(parse_script, $4); -+ if ($2->date_part == -1) -+ { YYERROR; /*vr called yyerror()*/ } -+ -+ $2 = canon_dttags($2); -+ -+ $$ = build_date(DATE, $2, $3, $5); -+ if ($$ == NULL) { -+ yyerror(parse_script, "unable to find a compatible comparator"); -+ YYERROR; } -+ } -+ | CURRENTDATE dttags STRING STRING stringlist -+ { -+ $2->date_part = verify_date_part(parse_script, $4); -+ if ($2->date_part == -1) -+ { YYERROR; /*vr called yyerror()*/ } -+ -+ $2 = canon_dttags($2); -+ -+ $$ = build_date(CURRENTDATE, $2, $3, $5); -+ if ($$ == NULL) { -+ yyerror(parse_script, "unable to find a compatible comparator"); -+ YYERROR; } -+ } - | error { $$ = NULL; } - ; - -@@ -688,6 +733,43 @@ btags: /* empty */ { $$ = new_btags(); } - $$->comparator = $3; } } - ; - -+dttags: /* empty */ { $$ = new_dttags(); } -+ | dttags comptag { $$ = $1; -+ if ($$->comptag != -1) { -+ yyerror(parse_script, "duplicate comparator type tag"); YYERROR; } -+ else { $$->comptag = $2; } } -+ -+ | dttags relcomp STRING { $$ = $1; -+ if ($$->comptag != -1) { -+ yyerror(parse_script, "duplicate comparator type tag"); YYERROR; } -+ else { -+ $$->comptag = $2; -+ $$->relation = verify_relat(parse_script, $3); -+ if ($$->relation == -1) { -+ YYERROR; /*vr called yyerror()*/ } } } -+ -+ | dttags COMPARATOR STRING { $$ = $1; -+ if ($$->comparator != NULL) { -+ yyerror(parse_script, "duplicate comparator tag"); YYERROR; } -+ else if (!strcmp($3, "i;ascii-numeric") && -+ !parse_script->support.i_ascii_numeric) { -+ yyerror(parse_script, "comparator-i;ascii-numeric MUST be enabled with \"require\""); YYERROR; } -+ else { $$->comparator = $3; } } -+ -+ | dttags ZONE STRING { $$ = $1; -+ if ($$->zonetag != -1) { -+ yyerror(parse_script, "duplicate zone tag"); YYERROR; } -+ else { -+ if (verify_zone(parse_script, $3) == -1) { -+ YYERROR; /*vr called yyerror()*/ } -+ else { $$->zone = $3; -+ $$->zonetag = ZONE; } } } -+ -+ | dttags ORIGINALZONE { $$ = $1; -+ if ($$->zonetag != -1) { -+ yyerror(parse_script, "duplicate zone tag"); YYERROR; } -+ else { $$->zonetag = ORIGINALZONE; } } -+ ; - - addrparttag: ALL { $$ = ALL; } - | LOCALPART { $$ = LOCALPART; } -@@ -943,6 +1025,27 @@ static commandlist_t *build_include(int t, struct itags *i, char* script) - return ret; - } - -+static test_t *build_date(int t, struct dttags *dt, -+ char *hn, strarray_t *kl) -+{ -+ test_t *ret = new_test(t); -+ assert(t == DATE || t == CURRENTDATE); -+ -+ if (ret) { -+ ret->u.dt.zone = (dt->zone ? xstrdup(dt->zone) : NULL); -+ ret->u.dt.comparator = xstrdup(dt->comparator); -+ ret->u.dt.zonetag = dt->zonetag; -+ ret->u.dt.comptag = dt->comptag; -+ ret->u.dt.relation = dt->relation; -+ ret->u.dt.date_part = dt->date_part; -+ ret->u.dt.header_name = hn; -+ ret->u.dt.kl = kl; -+ free_dttags(dt); -+ } -+ return ret; -+} -+ -+ - static struct aetags *new_aetags(void) - { - struct aetags *r = (struct aetags *) xmalloc(sizeof(struct aetags)); -@@ -1077,6 +1180,54 @@ static struct itags *new_itags() { - return r; - } - -+static struct dttags *new_dttags(void) -+{ -+ struct dttags *dt = (struct dttags *) xmalloc(sizeof(struct dttags)); -+ dt->comptag = -1; -+ dt->zonetag = -1; -+ dt->relation = -1; -+ dt->comparator = NULL; -+ dt->zone = NULL; -+ dt->date_part = -1; -+ return dt; -+} -+ -+static struct dttags *canon_dttags(struct dttags *dt) -+{ -+ char zone[6]; -+ int gmoffset; -+ int hours; -+ int minutes; -+ struct tm *tm; -+ time_t t; -+ -+ if (dt->comparator == NULL) { -+ dt->comparator = xstrdup("i;ascii-casemap"); -+ } -+ if (dt->zonetag == -1) { -+ t = time(NULL); -+ tm = localtime(&t); -+ gmoffset = gmtoff_of(tm, &t) / 60; -+ hours = abs(gmoffset) / 60; -+ minutes = abs(gmoffset) % 60; -+ snprintf(zone, 6, "%c%02d%02d", (gmoffset >= 0 ? '+' : '-'), hours, minutes); -+ dt->zone = xstrdup(zone); -+ dt->zonetag = ZONE; -+ } -+ if (dt->comptag == -1) { -+ dt->comptag = IS; -+ } -+ return dt; -+} -+ -+static void free_dttags(struct dttags *dt) -+{ -+ free(dt->comparator); -+ free(dt->zone); -+ free(dt); -+} -+ -+ - static struct ntags *new_ntags(void) - { - struct ntags *r = (struct ntags *) xmalloc(sizeof(struct ntags)); -@@ -1242,6 +1393,67 @@ static int verify_relat(sieve_script_t *parse_script, char *r) - - } - -+static int verify_zone(sieve_script_t *parse_script, char *tz) -+{ -+ int valid = 0; -+ unsigned hours; -+ unsigned minutes; -+ char sign; -+ -+ if (sscanf(tz, "%c%02u%02u", &sign, &hours, &minutes) != 3) { -+ valid |= -1; -+ } -+ -+ // test sign -+ switch (sign) { -+ case '+': -+ case '-': -+ break; -+ -+ default: -+ valid |= -1; -+ break; -+ } -+ -+ // test minutes -+ if (minutes > 59) { -+ valid |= -1; -+ } -+ -+ if (valid != 0) { -+ snprintf(parse_script->sieveerr, ERR_BUF_SIZE, -+ "flag '%s': not a valid timezone offset", tz); -+ yyerror(parse_script, parse_script->sieveerr); -+ } -+ -+ return valid; -+} -+ -+static int verify_date_part(sieve_script_t *parse_script, char *dp) -+{ -+ lcase(dp); -+ if (!strcmp(dp, "year")) { return YEAR; } -+ else if (!strcmp(dp, "month")) { return MONTH; } -+ else if (!strcmp(dp, "day")) { return DAY; } -+ else if (!strcmp(dp, "date")) { return DATE; } -+ else if (!strcmp(dp, "julian")) { return JULIAN; } -+ else if (!strcmp(dp, "hour")) { return HOUR; } -+ else if (!strcmp(dp, "minute")) { return MINUTE; } -+ else if (!strcmp(dp, "second")) { return SECOND; } -+ else if (!strcmp(dp, "time")) { return TIME; } -+ else if (!strcmp(dp, "iso8601")) { return ISO8601; } -+ else if (!strcmp(dp, "std11")) { return STD11; } -+ else if (!strcmp(dp, "zone")) { return ZONE; } -+ else if (!strcmp(dp, "weekday")) { return WEEKDAY; } -+ else { -+ snprintf(parse_script->sieveerr, ERR_BUF_SIZE, -+ "flag '%s': not a valid relational operation", dp); -+ yyerror(parse_script, parse_script->sieveerr); -+ } -+ -+ return -1; -+} -+ - - - -diff --git a/sieve/sieved.c b/sieve/sieved.c -index df09c39..4700c54 100644 ---- a/sieve/sieved.c -+++ b/sieve/sieved.c -@@ -324,6 +324,65 @@ static int dump2_test(bytecode_input_t * d, int i) - i=write_list(ntohl(d[i].len), i+1, d); - printf(" ]\n"); - break; -+ case BC_DATE:/*11*/ -+ case BC_CURRENTDATE:/*12*/ -+ /* current date */ -+ if (BC_DATE == ntohl(d[i++].value)) { -+ printf("date ["); -+ } -+ else { -+ printf("currentdate ["); -+ } -+ -+ /* zone tag */ -+ { -+ int zone; -+ int timezone_offset; -+ -+ printf("Zone-Tag: "); -+ zone = ntohl(d[i++].value); -+ switch (zone) { -+ case B_TIMEZONE: -+ timezone_offset = ntohl(d[i++].value); -+ printf("Specific timezone: offset by %d minutes.\n", timezone_offset); -+ break; -+ case B_ORIGINALZONE: -+ printf("Original zone.\n"); -+ break; -+ } -+ } -+ -+ i=printComparison(d, i); -+ -+ printf(" Date-Type: "); -+ switch(ntohl(d[i++].value)) -+ { -+ case B_YEAR: printf("year\n"); break; -+ case B_MONTH: printf("month\n"); break; -+ case B_DAY: printf("day\n"); break; -+ case B_JULIAN: printf("julian\n"); break; -+ case B_HOUR: printf("hour\n"); break; -+ case B_MINUTE: printf("minute\n"); break; -+ case B_SECOND: printf("second\n"); break; -+ case B_TIME: printf("time\n"); break; -+ case B_ISO8601: printf("iso8601\n"); break; -+ case B_STD11: printf("std11\n"); break; -+ case B_ZONE: printf("zone\n"); break; -+ case B_WEEKDAY: printf("weekday\n"); break; -+ } -+ -+ /* header name */ -+ { -+ const char *data; -+ int len; -+ i = unwrap_string(d, i, &data, &len); -+ printf(" Header Name: {%d}%s\n", len, data); -+ } -+ -+ printf(" Key List: "); -+ i=write_list(ntohl(d[i].len), i+1, d); -+ printf(" ]\n"); -+ break; - default: - printf("WERT %d ", ntohl(d[i].value)); - } -diff --git a/sieve/tests/testExtension/uberExtensionTestScript.s b/sieve/tests/testExtension/uberExtensionTestScript.s -index d5d75a1..0ce0379 100644 ---- a/sieve/tests/testExtension/uberExtensionTestScript.s -+++ b/sieve/tests/testExtension/uberExtensionTestScript.s -@@ -1,4 +1,4 @@ --require ["regex", "relational", "comparator-i;ascii-numeric", "subaddress", "envelope"]; -+require ["regex", "relational", "comparator-i;ascii-numeric", "subaddress", "envelope", "date"]; - - #this is for the extra thigns we have added to sieve - #test extensions -@@ -145,3 +145,27 @@ if envelope :detail :contains "from" "k" - - if envelope :detail :matches "from" "e*k" - {redirect "me+goodedetailmatches@blah.com";} -+ -+###################################################################### -+#DATE -+###################################################################### -+ -+if allof(header :is "from" "boss@example.com", -+ date :value "ge" :originalzone "date" "hour" "09", -+ date :value "lt" :originalzone "date" "hour" "17") -+{redirect "me+urgent@blah.com";} -+ -+if anyof(date :is "received" "weekday" "0", -+ date :is "received" "weekday" "6") -+{redirect "me+weekend@blah.com";} -+ -+if anyof(date :is :zone "-0800" "received" "weekday" "0", -+ date :is :zone "-0800" "received" "weekday" "6") -+{redirect "me+weekend(pst)@blah.com";} -+ -+if date :is "received" "year" [ "1983", "1993", "2003", "2013" ] -+{redirect "me+yearsofthree@blah.com";} -+ -+if currentdate :value "ge" :originalzone "received" "year" "2013" -+{redirect "me+yearsofthree@blah.com";} -+ -diff --git a/sieve/tree.c b/sieve/tree.c -index fa4b629..fff83fd 100644 ---- a/sieve/tree.c -+++ b/sieve/tree.c -@@ -159,6 +159,13 @@ void free_test(test_t *t) - case NOT: - free_test(t->u.t); - break; -+ -+ case DATE: -+ case CURRENTDATE: -+ free(t->u.dt.comparator); -+ free(t->u.dt.zone); -+ strarray_free(t->u.dt.kl); -+ break; - } - - free(t); -diff --git a/sieve/tree.h b/sieve/tree.h -index 3884e54..ecb42d6 100644 ---- a/sieve/tree.h -+++ b/sieve/tree.h -@@ -101,6 +101,16 @@ struct Test { - int t; /* tag */ - int n; /* param */ - } sz; -+ struct { /* it's a date test */ -+ int zonetag; -+ char *zone; -+ int comptag; -+ int relation; -+ char *comparator; -+ int date_part; -+ char *header_name; -+ strarray_t *kl; -+ } dt; - } u; - }; - --- -1.9.3 -
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.