build: use pkg-config for i18n
[exim.git] / src / src / rda.c
index 0dc859107746cb6d8911ed8c65bcfb26fd612f67..405e4646ae913557c9821fb3ab2d0ee25cd2e0b3 100644 (file)
@@ -2,9 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim maintainers 2020 - 2024 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* This module contains code for extracting addresses from a forwarding list
 (from an alias or forward file) or by running the filter interpreter. It may do
 
 /* This module contains code for extracting addresses from a forwarding list
 (from an alias or forward file) or by running the filter interpreter. It may do
@@ -42,7 +43,7 @@ Returns:   FILTER_EXIM    if it starts with "# Exim filter"
 static BOOL
 match_tag(const uschar *s, const uschar *tag)
 {
 static BOOL
 match_tag(const uschar *s, const uschar *tag)
 {
-for (; *tag != 0; s++, tag++)
+for (; *tag; s++, tag++)
   if (*tag == ' ')
     {
     while (*s == ' ' || *s == '\t') s++;
   if (*tag == ' ')
     {
     while (*s == ' ' || *s == '\t') s++;
@@ -60,10 +61,10 @@ tags for other types of filter. */
 int
 rda_is_filter(const uschar *s)
 {
 int
 rda_is_filter(const uschar *s)
 {
-while (isspace(*s)) s++;     /* Skips initial blank lines */
-if (match_tag(s, CUS"# exim filter")) return FILTER_EXIM;
-  else if (match_tag(s, CUS"# sieve filter")) return FILTER_SIEVE;
-    else return FILTER_FORWARD;
+Uskip_whitespace(&s);                  /* Skips initial blank lines */
+if (match_tag(s, CUS"# exim filter"))          return FILTER_EXIM;
+else if (match_tag(s, CUS"# sieve filter"))    return FILTER_SIEVE;
+else                                           return FILTER_FORWARD;
 }
 
 
 }
 
 
@@ -166,7 +167,7 @@ Returns:      pointer to string in store; NULL on error
 */
 
 static uschar *
 */
 
 static uschar *
-rda_get_file_contents(redirect_block *rdata, int options, uschar **error,
+rda_get_file_contents(const redirect_block *rdata, int options, uschar **error,
   int *yield)
 {
 FILE *fwd;
   int *yield)
 {
 FILE *fwd;
@@ -222,7 +223,7 @@ if (!(fwd = Ufopen(filename, "rb"))) switch(errno)
 
 DEFAULT_ERROR:
   default:
 
 DEFAULT_ERROR:
   default:
-    *error = string_open_failed(errno, "%s", filename);
+    *error = string_open_failed("%s", filename);
     *yield = FF_ERROR;
     return NULL;
   }
     *yield = FF_ERROR;
     return NULL;
   }
@@ -284,7 +285,7 @@ if (statbuf.st_size > MAX_FILTER_SIZE)
 
 /* Read the file in one go in order to minimize the time we have it open. */
 
 
 /* Read the file in one go in order to minimize the time we have it open. */
 
-filebuf = store_get(statbuf.st_size + 1, is_tainted(filename));
+filebuf = store_get(statbuf.st_size + 1, filename);
 
 if (fread(filebuf, 1, statbuf.st_size, fwd) != statbuf.st_size)
   {
 
 if (fread(filebuf, 1, statbuf.st_size, fwd) != statbuf.st_size)
   {
@@ -321,10 +322,7 @@ Arguments:
   rdata                     the redirection block
   options                   the options bits
   include_directory         restrain to this directory
   rdata                     the redirection block
   options                   the options bits
   include_directory         restrain to this directory
-  sieve_vacation_directory  passed to sieve_interpret
-  sieve_enotify_mailto_owner passed to sieve_interpret
-  sieve_useraddress         passed to sieve_interpret
-  sieve_subaddress          passed to sieve_interpret
+  sieve                            passed to sieve_interpret
   generated                 where to hang generated addresses
   error                     for error messages
   eblockp                   for details of skipped syntax errors
   generated                 where to hang generated addresses
   error                     for error messages
   eblockp                   for details of skipped syntax errors
@@ -339,13 +337,12 @@ Returns:                    a suitable return for rda_interpret()
 */
 
 static int
 */
 
 static int
-rda_extract(redirect_block *rdata, int options, uschar *include_directory,
-  uschar *sieve_vacation_directory, uschar *sieve_enotify_mailto_owner,
-  uschar *sieve_useraddress, uschar *sieve_subaddress,
-  address_item **generated, uschar **error, error_block **eblockp,
-  int *filtertype)
+rda_extract(const redirect_block * rdata, int options,
+  const uschar * include_directory, const sieve_block * sieve,
+  address_item ** generated, uschar ** error,
+  error_block ** eblockp, int * filtertype)
 {
 {
-uschar *data;
+const uschar * data;
 
 if (rdata->isfile)
   {
 
 if (rdata->isfile)
   {
@@ -386,23 +383,40 @@ if (*filtertype != FILTER_FORWARD)
 
   if (*filtertype == FILTER_EXIM)
     {
 
   if (*filtertype == FILTER_EXIM)
     {
-    if ((options & RDO_EXIM_FILTER) != 0)
+    const misc_module_info * mi;
+    typedef int (*fn_t)(const uschar *, int, address_item **, uschar **);
+
+    if (options & RDO_EXIM_FILTER)
       {
       *error = US"Exim filtering not enabled";
       return FF_ERROR;
       }
       {
       *error = US"Exim filtering not enabled";
       return FF_ERROR;
       }
-    frc = filter_interpret(data, options, generated, error);
+    if (!(mi = misc_mod_find(US"exim_filter", NULL)))
+      {
+      *error = US"Exim-filtering not available";
+      return FF_ERROR;
+      }
+    frc = (((fn_t *) mi->functions)[EXIM_INTERPRET])
+                                     (data, options, generated, error);
     }
   else
     {
     }
   else
     {
+    const misc_module_info * mi;
+    typedef int (*fn_t)(const uschar *, int, const sieve_block *,
+                      address_item **, uschar **);
+
     if (options & RDO_SIEVE_FILTER)
       {
       *error = US"Sieve filtering not enabled";
       return FF_ERROR;
       }
     if (options & RDO_SIEVE_FILTER)
       {
       *error = US"Sieve filtering not enabled";
       return FF_ERROR;
       }
-    frc = sieve_interpret(data, options, sieve_vacation_directory,
-      sieve_enotify_mailto_owner, sieve_useraddress, sieve_subaddress,
-      generated, error);
+    if (!(mi = misc_mod_find(US"sieve_filter", NULL)))
+      {
+      *error = US"Sieve filtering not available";
+      return FF_ERROR;
+      }
+    frc = (((fn_t *) mi->functions)[SIEVE_INTERPRET])
+                                     (data, options, sieve, generated, error);
     }
 
   expand_forbid = old_expand_forbid;
     }
 
   expand_forbid = old_expand_forbid;
@@ -442,9 +456,9 @@ Returns:     -1 on error, else 0
 static int
 rda_write_string(int fd, const uschar *s)
 {
 static int
 rda_write_string(int fd, const uschar *s)
 {
-int len = (s == NULL)? 0 : Ustrlen(s) + 1;
+int len = s ? Ustrlen(s) + 1 : 0;
 return (  write(fd, &len, sizeof(int)) != sizeof(int)
 return (  write(fd, &len, sizeof(int)) != sizeof(int)
-       || (s != NULL  &&  write(fd, s, len) != len)
+       || (s   &&  write(fd, s, len) != len)
        )
        ? -1 : 0;
 }
        )
        ? -1 : 0;
 }
@@ -465,7 +479,7 @@ Returns:     FALSE if data missing
 */
 
 static BOOL
 */
 
 static BOOL
-rda_read_string(int fd, uschar **sp)
+rda_read_string(int fd, uschar ** sp)
 {
 int len;
 
 {
 int len;
 
@@ -476,7 +490,7 @@ else
   /* We know we have enough memory so disable the error on "len" */
   /* coverity[tainted_data] */
   /* We trust the data source, so untainted */
   /* We know we have enough memory so disable the error on "len" */
   /* coverity[tainted_data] */
   /* We trust the data source, so untainted */
-  if (read(fd, *sp = store_get(len, FALSE), len) != len) return FALSE;
+  if (read(fd, *sp = store_get(len, GET_UNTAINTED), len) != len) return FALSE;
 return TRUE;
 }
 
 return TRUE;
 }
 
@@ -512,10 +526,7 @@ Arguments:
   options                   options to pass to the extraction functions,
                               plus ENOTDIR and EACCES handling bits
   include_directory         restrain :include: to this directory
   options                   options to pass to the extraction functions,
                               plus ENOTDIR and EACCES handling bits
   include_directory         restrain :include: to this directory
-  sieve_vacation_directory  directory passed to sieve_interpret
-  sieve_enotify_mailto_owner passed to sieve_interpret
-  sieve_useraddress         passed to sieve_interpret
-  sieve_subaddress          passed to sieve_interpret
+  sieve                            passed to sieve_interpret
   ugid                      uid/gid to run under - if NULL, no change
   generated                 where to hang generated addresses, initially NULL
   error                     pointer for error message
   ugid                      uid/gid to run under - if NULL, no change
   generated                 where to hang generated addresses, initially NULL
   error                     pointer for error message
@@ -541,11 +552,10 @@ Returns:        values from extraction function, or FF_NONEXIST:
 */
 
 int
 */
 
 int
-rda_interpret(redirect_block *rdata, int options, uschar *include_directory,
-  uschar *sieve_vacation_directory, uschar *sieve_enotify_mailto_owner,
-  uschar *sieve_useraddress, uschar *sieve_subaddress, ugid_block *ugid,
-  address_item **generated, uschar **error, error_block **eblockp,
-  int *filtertype, uschar *rname)
+rda_interpret(redirect_block * rdata, int options,
+  const uschar * include_directory, const sieve_block * sieve,
+  const ugid_block * ugid, address_item ** generated,
+  uschar ** error, error_block ** eblockp, int * filtertype, const uschar * rname)
 {
 int fd, rc, pfd[2];
 int yield, status;
 {
 int fd, rc, pfd[2];
 int yield, status;
@@ -583,13 +593,13 @@ with #Exim filter or #Sieve filter, and does not contain :include:, do all the
 work in this process. Note that for a system filter, we always have a file, so
 the work is done in this process only if no user is supplied. */
 
 work in this process. Note that for a system filter, we always have a file, so
 the work is done in this process only if no user is supplied. */
 
-if (!ugid->uid_set ||                         /* Either there's no uid, or */
-    (!rdata->isfile &&                        /* We've got the data, and */
-     rda_is_filter(data) == FILTER_FORWARD && /* It's not a filter script, */
-     Ustrstr(data, ":include:") == NULL))     /* and there's no :include: */
-  return rda_extract(rdata, options, include_directory,
-    sieve_vacation_directory, sieve_enotify_mailto_owner, sieve_useraddress,
-    sieve_subaddress, generated, error, eblockp, filtertype);
+if (  !ugid->uid_set                           /* Either there's no uid, or */
+   || (  !rdata->isfile                                /* We've got the data, and */
+      && rda_is_filter(data) == FILTER_FORWARD /* It's not a filter script, */
+      && Ustrstr(data, ":include:") == NULL    /* and there's no :include: */
+   )  )
+  return rda_extract(rdata, options, include_directory, sieve,
+                   generated, error, eblockp, filtertype);
 
 /* We need to run the processing code in a sub-process. However, if we can
 determine the non-existence of a file first, we can decline without having to
 
 /* We need to run the processing code in a sub-process. However, if we can
 determine the non-existence of a file first, we can decline without having to
@@ -618,9 +628,14 @@ search_tidyup();
 if ((pid = exim_fork(US"router-interpret")) == 0)
   {
   header_line *waslast = header_last;   /* Save last header */
 if ((pid = exim_fork(US"router-interpret")) == 0)
   {
   header_line *waslast = header_last;   /* Save last header */
+  int fd_flags = -1;
 
   fd = pfd[pipe_write];
   (void)close(pfd[pipe_read]);
 
   fd = pfd[pipe_write];
   (void)close(pfd[pipe_read]);
+
+  if ((fd_flags = fcntl(fd, F_GETFD)) == -1) goto bad;
+  if (fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC) == -1) goto bad;
+
   exim_setugid(ugid->uid, ugid->gid, FALSE, rname);
 
   /* Addresses can get rewritten in filters; if we are not root or the exim
   exim_setugid(ugid->uid, ugid->gid, FALSE, rname);
 
   /* Addresses can get rewritten in filters; if we are not root or the exim
@@ -636,9 +651,8 @@ if ((pid = exim_fork(US"router-interpret")) == 0)
 
   /* Now do the business */
 
 
   /* Now do the business */
 
-  yield = rda_extract(rdata, options, include_directory,
-    sieve_vacation_directory, sieve_enotify_mailto_owner, sieve_useraddress,
-    sieve_subaddress, generated, error, eblockp, filtertype);
+  yield = rda_extract(rdata, options, include_directory, sieve,
+                     generated, error, eblockp, filtertype);
 
   /* Pass back whether it was a filter, and the return code and any overall
   error text via the pipe. */
 
   /* Pass back whether it was a filter, and the return code and any overall
   error text via the pipe. */
@@ -803,7 +817,7 @@ if (eblockp)
     uschar *s;
     if (!rda_read_string(fd, &s)) goto DISASTER;
     if (!s) break;
     uschar *s;
     if (!rda_read_string(fd, &s)) goto DISASTER;
     if (!s) break;
-    e = store_get(sizeof(error_block), FALSE);
+    e = store_get(sizeof(error_block), GET_UNTAINTED);
     e->next = NULL;
     e->text1 = s;
     if (!rda_read_string(fd, &s)) goto DISASTER;
     e->next = NULL;
     e->text1 = s;
     if (!rda_read_string(fd, &s)) goto DISASTER;
@@ -860,9 +874,9 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
   for (;;)
     {
     int i, reply_options;
   for (;;)
     {
     int i, reply_options;
-    address_item *addr;
-    uschar *recipient;
-    uschar *expandn[EXPAND_MAXN + 2];
+    address_item * addr;
+    uschar * recipient, * s;
+    uschar * expandn[EXPAND_MAXN + 2];
 
     /* First string is the address; NULL => end of addresses */
 
 
     /* First string is the address; NULL => end of addresses */
 
@@ -879,10 +893,11 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
 
     if (  read(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode)
        || read(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags)
 
     if (  read(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode)
        || read(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags)
-       || !rda_read_string(fd, &addr->prop.errors_address)
+       || !rda_read_string(fd, &s)
        || read(fd, &i, sizeof(i)) != sizeof(i)
        )
       goto DISASTER;
        || read(fd, &i, sizeof(i)) != sizeof(i)
        )
       goto DISASTER;
+    addr->prop.errors_address = s;
     addr->prop.ignore_error = (i != 0);
 
     /* Next comes a possible setting for $thisaddress and any numerical
     addr->prop.ignore_error = (i != 0);
 
     /* Next comes a possible setting for $thisaddress and any numerical
@@ -902,7 +917,7 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
 
     if (i > 0)
       {
 
     if (i > 0)
       {
-      addr->pipe_expandn = store_get((i+1) * sizeof(uschar *), FALSE);
+      addr->pipe_expandn = store_get((i+1) * sizeof(uschar *), GET_UNTAINTED);
       addr->pipe_expandn[i] = NULL;
       while (--i >= 0) addr->pipe_expandn[i] = expandn[i];
       }
       addr->pipe_expandn[i] = NULL;
       while (--i >= 0) addr->pipe_expandn[i] = expandn[i];
       }
@@ -912,7 +927,7 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
     if (read(fd, &reply_options, sizeof(int)) != sizeof(int)) goto DISASTER;
     if ((reply_options & REPLY_EXISTS) != 0)
       {
     if (read(fd, &reply_options, sizeof(int)) != sizeof(int)) goto DISASTER;
     if ((reply_options & REPLY_EXISTS) != 0)
       {
-      addr->reply = store_get(sizeof(reply_item), FALSE);
+      addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED);
 
       addr->reply->file_expand = (reply_options & REPLY_EXPAND) != 0;
       addr->reply->return_message = (reply_options & REPLY_RETURN) != 0;
 
       addr->reply->file_expand = (reply_options & REPLY_EXPAND) != 0;
       addr->reply->return_message = (reply_options & REPLY_RETURN) != 0;
@@ -956,16 +971,14 @@ if (had_disaster)
   *error = string_sprintf("internal problem in %s: failure to transfer "
     "data from subprocess: status=%04x%s%s%s", rname,
     status, readerror,
   *error = string_sprintf("internal problem in %s: failure to transfer "
     "data from subprocess: status=%04x%s%s%s", rname,
     status, readerror,
-    (*error == NULL)? US"" : US": error=",
-    (*error == NULL)? US"" : *error);
+    *error ? US": error=" : US"",
+    *error ? *error : US"");
   log_write(0, LOG_MAIN|LOG_PANIC, "%s", *error);
   }
 else if (status != 0)
   log_write(0, LOG_MAIN|LOG_PANIC, "%s", *error);
   }
 else if (status != 0)
-  {
   log_write(0, LOG_MAIN|LOG_PANIC, "internal problem in %s: unexpected status "
     "%04x from redirect subprocess (but data correctly received)", rname,
     status);
   log_write(0, LOG_MAIN|LOG_PANIC, "internal problem in %s: unexpected status "
     "%04x from redirect subprocess (but data correctly received)", rname,
     status);
-  }
 
 FINAL_EXIT:
 (void)close(fd);
 
 FINAL_EXIT:
 (void)close(fd);