1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2003-2021 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 3, 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
15 License along with GNU Mailutils. If not, see
16 <http://www.gnu.org/licenses/>. */
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/sieve.h>
29
30
31
32 /* Auxiliary functions */
33 struct header_closure
34 {
35 mu_header_t header; /* Message header */
36 int index; /* Header index */
37 char *delim; /* List delimiter */
38 char **valv; /* Retrieved and split-out header values */
39 size_t valc; /* Number of values in valv */
40 size_t vali; /* Current index in valv */
41 };
42
43 static void
cleanup(struct header_closure * hc)44 cleanup (struct header_closure *hc)
45 {
46 mu_argcv_free (hc->valc, hc->valv);
47 hc->valv = NULL;
48 hc->valc = hc->vali = 0;
49 }
50
51 static int
retrieve_next_header(struct header_closure * hc,char * name,char ** pval)52 retrieve_next_header (struct header_closure *hc, char *name, char **pval)
53 {
54 const char *buf;
55
56 cleanup (hc);
57 while (!mu_header_sget_field_name (hc->header, hc->index, &buf))
58 {
59 int i = hc->index++;
60 if (mu_c_strcasecmp (buf, name) == 0)
61 {
62 const char *value;
63 struct mu_wordsplit ws;
64
65 if (mu_header_sget_field_value (hc->header, i, &value))
66 return 1;
67 ws.ws_delim = hc->delim;
68 if (mu_wordsplit (value, &ws,
69 MU_WRDSF_DELIM|MU_WRDSF_SQUEEZE_DELIMS|
70 MU_WRDSF_WS|
71 MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
72 {
73 mu_error (_("cannot split line `%s': %s"), value,
74 mu_wordsplit_strerror (&ws));
75 return 1;
76 }
77 if (ws.ws_wordc == 0)
78 {
79 cleanup (hc);
80 mu_wordsplit_free (&ws);
81 return 1;
82 }
83 mu_wordsplit_get_words (&ws, &hc->valc, &hc->valv);
84 mu_wordsplit_free (&ws);
85 hc->vali = 0;
86 *pval = hc->valv[hc->vali++];
87 return 0;
88 }
89 }
90
91 return 1;
92 }
93
94 static int
list_retrieve_header(void * item,void * data,size_t idx,char ** pval)95 list_retrieve_header (void *item, void *data, size_t idx, char **pval)
96 {
97 struct header_closure *hc = data;
98 char *p;
99
100 if (idx == 0)
101 hc->index = 1;
102
103 while (1)
104 {
105 if (!hc->valv)
106 {
107 if (retrieve_next_header (hc, (char*) item, &p))
108 return MU_ERR_NOENT;
109 }
110 else if (hc->vali == hc->valc)
111 {
112 cleanup (hc);
113 continue;
114 }
115 else
116 p = hc->valv[hc->vali++];
117
118 if ((*pval = strdup (p)) == NULL)
119 return errno;
120 return 0;
121 }
122
123 return MU_ERR_NOENT;
124 }
125
126
127 /* The test proper */
128
129 /* Syntax: list [COMPARATOR] [MATCH-TYPE]
130 [ :delim <delimiters: string> ]
131 <headers: string-list> <key-list: string-list>
132
133 The "list" test evaluates to true if any of the headers
134 match any key. Each header is regarded as containing a
135 list of keywords. By default, comma is assumed as list
136 separator. This can be overridden by specifying ":delim"
137 tag, whose value is a string consisting of valid list
138 delimiter characters.
139
140 list :matches :delim " ," [ "X-Spam-Keywords", "X-Spamd-Keywords" ]
141 [ "HTML_*", "FORGED_*" ]
142
143
144 */
145
146 static int
list_test(mu_sieve_machine_t mach)147 list_test (mu_sieve_machine_t mach)
148 {
149 mu_sieve_value_t *h, *v;
150 struct header_closure clos;
151 int result;
152
153 memset (&clos, 0, sizeof clos);
154 if (!mu_sieve_get_tag (mach, "delim", SVT_STRING, &clos.delim))
155 clos.delim = ",";
156
157 h = mu_sieve_get_arg_untyped (mach, 0);
158 v = mu_sieve_get_arg_untyped (mach, 1);
159 mu_message_get_header (mu_sieve_get_message (mach), &clos.header);
160 result = mu_sieve_vlist_compare (mach, h, v, list_retrieve_header, NULL,
161 &clos);
162 cleanup (&clos);
163 return result;
164 }
165
166
167 /* Initialization */
168
169 /* Required arguments: */
170 static mu_sieve_data_type list_req_args[] = {
171 SVT_STRING_LIST,
172 SVT_STRING_LIST,
173 SVT_VOID
174 };
175
176 static mu_sieve_tag_def_t match_part_tags[] = {
177 { "is", SVT_VOID },
178 { "contains", SVT_VOID },
179 { "matches", SVT_VOID },
180 { "regex", SVT_VOID },
181 { "count", SVT_STRING },
182 { "value", SVT_STRING },
183 { "comparator", SVT_STRING },
184 { NULL }
185 };
186
187 static mu_sieve_tag_def_t delim_part_tags[] = {
188 { "delim", SVT_STRING },
189 { NULL }
190 };
191
192 static mu_sieve_tag_group_t list_tag_groups[] = {
193 { match_part_tags, mu_sieve_match_part_checker },
194 { delim_part_tags, NULL },
195 { NULL }
196 };
197
198 /* Initialization function. */
199 int
SIEVE_EXPORT(list,init)200 SIEVE_EXPORT(list,init) (mu_sieve_machine_t mach)
201 {
202 mu_sieve_register_test (mach, "list", list_test,
203 list_req_args, list_tag_groups, 1);
204 return 0;
205 }
206
207 /* End of list.c */
208