[Dovecot] Sieve "header :value" test does not work
hi,
After following some examples and searching for sieve samples in this ml history, I'm quite sure that the following sieve snippet should give no surprises:
if header :value "ge" :comparator "i;ascii-numeric" "X-Header-Name" "0.99" { /* do something */ }
However the Pigeonhole Sieve 0.2.3 on dovecot 2.0.14 gives me following error:
test: line 3: error: unknown tagged argument ':value' for the header test (reported only once at first occurence). test: error: validation failed.
What obvious mistake am I failing to see here?
-- Tom
On 06/09/11 21:11, Tom Hendrikx wrote:
hi,
After following some examples and searching for sieve samples in this ml history, I'm quite sure that the following sieve snippet should give no surprises:
if header :value "ge" :comparator "i;ascii-numeric" "X-Header-Name" "0.99" { /* do something */ }
However the Pigeonhole Sieve 0.2.3 on dovecot 2.0.14 gives me following error:
test: line 3: error: unknown tagged argument ':value' for the header test (reported only once at first occurence). test: error: validation failed.
What obvious mistake am I failing to see here?
To answer my own question (which was found by grepping through /usr/share/doc/dovecot-2.0.14/sieve/rfc/*bz2): including the "relational" extension in the "require" statement adds support for this test.
Sorry for the noise :/
Tom
On 06/09/11 21:21, Tom Hendrikx wrote:
On 06/09/11 21:11, Tom Hendrikx wrote:
hi,
After following some examples and searching for sieve samples in this ml history, I'm quite sure that the following sieve snippet should give no surprises:
if header :value "ge" :comparator "i;ascii-numeric" "X-Header-Name" "0.99" { /* do something */ }
However the Pigeonhole Sieve 0.2.3 on dovecot 2.0.14 gives me following error:
test: line 3: error: unknown tagged argument ':value' for the header test (reported only once at first occurence). test: error: validation failed.
What obvious mistake am I failing to see here?
To answer my own question (which was found by grepping through /usr/share/doc/dovecot-2.0.14/sieve/rfc/*bz2): including the "relational" extension in the "require" statement adds support for this test.
Sorry for the noise :/
Tom
The above snippet poses some other issue that I cannot easily solve: the ascii-numeric comparator only handles integer values.
All 0.xxxx header values are truncated to 0 by the comparator, just like the sieve script value "0.95". After comparision, this results in true for all cases.
I don't really see a way to interact with floats in sieve, other than using regular expressions. However this gets clumsy/hairy quite fast when you're matching a hypothetical header value >=0.73 in stead of >=0.99.
Any ideas?
-- Regards, Tom
On 9/7/2011 2:40 PM, Tom Hendrikx wrote:
The above snippet poses some other issue that I cannot easily solve: the ascii-numeric comparator only handles integer values.
All 0.xxxx header values are truncated to 0 by the comparator, just like the sieve script value "0.95". After comparision, this results in true for all cases.
I don't really see a way to interact with floats in sieve, other than using regular expressions. However this gets clumsy/hairy quite fast when you're matching a hypothetical header value>=0.73 in stead of>=0.99.
Any ideas?
Yes. This can be a problem. However, the usual application for this is matching against a spam header. If it is, you can use the spamtest extension instead. Then you can configure the gory details in the background (http://wiki2.dovecot.org/Pigeonhole/Sieve/Extensions/SpamtestVirustest).
Otherwise, things indeed tend to get hairy. I've puzzled a bit and came up with the following:
==== require "variables"; require "relational"; require "comparator-i;ascii-numeric"; require "regex";
# Extract integer and fractional part separately: set "val_int" "0"; set "val_frac" "0"; if header :regex "X-Header-Name" "([0-9]+)\\.([0-9]+)" { set "val_int" "${1}"; set "val_frac" "${2}"; }
if allof ( /* Compare the integer part */ string :comparator "i;ascii-numeric" :value "ge" "${val_int}" "5", /* Compare the fractional part */ string :value "ge" "${val_frac}" "34" ) { discard; }
As you can see, the integer and fractional parts of the fractional number are extracted separately using a :regex match. Then the comparison is performed. The integer part is compared using i;ascii-numeric. Quite counter-intuitively, the fractional part is compared using a normal string comparison. The earlier regex match made sure that the ${val_frac} variable only contains digits. The string comparison makes sure that the length of the fractional part does not matter (much) and that the comparison works as expected. A length difference will only have an effect when there are spurious trailing zeros and all the preceeding digits are equal, thereby causing the longer string to have higher value, which is not strictly correct.
The above certainly does not deserve an award for beauty, it does not
handle negative numbers (can be added), and it is not tested very well.
So, use this with caution. Unfortunately, there is no i;ascii-fractional
(or whatever) collation and afaik nothing like that is in the works at
the IETF.
Regards,
Stephan.
On 07/09/11 15:48, Stephan Bosch wrote:
On 9/7/2011 2:40 PM, Tom Hendrikx wrote:
The above snippet poses some other issue that I cannot easily solve: the ascii-numeric comparator only handles integer values.
All 0.xxxx header values are truncated to 0 by the comparator, just like the sieve script value "0.95". After comparision, this results in true for all cases.
I don't really see a way to interact with floats in sieve, other than using regular expressions. However this gets clumsy/hairy quite fast when you're matching a hypothetical header value>=0.73 in stead of>=0.99.
Any ideas?
Yes. This can be a problem. However, the usual application for this is matching against a spam header. If it is, you can use the spamtest extension instead. Then you can configure the gory details in the background (http://wiki2.dovecot.org/Pigeonhole/Sieve/Extensions/SpamtestVirustest).
Otherwise, things indeed tend to get hairy. I've puzzled a bit and came up with the following:
==== require "variables"; require "relational"; require "comparator-i;ascii-numeric"; require "regex";
# Extract integer and fractional part separately: set "val_int" "0"; set "val_frac" "0"; if header :regex "X-Header-Name" "([0-9]+)\\.([0-9]+)" { set "val_int" "${1}"; set "val_frac" "${2}"; }
if allof ( /* Compare the integer part */ string :comparator "i;ascii-numeric" :value "ge" "${val_int}" "5", /* Compare the fractional part */ string :value "ge" "${val_frac}" "34" ) { discard; }
As you can see, the integer and fractional parts of the fractional number are extracted separately using a :regex match. Then the comparison is performed. The integer part is compared using i;ascii-numeric. Quite counter-intuitively, the fractional part is compared using a normal string comparison. The earlier regex match made sure that the ${val_frac} variable only contains digits. The string comparison makes sure that the length of the fractional part does not matter (much) and that the comparison works as expected. A length difference will only have an effect when there are spurious trailing zeros and all the preceeding digits are equal, thereby causing the longer string to have higher value, which is not strictly correct.
The above certainly does not deserve an award for beauty, it does not handle negative numbers (can be added), and it is not tested very well. So, use this with caution. Unfortunately, there is no i;ascii-fractional (or whatever) collation and afaik nothing like that is in the works at the IETF.
Thanks for the idea :) My first alternative way of thinking was to put the header value in a variable, then multiply it by 10000 (in my actual issue, the fraction always has 4 characters), then handle it as an int. However, doing a calculation is also not supported.
I was trying to do some nifty stuff with DSPAM headers, which adds header that indicates "Spam" or "Innocent", and a fractional value between 0 and 1 that indicates the confidence that the filter has in its own classification:
X-DSPAM-Result: Spam X-DSPAM-Confidence: 0.9456 ==> quite sure it is spam
X-DSPAM-Result: Innocent X-DSPAM-Confidence: 0.9889 ==> even more sure that it is not spam
Based on these headers, I wanted to do something with messages that the filter was really sure about, like pushing them to spamcop.
Combining these headers in the sieve spamtest configuration to create a sliding scale between 1 and 10 is not really possible.
-- Regards, Tom
On 9/7/2011 4:17 PM, Tom Hendrikx wrote:
Thanks for the idea :) My first alternative way of thinking was to put the header value in a variable, then multiply it by 10000 (in my actual issue, the fraction always has 4 characters), then handle it as an int. However, doing a calculation is also not supported.
I was trying to do some nifty stuff with DSPAM headers, which adds header that indicates "Spam" or "Innocent", and a fractional value between 0 and 1 that indicates the confidence that the filter has in its own classification:
X-DSPAM-Result: Spam X-DSPAM-Confidence: 0.9456 ==> quite sure it is spam
X-DSPAM-Result: Innocent X-DSPAM-Confidence: 0.9889 ==> even more sure that it is not spam
Based on these headers, I wanted to do something with messages that the filter was really sure about, like pushing them to spamcop.
Combining these headers in the sieve spamtest configuration to create a sliding scale between 1 and 10 is not really possible.
Not with the current implementation, no. But I could incorporate this new type of specification in the configuration capabilities. Using the spamtest :percent test, this could for instance map as follows:
Result=Innocent; Confidence=0.0000 - 1.0000 => spamtest :percent yields 50 - 0 Result=Spam; Confidence=0.0000 - 1.0000 => spamtest :percent yields 50 - 100
Other mappings are possible of course.
Regards,
Stephan.
participants (2)
-
Stephan Bosch
-
Tom Hendrikx