Remove one case of BASE64 error detection FTTB (undocumented anyway)
[exim.git] / src / src / transport.c
index 1bdb677464f8458262211489778b02fd59ade7e2..e66e498be1e70f8ed6e2cc17e0d6f281ba7fd66c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/transport.c,v 1.5 2005/03/08 15:32:02 tom Exp $ */
+/* $Cambridge: exim/src/src/transport.c,v 1.13 2005/08/01 14:41:25 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -199,26 +199,42 @@ BOOL
 transport_write_block(int fd, uschar *block, int len)
 {
 int i, rc, save_errno;
+int local_timeout = transport_write_timeout;
+
+/* This loop is for handling incomplete writes and other retries. In most
+normal cases, it is only ever executed once. */
 
 for (i = 0; i < 100; i++)
   {
   DEBUG(D_transport)
     debug_printf("writing data block fd=%d size=%d timeout=%d\n",
-      fd, len, transport_write_timeout);
-  if (transport_write_timeout > 0) alarm(transport_write_timeout);
+      fd, len, local_timeout);
 
-  #ifdef SUPPORT_TLS
-  if (tls_active == fd) rc = tls_write(block, len); else
-  #endif
+  /* This code makes use of alarm() in order to implement the timeout. This
+  isn't a very tidy way of doing things. Using non-blocking I/O with select()
+  provides a neater approach. However, I don't know how to do this when TLS is
+  in use. */
 
-  rc = write(fd, block, len);
-  save_errno = errno;
+  if (transport_write_timeout <= 0)   /* No timeout wanted */
+    {
+    #ifdef SUPPORT_TLS
+    if (tls_active == fd) rc = tls_write(block, len); else
+    #endif
+    rc = write(fd, block, len);
+    save_errno = errno;
+    }
 
-  /* Cancel the alarm and deal with a timeout */
+  /* Timeout wanted. */
 
-  if (transport_write_timeout > 0)
+  else
     {
-    alarm(0);
+    alarm(local_timeout);
+    #ifdef SUPPORT_TLS
+    if (tls_active == fd) rc = tls_write(block, len); else
+    #endif
+    rc = write(fd, block, len);
+    save_errno = errno;
+    local_timeout = alarm(0);
     if (sigalrm_seen)
       {
       errno = ETIMEDOUT;
@@ -230,7 +246,8 @@ for (i = 0; i < 100; i++)
 
   if (rc == len) { transport_count += len; return TRUE; }
 
-  /* A non-negative return code is an incomplete write. Try again. */
+  /* A non-negative return code is an incomplete write. Try again for the rest
+  of the block. If we have exactly hit the timeout, give up. */
 
   if (rc >= 0)
     {
@@ -238,7 +255,7 @@ for (i = 0; i < 100; i++)
     block += rc;
     transport_count += rc;
     DEBUG(D_transport) debug_printf("write incomplete (%d)\n", rc);
-    continue;
+    goto CHECK_TIMEOUT;   /* A few lines below */
     }
 
   /* A negative return code with an EINTR error is another form of
@@ -248,7 +265,7 @@ for (i = 0; i < 100; i++)
     {
     DEBUG(D_transport)
       debug_printf("write interrupted before anything written\n");
-    continue;
+    goto CHECK_TIMEOUT;   /* A few lines below */
     }
 
   /* A response of EAGAIN from write() is likely only in the case of writing
@@ -259,6 +276,16 @@ for (i = 0; i < 100; i++)
     DEBUG(D_transport)
       debug_printf("write temporarily locked out, waiting 1 sec\n");
     sleep(1);
+
+    /* Before continuing to try another write, check that we haven't run out of
+    time. */
+
+    CHECK_TIMEOUT:
+    if (transport_write_timeout > 0 && local_timeout <= 0)
+      {
+      errno = ETIMEDOUT;
+      return FALSE;
+      }
     continue;
     }
 
@@ -797,7 +824,10 @@ if ((options & topt_no_headers) == 0)
   same alias might share some of them) but we want to output them in the
   opposite order. This is a bit tedious, but there shouldn't be very many
   of them. We just walk the list twice, reversing the pointers each time,
-  but on the second time, write out the items. */
+  but on the second time, write out the items.
+
+  Headers added to an address by a router are guaranteed to end with a newline.
+  */
 
   if (addr != NULL)
     {
@@ -824,7 +854,8 @@ if ((options & topt_no_headers) == 0)
   /* If a string containing additional headers exists, expand it and write
   out the result. This is done last so that if it (deliberately or accidentally)
   isn't in header format, it won't mess up any other headers. An empty string
-  or a forced expansion failure are noops. */
+  or a forced expansion failure are noops. An added header string from a
+  transport may not end with a newline; add one if it does not. */
 
   if (add_headers != NULL)
     {
@@ -846,7 +877,11 @@ if ((options & topt_no_headers) == 0)
         if (s[len-1] != '\n' && !write_chunk(fd, US"\n", 1, use_crlf))
           return FALSE;
         DEBUG(D_transport)
-          debug_printf("added header line(s):\n%s---\n", s);
+          {
+          debug_printf("added header line(s):\n%s", s);
+          if (s[len-1] != '\n') debug_printf("\n");
+          debug_printf("---\n");
+          }
         }
       }
     }
@@ -920,7 +955,7 @@ return (len = chunk_ptr - deliver_out_buffer) <= 0 ||
    signed message down the original fd (or TLS fd).
 
 Arguments:     as for internal_transport_write_message() above, with additional
-               arguments: 
+               arguments:
                uschar *dk_private_key         The private key to use (filename or plain data)
                uschar *dk_domain              Override domain (normally NULL)
                uschar *dk_selector            The selector to use.
@@ -948,8 +983,8 @@ dk_transport_write_message(address_item *addr, int fd, int options,
   int sread = 0;
   int wwritten = 0;
   uschar *dk_signature = NULL;
-  
-  snprintf(CS dk_spool_name, 256, "%s/input/%s/%s-K",
+
+  (void)string_format(dk_spool_name, 256, "%s/input/%s/%s-K",
           spool_directory, message_subdir, message_id);
   dk_fd = Uopen(dk_spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE);
   if (dk_fd < 0)
@@ -959,13 +994,13 @@ dk_transport_write_message(address_item *addr, int fd, int options,
     save_errno = errno;
     goto CLEANUP;
     }
-  
+
   /* Call original function */
   rc = transport_write_message(addr, dk_fd, options,
     size_limit, add_headers, remove_headers,
     check_string, escape_string, rewrite_rules,
     rewrite_existflags);
-  
+
   /* Save error state. We must clean up before returning. */
   if (!rc)
     {
@@ -980,7 +1015,7 @@ dk_transport_write_message(address_item *addr, int fd, int options,
                               dk_domain,
                               dk_selector,
                               dk_canon);
-    
+
   if (dk_signature != NULL)
     {
     /* Send the signature first */
@@ -1007,8 +1042,8 @@ dk_transport_write_message(address_item *addr, int fd, int options,
     uschar *dk_strict_result = expand_string(dk_strict);
     if (dk_strict_result != NULL)
       {
-      if ( (strcmpic(dk_strict,"1") == 0) ||
-           (strcmpic(dk_strict,"true") == 0) )
+      if ( (strcmpic(dk_strict,US"1") == 0) ||
+           (strcmpic(dk_strict,US"true") == 0) )
         {
         save_errno = errno;
         rc = FALSE;
@@ -1017,16 +1052,16 @@ dk_transport_write_message(address_item *addr, int fd, int options,
       }
     }
 
-  /* Rewind file and send it down the original fd. */ 
+  /* Rewind file and send it down the original fd. */
   lseek(dk_fd, 0, SEEK_SET);
-  
+
   while((sread = read(dk_fd,sbuf,2048)) > 0)
     {
     char *p = sbuf;
     /* write the chunk */
     DK_WRITE:
     #ifdef SUPPORT_TLS
-    if (tls_active == fd) wwritten = tls_write(p, sread); else
+    if (tls_active == fd) wwritten = tls_write(US p, sread); else
     #endif
     wwritten = write(fd,p,sread);
     if (wwritten == -1)
@@ -1044,7 +1079,7 @@ dk_transport_write_message(address_item *addr, int fd, int options,
       goto DK_WRITE;
       }
     }
-    
+
   if (sread == -1)
     {
     save_errno = errno;
@@ -1055,7 +1090,7 @@ dk_transport_write_message(address_item *addr, int fd, int options,
 
   CLEANUP:
   /* unlink -K file */
-  close(dk_fd);
+  (void)close(dk_fd);
   Uunlink(dk_spool_name);
   errno = save_errno;
   return rc;
@@ -1091,6 +1126,8 @@ int rc, len, yield, fd_read, fd_write, save_errno;
 int pfd[2];
 pid_t filter_pid, write_pid;
 
+transport_filter_timed_out = FALSE;
+
 /* If there is no filter command set up, call the internal function that does
 the actual work, passing it the incoming fd, and return its result. */
 
@@ -1127,10 +1164,10 @@ save_errno = 0;
 yield = FALSE;
 write_pid = (pid_t)(-1);
 
-fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
 filter_pid = child_open(transport_filter_argv, NULL, 077, &fd_write, &fd_read,
   FALSE);
-fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC);
+(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC);
 if (filter_pid < 0) goto TIDY_UP;      /* errno set */
 
 DEBUG(D_transport)
@@ -1145,25 +1182,25 @@ if (pipe(pfd) != 0) goto TIDY_UP;      /* errno set */
 if ((write_pid = fork()) == 0)
   {
   BOOL rc;
-  close(fd_read);
-  close(pfd[pipe_read]);
+  (void)close(fd_read);
+  (void)close(pfd[pipe_read]);
   nl_check_length = nl_escape_length = 0;
   rc = internal_transport_write_message(addr, fd_write,
     (options & ~(topt_use_crlf | topt_end_dot)),
     size_limit, add_headers, remove_headers, NULL, NULL,
     rewrite_rules, rewrite_existflags);
   save_errno = errno;
-  write(pfd[pipe_write], (void *)&rc, sizeof(BOOL));
-  write(pfd[pipe_write], (void *)&save_errno, sizeof(int));
-  write(pfd[pipe_write], (void *)&(addr->more_errno), sizeof(int));
+  (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));
   _exit(0);
   }
 save_errno = errno;
 
 /* Parent process: close our copy of the writing subprocess' pipes. */
 
-close(pfd[pipe_write]);
-close(fd_write);
+(void)close(pfd[pipe_write]);
+(void)close(fd_write);
 fd_write = -1;
 
 /* Writing process creation failed */
@@ -1203,6 +1240,7 @@ for (;;)
   if (sigalrm_seen)
     {
     errno = ETIMEDOUT;
+    transport_filter_timed_out = TRUE;
     goto TIDY_UP;
     }
 
@@ -1232,8 +1270,8 @@ sure. Also apply a paranoia timeout. */
 TIDY_UP:
 save_errno = errno;
 
-close(fd_read);
-if (fd_write > 0) close(fd_write);
+(void)close(fd_read);
+if (fd_write > 0) (void)close(fd_write);
 
 if (!yield)
   {
@@ -1265,11 +1303,11 @@ if (write_pid > 0)
     if (rc == 0)
       {
       BOOL ok;
-      read(pfd[pipe_read], (void *)&ok, sizeof(BOOL));
+      (void)read(pfd[pipe_read], (void *)&ok, sizeof(BOOL));
       if (!ok)
         {
-        read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
-        read(pfd[pipe_read], (void *)&(addr->more_errno), sizeof(int));
+        (void)read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
+        (void)read(pfd[pipe_read], (void *)&(addr->more_errno), sizeof(int));
         yield = FALSE;
         }
       }
@@ -1282,7 +1320,7 @@ if (write_pid > 0)
       }
     }
   }
-close(pfd[pipe_read]);
+(void)close(pfd[pipe_read]);
 
 /* If there have been no problems we can now add the terminating "." if this is
 SMTP output, turning off escaping beforehand. If the last character from the
@@ -1746,8 +1784,8 @@ if ((pid = fork()) == 0)
 
   if (socket_fd != 0)
     {
-    dup2(socket_fd, 0);
-    close(socket_fd);
+    (void)dup2(socket_fd, 0);
+    (void)close(socket_fd);
     }
 
   DEBUG(D_exec) debug_print_argv(argv);