This is useless.
[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 #define DAEMON_NAME "tst-filters"
42
43 DECLARE_MAIN
44
45 static char *read_query(const char *basepath, const char *filename,
46                         char *buff, char **end, query_t *q)
47 {
48     char path[FILENAME_MAX];
49     snprintf(path, FILENAME_MAX, "%s%s", basepath, filename);
50     {
51         file_map_t map;
52         if (!file_map_open(&map, path, false)) {
53             UNIXERR("open");
54             return NULL;
55         }
56         if (map.end - map.map >= BUFSIZ) {
57             err("File too large for a testcase: %s", path);
58             file_map_close(&map);
59             return NULL;
60         }
61         memcpy(buff, map.map, map.end - map.map);
62         if (end != NULL) {
63             *end = buff + (map.end - map.map);
64             **end = '\0';
65         } else {
66             buff[map.end - map.map] = '\0';
67         }
68         file_map_close(&map);
69     }
70
71     char *eoq = strstr(buff, "\n\n");
72     if (eoq == NULL) {
73         return NULL;
74     }
75     if (!query_parse(q, buff)) {
76         err("Cannot parse query from file %s", filename);
77         return NULL;
78     }
79     return eoq + 2;
80 }
81
82 static bool run_testcase(const config_t *config, const char *basepath,
83                          const char *filename)
84 {
85     char buff[BUFSIZ];
86     char *end;
87     query_t query;
88     const char *eol = read_query(basepath, filename, buff, &end, &query);
89     if (eol == NULL) {
90         return false;
91     }
92
93     bool ok = true;
94     while (eol < end) {
95         char *neol = memchr(eol, '\n', end - eol);
96         if (neol == NULL) {
97             neol = end;
98         }
99         *neol = '\0';
100         char *sep = memchr(eol, '=', neol - eol);
101         if (sep == NULL) {
102             eol = neol + 1;
103             err("missing separator");
104             continue;
105         }
106         *sep = '\0';
107
108         int pos = filter_find_with_name(&config->filters, eol);
109         if (pos == -1) {
110             err("Unknown filter %s", eol);
111             eol = neol + 1;
112             continue;
113         }
114         ++sep;
115         filter_result_t result = hook_tokenize(sep, neol - sep);
116         if (result == HTK_UNKNOWN) {
117             err("Unknown filter result %.*s", neol - sep, sep);
118             eol = neol + 1;
119             continue;
120         }
121         filter_t *filter = array_ptr(config->filters, pos);
122
123 #define TEST(Name, Run)                                                        \
124         do {                                                                   \
125           bool __test = (Run);                                                 \
126           printf("  test %s: %s\n", Name, __test ? "SUCCESS" : "FAILED");      \
127           ok = ok && __test;                                                   \
128         } while (0)
129         TEST(filter->name, filter_test(filter, &query, result));
130         eol = neol + 1;
131     }
132     return ok;
133 }
134
135 static bool run_greylisttest(const config_t *config, const char *basepath)
136 {
137     char buff_q1[BUFSIZ];
138     char buff_q2[BUFSIZ];
139     char buff_q3[BUFSIZ];
140     query_t q1;
141     query_t q2;
142     query_t q3;
143     bool ok = true;
144
145     filter_t *greylist1;
146 //    filter_t *greylist2;
147
148 #define QUERY(Q)                                                               \
149     if (read_query(basepath, "greylist_" STR(Q), buff_##Q, NULL, &Q) == NULL) {    \
150         return false;                                                          \
151     }
152     QUERY(q1);
153     QUERY(q2);
154     QUERY(q3);
155 #undef QUERY
156
157 #define FILTER(F)                                                              \
158     do {                                                                       \
159       int __p = filter_find_with_name(&config->filters, STR(F));               \
160       if (__p < 0) {                                                           \
161           return false;                                                        \
162       }                                                                        \
163       F = array_ptr(config->filters, __p);                                     \
164     } while (0)
165     FILTER(greylist1);
166 //    FILTER(greylist2);
167 #undef FILTER
168
169     /* Test greylist */
170     TEST("greylisted", filter_test(greylist1, &q1, HTK_GREYLIST));
171     TEST("too_fast", filter_test(greylist1, &q1, HTK_GREYLIST));
172     sleep(5);
173     TEST("too_slow", filter_test(greylist1, &q1, HTK_GREYLIST));
174     sleep(2);
175     TEST("whitelisted", filter_test(greylist1, &q1, HTK_WHITELIST));
176     TEST("other_greylisted", filter_test(greylist1, &q2, HTK_GREYLIST));
177     TEST("auto_whitelisted", filter_test(greylist1, &q1, HTK_WHITELIST));
178     TEST("other_auto_whitelisted", filter_test(greylist1, &q2, HTK_WHITELIST));
179     TEST("greylisted", filter_test(greylist1, &q3, HTK_GREYLIST));
180     sleep(10);
181     TEST("cleanup", filter_test(greylist1, &q1, HTK_GREYLIST));
182
183     return ok;
184 }
185
186 int main(int argc, char *argv[])
187 {
188     char basepath[FILENAME_MAX];
189     char path[FILENAME_MAX];
190     char *p;
191
192     p = strrchr(argv[0], '/');
193     if (p == NULL) {
194         p = argv[0];
195     } else {
196         ++p;
197     }
198     snprintf(basepath, FILENAME_MAX, "%.*sdata/", p - argv[0], argv[0]);
199
200     /* Cleanup */
201     {
202 #define RM(File)                                                               \
203       snprintf(path, FILENAME_MAX, "%s/%s", basepath, File);                   \
204       unlink(path);
205 //      RM("test1_greylist.db");
206 //      RM("test1_whitelist.db");
207       RM("test2_greylist.db");
208       RM("test2_whitelist.db");
209 #undef RM
210     }
211
212     snprintf(path, FILENAME_MAX, "%s/test.conf", basepath);
213
214     config_t *config = config_read(path);
215     if (config == NULL) {
216         return 1;
217     }
218
219
220 #define RUN(Name, Test, ...)                                                   \
221     printf("Running %s:\n", Name);                                             \
222     printf("%s\n", run_##Test(config, basepath, ##__VA_ARGS__) ? "SUCCESS"     \
223                                                                : "FAILED");
224
225     /* Test stateless filters */
226     DIR *dir = opendir(basepath);
227     if (dir == NULL) {
228         UNIXERR("opendir");
229         return 1;
230     }
231     struct dirent *ent;
232     while ((ent = readdir(dir)) != NULL) {
233         if (strncmp("testcase_", ent->d_name, 9) == 0) {
234             RUN(ent->d_name, testcase, ent->d_name);
235         }
236     }
237     closedir(dir);
238
239     /* Test greylist */
240     RUN("greylist", greylisttest);
241
242
243 #undef RUN
244     return 0;
245 }