Commit 5b38e1df 5b38e1df9bbfcb12e980b7e7a92f50900c11488e by Wojciech Polak

Moved from ../../examples/

1 parent 5f830276
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2003 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with GNU Mailutils; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
18 /* Implements "list" sieve extension test. See "Syntax:" below for the
19 description */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <mailutils/libsieve.h>
29
30
31
32 /* Auxiliary functions */
33 struct header_closure {
34 header_t header; /* Message header */
35 int index; /* Header index */
36 char *delim; /* List delimiter */
37 char *value; /* Retrieved header value */
38 char *save; /* Save pointer for strtok_r */
39 };
40
41 static void
42 cleanup (struct header_closure *hc)
43 {
44 free (hc->value);
45 hc->value = hc->save = NULL;
46 }
47
48 static int
49 retrieve_next_header (struct header_closure *hc, char *name, char **pval)
50 {
51 char buf[512];
52 size_t n;
53
54 cleanup (hc);
55 while (!header_get_field_name (hc->header, hc->index, buf, sizeof(buf), &n))
56 {
57 int i = hc->index++;
58 if (strcasecmp (buf, name) == 0)
59 {
60 if (header_aget_field_value (hc->header, i, &hc->value))
61 return 1;
62 *pval = strtok_r (hc->value, hc->delim, &hc->save);
63 if (*pval == NULL)
64 {
65 cleanup (hc);
66 return 1;
67 }
68 return 0;
69 }
70 }
71
72 return 1;
73 }
74
75 static int
76 list_retrieve_header (void *item, void *data, int idx, char **pval)
77 {
78 struct header_closure *hc = data;
79 char *p;
80
81 if (idx == 0)
82 hc->index = 1;
83
84 while (1)
85 {
86 if (!hc->value)
87 {
88 if (retrieve_next_header (hc, (char*) item, &p))
89 return 1;
90 }
91 else
92 {
93 p = strtok_r (NULL, hc->delim, &hc->save);
94 if (!p)
95 {
96 cleanup (hc);
97 continue;
98 }
99 }
100
101 *pval = strdup (p);
102 return 0;
103 }
104
105 return 1;
106 }
107
108
109 /* The test proper */
110
111 /* Syntax: list [COMPARATOR] [MATCH-TYPE]
112 [ :delim <delimiters: string> ]
113 <headers: string-list> <key-list: string-list>
114
115 The "list" test evaluates to true if any of the headers
116 match any key. Each header is regarded as containing a
117 list of keywords. By default, comma is assumed as list
118 separator. This can be overridden by specifying ":delim"
119 tag, whose value is a string consisting of valid list
120 delimiter characters.
121
122 list :matches :delim " ," [ "X-Spam-Keywords", "X-Spamd-Keywords" ]
123 [ "HTML_*", "FORGED_*" ]
124
125
126 */
127
128 static int
129 list_test (sieve_machine_t mach, list_t args, list_t tags)
130 {
131 sieve_value_t *h, *v, *arg;
132 sieve_comparator_t comp = sieve_get_comparator (mach, tags);
133 struct header_closure clos;
134 int result;
135
136 memset (&clos, 0, sizeof clos);
137 if (sieve_tag_lookup (tags, "delim", &arg))
138 clos.delim = arg->v.string;
139 else
140 clos.delim = ",";
141
142 h = sieve_value_get (args, 0);
143 if (!h)
144 {
145 sieve_error (mach, _("list: can't get argument 1"));
146 sieve_abort (mach);
147 }
148 v = sieve_value_get (args, 1);
149 if (!v)
150 {
151 sieve_error (mach, _("list: can't get argument 2"));
152 sieve_abort (mach);
153 }
154
155 message_get_header (sieve_get_message (mach), &clos.header);
156 result = sieve_vlist_compare (h, v, comp, sieve_get_relcmp (mach, tags),
157 list_retrieve_header,
158 &clos, NULL) > 0;
159 cleanup (&clos);
160 return result;
161 }
162
163
164 /* Initialization */
165
166 /* Required arguments: */
167 static sieve_data_type list_req_args[] = {
168 SVT_STRING_LIST,
169 SVT_STRING_LIST,
170 SVT_VOID
171 };
172
173 static sieve_tag_def_t match_part_tags[] = {
174 { "is", SVT_VOID },
175 { "contains", SVT_VOID },
176 { "matches", SVT_VOID },
177 { "regex", SVT_VOID },
178 { "count", SVT_STRING },
179 { "value", SVT_STRING },
180 { "comparator", SVT_STRING },
181 { NULL }
182 };
183
184 static sieve_tag_def_t delim_part_tags[] = {
185 { "delim", SVT_STRING },
186 { NULL }
187 };
188
189 static sieve_tag_group_t list_tag_groups[] = {
190 { match_part_tags, sieve_match_part_checker },
191 { delim_part_tags, NULL },
192 { NULL }
193 };
194
195 /* Initialization function. */
196 int
197 SIEVE_EXPORT(list,init) (sieve_machine_t mach)
198 {
199 return sieve_register_test (mach, "list", list_test,
200 list_req_args, list_tag_groups, 1);
201 }
202
203 /* End of list.c */
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2003 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with GNU Mailutils; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
18 /* This module implements sieve extension test "spamd": an interface to
19 the SpamAssassin spamd daemon. See "Usage:" below for the description */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/un.h>
30 #include <string.h>
31 #include <signal.h>
32 #include <mailutils/libsieve.h>
33 #include <mailutils/mu_auth.h>
34
35 #define DEFAULT_SPAMD_PORT 783
36
37
38 /* Auxiliary functions */
39
40 static int
41 spamd_connect_tcp (sieve_machine_t mach, stream_t *stream,
42 char *host, int port)
43 {
44 int rc = tcp_stream_create (stream, host, port, 0);
45 if (rc)
46 {
47 sieve_error (mach, "tcp_stream_create: %s", mu_strerror (rc));
48 return rc;
49 }
50 rc = stream_open (*stream);
51 if (rc)
52 sieve_error (mach, "opening tcp stream: %s", mu_strerror (rc));
53 return rc;
54 }
55
56 static int
57 spamd_connect_socket (sieve_machine_t mach, stream_t *stream, char *path)
58 {
59 /* FIXME: A library deficiency: we cannot create a unix socket stream */
60 int fd, rc;
61 FILE *fp;
62 struct sockaddr_un addr;
63
64 if ((fd = socket (PF_UNIX, SOCK_STREAM, 0)) < 0)
65 {
66 sieve_error (mach, "socket: %s", mu_strerror (errno));
67 return errno;
68 }
69
70 memset(&addr, 0, sizeof addr);
71 addr.sun_family = AF_UNIX;
72 strncpy(addr.sun_path, path, sizeof addr.sun_path - 1);
73 addr.sun_path[sizeof addr.sun_path - 1] = 0;
74 if (connect (fd, (struct sockaddr *) &addr, sizeof(addr)))
75 {
76 sieve_error (mach, "connect: %s", mu_strerror (errno));
77 close (fd);
78 return errno;
79 }
80
81 fp = fdopen (fd, "w+");
82 rc = stdio_stream_create (stream, fp, MU_STREAM_RDWR);
83 if (rc)
84 {
85 sieve_error (mach, "stdio_stream_create: %s", mu_strerror (rc));
86 fclose (fp);
87 return rc;
88 }
89
90 rc = stream_open (*stream);
91 if (rc)
92 {
93 sieve_error (mach, "stream_open: %s", mu_strerror (rc));
94 stream_destroy (stream, stream_get_owner (*stream));
95 }
96 return rc;
97 }
98
99 static void
100 spamd_destroy (stream_t *stream)
101 {
102 stream_close (*stream);
103 stream_destroy (stream, stream_get_owner (*stream));
104 }
105
106 static void
107 spamd_shutdown (stream_t stream, int flag)
108 {
109 int fd;
110 stream_flush (stream);
111 stream_get_fd (stream, &fd);
112 shutdown (fd, flag);
113 }
114
115 static void
116 spamd_send_command (stream_t stream, const char *fmt, ...)
117 {
118 char buf[512];
119 size_t n;
120 va_list ap;
121
122 va_start (ap, fmt);
123 n = vsnprintf (buf, sizeof buf, fmt, ap);
124 va_end (ap);
125 stream_sequential_write (stream, buf, n);
126 stream_sequential_write (stream, "\r\n", 2);
127 }
128
129 static void
130 spamd_send_message (stream_t stream, message_t msg)
131 {
132 size_t size;
133 char buf[512];
134 stream_t mstr;
135
136 message_get_stream (msg, &mstr);
137 stream_seek (mstr, 0, SEEK_SET);
138 while (stream_sequential_readline (mstr, buf, sizeof (buf), &size) == 0
139 && size > 0)
140 {
141 char *nl = NULL;
142
143 if (buf[size-1] == '\n')
144 {
145 size--;
146 nl = "\r\n";
147 }
148 stream_sequential_write (stream, buf, size);
149 if (nl)
150 stream_sequential_write (stream, nl, 2);
151 }
152 }
153
154 static size_t
155 spamd_read_line (sieve_machine_t mach, stream_t stream,
156 char *buffer, size_t size, size_t *pn)
157 {
158 size_t n = 0;
159 int rc = stream_sequential_readline (stream, buffer, size, &n);
160 if (rc == 0)
161 {
162 if (pn)
163 *pn = n;
164 while (n > 0 && (buffer[n-1] == '\r' || buffer[n-1] == '\n'))
165 n--;
166 buffer[n] = 0;
167 if (sieve_get_debug_level (mach) & MU_SIEVE_DEBUG_TRACE)
168 sieve_debug (mach, ">> %s\n", buffer);
169 }
170 return rc;
171 }
172
173 #define char_to_num(c) (c-'0')
174
175 static void
176 decode_float (size_t *vn, char *str, int digits)
177 {
178 size_t v;
179 size_t frac = 0;
180 size_t base = 1;
181 int i;
182
183 for (i = 0; i < digits; i++)
184 base *= 10;
185
186 v = strtoul (str, &str, 10);
187 v *= base;
188 if (*str == '.')
189 {
190 for (str++, i = 0; *str && i < digits; i++, str++)
191 frac = frac * 10 + char_to_num (*str);
192 if (*str)
193 {
194 if (char_to_num (*str) >= 5)
195 frac++;
196 }
197 else
198 for (; i < digits; i++)
199 frac *= 10;
200 }
201 *vn = v + frac;
202 }
203
204 static int
205 decode_boolean (char *str)
206 {
207 if (strcasecmp (str, "true") == 0)
208 return 1;
209 else if (strcasecmp (str, "false") == 0)
210 return 0;
211 /*else?*/
212 return 0;
213 }
214
215
216 /* Signal handling */
217
218 typedef RETSIGTYPE (*signal_handler)(int);
219
220 static signal_handler
221 set_signal_handler (int sig, signal_handler h)
222 {
223 #ifdef HAVE_SIGACTION
224 struct sigaction act, oldact;
225 act.sa_handler = h;
226 sigemptyset (&act.sa_mask);
227 act.sa_flags = 0;
228 sigaction (sig, &act, &oldact);
229 return oldact.sa_handler;
230 #else
231 return signal (sig, h);
232 #endif
233 }
234
235 void
236 spamd_abort (sieve_machine_t mach, stream_t *stream, signal_handler handler)
237 {
238 spamd_destroy (stream);
239 set_signal_handler (SIGPIPE, handler);
240 sieve_abort (mach);
241 }
242
243 static int got_sigpipe;
244
245 static RETSIGTYPE
246 sigpipe_handler (int sig ARG_UNUSED)
247 {
248 got_sigpipe = 1;
249 }
250
251
252 /* The test proper */
253
254 /* Syntax: spamd [":host" <tcp-host: string]
255 [":port" <tcp-port: number> /
256 ":socket" <unix-socket: string>]
257 [":over" / ":under" <limit: string>]
258
259 The "spamd" test is an interface to "spamd" facility of
260 SpamAssassin mail filter. It evaluates to true if SpamAssassin
261 recognized the message as spam, or the message spam score
262 satisfies the given relation.
263
264 If the argument is ":over" and the spam score is greater than
265 or equal to the number provided, the test is true; otherwise,
266 it is false.
267
268 If the argument is ":under" and the spam score is less than
269 or equal to the number provided, the test is true; otherwise,
270 it is false.
271
272 Spam score is a floating point number. The comparison takes into
273 account three decimal digits.
274
275 */
276
277 static int
278 spamd_test (sieve_machine_t mach, list_t args, list_t tags)
279 {
280 char buffer[512];
281 char version_str[19];
282 char spam_str[6], score_str[21], threshold_str[21];
283 int response, rc;
284 size_t version;
285 int result;
286 size_t score, threshold, limit;
287 stream_t stream = NULL;
288 sieve_value_t *arg;
289 message_t msg;
290 size_t m_size, m_lines, size;
291 struct mu_auth_data *auth;
292 signal_handler handler;
293 char *host;
294 header_t hdr;
295
296 if (sieve_get_debug_level (mach) & MU_SIEVE_DEBUG_TRACE)
297 sieve_debug (mach, "spamd_test %lu\n",
298 (u_long) sieve_get_message_num (mach));
299
300 if (sieve_tag_lookup (tags, "host", &arg))
301 host = arg->v.string;
302 else
303 host = "127.0.0.1";
304
305 if (sieve_tag_lookup (tags, "port", &arg))
306 result = spamd_connect_tcp (mach, &stream, host, arg->v.number);
307 else if (sieve_tag_lookup (tags, "socket", &arg))
308 result = spamd_connect_socket (mach, &stream, arg->v.string);
309 else
310 result = spamd_connect_tcp (mach, &stream, host, DEFAULT_SPAMD_PORT);
311 if (result) /* spamd_connect_ already reported error */
312 sieve_abort (mach);
313
314 msg = sieve_get_message (mach);
315 message_size (msg, &m_size);
316 message_lines (msg, &m_lines);
317
318 auth = mu_get_auth_by_uid (geteuid ());
319 spamd_send_command (stream, "SYMBOLS SPAMC/1.2");
320 spamd_send_command (stream, "Content-length: %lu",
321 (u_long) (m_size + m_lines));
322 spamd_send_command (stream, "User: %s", auth ? auth->name : "root");
323 mu_auth_data_free (auth);
324
325 got_sigpipe = 0;
326 handler = set_signal_handler (SIGPIPE, sigpipe_handler);
327
328 spamd_send_command (stream, "");
329 spamd_send_message (stream, msg);
330 spamd_shutdown (stream, SHUT_WR);
331
332 spamd_read_line (mach, stream, buffer, sizeof buffer, NULL);
333
334 if (got_sigpipe)
335 {
336 sieve_error (mach, "remote side has closed connection");
337 spamd_abort (mach, &stream, handler);
338 }
339
340 if (sscanf (buffer, "SPAMD/%18s %d %*s", version_str, &response) != 2)
341 {
342 sieve_error (mach, "spamd responded with bad string '%s'", buffer);
343 spamd_abort (mach, &stream, handler);
344 }
345
346 decode_float (&version, version_str, 1);
347 if (version < 10)
348 {
349 sieve_error (mach, "unsupported SPAMD version: %s", version_str);
350 spamd_abort (mach, &stream, handler);
351 }
352
353 /*
354 if (response)
355 ...
356 */
357
358 spamd_read_line (mach, stream, buffer, sizeof buffer, NULL);
359 if (sscanf (buffer, "Spam: %5s ; %20s / %20s",
360 spam_str, score_str, threshold_str) != 3)
361 {
362 sieve_error (mach, "spamd responded with bad Spam header '%s'", buffer);
363 spamd_abort (mach, &stream, handler);
364 }
365
366 result = decode_boolean (spam_str);
367 score = strtoul (score_str, NULL, 10);
368 decode_float (&score, score_str, 3);
369 decode_float (&threshold, threshold_str, 3);
370
371 if (!result)
372 {
373 if (sieve_tag_lookup (tags, "over", &arg))
374 {
375 decode_float (&limit, arg->v.string, 3);
376 result = score >= limit;
377 }
378 else if (sieve_tag_lookup (tags, "over", &arg))
379 {
380 decode_float (&limit, arg->v.string, 3);
381 result = score <= limit;
382 }
383 }
384
385 /* Skip newline */
386 spamd_read_line (mach, stream, buffer, sizeof buffer, NULL);
387 /* Read symbol list */
388 spamd_read_line (mach, stream, buffer, sizeof buffer, &size);
389
390 rc = message_get_header (msg, &hdr);
391 if (rc)
392 {
393 sieve_error (mach, "cannot get message header: %s", mu_strerror (rc));
394 spamd_abort (mach, &stream, handler);
395 }
396
397 header_set_value (hdr, "X-Spamd-Status", spam_str, 1);
398 header_set_value (hdr, "X-Spamd-Score", score_str, 1);
399 header_set_value (hdr, "X-Spamd-Threshold", threshold_str, 1);
400 header_set_value (hdr, "X-Spamd-Keywords", buffer, 1);
401
402 while (spamd_read_line (mach, stream, buffer, sizeof buffer, &size) == 0
403 && size > 0)
404 /* Drain input */;
405
406 spamd_destroy (&stream);
407 set_signal_handler (SIGPIPE, handler);
408
409 return result;
410 }
411
412
413 /* Initialization */
414
415 /* Required arguments: */
416 static sieve_data_type spamd_req_args[] = {
417 SVT_VOID
418 };
419
420 /* Tagged arguments: */
421 static sieve_tag_def_t spamd_tags[] = {
422 { "host", SVT_STRING },
423 { "port", SVT_NUMBER },
424 { "socket", SVT_STRING },
425 { "over", SVT_STRING },
426 { "under", SVT_STRING },
427 { NULL }
428 };
429
430 static sieve_tag_group_t spamd_tag_groups[] = {
431 { spamd_tags, NULL },
432 { NULL }
433 };
434
435
436 /* Initialization function. */
437 int
438 SIEVE_EXPORT(spamd,init) (sieve_machine_t mach)
439 {
440 return sieve_register_test (mach, "spamd", spamd_test,
441 spamd_req_args, spamd_tag_groups, 1);
442 }
443
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2003 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with GNU Mailutils; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
18 /* Syntax: timestamp [":before"/":after"] <header-name: string>
19 <date: datestring>
20
21 The "timestamp" test compares the value of a structured date header
22 field with the given date.
23
24 If the tagged argument is ":after" and the date from the header is
25 after the specified date the result is true, otherwise, if the
26 header date is before the given date, the result is false.
27
28 If the tagged argument is ":before" and the date from the header is
29 before the specified date the result is true, otherwise, if the
30 header date is after the given date, the result is false.
31
32 If no tagged argument is supplied, :after is assumed.
33
34 Almost any date format is understood.
35
36 Example: timestamp :before "X-Expire-Timestamp" "now - 5 days"
37
38 This test will return true, if the date in X-Expire-Timestamp is
39 more than 5 days older than the current date. */
40
41 #ifdef HAVE_CONFIG_H
42 # include <config.h>
43 #endif
44
45 #include <stdlib.h>
46 #include <mailutils/libsieve.h>
47
48 /* Handler for the timestamp test */
49 static int
50 timestamp_test (sieve_machine_t mach, list_t args, list_t tags)
51 {
52 sieve_value_t *h, *v;
53 header_t hdr;
54 char *val;
55 time_t now = time (NULL);
56 time_t tlimit, tval;
57 int rc;
58
59 if (sieve_get_debug_level (mach) & MU_SIEVE_DEBUG_TRACE)
60 sieve_debug (mach, "TIMESTAMP\n");
61
62 /* Retrieve required arguments: */
63 /* First argument: header name */
64 h = sieve_value_get (args, 0);
65 if (!h)
66 {
67 sieve_error (mach, "timestamp: can't get argument 1");
68 sieve_abort (mach);
69 }
70 /* Second argument: date displacement */
71 v = sieve_value_get (args, 1);
72 if (!v)
73 {
74 sieve_error (mach, "timestamp: can't get argument 2");
75 sieve_abort (mach);
76 }
77
78 if (mu_parse_date (v->v.string, &tlimit, &now))
79 {
80 sieve_error (mach, "timestamp: can't parse date specification (%s)",
81 v->v.string);
82 sieve_abort (mach);
83 }
84
85 rc = message_get_header (sieve_get_message (mach), &hdr);
86 if (rc)
87 {
88 sieve_error (mach, "message_get_header: %s", mu_strerror (rc));
89 sieve_abort (mach);
90 }
91
92 if (header_aget_value (hdr, h->v.string, &val))
93 return 0;
94
95 if (mu_parse_date (val, &tval, &now))
96 {
97 sieve_error (mach,
98 "timestamp: can't parse header date specification (%s)",
99 val);
100 free (val);
101 sieve_abort (mach);
102 }
103 free (val);
104
105 rc = tval > tlimit;
106
107 if (sieve_tag_lookup (tags, "before", NULL))
108 rc = !rc;
109
110 return rc;
111 }
112
113 /* Required arguments: */
114 static sieve_data_type timestamp_req_args[] = {
115 SVT_STRING,
116 SVT_STRING,
117 SVT_VOID
118 };
119
120 /* Tagged arguments: */
121 static sieve_tag_def_t timestamp_tags[] = {
122 { "after", SVT_VOID },
123 { "before", SVT_VOID },
124 { NULL }
125 };
126
127 static sieve_tag_group_t timestamp_tag_groups[] = {
128 { timestamp_tags, NULL },
129 { NULL }
130 };
131
132 /* Initialization function. It is the only function exported from this
133 module. */
134 int
135 SIEVE_EXPORT(timestamp,init) (sieve_machine_t mach)
136 {
137 return sieve_register_test (mach, "timestamp", timestamp_test,
138 timestamp_req_args, timestamp_tag_groups, 1);
139 }