* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for matching strings */
const check_string_block *cb = arg;
int search_type, partial, affixlen, starflags;
int expand_setup = cb->expand_setup;
-const uschar *affix;
+const uschar * affix, * opts;
uschar *s;
uschar *filename = NULL;
uschar *keyquery, *result, *semicolon;
void *handle;
-error = error; /* Keep clever compilers from complaining */
-
-if (valueptr != NULL) *valueptr = NULL; /* For non-lookup matches */
+if (valueptr) *valueptr = NULL;
/* For regular expressions, use cb->origsubject rather than cb->subject so that
it works if the pattern uses (?-i) to turn off case-independence, overriding
expand_nmax = -1;
if (expand_setup == 0)
{
- expand_nstring[0] = s;
+ expand_nstring[0] = s; /* $0 (might be) the matched subject in full */
expand_nlength[0] = Ustrlen(s);
}
else if (expand_setup > 0) expand_setup--;
if (pattern[0] == '^')
{
- const pcre *re = regex_must_compile(pattern, cb->caseless, FALSE);
- return ((expand_setup < 0)?
- pcre_exec(re, NULL, CCS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) >= 0
- :
- regex_match_and_setup(re, s, 0, expand_setup)
- )?
- OK : FAIL;
+ const pcre * re = regex_must_compile(pattern, cb->caseless, FALSE);
+ if (expand_setup < 0
+ ? pcre_exec(re, NULL, CCS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) < 0
+ : !regex_match_and_setup(re, s, 0, expand_setup)
+ )
+ return FAIL;
+ if (valueptr) *valueptr = pattern; /* "value" gets the RE */
+ return OK;
}
/* Tail match */
if (pattern[0] == '*')
{
- BOOL yield;
int slen = Ustrlen(s);
int patlen; /* Sun compiler doesn't like non-constant initializer */
patlen = Ustrlen(++pattern);
if (patlen > slen) return FAIL;
- yield = cb->caseless?
- (strncmpic(s + slen - patlen, pattern, patlen) == 0) :
- (Ustrncmp(s + slen - patlen, pattern, patlen) == 0);
- if (yield && expand_setup >= 0)
+ if (cb->caseless
+ ? strncmpic(s + slen - patlen, pattern, patlen) != 0
+ : Ustrncmp(s + slen - patlen, pattern, patlen) != 0)
+ return FAIL;
+ if (expand_setup >= 0)
{
- expand_nstring[++expand_setup] = s;
+ expand_nstring[++expand_setup] = s; /* write a $n, the matched subject variable-part */
expand_nlength[expand_setup] = slen - patlen;
- expand_nmax = expand_setup;
+ expand_nmax = expand_setup; /* commit also $0, the matched subject */
}
- return yield? OK : FAIL;
+ if (valueptr) *valueptr = pattern - 1; /* "value" gets the (original) pattern */
+ return OK;
}
/* Match a special item starting with @ if so enabled. On its own, "@" matches
if (Ustrcmp(pattern, "@[]") == 0)
{
- ip_address_item *ip;
int slen = Ustrlen(s);
- if (s[0] != '[' && s[slen-1] != ']') return FAIL;
- for (ip = host_find_interfaces(); ip != NULL; ip = ip->next)
+ if (s[0] != '[' && s[slen-1] != ']') return FAIL; /*XXX should this be || ? */
+ for (ip_address_item * ip = host_find_interfaces(); ip; ip = ip->next)
if (Ustrncmp(ip->address, s+1, slen - 2) == 0
&& ip->address[slen - 2] == 0)
+ {
+ if (expand_setup >= 0) expand_nmax = expand_setup; /* commit $0, the IP addr */
+ if (valueptr) *valueptr = pattern; /* "value" gets the pattern */
return OK;
+ }
return FAIL;
}
else goto NOT_AT_SPECIAL;
if (strncmpic(ss, US"/ignore=", 8) == 0) ignore_target_hosts = ss + 8;
- else if (*ss != 0) goto NOT_AT_SPECIAL;
+ else if (*ss) goto NOT_AT_SPECIAL;
h.next = NULL;
h.name = s;
return DEFER;
}
- if (rc == HOST_FOUND_LOCAL && !secy) return OK;
- if (prim) return FAIL;
- return removed? OK : FAIL;
+ if ((rc != HOST_FOUND_LOCAL || secy) && (prim || !removed))
+ return FAIL;
+
+ if (expand_setup >= 0) expand_nmax = expand_setup; /* commit $0, the matched subject */
+ if (valueptr) *valueptr = pattern; /* "value" gets the patterm */
+ return OK;
/*** The above line used to be the following line, but this is incorrect,
because host_find_bydns() may return HOST_NOT_FOUND if it removed some MX
if ((semicolon = Ustrchr(pattern, ';')) == NULL)
{
- BOOL yield = cb->caseless?
- (strcmpic(s, pattern) == 0) : (Ustrcmp(s, pattern) == 0);
- if (yield && expand_setup >= 0) expand_nmax = expand_setup;
- return yield? OK : FAIL;
+ if (cb->caseless ? strcmpic(s, pattern) != 0 : Ustrcmp(s, pattern) != 0)
+ return FAIL;
+ if (expand_setup >= 0) expand_nmax = expand_setup; /* Original code! $0 gets the matched subject */
+ if (valueptr) *valueptr = pattern; /* "value" gets the pattern */
+ return OK;
}
/* Otherwise we have a lookup item. The lookup type, including partial, etc. is
*semicolon = 0;
search_type = search_findtype_partial(pattern, &partial, &affix, &affixlen,
- &starflags);
+ &starflags, &opts);
*semicolon = ';';
if (search_type < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
search_error_message);
/* Set the parameters for the three different kinds of lookup. */
-keyquery = semicolon + 1;
-while (isspace(*keyquery)) keyquery++;
-
-if (mac_islookup(search_type, lookup_absfilequery))
- {
- filename = keyquery;
- while (*keyquery != 0 && !isspace(*keyquery)) keyquery++;
- filename = string_copyn(filename, keyquery - filename);
- while (isspace(*keyquery)) keyquery++;
- }
-
-else if (!mac_islookup(search_type, lookup_querystyle))
- {
- filename = keyquery;
- keyquery = s;
- }
+keyquery = search_args(search_type, s, semicolon+1, &filename, opts);
/* Now do the actual lookup; throw away the data returned unless it was asked
for; partial matching is all handled inside search_find(). Note that there is
no search_close() because of the caching arrangements. */
-handle = search_open(filename, search_type, 0, NULL, NULL);
-if (handle == NULL) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
- search_error_message);
+if (!(handle = search_open(filename, search_type, 0, NULL, NULL)))
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", search_error_message);
result = search_find(handle, filename, keyquery, partial, affix, affixlen,
- starflags, &expand_setup);
+ starflags, &expand_setup, opts);
-if (result == NULL) return search_find_defer? DEFER : FAIL;
-if (valueptr != NULL) *valueptr = result;
+if (!result) return f.search_find_defer ? DEFER : FAIL;
+if (valueptr) *valueptr = result;
expand_nmax = expand_setup;
return OK;
{
check_string_block cb;
cb.origsubject = s;
-cb.subject = caseless? string_copylc(s) : string_copy(s);
+cb.subject = caseless ? string_copylc(s) : string_copy(s);
cb.expand_setup = expand_setup;
cb.use_partial = use_partial;
cb.caseless = caseless;
case MCL_STRING:
case MCL_DOMAIN:
case MCL_LOCALPART:
- return ((check_string_block *)arg)->subject;
+ return ((check_string_block *)arg)->subject;
case MCL_HOST:
- return ((check_host_block *)arg)->host_address;
+ return ((check_host_block *)arg)->host_address;
case MCL_ADDRESS:
- return ((check_address_block *)arg)->address;
+ return ((check_address_block *)arg)->address;
}
return US""; /* In practice, should never happen */
}
const uschar *list;
uschar *sss;
uschar *ot = NULL;
-uschar buffer[1024];
/* Save time by not scanning for the option name when we don't need it. */
if (!list)
{
- if (expand_string_forcedfail)
+ if (f.expand_string_forcedfail)
{
HDEBUG(D_lists) debug_printf("expansion of \"%s\" forced failure: "
"assume not in this list\n", *listptr);
/* For an unnamed list, use the expanded version in comments */
-HDEBUG(D_any) if (ot == NULL) ot = string_sprintf("%s in \"%s\"?", name, list);
+HDEBUG(D_any) if (!ot) ot = string_sprintf("%s in \"%s\"?", name, list);
/* Now scan the list and process each item in turn, until one of them matches,
or we hit an error. */
-while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
+while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
{
uschar * ss = sss;
if (Ustrcmp(ss, "+caseful") == 0)
{
check_string_block *cb = (check_string_block *)arg;
- Ustrcpy(cb->subject, cb->origsubject);
+ Ustrcpy(US cb->subject, cb->origsubject);
cb->caseless = FALSE;
continue;
}
so we use the permanent store pool */
store_pool = POOL_PERM;
- p = store_get(sizeof(namedlist_cacheblock));
+ p = store_get(sizeof(namedlist_cacheblock), FALSE);
p->key = string_copy(get_check_key(arg, type));
nb->cache_data = p;
if (*valueptr)
DEBUG(D_lists) debug_printf("data from lookup saved for "
- "cache for %s: %s\n", ss, *valueptr);
+ "cache for %s: key '%s' value '%s'\n", ss, p->key, *valueptr);
}
}
}
else
{
DEBUG(D_lists) debug_printf("cached %s match for %s\n",
- ((bits & (-bits)) == bits)? "yes" : "no", ss);
+ (bits & (-bits)) == bits ? "yes" : "no", ss);
cached = US" - cached";
if (valueptr)
{
const uschar *key = get_check_key(arg, type);
- namedlist_cacheblock *p;
- for (p = nb->cache_data; p; p = p->next)
+
+ for (namedlist_cacheblock * p = nb->cache_data; p; p = p->next)
if (Ustrcmp(key, p->key) == 0)
{
*valueptr = p->data;
if (listname[0] == 0)
listname = string_sprintf("\"%s\"", *listptr);
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
- string_open_failed(errno, "%s when checking %s", sss, listname));
+ string_open_failed("%s when checking %s", sss, listname));
}
/* Trailing comments are introduced by #, but in an address list or local
unsigned int *local_cache_bits = cache_bits;
check_string_block cb;
cb.origsubject = s;
-cb.subject = caseless? string_copylc(s) : string_copy(s);
-cb.expand_setup = (sep > UCHAR_MAX)? 0 : -1;
+cb.subject = caseless ? string_copylc(s) : string_copy(s);
+cb.at_is_special = FALSE;
+switch (type & ~MCL_NOEXPAND)
+ {
+ case MCL_DOMAIN: cb.at_is_special = TRUE; /*FALLTHROUGH*/
+ case MCL_LOCALPART: cb.expand_setup = 0; break;
+ default: cb.expand_setup = sep > UCHAR_MAX ? 0 : -1; break;
+ }
cb.use_partial = TRUE;
cb.caseless = caseless;
-cb.at_is_special = (type == MCL_DOMAIN || type == MCL_DOMAIN + MCL_NOEXPAND);
-if (valueptr != NULL) *valueptr = NULL;
+if (valueptr) *valueptr = NULL;
return match_check_list(listptr, sep, anchorptr, &local_cache_bits,
check_string, &cb, type, s, valueptr);
}
const uschar *s;
uschar *pdomain, *sdomain;
-error = error; /* Keep clever compilers from complaining */
-
DEBUG(D_lists) debug_printf("address match test: subject=%s pattern=%s\n",
subject, pattern);
if (pattern[0] == '@' && pattern[1] == '@')
{
int watchdog = 50;
- const uschar *key;
uschar *list, *ss;
uschar buffer[1024];
/* Loop for handling chains. The last item in any list may be of the form
">name" in order to chain on to another list. */
- for (key = sdomain + 1; key != NULL && watchdog-- > 0; )
+ for (const uschar * key = sdomain + 1; key && watchdog-- > 0; )
{
int sep = 0;
{
int cllen = pllen - 1;
if (sllen < cllen) return FAIL;
- if (cb->caseless)
- {
- if (strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0)
- return FAIL;
- }
- else
- {
- if (Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0)
+ if (cb->caseless
+ ? strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0
+ : Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0)
return FAIL;
- }
if (cb->expand_setup > 0)
{
expand_nstring[cb->expand_setup] = subject;
else
{
if (sllen != pllen) return FAIL;
- if (cb->caseless)
- {
- if (strncmpic(subject, pattern, sllen) != 0) return FAIL;
- }
- else
- {
- if (Ustrncmp(subject, pattern, sllen) != 0) return FAIL;
- }
+ if (cb->caseless
+ ? strncmpic(subject, pattern, sllen) != 0
+ : Ustrncmp(subject, pattern, sllen) != 0) return FAIL;
}
}
original code read as follows:
return match_check_string(sdomain + 1,
- (pdomain == NULL)? pattern : pdomain + 1,
+ pdomain ? pdomain + 1 : pattern,
cb->expand_setup + expand_inc, TRUE, cb->caseless, TRUE, NULL);
This supported only literal domains and *.x.y patterns. In order to allow for
was changed to use the list scanning function. */
csb.origsubject = sdomain + 1;
-csb.subject = (cb->caseless)? string_copylc(sdomain+1) : string_copy(sdomain+1);
+csb.subject = cb->caseless ? string_copylc(sdomain+1) : string_copy(sdomain+1);
csb.expand_setup = cb->expand_setup + expand_inc;
csb.use_partial = TRUE;
csb.caseless = cb->caseless;
csb.at_is_special = TRUE;
-listptr = (pdomain == NULL)? pattern : pdomain + 1;
-if (valueptr != NULL) *valueptr = NULL;
+listptr = pdomain ? pdomain + 1 : pattern;
+if (valueptr) *valueptr = NULL;
return match_check_list(
&listptr, /* list of one item */
const uschar **listptr, unsigned int *cache_bits, int expand_setup, int sep,
const uschar **valueptr)
{
-uschar *p;
check_address_block ab;
unsigned int *local_cache_bits = cache_bits;
+int len;
/* RFC 2505 recommends that for spam checking, local parts should be caselessly
compared. Therefore, Exim now forces the entire address into lower case here,
the list can be used to restore a caseful copy of the local part from the
original address. */
-sprintf(CS big_buffer, "%.*s", big_buffer_size - 1, address);
-for (p = big_buffer + Ustrlen(big_buffer) - 1; p >= big_buffer; p--)
+if ((len = Ustrlen(address)) > 255) len = 255;
+ab.address = string_copyn(address, len);
+
+for (uschar * p = ab.address + len - 1; p >= ab.address; p--)
{
if (!caseless && *p == '@') break;
*p = tolower(*p);
/* Set up the data to be passed ultimately to check_address. */
ab.origaddress = address;
-ab.address = big_buffer;
+/* ab.address is above */
ab.expand_setup = expand_setup;
ab.caseless = caseless;