pfix-srsd: add a -I option
[apps/pfixtools.git] / postlicyd / tst-filters.c
1 /******************************************************************************/
2 /*          pfixtools: a collection of postfix related tools                  */
3 /*          ~~~~~~~~~                                                         */
4 /*  ________________________________________________________________________  */
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 /*  2. Redistributions in binary form must reproduce the above copyright      */
13 /*     notice, this list of conditions and the following disclaimer in the    */
14 /*     documentation and/or other materials provided with the distribution.   */
15 /*  3. The names of its contributors may not be used to endorse or promote    */
16 /*     products derived from this software without specific prior written     */
17 /*     permission.                                                            */
18 /*                                                                            */
19 /*  THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS   */
20 /*  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED         */
21 /*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE    */
22 /*  DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY         */
23 /*  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL        */
24 /*  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS   */
25 /*  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)     */
26 /*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,       */
27 /*  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN  */
28 /*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
29 /*  POSSIBILITY OF SUCH DAMAGE.                                               */
30 /*                                                                            */
31 /*   Copyright (c) 2006-2008 the Authors                                      */
32 /*   see AUTHORS and source files for details                                 */
33 /******************************************************************************/
34
35 /*
36  * Copyright © 2008 Florent Bruneau
37  */
38
39 #include "str.h"
40 #include "config.h"
41 #include "file.h"
42 #include <dirent.h>
43
44 static char *read_query(const char *basepath, const char *filename,
45                         char *buff, char **end, query_t *q)
46 {
47     char path[FILENAME_MAX];
48     snprintf(path, FILENAME_MAX, "%s%s", basepath, filename);
49     {
50         file_map_t map;
51         if (!file_map_open(&map, path, false)) {
52             UNIXERR("open");
53             return NULL;
54         }
55         if (map.end - map.map >= BUFSIZ) {
56             err("File too large for a testcase: %s", path);
57             file_map_close(&map);
58             return NULL;
59         }
60         memcpy(buff, map.map, map.end - map.map);
61         if (end != NULL) {
62             *end = buff + (map.end - map.map);
63             **end = '\0';
64         } else {
65             buff[map.end - map.map] = '\0';
66         }
67         file_map_close(&map);
68     }
69
70     char *eoq = strstr(buff, "\n\n");
71     if (eoq == NULL) {
72         return NULL;
73     }
74     if (!query_parse(q, buff)) {
75         err("Cannot parse query from file %s", filename);
76         return NULL;
77     }
78     return eoq + 2;
79 }
80
81 static bool run_testcase(const config_t *config, const char *basepath,
82                          const char *filename)
83 {
84     char buff[BUFSIZ];
85     char *end;
86     query_t query;
87     const char *eol = read_query(basepath, filename, buff, &end, &query);
88     if (eol == NULL) {
89         return false;
90     }
91
92     bool ok = true;
93     filter_context_t context;
94     filter_context_prepare(&context, NULL);
95
96     while (eol < end) {
97         char *neol = memchr(eol, '\n', end - eol);
98         if (neol == NULL) {
99             neol = end;
100         }
101         *neol = '\0';
102         char *sep = memchr(eol, '=', neol - eol);
103         if (sep == NULL) {
104             eol = neol + 1;
105             err("missing separator");
106             continue;
107         }
108         *sep = '\0';
109
110         int pos = filter_find_with_name(&config->filters, eol);
111         if (pos == -1) {
112             err("Unknown filter %s", eol);
113             eol = neol + 1;
114             continue;
115         }
116         ++sep;
117         filter_result_t result = hook_tokenize(sep, neol - sep);
118         if (result == HTK_UNKNOWN) {
119             err("Unknown filter result %.*s", neol - sep, sep);
120             eol = neol + 1;
121             continue;
122         }
123         filter_t *filter = array_ptr(config->filters, pos);
124
125 #define TEST(Name, Run)                                                        \
126         do {                                                                   \
127           bool __test = (Run);                                                 \
128           printf("  test %s: %s\n", Name, __test ? "SUCCESS" : "FAILED");      \
129           ok = ok && __test;                                                   \
130         } while (0)
131         TEST(filter->name, filter_test(filter, &query, &context, result));
132         eol = neol + 1;
133
134     }
135     filter_context_wipe(&context);
136     return ok;
137 }
138
139 static bool run_greylisttest(const config_t *config, const char *basepath)
140 {
141     char buff_q1[BUFSIZ];
142     char buff_q2[BUFSIZ];
143     char buff_q3[BUFSIZ];
144     query_t q1;
145     query_t q2;
146     query_t q3;
147     bool ok = true;
148
149     filter_t *greylist1;
150 //    filter_t *greylist2;
151
152 #define QUERY(Q)                                                               \
153     if (read_query(basepath, "greylist_" STR(Q), buff_##Q, NULL, &Q) == NULL) {    \
154         return false;                                                          \
155     }
156     QUERY(q1);
157     QUERY(q2);
158     QUERY(q3);
159 #undef QUERY
160
161 #define FILTER(F)                                                              \
162     do {                                                                       \
163       int __p = filter_find_with_name(&config->filters, STR(F));               \
164       if (__p < 0) {                                                           \
165           return false;                                                        \
166       }                                                                        \
167       F = array_ptr(config->filters, __p);                                     \
168     } while (0)
169     FILTER(greylist1);
170 //    FILTER(greylist2);
171 #undef FILTER
172
173     filter_context_t context;
174     filter_context_prepare(&context, NULL);
175
176     /* Test greylist */
177     TEST("greylisted", filter_test(greylist1, &q1, &context, HTK_GREYLIST));
178     TEST("too_fast", filter_test(greylist1, &q1, &context, HTK_GREYLIST));
179     sleep(5);
180     TEST("too_slow", filter_test(greylist1, &q1, &context, HTK_GREYLIST));
181     sleep(2);
182     TEST("whitelisted", filter_test(greylist1, &q1, &context, HTK_WHITELIST));
183     TEST("other_greylisted", filter_test(greylist1, &q2, &context, HTK_GREYLIST));
184     TEST("auto_whitelisted", filter_test(greylist1, &q1, &context, HTK_WHITELIST));
185     TEST("other_auto_whitelisted", filter_test(greylist1, &q2, &context, HTK_WHITELIST));
186     TEST("greylisted", filter_test(greylist1, &q3, &context, HTK_GREYLIST));
187     sleep(10);
188     TEST("cleanup", filter_test(greylist1, &q1, &context, HTK_GREYLIST));
189
190     filter_context_wipe(&context);
191     return ok;
192 }
193
194 int main(int argc, char *argv[])
195 {
196     char basepath[FILENAME_MAX];
197     char path[FILENAME_MAX];
198     char *p;
199
200     common_startup();
201     p = strrchr(argv[0], '/');
202     if (p == NULL) {
203         p = argv[0];
204     } else {
205         ++p;
206     }
207     snprintf(basepath, FILENAME_MAX, "%.*sdata/", p - argv[0], argv[0]);
208
209     /* Cleanup */
210     {
211 #define RM(File)                                                               \
212       snprintf(path, FILENAME_MAX, "%s/%s", basepath, File);                   \
213       unlink(path);
214       RM("test1_greylist.db");
215       RM("test1_whitelist.db");
216       RM("test2_greylist.db");
217       RM("test2_whitelist.db");
218 #undef RM
219     }
220
221     snprintf(path, FILENAME_MAX, "%s/test.conf", basepath);
222
223     config_t *config = config_read(path);
224     if (config == NULL) {
225         return 1;
226     }
227
228
229 #define RUN(Name, Test, ...)                                                   \
230     printf("Running %s:\n", Name);                                             \
231     printf("%s\n", run_##Test(config, basepath, ##__VA_ARGS__) ? "SUCCESS"     \
232                                                                : "FAILED");
233
234     /* Test stateless filters */
235     DIR *dir = opendir(basepath);
236     if (dir == NULL) {
237         UNIXERR("opendir");
238         return 1;
239     }
240     struct dirent *ent;
241     while ((ent = readdir(dir)) != NULL) {
242         if (strncmp("testcase_", ent->d_name, 9) == 0) {
243             RUN(ent->d_name, testcase, ent->d_name);
244         }
245     }
246     closedir(dir);
247
248     /* Test greylist */
249     RUN("greylist", greylisttest);
250
251
252 #undef RUN
253     return 0;
254 }