Projects
Kolab:3.4
cyrus-imapd
cyrus-imapd-2.5-sieve-date-extension.patch
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File cyrus-imapd-2.5-sieve-date-extension.patch of Package cyrus-imapd (Revision 76)
Currently displaying revision
76
,
Show latest
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
.