PRDR support, if compiled with EXPERIMENTAL_PRDR
[exim.git] / src / src / acl.c
index 3b23a915b5aaa8fcd89812df1203a7d9e05e25e3..f61d2dfdfe560ec04eab4711533a1375ee4c4e16 100644 (file)
@@ -368,6 +368,9 @@ static unsigned int cond_forbids[] = {
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* add_header */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif
     (1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
     (1<<ACL_WHERE_DKIM)|
     (1<<ACL_WHERE_NOTSMTP_START)),
@@ -380,6 +383,9 @@ static unsigned int cond_forbids[] = {
   (1<<ACL_WHERE_AUTH)|                             /* bmi_optin */
     (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
     (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_MIME)|
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif
     (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
     (1<<ACL_WHERE_MAILAUTH)|
     (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
@@ -398,7 +404,11 @@ static unsigned int cond_forbids[] = {
 
   #ifdef EXPERIMENTAL_DCC
   (unsigned int)
-  ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)),   /* dcc */
+  ~((1<<ACL_WHERE_DATA)|                           /* dcc */
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif /* EXPERIMENTAL_PRDR */
+    (1<<ACL_WHERE_NOTSMTP)),
   #endif
 
   #ifdef WITH_CONTENT_SCAN
@@ -410,7 +420,11 @@ static unsigned int cond_forbids[] = {
 
   #ifdef WITH_OLD_DEMIME
   (unsigned int)
-  ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)),   /* demime */
+  ~((1<<ACL_WHERE_DATA)|                           /* demime */
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif /* EXPERIMENTAL_PRDR */
+    (1<<ACL_WHERE_NOTSMTP)),
   #endif
 
   #ifndef DISABLE_DKIM
@@ -425,7 +439,11 @@ static unsigned int cond_forbids[] = {
     (1<<ACL_WHERE_NOTSMTP_START),
 
   (unsigned int)
-  ~(1<<ACL_WHERE_RCPT),                            /* domains */
+  ~((1<<ACL_WHERE_RCPT)                            /* domains */
+  #ifdef EXPERIMENTAL_PRDR
+    |(1<<ACL_WHERE_PRDR)
+  #endif
+    ),
 
   (1<<ACL_WHERE_NOTSMTP)|                          /* encrypted */
     (1<<ACL_WHERE_CONNECT)|
@@ -438,7 +456,11 @@ static unsigned int cond_forbids[] = {
     (1<<ACL_WHERE_NOTSMTP_START),
 
   (unsigned int)
-  ~(1<<ACL_WHERE_RCPT),                            /* local_parts */
+  ~((1<<ACL_WHERE_RCPT)                             /* local_parts */
+  #ifdef EXPERIMENTAL_PRDR
+    |(1<<ACL_WHERE_PRDR)
+  #endif
+    ),
 
   0,                                               /* log_message */
 
@@ -448,7 +470,11 @@ static unsigned int cond_forbids[] = {
 
   #ifdef WITH_CONTENT_SCAN
   (unsigned int)
-  ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)),   /* malware */
+  ~((1<<ACL_WHERE_DATA)|                           /* malware */
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif /* EXPERIMENTAL_PRDR */
+    (1<<ACL_WHERE_NOTSMTP)),
   #endif
 
   0,                                               /* message */
@@ -465,13 +491,20 @@ static unsigned int cond_forbids[] = {
 
   #ifdef WITH_CONTENT_SCAN
   (unsigned int)
-  ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)|    /* regex */
+  ~((1<<ACL_WHERE_DATA)|                           /* regex */
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif /* EXPERIMENTAL_PRDR */
+    (1<<ACL_WHERE_NOTSMTP)|
     (1<<ACL_WHERE_MIME)),
   #endif
 
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* remove_header */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif
     (1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
     (1<<ACL_WHERE_NOTSMTP_START)),
 
@@ -491,7 +524,11 @@ static unsigned int cond_forbids[] = {
 
   #ifdef WITH_CONTENT_SCAN
   (unsigned int)
-  ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)),   /* spam */
+  ~((1<<ACL_WHERE_DATA)|                           /* spam */
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif /* EXPERIMENTAL_PRDR */
+    (1<<ACL_WHERE_NOTSMTP)),
   #endif
 
   #ifdef EXPERIMENTAL_SPF
@@ -535,6 +572,9 @@ static unsigned int control_forbids[] = {
 
   #ifndef DISABLE_DKIM
   (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)|      /* dkim_disable_verify */
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif /* EXPERIMENTAL_PRDR */
     (1<<ACL_WHERE_NOTSMTP_START),
   #endif
 
@@ -562,11 +602,13 @@ static unsigned int control_forbids[] = {
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* freeze */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+    // (1<<ACL_WHERE_PRDR)|    /* Not allow one user to freeze for all */
     (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_MIME)),
 
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* queue_only */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+    // (1<<ACL_WHERE_PRDR)|    /* Not allow one user to freeze for all */
     (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_MIME)),
 
   (unsigned int)
@@ -582,17 +624,24 @@ static unsigned int control_forbids[] = {
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* no_mbox_unspool */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+    // (1<<ACL_WHERE_PRDR)|    /* Not allow one user to freeze for all */
     (1<<ACL_WHERE_MIME)),
   #endif
 
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* fakedefer */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif /* EXPERIMENTAL_PRDR */
     (1<<ACL_WHERE_MIME)),
 
   (unsigned int)
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* fakereject */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+  #ifdef EXPERIMENTAL_PRDR
+    (1<<ACL_WHERE_PRDR)|
+  #endif /* EXPERIMENTAL_PRDR */
     (1<<ACL_WHERE_MIME)),
 
   (1<<ACL_WHERE_NOTSMTP)|                          /* no_multiline */
@@ -958,10 +1007,13 @@ setup_header(uschar *hstring)
 uschar *p, *q;
 int hlen = Ustrlen(hstring);
 
-/* An empty string does nothing; otherwise add a final newline if necessary. */
+/* Ignore any leading newlines */
+while (*hstring == '\n') hstring++, hlen--;
 
+/* An empty string does nothing; ensure exactly one final newline. */
 if (hlen <= 0) return;
-if (hstring[hlen-1] != '\n') hstring = string_sprintf("%s\n", hstring);
+if (hstring[--hlen] != '\n') hstring = string_sprintf("%s\n", hstring);
+else while(hstring[--hlen] == '\n') hstring[hlen+1] = '\0';
 
 /* Loop for multiple header lines, taking care about continuations */
 
@@ -1047,6 +1099,44 @@ for (p = q = hstring; *p != 0; )
 
 
 
+/*************************************************
+*        List the added header lines            *
+*************************************************/
+uschar *
+fn_hdrs_added(void)
+{
+uschar * ret = NULL;
+header_line * h = acl_added_headers;
+uschar * s;
+uschar * cp;
+int size = 0;
+int ptr = 0;
+
+if (!h) return NULL;
+
+do
+  {
+  s = h->text;
+  while ((cp = Ustrchr(s, '\n')) != NULL)
+    {
+    if (cp[1] == '\0') break;
+
+    /* contains embedded newline; needs doubling */
+    ret = string_cat(ret, &size, &ptr, s, cp-s+1);
+    ret = string_cat(ret, &size, &ptr, US"\n", 1);
+    s = cp+1;
+    }
+  /* last bit of header */
+
+  ret = string_cat(ret, &size, &ptr, s, cp-s+1);       /* newline-sep list */
+  }
+while((h = h->next));
+
+ret[ptr-1] = '\0';     /* overwrite last newline */
+return ret;
+}
+
+
 /*************************************************
 *        Set up removed header line(s)           *
 *************************************************/
@@ -3913,8 +4003,11 @@ acl_check_wargs(int where, address_item *addr, uschar *s, int level,
 {
 uschar * tmp;
 uschar * tmp_arg[9];   /* must match acl_arg[] */
+uschar * sav_arg[9];   /* must match acl_arg[] */
+int sav_narg;
 uschar * name;
 int i;
+int ret;
 
 if (!(tmp = string_dequote(&s)) || !(name = expand_string(tmp)))
   goto bad;
@@ -3929,11 +4022,25 @@ for (i = 0; i < 9; i++)
     goto bad;
     }
   }
+
+sav_narg = acl_narg;
 acl_narg = i;
-for (i = 0; i < acl_narg; i++) acl_arg[i] = tmp_arg[i];
-while (i < 9) acl_arg[i++] = NULL;
+for (i = 0; i < acl_narg; i++)
+  {
+  sav_arg[i] = acl_arg[i];
+  acl_arg[i] = tmp_arg[i];
+  }
+while (i < 9)
+  {
+  sav_arg[i] = acl_arg[i];
+  acl_arg[i++] = NULL;
+  }
 
-return acl_check_internal(where, addr, name, level, user_msgptr, log_msgptr);
+ret = acl_check_internal(where, addr, name, level, user_msgptr, log_msgptr);
+
+acl_narg = sav_narg;
+for (i = 0; i < 9; i++) acl_arg[i] = sav_arg[i];
+return ret;
 
 bad:
 if (expand_string_forcedfail) return ERROR;
@@ -3948,6 +4055,34 @@ return search_find_defer?DEFER:ERROR;
 *        Check access using an ACL               *
 *************************************************/
 
+/* Alternate interface for ACL, used by expansions */
+int
+acl_eval(int where, uschar *s, uschar **user_msgptr, uschar **log_msgptr)
+{
+address_item adb;
+address_item *addr = NULL;
+
+*user_msgptr = *log_msgptr = NULL;
+sender_verified_failed = NULL;
+ratelimiters_cmd = NULL;
+log_reject_target = LOG_MAIN|LOG_REJECT;
+
+if (where == ACL_WHERE_RCPT)
+  {
+  adb = address_defaults;
+  addr = &adb;
+  addr->address = expand_string(US"$local_part@$domain");
+  addr->domain = deliver_domain;
+  addr->local_part = deliver_localpart;
+  addr->cc_local_part = deliver_localpart;
+  addr->lc_local_part = deliver_localpart;
+  }
+
+return acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr);
+}
+
+
+
 /* This is the external interface for ACL checks. It sets up an address and the
 expansions for $domain and $local_part when called after RCPT, then calls
 acl_check_internal() to do the actual work.
@@ -3966,6 +4101,7 @@ Returns:       OK         access is granted by an ACCEPT verb
                DEFER      can't tell at the moment
                ERROR      disaster
 */
+int acl_where = ACL_WHERE_UNKNOWN;
 
 int
 acl_check(int where, uschar *recipient, uschar *s, uschar **user_msgptr,
@@ -3980,7 +4116,11 @@ sender_verified_failed = NULL;
 ratelimiters_cmd = NULL;
 log_reject_target = LOG_MAIN|LOG_REJECT;
 
-if (where == ACL_WHERE_RCPT)
+#ifdef EXPERIMENTAL_PRDR
+if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR )
+#else
+if (where == ACL_WHERE_RCPT )
+#endif
   {
   adb = address_defaults;
   addr = &adb;
@@ -3994,7 +4134,9 @@ if (where == ACL_WHERE_RCPT)
   deliver_localpart = addr->local_part;
   }
 
+acl_where = where;
 rc = acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr);
+acl_where = ACL_WHERE_UNKNOWN;
 
 /* Cutthrough - if requested,
 and WHERE_RCPT and not yet opened conn as result of recipient-verify,
@@ -4018,6 +4160,9 @@ If conn-failure, no action (and keep the spooled copy).
 switch (where)
 {
 case ACL_WHERE_RCPT:
+#ifdef EXPERIMENTAL_PRDR
+case ACL_WHERE_PRDR:
+#endif
   if( rcpt_count > 1 )
     cancel_cutthrough_connection("more than one recipient");
   else if (rc == OK  &&  cutthrough_delivery  &&  cutthrough_fd < 0)
@@ -4077,7 +4222,6 @@ return rc;
 }
 
 
-
 /*************************************************
 *             Create ACL variable                *
 *************************************************/