1 /* interp.c -- sieve script interpreter builder
2  * Larry Greenfield
3  *
4  * Copyright (c) 1994-2008 Carnegie Mellon University.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * 3. The name "Carnegie Mellon University" must not be used to
19  *    endorse or promote products derived from this software without
20  *    prior written permission. For permission or any legal
21  *    details, please contact
22  *      Carnegie Mellon University
23  *      Center for Technology Transfer and Enterprise Creation
24  *      4615 Forbes Avenue
25  *      Suite 302
26  *      Pittsburgh, PA  15213
27  *      (412) 268-7393, fax: (412) 268-7395
28  *      innovation@andrew.cmu.edu
29  *
30  * 4. Redistributions of any form whatsoever must retain the following
31  *    acknowledgment:
32  *    "This product includes software developed by Computing Services
33  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
34  *
35  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
36  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
37  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
38  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
39  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
40  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
41  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
42  */
43 
44 #ifdef HAVE_CONFIG_H
45 #include <config.h>
46 #endif
47 
48 #include <stdlib.h>
49 #include <string.h>
50 
51 #include "xmalloc.h"
52 #include "xstrlcat.h"
53 
54 #include "sieve_interface.h"
55 #include "interp.h"
56 #include "libconfig.h"
57 #include "times.h"
58 #include "util.h"
59 
60 /* build a sieve interpreter */
sieve_interp_alloc(void * interp_context)61 EXPORTED sieve_interp_t *sieve_interp_alloc(void *interp_context)
62 {
63     sieve_interp_t *i;
64     static int initonce;
65 
66     if (!initonce) {
67         initialize_siev_error_table();
68         initonce = 1;
69     }
70 
71     i = (sieve_interp_t *) xzmalloc(sizeof(sieve_interp_t));
72 
73     i->interp_context = interp_context;
74     i->extensions = NULL;
75 
76     i->time = time(NULL);
77 
78     return i;
79 }
80 
sieve_listextensions(sieve_interp_t * i)81 EXPORTED const strarray_t *sieve_listextensions(sieve_interp_t *i)
82 {
83     if (i->extensions == NULL) {
84         unsigned long config_sieve_extensions =
85             config_getbitfield(IMAPOPT_SIEVE_EXTENSIONS);
86         struct buf buf = BUF_INITIALIZER;
87         int ext_pos;
88 
89         /* strarray of ManageSieve capability/value pairs */
90         i->extensions = strarray_new();
91 
92         /* Add SIEVE capability */
93         strarray_append(i->extensions, "SIEVE");
94 
95         /* Add placeholder for Sieve extensions string */
96         ext_pos = strarray_append(i->extensions, NULL);
97 
98         /* Build Sieve extensions string */
99         buf_setcstr(&buf, "encoded-character");
100 
101         /* add comparators */
102         buf_appendcstr(&buf, " comparator-i;ascii-numeric");
103 
104         /* add actions */
105         if (i->fileinto &&
106             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_FILEINTO))
107             buf_appendcstr(&buf, " fileinto");
108         if (i->reject &&
109             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_REJECT))
110             buf_appendcstr(&buf, " reject ereject");
111         if (i->vacation &&
112             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_VACATION_SECONDS))
113             buf_appendcstr(&buf, " vacation vacation-seconds");
114         else if (i->vacation &&
115             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_VACATION))
116             buf_appendcstr(&buf, " vacation");
117         if (i->notify &&
118             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_NOTIFY)) {
119             buf_appendcstr(&buf, " notify enotify");
120 
121             /* Add NOTIFY capability */
122             strarray_append(i->extensions, "NOTIFY");
123             strarray_append(i->extensions, "mailto");
124         }
125         if (i->getinclude &&
126             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_INCLUDE))
127             buf_appendcstr(&buf, " include");
128         if (i->addheader &&
129             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_EDITHEADER))
130             buf_appendcstr(&buf, " editheader");
131 #if 0  /* Don't advertise this to ManageSieve clients -
132           We probably don't want end users adding this action themselves */
133         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_VND_CYRUS_LOG)
134             buf_appendcstr(&buf, " vnd.cyrus.log");
135 #endif
136         if (i->snooze &&
137             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_SNOOZE))
138             buf_appendcstr(&buf, " vnd.cyrus.snooze");
139 
140         /* add tests */
141         if (i->getenvelope &&
142             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_ENVELOPE))
143             buf_appendcstr(&buf, " envelope");
144         if (i->getenvironment &&
145             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_ENVIRONMENT))
146             buf_appendcstr(&buf, " environment");
147         if (i->getbody &&
148             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_BODY))
149             buf_appendcstr(&buf, " body");
150         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_IMAP4FLAGS)
151             buf_appendcstr(&buf, " imap4flags");
152         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_DATE)
153             buf_appendcstr(&buf, " date");
154         if ((config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_IHAVE))
155             buf_appendcstr(&buf, " ihave");
156         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_MAILBOX)
157             buf_appendcstr(&buf, " mailbox");
158         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_MBOXMETADATA)
159             buf_appendcstr(&buf, " mboxmetadata");
160         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_SERVERMETADATA)
161             buf_appendcstr(&buf, " servermetadata");
162         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_DUPLICATE)
163             buf_appendcstr(&buf, " duplicate");
164         if (i->jmapquery &&
165             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_VND_CYRUS_JMAPQUERY))
166             buf_appendcstr(&buf, " vnd.cyrus.jmapquery");
167 
168         /* add match-types */
169         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_RELATIONAL)
170             buf_appendcstr(&buf, " relational");
171 #ifdef ENABLE_REGEX
172         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_REGEX)
173             buf_appendcstr(&buf, " regex");
174 #endif
175         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_EXTLISTS) {
176             buf_appendcstr(&buf, " extlists");
177 
178             /* Add EXTLISTS capability */
179             strarray_append(i->extensions, "EXTLISTS");
180             strarray_append(i->extensions, "urn:ietf:params:sieve:addrbook");
181         }
182 
183         /* add misc extensions */
184         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_SUBADDRESS)
185             buf_appendcstr(&buf, " subaddress");
186         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_COPY)
187             buf_appendcstr(&buf, " copy");
188         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_INDEX)
189             buf_appendcstr(&buf, " index");
190         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_VARIABLES)
191             buf_appendcstr(&buf, " variables");
192         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_REDIRECT_DELIVERBY)
193             buf_appendcstr(&buf, " redirect-deliverby");
194         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_REDIRECT_DSN)
195             buf_appendcstr(&buf, " redirect-dsn");
196         if (i->getspecialuseexists &&
197             (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_SPECIAL_USE))
198             buf_appendcstr(&buf, " special-use");
199         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_FCC)
200             buf_appendcstr(&buf, " fcc");
201         if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_MAILBOXID)
202             buf_appendcstr(&buf, " mailboxid");
203 
204         /* Set Sieve extensions string */
205         strarray_setm(i->extensions, ext_pos, buf_release(&buf));
206     }
207 
208     return i->extensions;
209 }
210 
sieve_interp_free(sieve_interp_t ** interp)211 EXPORTED int sieve_interp_free(sieve_interp_t **interp)
212 {
213     if (*interp) {
214         free((*interp)->lastitem);
215         strarray_free((*interp)->extensions);
216         free(*interp);
217         *interp = NULL;
218     }
219 
220     return SIEVE_OK;
221 }
222 
223 /* add the callbacks */
sieve_register_redirect(sieve_interp_t * interp,sieve_callback * f)224 EXPORTED void sieve_register_redirect(sieve_interp_t *interp, sieve_callback *f)
225 {
226     interp->redirect = f;
227 }
228 
sieve_register_discard(sieve_interp_t * interp,sieve_callback * f)229 EXPORTED void sieve_register_discard(sieve_interp_t *interp, sieve_callback *f)
230 {
231     interp->discard = f;
232 }
233 
sieve_register_reject(sieve_interp_t * interp,sieve_callback * f)234 EXPORTED void sieve_register_reject(sieve_interp_t *interp, sieve_callback *f)
235 {
236     interp->reject = f;
237 }
238 
sieve_register_fileinto(sieve_interp_t * interp,sieve_callback * f)239 EXPORTED void sieve_register_fileinto(sieve_interp_t *interp, sieve_callback *f)
240 {
241     interp->fileinto = f;
242 }
243 
sieve_register_snooze(sieve_interp_t * interp,sieve_callback * f)244 EXPORTED void sieve_register_snooze(sieve_interp_t *interp, sieve_callback *f)
245 {
246     interp->snooze = f;
247 }
248 
sieve_register_keep(sieve_interp_t * interp,sieve_callback * f)249 EXPORTED void sieve_register_keep(sieve_interp_t *interp, sieve_callback *f)
250 {
251     interp->keep = f;
252 }
253 
sieve_register_notify(sieve_interp_t * interp,sieve_callback * f,const strarray_t * methods)254 EXPORTED void sieve_register_notify(sieve_interp_t *interp,
255                                     sieve_callback *f, const strarray_t *methods)
256 {
257     static strarray_t default_methods = STRARRAY_INITIALIZER;
258 
259     if (!default_methods.count)
260         strarray_append(&default_methods, "mailto:");
261 
262     interp->notifymethods =
263         (methods && methods->data && methods->count) ? methods : &default_methods;
264 
265     interp->notify = f;
266 }
267 
268 /* add the callbacks for messages. again, undefined if used after
269    sieve_script_parse */
sieve_register_size(sieve_interp_t * interp,sieve_get_size * f)270 EXPORTED void sieve_register_size(sieve_interp_t *interp, sieve_get_size *f)
271 {
272     interp->getsize = f;
273 }
274 
sieve_register_mailboxexists(sieve_interp_t * interp,sieve_get_mailboxexists * f)275 EXPORTED void sieve_register_mailboxexists(sieve_interp_t *interp, sieve_get_mailboxexists *f)
276 {
277     interp->getmailboxexists = f;
278 }
279 
sieve_register_mailboxidexists(sieve_interp_t * interp,sieve_get_mailboxidexists * f)280 EXPORTED void sieve_register_mailboxidexists(sieve_interp_t *interp, sieve_get_mailboxidexists *f)
281 {
282     interp->getmailboxidexists = f;
283 }
284 
sieve_register_metadata(sieve_interp_t * interp,sieve_get_metadata * f)285 EXPORTED void sieve_register_metadata(sieve_interp_t *interp, sieve_get_metadata *f)
286 {
287     interp->getmetadata = f;
288 }
289 
sieve_register_specialuseexists(sieve_interp_t * interp,sieve_get_specialuseexists * f)290 EXPORTED void sieve_register_specialuseexists(sieve_interp_t *interp, sieve_get_specialuseexists *f)
291 {
292     interp->getspecialuseexists = f;
293 }
294 
sieve_register_header(sieve_interp_t * interp,sieve_get_header * f)295 EXPORTED void sieve_register_header(sieve_interp_t *interp, sieve_get_header *f)
296 {
297     interp->getheader = f;
298 }
299 
sieve_register_headersection(sieve_interp_t * interp,sieve_get_headersection * f)300 EXPORTED void sieve_register_headersection(sieve_interp_t *interp,
301                                            sieve_get_headersection *f)
302 {
303     interp->getheadersection = f;
304 }
305 
sieve_register_addheader(sieve_interp_t * interp,sieve_add_header * f)306 EXPORTED int sieve_register_addheader(sieve_interp_t *interp, sieve_add_header *f)
307 {
308     if (!interp->getheadersection) {
309         return SIEVE_NOT_FINALIZED; /* we need getheadersection for editheader! */
310     }
311 
312     interp->addheader = f;
313     return SIEVE_OK;
314 }
315 
sieve_register_deleteheader(sieve_interp_t * interp,sieve_delete_header * f)316 EXPORTED int sieve_register_deleteheader(sieve_interp_t *interp, sieve_delete_header *f)
317 {
318     if (!interp->getheadersection) {
319         return SIEVE_NOT_FINALIZED; /* we need getheadersection for editheader! */
320     }
321 
322     interp->deleteheader = f;
323     return SIEVE_OK;
324 }
325 
sieve_register_fname(sieve_interp_t * interp,sieve_get_fname * f)326 EXPORTED void sieve_register_fname(sieve_interp_t *interp, sieve_get_fname *f)
327 {
328     interp->getfname = f;
329 }
330 
sieve_register_envelope(sieve_interp_t * interp,sieve_get_envelope * f)331 EXPORTED void sieve_register_envelope(sieve_interp_t *interp, sieve_get_envelope *f)
332 {
333     interp->getenvelope = f;
334 }
335 
sieve_register_include(sieve_interp_t * interp,sieve_get_include * f)336 EXPORTED void sieve_register_include(sieve_interp_t *interp, sieve_get_include *f)
337 {
338     interp->getinclude = f;
339 }
340 
sieve_register_logger(sieve_interp_t * interp,sieve_logger * f)341 EXPORTED void sieve_register_logger(sieve_interp_t *interp, sieve_logger *f)
342 {
343     interp->log = f;
344 }
345 
sieve_register_environment(sieve_interp_t * interp,sieve_get_environment * f)346 EXPORTED void sieve_register_environment(sieve_interp_t *interp,
347                                          sieve_get_environment *f)
348 {
349     interp->getenvironment = f;
350 }
351 
sieve_register_body(sieve_interp_t * interp,sieve_get_body * f)352 EXPORTED void sieve_register_body(sieve_interp_t *interp, sieve_get_body *f)
353 {
354     interp->getbody = f;
355 }
356 
sieve_register_vacation(sieve_interp_t * interp,sieve_vacation_t * v)357 EXPORTED int sieve_register_vacation(sieve_interp_t *interp, sieve_vacation_t *v)
358 {
359     if (!interp->getenvelope) {
360         return SIEVE_NOT_FINALIZED; /* we need envelope for vacation! */
361     }
362 
363     if (v->min_response == 0)
364         v->min_response = config_getduration(IMAPOPT_SIEVE_VACATION_MIN_RESPONSE, 's');
365     if (v->max_response == 0)
366         v->max_response = config_getduration(IMAPOPT_SIEVE_VACATION_MAX_RESPONSE, 's');
367     if (v->min_response < 0 || v->max_response < 7 * DAY2SEC || !v->autorespond
368         || !v->send_response) {
369         return SIEVE_FAIL;
370     }
371 
372     interp->vacation = v;
373     return SIEVE_OK;
374 }
375 
sieve_register_extlists(sieve_interp_t * interp,sieve_list_validator * v,sieve_list_comparator * c)376 EXPORTED void sieve_register_extlists(sieve_interp_t *interp,
377                                       sieve_list_validator *v,
378                                       sieve_list_comparator *c)
379 {
380     interp->isvalidlist = v;
381     interp->listcompare = c;
382 }
383 
sieve_register_duplicate(sieve_interp_t * interp,sieve_duplicate_t * d)384 EXPORTED int sieve_register_duplicate(sieve_interp_t *interp,
385                                       sieve_duplicate_t *d)
386 {
387     if (!interp->getheader) {
388         return SIEVE_NOT_FINALIZED; /* we need header for duplicate! */
389     }
390 
391     if (!(d->check && d->track)) {
392         return SIEVE_FAIL;
393     }
394 
395     if (d->max_expiration > 7776000) d->max_expiration = 7776000;  /* 90 days */
396 
397     interp->duplicate = d;
398     return SIEVE_OK;
399 }
400 
sieve_register_jmapquery(sieve_interp_t * interp,sieve_jmapquery * f)401 EXPORTED void sieve_register_jmapquery(sieve_interp_t *interp, sieve_jmapquery *f)
402 {
403     interp->jmapquery = f;
404 }
405 
sieve_register_parse_error(sieve_interp_t * interp,sieve_parse_error * f)406 EXPORTED void sieve_register_parse_error(sieve_interp_t *interp, sieve_parse_error *f)
407 {
408     interp->err = f;
409 }
410 
sieve_register_execute_error(sieve_interp_t * interp,sieve_execute_error * f)411 EXPORTED void sieve_register_execute_error(sieve_interp_t *interp, sieve_execute_error *f)
412 {
413     interp->execute_err = f;
414 }
415 
interp_verify(sieve_interp_t * i)416 int interp_verify(sieve_interp_t *i)
417 {
418     if (i->redirect && i->keep && i->getsize && i->getheader) {
419         return SIEVE_OK;
420     } else {
421         return SIEVE_NOT_FINALIZED;
422     }
423 }
424 
425 /* Array of Sieve capabilities */
426 static const struct sieve_capa_t {
427     const char *str;
428     unsigned long long flag;
429 } sieve_capabilities[] =
430 {
431     /* Sieve "base" - RFC 5228 */
432     { "comparator-i;octet",         SIEVE_CAPA_BASE },
433     { "comparator-i;ascii-casemap", SIEVE_CAPA_BASE },
434     { "comparator-i;ascii-numeric", SIEVE_CAPA_COMP_NUMERIC },
435 
436     { "encoded-character", SIEVE_CAPA_ENCODED_CHAR },
437     { "envelope",          SIEVE_CAPA_ENVELOPE },
438     { "fileinto",          SIEVE_CAPA_FILEINTO },
439 
440     /* Regular Expressions - draft-ietf-sieve-regex */
441     { "regex", SIEVE_CAPA_REGEX },
442 
443     /* Copy - RFC 3894 */
444     { "copy", SIEVE_CAPA_COPY },
445 
446     /* Body - RFC 5173 */
447     { "body", SIEVE_CAPA_BODY },
448 
449     /* Environment - RFC 5183 */
450     { "environment", SIEVE_CAPA_ENVIRONMENT },
451 
452     /* Variables - RFC 5229 */
453     { "variables", SIEVE_CAPA_VARIABLES },
454 
455     /* Vacation - RFC 5230 */
456     { "vacation", SIEVE_CAPA_VACATION },
457 
458     /* Relational - RFC 5231 */
459     { "relational", SIEVE_CAPA_RELATIONAL },
460 
461     /* IMAP4 Flags - RFC 5232 */
462     { "imap4flags", SIEVE_CAPA_IMAP4FLAGS },
463     { "imapflags",  SIEVE_CAPA_IMAP4FLAGS }, /* draft-melnikov-sieve-imapflags-04 */
464 
465     /* Subaddress - RFC 5233 */
466     { "subaddress", SIEVE_CAPA_SUBADDRESS },
467 
468     /* Spamtest & Virustest - RFC 5235 */
469     { "spamtest",     SIEVE_CAPA_SPAM },
470     { "spamtestplus", SIEVE_CAPA_SPAMPLUS },
471     { "virustest",    SIEVE_CAPA_VIRUS },
472 
473     /* Date & Index - RFC 5260 */
474     { "date",  SIEVE_CAPA_DATE },
475     { "index", SIEVE_CAPA_INDEX },
476 
477     /* Editheader - RFC 5293 */
478     { "editheader", SIEVE_CAPA_EDITHEADER },
479 
480     /* [Extended] Reject - RFC 5429 */
481     { "ereject", SIEVE_CAPA_EREJECT },
482     { "reject",  SIEVE_CAPA_REJECT },
483 
484     /* Notifications - RFC 5435 */
485     { "enotify", SIEVE_CAPA_ENOTIFY },
486     { "notify",  SIEVE_CAPA_NOTIFY }, /* draft-martin-sieve-notify-01 */
487 
488     /* Ihave - RFC 5463 */
489     { "ihave", SIEVE_CAPA_IHAVE },
490 
491     /* Mailbox & Metadata - RFC 5490 */
492     { "mailbox",        SIEVE_CAPA_MAILBOX },
493     { "mboxmetadata",   SIEVE_CAPA_MBOXMETA },
494     { "servermetadata", SIEVE_CAPA_SERVERMETA },
495 
496     /* MIME Part Handling - RFC 5703 */
497     { "enclose",      SIEVE_CAPA_ENCLOSE },
498     { "extracttest",  SIEVE_CAPA_EXTRACT },
499     { "foreverypart", SIEVE_CAPA_FOREVERYPART },
500     { "mime",         SIEVE_CAPA_MIME },
501     { "replace",      SIEVE_CAPA_REPLACE },
502 
503     /* DSN & Deliver-By - RFC 6009 */
504     { "envelope-deliverby", SIEVE_CAPA_ENV_DELBY },
505     { "envelope-dsn",       SIEVE_CAPA_ENV_DSN },
506     { "redirect-deliverby", SIEVE_CAPA_REDIR_DELBY },
507     { "redirect-dsn",       SIEVE_CAPA_REDIR_DSN },
508 
509     /* Vacation :seconds - RFC 6131 */
510     { "vacation-seconds", SIEVE_CAPA_VACATION_SEC },
511 
512     /* External Lists - RFC 6134 */
513     { "extlists", SIEVE_CAPA_EXTLISTS },
514 
515     /* Convert - RFC 6558 */
516     { "convert", SIEVE_CAPA_CONVERT },
517 
518     /* Include - RFC 6609 */
519     { "include", SIEVE_CAPA_INCLUDE },
520 
521     /* IMAP Events - RFC 6785 */
522     { "imapsieve", SIEVE_CAPA_IMAP },
523 
524     /* Duplicate - RFC 7352 */
525     { "duplicate", SIEVE_CAPA_DUPLICATE },
526 
527     /* Special-Use - RFC 8579 */
528     { "special-use", SIEVE_CAPA_SPECIAL_USE },
529 
530     /* Fcc - RFC 8580 */
531     { "fcc", SIEVE_CAPA_FCC },
532 
533     /* Mailboxid - draft-ietf-extra-sieve-mailboxid */
534     { "mailboxid", SIEVE_CAPA_MAILBOXID },
535 
536     /* Log - vnd.cyrus.log */
537     { "vnd.cyrus.log", SIEVE_CAPA_LOG },
538     { "x-cyrus-log",   SIEVE_CAPA_LOG },              // legacy capability
539 
540     /* JMAP Query - vnd.cyrus.jmapquery */
541     { "vnd.cyrus.jmapquery", SIEVE_CAPA_JMAPQUERY },
542     { "x-cyrus-jmapquery",   SIEVE_CAPA_JMAPQUERY },  // legacy capability
543 
544     /* Snooze - draft-ietf-extra-sieve-snooze */
545     { "snooze",           SIEVE_CAPA_SNOOZE },
546     { "vnd.cyrus.snooze", SIEVE_CAPA_SNOOZE },        // legacy capability
547     { "x-cyrus-snooze",   SIEVE_CAPA_SNOOZE },        // legacy capability
548 
549     { NULL, 0 }
550 };
551 
552 
lookup_capability_string(unsigned long long flag)553 EXPORTED const char *lookup_capability_string(unsigned long long flag)
554 {
555     const struct sieve_capa_t *capa;
556 
557     for (capa = sieve_capabilities; capa->str; capa++) {
558         if (flag == capa->flag) return capa->str;
559     }
560 
561     return NULL;
562 }
563 
lookup_capability(const char * str)564 unsigned long long lookup_capability(const char *str)
565 {
566     const struct sieve_capa_t *capa;
567 
568     for (capa = sieve_capabilities; capa->str; capa++) {
569         if (!strcmp(str, capa->str)) return capa->flag;
570     }
571 
572     return 0;
573 }
574 
extension_isactive(sieve_interp_t * interp,const char * str)575 unsigned long long extension_isactive(sieve_interp_t *interp, const char *str)
576 {
577     unsigned long config_ext = config_getbitfield(IMAPOPT_SIEVE_EXTENSIONS);
578     unsigned long long capa = lookup_capability(str);
579 
580     switch (capa) {
581     case SIEVE_CAPA_BASE:
582     case SIEVE_CAPA_COMP_NUMERIC:
583     case SIEVE_CAPA_ENCODED_CHAR:
584         /* always enabled */
585         break;
586 
587     case SIEVE_CAPA_ENVELOPE:
588         if (!(interp->getenvelope &&
589               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_ENVELOPE))) capa = 0;
590         break;
591 
592     case SIEVE_CAPA_FILEINTO:
593         if (!(interp->fileinto &&
594               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_FILEINTO))) capa = 0;
595         break;
596 
597     case SIEVE_CAPA_REGEX:
598         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_REGEX)) capa = 0;
599         break;
600 
601     case SIEVE_CAPA_COPY:
602         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_COPY)) capa = 0;
603         break;
604 
605     case SIEVE_CAPA_ENVIRONMENT:
606         if (!(interp->getenvironment &&
607               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_ENVIRONMENT))) capa = 0;
608         break;
609 
610     case SIEVE_CAPA_BODY:
611         if (!(interp->getbody &&
612               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_BODY))) capa = 0;
613         break;
614 
615     case SIEVE_CAPA_VARIABLES:
616         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_VARIABLES)) capa = 0;
617         break;
618 
619     case SIEVE_CAPA_VACATION:
620         if (!(interp->vacation &&
621               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_VACATION))) capa = 0;
622         break;
623 
624     case SIEVE_CAPA_RELATIONAL:
625         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_RELATIONAL)) capa = 0;
626         break;
627 
628     case SIEVE_CAPA_IMAP4FLAGS:
629         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_IMAP4FLAGS)) capa = 0;
630         break;
631 
632     case SIEVE_CAPA_SUBADDRESS:
633         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_SUBADDRESS)) capa = 0;
634         break;
635 
636     case SIEVE_CAPA_DATE:
637         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_DATE)) capa = 0;
638         break;
639 
640     case SIEVE_CAPA_INDEX:
641         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_INDEX)) capa = 0;
642         break;
643 
644     case SIEVE_CAPA_EDITHEADER:
645         if (!(interp->addheader && interp->deleteheader &&
646               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_EDITHEADER))) capa = 0;
647         break;
648 
649     case SIEVE_CAPA_EREJECT:
650     case SIEVE_CAPA_REJECT:
651         if (!(interp->reject &&
652               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_REJECT))) capa = 0;
653         break;
654 
655     case SIEVE_CAPA_ENOTIFY:
656     case SIEVE_CAPA_NOTIFY:
657         if (!(interp->notify &&
658               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_NOTIFY))) capa = 0;
659         break;
660 
661     case SIEVE_CAPA_IHAVE:
662         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_IHAVE)) capa = 0;
663         break;
664 
665     case SIEVE_CAPA_MAILBOX:
666         if (!(interp->getmailboxexists &&
667               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_MAILBOX))) capa = 0;
668         break;
669 
670     case SIEVE_CAPA_MBOXMETA:
671         if (!(interp->getmetadata &&
672               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_MBOXMETADATA))) capa = 0;
673         break;
674 
675     case SIEVE_CAPA_SERVERMETA:
676         if (!(interp->getmetadata &&
677               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_SERVERMETADATA))) capa = 0;
678         break;
679 
680     case SIEVE_CAPA_VACATION_SEC:
681         if (!(interp->vacation &&
682               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_VACATION_SECONDS))) {
683             capa = 0;
684         } else {
685             /* Note that "vacation-seconds" implies "vacation", and a script
686              * with "vacation-seconds" in a "require" list MAY omit "vacation"
687              * from that list. */
688             capa |= SIEVE_CAPA_VACATION;
689         }
690         break;
691 
692     case SIEVE_CAPA_EXTLISTS:
693         if (!(interp->isvalidlist && interp->listcompare &&
694               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_EXTLISTS) &&
695               (config_getbitfield(IMAPOPT_HTTPMODULES) & IMAP_ENUM_HTTPMODULES_CARDDAV)))
696             capa = 0;
697         break;
698 
699     case SIEVE_CAPA_INCLUDE:
700         if (!(interp->getinclude &&
701               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_INCLUDE))) capa = 0;
702         break;
703 
704     case SIEVE_CAPA_DUPLICATE:
705         if (!(interp->duplicate &&
706               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_DUPLICATE))) capa = 0;
707         break;
708 
709     case SIEVE_CAPA_SPECIAL_USE:
710         if (!(interp->getspecialuseexists &&
711               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_SPECIAL_USE))) capa = 0;
712         break;
713 
714     case SIEVE_CAPA_FCC:
715         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_FCC)) capa = 0;
716         break;
717 
718     case SIEVE_CAPA_REDIR_DELBY:
719         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_REDIRECT_DELIVERBY))
720             capa = 0;
721         break;
722 
723     case SIEVE_CAPA_REDIR_DSN:
724         if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_REDIRECT_DSN)) capa = 0;
725         break;
726 
727     case SIEVE_CAPA_MAILBOXID:
728         if (!(interp->getmailboxidexists &&
729               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_MAILBOXID))) capa = 0;
730         break;
731 
732     case SIEVE_CAPA_LOG:
733         if (!(interp->log &&
734               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_VND_CYRUS_LOG))) capa = 0;
735         break;
736 
737     case SIEVE_CAPA_JMAPQUERY:
738         if (!(interp->jmapquery &&
739               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_VND_CYRUS_JMAPQUERY)))
740             capa = 0;
741         break;
742 
743     case SIEVE_CAPA_SNOOZE:
744         if (!(interp->snooze &&
745               (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_SNOOZE))) capa = 0;
746         break;
747 
748     default:
749         /* not supported */
750         capa = 0;
751         break;
752     }
753 
754     return (capa);
755 }
756