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