ietf-mta-filters
[Top] [All Lists]

Re: variables: open issue a) date variables

2003-05-06 05:58:19
On Monday 05 May 2003 00:19, Kjetil Torgrim Homme wrote:
<snip>
I don't agree that ${year} is a system variable.

Granted it's not a clean-cut one, but see below.

  > I think an explicit action to set them is easier to handle
  > than a multitude of variables which can change as side-effects.

  But they _do_ change as side effects. You can't go and pretend
  they don't.

I wouldn't say it's a side effect, SETDATE is quite explicit. 
nothing else will change the variables.
<snip>

It's a side-effect of executing setdate. I call this a side-effect, 
since it's not an explicit "set".

  >   SET "hour" "${_internal_startup_hour}";
<snip>
${_internal_startup_hour} doesn't exist...

So how can SET "hour" ${_internal_startup_hour}" work?

  > what actions can influence ${_imapflags}?  how will its value
  > change?

  ${#imapflags} is (if resolved) a space-separated list of imap
  flags. The order of flags in ${#imapflags} is undefined (it's
  conceptually a set).

so

  if string :matches "${#imapflags}" "*\\Answered*\\Flagged*" {
    ...
  }

may or may not match even if both flags are set.

Correct.

(easily fixed, just 
specify lexical sorting of the "set" at all times.)

I always wondered why imapflags didn't include a flag test. It should, 
esp. if one considers the include extension where a included script 
doesn't know what it's parent has already done and might want to know.

  addflag
  - adds the flags given as arg to #imapflags then
  - removes all duplicates.

  removeflag
  - removes all flags given as arg from #imapflags

wouldn't it be better to make generic functions for this?
    addflag "flag" "\\Flagged";
changes ${flag}.  you'd now use
    keep :flags "${flag}";
rather than
    keep :globalflags;
and
    keep :flags "${flag} \\Flagged";
instead of
    keep :globalflags_plus "\\Flagged";

some scripts may become more verbose, but the complexity of the
IMAPFLAGS is decreased, IMO.  (ADDFLAG and REMOVEFLAG could move to a
separate extension.)

One could always add the feature to accept multiple flags per string 
argument, so you can use

set "myflags" "${#imapflags}" # save away
# ...
setflags "${myflags}" # restore

I fact, you already used that feature in the code above. Also, keep in 
mind that imapflags implementations already exist and you can imagine 
what a confusion would arise if addflags' et al. syntax would be 
changed.

  setflag
  - clears #imapflags
  - executes addflag <args>

what would SETFLAG "${#imapflags}" do?  just one of those things
which need to be clarified.

With the above feature, it would be a no-op.

  mark
  - equivalent to addflag "\\Flagged"

  unmark
  - equivalent to removeflag "\\Flagged"

what is the rationale for MARK and UNMARK?  according to the draft
these actions are _not_ equivalent to ADDFLAG "\\Flagged".

But IIRC, it's suggested they are. However, this is nitpicking again, 
since it's clear that the exact (un)mark semantics are 
implementation-defined. I've just chosen the obvious one.

  Summary:
  There are two issues, let's separate them:
  1. Should set be allowed to act on system variables?
     I hope to have made it clear that that's a bad idea.

I strongly agree!

:-)

  2. Should system variables be prefixed?
     Well, why _not_? If it makes set's job easier to determine if
     something is read-only or not? If it makes system variable
     references more explicit?

I don't want to introduce system variables at all.  I haven't seen a
compelling case for adding them.

Ok, here's what I think are properties of system variables, why they are 
needed and why they should be prefixed:

1. System variables are implicitly changed by certain commands and vice
   versa: the value of system variables affects certain commands.

   Examples: ${#imapflags} is changed by {add,remove,set}flags,
             {un,}mark. It implicitly affects keep, fileinto and a
             potential "flags" test.

             A ${#currentdate} variable may affect the to-be-defined
             date test.

   This is the most visible property of system variables, though in
   itself alone it doesn't require system variables to be handled
   specially.

2. They are read-only to "set".

   Examples: ${#imapflags} should be written to only by the imapflags
             actions.
             ${#currentdate} should never be written to.

   So the set action needs to raise an error if the script author tries
   to assign to a system variable. That begs the question of how set
   should know what is a system variable and what isn't. You could keep
   a list of system variables, which is complicated by the fact that (as
   you said) it should be perfectly OK to use the name of a system
   variable as long as the corresponding extension is not "required".
   But apart from the fact that you forgot about the include extension
   (where variables have global, but require'd extensions local scope),
   it would be much easier (for user _and_ engine) if system variables
   had a common prefix to detect them.

3. They may represent concepts that are not naturally represented as
   strings.

   Examples: ${#imapflags} is a set of imap flags, not a string.
             ${#currentdate} is a number or struct tm or whatever, not a
             string.

   There are many issues if you allow ${#imapflags} to be "set" by the
   user (duplicate or invalid flags, ordering issues). However, it's a
   variable (remember that imapflags was what prompted a variables
   extension in the first place, even if you were not personally driven
   by that) and there was objection to define extensions that have any
   kind of "internal variable" (as imapflags once called it) without
   first having variables for Sieve in general.

   It is, however, potentially very useful to allow interpolation into
   strings, and a system variable declaration must specify the string
   representation to use. I've already given an example for
   ${#imapflags}, ${#currentdate} could be interpolated in rfc2822
   format.

   So system variables could be handled internally in their natural
   representation, with a string representation being required only on
   expansion into strings, which should be pretty rare.

4. Their values may not be computed with Sieve.

   You said setdate would be a macro for a list of set's. But it isn't.
   You can't represent setdate "+0200" like that.

   E.g. a hypothetical ${#envelope_sender} system variable would not be
   a good idea, since you can always obtain that by using the
   matches-set idiom:
     if :envelope :all :matches "from" "*" {
       set "myenvelopesender" "${1}";
     }

5. Their values may not be available from the message.

   Example: ${#currentdate}

I now agree that ${year} as specified is not a proper system variable.

Here's a proposal for a date test (which is important to have in it own 
right) that makes setdate superfluous by using the matches-set idiom to 
explicitly set the setdate variables:

if date :current :timezone "+0200" :hour :matches "*" {
  set "${hour}" "${1}";
}

where the date test syntax would be:

date [COMPARATOR] WHICH [TIMEZONE] [DATE-PART] [MATCH-TYPE]
     <key-list: string-list>

WHICH := ":current" / ":received" / ":sent"
         / ( ":header" <headerfield: string-list> )
DATE-PART := ":weekday" / ":day" / ":month" / ":year"
         / ":hour" / ":minute" / ":second" / ":numericzone"
         / ":all" / ... ; ":all" is default
TIMEZONE := ":timezone" <timezone: string>
     ; server's local timezone is default for :current,
       timezone specified in the header is default for :received, :sent
       and :header.

Together with the relational extension and a rfc822-date comparator, you 
could check for dates:

  if allof( date :comparator "rfc822-date"
      :current :value "ge" "Tue, 06 May 2003 14:02:33 +0200",
            date :comparator "rfc822-date"
      :current :all :value "le" "Tue, 13 May 2003 14:04:05 +0200" )
  {
    vacation "blah, blubb";
  }

And for those that want "old" setdate:

  set "setdate_timezone" "CEST"
  include :global "setdate";

where "setdate" includes the matches-set idioms:

--begin datevariables--
require [ "variables", "dates" ];

if date :current :timezone "${datevariables_get_timezone}"
   :year :matches "*" {
  set "year" "${1}";
}
if date ... # and so on for any other setdate variable
--end datevariables--

In this scenario, ${#currentdate} would be a system variable with all 
properties as defined above: It affects the date :current test, is 
read-only, it's natural representation is not a string, it's value 
can't be computed with Sieve and it can't be extracted from the 
message.

Marc

-- 
Ein Grundrecht auf Sicherheit steht bewusst nicht in der Verfassung.
  -- Sabine Leutheusser-Schnarrenberger (ehem. Bundesjustizministerin)

Attachment: pgpVczfI47Kpn.pgp
Description: signature