PageRenderTime 96ms CodeModel.GetById 32ms RepoModel.GetById 7ms app.codeStats 0ms

/libmu_sieve/comparator.c

#
C | 521 lines | 431 code | 63 blank | 27 comment | 96 complexity | 79681f57798c2e60574de30ac79b2b96 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, CC-BY-SA-3.0
  1. /* GNU Mailutils -- a suite of utilities for electronic mail
  2. Copyright (C) 1999-2002, 2004-2005, 2007-2012, 2014 Free Software
  3. Foundation, Inc.
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 3 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General
  13. Public License along with this library. If not, see
  14. <http://www.gnu.org/licenses/>. */
  15. #ifdef HAVE_CONFIG_H
  16. # include <config.h>
  17. #endif
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <unistd.h>
  21. #include <string.h>
  22. #include <sieve-priv.h>
  23. #include <fnmatch.h>
  24. #include <regex.h>
  25. #include <mailutils/cctype.h>
  26. #include <mailutils/cstr.h>
  27. typedef struct {
  28. const char *name;
  29. int required;
  30. mu_sieve_comparator_t comp[MU_SIEVE_MATCH_LAST];
  31. } sieve_comparator_record_t;
  32. int
  33. mu_sieve_register_comparator (mu_sieve_machine_t mach,
  34. const char *name,
  35. int required,
  36. mu_sieve_comparator_t is,
  37. mu_sieve_comparator_t contains,
  38. mu_sieve_comparator_t matches,
  39. mu_sieve_comparator_t regex,
  40. mu_sieve_comparator_t eq)
  41. {
  42. sieve_comparator_record_t *rp;
  43. if (!mach->comp_list)
  44. {
  45. int rc = mu_list_create (&mach->comp_list);
  46. if (rc)
  47. return rc;
  48. }
  49. rp = mu_sieve_malloc (mach, sizeof (*rp));
  50. rp->required = required;
  51. rp->name = name;
  52. rp->comp[MU_SIEVE_MATCH_IS] = is;
  53. rp->comp[MU_SIEVE_MATCH_CONTAINS] = contains;
  54. rp->comp[MU_SIEVE_MATCH_MATCHES] = matches;
  55. rp->comp[MU_SIEVE_MATCH_REGEX] = regex;
  56. rp->comp[MU_SIEVE_MATCH_EQ] = eq;
  57. return mu_list_append (mach->comp_list, rp);
  58. }
  59. sieve_comparator_record_t *
  60. _lookup (mu_list_t list, const char *name)
  61. {
  62. mu_iterator_t itr;
  63. sieve_comparator_record_t *reg;
  64. if (!list || mu_list_get_iterator (list, &itr))
  65. return NULL;
  66. for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr))
  67. {
  68. mu_iterator_current (itr, (void **)&reg);
  69. if (strcmp (reg->name, name) == 0)
  70. break;
  71. else
  72. reg = NULL;
  73. }
  74. mu_iterator_destroy (&itr);
  75. return reg;
  76. }
  77. int
  78. mu_sieve_require_comparator (mu_sieve_machine_t mach, const char *name)
  79. {
  80. sieve_comparator_record_t *reg = _lookup (mach->comp_list, name);
  81. if (!reg)
  82. {
  83. if (!(mu_sieve_load_ext (mach, name) == 0
  84. && (reg = _lookup (mach->comp_list, name)) != NULL))
  85. return 1;
  86. }
  87. reg->required = 1;
  88. return 0;
  89. }
  90. mu_sieve_comparator_t
  91. mu_sieve_comparator_lookup (mu_sieve_machine_t mach, const char *name,
  92. int matchtype)
  93. {
  94. sieve_comparator_record_t *reg = _lookup (mach->comp_list, name);
  95. if (reg && reg->comp[matchtype])
  96. return reg->comp[matchtype];
  97. return NULL;
  98. }
  99. static int
  100. _find_comparator (void *item, void *data)
  101. {
  102. mu_sieve_runtime_tag_t *tag = item;
  103. if (strcmp (tag->tag, TAG_COMPFUN) == 0)
  104. {
  105. *(mu_sieve_comparator_t*)data = tag->arg->v.ptr;
  106. return 1;
  107. }
  108. return 0;
  109. }
  110. mu_sieve_comparator_t
  111. mu_sieve_get_comparator (mu_sieve_machine_t mach, mu_list_t tags)
  112. {
  113. mu_sieve_comparator_t comp = NULL;
  114. mu_list_foreach (tags, _find_comparator, &comp);
  115. return comp ? comp : mu_sieve_comparator_lookup (mach,
  116. "i;ascii-casemap",
  117. MU_SIEVE_MATCH_IS);
  118. }
  119. /* Compile time support */
  120. struct regex_data {
  121. int flags;
  122. mu_list_t list;
  123. };
  124. #ifndef FNM_CASEFOLD
  125. static int
  126. _pattern_upcase (void *item, void *data)
  127. {
  128. mu_strupper (item);
  129. return 0;
  130. }
  131. #endif
  132. static int
  133. _regex_compile (void *item, void *data)
  134. {
  135. struct regex_data *rd = data;
  136. int rc;
  137. regex_t *preg = mu_sieve_malloc (mu_sieve_machine, sizeof (*preg));
  138. rc = regcomp (preg, (char*)item, rd->flags);
  139. if (rc)
  140. {
  141. size_t size = regerror (rc, preg, NULL, 0);
  142. char *errbuf = malloc (size + 1);
  143. if (errbuf)
  144. {
  145. regerror (rc, preg, errbuf, size);
  146. mu_sv_compile_error (&mu_sieve_locus, _("regex error: %s"), errbuf);
  147. free (errbuf);
  148. }
  149. else
  150. mu_sv_compile_error (&mu_sieve_locus, _("regex error"));
  151. return rc;
  152. }
  153. mu_list_append (rd->list, preg);
  154. return 0;
  155. }
  156. static int
  157. _free_regex (void *item, void *unused)
  158. {
  159. regfree ((regex_t*)item);
  160. return 0;
  161. }
  162. static void
  163. _free_reglist (void *data)
  164. {
  165. mu_list_t list = data;
  166. mu_list_foreach (list, _free_regex, NULL);
  167. mu_list_destroy (&list);
  168. }
  169. static int
  170. comp_false (const char *pattern, const char *text)
  171. {
  172. return 0;
  173. }
  174. int
  175. mu_sieve_match_part_checker (const char *name, mu_list_t tags, mu_list_t args)
  176. {
  177. mu_iterator_t itr;
  178. mu_sieve_runtime_tag_t *match = NULL;
  179. mu_sieve_runtime_tag_t *comp = NULL;
  180. mu_sieve_runtime_tag_t *tmp;
  181. mu_sieve_comparator_t compfun = NULL;
  182. char *compname = "false";
  183. int matchtype;
  184. int err = 0;
  185. if (!tags || mu_list_get_iterator (tags, &itr))
  186. return 0;
  187. for (mu_iterator_first (itr); !err && !mu_iterator_is_done (itr);
  188. mu_iterator_next (itr))
  189. {
  190. mu_sieve_runtime_tag_t *t;
  191. mu_iterator_current (itr, (void **)&t);
  192. if (strcmp (t->tag, "is") == 0
  193. || strcmp (t->tag, "contains") == 0
  194. || strcmp (t->tag, "matches") == 0
  195. || strcmp (t->tag, "regex") == 0
  196. || strcmp (t->tag, "count") == 0
  197. || strcmp (t->tag, "value") == 0)
  198. {
  199. if (match)
  200. {
  201. mu_sv_compile_error (&mu_sieve_locus,
  202. _("match type specified twice in call to `%s'"),
  203. name);
  204. err = 1;
  205. }
  206. else
  207. match = t;
  208. }
  209. else if (strcmp (t->tag, "comparator") == 0)
  210. comp = t;
  211. }
  212. mu_iterator_destroy (&itr);
  213. if (err)
  214. return 1;
  215. if (!match || strcmp (match->tag, "is") == 0)
  216. matchtype = MU_SIEVE_MATCH_IS;
  217. else if (strcmp (match->tag, "contains") == 0)
  218. matchtype = MU_SIEVE_MATCH_CONTAINS;
  219. else if (strcmp (match->tag, "matches") == 0)
  220. matchtype = MU_SIEVE_MATCH_MATCHES;
  221. else if (strcmp (match->tag, "regex") == 0)
  222. matchtype = MU_SIEVE_MATCH_REGEX;
  223. else
  224. {
  225. char *str = match->arg->v.string;
  226. if (strcmp (match->tag, "count") == 0)
  227. {
  228. mu_sieve_value_t *val;
  229. char *str;
  230. size_t count;
  231. if (comp && strcmp (comp->arg->v.string, "i;ascii-numeric"))
  232. {
  233. mu_sv_compile_error (&mu_sieve_locus,
  234. /* TRANSLATORS: Do not translate ':count'.
  235. It is the name of a Sieve tag */
  236. _("comparator %s is incompatible with "
  237. ":count in call to `%s'"),
  238. comp->arg->v.string,
  239. name);
  240. return 1;
  241. }
  242. matchtype = MU_SIEVE_MATCH_LAST; /* to not leave it undefined */
  243. compfun = comp_false;
  244. val = mu_sieve_value_get (args, 1);
  245. if (!val)
  246. return 1; /* shouldn't happen */
  247. /* NOTE: Type of v is always SVT_STRING_LIST */
  248. mu_list_count (val->v.list, &count);
  249. if (count > 1)
  250. {
  251. mu_sv_compile_error (&mu_sieve_locus,
  252. _("second argument must be a list of one element"));
  253. return 1;
  254. }
  255. mu_list_get (val->v.list, 0, (void **) &str);
  256. count = strtoul (str, &str, 10);
  257. if (*str)
  258. {
  259. mu_sv_compile_error (&mu_sieve_locus,
  260. _("second argument cannot be converted to number"));
  261. return 1;
  262. }
  263. }
  264. else
  265. matchtype = MU_SIEVE_MATCH_EQ;
  266. if (mu_sieve_str_to_relcmp (str, NULL, NULL))
  267. {
  268. mu_sv_compile_error (&mu_sieve_locus,
  269. _("invalid relational match `%s' in call to `%s'"),
  270. str, name);
  271. return 1;
  272. }
  273. }
  274. if (!compfun)
  275. {
  276. compname = comp ? comp->arg->v.string : "i;ascii-casemap";
  277. compfun = mu_sieve_comparator_lookup (mu_sieve_machine, compname,
  278. matchtype);
  279. if (!compfun)
  280. {
  281. mu_sv_compile_error (&mu_sieve_locus,
  282. _("comparator `%s' is incompatible with match type `%s' in call to `%s'"),
  283. compname, match ? match->tag : "is", name);
  284. return 1;
  285. }
  286. }
  287. tmp = mu_sieve_malloc (mu_sieve_machine, sizeof (*tmp));
  288. tmp->tag = TAG_COMPFUN;
  289. tmp->arg = mu_sieve_value_create (SVT_POINTER, compfun);
  290. mu_list_append (tags, tmp);
  291. if (matchtype == MU_SIEVE_MATCH_REGEX)
  292. {
  293. /* To speed up things, compile all patterns at once.
  294. Notice that it is supposed that patterns are in arg 2 */
  295. mu_sieve_value_t *val, *newval;
  296. struct regex_data rd;
  297. int rc;
  298. if (mu_list_get (args, 1, (void**)&val))
  299. return 0;
  300. rd.flags = REG_EXTENDED;
  301. if (strcmp (compname, "i;ascii-casemap") == 0)
  302. rd.flags |= REG_ICASE;
  303. mu_list_create (&rd.list);
  304. rc = mu_sieve_vlist_do (val, _regex_compile, &rd);
  305. mu_sieve_machine_add_destructor (mu_sieve_machine, _free_reglist,
  306. rd.list);
  307. if (rc)
  308. return rc;
  309. newval = mu_sieve_value_create (SVT_STRING_LIST, rd.list);
  310. mu_list_replace (args, val, newval);
  311. }
  312. #ifndef FNM_CASEFOLD
  313. else if (matchtype == MU_SIEVE_MATCH_MATCHES
  314. && strcmp (compname, "i;ascii-casemap") == 0)
  315. {
  316. int rc;
  317. mu_sieve_value_t *val;
  318. if (mu_list_get (args, 1, (void**)&val))
  319. return 0;
  320. rc = mu_sieve_vlist_do (val, _pattern_upcase, NULL);
  321. if (rc)
  322. return rc;
  323. }
  324. #endif
  325. return 0;
  326. }
  327. /* Particular comparators */
  328. /* :comparator i;octet */
  329. static int
  330. i_octet_is (const char *pattern, const char *text)
  331. {
  332. return strcmp (pattern, text) == 0;
  333. }
  334. static int
  335. i_octet_contains (const char *pattern, const char *text)
  336. {
  337. return strstr (text, pattern) != NULL;
  338. }
  339. static int
  340. i_octet_matches (const char *pattern, const char *text)
  341. {
  342. return fnmatch (pattern, text, 0) == 0;
  343. }
  344. static int
  345. i_octet_regex (const char *pattern, const char *text)
  346. {
  347. return regexec ((regex_t *) pattern, text, 0, NULL, 0) == 0;
  348. }
  349. static int
  350. i_octet_eq (const char *pattern, const char *text)
  351. {
  352. return strcmp (text, pattern);
  353. }
  354. /* :comparator i;ascii-casemap */
  355. static int
  356. i_ascii_casemap_is (const char *pattern, const char *text)
  357. {
  358. return mu_c_strcasecmp (pattern, text) == 0;
  359. }
  360. static int
  361. i_ascii_casemap_contains (const char *pattern, const char *text)
  362. {
  363. return mu_c_strcasestr (text, pattern) != NULL;
  364. }
  365. static int
  366. i_ascii_casemap_matches (const char *pattern, const char *text)
  367. {
  368. #ifdef FNM_CASEFOLD
  369. return fnmatch (pattern, text, FNM_CASEFOLD) == 0;
  370. #else
  371. int rc;
  372. char *p = strdup (text);
  373. if (!p)
  374. return 0;
  375. _pattern_upcase (p, NULL);
  376. rc = fnmatch (pattern, p, 0) == 0;
  377. free (p);
  378. return rc;
  379. #endif
  380. }
  381. static int
  382. i_ascii_casemap_regex (const char *pattern, const char *text)
  383. {
  384. return regexec ((regex_t *) pattern, text, 0, NULL, 0) == 0;
  385. }
  386. static int
  387. i_ascii_casemap_eq (const char *pattern, const char *text)
  388. {
  389. return mu_c_strcasecmp (text, pattern);
  390. }
  391. /* :comparator i;ascii-numeric */
  392. static int
  393. i_ascii_numeric_is (const char *pattern, const char *text)
  394. {
  395. if (mu_isdigit (*pattern))
  396. {
  397. if (mu_isdigit (*text))
  398. return strtol (pattern, NULL, 10) == strtol (text, NULL, 10);
  399. else
  400. return 0;
  401. }
  402. else if (mu_isdigit (*text))
  403. return 0;
  404. else
  405. return 1;
  406. }
  407. static int
  408. i_ascii_numeric_eq (const char *pattern, const char *text)
  409. {
  410. if (mu_isdigit (*pattern))
  411. {
  412. if (mu_isdigit (*text))
  413. {
  414. size_t a = strtoul (pattern, NULL, 10);
  415. size_t b = strtoul (text, NULL, 10);
  416. if (b > a)
  417. return 1;
  418. else if (b < a)
  419. return -1;
  420. return 0;
  421. }
  422. else
  423. return 1;
  424. }
  425. else
  426. return 1;
  427. }
  428. void
  429. mu_sv_register_standard_comparators (mu_sieve_machine_t mach)
  430. {
  431. mu_sieve_register_comparator (mach,
  432. "i;octet",
  433. 1,
  434. i_octet_is,
  435. i_octet_contains,
  436. i_octet_matches,
  437. i_octet_regex,
  438. i_octet_eq);
  439. mu_sieve_register_comparator (mach,
  440. "i;ascii-casemap",
  441. 1,
  442. i_ascii_casemap_is,
  443. i_ascii_casemap_contains,
  444. i_ascii_casemap_matches,
  445. i_ascii_casemap_regex,
  446. i_ascii_casemap_eq);
  447. mu_sieve_register_comparator (mach,
  448. "i;ascii-numeric",
  449. 0,
  450. i_ascii_numeric_is,
  451. NULL,
  452. NULL,
  453. NULL,
  454. i_ascii_numeric_eq);
  455. }