1 /*
2
3 Copyright (C) 1999-2004 Aaron Stone aaron at serendipity dot cx
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later
9 version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 *
21 * Functions for running user defined sorting rules
22 * on a message in the temporary store, usually
23 * just delivering the message to the user's INBOX
24 * ...unless they have fancy rules defined, that is :-)
25 *
26 */
27
28 #include "dbmail.h"
29
30 #define THIS_MODULE "sort"
31
32 /* Used by us to keep track of libSieve. */
33 struct sort_context {
34 char *s_buf;
35 char *script;
36 uint64_t user_idnr;
37 DbmailMessage *message;
38 struct sort_result *result;
39 GList *freelist;
40 };
41
42 /* Returned opaquely as type SortResult_T. */
43 struct sort_result {
44 int cancelkeep;
45 const char *mailbox;
46 int reject;
47 GString *rejectmsg;
48 int error_runtime;
49 int error_parse;
50 GString *errormsg;
51 };
52
53 /* [DELIVERY] SIEVE_* settings in dbmail.conf */
54 struct sort_sieve_config {
55 int vacation;
56 int notify;
57 int debug;
58 };
59
sort_sieve_get_config(struct sort_sieve_config * sieve_config)60 static void sort_sieve_get_config(struct sort_sieve_config *sieve_config)
61 {
62 Field_T val;
63
64 assert(sieve_config != NULL);
65
66 sieve_config->vacation = 0;
67 sieve_config->notify = 0;
68 sieve_config->debug = 0;
69
70 config_get_value("SIEVE_VACATION", "DELIVERY", val);
71 if (strcasecmp(val, "yes") == 0) {
72 sieve_config->vacation = 1;
73 }
74
75 config_get_value("SIEVE_NOTIFY", "DELIVERY", val);
76 if (strcasecmp(val, "yes") == 0) {
77 sieve_config->notify= 1;
78 }
79
80 config_get_value("SIEVE_DEBUG", "DELIVERY", val);
81 if (strcasecmp(val, "yes") == 0) {
82 sieve_config->debug = 1;
83 }
84 }
85
86 /*
87 * Send a vacation message. FIXME: this should provide
88 * MIME support, to comply with the Sieve-Vacation spec.
89 */
send_vacation(DbmailMessage * message,const char * to,const char * from,const char * subject,const char * body,const char * handle)90 static int send_vacation(DbmailMessage *message,
91 const char *to, const char *from,
92 const char *subject, const char *body, const char *handle)
93 {
94 int result;
95 const char *x_dbmail_vacation = dbmail_message_get_header(message, "X-Dbmail-Vacation");
96
97 if (x_dbmail_vacation) {
98 TRACE(TRACE_NOTICE, "vacation loop detected [%s]", x_dbmail_vacation);
99 return 0;
100 }
101
102 DbmailMessage *new_message = dbmail_message_new(message->pool);
103 new_message = dbmail_message_construct(new_message, to, from, subject, body);
104 dbmail_message_set_header(new_message, "X-DBMail-Vacation", handle);
105
106 result = send_mail(new_message, to, from, NULL, SENDMESSAGE, SENDMAIL);
107
108 dbmail_message_free(new_message);
109
110 return result;
111 }
112
send_redirect(DbmailMessage * message,const char * to,const char * from)113 static int send_redirect(DbmailMessage *message, const char *to, const char *from)
114 {
115 if (!to || !from) {
116 TRACE(TRACE_ERR, "both To and From addresses must be specified");
117 return -1;
118 }
119
120 return send_mail(message, to, from, NULL, SENDRAW, SENDMAIL);
121 }
122
send_alert(uint64_t user_idnr,char * subject,char * body)123 int send_alert(uint64_t user_idnr, char *subject, char *body)
124 {
125 DbmailMessage *new_message;
126 Field_T postmaster;
127 char *from;
128 int msgflags[IMAP_NFLAGS];
129
130 // Only send each unique alert once a day.
131 char *tmp = g_strconcat(subject, body, NULL);
132 char *userchar = g_strdup_printf("%" PRIu64 "", user_idnr);
133 char handle[FIELDSIZE];
134
135 memset(handle, 0, sizeof(handle));
136 dm_md5(tmp, handle);
137 if (db_replycache_validate(userchar, "send_alert", handle, 1) != DM_SUCCESS) {
138 TRACE(TRACE_INFO, "Already sent alert [%s] to user [%" PRIu64 "] today", subject, user_idnr);
139 g_free(userchar);
140 g_free(tmp);
141 return 0;
142 } else {
143 TRACE(TRACE_INFO, "Sending alert [%s] to user [%" PRIu64 "]", subject, user_idnr);
144 db_replycache_register(userchar, "send_alert", handle);
145 g_free(userchar);
146 g_free(tmp);
147 }
148
149 // From the Postmaster.
150 if (config_get_value("POSTMASTER", "DBMAIL", postmaster) < 0) {
151 TRACE(TRACE_NOTICE, "no config value for POSTMASTER");
152 }
153 if (strlen(postmaster))
154 from = postmaster;
155 else
156 from = DEFAULT_POSTMASTER;
157
158 // Set the \Flagged flag.
159 memset(msgflags, 0, sizeof(msgflags));
160 msgflags[IMAP_FLAG_FLAGGED] = 1;
161
162 // Get the user's login name.
163 char *to = auth_get_userid(user_idnr);
164
165 new_message = dbmail_message_new(NULL);
166 new_message = dbmail_message_construct(new_message, to, from, subject, body);
167
168 // Pre-insert the message and get a new_message->id
169 dbmail_message_store(new_message);
170 uint64_t tmpid = new_message->id;
171
172 if (sort_deliver_to_mailbox(new_message, user_idnr,
173 "INBOX", BOX_BRUTEFORCE, msgflags, NULL) != DSN_CLASS_OK) {
174 TRACE(TRACE_ERR, "Unable to deliver alert [%s] to user [%" PRIu64 "]", subject, user_idnr);
175 }
176
177 g_free(to);
178 db_delete_message(tmpid);
179 dbmail_message_free(new_message);
180
181 return 0;
182 }
183
184
185 /* SIEVE CALLBACKS */
186
187 /*
188 From http://www.ietf.org/internet-drafts/draft-ietf-sieve-vacation-06.txt
189
190 Usage: vacation [":days" number] [":subject" string]
191 [":from" string] [":addresses" string-list]
192 [":mime"] [":handle" string] <reason: string>
193
194 The parameters that an implementation needs to know about are:
195 days, subject, from, mime, handle, reason. Addresses is used
196 internally by libSieve implementation to comply with RFCs.
197
198 We need to make sure to respect the implementation requirements.
199 */
sort_vacation(sieve2_context_t * s,void * my)200 int sort_vacation(sieve2_context_t *s, void *my)
201 {
202 struct sort_context *m = (struct sort_context *)my;
203 const char *message, *subject, *fromaddr, *handle;
204 const char *rc_to, *rc_from;
205 char rc_handle[FIELDSIZE];
206 int days;
207 //int mime;
208
209 days = sieve2_getvalue_int(s, "days");
210 //mime = sieve2_getvalue_int(s, "mime"); // mime: 1 if message is mime coded. FIXME.
211 message = sieve2_getvalue_string(s, "message");
212 subject = sieve2_getvalue_string(s, "subject");
213 fromaddr = sieve2_getvalue_string(s, "fromaddr"); // From: specified by the script.
214 handle = sieve2_getvalue_string(s, "hash");
215
216 /* Default to a week, upper limit of a month.
217 * This is our only loop prevention mechanism! The value must be
218 * greater than 0, else the replycache code will always indicate
219 * that we haven't seen anything since 0 days ago... */
220 if (days == 0) days = 7;
221 if (days < 1) days = 1;
222 if (days > 30) days = 30;
223
224 memset(rc_handle, 0, sizeof(rc_handle));
225 dm_md5((char * const) handle, rc_handle);
226
227 // FIXME: should be validated as a user might try
228 // to forge an address from their script.
229 rc_from = fromaddr;
230 if (!rc_from)
231 rc_from = dbmail_message_get_header(m->message, "Delivered-To");
232 if (!rc_from)
233 rc_from = p_string_str(m->message->envelope_recipient);
234
235 rc_to = dbmail_message_get_header(m->message, "Reply-To");
236 if (!rc_to)
237 rc_to = dbmail_message_get_header(m->message, "Return-Path");
238
239 if (db_replycache_validate(rc_to, rc_from, rc_handle, days) == DM_SUCCESS) {
240 if (send_vacation(m->message, rc_to, rc_from, subject, message, rc_handle) == 0)
241 db_replycache_register(rc_to, rc_from, rc_handle);
242 TRACE(TRACE_INFO, "Sending vacation to [%s] from [%s] handle [%s] repeat days [%d]",
243 rc_to, rc_from, rc_handle, days);
244 } else {
245 TRACE(TRACE_INFO, "Vacation suppressed to [%s] from [%s] handle [%s] repeat days [%d]",
246 rc_to, rc_from, rc_handle, days);
247 }
248
249 m->result->cancelkeep = 0;
250 return SIEVE2_OK;
251 }
252
253 /*
254 From http://www.ietf.org/internet-drafts/draft-ietf-sieve-notify-07.txt
255
256 Usage: notify [":from" string] [":importance" <"1" / "2" / "3">]
257 [":options" string-list] [":message" string] <method: string>
258 */
sort_notify(sieve2_context_t * s,void * my)259 int sort_notify(sieve2_context_t *s, void *my)
260 {
261 struct sort_context *m = (struct sort_context *)my;
262 const char *fromaddr;
263 //const char *message;
264 //const char *method;
265 const char *rc_to, *rc_from;
266 //int importance;
267 //char * const * options;
268
269 fromaddr = sieve2_getvalue_string(s, "fromaddr");
270 //method = sieve2_getvalue_string(s, "method");
271 //message = sieve2_getvalue_string(s, "message");
272 //importance = sieve2_getvalue_int(s, "importance");
273 //options = sieve2_getvalue_stringlist(s, "options");
274
275 // FIXME: should be validated as a user might try
276 // to forge an address from their script.
277 rc_from = fromaddr;
278 if (!rc_from)
279 rc_from = dbmail_message_get_header(m->message, "Delivered-To");
280 if (!rc_from)
281 rc_from = p_string_str(m->message->envelope_recipient);
282
283 rc_to = dbmail_message_get_header(m->message, "Reply-To");
284 if (!rc_to)
285 rc_to = dbmail_message_get_header(m->message, "Return-Path");
286
287 // send_notification(m->message, rc_to, rc_from, method, message);
288
289 TRACE(TRACE_INFO, "Notification from [%s] to [%s] was not sent as notify is not supported in this release.", rc_from, rc_to);
290
291 return SIEVE2_OK;
292 }
293
sort_redirect(sieve2_context_t * s,void * my)294 int sort_redirect(sieve2_context_t *s, void *my)
295 {
296 struct sort_context *m = (struct sort_context *)my;
297 const char *to;
298 const char *from;
299
300 to = sieve2_getvalue_string(s, "address");
301
302 TRACE(TRACE_INFO, "Action is REDIRECT: REDIRECT destination is [%s].",
303 to);
304
305 /* According to a clarification from the ietf-mta-filter mailing list,
306 * the redirect is supposed to be absolutely transparent: the envelope
307 * sender is the original envelope sender, with only the envelope
308 * recipient changed. As a fallback, we'll use the redirecting user. */
309 from = dbmail_message_get_header(m->message, "Return-Path");
310 if (!from)
311 from = p_string_str(m->message->envelope_recipient);
312
313 if (send_redirect(m->message, to, from) != 0) {
314 return SIEVE2_ERROR_FAIL;
315 }
316
317 m->result->cancelkeep = 1;
318 return SIEVE2_OK;
319 }
320
sort_reject(sieve2_context_t * s,void * my)321 int sort_reject(sieve2_context_t *s, void *my)
322 {
323 struct sort_context *m = (struct sort_context *)my;
324 const char *message;
325
326 message = sieve2_getvalue_string(s, "message");
327
328 TRACE(TRACE_INFO, "Action is REJECT: REJECT message is [%s].", message);
329
330 m->result->rejectmsg = g_string_new(message);
331
332 /* Reject also discards. */
333 m->result->cancelkeep = 1;
334 m->result->reject = 1;
335 return SIEVE2_OK;
336 }
337
sort_discard(sieve2_context_t * s UNUSED,void * my)338 int sort_discard(sieve2_context_t *s UNUSED, void *my)
339 {
340 struct sort_context *m = (struct sort_context *)my;
341
342 TRACE(TRACE_INFO, "Action is DISCARD.");
343
344 m->result->cancelkeep = 1;
345 return SIEVE2_OK;
346 }
347
sort_fileinto(sieve2_context_t * s,void * my)348 int sort_fileinto(sieve2_context_t *s, void *my)
349 {
350 struct sort_context *m = (struct sort_context *)my;
351 extern const char * imap_flag_desc[];
352 char * const * flaglist;
353 const char * mailbox;
354 int msgflags[IMAP_NFLAGS];
355 int *has_msgflags = NULL;
356 GList *keywords = NULL;
357 char *allflags = NULL;
358 char **flags = NULL;
359
360 mailbox = sieve2_getvalue_string(s, "mailbox");
361 flaglist = sieve2_getvalue_stringlist(s, "flags");
362 allflags = g_strjoinv(" ", (char **)flaglist);
363 flags = g_strsplit(allflags, " ", 0);
364
365 /* This condition exists for the KEEP callback. */
366 if (! mailbox) {
367 mailbox = "INBOX";
368 }
369
370 TRACE(TRACE_INFO, "Action is FILEINTO: mailbox is [%s] flags are [%s]",
371 mailbox, allflags);
372
373 /* If there were any imapflags, set them. */
374 if (flags) {
375 int i, j;
376 memset(msgflags, 0, sizeof(msgflags));
377
378 // Loop through all script/user-specified flags.
379 for (i = 0; flags[i]; i++) {
380 // Find the ones we support.
381 int baseflag = FALSE;
382 char *flag = strrchr(flags[i], '\\');
383 if (flag)
384 flag++;
385 else
386 flag = flags[i];
387
388 for (j = 0; imap_flag_desc[j] && j < IMAP_NFLAGS; j++) {
389 if (g_strcasestr(imap_flag_desc[j], flag)) {
390 TRACE(TRACE_DEBUG, "set baseflag [%s]", flag);
391 // Flag 'em.
392 msgflags[j] = 1;
393 baseflag = TRUE;
394 // Only pass msgflags if we found something.
395 has_msgflags = msgflags;
396 }
397 }
398 if (! baseflag) {
399 TRACE(TRACE_DEBUG, "set keyword [%s]", flag);
400 keywords = g_list_append(keywords, g_strdup(flag));
401 }
402
403 }
404 g_strfreev(flags);
405 }
406 g_free(allflags);
407
408
409 /* Don't cancel the keep if there's a problem storing the message. */
410 if (sort_deliver_to_mailbox(m->message, m->user_idnr,
411 mailbox, BOX_SORTING, has_msgflags, keywords) != DSN_CLASS_OK) {
412 TRACE(TRACE_ERR, "Could not file message into mailbox; not cancelling keep.");
413 m->result->cancelkeep = 0;
414 } else {
415 m->result->cancelkeep = 1;
416 }
417
418 if (keywords)
419 g_list_destroy(keywords);
420
421 return SIEVE2_OK;
422 }
423
424 /* This should only happen if the user has uploaded an invalid script.
425 * Possible causes are a homebrew script uploader that does not do proper
426 * validation, libSieve's removal of a deprecated Sieve language feature,
427 * or perhaps some bugginess elsewhere. */
sort_errparse(sieve2_context_t * s,void * my)428 int sort_errparse(sieve2_context_t *s, void *my)
429 {
430 struct sort_context *m = (struct sort_context *)my;
431 const char *message;
432 int lineno;
433
434 lineno = sieve2_getvalue_int(s, "lineno");
435 message = sieve2_getvalue_string(s, "message");
436
437 TRACE(TRACE_INFO, "Error is PARSE: Line is [%d], Message is [%s]", lineno, message);
438
439 g_string_append_printf(m->result->errormsg, "Parse error on line [%d]: %s", lineno, message);
440
441 if (m->message) {
442 char *alertbody = g_strdup_printf(
443 "Your Sieve script [%s] failed to parse correctly.\n"
444 "Messages will be delivered to your INBOX for now.\n"
445 "The error message is:\n"
446 "%s\n",
447 m->script, message);
448 send_alert(m->user_idnr, "Sieve script parse error", alertbody);
449 g_free(alertbody);
450 }
451
452 m->result->error_parse = 1;
453 return SIEVE2_OK;
454 }
455
sort_errexec(sieve2_context_t * s,void * my)456 int sort_errexec(sieve2_context_t *s, void *my)
457 {
458 struct sort_context *m = (struct sort_context *)my;
459 const char *message;
460
461 message = sieve2_getvalue_string(s, "message");
462
463 TRACE(TRACE_INFO, "Error is EXEC: Message is [%s]", message);
464
465 /* This turns out to be incredibly annoying, as libSieve
466 * throws execution errors on malformed addresses coming
467 * from the wild. As you might guess, that happens with
468 * greater than trivial frequency.
469
470 g_string_append_printf(m->result->errormsg, "Execution error: %s", message);
471
472 if (m->message) {
473 char *alertbody = g_strdup_printf(
474 "Your Sieve script [%s] failed to run correctly.\n"
475 "Messages will be delivered to your INBOX for now.\n"
476 "The error message is:\n"
477 "%s\n",
478 m->script, message);
479 send_alert(m->user_idnr, "Sieve script run error", alertbody);
480 g_free(alertbody);
481 }
482 */
483
484 m->result->error_runtime = 1;
485 return SIEVE2_OK;
486 }
487
sort_getscript(sieve2_context_t * s,void * my)488 int sort_getscript(sieve2_context_t *s, void *my)
489 {
490 struct sort_context *m = (struct sort_context *)my;
491 const char * path, * name;
492 int res;
493
494 /* Path could be :general, :personal, or empty. */
495 path = sieve2_getvalue_string(s, "path");
496
497 /* If no file is named, we're looking for the main file. */
498 name = sieve2_getvalue_string(s, "name");
499
500 if (path == NULL || name == NULL)
501 return SIEVE2_ERROR_BADARGS;
502
503 if (strlen(path) && strlen(name)) {
504 /* TODO: handle included files. */
505 TRACE(TRACE_INFO, "Include requested from [%s] named [%s]", path, name);
506 } else
507 if (!strlen(path) && !strlen(name)) {
508 /* Read the script file given as an argument. */
509 TRACE(TRACE_INFO, "Getting default script named [%s]", m->script);
510 res = dm_sievescript_getbyname(m->user_idnr, m->script, &m->s_buf);
511 if (res != SIEVE2_OK) {
512 TRACE(TRACE_ERR, "sort_getscript: read_file() returns %d\n", res);
513 return SIEVE2_ERROR_FAIL;
514 }
515 sieve2_setvalue_string(s, "script", m->s_buf);
516 } else {
517 return SIEVE2_ERROR_BADARGS;
518 }
519
520 return SIEVE2_OK;
521 }
522
sort_getheader(sieve2_context_t * s,void * my)523 int sort_getheader(sieve2_context_t *s, void *my)
524 {
525 struct sort_context *m = (struct sort_context *)my;
526 char *header;
527 char **bodylist;
528 GList *headers;
529 unsigned i;
530
531 header = (char *)sieve2_getvalue_string(s, "header");
532
533 headers = dbmail_message_get_header_repeated(m->message, header);
534
535 bodylist = g_new0(char *,g_list_length(headers)+1);
536 i = 0;
537 while (headers) {
538 char *decoded = dbmail_iconv_decode_text((char *)headers->data);
539 bodylist[i++] = decoded;
540 /* queue the decoded value for freeing later on */
541 m->freelist = g_list_prepend(m->freelist, decoded);
542
543 if (! g_list_next(headers))
544 break;
545 headers = g_list_next(headers);
546 }
547 g_list_free(g_list_first(headers));
548
549 /* We have to free the header array. */
550 m->freelist = g_list_prepend(m->freelist, bodylist);
551
552 for (i = 0; bodylist[i] != NULL; i++) {
553 TRACE(TRACE_INFO, "Getting header [%s] returning value [%s]",
554 header, bodylist[i]);
555 }
556
557 sieve2_setvalue_stringlist(s, "body", bodylist);
558
559 return SIEVE2_OK;
560 }
561
562 /* Return both the to and from headers. */
sort_getenvelope(sieve2_context_t * s,void * my)563 int sort_getenvelope(sieve2_context_t *s, void *my)
564 {
565 struct sort_context *m = (struct sort_context *)my;
566 const char *to, *from;
567
568 to = dbmail_message_get_envelope_recipient(m->message);
569 from = dbmail_message_get_header(m->message, "Return-Path");
570
571 TRACE(TRACE_DEBUG, "from [%s], to [%s]", from, to);
572
573 sieve2_setvalue_string(s, "to", (char *)to);
574 sieve2_setvalue_string(s, "from", (char *)from);
575
576 return SIEVE2_OK;
577 }
578
sort_getbody(sieve2_context_t * s UNUSED,void * my UNUSED)579 int sort_getbody(sieve2_context_t *s UNUSED, void *my UNUSED)
580 {
581 return SIEVE2_ERROR_UNSUPPORTED;
582 }
583
sort_getsize(sieve2_context_t * s,void * my)584 int sort_getsize(sieve2_context_t *s, void *my)
585 {
586 struct sort_context *m = (struct sort_context *)my;
587 int rfcsize;
588
589 rfcsize = dbmail_message_get_size(m->message, TRUE);
590
591 TRACE(TRACE_INFO, "Getting message size [%d]", rfcsize);
592
593 sieve2_setvalue_int(s, "size", rfcsize);
594
595 return SIEVE2_OK;
596 }
597
sort_getsubaddress(sieve2_context_t * s,void * my)598 int sort_getsubaddress(sieve2_context_t *s, void *my)
599 {
600 struct sort_context *m = (struct sort_context *)my;
601 const char *address;
602 char *user = NULL, *detail = NULL,
603 *localpart = NULL, *domain = NULL;
604
605 address = sieve2_getvalue_string(s, "address");
606
607 /* Simple address parsing. libSieve only shows us
608 * the localpart@domain portion, so we don't need
609 * to handle anything exciting or exotic here. */
610 // TODO: Unify with the delivery chain subaddress code.
611
612 localpart = strdup(address);
613 domain = strchr(localpart, '@');
614 if (domain) {
615 *domain = '\0';
616 domain++;
617 } else {
618 // Malformed address.
619 }
620
621 user = strdup(localpart);
622 detail = strchr(user, '+');
623 if (detail) {
624 *detail = '\0';
625 detail++;
626 } else {
627 // No detail present.
628 }
629
630 sieve2_setvalue_string(s, "user", user);
631 sieve2_setvalue_string(s, "detail", detail);
632 sieve2_setvalue_string(s, "localpart", localpart);
633 sieve2_setvalue_string(s, "domain", domain);
634
635 m->freelist = g_list_prepend(m->freelist, user);
636 m->freelist = g_list_prepend(m->freelist, localpart);
637
638 return SIEVE2_OK;
639 }
640
sort_debugtrace(sieve2_context_t * s,void * my UNUSED)641 int sort_debugtrace(sieve2_context_t *s, void *my UNUSED)
642 {
643 int trace_level;
644
645 switch (sieve2_getvalue_int(s, "level")) {
646 case 0:
647 case 1:
648 case 2:
649 trace_level = TRACE_INFO;
650 break;
651 case 3:
652 case 4:
653 case 5:
654 default:
655 trace_level = TRACE_DEBUG;
656 break;
657 }
658
659 TRACE(trace_level, "sieve: [%s,%s,%s: [%s]\n",
660 sieve2_getvalue_string(s, "module"),
661 sieve2_getvalue_string(s, "file"),
662 sieve2_getvalue_string(s, "function"),
663 sieve2_getvalue_string(s, "message"));
664
665 return SIEVE2_OK;
666 }
667
668 /* END OF CALLBACKS */
669
670
671 sieve2_callback_t vacation_callbacks[] = {
672 { SIEVE2_ACTION_VACATION, sort_vacation },
673 { 0, 0 } };
674
675 sieve2_callback_t notify_callbacks[] = {
676 { SIEVE2_ACTION_NOTIFY, sort_notify },
677 { 0, 0 } };
678
679 sieve2_callback_t debug_callbacks[] = {
680 { SIEVE2_DEBUG_TRACE, sort_debugtrace },
681 { 0, 0 } };
682
683 sieve2_callback_t sort_callbacks[] = {
684 { SIEVE2_ERRCALL_RUNTIME, sort_errexec },
685 { SIEVE2_ERRCALL_PARSE, sort_errparse },
686
687 { SIEVE2_ACTION_REDIRECT, sort_redirect },
688 { SIEVE2_ACTION_DISCARD, sort_discard },
689 { SIEVE2_ACTION_REJECT, sort_reject },
690 { SIEVE2_ACTION_FILEINTO, sort_fileinto },
691 { SIEVE2_ACTION_KEEP, sort_fileinto },
692
693 { SIEVE2_SCRIPT_GETSCRIPT, sort_getscript },
694 { SIEVE2_MESSAGE_GETHEADER, sort_getheader },
695 { SIEVE2_MESSAGE_GETENVELOPE, sort_getenvelope },
696 { SIEVE2_MESSAGE_GETBODY, sort_getbody },
697 { SIEVE2_MESSAGE_GETSIZE, sort_getsize },
698 { SIEVE2_MESSAGE_GETSUBADDRESS, sort_getsubaddress },
699 { 0, 0 } };
700
701
sort_teardown(sieve2_context_t ** s2c,struct sort_context ** sc)702 static int sort_teardown(sieve2_context_t **s2c,
703 struct sort_context **sc)
704 {
705 assert(s2c != NULL);
706 assert(sc != NULL);
707
708 sieve2_context_t *sieve2_context = *s2c;
709 struct sort_context *sort_context = *sc;
710 int res;
711
712 g_list_destroy(sort_context->freelist);
713
714 if (sort_context) {
715 g_free(sort_context);
716 }
717
718 res = sieve2_free(&sieve2_context);
719 if (res != SIEVE2_OK) {
720 TRACE(TRACE_ERR, "Error [%d] when calling sieve2_free: [%s]",
721 res, sieve2_errstr(res));
722 return DM_EGENERAL;
723 }
724
725 *s2c = NULL;
726 *sc = NULL;
727
728 return DM_SUCCESS;
729 }
730
sort_startup(sieve2_context_t ** s2c,struct sort_context ** sc)731 static int sort_startup(sieve2_context_t **s2c,
732 struct sort_context **sc)
733 {
734 assert(s2c != NULL);
735 assert(sc != NULL);
736
737 sieve2_context_t *sieve2_context = NULL;
738 struct sort_context *sort_context = NULL;
739 struct sort_sieve_config sieve_config;
740 int res;
741
742 res = sieve2_alloc(&sieve2_context);
743 if (res != SIEVE2_OK) {
744 TRACE(TRACE_ERR, "Error [%d] when calling sieve2_alloc: [%s]",
745 res, sieve2_errstr(res));
746 return DM_EGENERAL;
747 }
748
749 sort_sieve_get_config(&sieve_config);
750
751 res = sieve2_callbacks(sieve2_context, sort_callbacks);
752 if (res != SIEVE2_OK) {
753 TRACE(TRACE_ERR, "Error [%d] when calling sieve2_callbacks: [%s]",
754 res, sieve2_errstr(res));
755 sort_teardown(&sieve2_context, &sort_context);
756 return DM_EGENERAL;
757 }
758 if (sieve_config.vacation) {
759 TRACE(TRACE_DEBUG, "Sieve vacation enabled.");
760 res = sieve2_callbacks(sieve2_context, vacation_callbacks);
761 if (res != SIEVE2_OK) {
762 TRACE(TRACE_ERR, "Error [%d] when calling sieve2_callbacks: [%s]",
763 res, sieve2_errstr(res));
764 sort_teardown(&sieve2_context, &sort_context);
765 return DM_EGENERAL;
766 }
767 }
768 if (sieve_config.notify) {
769 TRACE(TRACE_INFO, "Sieve notify is not supported in this release.");
770 res = sieve2_callbacks(sieve2_context, notify_callbacks);
771 if (res != SIEVE2_OK) {
772 TRACE(TRACE_ERR, "Error [%d] when calling sieve2_callbacks: [%s]",
773 res, sieve2_errstr(res));
774 sort_teardown(&sieve2_context, &sort_context);
775 return DM_EGENERAL;
776 }
777 }
778 if (sieve_config.debug) {
779 TRACE(TRACE_DEBUG, "Sieve debugging enabled.");
780 res = sieve2_callbacks(sieve2_context, debug_callbacks);
781 if (res != SIEVE2_OK) {
782 TRACE(TRACE_ERR, "Error [%d] when calling sieve2_callbacks: [%s]",
783 res, sieve2_errstr(res));
784 sort_teardown(&sieve2_context, &sort_context);
785 return DM_EGENERAL;
786 }
787 }
788
789 sort_context = g_new0(struct sort_context, 1);
790 if (!sort_context) {
791 sort_teardown(&sieve2_context, &sort_context);
792 return DM_EGENERAL;
793 }
794 memset(sort_context, 0, sizeof(struct sort_context));
795
796 sort_context->freelist = NULL;
797
798 *s2c = sieve2_context;
799 *sc = sort_context;
800
801 return DM_SUCCESS;
802 }
803
804 /* The caller is responsible for freeing memory here. */
sort_listextensions(void)805 const char * sort_listextensions(void)
806 {
807 sieve2_context_t *sieve2_context;
808 const char * extensions;
809 struct sort_sieve_config sieve_config;
810
811 if (sieve2_alloc(&sieve2_context) != SIEVE2_OK)
812 return NULL;
813
814 if (sieve2_callbacks(sieve2_context, sort_callbacks))
815 return NULL;
816
817 sort_sieve_get_config(&sieve_config);
818
819 if (sieve_config.vacation) {
820 TRACE(TRACE_DEBUG, "Sieve vacation enabled.");
821 sieve2_callbacks(sieve2_context, vacation_callbacks);
822 }
823 if (sieve_config.notify) {
824 TRACE(TRACE_ERR, "Sieve notify is not supported in this release.");
825 sieve2_callbacks(sieve2_context, notify_callbacks);
826 }
827 if (sieve_config.debug) {
828 TRACE(TRACE_DEBUG, "Sieve debugging enabled.");
829 sieve2_callbacks(sieve2_context, debug_callbacks);
830 }
831
832 /* This will be freed by sieve2_free. */
833 extensions = sieve2_listextensions(sieve2_context);
834
835 /* So we'll make our own copy. */
836 if (extensions)
837 extensions = g_strstrip(g_strdup(extensions));
838
839 /* If this fails, then we don't care about the
840 * memory leak, because the program has to bomb out.
841 * It will not be possible to start libSieve up again
842 * if it does not free properly. */
843 if (sieve2_free(&sieve2_context) != SIEVE2_OK)
844 return NULL;
845
846 return extensions;
847 }
848
849 /* Return 0 on script OK, 1 on script error, 2 on misc error. */
sort_validate(uint64_t user_idnr,char * scriptname)850 SortResult_T *sort_validate(uint64_t user_idnr, char *scriptname)
851 {
852 int res, exitnull = 0;
853 struct sort_result *result = NULL;
854 sieve2_context_t *sieve2_context;
855 struct sort_context *sort_context;
856
857 /* The contents of this function are taken from
858 * the libSieve distribution, sv_test/example.c,
859 * and are provided under an "MIT style" license.
860 * */
861
862 if (sort_startup(&sieve2_context, &sort_context) != DM_SUCCESS) {
863 return NULL;
864 }
865
866 sort_context->script = scriptname;
867 sort_context->user_idnr = user_idnr;
868 sort_context->result = g_new0(struct sort_result, 1);
869 if (! sort_context->result) {
870 return NULL;
871 }
872 sort_context->result->errormsg = g_string_new("");
873
874 res = sieve2_validate(sieve2_context, sort_context);
875 if (res != SIEVE2_OK) {
876 TRACE(TRACE_ERR, "Error %d when calling sieve2_validate: %s",
877 res, sieve2_errstr(res));
878 exitnull = 1;
879 goto freesieve;
880 }
881
882 /* At this point the callbacks are called from within libSieve. */
883
884 freesieve:
885 if (sort_context->s_buf)
886 g_free(sort_context->s_buf);
887
888 if (exitnull)
889 result = NULL;
890 else
891 result = sort_context->result;
892
893 sort_teardown(&sieve2_context, &sort_context);
894
895 return result;
896 }
897
898 /* Pull up the relevant sieve scripts for this
899 * user and begin running them against the header
900 * and possibly the body of the message.
901 *
902 * Returns 0 on success, -1 on failure,
903 * and +1 on success but with memory leaking.
904 * In the +1 case, if called from a daemon
905 * such as dbmail-lmtpd, the daemon should
906 * finish storing the message and restart.
907 * */
sort_process(uint64_t user_idnr,DbmailMessage * message,const char * mailbox)908 SortResult_T *sort_process(uint64_t user_idnr, DbmailMessage *message, const char *mailbox)
909 {
910 int res, exitnull = 0;
911 struct sort_result *result = NULL;
912 sieve2_context_t *sieve2_context;
913 struct sort_context *sort_context;
914
915 /* The contents of this function are taken from
916 * the libSieve distribution, sv_test/example.c,
917 * and are provided under an "MIT style" license.
918 * */
919
920 if (sort_startup(&sieve2_context, &sort_context) != DM_SUCCESS) {
921 return NULL;
922 }
923
924 sort_context->message = message;
925 sort_context->user_idnr = user_idnr;
926 sort_context->result = g_new0(struct sort_result, 1);
927 if (! sort_context->result) {
928 exitnull = 1;
929 goto freesieve;
930 }
931 sort_context->result->errormsg = g_string_new("");
932 if (mailbox)
933 sort_context->result->mailbox = mailbox;
934
935 res = dm_sievescript_get(user_idnr, &sort_context->script);
936 if (res != 0) {
937 TRACE(TRACE_ERR, "Error [%d] when calling db_getactive_sievescript", res);
938 exitnull = 1;
939 goto freesieve;
940 }
941 if (sort_context->script == NULL) {
942 TRACE(TRACE_INFO, "User doesn't have any active sieve scripts.");
943 exitnull = 1;
944 goto freesieve;
945 }
946
947 res = sieve2_execute(sieve2_context, sort_context);
948 if (res != SIEVE2_OK) {
949 TRACE(TRACE_ERR, "Error [%d] when calling sieve2_execute: [%s]",
950 res, sieve2_errstr(res));
951 exitnull = 1;
952 }
953 if (! sort_context->result->cancelkeep) {
954 TRACE(TRACE_INFO, "No actions taken; message must be kept.");
955 }
956
957 /* At this point the callbacks are called from within libSieve. */
958
959 freesieve:
960 if (sort_context->s_buf)
961 g_free(sort_context->s_buf);
962 if (sort_context->script)
963 g_free(sort_context->script);
964
965 if (exitnull)
966 result = NULL;
967 else
968 result = sort_context->result;
969
970 sort_teardown(&sieve2_context, &sort_context);
971
972 return result;
973 }
974
975 /* SORT RESULT INTERFACE */
976
sort_free_result(SortResult_T * result)977 void sort_free_result(SortResult_T *result)
978 {
979 if (result == NULL) return;
980 if (result->errormsg != NULL)
981 g_string_free(result->errormsg, TRUE);
982 if (result->rejectmsg != NULL)
983 g_string_free(result->rejectmsg, TRUE);
984 g_free(result);
985 }
986
sort_get_cancelkeep(SortResult_T * result)987 int sort_get_cancelkeep(SortResult_T *result)
988 {
989 if (result == NULL) return 0;
990 return result->cancelkeep;
991 }
992
sort_get_mailbox(SortResult_T * result)993 const char * sort_get_mailbox(SortResult_T *result)
994 {
995 if (result == NULL) return NULL;
996 return result->mailbox;
997 }
998
sort_get_reject(SortResult_T * result)999 int sort_get_reject(SortResult_T *result)
1000 {
1001 if (result == NULL) return 0;
1002 return result->reject;
1003 }
1004
sort_get_rejectmsg(SortResult_T * result)1005 const char *sort_get_rejectmsg(SortResult_T *result)
1006 {
1007 if (result == NULL) return NULL;
1008 return result->rejectmsg->str;
1009 }
1010
sort_get_error(SortResult_T * result)1011 int sort_get_error(SortResult_T *result)
1012 {
1013 if (result == NULL) return 0;
1014 return result->errormsg->len;
1015 }
1016
sort_get_errormsg(SortResult_T * result)1017 const char * sort_get_errormsg(SortResult_T *result)
1018 {
1019 if (result == NULL) return NULL;
1020 return result->errormsg->str;
1021 }
1022
1023