2
* Copyright (c) 2007-2012, Vsevolod Stakhov
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions are met:
7
* Redistributions of source code must retain the above copyright notice, this
8
* list of conditions and the following disclaimer. Redistributions in binary form
9
* must reproduce the above copyright notice, this list of conditions and the
10
* following disclaimer in the documentation and/or other materials provided with
11
* the distribution. Neither the name of the author nor the names of its
12
* contributors may be used to endorse or promote products derived from this
13
* software without specific prior written permission.
15
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
parse_err (const char *fmt, ...)
41
char logbuf[BUFSIZ], readbuf[32];
45
rmilter_strlcpy (readbuf, yytext, sizeof (readbuf));
47
r = snprintf (logbuf, sizeof (logbuf), "config file parse error! line: %d, text: %s, reason: ", yylineno, readbuf);
48
r += vsnprintf (logbuf + r, sizeof (logbuf) - r, fmt, aq);
51
fprintf (stderr,"%s\n", logbuf);
52
syslog (LOG_ERR, "%s", logbuf);
56
parse_warn (const char *fmt, ...)
59
char logbuf[BUFSIZ], readbuf[32];
63
rmilter_strlcpy (readbuf, yytext, sizeof (readbuf));
65
r = snprintf (logbuf, sizeof (logbuf), "config file parse warning! line: %d, text: %s, reason: ", yylineno, readbuf);
66
r += vsnprintf (logbuf + r, sizeof (logbuf) - r, fmt, aq);
69
syslog (LOG_ERR, "%s", logbuf);
74
copy_regexp (char **dst, const char *src)
77
if (!src || *src == '\0') return 0;
86
if (src[len - 1] == '/') {
90
*dst = malloc (len + 1);
93
return rmilter_strlcpy (*dst, src, len + 1);
97
add_memcached_server (struct config_file *cf, char *str, char *str2, int type)
99
char *cur_tok, *err_str;
100
struct memcached_server *mc = NULL;
104
if (str == NULL) return 0;
106
if (type == MEMCACHED_SERVER_GREY) {
107
if(cf->memcached_servers_grey_num == MAX_MEMCACHED_SERVERS) {
108
yywarn ("yyparse: maximum number of memcached servers is reached %d", MAX_MEMCACHED_SERVERS);
112
mc = &cf->memcached_servers_grey[cf->memcached_servers_grey_num];
114
else if (type == MEMCACHED_SERVER_WHITE) {
115
if(cf->memcached_servers_white_num == MAX_MEMCACHED_SERVERS) {
116
yywarn ("yyparse: maximum number of whitelist memcached servers is reached %d", MAX_MEMCACHED_SERVERS);
120
mc = &cf->memcached_servers_white[cf->memcached_servers_white_num];
122
else if (type == MEMCACHED_SERVER_LIMITS) {
123
if(cf->memcached_servers_limits_num == MAX_MEMCACHED_SERVERS) {
124
yywarn ("yyparse: maximum number of limits memcached servers is reached %d", MAX_MEMCACHED_SERVERS);
128
mc = &cf->memcached_servers_limits[cf->memcached_servers_limits_num];
130
else if (type == MEMCACHED_SERVER_ID) {
131
if(cf->memcached_servers_id_num == MAX_MEMCACHED_SERVERS) {
132
yywarn ("yyparse: maximum number of id memcached servers is reached %d", MAX_MEMCACHED_SERVERS);
136
mc = &cf->memcached_servers_id[cf->memcached_servers_id_num];
138
if (mc == NULL) return 0;
140
cur_tok = strsep (&str, ":");
142
if (cur_tok == NULL || *cur_tok == '\0') return 0;
144
/* cur_tok - server name, str - server port */
146
port = htons(DEFAULT_MEMCACHED_PORT);
149
port = htons ((uint16_t)strtoul (str, &err_str, 10));
150
if (*err_str != '\0') {
155
if (!inet_aton (cur_tok, &mc->addr[0])) {
156
/* Try to call gethostbyname */
157
he = gethostbyname (cur_tok);
162
memcpy((char *)&mc->addr[0], he->h_addr, sizeof(struct in_addr));
172
cur_tok = strsep (&str2, ":");
174
if (cur_tok == NULL || *cur_tok == '\0') return 0;
176
/* cur_tok - server name, str - server port */
178
port = htons(DEFAULT_MEMCACHED_PORT);
181
port = htons ((uint16_t)strtoul (str2, &err_str, 10));
182
if (*err_str != '\0') {
187
if (!inet_aton (cur_tok, &mc->addr[1])) {
188
/* Try to call gethostbyname */
189
he = gethostbyname (cur_tok);
194
memcpy((char *)&mc->addr[1], he->h_addr, sizeof(struct in_addr));
203
if (type == MEMCACHED_SERVER_GREY) {
204
cf->memcached_servers_grey_num++;
206
else if (type == MEMCACHED_SERVER_WHITE) {
207
cf->memcached_servers_white_num++;
209
else if (type == MEMCACHED_SERVER_LIMITS) {
210
cf->memcached_servers_limits_num++;
212
else if (type == MEMCACHED_SERVER_ID) {
213
cf->memcached_servers_id_num++;
220
add_clamav_server (struct config_file *cf, char *str)
222
char *cur_tok, *err_str;
223
struct clamav_server *srv;
226
if (str == NULL) return 0;
228
cur_tok = strsep (&str, ":");
230
if (cur_tok == NULL || *cur_tok == '\0') return 0;
232
if (cf->clamav_servers_num == MAX_CLAMAV_SERVERS) {
233
yywarn ("yyparse: maximum number of clamav servers is reached %d", MAX_CLAMAV_SERVERS);
236
srv = &cf->clamav_servers[cf->clamav_servers_num];
238
if (srv == NULL) return 0;
240
if (cur_tok[0] == '/' || cur_tok[0] == '.') {
241
srv->sock.unix_path = strdup (cur_tok);
242
srv->sock_type = AF_UNIX;
243
srv->name = srv->sock.unix_path;
244
if (str != NULL && *str != '\0') {
245
srv->up.priority = strtoul (str, &err_str, 10);
246
if (*err_str != '\0') {
249
cf->weighted_clamav = 1;
252
cf->clamav_servers_num++;
257
srv->sock.inet.port = htons (DEFAULT_CLAMAV_PORT);
260
srv->sock.inet.port = htons ((uint16_t)strtoul (str, &err_str, 10));
261
if (*err_str != '\0') {
267
if (!inet_aton (cur_tok, &srv->sock.inet.addr)) {
268
/* Try to call gethostbyname */
269
he = gethostbyname (cur_tok);
274
srv->name = strdup (cur_tok);
275
memcpy((char *)&srv->sock.inet.addr, he->h_addr, sizeof(struct in_addr));
278
/* Try to parse priority */
279
cur_tok = strsep (&str, ":");
280
if (str != NULL && *str != '\0') {
281
srv->up.priority = strtoul (str, &err_str, 10);
282
if (*err_str != '\0') {
285
cf->weighted_clamav = 1;
288
srv->sock_type = AF_INET;
289
cf->clamav_servers_num++;
297
add_spamd_server (struct config_file *cf, char *str, int is_extra)
299
char *cur_tok, *err_str;
300
struct spamd_server *srv;
303
if (str == NULL) return 0;
306
if (cf->extra_spamd_servers_num == MAX_SPAMD_SERVERS) {
307
yywarn ("yyparse: maximum number of spamd servers is reached %d", MAX_SPAMD_SERVERS);
312
if (cf->spamd_servers_num == MAX_SPAMD_SERVERS) {
313
yywarn ("yyparse: maximum number of spamd servers is reached %d", MAX_SPAMD_SERVERS);
319
srv = &cf->extra_spamd_servers[cf->extra_spamd_servers_num];
322
srv = &cf->spamd_servers[cf->spamd_servers_num];
325
if (*str == 'r' && *(str + 1) == ':') {
326
srv->type = SPAMD_RSPAMD;
330
srv->type = SPAMD_SPAMASSASSIN;
334
cur_tok = strsep (&str, ":");
336
if (cur_tok == NULL || *cur_tok == '\0') return 0;
338
if (cur_tok[0] == '/' || cur_tok[0] == '.') {
339
srv->sock.unix_path = strdup (cur_tok);
340
srv->sock_type = AF_UNIX;
341
srv->name = srv->sock.unix_path;
344
cf->extra_spamd_servers_num++;
347
cf->spamd_servers_num++;
353
srv->sock.inet.port = htons (DEFAULT_SPAMD_PORT);
356
srv->sock.inet.port = htons ((uint16_t)strtoul (str, &err_str, 10));
357
if (*err_str != '\0') {
362
if (!inet_aton (cur_tok, &srv->sock.inet.addr)) {
363
/* Try to call gethostbyname */
364
he = gethostbyname (cur_tok);
369
memcpy((char *)&srv->sock.inet.addr, he->h_addr, sizeof(struct in_addr));
372
srv->name = strdup (cur_tok);
374
srv->sock_type = AF_INET;
376
cf->extra_spamd_servers_num++;
379
cf->spamd_servers_num++;
388
add_beanstalk_server (struct config_file *cf, char *str, int type)
390
char *cur_tok, *err_str;
391
struct beanstalk_server *srv;
394
if (str == NULL) return 0;
396
cur_tok = strsep (&str, ":");
398
if (cur_tok == NULL || *cur_tok == '\0') return 0;
401
cf->copy_server = malloc (sizeof (struct beanstalk_server));
402
srv = cf->copy_server;
404
else if (type == 2) {
405
cf->spam_server = malloc (sizeof (struct beanstalk_server));
406
srv = cf->spam_server;
409
if (cf->beanstalk_servers_num == MAX_BEANSTALK_SERVERS) {
410
yywarn ("yyparse: maximum number of beanstalk servers is reached %d", MAX_BEANSTALK_SERVERS);
413
srv = &cf->beanstalk_servers[cf->beanstalk_servers_num];
416
if (srv == NULL) return 0;
419
srv->port = htons (DEFAULT_BEANSTALK_PORT);
422
srv->port = htons ((uint16_t)strtoul (str, &err_str, 10));
423
if (*err_str != '\0') {
428
if (!inet_aton (cur_tok, &srv->addr)) {
429
/* Try to call gethostbyname */
430
he = gethostbyname (cur_tok);
435
srv->name = strdup (cur_tok);
436
memcpy((char *)&srv->addr, he->h_addr, sizeof(struct in_addr));
439
cf->beanstalk_servers_num ++;
444
srv->name = strdup (cur_tok);
446
cf->beanstalk_servers_num ++;
456
create_action (enum action_type type, const char *message)
459
size_t len = strlen (message);
461
if (message == NULL) return NULL;
463
new = (struct action *)malloc (sizeof (struct action));
465
if (new == NULL) return NULL;
469
if (*message == '"') {
473
if (message[len - 1] == '"') {
477
new->message = (char *)malloc (len + 1);
479
if (new->message == NULL) return NULL;
481
rmilter_strlcpy (new->message, message, len + 1);
487
create_cond (enum condition_type type, const char *arg1, const char *arg2)
489
struct condition *new;
491
const char *read_err;
493
new = (struct condition *)malloc (sizeof (struct condition));
494
bzero (new, sizeof (struct condition));
496
if (new == NULL) return NULL;
498
if (arg1 == NULL || *arg1 == '\0') {
499
new->args[0].empty = 1;
502
if (!copy_regexp (&new->args[0].src, arg1)) {
503
new->args[0].empty = 1;
506
new->args[0].re = pcre_compile (new->args[0].src, 0, &read_err, &offset, NULL);
507
if (new->args[0].re == NULL) {
508
new->args[0].empty = 1;
512
if (arg2 == NULL || *arg2 == '\0') {
513
new->args[1].empty = 1;
516
if (!copy_regexp (&new->args[1].src, arg2)) {
517
new->args[1].empty = 1;
520
new->args[1].re = pcre_compile (new->args[1].src, 0, &read_err, &offset, NULL);
521
if (new->args[1].re == NULL) {
522
new->args[1].empty = 1;
533
add_spf_domain (struct config_file *cfg, char *domain)
535
if (!domain) return 0;
537
if (cfg->spf_domains_num > MAX_SPF_DOMAINS) {
541
cfg->spf_domains[cfg->spf_domains_num] = domain;
542
cfg->spf_domains_num ++;
548
add_ip_radix (radix_tree_t *tree, char *ipnet)
550
uint32_t mask = 0xFFFFFFFF;
556
token = strsep (&ipnet, "/");
560
if (k > 32 || k < 0) {
561
yywarn ("add_ip_radix: invalid netmask value: %d", k);
568
if (inet_aton (token, &ina) == 0) {
569
yyerror ("add_ip_radix: invalid ip address: %s", token);
573
ip = ntohl((uint32_t)ina.s_addr);
574
k = radix32tree_insert (tree, ip, mask, 1);
576
yyerror ("add_ip_radix: cannot insert ip to tree: %s, mask %X", inet_ntoa (ina), mask);
580
yywarn ("add_ip_radix: ip %s, mask %X, value already exists", inet_ntoa (ina), mask);
587
add_hashed_header (const char *name, struct dkim_hash_entry **hash)
589
struct dkim_hash_entry *new;
591
new = malloc (sizeof (struct dkim_hash_entry));
592
new->name = strdup (name);
593
HASH_ADD_KEYPTR (hh, *hash, new->name, strlen (new->name), new);
597
init_defaults (struct config_file *cfg)
599
LIST_INIT (&cfg->rules);
600
cfg->wlist_rcpt_global = NULL;
601
cfg->wlist_rcpt_limit = NULL;
602
LIST_INIT (&cfg->bounce_addrs);
604
cfg->clamav_connect_timeout = DEFAULT_CLAMAV_CONNECT_TIMEOUT;
605
cfg->clamav_port_timeout = DEFAULT_CLAMAV_PORT_TIMEOUT;
606
cfg->clamav_results_timeout = DEFAULT_CLAMAV_RESULTS_TIMEOUT;
607
cfg->memcached_connect_timeout = DEFAULT_MEMCACHED_CONNECT_TIMEOUT;
608
cfg->beanstalk_connect_timeout = DEFAULT_MEMCACHED_CONNECT_TIMEOUT;
609
cfg->spamd_connect_timeout = DEFAULT_SPAMD_CONNECT_TIMEOUT;
610
cfg->spamd_results_timeout = DEFAULT_SPAMD_RESULTS_TIMEOUT;
612
cfg->clamav_error_time = DEFAULT_UPSTREAM_ERROR_TIME;
613
cfg->clamav_dead_time = DEFAULT_UPSTREAM_DEAD_TIME;
614
cfg->clamav_maxerrors = DEFAULT_UPSTREAM_MAXERRORS;
616
cfg->spamd_error_time = DEFAULT_UPSTREAM_ERROR_TIME;
617
cfg->spamd_dead_time = DEFAULT_UPSTREAM_DEAD_TIME;
618
cfg->spamd_maxerrors = DEFAULT_UPSTREAM_MAXERRORS;
619
cfg->spamd_reject_message = strdup (DEFAUL_SPAMD_REJECT);
620
cfg->rspamd_metric = strdup (DEFAULT_RSPAMD_METRIC);
621
cfg->spam_header = strdup (DEFAULT_SPAM_HEADER);
622
cfg->spam_header_value = strdup (DEFAULT_SPAM_HEADER_VALUE);
624
cfg->memcached_error_time = DEFAULT_UPSTREAM_ERROR_TIME;
625
cfg->memcached_dead_time = DEFAULT_UPSTREAM_DEAD_TIME;
626
cfg->memcached_maxerrors = DEFAULT_UPSTREAM_MAXERRORS;
627
cfg->memcached_protocol = UDP_TEXT;
629
cfg->beanstalk_error_time = DEFAULT_UPSTREAM_ERROR_TIME;
630
cfg->beanstalk_dead_time = DEFAULT_UPSTREAM_DEAD_TIME;
631
cfg->beanstalk_maxerrors = DEFAULT_UPSTREAM_MAXERRORS;
632
cfg->beanstalk_protocol = BEANSTALK_TCP_TEXT;
633
cfg->beanstalk_lifetime = DEFAULT_BEANSTALK_LIFETIME;
634
cfg->copy_server = NULL;
635
cfg->spam_server = NULL;
637
cfg->grey_whitelist_tree = radix_tree_create ();
638
cfg->limit_whitelist_tree = radix_tree_create ();
639
cfg->spamd_whitelist = radix_tree_create ();
640
cfg->greylisted_message = strdup (DEFAULT_GREYLISTED_MESSAGE);
642
cfg->spf_domains = (char **) calloc (MAX_SPF_DOMAINS, sizeof (char *));
644
cfg->beanstalk_copy_prob = 100.0;
646
cfg->spamd_soft_fail = 1;
647
cfg->spamd_greylist = 1;
649
cfg->dkim_auth_only = 1;
652
/* Init static defaults */
653
white_from_abuse.addr = "abuse";
654
white_from_abuse.len = sizeof ("abuse") - 1;
655
white_from_postmaster.addr = "postmaster";
656
white_from_postmaster.len = sizeof ("postmaster") - 1;
657
LIST_INSERT_HEAD (&cfg->whitelist_static, &white_from_abuse, next);
658
LIST_INSERT_HEAD (&cfg->whitelist_static, &white_from_postmaster, next);
662
cfg->dkim_lib = dkim_init (NULL, NULL);
663
/* Add recommended by rfc headers */
664
add_hashed_header ("from", &cfg->headers);
665
add_hashed_header ("sender", &cfg->headers);
666
add_hashed_header ("reply-to", &cfg->headers);
667
add_hashed_header ("subject", &cfg->headers);
668
add_hashed_header ("date", &cfg->headers);
669
add_hashed_header ("message-id", &cfg->headers);
670
add_hashed_header ("to", &cfg->headers);
671
add_hashed_header ("cc", &cfg->headers);
672
add_hashed_header ("date", &cfg->headers);
673
add_hashed_header ("mime-version", &cfg->headers);
674
add_hashed_header ("content-type", &cfg->headers);
675
add_hashed_header ("content-transfer-encoding", &cfg->headers);
676
add_hashed_header ("resent-to", &cfg->headers);
677
add_hashed_header ("resent-cc", &cfg->headers);
678
add_hashed_header ("resent-from", &cfg->headers);
679
add_hashed_header ("resent-sender", &cfg->headers);
680
add_hashed_header ("resent-message-id", &cfg->headers);
681
add_hashed_header ("in-reply-to", &cfg->headers);
682
add_hashed_header ("references", &cfg->headers);
683
add_hashed_header ("list-id", &cfg->headers);
684
add_hashed_header ("list-owner", &cfg->headers);
685
add_hashed_header ("list-unsubscribe", &cfg->headers);
686
add_hashed_header ("list-subscribe", &cfg->headers);
687
add_hashed_header ("list-post", &cfg->headers);
688
/* TODO: make it configurable */
693
free_config (struct config_file *cfg)
696
struct rule *cur, *tmp_rule;
697
struct condition *cond, *tmp_cond;
698
struct addr_list_entry *addr_cur, *addr_tmp;
699
struct whitelisted_rcpt_entry *rcpt_cur, *rcpt_tmp;
702
free (cfg->pid_file);
705
free (cfg->temp_dir);
707
if (cfg->sock_cred) {
708
free (cfg->sock_cred);
711
if (cfg->spf_domains) {
712
for (i = 0; i < MAX_SPF_DOMAINS; i++) {
713
free (cfg->spf_domains[i]);
715
free (cfg->spf_domains);
718
if (cfg->special_mid_re) {
719
pcre_free (cfg->special_mid_re);
722
for (i = 0; i < cfg->clamav_servers_num; i++) {
723
free (cfg->clamav_servers[i].name);
725
for (i = 0; i < cfg->spamd_servers_num; i++) {
726
free (cfg->spamd_servers[i].name);
728
/* Free rules list */
729
LIST_FOREACH_SAFE (cur, &cfg->rules, next, tmp_rule) {
730
LIST_FOREACH_SAFE (cond, cur->conditions, next, tmp_cond) {
731
if (!cond->args[0].empty) {
732
if (cond->args[0].re != NULL) {
733
pcre_free (cond->args[0].re);
735
if (cond->args[0].src != NULL) {
736
free (cond->args[0].src);
739
if (!cond->args[1].empty) {
740
if (cond->args[1].re != NULL) {
741
pcre_free (cond->args[1].re);
743
if (cond->args[1].src != NULL) {
744
free (cond->args[1].src);
747
LIST_REMOVE (cond, next);
750
if (cur->act->message) {
751
free (cur->act->message);
754
LIST_REMOVE (cur, next);
757
/* Free whitelists and bounce list*/
758
HASH_ITER (hh, cfg->wlist_rcpt_global, rcpt_cur, rcpt_tmp) {
759
HASH_DEL (cfg->wlist_rcpt_global, rcpt_cur);
760
free (rcpt_cur->rcpt);
763
HASH_ITER (hh, cfg->wlist_rcpt_limit, rcpt_cur, rcpt_tmp) {
764
HASH_DEL (cfg->wlist_rcpt_limit, rcpt_cur);
765
free (rcpt_cur->rcpt);
768
LIST_FOREACH_SAFE (addr_cur, &cfg->bounce_addrs, next, addr_tmp) {
769
if (addr_cur->addr) {
770
free (addr_cur->addr);
772
LIST_REMOVE (addr_cur, next);
776
radix_tree_free (cfg->grey_whitelist_tree);
777
free (cfg->grey_whitelist_tree);
779
radix_tree_free (cfg->spamd_whitelist);
780
free (cfg->spamd_whitelist);
782
radix_tree_free (cfg->limit_whitelist_tree);
783
free (cfg->limit_whitelist_tree);
785
if (cfg->spamd_reject_message) {
786
free (cfg->spamd_reject_message);
788
if (cfg->rspamd_metric) {
789
free (cfg->rspamd_metric);
791
if (cfg->spam_header) {
792
free (cfg->spam_header);
794
if (cfg->spam_header_value) {
795
free (cfg->spam_header_value);
797
if (cfg->id_prefix) {
798
free (cfg->id_prefix);
800
if (cfg->grey_prefix) {
801
free (cfg->grey_prefix);
803
if (cfg->white_prefix) {
804
free (cfg->white_prefix);
806
if (cfg->copy_server) {
807
free (cfg->copy_server);
809
if (cfg->spam_server) {
810
free (cfg->spam_server);
812
if (cfg->greylisted_message) {
813
free (cfg->greylisted_message);
816
if (cfg->awl_enable && cfg->awl_hash != NULL) {
817
free (cfg->awl_hash->pool);
818
free (cfg->awl_hash);
823
struct dkim_hash_entry *curh, *tmph;
824
struct dkim_domain_entry *curd, *tmpd;
827
dkim_close (cfg->dkim_lib);
829
HASH_ITER (hh, cfg->headers, curh, tmph) {
830
HASH_DEL (cfg->headers, curh); /* delete; users advances to next */
834
HASH_ITER (hh, cfg->dkim_domains, curd, tmpd) {
835
HASH_DEL (cfg->dkim_domains, curd); /* delete; users advances to next */
836
if (curd->key != MAP_FAILED && curd->key != NULL) {
837
munmap (curd->key, curd->keylen);
842
if (curd->selector) {
843
free (curd->selector);
846
free (curd->keyfile);
854
add_rcpt_whitelist (struct config_file *cfg, const char *rcpt, int is_global)
856
struct whitelisted_rcpt_entry *t;
857
t = (struct whitelisted_rcpt_entry *)malloc (sizeof (struct whitelisted_rcpt_entry));
859
t->type = WLIST_RCPT_DOMAIN;
862
else if (strchr (rcpt, '@') != NULL) {
863
t->type = WLIST_RCPT_USERDOMAIN;
866
t->type = WLIST_RCPT_USER;
868
t->rcpt = strdup (rcpt);
869
t->len = strlen (t->rcpt);
871
HASH_ADD_KEYPTR (hh, cfg->wlist_rcpt_global, t->rcpt, t->len, t);
874
HASH_ADD_KEYPTR (hh, cfg->wlist_rcpt_limit, t->rcpt, t->len, t);
879
is_whitelisted_rcpt (struct config_file *cfg, const char *str, int is_global)
882
struct whitelisted_rcpt_entry *entry, *list;
883
char rcptbuf[ADDRLEN + 1], *domain;
889
len = strcspn (str, ">");
890
rmilter_strlcpy (rcptbuf, str, MIN (len + 1, sizeof (rcptbuf)));
893
list = cfg->wlist_rcpt_global;
896
list = cfg->wlist_rcpt_limit;
898
/* Initially search for userdomain */
899
HASH_FIND_STR (list, rcptbuf, entry, strncasecmp);
900
if (entry != NULL && entry->type == WLIST_RCPT_USERDOMAIN) {
903
domain = strchr (rcptbuf, '@');
904
if (domain == NULL && entry != NULL && entry->type == WLIST_RCPT_USER) {
907
/* Search for user */
908
if (domain != NULL) {
911
HASH_FIND_STR (list, rcptbuf, entry, strncasecmp);
912
if (entry != NULL && entry->type == WLIST_RCPT_USER) {
915
if (domain != NULL) {
916
/* Search for domain */
918
HASH_FIND_STR (list, domain, entry, strncasecmp);
919
if (entry != NULL && entry->type == WLIST_RCPT_DOMAIN) {