build: use pkg-config for i18n
[exim.git] / src / src / routers / redirect.c
index cd225d8e91f59e44b73c7df5fecabd8a75d62856..86278d818806823e460248f8d6732fefc0fa1ac8 100644 (file)
@@ -2,12 +2,15 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 
 #include "../exim.h"
+
+#ifdef ROUTER_REDIRECT /* Remainder of file */
 #include "rf_functions.h"
 #include "redirect.h"
 
@@ -89,17 +92,11 @@ optionlist redirect_router_options[] = {
       LOFF(bit_options) },
 
   { "sieve_enotify_mailto_owner", opt_stringptr, LOFF(sieve_enotify_mailto_owner) },
-  { "sieve_subaddress", opt_stringptr,         LOFF(sieve_subaddress) },
-  { "sieve_useraddress", opt_stringptr,                LOFF(sieve_useraddress) },
+  { "sieve_inbox",       opt_stringptr,        LOFF(sieve_inbox) },
+  { "sieve_subaddress",   opt_stringptr,       LOFF(sieve_subaddress) },
+  { "sieve_useraddress",  opt_stringptr,       LOFF(sieve_useraddress) },
   { "sieve_vacation_directory", opt_stringptr, LOFF(sieve_vacation_directory) },
   { "skip_syntax_errors", opt_bool,            LOFF(skip_syntax_errors) },
-#ifdef EXPERIMENTAL_SRS
-  { "srs",                opt_stringptr,       LOFF(srs) },
-  { "srs_alias",          opt_stringptr,       LOFF(srs_alias) },
-  { "srs_condition",      opt_stringptr,       LOFF(srs_condition) },
-  { "srs_dbinsert",       opt_stringptr,       LOFF(srs_dbinsert) },
-  { "srs_dbselect",       opt_stringptr,       LOFF(srs_dbselect) },
-#endif
   { "syntax_errors_text", opt_stringptr,       LOFF(syntax_errors_text) },
   { "syntax_errors_to",   opt_stringptr,       LOFF(syntax_errors_to) }
 };
@@ -115,7 +112,7 @@ int redirect_router_options_count =
 
 /* Dummy entries */
 redirect_router_options_block redirect_router_option_defaults = {0};
-void redirect_router_init(router_instance *rblock) {}
+void redirect_router_init(driver_instance *rblock) {}
 int redirect_router_entry(router_instance *rblock, address_item *addr,
   struct passwd *pw, int verify, address_item **addr_local,
   address_item **addr_remote, address_item **addr_new,
@@ -125,50 +122,14 @@ int redirect_router_entry(router_instance *rblock, address_item *addr,
 
 
 
-/* Default private options block for the redirect router. */
+/* Default private options block for the redirect router.
+Unlisted elements are 0/NULL/FALSE */
 
 redirect_router_options_block redirect_router_option_defaults = {
-  NULL,        /* directory_transport */
-  NULL,        /* file_transport */
-  NULL,        /* pipe_transport */
-  NULL,        /* reply_transport */
-  NULL,        /* data */
-  NULL,        /* directory_transport_name */
-  NULL,        /* file */
-  NULL,        /* file_dir */
-  NULL,        /* file_transport_name */
-  NULL,        /* include_directory */
-  NULL,        /* pipe_transport_name */
-  NULL,        /* reply_transport_name */
-  NULL,        /* sieve_subaddress */
-  NULL,        /* sieve_useraddress */
-  NULL,        /* sieve_vacation_directory */
-  NULL,        /* sieve_enotify_mailto_owner */
-  NULL,        /* syntax_errors_text */
-  NULL,        /* syntax_errors_to */
-  NULL,        /* qualify_domain */
-  NULL,        /* owners */
-  NULL,        /* owngroups */
-#ifdef EXPERIMENTAL_SRS
-  NULL,        /* srs */
-  NULL,        /* srs_alias */
-  NULL,        /* srs_condition */
-  NULL,        /* srs_dbinsert */
-  NULL,        /* srs_dbselect */
-#endif
-  022,         /* modemask */
-  RDO_REWRITE | RDO_PREPEND_HOME, /* bit_options */
-  FALSE,       /* check_ancestor */
-  TRUE_UNSET,  /* check_owner */
-  TRUE_UNSET,  /* check_group */
-  FALSE,       /* forbid_file */
-  FALSE,       /* forbid_filter_reply */
-  FALSE,       /* forbid_pipe */
-  FALSE,       /* forbid_smtp_code */
-  FALSE,       /* hide_child_in_errmsg */
-  FALSE,       /* one_time */
-  FALSE,       /* qualify_preserve_domain */
-  FALSE        /* skip_syntax_errors */
+  .modemask = 022,
+  .bit_options = RDO_REWRITE | RDO_PREPEND_HOME,
+  .check_owner = TRUE_UNSET,
+  .check_group = TRUE_UNSET,
 };
 
 
@@ -180,17 +141,19 @@ redirect_router_options_block redirect_router_option_defaults = {
 /* Called for each instance, after its options have been read, to enable
 consistency checks to be done, or anything else that needs to be set up. */
 
-void redirect_router_init(router_instance *rblock)
+void
+redirect_router_init(driver_instance * r)
 {
-redirect_router_options_block *ob =
-  (redirect_router_options_block *)(rblock->options_block);
+router_instance * rblock = (router_instance *)r;
+redirect_router_options_block * ob =
+  (redirect_router_options_block *)(r->options_block);
 
 /* Either file or data must be set, but not both */
 
 if ((ob->file == NULL) == (ob->data == NULL))
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n  "
     "%sone of \"file\" or \"data\" must be specified",
-    rblock->name, (ob->file == NULL)? "" : "only ");
+    r->name, ob->file ? "only " : "");
 
 /* Onetime aliases can only be real addresses. Headers can't be manipulated.
 The combination of one_time and unseen is not allowed. We can't check the
@@ -203,10 +166,10 @@ if (ob->one_time)
   if (rblock->extra_headers || rblock->remove_headers)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n  "
       "\"headers_add\" and \"headers_remove\" are not permitted with "
-      "\"one_time\"", rblock->name);
+      "\"one_time\"", r->name);
   if (rblock->unseen || rblock->expand_unseen)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n  "
-      "\"unseen\" may not be used with \"one_time\"", rblock->name);
+      "\"unseen\" may not be used with \"one_time\"", r->name);
   }
 
 /* The defaults for check_owner and check_group depend on other settings. The
@@ -227,7 +190,7 @@ if (ob->check_group == TRUE_UNSET)
 if (ob->qualify_domain && ob->qualify_preserve_domain)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n  "
     "only one of \"qualify_domain\" or \"qualify_preserve_domain\" must be set",
-    rblock->name);
+    r->name);
 
 /* If allow_filter is set, either user or check_local_user must be set. */
 
@@ -237,7 +200,7 @@ if (!rblock->check_local_user &&
     (ob->bit_options & RDO_FILTER) != 0)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n  "
     "\"user\" or \"check_local_user\" must be set with \"allow_filter\"",
-    rblock->name);
+    r->name);
 }
 
 
@@ -314,21 +277,20 @@ add_generated(router_instance *rblock, address_item **addr_new,
   address_item *addr, address_item *generated,
   address_item_propagated *addr_prop, ugid_block *ugidptr, struct passwd *pw)
 {
-redirect_router_options_block *ob =
-  (redirect_router_options_block *)(rblock->options_block);
+redirect_router_options_block * ob =
+  (redirect_router_options_block *)(rblock->drinst.options_block);
 
 while (generated)
   {
-  address_item *parent;
-  address_item *next = generated;
-  uschar *errors_address = next->prop.errors_address;
+  address_item * next = generated, * parent;
+  const uschar * errors_address = next->prop.errors_address;
 
   generated = next->next;
   next->parent = addr;
   next->start_router = rblock->redirect_router;
   if (addr->child_count == USHRT_MAX)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d "
-      "child addresses for <%s>", rblock->name, USHRT_MAX, addr->address);
+      "child addresses for <%s>", rblock->drinst.name, USHRT_MAX, addr->address);
   addr->child_count++;
 
   next->next = *addr_new;
@@ -384,12 +346,11 @@ while (generated)
     in \N...\N to avoid expansion later. In Cygwin, home directories can
     contain $ characters. */
 
-    if (rblock->home_directory != NULL)
+    if (rblock->home_directory)
       next->home_dir = rblock->home_directory;
     else if (rblock->check_local_user)
       next->home_dir = string_sprintf("\\N%s\\N", pw->pw_dir);
-    else if (rblock->router_home_directory != NULL &&
-             testflag(addr, af_home_expanded))
+    else if (rblock->router_home_directory && testflag(addr, af_home_expanded))
       {
       next->home_dir = deliver_home;
       setflag(next, af_home_expanded);
@@ -409,15 +370,17 @@ while (generated)
     if (next->address[0] == '|')
       {
       address_pipe = next->address;
-      if (rf_get_transport(ob->pipe_transport_name, &(ob->pipe_transport),
-          next, rblock->name, US"pipe_transport"))
+      GET_OPTION("pipe_transport");
+      if (rf_get_transport(ob->pipe_transport_name, &ob->pipe_transport,
+          next, rblock->drinst.name, US"pipe_transport"))
         next->transport = ob->pipe_transport;
       address_pipe = NULL;
       }
     else if (next->address[0] == '>')
       {
-      if (rf_get_transport(ob->reply_transport_name, &(ob->reply_transport),
-          next, rblock->name, US"reply_transport"))
+      GET_OPTION("reply_transport");
+      if (rf_get_transport(ob->reply_transport_name, &ob->reply_transport,
+          next, rblock->drinst.name, US"reply_transport"))
         next->transport = ob->reply_transport;
       }
     else  /* must be file or directory */
@@ -426,17 +389,20 @@ while (generated)
       address_file = next->address;
       if (next->address[len-1] == '/')
         {
+       GET_OPTION("directory_transport");
         if (rf_get_transport(ob->directory_transport_name,
-            &(ob->directory_transport), next, rblock->name,
+            &ob->directory_transport, next, rblock->drinst.name,
             US"directory_transport"))
           next->transport = ob->directory_transport;
         }
       else
-        {
-        if (rf_get_transport(ob->file_transport_name, &(ob->file_transport),
-            next, rblock->name, US"file_transport"))
+       {
+       GET_OPTION("file_transport");
+        if (rf_get_transport(ob->file_transport_name, &ob->file_transport,
+            next, rblock->drinst.name, US"file_transport"))
           next->transport = ob->file_transport;
-        }
+       }
+
       address_file = NULL;
       }
     }
@@ -450,11 +416,11 @@ while (generated)
   DEBUG(D_route)
     {
     debug_printf("%s router generated %s\n  %serrors_to=%s transport=%s\n",
-      rblock->name,
+      rblock->drinst.name,
       next->address,
       testflag(next, af_pfr)? "pipe, file, or autoreply\n  " : "",
       next->prop.errors_address,
-      (next->transport == NULL)? US"NULL" : next->transport->name);
+      next->transport ? next->transport->drinst.name : US"NULL");
 
     if (testflag(next, af_uid_set))
       debug_printf("  uid=%ld ", (long int)(next->uid));
@@ -514,8 +480,8 @@ int redirect_router_entry(
   address_item **addr_new,        /* put new addresses on here */
   address_item **addr_succeed)    /* put old address here on success */
 {
-redirect_router_options_block *ob =
-  (redirect_router_options_block *)(rblock->options_block);
+redirect_router_options_block * ob =
+  (redirect_router_options_block *)(rblock->drinst.options_block);
 address_item *generated = NULL;
 const uschar *save_qualify_domain_recipient = qualify_domain_recipient;
 uschar *discarded = US"discarded";
@@ -523,15 +489,13 @@ address_item_propagated addr_prop;
 error_block *eblock = NULL;
 ugid_block ugid;
 redirect_block redirect;
+sieve_block sieve;
 int filtertype = FILTER_UNSET;
 int yield = OK;
 int options = ob->bit_options;
 int frc = 0;
 int xrc = 0;
 
-addr_local = addr_local;     /* Keep picky compilers happy */
-addr_remote = addr_remote;
-
 /* Initialize the data to be propagated to the children */
 
 addr_prop.address_data = deliver_address_data;
@@ -543,9 +507,6 @@ addr_prop.remove_headers = NULL;
 addr_prop.variables = NULL;
 tree_dup((tree_node **)&addr_prop.variables, addr->prop.variables);
 
-#ifdef EXPERIMENTAL_SRS
-addr_prop.srs_sender = NULL;
-#endif
 #ifdef SUPPORT_I18N
 addr_prop.utf8_msg = addr->prop.utf8_msg;
 addr_prop.utf8_downcvt = addr->prop.utf8_downcvt;
@@ -566,107 +527,18 @@ can't be found in the password file. Other errors set the freezing bit. */
 
 if (!rf_get_ugid(rblock, addr, &ugid)) return DEFER;
 
-if (!ugid.uid_set && pw != NULL)
+if (!ugid.uid_set && pw)
   {
   ugid.uid = pw->pw_uid;
   ugid.uid_set = TRUE;
   }
 
-if (!ugid.gid_set && pw != NULL)
+if (!ugid.gid_set && pw)
   {
   ugid.gid = pw->pw_gid;
   ugid.gid_set = TRUE;
   }
 
-#ifdef EXPERIMENTAL_SRS
-  /* Perform SRS on recipient/return-path as required  */
-
-  if(ob->srs != NULL)
-  {
-    BOOL usesrs = TRUE;
-
-    if(ob->srs_condition != NULL)
-      usesrs = expand_check_condition(ob->srs_condition, "srs_condition expansion failed", NULL);
-
-    if(usesrs)
-    {
-      int srs_action = 0, n_srs;
-      uschar *res;
-      uschar *usedomain;
-
-      /* What are we doing? */
-      if(Ustrcmp(ob->srs, "forward") == 0)
-        srs_action = 1;
-      else if(Ustrcmp(ob->srs, "reverseandforward") == 0)
-      {
-        srs_action = 3;
-
-        if((ob->srs_dbinsert == NULL) ^ (ob->srs_dbselect == NULL))
-          return DEFER;
-      }
-      else if(Ustrcmp(ob->srs, "reverse") == 0)
-        srs_action = 2;
-
-      /* Reverse SRS */
-      if(srs_action & 2)
-      {
-        srs_orig_recipient = addr->address;
-
-        eximsrs_init();
-        if(ob->srs_dbselect)
-          eximsrs_db_set(TRUE, ob->srs_dbselect);
-/* Comment this out for now...
-//        else
-//          eximsrs_db_set(TRUE, NULL);
-*/
-
-        if((n_srs = eximsrs_reverse(&res, addr->address)) == OK)
-        {
-          srs_recipient = res;
-          DEBUG(D_any)
-            debug_printf("SRS (reverse): Recipient '%s' rewritten to '%s'\n", srs_orig_recipient, srs_recipient);
-        }
-
-        eximsrs_done();
-
-        if(n_srs != OK)
-          return n_srs;
-      }
-
-      /* Forward SRS */
-      /* No point in actually performing SRS if we are just verifying a recipient */
-      if((srs_action & 1) && verify == v_none &&
-         (sender_address ? sender_address[0] != 0 : FALSE))
-      {
-
-        srs_orig_sender = sender_address;
-        eximsrs_init();
-        if(ob->srs_dbinsert)
-          eximsrs_db_set(FALSE, ob->srs_dbinsert);
-/* Comment this out for now...
-//        else
-//          eximsrs_db_set(FALSE, NULL);
-*/
-
-        if (!(usedomain = ob->srs_alias ? expand_string(ob->srs_alias) : NULL))
-          usedomain = string_copy(deliver_domain);
-
-        if((n_srs = eximsrs_forward(&res, sender_address, usedomain)) == OK)
-        {
-          addr_prop.srs_sender = res;
-          DEBUG(D_any)
-            debug_printf("SRS (forward): Sender '%s' rewritten to '%s'\n", srs_orig_sender, res);
-        }
-
-        eximsrs_done();
-
-        if(n_srs != OK)
-          return n_srs;
-      }
-    }
-  }
-#endif
-
 /* Call the function that interprets redirection data, either inline or from a
 file. This is a separate function so that the system filter can use it. It will
 run the function in a subprocess if necessary. If qualify_preserve_domain is
@@ -676,11 +548,15 @@ address. Otherwise, if a local qualify_domain is provided, set that up. */
 
 if (ob->qualify_preserve_domain)
   qualify_domain_recipient = addr->domain;
-else if (ob->qualify_domain)
+else
   {
-  uschar *new_qdr = rf_expand_data(addr, ob->qualify_domain, &xrc);
-  if (!new_qdr) return xrc;
-  qualify_domain_recipient = new_qdr;
+  GET_OPTION("qualify_domain");
+  if (ob->qualify_domain)
+    {
+    uschar *new_qdr = rf_expand_data(addr, ob->qualify_domain, &xrc);
+    if (!new_qdr) return xrc;
+    qualify_domain_recipient = new_qdr;
+    }
   }
 
 redirect.owners = ob->owners;
@@ -693,11 +569,16 @@ redirect.pw = pw;
 redirect.string = (redirect.isfile = (ob->file != NULL))
   ? ob->file : ob->data;
 
-frc = rda_interpret(&redirect, options, ob->include_directory,
-  ob->sieve_vacation_directory, ob->sieve_enotify_mailto_owner,
-  ob->sieve_useraddress, ob->sieve_subaddress, &ugid, &generated,
-  &(addr->message), ob->skip_syntax_errors? &eblock : NULL, &filtertype,
-  string_sprintf("%s router (recipient is %s)", rblock->name, addr->address));
+sieve.inbox = ob->sieve_inbox;
+sieve.subaddress = ob->sieve_subaddress;
+sieve.vacation_dir = ob->sieve_vacation_directory;
+sieve.useraddress = ob->sieve_useraddress;
+sieve.enotify_mailto_owner = ob->sieve_enotify_mailto_owner;
+
+frc = rda_interpret(&redirect, options, ob->include_directory, &sieve, &ugid,
+  &generated, &addr->message, ob->skip_syntax_errors ? &eblock : NULL,
+  &filtertype, string_sprintf("%s router (recipient is %s)",
+  rblock->drinst.name, addr->address));
 
 qualify_domain_recipient = save_qualify_domain_recipient;
 
@@ -737,15 +618,13 @@ switch (frc)
       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);
@@ -787,7 +666,7 @@ switch (frc)
 
     if (filtertype != FILTER_FORWARD && ob->skip_syntax_errors)
       {
-      eblock = store_get(sizeof(error_block), FALSE);
+      eblock = store_get(sizeof(error_block), GET_UNTAINTED);
       eblock->next = NULL;
       eblock->text1 = addr->message;
       eblock->text2 = NULL;
@@ -824,7 +703,7 @@ dealing with it, the router declines. */
 if (eblock != NULL)
   {
   if (!moan_skipped_syntax_errors(
-        rblock->name,                            /* For message content */
+        rblock->drinst.name,                    /* For message content */
         eblock,                                  /* Ditto */
         (verify != v_none || f.address_test_mode)?
           NULL : ob->syntax_errors_to,           /* Who to mail */
@@ -832,7 +711,7 @@ if (eblock != NULL)
         ob->syntax_errors_text))                 /* Custom message */
     return DEFER;
 
-  if (filtertype != FILTER_FORWARD || generated == NULL)
+  if (filtertype != FILTER_FORWARD || !generated)
     {
     addr->message = US"syntax error in redirection data";
     return DECLINE;
@@ -845,7 +724,7 @@ calling sort_errors_and_headers() in case this router declines - that function
 may modify the errors_address field in the current address, and we don't want
 to do that for a decline. */
 
-if (generated != NULL)
+if (generated)
   {
   if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) != OK)
     return xrc;
@@ -861,7 +740,7 @@ if (frc == FF_DELIVERED)
   if (generated == NULL && verify == v_none && !f.address_test_mode)
     {
     log_write(0, LOG_MAIN, "=> %s <%s> R=%s", discarded, addr->address,
-      rblock->name);
+      rblock->drinst.name);
     yield = DISCARD;
     }
   }
@@ -892,7 +771,7 @@ else
   next->prop = addr_prop;
 
   DEBUG(D_route) debug_printf("%s router autogenerated %s\n%s%s%s",
-    rblock->name,
+    rblock->drinst.name,
     next->address,
     (addr_prop.errors_address != NULL)? "  errors to " : "",
     (addr_prop.errors_address != NULL)? addr_prop.errors_address : US"",
@@ -909,5 +788,30 @@ addr->next = *addr_succeed;
 return yield;
 }
 
+
+
+# ifdef DYNLOOKUP
+#  define redirect_router_info _router_info
+# endif
+
+router_info redirect_router_info =
+{
+.drinfo = {
+  .driver_name =       US"redirect",
+  .options =           redirect_router_options,
+  .options_count =     &redirect_router_options_count,
+  .options_block =     &redirect_router_option_defaults,
+  .options_len =       sizeof(redirect_router_options_block),
+  .init =              redirect_router_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         ROUTER_MAGIC,
+# endif
+  },
+.code =                        redirect_router_entry,
+.tidyup =              NULL,     /* no tidyup entry */
+.ri_flags =            ri_notransport
+};
+
 #endif   /*!MACRO_PREDEF*/
+#endif /*ROUTER_REDIRECT*/
 /* End of routers/redirect.c */