mean slightly faster delivery, and also confirmation of reception
to senders.
+JH/02 Move from using the pcre library to pcre2. The former is no longer
+ being developed or supported (by the original developer).
+
Exim version 4.95
-----------------
# PCRE_LIBS contains the library to be linked for PCRE
-PCRE_LIBS=-lpcre
+PCRE_LIBS=-lpcre2-8
# LIBS and EXTRALIBS contain library settings that are used on linking
int queue_update = 60;
int queue_width = 600;
-pcre *yyyymmdd_regex;
+pcre2_code *yyyymmdd_regex;
uschar *size_stripchart = NULL;
uschar *size_stripchart_name = NULL;
int start_small = FALSE;
int stripchart_height = 90;
int stripchart_number = 1;
-pcre **stripchart_regex;
+pcre2_code **stripchart_regex;
uschar **stripchart_title;
int *stripchart_total;
int stripchart_update = 60;
/* Regular expression include */
-#include <pcre.h>
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
/* Includes from the main source of Exim. One of these days I should tidy up
this interface so that this kind of kludge isn't needed. */
extern int queue_update; /* update interval */
extern int queue_width; /* width of queue window */
-extern pcre *yyyymmdd_regex; /* for matching yyyy-mm-dd */
+extern pcre2_code *yyyymmdd_regex; /* for matching yyyy-mm-dd */
extern uschar *size_stripchart; /* path for size monitoring */
extern uschar *size_stripchart_name; /* name for size stripchart */
extern int start_small; /* True to start with small window */
extern int stripchart_height; /* height of stripcharts */
extern int stripchart_number; /* number of stripcharts */
-extern pcre **stripchart_regex; /* vector of regexps */
+extern pcre2_code **stripchart_regex; /* vector of regexps */
extern uschar **stripchart_title; /* vector of titles */
extern int *stripchart_total; /* vector of accumulating values */
extern int stripchart_update; /* update interval */
work. */
for (i = 0; i <= 1; i++)
-
{
int first = 1;
int count = 0;
buffer[p-pp] = 0;
if (first)
{
- int offset;
- const uschar *error;
- if (!(stripchart_regex[indx] = pcre_compile(CS buffer, PCRE_COPT,
- CCSS &error, &offset, NULL)))
+ size_t offset;
+ int err;
+
+ if (!(stripchart_regex[indx] =
+ pcre2_compile((PCRE2_SPTR)buffer,
+ PCRE2_ZERO_TERMINATED, PCRE_COPT,
+ &err, &offset, NULL)))
{
- printf("regular expression error: %s at offset %d "
- "while compiling %s\n", error, offset, buffer);
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
+ printf("regular expression error: %s at offset %l "
+ "while compiling %s\n", errbuf, (long)offset, buffer);
exit(99);
}
}
if (i == 0)
{
stripchart_number += count;
- stripchart_regex = (pcre **)store_malloc(stripchart_number * sizeof(pcre *));
+ stripchart_regex = (pcre2_code **)store_malloc(stripchart_number * sizeof(pcre2_code *));
stripchart_title = (uschar **)store_malloc(stripchart_number * sizeof(uschar *));
}
}
void init(int argc, uschar **argv)
{
int x;
-int erroroffset;
+size_t erroroffset;
uschar *s;
const uschar *error;
/* Compile the regex for matching yyyy-mm-dd at the start of a string. */
-yyyymmdd_regex = pcre_compile("^\\d{4}-\\d\\d-\\d\\d\\s", PCRE_COPT,
- CCSS &error, &erroroffset, NULL);
+yyyymmdd_regex = pcre2_compile((PCRE2_SPTR)"^\\d{4}-\\d\\d-\\d\\d\\s",
+ PCRE2_ZERO_TERMINATED, PCRE_COPT, &x, &erroroffset, NULL);
}
/* End of em_init.c */
uschar *p = buffer;
rmark reset_point;
int length = Ustrlen(buffer);
- int i;
+ pcre2_match_data * md = pcre2_match_data_create(1, NULL);
/* Skip totally blank lines (paranoia: there shouldn't be any) */
stripchart is the queue length, which is handled elsewhere, and the
1st may the a size monitor. */
- for (i = stripchart_varstart; i < stripchart_number; i++)
- {
- if (pcre_exec(stripchart_regex[i], NULL, CS buffer, length, 0, PCRE_EOPT,
- NULL, 0) >= 0)
+ for (int i = stripchart_varstart; i < stripchart_number; i++)
+ if (pcre2_match(stripchart_regex[i], (PCRE2_SPTR)buffer, length,
+ 0, PCRE_EOPT, md, NULL) >= 0)
stripchart_total[i]++;
- }
/* Munge the log entry and display shortened form on one line.
We omit the date and show only the time. Remove any time zone offset.
Take note of the presence of [pid]. */
- if (pcre_exec(yyyymmdd_regex,NULL,CS buffer,length,0,PCRE_EOPT,NULL,0) >= 0)
+ if (pcre2_match(yyyymmdd_regex, (PCRE2_SPTR) buffer, length, 0, PCRE_EOPT,
+ md, NULL) >= 0)
{
int pidlength = 0;
- if ((buffer[20] == '+' || buffer[20] == '-') &&
- isdigit(buffer[21]) && buffer[25] == ' ')
+ if ( (buffer[20] == '+' || buffer[20] == '-')
+ && isdigit(buffer[21]) && buffer[25] == ' ')
memmove(buffer + 20, buffer + 26, Ustrlen(buffer + 26) + 1);
if (buffer[20] == '[')
- {
- while (Ustrchr("[]0123456789", buffer[20+pidlength++]) != NULL);
- }
+ while (Ustrchr("[]0123456789", buffer[20+pidlength++]) != NULL)
+ ;
id = string_copyn(buffer + 20 + pidlength, MESSAGE_ID_LENGTH);
show_log("%s", buffer+11);
}
id = US"";
show_log("%s", buffer);
}
+ pcre2_match_data_free(md);
/* Deal with frozen and unfrozen messages */
egrep "^[$st]*(AUTH|LOOKUP)_[A-Z0-9_]*[$st]*=[$st]*" $mft | \
sed "s/[$st]*=/='/" | \
sed "s/\$/'/" > $mftt
-egrep "^[$st]*((USE_(OPENSSL|GNUTLS)_PC)|SUPPORT_TLS|USE_GNUTLS|PCRE_CONFIG|AVOID_GNUTLS_PKCS11)[$st]*=[$st]*" $mft | \
+egrep "^[$st]*((USE_(OPENSSL|GNUTLS)_PC)|SUPPORT_TLS|USE_GNUTLS|PCRE2?_CONFIG|AVOID_GNUTLS_PKCS11)[$st]*=[$st]*" $mft | \
sed "s/[$st]*=/='/" | \
sed "s/\$/'/" >> $mftt
if test -s $mftt
PCRE_CONFIG)
case $PCRE_CONFIG in
yes|YES|y|Y)
- cflags=`pcre-config --cflags`
+ echo >&2 "pcre is no longer supported; migrate to pcre2"
+ exit 1
+
+# cflags=`pcre-config --cflags`
+# if [ $? -ne 0 ]; then
+# echo >&2 "*** Missing pcre-config for regular expression support"
+# exit 1
+# fi
+# libs=`pcre-config --libs`
+# if [ ".$cflags" != "." ]; then
+# echo "INCLUDE += $cflags"
+# fi
+# echo "PCRE_LIBS=$libs"
+ ;;
+ esac
+ ;;
+
+ PCRE2_CONFIG)
+ case $PCRE2_CONFIG in
+ yes|YES|y|Y)
+ cflags=`pcre2-config --cflags`
if [ $? -ne 0 ]; then
- echo >&2 "*** Missing pcre-config for regular expression support"
+ echo >&2 "*** Missing pcre2-config for regular expression support"
exit 1
fi
- libs=`pcre-config --libs`
+ libs=`pcre2-config --libs8`
if [ ".$cflags" != "." ]; then
echo "INCLUDE += $cflags"
fi
#------------------------------------------------------------------------------
-# The PCRE library is required for Exim. There is no longer an embedded
+# The PCRE2 library is required for Exim. There is no longer an embedded
# version of the PCRE library included with the source code, instead you
-# must use a system library or build your own copy of PCRE.
+# must use a system library or build your own copy of PCRE2.
# In either case you must specify the library link info here. If the
-# PCRE header files are not in the standard search path you must also
+# PCRE2 header files are not in the standard search path you must also
# modify the INCLUDE path (above)
#
# Use PCRE_CONFIG to query the pcre-config command (first found in $PATH)
# to find the include files and libraries, else use PCRE_LIBS and set INCLUDE
# too if needed.
-PCRE_CONFIG=yes
-# PCRE_LIBS=-lpcre
+PCRE2_CONFIG=yes
+# PCRE_LIBS=-lpcre2
#------------------------------------------------------------------------------
if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT)
{
- int ovector[3*(EXPAND_MAXN+1)];
-
dns_pattern_init();
- if (pcre_exec(regex_check_dns_names, NULL, CCS name, Ustrlen(name),
- 0, PCRE_EOPT, ovector, nelem(ovector)) < 0)
+ if (!regex_match(regex_check_dns_names, name, -1, NULL))
{
DEBUG(D_dns)
debug_printf("DNS name syntax check failed: %s (%s)\n", name,
}
else
{
- const pcre *regex_islookupmod = regex_must_compile(
+ const pcre2_code *regex_islookupmod = regex_must_compile(
US"\\." DYNLIB_FN_EXT "$", FALSE, TRUE);
DEBUG(D_lookup) debug_printf("Loading lookup modules from %s\n", LOOKUP_MODULE_DIR);
while ((ent = readdir(dd)))
{
- char *name = ent->d_name;
+ char * name = ent->d_name;
int len = (int)strlen(name);
- if (pcre_exec(regex_islookupmod, NULL, name, len, 0, PCRE_EOPT, NULL, 0) >= 0)
+ if (regex_match(regex_islookupmod, US name, len, NUL))
{
int pathnamelen = len + (int)strlen(LOOKUP_MODULE_DIR) + 2;
void *dl;
regular expression for a long time; the other for short-term use. */
static void *
-function_store_get(size_t size)
+function_store_get(PCRE2_SIZE size, void * tag)
{
/* For now, regard all RE results as potentially tainted. We might need
more intelligence on this point. */
}
static void
-function_dummy_free(void * block) {}
+function_dummy_free(void * block, void * tag) {}
static void *
-function_store_malloc(size_t size)
+function_store_malloc(PCRE2_SIZE size, void * tag)
{
return store_malloc((int)size);
}
static void
-function_store_free(void * block)
+function_store_free(void * block, void * tag)
{
store_free(block);
}
Returns: pointer to the compiled pattern
*/
-const pcre *
-regex_must_compile(const uschar *pattern, BOOL caseless, BOOL use_malloc)
+const pcre2_code *
+regex_must_compile(const uschar * pattern, BOOL caseless, BOOL use_malloc)
{
-int offset;
-int options = PCRE_COPT;
-const pcre *yield;
-const uschar *error;
+size_t offset;
+int options = caseless ? PCRE_COPT|PCRE2_CASELESS : PCRE_COPT;
+const pcre2_code * yield;
+int err;
+pcre2_general_context * gctx;
+pcre2_compile_context * cctx;
+
if (use_malloc)
{
- pcre_malloc = function_store_malloc;
- pcre_free = function_store_free;
+ gctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL);
+ cctx = pcre2_compile_context_create(gctx);
}
-if (caseless) options |= PCRE_CASELESS;
-yield = pcre_compile(CCS pattern, options, CCSS &error, &offset, NULL);
-pcre_malloc = function_store_get;
-pcre_free = function_dummy_free;
-if (yield == NULL)
+else
+ cctx = pcre_cmp_ctx;
+
+if (!(yield = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, options,
+ &err, &offset, cctx)))
+ {
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: "
- "%s at offset %d while compiling %s", error, offset, pattern);
+ "%s at offset %d while compiling %s", errbuf, (long)offset, pattern);
+ }
+
+if (use_malloc)
+ {
+ pcre2_compile_context_free(cctx);
+ pcre2_general_context_free(gctx);
+ }
return yield;
}
+static void
+pcre_init(void)
+{
+pcre_gen_ctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL);
+pcre_cmp_ctx = pcre2_compile_context_create(pcre_gen_ctx);
+pcre_mtc_ctx = pcre2_match_context_create(pcre_gen_ctx);
+}
+
+
/*************************************************
*************************************************/
/* This function runs a regular expression match, and sets up the pointers to
-the matched substrings.
+the matched substrings. The matched strings are copied.
+
+We might consider tracing the uses of expand_nstring to see if consitification
+is viable, and save the copy cost by just using the pointers into the subject string.
+Pre-pcre2 we did that without noticing, so it might just work - or might have been
+a bug. It was certainly a risk in the implemenation.
Arguments:
re the compiled expression
if >= 0 setup from setup+1 onwards,
excluding the full matched string
-Returns: TRUE or FALSE
+Returns: TRUE if matched, or FALSE
*/
BOOL
-regex_match_and_setup(const pcre *re, const uschar *subject, int options, int setup)
+regex_match_and_setup(const pcre2_code * re, const uschar * subject, int options, int setup)
{
-int ovector[3*(EXPAND_MAXN+1)];
-uschar * s = string_copy(subject); /* de-constifying */
-int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0,
- PCRE_EOPT | options, ovector, nelem(ovector));
-BOOL yield = n >= 0;
-if (n == 0) n = EXPAND_MAXN + 1;
-if (yield)
+pcre2_match_data * md = pcre2_match_data_create_from_pattern(re, pcre_gen_ctx);
+int res = pcre2_match(re, (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED, 0,
+ PCRE_EOPT | options, md, pcre_mtc_ctx);
+BOOL yield;
+
+if ((yield = (res >= 0)))
{
+ res = pcre2_get_ovector_count(md);
expand_nmax = setup < 0 ? 0 : setup + 1;
- for (int nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2)
+ for (int matchnum = setup < 0 ? 0 : 1; matchnum < res; matchnum++)
{
- expand_nstring[expand_nmax] = s + ovector[nn];
- expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
+ PCRE2_SIZE len;
+ pcre2_substring_get_bynumber(md, matchnum,
+ (PCRE2_UCHAR **)&expand_nstring[expand_nmax], &len);
+ expand_nlength[expand_nmax++] = (int)len;
}
expand_nmax--;
}
+else if (res != PCRE2_ERROR_NOMATCH) DEBUG(D_any)
+ {
+ uschar errbuf[128];
+ pcre2_get_error_message(res, errbuf, sizeof(errbuf));
+ debug_printf_indent("pcre2: %s\n", errbuf);
+ }
+pcre2_match_data_free(md);
return yield;
}
+/* Check just for match with regex. Uses the common memory-handling.
+
+Arguments:
+ re compiled regex
+ subject string to be checked
+ slen length of subject; -1 for nul-terminated
+ rptr pointer for matched string, copied, or NULL
+
+Return: TRUE for a match.
+*/
+
+BOOL
+regex_match(const pcre2_code * re, const uschar * subject, int slen, uschar ** rptr)
+{
+pcre2_match_data * md = pcre2_match_data_create(1, pcre_gen_ctx);
+int rc = pcre2_match(re, (PCRE2_SPTR)subject,
+ slen >= 0 ? slen : PCRE2_ZERO_TERMINATED,
+ 0, PCRE_EOPT, md, pcre_mtc_ctx);
+PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
+if (rc < 0)
+ return FALSE;
+if (rptr)
+ *rptr = string_copyn(subject + ovec[0], ovec[1] - ovec[0]);
+return TRUE;
+}
+
/*************************************************
#endif
#define QUOTE(X) #X
#define EXPAND_AND_QUOTE(X) QUOTE(X)
- fprintf(fp, "Library version: PCRE: Compile: %d.%d%s\n"
+ {
+ uschar buf[24];
+ pcre2_config(PCRE2_CONFIG_VERSION, buf);
+ fprintf(fp, "Library version: PCRE2: Compile: %d.%d%s\n"
" Runtime: %s\n",
- PCRE_MAJOR, PCRE_MINOR,
- EXPAND_AND_QUOTE(PCRE_PRERELEASE) "",
- pcre_version());
+ PCRE2_MAJOR, PCRE2_MINOR,
+ EXPAND_AND_QUOTE(PCRE2_PRERELEASE) "",
+ buf);
+ }
#undef QUOTE
#undef EXPAND_AND_QUOTE
continue;
if ((len = m->replen) == 0)
continue;
- n = pcre_exec(regex_whitelisted_macro, NULL, CS m->replacement, len,
- 0, PCRE_EOPT, NULL, 0);
- if (n < 0)
- {
- if (n != PCRE_ERROR_NOMATCH)
- debug_printf("macros_trusted checking %s returned %d\n", m->name, n);
+ if (!regex_match(regex_whitelisted_macro, m->replacement, len, NULL))
return FALSE;
- }
}
DEBUG(D_any) debug_printf("macros_trusted overridden to true by whitelisting\n");
return TRUE;
#endif
store_init(); /* Initialise the memory allocation susbsystem */
+pcre_init(); /* Set up memory handling for pcre */
/* If the Exim user and/or group and/or the configuration file owner/group were
defined by ref:name at build time, we must now find the actual uid/gid values.
if (fstat(fileno(stderr), &statbuf) >= 0) log_stderr = stderr;
-/* Arrange for the PCRE regex library to use our store functions. Note that
-the normal calls are actually macros that add additional arguments for
-debugging purposes so we have to assign specially constructed functions here.
-The default is to use store in the stacking pool, but this is overridden in the
-regex_must_compile() function. */
-
-pcre_malloc = function_store_get;
-pcre_free = function_dummy_free;
-
/* Ensure there is a big buffer for temporary use in several places. It is put
in malloc store so that it can be freed for enlargement if necessary. */
if (gecos_pattern && gecos_name)
{
- const pcre *re;
+ const pcre2_code *re;
re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */
if (regex_match_and_setup(re, name, 0, -1))
/* The header from the PCRE regex package */
-#include <pcre.h>
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
/* Exim includes are in several files. Note that local_scan.h #includes
config.h, mytypes.h, and store.h, so we don't need to mention them explicitly.
BOOL *subcondptr;
BOOL sub2_honour_dollar = TRUE;
BOOL is_forany, is_json, is_jsons;
-int rc, cond_type, roffset;
+int rc, cond_type;
int_eximarith_t num[2];
struct stat statbuf;
uschar * opname;
uschar name[256];
const uschar *sub[10];
-const pcre *re;
-const uschar *rerror;
-
for (;;)
if (Uskip_whitespace(&s) == '!') { testfor = !testfor; s++; } else break;
break;
case ECOND_MATCH: /* Regular expression match */
- if (!(re = pcre_compile(CS sub[1], PCRE_COPT, CCSS &rerror,
- &roffset, NULL)))
{
- expand_string_message = string_sprintf("regular expression error in "
- "\"%s\": %s at offset %d", sub[1], rerror, roffset);
- return NULL;
+ const pcre2_code * re;
+ PCRE2_SIZE offset;
+ int err;
+
+ if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED,
+ PCRE_COPT, &err, &offset, pcre_cmp_ctx)))
+ {
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
+ expand_string_message = string_sprintf("regular expression error in "
+ "\"%s\": %s at offset %d", sub[1], errbuf, offset);
+ return NULL;
+ }
+
+ tempcond = regex_match_and_setup(re, sub[0], 0, -1);
+ break;
}
- tempcond = regex_match_and_setup(re, sub[0], 0, -1);
- break;
case ECOND_MATCH_ADDRESS: /* Match in an address list */
rc = match_address_list(sub[0], TRUE, FALSE, &(sub[1]), NULL, -1, 0, NULL);
/* ${if inbound_srs {local_part}{secret} {yes}{no}} */
{
uschar * sub[2];
- const pcre * re;
- int ovec[3*(4+1)];
- int n, quoting = 0;
+ const pcre2_code * re;
+ pcre2_match_data * md;
+ PCRE2_SIZE * ovec;
+ int quoting = 0;
uschar cksum[4];
BOOL boolvalue = FALSE;
re = regex_must_compile(US"^(?i)SRS0=([^=]+)=([A-Z2-7]+)=([^=]*)=(.*)$",
TRUE, FALSE);
- if (pcre_exec(re, NULL, CS sub[0], Ustrlen(sub[0]), 0, PCRE_EOPT,
- ovec, nelem(ovec)) < 0)
+ md = pcre2_match_data_create(4+1, pcre_gen_ctx);
+ if (pcre2_match(re, sub[0], PCRE2_ZERO_TERMINATED, 0, PCRE_EOPT,
+ md, pcre_mtc_ctx) < 0)
{
DEBUG(D_expand) debug_printf("no match for SRS'd local-part pattern\n");
goto srs_result;
}
+ ovec = pcre2_get_ovector_pointer(md);
if (sub[0][0] == '"')
quoting = 1;
struct timeval now;
uschar * ss = sub[0] + ovec[4]; /* substring 2, the timestamp */
long d;
+ int n;
gettimeofday(&now, NULL);
now.tv_sec /= 86400; /* days since epoch */
{
uschar *sub_arg[3];
gstring * g;
- const pcre *re;
+ const pcre2_code *re;
uschar *p;
/* TF: Ugliness: We want to expand parameter 1 first, then set
case EITEM_SG:
{
- const pcre *re;
+ const pcre2_code * re;
int moffset, moffsetextra, slen;
- int roffset;
- int emptyopt;
- const uschar *rerror;
+ PCRE2_SIZE roffset;
+ pcre2_match_data * md;
+ int err, emptyopt;
uschar *subject;
uschar *sub[3];
int save_expand_nmax =
/* Compile the regular expression */
- if (!(re = pcre_compile(CS sub[1], PCRE_COPT, CCSS &rerror,
- &roffset, NULL)))
+ if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED,
+ PCRE_COPT, &err, &roffset, pcre_cmp_ctx)))
{
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
expand_string_message = string_sprintf("regular expression error in "
- "\"%s\": %s at offset %d", sub[1], rerror, roffset);
+ "\"%s\": %s at offset %l", sub[1], errbuf, (long)roffset);
goto EXPAND_FAILED;
}
+ md = pcre2_match_data_create(EXPAND_MAXN + 1, pcre_gen_ctx);
/* Now run a loop to do the substitutions as often as necessary. It ends
when there are no more matches. Take care over matches of the null string;
for (;;)
{
- int ovector[3*(EXPAND_MAXN+1)];
- int n = pcre_exec(re, NULL, CS subject, slen, moffset + moffsetextra,
- PCRE_EOPT | emptyopt, ovector, nelem(ovector));
+ PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
+ int n = pcre2_match(re, (PCRE2_SPTR)subject, slen, moffset + moffsetextra,
+ PCRE_EOPT | emptyopt, md, pcre_mtc_ctx);
uschar *insert;
/* No match - if we previously set PCRE_NOTEMPTY after a null match, this
expand_nmax = 0;
for (int nn = 0; nn < n*2; nn += 2)
{
- expand_nstring[expand_nmax] = subject + ovector[nn];
- expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
+ expand_nstring[expand_nmax] = subject + ovec[nn];
+ expand_nlength[expand_nmax++] = ovec[nn+1] - ovec[nn];
}
expand_nmax--;
/* Copy the characters before the match, plus the expanded insertion. */
- yield = string_catn(yield, subject + moffset, ovector[0] - moffset);
+ yield = string_catn(yield, subject + moffset, ovec[0] - moffset);
if (!(insert = expand_string(sub[2])))
goto EXPAND_FAILED;
yield = string_cat(yield, insert);
- moffset = ovector[1];
+ moffset = ovec[1];
moffsetextra = 0;
emptyopt = 0;
string at the same point. If this fails (picked up above) we advance to
the next character. */
- if (ovector[0] == ovector[1])
+ if (ovec[0] == ovec[1])
{
- if (ovector[0] == slen) break;
- emptyopt = PCRE_NOTEMPTY | PCRE_ANCHORED;
+ if (ovec[0] == slen) break;
+ emptyopt = PCRE2_NOTEMPTY | PCRE2_ANCHORED;
}
}
BOOL
-regex_match_and_setup(const pcre *re, uschar *subject, int options, int setup)
+regex_match_and_setup(const pcre2_code *re, uschar *subject, int options, int setup)
{
-int ovector[3*(EXPAND_MAXN+1)];
+int ovec[3*(EXPAND_MAXN+1)];
int n = pcre_exec(re, NULL, subject, Ustrlen(subject), 0, PCRE_EOPT|options,
- ovector, nelem(ovector));
+ ovec, nelem(ovec));
BOOL yield = n >= 0;
if (n == 0) n = EXPAND_MAXN + 1;
if (yield)
expand_nmax = setup < 0 ? 0 : setup + 1;
for (int nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2)
{
- expand_nstring[expand_nmax] = subject + ovector[nn];
- expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
+ expand_nstring[expand_nmax] = subject + ovec[nn];
+ expand_nlength[expand_nmax++] = ovec[nn+1] - ovec[nn];
}
expand_nmax--;
}
test_condition(condition_block *c, BOOL toplevel)
{
BOOL yield = FALSE;
-const pcre *re;
uschar *exp[2], *p, *pp;
-const uschar *regcomp_error = NULL;
-int regcomp_error_offset;
int val[2];
int i;
case cond_matches:
case cond_MATCHES:
- if ((filter_test != FTEST_NONE && debug_selector != 0) ||
- (debug_selector & D_filter) != 0)
{
- debug_printf_indent("Match expanded arguments:\n");
- debug_printf_indent(" Subject = %s\n", exp[0]);
- debug_printf_indent(" Pattern = %s\n", exp[1]);
- }
+ const pcre2_code *re;
+ int err;
+ PCRE2_SIZE offset;
- if (!(re = pcre_compile(CS exp[1],
- PCRE_COPT | ((c->type == cond_matches)? PCRE_CASELESS : 0),
- CCSS ®comp_error, ®comp_error_offset, NULL)))
- {
- *error_pointer = string_sprintf("error while compiling "
- "regular expression \"%s\": %s at offset %d",
- exp[1], regcomp_error, regcomp_error_offset);
- return FALSE;
- }
+ if ((filter_test != FTEST_NONE && debug_selector != 0) ||
+ (debug_selector & D_filter) != 0)
+ {
+ debug_printf_indent("Match expanded arguments:\n");
+ debug_printf_indent(" Subject = %s\n", exp[0]);
+ debug_printf_indent(" Pattern = %s\n", exp[1]);
+ }
- yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
- break;
+ if (!(re = pcre2_compile((PCRE2_SPTR)exp[1], PCRE2_ZERO_TERMINATED,
+ PCRE_COPT | (c->type == cond_matches ? PCRE2_CASELESS : 0),
+ &err, &offset, pcre_cmp_ctx)))
+ {
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
+ *error_pointer = string_sprintf("error while compiling "
+ "regular expression \"%s\": %s at offset %l",
+ exp[1], errbuf, (long)offset);
+ return FALSE;
+ }
+
+ yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
+ break;
+ }
/* For above and below, convert the strings to numbers */
#ifdef WITH_CONTENT_SCAN
extern int regex(const uschar **);
#endif
-extern BOOL regex_match_and_setup(const pcre *, const uschar *, int, int);
-extern const pcre *regex_must_compile(const uschar *, BOOL, BOOL);
+extern BOOL regex_match(const pcre2_code *, const uschar *, int, uschar **);
+extern BOOL regex_match_and_setup(const pcre2_code *, const uschar *, int, int);
+extern const pcre2_code *regex_must_compile(const uschar *, BOOL, BOOL);
extern void retry_add_item(address_item *, uschar *, int);
extern BOOL retry_check_address(const uschar *, host_item *, uschar *, BOOL,
uschar **, uschar **);
uschar *dsn_envid = NULL;
int dsn_ret = 0;
-const pcre *regex_DSN = NULL;
+const pcre2_code *regex_DSN = NULL;
uschar *dsn_advertise_hosts = NULL;
#ifndef DISABLE_TLS
BOOL gnutls_allow_auto_pkcs11 = FALSE;
uschar *hosts_require_alpn = NULL;
uschar *openssl_options = NULL;
-const pcre *regex_STARTTLS = NULL;
+const pcre2_code *regex_STARTTLS = NULL;
uschar *tls_advertise_hosts = US"*";
uschar *tls_alpn = US"smtp:esmtp";
uschar *tls_certificate = NULL;
/* Per Recipient Data Response variables */
BOOL prdr_enable = FALSE;
BOOL prdr_requested = FALSE;
-const pcre *regex_PRDR = NULL;
+const pcre2_code *regex_PRDR = NULL;
#endif
#ifdef SUPPORT_I18N
-const pcre *regex_UTF8 = NULL;
+const pcre2_code *regex_UTF8 = NULL;
#endif
/* Input-reading functions for messages, so we can use special ones for
unsigned chunking_datasize = 0;
unsigned chunking_data_left = 0;
chunking_state_t chunking_state= CHUNKING_NOT_OFFERED;
-const pcre *regex_CHUNKING = NULL;
+const pcre2_code *regex_CHUNKING = NULL;
#ifdef EXPERIMENTAL_ESMTP_LIMITS
-const pcre *regex_LIMITS = NULL;
+const pcre2_code *regex_LIMITS = NULL;
#endif
uschar *client_authenticator = NULL;
uschar *override_local_interfaces = NULL;
uschar *override_pid_file_path = NULL;
+pcre2_general_context * pcre_gen_ctx = NULL;
+pcre2_compile_context * pcre_cmp_ctx = NULL;
+pcre2_match_context * pcre_mtc_ctx = NULL;
+
uschar *percent_hack_domains = NULL;
uschar *pid_file_path = US PID_FILE_PATH
"\0<--------------Space to patch pid_file_path->";
recipient_item *recipients_list = NULL;
int recipients_list_max = 0;
int recipients_max = 50000;
-const pcre *regex_AUTH = NULL;
-const pcre *regex_check_dns_names = NULL;
-const pcre *regex_From = NULL;
-const pcre *regex_IGNOREQUOTA = NULL;
-const pcre *regex_PIPELINING = NULL;
-const pcre *regex_SIZE = NULL;
+const pcre2_code *regex_AUTH = NULL;
+const pcre2_code *regex_check_dns_names = NULL;
+const pcre2_code *regex_From = NULL;
+const pcre2_code *regex_IGNOREQUOTA = NULL;
+const pcre2_code *regex_PIPELINING = NULL;
+const pcre2_code *regex_SIZE = NULL;
#ifndef DISABLE_PIPE_CONNECT
-const pcre *regex_EARLY_PIPE = NULL;
+const pcre2_code *regex_EARLY_PIPE = NULL;
#endif
-const pcre *regex_ismsgid = NULL;
-const pcre *regex_smtp_code = NULL;
+const pcre2_code *regex_ismsgid = NULL;
+const pcre2_code *regex_smtp_code = NULL;
uschar *regex_vars[REGEX_VARS];
#ifdef WHITELIST_D_MACROS
-const pcre *regex_whitelisted_macro = NULL;
+const pcre2_code *regex_whitelisted_macro = NULL;
#endif
#ifdef WITH_CONTENT_SCAN
uschar *regex_match_string = NULL;
extern BOOL gnutls_allow_auto_pkcs11; /* Let GnuTLS autoload PKCS11 modules */
extern uschar *hosts_require_alpn; /* Mandatory ALPN successful nogitiation */
extern uschar *openssl_options; /* OpenSSL compatibility options */
-extern const pcre *regex_STARTTLS; /* For recognizing STARTTLS settings */
+extern const pcre2_code *regex_STARTTLS; /* For recognizing STARTTLS settings */
extern uschar *tls_alpn; /* ALPN names acceptable */
extern uschar *tls_certificate; /* Certificate file */
extern uschar *tls_crl; /* CRL File */
extern uschar *dsn_envid; /* DSN envid string */
extern int dsn_ret; /* DSN ret type*/
-extern const pcre *regex_DSN; /* For recognizing DSN settings */
+extern const pcre2_code *regex_DSN; /* For recognizing DSN settings */
extern uschar *dsn_advertise_hosts; /* host for which TLS is advertised */
/* Input-reading functions for messages, so we can use special ones for
extern uschar *acl_smtp_data; /* ACL run after DATA received */
#ifndef DISABLE_PRDR
extern uschar *acl_smtp_data_prdr; /* ACL run after DATA received if in PRDR mode*/
-const extern pcre *regex_PRDR; /* For recognizing PRDR settings */
+const extern pcre2_code *regex_PRDR; /* For recognizing PRDR settings */
#endif
#ifndef DISABLE_DKIM
extern uschar *acl_smtp_dkim; /* ACL run for DKIM signatures / domains */
#ifdef SUPPORT_I18N
extern BOOL message_smtputf8; /* Internationalized mail handling */
extern int message_utf8_downconvert; /* convert from utf8 */
-const extern pcre *regex_UTF8; /* For recognizing SMTPUTF8 settings */
+const extern pcre2_code *regex_UTF8; /* For recognizing SMTPUTF8 settings */
#endif
extern uschar message_subdir[]; /* Subdirectory for messages */
extern uschar *message_reference; /* Reference for error messages */
extern uschar *override_local_interfaces; /* Value of -oX argument */
extern uschar *override_pid_file_path; /* Value of -oP argument */
+extern pcre2_general_context * pcre_gen_ctx; /* pcre memory management */
+extern pcre2_compile_context * pcre_cmp_ctx;
+extern pcre2_match_context * pcre_mtc_ctx;
+
extern uschar *percent_hack_domains; /* Local domains for which '% operates */
extern uschar *pid_file_path; /* For writing daemon pids */
#ifndef DISABLE_PIPE_CONNECT
extern int recipients_list_max; /* Maximum number fitting in list */
extern int recipients_max; /* Max permitted */
extern BOOL recipients_max_reject; /* If TRUE, reject whole message */
-extern const pcre *regex_AUTH; /* For recognizing AUTH settings */
-extern const pcre *regex_check_dns_names; /* For DNS name checking */
-extern const pcre *regex_From; /* For recognizing "From_" lines */
-extern const pcre *regex_CHUNKING; /* For recognizing CHUNKING (RFC 3030) */
-extern const pcre *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */
+extern const pcre2_code *regex_AUTH; /* For recognizing AUTH settings */
+extern const pcre2_code *regex_check_dns_names; /* For DNS name checking */
+extern const pcre2_code *regex_From; /* For recognizing "From_" lines */
+extern const pcre2_code *regex_CHUNKING; /* For recognizing CHUNKING (RFC 3030) */
+extern const pcre2_code *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */
#ifdef EXPERIMENTAL_ESMTP_LIMITS
-extern const pcre *regex_LIMITS; /* For recognizing LIMITS */
+extern const pcre2_code *regex_LIMITS; /* For recognizing LIMITS */
#endif
-extern const pcre *regex_PIPELINING; /* For recognizing PIPELINING */
-extern const pcre *regex_SIZE; /* For recognizing SIZE settings */
+extern const pcre2_code *regex_PIPELINING; /* For recognizing PIPELINING */
+extern const pcre2_code *regex_SIZE; /* For recognizing SIZE settings */
#ifndef DISABLE_PIPE_CONNECT
-extern const pcre *regex_EARLY_PIPE; /* For recognizing PIPE_CONNCT */
+extern const pcre2_code *regex_EARLY_PIPE; /* For recognizing PIPE_CONNCT */
#endif
-extern const pcre *regex_ismsgid; /* Compiled r.e. for message it */
-extern const pcre *regex_smtp_code; /* For recognizing SMTP codes */
+extern const pcre2_code *regex_ismsgid; /* Compiled r.e. for message it */
+extern const pcre2_code *regex_smtp_code; /* For recognizing SMTP codes */
extern uschar *regex_vars[]; /* $regexN variables */
#ifdef WHITELIST_D_MACROS
-extern const pcre *regex_whitelisted_macro; /* For -D macro values */
+extern const pcre2_code *regex_whitelisted_macro; /* For -D macro values */
#endif
#ifdef WITH_CONTENT_SCAN
extern uschar *regex_match_string; /* regex that matched a line (regex ACL condition) */
one_pattern_match(uschar *name, int slen, BOOL has_addresses, uschar *pattern)
{
BOOL yield = FALSE;
-const pcre *re = NULL;
+const pcre2_code *re = NULL;
/* If the pattern is a regex, compile it. Bomb out if compiling fails; these
patterns are all constructed internally and should be valid. */
/* Otherwise, test for the pattern; a non-regex must be an exact match */
- yield = !re
- ? (strcmpic(next, pattern) == 0)
- : (pcre_exec(re, NULL, CS next, Ustrlen(next), 0, PCRE_EOPT, NULL, 0)
- >= 0);
+ yield = re
+ ? regex_match(re, next, -1, NULL)
+ : (strcmpic(next, pattern) == 0);
}
}
else
{
- yield = (re == NULL)?
- (strstric(h->text, pattern, FALSE) != NULL)
- :
- (pcre_exec(re, NULL, CS h->text, h->slen, 0, PCRE_EOPT, NULL, 0) >= 0);
+ yield = re
+ ? regex_match(re, h->text, h->slen, NULL)
+ : (strstric(h->text, pattern, FALSE) != NULL);
}
}
/* Macros for trivial functions */
-#define mac_ismsgid(s) \
- (pcre_exec(regex_ismsgid,NULL,CS s,Ustrlen(s),0,PCRE_EOPT,NULL,0) >= 0)
+#define mac_ismsgid(s) (regex_match(regex_ismsgid, (s), -1, NULL))
/* Options for dns_next_rr */
#define MALWARE_TIMEOUT 120 /* default timeout, seconds */
static const uschar * malware_regex_default = US ".+";
-static const pcre * malware_default_re = NULL;
+static const pcre2_code * malware_default_re = NULL;
#ifndef DISABLE_MAL_CLAM
# define DERR_BAD_CALL (1<<15) /* wrong command */
static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
-static const pcre * drweb_re = NULL;
+static const pcre2_code * drweb_re = NULL;
#endif
#ifndef DISABLE_MAL_FSECURE
static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
-static const pcre * fsec_re = NULL;
+static const pcre2_code * fsec_re = NULL;
#endif
#ifndef DISABLE_MAL_KAV
static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
-static const pcre * kav_re_sus = NULL;
-static const pcre * kav_re_inf = NULL;
+static const pcre2_code * kav_re_sus = NULL;
+static const pcre2_code * kav_re_inf = NULL;
#endif
#ifndef DISABLE_MAL_AVAST
static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d+\\.0\\t0\\s(.*)";
static const uschar * ava_re_error_str = US "(?!\\\\)\\t\\[E\\]\\d+\\.0\\tError\\s\\d+\\s(.*)";
-static const pcre * ava_re_clean = NULL;
-static const pcre * ava_re_virus = NULL;
-static const pcre * ava_re_error = NULL;
+static const pcre2_code * ava_re_clean = NULL;
+static const pcre2_code * ava_re_virus = NULL;
+static const pcre2_code * ava_re_error = NULL;
#endif
#ifndef DISABLE_MAL_FFROT6D
static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
-static const pcre * fprot6d_re_error = NULL;
-static const pcre * fprot6d_re_virus = NULL;
+static const pcre2_code * fprot6d_re_error = NULL;
+static const pcre2_code * fprot6d_re_virus = NULL;
#endif
return sock;
}
-static const pcre *
+static const pcre2_code *
m_pcre_compile(const uschar * re, uschar ** errstr)
{
-const uschar * rerror;
-int roffset;
-const pcre * cre;
+int err;
+PCRE2_SIZE roffset;
+const pcre2_code * cre;
-if (!(cre = pcre_compile(CS re, PCRE_COPT, CCSS &rerror, &roffset, NULL)))
- *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
- re, rerror, roffset);
+if (!(cre = pcre2_compile((PCRE2_SPTR)re, PCRE2_ZERO_TERMINATED,
+ PCRE_COPT, &err, &roffset, pcre_cmp_ctx)))
+ {
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
+ *errstr= string_sprintf("regular expression error in '%s': %s at offset %l",
+ re, errbuf, (long)roffset);
+ }
return cre;
}
uschar *
-m_pcre_exec(const pcre * cre, uschar * text)
+m_pcre_exec(const pcre2_code * cre, uschar * text)
{
-int ovector[10*3];
-int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
- ovector, nelem(ovector));
-uschar * substr = NULL;
+pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
+int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_mtc_ctx);
+PCRE2_UCHAR * substr = NULL;
+PCRE2_SIZE slen;
+
if (i >= 2) /* Got it */
- pcre_get_substring(CS text, ovector, i, 1, CCSS &substr);
-return substr;
+ pcre2_substring_get_bynumber(md, 1, &substr, &slen);
+return US substr;
}
-static const pcre *
+static const pcre2_code *
m_pcre_nextinlist(const uschar ** list, int * sep,
char * listerr, uschar ** errstr)
{
const uschar * list_ele;
-const pcre * cre = NULL;
+const pcre2_code * cre = NULL;
if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
*errstr = US listerr;
uschar *scanner_name;
unsigned long mbox_size;
FILE *mbox_file;
-const pcre *re;
+const pcre2_code *re;
uschar * errstr;
struct scan * scanent;
const uschar * scanner_options;
/* read and concatenate virus names into one string */
for (int i = 0; i < drweb_vnum; i++)
{
- int ovector[10*3];
+ pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
/* read the size of report */
if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
tmpbuf[drweb_slen] = '\0';
/* try matcher on the line, grab substring */
- result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
- ovector, nelem(ovector));
+ result = pcre2_match(drweb_re, (PCRE2_SPTR)tmpbuf, PCRE2_ZERO_TERMINATED,
+ 0, 0, md, pcre_mtc_ctx);
if (result >= 2)
{
- const char * pre_malware_nb;
-
- pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
+ PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
if (i==0) /* the first name we just copy to malware_name */
- g = string_cat(NULL, US pre_malware_nb);
+ g = string_catn(NULL, US ovec[2], ovec[3] - ovec[2]);
- /*XXX could be string_append_listele? */
else /* concatenate each new virus name to previous */
- g = string_append(g, 2, "/", pre_malware_nb);
-
- pcre_free_substring(pre_malware_nb);
+ {
+ g = string_catn(g, US"/", 1);
+ g = string_catn(g, US ovec[2], ovec[3] - ovec[2]);
+ }
}
}
malware_name = string_from_gstring(g);
int kav_rc;
unsigned long kav_reportlen;
int bread;
- const pcre *kav_re;
+ const pcre2_code *kav_re;
uschar *p;
/* get current date and time, build scan request */
case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
{
const uschar *cmdline_scanner = scanner_options;
- const pcre *cmdline_trigger_re;
- const pcre *cmdline_regex_re;
+ const pcre2_code *cmdline_trigger_re;
+ const pcre2_code *cmdline_regex_re;
uschar * file_name;
uschar * commandline;
void (*eximsigchld)(int);
uschar * linebuffer;
uschar * sockline_scanner;
uschar sockline_scanner_default[] = "%s\n";
- const pcre *sockline_trig_re;
- const pcre *sockline_name_re;
+ const pcre2_code *sockline_trig_re;
+ const pcre2_code *sockline_name_re;
/* find scanner command line */
if ( (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
if (malware_name) /* Nothing else matters, just read on */
break;
- if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
+ if (regex_match(ava_re_clean, buf, slen, NULL))
break;
if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
break;
}
}
- else if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
+ else if (regex_match(ava_re_error, buf, slen, NULL))
{
log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
break;
if (pattern[0] == '^')
{
- const pcre * re = regex_must_compile(pattern, cb->caseless, FALSE);
+ const pcre2_code * 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(re, s, -1, NULL)
: !regex_match_and_setup(re, s, 0, expand_setup)
)
return FAIL;
{
BOOL force_delivery = f.queue_run_force || deliver_selectstring != NULL ||
deliver_selectstring_sender != NULL;
-const pcre *selectstring_regex = NULL;
-const pcre *selectstring_regex_sender = NULL;
+const pcre2_code *selectstring_regex = NULL;
+const pcre2_code *selectstring_regex_sender = NULL;
uschar *log_detail = NULL;
int subcount = 0;
uschar subdirs[64];
else if ( deliver_selectstring_sender
&& !(f.deliver_selectstring_sender_regex
- ? (pcre_exec(selectstring_regex_sender, NULL,
- CS sender_address, Ustrlen(sender_address), 0, PCRE_EOPT,
- NULL, 0) >= 0)
+ ? regex_match(selectstring_regex_sender, sender_address, -1, NULL)
: (strstric(sender_address, deliver_selectstring_sender, FALSE)
!= NULL)
) )
{
uschar *address = recipients_list[i].address;
if ( (f.deliver_selectstring_regex
- ? (pcre_exec(selectstring_regex, NULL, CS address,
- Ustrlen(address), 0, PCRE_EOPT, NULL, 0) >= 0)
+ ? regex_match(selectstring_regex, address, -1, NULL)
: (strstric(address, deliver_selectstring, FALSE) != NULL)
)
&& tree_search(tree_nonrecipients, address) == NULL
/* Structure to hold a list of Regular expressions */
typedef struct pcre_list {
- pcre *re;
+ pcre2_code *re;
uschar *pcre_text;
struct pcre_list *next;
} pcre_list;
{
int sep = 0;
uschar *regex_string;
-const char *pcre_error;
-int pcre_erroffset;
pcre_list *re_list_head = NULL;
pcre_list *ri;
while ((regex_string = string_nextinlist(&list, &sep, NULL, 0)))
if (strcmpic(regex_string, US"false") != 0 && Ustrcmp(regex_string, "0") != 0)
{
- pcre *re;
+ pcre2_code * re;
+ int err;
+ PCRE2_SIZE pcre_erroffset;
/* compile our regular expression */
- if (!(re = pcre_compile( CS regex_string,
- 0, &pcre_error, &pcre_erroffset, NULL )))
+ if (!(re = pcre2_compile( (PCRE2_SPTR) regex_string, PCRE2_ZERO_TERMINATED,
+ 0, &err, &pcre_erroffset, pcre_cmp_ctx)))
{
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
log_write(0, LOG_MAIN,
- "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.",
- regex_string, pcre_error, pcre_erroffset);
+ "regex acl condition warning - error in regex '%s': %s at offset %l, skipped.",
+ regex_string, errbuf, (long)pcre_erroffset);
continue;
}
static int
matcher(pcre_list * re_list_head, uschar * linebuffer, int len)
{
-for(pcre_list * ri = re_list_head; ri; ri = ri->next)
+pcre2_match_data * md = pcre2_match_data_create(REGEX_VARS + 1, pcre_gen_ctx);
+
+for (pcre_list * ri = re_list_head; ri; ri = ri->next)
{
- int ovec[3*(REGEX_VARS+1)];
int n;
/* try matcher on the line */
- if ((n = pcre_exec(ri->re, NULL, CS linebuffer, len, 0, 0, ovec, nelem(ovec))) > 0)
+ if ((n = pcre2_match(ri->re, (PCRE2_SPTR)linebuffer, len, 0, 0, md, pcre_mtc_ctx)) > 0)
{
Ustrncpy(regex_match_string_buffer, ri->pcre_text,
sizeof(regex_match_string_buffer)-1);
regex_match_string = regex_match_string_buffer;
for (int nn = 1; nn < n; nn++)
- regex_vars[nn-1] =
- string_copyn(linebuffer + ovec[nn*2], ovec[nn*2+1] - ovec[nn*2]);
+ {
+ PCRE2_UCHAR * cstr;
+ PCRE2_SIZE cslen;
+ pcre2_substring_get_bynumber(md, nn, &cstr, &cslen);
+ regex_vars[nn-1] = US cstr;
+ }
return OK;
}
}
+pcre2_match_data_free(md);
return FAIL;
}
address_item *new_addr;
iplookup_router_options_block *ob =
(iplookup_router_options_block *)(rblock->options_block);
-const pcre *re = ob->re_response_pattern;
+const pcre2_code *re = ob->re_response_pattern;
int count, query_len, rc;
int sep = 0;
uschar *query;
uschar *response_pattern;
uschar *reroute;
- const pcre *re_response_pattern;
+ const pcre2_code *re_response_pattern;
BOOL optional;
} iplookup_router_options_block;
addr->message = yield == FAIL ? US"forced rejection" : US"forced defer";
else
{
- int ovector[3];
- if (ob->forbid_smtp_code &&
- pcre_exec(regex_smtp_code, NULL, CS addr->message,
- Ustrlen(addr->message), 0, PCRE_EOPT,
- ovector, sizeof(ovector)/sizeof(int)) >= 0)
+ uschar * matched;
+ if ( ob->forbid_smtp_code
+ && regex_match(regex_smtp_code, addr->message, -1, &matched))
{
DEBUG(D_route) debug_printf("SMTP code at start of error message "
"is ignored because forbid_smtp_code is set\n");
- addr->message += ovector[1];
+ addr->message += Ustrlen(matched);
}
addr->user_message = addr->message;
setflag(addr, af_pass_message);
smtp_message_code(uschar **code, int *codelen, uschar **msg, uschar **log_msg,
BOOL check_valid)
{
-int n;
-int ovector[3];
+uschar * match;
+int len;
-if (!msg || !*msg) return;
-
-if ((n = pcre_exec(regex_smtp_code, NULL, CS *msg, Ustrlen(*msg), 0,
- PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int))) < 0) return;
+if (!msg || !*msg || !regex_match(regex_smtp_code, *msg, -1, &match))
+ return;
+len = Ustrlen(match);
if (check_valid && (*msg)[0] != (*code)[0])
{
log_write(0, LOG_MAIN|LOG_PANIC, "configured error code starts with "
"incorrect digit (expected %c) in \"%s\"", (*code)[0], *msg);
- if (log_msg != NULL && *log_msg == *msg)
- *log_msg = string_sprintf("%s %s", *code, *log_msg + ovector[1]);
+ if (log_msg && *log_msg == *msg)
+ *log_msg = string_sprintf("%s %s", *code, *log_msg + len);
}
else
{
*code = *msg;
- *codelen = ovector[1]; /* Includes final space */
+ *codelen = len; /* Includes final space */
}
-*msg += ovector[1]; /* Chop the code off the message */
+*msg += len; /* Chop the code off the message */
return;
}
Arguments:
dirname the name of the directory
countptr where to add the file count (because this function recurses)
- regex a compiled regex to get the size from a name
+ re a compiled regex to get the size from a name
Returns: the sum of the sizes of the stattable files
zero if the directory cannot be opened
*/
off_t
-check_dir_size(const uschar * dirname, int *countptr, const pcre *regex)
+check_dir_size(const uschar * dirname, int * countptr, const pcre2_code * re)
{
DIR *dir;
off_t sum = 0;
/* If there's a regex, try to find the size using it */
- if (regex)
+ if (re)
{
- int ovector[6];
- if (pcre_exec(regex, NULL, CS name, Ustrlen(name), 0, 0, ovector,6) >= 2)
+ pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
+ int rc = pcre2_match(re, (PCRE2_SPTR)name, PCRE2_ZERO_TERMINATED,
+ 0, 0, md, pcre_mtc_ctx);
+ PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
+ if ( rc >= 0
+ && (rc = pcre2_get_ovector_count(md)) >= 2)
{
uschar *endptr;
- off_t size = (off_t)Ustrtod(name + ovector[2], &endptr);
- if (endptr == name + ovector[3])
+ off_t size = (off_t)Ustrtod(name + ovec[2], &endptr);
+ if (endptr == name + ovec[3])
{
sum += size;
DEBUG(D_transport)
if ((statbuf.st_mode & S_IFMT) == S_IFREG)
sum += statbuf.st_size / statbuf.st_nlink;
else if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
- sum += check_dir_size(path, &count, regex);
+ sum += check_dir_size(path, &count, re);
}
closedir(dir);
else
{
uschar *check_path; /* Default quota check path */
- const pcre *regex = NULL; /* Regex for file size from file name */
+ const pcre2_code * re = NULL; /* Regex for file size from file name */
if (!check_creation(string_sprintf("%s/any", path),
ob->create_file, deliver_dir))
if (ob->quota_value > 0 || THRESHOLD_CHECK || ob->maildir_use_size_file)
{
- const uschar *error;
- int offset;
+ PCRE2_SIZE offset;
+ int err;
/* Compile the regex if there is one. */
if (ob->quota_size_regex)
{
- if (!(regex = pcre_compile(CS ob->quota_size_regex, PCRE_COPT,
- CCSS &error, &offset, NULL)))
+ if (!(re = pcre2_compile((PCRE2_SPTR)ob->quota_size_regex,
+ PCRE2_ZERO_TERMINATED, PCRE_COPT, &err, &offset, pcre_cmp_ctx)))
{
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
addr->message = string_sprintf("appendfile: regular expression "
- "error: %s at offset %d while compiling %s", error, offset,
+ "error: %s at offset %l while compiling %s", errbuf, (long)offset,
ob->quota_size_regex);
return FALSE;
}
#ifdef SUPPORT_MAILDIR
if (ob->maildir_use_size_file)
{
- const pcre *dir_regex = NULL;
- const uschar *error;
- int offset;
+ const pcre2_code * dir_regex = NULL;
+ PCRE2_SIZE offset;
+ int err;
if (ob->maildir_dir_regex)
{
int check_path_len = Ustrlen(check_path);
- if (!(dir_regex = pcre_compile(CS ob->maildir_dir_regex, PCRE_COPT,
- CCSS &error, &offset, NULL)))
+ if (!(dir_regex = pcre2_compile((PCRE2_SPTR)ob->maildir_dir_regex,
+ PCRE2_ZERO_TERMINATED, PCRE_COPT, &err, &offset, pcre_cmp_ctx)))
{
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
addr->message = string_sprintf("appendfile: regular expression "
- "error: %s at offset %d while compiling %s", error, offset,
+ "error: %s at offset %l while compiling %s", errbuf, (long)offset,
ob->maildir_dir_regex);
return FALSE;
}
uschar *s = path + check_path_len;
while (*s == '/') s++;
s = *s ? string_sprintf("%s/new", s) : US"new";
- if (pcre_exec(dir_regex, NULL, CS s, Ustrlen(s), 0, 0, NULL, 0) < 0)
+ if (!regex_match(dir_regex, s, -1, NULL))
{
disable_quota = TRUE;
DEBUG(D_transport) debug_printf("delivery directory does not match "
off_t size;
int filecount;
- if ((maildirsize_fd = maildir_ensure_sizefile(check_path, ob, regex, dir_regex,
+ if ((maildirsize_fd = maildir_ensure_sizefile(check_path, ob, re, dir_regex,
&size, &filecount)) == -1)
{
addr->basic_errno = errno;
* (void)unlink(CS string_sprintf("%s/maildirsize", check_path));
* if (THRESHOLD_CHECK)
* mailbox_size = maildir_compute_size(check_path, &mailbox_filecount, &old_latest,
- * regex, dir_regex, FALSE);
+ * re, dir_regex, FALSE);
* }
*/
int filecount = 0;
DEBUG(D_transport)
debug_printf("quota checks on directory %s\n", check_path);
- size = check_dir_size(check_path, &filecount, regex);
+ size = check_dir_size(check_path, &filecount, re);
if (mailbox_size < 0) mailbox_size = size;
if (mailbox_filecount < 0) mailbox_filecount = filecount;
}
/* Function that is shared with tf_maildir.c */
-extern off_t check_dir_size(const uschar *, int *, const pcre *);
+extern off_t check_dir_size(const uschar *, int *, const pcre2_code *);
/* End of transports/appendfile.h */
/* First thing is to wait for an initial greeting. */
Ustrcpy(big_buffer, US"initial connection");
-if (!lmtp_read_response(out, buffer, sizeof(buffer), '2',
- timeout)) goto RESPONSE_FAILED;
+if (!lmtp_read_response(out, buffer, sizeof(buffer), '2', timeout))
+ goto RESPONSE_FAILED;
/* Next, we send a LHLO command, and expect a positive response */
-if (!lmtp_write_command(fd_in, "%s %s\r\n", "LHLO",
- primary_hostname)) goto WRITE_FAILED;
+if (!lmtp_write_command(fd_in, "%s %s\r\n", "LHLO", primary_hostname))
+ goto WRITE_FAILED;
-if (!lmtp_read_response(out, buffer, sizeof(buffer), '2',
- timeout)) goto RESPONSE_FAILED;
+if (!lmtp_read_response(out, buffer, sizeof(buffer), '2', timeout))
+ goto RESPONSE_FAILED;
/* If the ignore_quota option is set, note whether the server supports the
IGNOREQUOTA option, and if so, set an appropriate addition for RCPT. */
if (ob->ignore_quota)
- igquotstr = (pcre_exec(regex_IGNOREQUOTA, NULL, CS buffer,
- Ustrlen(CS buffer), 0, PCRE_EOPT, NULL, 0) >= 0)? US" IGNOREQUOTA" : US"";
+ igquotstr = regex_match(regex_IGNOREQUOTA, buffer, -1, NULL)
+ ? US" IGNOREQUOTA" : US"";
/* Now the envelope sender */
void
smtp_deliver_init(void)
{
-if (!regex_PIPELINING) regex_PIPELINING =
- regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_SIZE) regex_SIZE =
- regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_AUTH) regex_AUTH =
- regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
+struct list
+ {
+ const pcre2_code ** re;
+ const uschar * string;
+ } list[] =
+ {
+ { ®ex_AUTH, AUTHS_REGEX },
+ { ®ex_CHUNKING, US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)" },
+ { ®ex_DSN, US"\\n250[\\s\\-]DSN(\\s|\\n|$)" },
+ { ®ex_IGNOREQUOTA, US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)" },
+ { ®ex_PIPELINING, US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)" },
+ { ®ex_SIZE, US"\\n250[\\s\\-]SIZE(\\s|\\n|$)" },
#ifndef DISABLE_TLS
-if (!regex_STARTTLS) regex_STARTTLS =
- regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
+ { ®ex_STARTTLS, US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)" },
#endif
-
-if (!regex_CHUNKING) regex_CHUNKING =
- regex_must_compile(US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)", FALSE, TRUE);
-
#ifndef DISABLE_PRDR
-if (!regex_PRDR) regex_PRDR =
- regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
+ { ®ex_PRDR, US"\\n250[\\s\\-]PRDR(\\s|\\n|$)" },
#endif
-
#ifdef SUPPORT_I18N
-if (!regex_UTF8) regex_UTF8 =
- regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
+ { ®ex_UTF8, US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)" },
#endif
-
-if (!regex_DSN) regex_DSN =
- regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
- regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
-
#ifndef DISABLE_PIPE_CONNECT
-if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
- regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
+ { ®ex_EARLY_PIPE, US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)" },
#endif
-
#ifdef EXPERIMENTAL_ESMTP_LIMITS
-if (!regex_LIMITS) regex_LIMITS =
- regex_must_compile(US"\\n250[\\s\\-]LIMITS\\s", FALSE, TRUE);
+ { ®ex_LIMITS, US"\\n250[\\s\\-]LIMITS\\s" },
#endif
+ };
+
+for (struct list * l = list; l < list + nelem(list); l++)
+ if (!*l->re)
+ *l->re = regex_must_compile(l->string, FALSE, TRUE);
}
static void
ehlo_response_limits_read(smtp_context * sx)
{
-int ovec[3]; /* results vector for a main-match only */
+uschar * match;
/* matches up to just after the first space after the keyword */
-if (pcre_exec(regex_LIMITS, NULL, CS sx->buffer, Ustrlen(sx->buffer),
- 0, PCRE_EOPT, ovec, nelem(ovec)) >= 0)
- for (const uschar * s = sx->buffer + ovec[1]; *s; )
+if (regex_match(regex_LIMITS, sx->buffer, -1, &match))
+ for (const uschar * s = sx->buffer + Ustrlen(match); *s; )
{
while (isspace(*s)) s++;
if (*s == '\n') break;
static unsigned
ehlo_response(uschar * buf, unsigned checks)
{
-size_t bsize = Ustrlen(buf);
+PCRE2_SIZE bsize = Ustrlen(buf);
+pcre2_match_data * md = pcre2_match_data_create(1, pcre_gen_ctx);
/* debug_printf("%s: check for 0x%04x\n", __FUNCTION__, checks); */
#ifndef DISABLE_TLS
if ( checks & OPTION_TLS
- && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_STARTTLS,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
#endif
checks &= ~OPTION_TLS;
if ( checks & OPTION_IGNQ
- && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0,
- PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_IGNOREQUOTA,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
checks &= ~OPTION_IGNQ;
if ( checks & OPTION_CHUNKING
- && pcre_exec(regex_CHUNKING, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_CHUNKING,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
checks &= ~OPTION_CHUNKING;
#ifndef DISABLE_PRDR
if ( checks & OPTION_PRDR
- && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_PRDR,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
#endif
checks &= ~OPTION_PRDR;
#ifdef SUPPORT_I18N
if ( checks & OPTION_UTF8
- && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_UTF8,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
#endif
checks &= ~OPTION_UTF8;
if ( checks & OPTION_DSN
- && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_DSN,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
checks &= ~OPTION_DSN;
if ( checks & OPTION_PIPE
- && pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0,
- PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_PIPELINING,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
checks &= ~OPTION_PIPE;
if ( checks & OPTION_SIZE
- && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_SIZE,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
checks &= ~OPTION_SIZE;
#ifndef DISABLE_PIPE_CONNECT
if ( checks & OPTION_EARLY_PIPE
- && pcre_exec(regex_EARLY_PIPE, NULL, CS buf, bsize, 0,
- PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_EARLY_PIPE,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
#endif
checks &= ~OPTION_EARLY_PIPE;
+pcre2_match_data_free(md);
/* debug_printf("%s: found 0x%04x\n", __FUNCTION__, checks); */
return checks;
}
/* If the basic path matches maildirfolder_create_regex, we are dealing with
a subfolder, and should ensure that a maildirfolder file exists. */
-if (maildirfolder_create_regex != NULL)
+if (maildirfolder_create_regex)
{
- const uschar *error;
- int offset;
- const pcre *regex;
+ int err;
+ PCRE2_SIZE offset;
+ const pcre2_code * re;
DEBUG(D_transport) debug_printf("checking for maildirfolder requirement\n");
- if (!(regex = pcre_compile(CS maildirfolder_create_regex, PCRE_COPT,
- CCSS &error, &offset, NULL)))
+ if (!(re = pcre2_compile((PCRE2_SPTR)maildirfolder_create_regex,
+ PCRE2_ZERO_TERMINATED, PCRE_COPT, &err, &offset, pcre_cmp_ctx)))
{
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
addr->message = string_sprintf("appendfile: regular expression "
- "error: %s at offset %d while compiling %s", error, offset,
+ "error: %s at offset %l while compiling %s", errbuf, (long)offset,
maildirfolder_create_regex);
return FALSE;
}
- if (pcre_exec(regex, NULL, CS path, Ustrlen(path), 0, 0, NULL, 0) >= 0)
+ if (regex_match(re, path, -1, NULL))
{
uschar *fname = string_sprintf("%s/maildirfolder", path);
if (Ustat(fname, &statbuf) == 0)
off_t
maildir_compute_size(uschar *path, int *filecount, time_t *latest,
- const pcre *regex, const pcre *dir_regex, BOOL timestamp_only)
+ const pcre2_code *regex, const pcre2_code *dir_regex, BOOL timestamp_only)
{
DIR *dir;
off_t sum = 0;
scan. We do the regex match first, because that avoids a stat() for names
we aren't interested in. */
- if (dir_regex != NULL &&
- pcre_exec(dir_regex, NULL, CS name, Ustrlen(name), 0, 0, NULL, 0) < 0)
+ if (dir_regex && !regex_match(dir_regex, name, -1, NULL))
{
DEBUG(D_transport)
debug_printf("skipping %s/%s: dir_regex does not match\n", path, name);
int
maildir_ensure_sizefile(uschar *path, appendfile_transport_options_block *ob,
- const pcre *regex, const pcre *dir_regex, off_t *returned_size,
+ const pcre2_code *regex, const pcre2_code *dir_regex, off_t *returned_size,
int *returned_filecount)
{
int count, fd;
/* Header file for the functions that are used to support the use of
maildirsize files for quota handling in maildir directories. */
-extern off_t maildir_compute_size(uschar *, int *, time_t *, const pcre *,
- const pcre *, BOOL);
+extern off_t maildir_compute_size(uschar *, int *, time_t *, const pcre2_code *,
+ const pcre2_code *, BOOL);
extern BOOL maildir_ensure_directories(uschar *, address_item *, BOOL, int,
uschar *);
extern int maildir_ensure_sizefile(uschar *,
- appendfile_transport_options_block *, const pcre *,
- const pcre *, off_t *, int *);
+ appendfile_transport_options_block *, const pcre2_code *,
+ const pcre2_code *, off_t *, int *);
extern void maildir_record_length(int, int);
/* End of tf_maildir.h */