Bug 1031: Experimental TPDA
[exim.git] / src / src / transport.c
index e68a24dcbe7a4d313fdc4db112e132fd6a240217..d2540be620531df975b39f85a4150d2f1cfa408f 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/transport.c,v 1.26 2009/12/15 08:23:15 tom Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2012 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* General functions concerned with transportation, and generic options for all
@@ -70,11 +68,11 @@ optionlist optionlist_transports[] = {
                  (void *)(offsetof(transport_instance, envelope_to_add)) },
   { "group",             opt_expand_gid|opt_public,
                  (void *)offsetof(transport_instance, gid) },
-  { "headers_add",      opt_stringptr|opt_public,
+  { "headers_add",      opt_stringptr|opt_public|opt_rep_str,
                  (void *)offsetof(transport_instance, add_headers) },
   { "headers_only",     opt_bool|opt_public,
                  (void *)offsetof(transport_instance, headers_only) },
-  { "headers_remove",   opt_stringptr|opt_public,
+  { "headers_remove",   opt_stringptr|opt_public|opt_rep_str,
                  (void *)offsetof(transport_instance, remove_headers) },
   { "headers_rewrite",  opt_rewrite|opt_public,
                  (void *)offsetof(transport_instance, headers_rewrite) },
@@ -96,6 +94,10 @@ optionlist optionlist_transports[] = {
                  (void *)offsetof(transport_instance, shadow_condition) },
   { "shadow_transport", opt_stringptr|opt_public,
                  (void *)offsetof(transport_instance, shadow) },
+#ifdef EXPERIMENTAL_TPDA
+  { "tpda_delivery_action",opt_stringptr | opt_public,
+                 (void *)offsetof(transport_instance, tpda_delivery_action) },
+#endif
   { "transport_filter", opt_stringptr|opt_public,
                  (void *)offsetof(transport_instance, filter_command) },
   { "transport_filter_timeout", opt_time|opt_public,
@@ -221,7 +223,7 @@ for (i = 0; i < 100; i++)
   if (transport_write_timeout <= 0)   /* No timeout wanted */
     {
     #ifdef SUPPORT_TLS
-    if (tls_active == fd) rc = tls_write(block, len); else
+    if (tls_out.active == fd) rc = tls_write(FALSE, block, len); else
     #endif
     rc = write(fd, block, len);
     save_errno = errno;
@@ -233,7 +235,7 @@ for (i = 0; i < 100; i++)
     {
     alarm(local_timeout);
     #ifdef SUPPORT_TLS
-    if (tls_active == fd) rc = tls_write(block, len); else
+    if (tls_out.active == fd) rc = tls_write(FALSE, block, len); else
     #endif
     rc = write(fd, block, len);
     save_errno = errno;
@@ -324,7 +326,7 @@ Returns:      the yield of transport_write_block()
 */
 
 BOOL
-transport_write_string(int fd, char *format, ...)
+transport_write_string(int fd, const char *format, ...)
 {
 va_list ap;
 va_start(ap, format);
@@ -426,6 +428,7 @@ for (ptr = start; ptr < end; ptr++)
 
     if (use_crlf) *chunk_ptr++ = '\r';
     *chunk_ptr++ = '\n';
+    transport_newlines++;
 
     /* The check_string test (formerly "from hack") replaces the specific
     string at the start of a line with an escape string (e.g. "From " becomes
@@ -920,19 +923,19 @@ if ((options & topt_no_body) == 0)
       }
     }
 
-  /* Finished with the check string */
-
-  nl_check_length = nl_escape_length = 0;
-
   /* A read error on the body will have left len == -1 and errno set. */
 
   if (len != 0) return FALSE;
+  }
 
-  /* If requested, add a terminating "." line (SMTP output). */
+/* Finished with the check string */
 
-  if ((options & topt_end_dot) != 0 && !write_chunk(fd, US".\n", 2, use_crlf))
-    return FALSE;
-  }
+nl_check_length = nl_escape_length = 0;
+
+/* If requested, add a terminating "." line (SMTP output). */
+
+if ((options & topt_end_dot) != 0 && !write_chunk(fd, US".\n", 2, use_crlf))
+  return FALSE;
 
 /* Write out any remaining data in the buffer before returning. */
 
@@ -1047,7 +1050,7 @@ dkim_transport_write_message(address_item *addr, int fd, int options,
       int siglen = Ustrlen(dkim_signature);
       while(siglen > 0) {
         #ifdef SUPPORT_TLS
-        if (tls_active == fd) wwritten = tls_write(dkim_signature, siglen); else
+        if (tls_out.active == fd) wwritten = tls_write(FALSE, dkim_signature, siglen); else
         #endif
         wwritten = write(fd,dkim_signature,siglen);
         if (wwritten == -1) {
@@ -1073,7 +1076,7 @@ dkim_transport_write_message(address_item *addr, int fd, int options,
      to the socket. However only if we don't use TLS,
      in which case theres another layer of indirection
      before the data finally hits the socket. */
-  if (tls_active != fd)
+  if (tls_out.active != fd)
     {
     ssize_t copied = 0;
     off_t offset = 0;
@@ -1097,7 +1100,7 @@ dkim_transport_write_message(address_item *addr, int fd, int options,
     /* write the chunk */
     DKIM_WRITE:
     #ifdef SUPPORT_TLS
-    if (tls_active == fd) wwritten = tls_write(US p, sread); else
+    if (tls_out.active == fd) wwritten = tls_write(FALSE, US p, sread); else
     #endif
     wwritten = write(fd,p,sread);
     if (wwritten == -1)
@@ -1227,9 +1230,14 @@ if ((write_pid = fork()) == 0)
     size_limit, add_headers, remove_headers, NULL, NULL,
     rewrite_rules, rewrite_existflags);
   save_errno = errno;
-  (void)write(pfd[pipe_write], (void *)&rc, sizeof(BOOL));
-  (void)write(pfd[pipe_write], (void *)&save_errno, sizeof(int));
-  (void)write(pfd[pipe_write], (void *)&(addr->more_errno), sizeof(int));
+  if (  write(pfd[pipe_write], (void *)&rc, sizeof(BOOL))
+        != sizeof(BOOL)
+     || write(pfd[pipe_write], (void *)&save_errno, sizeof(int))
+        != sizeof(int)
+     || write(pfd[pipe_write], (void *)&(addr->more_errno), sizeof(int))
+        != sizeof(int)
+     )
+    rc = FALSE;        /* compiler quietening */
   _exit(0);
   }
 save_errno = errno;
@@ -1340,11 +1348,11 @@ if (write_pid > 0)
     if (rc == 0)
       {
       BOOL ok;
-      (void)read(pfd[pipe_read], (void *)&ok, sizeof(BOOL));
+      int dummy = read(pfd[pipe_read], (void *)&ok, sizeof(BOOL));
       if (!ok)
         {
-        (void)read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
-        (void)read(pfd[pipe_read], (void *)&(addr->more_errno), sizeof(int));
+        dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
+        dummy = read(pfd[pipe_read], (void *)&(addr->more_errno), sizeof(int));
         yield = FALSE;
         }
       }
@@ -1993,7 +2001,122 @@ if (expand_arguments)
         memmove(argv + i + 1 + additional, argv + i + 1,
           (argcount - i)*sizeof(uschar *));
 
-      for (ad = addr; ad != NULL; ad = ad->next) argv[i++] = ad->address;
+      for (ad = addr; ad != NULL; ad = ad->next) {
+          argv[i++] = ad->address;
+          argcount++;
+      }
+
+      /* Subtract one since we replace $pipe_addresses */
+      argcount--;
+      i--;
+      }
+
+      /* Handle special case of $address_pipe when af_force_command is set */
+
+    else if (addr != NULL && testflag(addr,af_force_command) &&
+        (Ustrcmp(argv[i], "$address_pipe") == 0 ||
+         Ustrcmp(argv[i], "${address_pipe}") == 0))
+      {
+      int address_pipe_i;
+      int address_pipe_argcount = 0;
+      int address_pipe_max_args;
+      uschar **address_pipe_argv;
+
+      /* We can never have more then the argv we will be loading into */
+      address_pipe_max_args = max_args - argcount + 1;
+
+      DEBUG(D_transport)
+        debug_printf("address_pipe_max_args=%d\n", address_pipe_max_args);
+
+      /* We allocate an additional for (uschar *)0 */
+      address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *));
+
+      /* +1 because addr->local_part[0] == '|' since af_force_command is set */
+      s = expand_string(addr->local_part + 1);
+
+      if (s == NULL || *s == '\0')
+        {
+        addr->transport_return = FAIL;
+        addr->message = string_sprintf("Expansion of \"%s\" "
+           "from command \"%s\" in %s failed: %s",
+           (addr->local_part + 1), cmd, etext, expand_string_message);
+        return FALSE;
+        }
+
+      while (isspace(*s)) s++; /* strip leading space */
+
+      while (*s != 0 && address_pipe_argcount < address_pipe_max_args)
+        {
+        if (*s == '\'')
+          {
+          ss = s + 1;
+          while (*ss != 0 && *ss != '\'') ss++;
+          address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++);
+          while (*s != 0 && *s != '\'') *ss++ = *s++;
+          if (*s != 0) s++;
+          *ss++ = 0;
+          }
+        else address_pipe_argv[address_pipe_argcount++] = string_dequote(&s);
+        while (isspace(*s)) s++; /* strip space after arg */
+        }
+
+      address_pipe_argv[address_pipe_argcount] = (uschar *)0;
+
+      /* If *s != 0 we have run out of argument slots. */
+      if (*s != 0)
+        {
+        uschar *msg = string_sprintf("Too many arguments in $address_pipe "
+          "\"%s\" in %s", addr->local_part + 1, etext);
+        if (addr != NULL)
+          {
+          addr->transport_return = FAIL;
+          addr->message = msg;
+          }
+        else *errptr = msg;
+        return FALSE;
+        }
+
+      /* address_pipe_argcount - 1
+       * because we are replacing $address_pipe in the argument list
+       * with the first thing it expands to */
+      if (argcount + address_pipe_argcount - 1 > max_args)
+        {
+        addr->transport_return = FAIL;
+        addr->message = string_sprintf("Too many arguments to command "
+          "\"%s\" after expanding $address_pipe in %s", cmd, etext);
+        return FALSE;
+        }
+
+      /* If we are not just able to replace the slot that contained
+       * $address_pipe (address_pipe_argcount == 1)
+       * We have to move the existing argv by address_pipe_argcount - 1
+       * Visually if address_pipe_argcount == 2:
+       * [argv 0][argv 1][argv 2($address_pipe)][argv 3][0]
+       * [argv 0][argv 1][ap_arg0][ap_arg1][old argv 3][0]
+       */
+      if (address_pipe_argcount > 1)
+        memmove(
+          /* current position + additonal args */
+          argv + i + address_pipe_argcount,
+          /* current position + 1 (for the (uschar *)0 at the end) */
+          argv + i + 1,
+          /* -1 for the (uschar *)0 at the end)*/
+          (argcount - i)*sizeof(uschar *)
+        );
+
+      /* Now we fill in the slots we just moved argv out of
+       * [argv 0][argv 1][argv 2=pipeargv[0]][argv 3=pipeargv[1]][old argv 3][0]
+       */
+      for (address_pipe_i = 0;
+           address_pipe_argv[address_pipe_i] != (uschar *)0;
+           address_pipe_i++)
+        {
+        argv[i++] = address_pipe_argv[address_pipe_i];
+        argcount++;
+        }
+
+      /* Subtract one since we replace $address_pipe */
+      argcount--;
       i--;
       }