Fix parsing of cmdline -os & -pr options. Bug 2538
[exim.git] / src / src / receive.c
index 0cb38626ac559bd432ad558fd8450d09e478836b..8f3bfefacd162cfaaa75d85b0f765e0322983034 100644 (file)
@@ -14,9 +14,9 @@
 extern int dcc_ok;
 #endif
 
-#ifdef EXPERIMENTAL_DMARC
+#ifdef SUPPORT_DMARC
 # include "dmarc.h"
-#endif /* EXPERIMENTAL_DMARC */
+#endif
 
 /*************************************************
 *                Local static variables          *
@@ -489,7 +489,7 @@ if (recipients_count >= recipients_list_max)
   recipient_item *oldlist = recipients_list;
   int oldmax = recipients_list_max;
   recipients_list_max = recipients_list_max ? 2*recipients_list_max : 50;
-  recipients_list = store_get(recipients_list_max * sizeof(recipient_item));
+  recipients_list = store_get(recipients_list_max * sizeof(recipient_item), FALSE);
   if (oldlist != NULL)
     memcpy(recipients_list, oldlist, oldmax * sizeof(recipient_item));
   }
@@ -571,6 +571,30 @@ return FALSE;
 
 
 
+/* Pause for a while waiting for input.  If none received in that time,
+close the logfile, if we had one open; then if we wait for a long-running
+datasource (months, in one use-case) log rotation will not leave us holding
+the file copy. */
+
+static void
+log_close_chk(void)
+{
+if (!receive_timeout)
+  {
+  struct timeval t;
+  timesince(&t, &received_time);
+  if (t.tv_sec > 30*60)
+    mainlog_close();
+  else
+    {
+    fd_set r;
+    FD_ZERO(&r); FD_SET(0, &r);
+    t.tv_sec = 30*60 - t.tv_sec; t.tv_usec = 0;
+    if (select(1, &r, NULL, NULL, &t) == 0) mainlog_close();
+    }
+  }
+}
+
 /*************************************************
 *     Read data portion of a non-SMTP message    *
 *************************************************/
@@ -619,9 +643,16 @@ register int linelength = 0;
 
 if (!f.dot_ends)
   {
-  register int last_ch = '\n';
+  int last_ch = '\n';
+
+/*XXX we do a gettimeofday before checking for every received char,
+which is hardly clever.  The function-indirection doesn't help, but
+an additional function to check for nonempty read buffer would help.
+See stdin_getc() / smtp_getc() / tls_getc() / bdat_getc(). */
 
-  for (; (ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF; last_ch = ch)
+  for ( ;
+       log_close_chk(), (ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF;
+       last_ch = ch)
     {
     if (ch == 0) body_zerocount++;
     if (last_ch == '\r' && ch != '\n')
@@ -663,7 +694,7 @@ if (!f.dot_ends)
 
 ch_state = 1;
 
-while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
+while (log_close_chk(), (ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
   {
   if (ch == 0) body_zerocount++;
   switch (ch_state)
@@ -1317,7 +1348,7 @@ if (received_protocol)
 if (LOGGING(pipelining) && f.smtp_in_pipelining_advertised)
   {
   g = string_catn(g, US" L", 2);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   if (f.smtp_in_early_pipe_used)
     g = string_catn(g, US"*", 1);
   else if (f.smtp_in_early_pipe_advertised)
@@ -1373,6 +1404,7 @@ for (header_line * my_headerlist = header_list; my_headerlist;
     goto DO_MIME_ACL;
     }
 
+mime_part_count = -1;
 DEBUG(D_receive) debug_printf("No Content-Type: header - presumably not a MIME message.\n");
 return TRUE;
 
@@ -1651,6 +1683,7 @@ uschar *frozen_by = NULL;
 uschar *queued_by = NULL;
 
 uschar *errmsg;
+rmark rcvd_log_reset_point;
 gstring * g;
 struct stat statbuf;
 
@@ -1661,6 +1694,7 @@ uschar *user_msg, *log_msg;
 
 /* Working header pointers */
 
+rmark reset_point;
 header_line *next;
 
 /* Flags for noting the existence of certain headers (only one left) */
@@ -1675,10 +1709,6 @@ header_line *msgid_header = NULL;
 header_line *received_header;
 BOOL msgid_header_newly_created = FALSE;
 
-#ifdef EXPERIMENTAL_DMARC
-int dmarc_up = 0;
-#endif /* EXPERIMENTAL_DMARC */
-
 /* Variables for use when building the Received: header. */
 
 uschar *timestamp;
@@ -1701,7 +1731,7 @@ if (extract_recip || !smtp_input)
 header. Temporarily mark it as "old", i.e. not to be used. We keep header_last
 pointing to the end of the chain to make adding headers simple. */
 
-received_header = header_list = header_last = store_get(sizeof(header_line));
+received_header = header_list = header_last = store_get(sizeof(header_line), FALSE);
 header_list->next = NULL;
 header_list->type = htype_old;
 header_list->text = NULL;
@@ -1709,8 +1739,9 @@ header_list->slen = 0;
 
 /* Control block for the next header to be read. */
 
-next = store_get(sizeof(header_line));
-next->text = store_get(header_size);
+reset_point = store_mark();
+next = store_get(sizeof(header_line), FALSE);  /* not tainted */
+next->text = store_get(header_size, TRUE);     /* tainted */
 
 /* Initialize message id to be null (indicating no message read), and the
 header names list to be the normal list. Indicate there is no data file open
@@ -1731,6 +1762,13 @@ if (thismessage_size_limit <= 0) thismessage_size_limit = INT_MAX;
 message_linecount = body_linecount = body_zerocount =
   max_received_linelength = 0;
 
+#ifdef WITH_CONTENT_SCAN
+/* reset non-per-part mime variables */
+mime_is_coverletter    = 0;
+mime_is_rfc822         = 0;
+mime_part_count        = -1;
+#endif
+
 #ifndef DISABLE_DKIM
 /* Call into DKIM to set up the context.  In CHUNKING mode
 we clear the dot-stuffing flag */
@@ -1738,9 +1776,8 @@ if (smtp_input && !smtp_batched_input && !f.dkim_disable_verify)
   dkim_exim_verify_init(chunking_state <= CHUNKING_OFFERED);
 #endif
 
-#ifdef EXPERIMENTAL_DMARC
-/* initialize libopendmarc */
-dmarc_up = dmarc_init();
+#ifdef SUPPORT_DMARC
+if (sender_host_address) dmarc_init(); /* initialize libopendmarc */
 #endif
 
 /* Remember the time of reception. Exim uses time+pid for uniqueness of message
@@ -1828,8 +1865,10 @@ for (;;)
       goto OVERSIZE;
     header_size *= 2;
 
-    if (!store_extend(next->text, oldsize, header_size))
-      next->text = store_newblock(next->text, header_size, ptr);
+    /* The data came from the message, so is tainted. */
+
+    if (!store_extend(next->text, TRUE, oldsize, header_size))
+      next->text = store_newblock(next->text, TRUE, header_size, ptr);
     }
 
   /* Cope with receiving a binary zero. There is dispute about whether
@@ -1884,7 +1923,7 @@ for (;;)
     if (ch == '\n')
       {
       message_ended = END_DOT;
-      store_reset(next);
+      reset_point = store_reset(reset_point);
       next = NULL;
       break;                    /* End character-reading loop */
       }
@@ -1990,7 +2029,7 @@ OVERSIZE:
 
   if (ptr == 1)
     {
-    store_reset(next);
+    reset_point = store_reset(reset_point);
     next = NULL;
     break;
     }
@@ -2019,7 +2058,7 @@ OVERSIZE:
 
   next->text[ptr] = 0;
   next->slen = ptr;
-  store_reset(next->text + ptr + 1);
+  store_release_above(next->text + ptr + 1);
 
   /* Check the running total size against the overall message size limit. We
   don't expect to fail here, but if the overall limit is set less than MESSAGE_
@@ -2215,9 +2254,10 @@ OVERSIZE:
 
   /* Set up for the next header */
 
+  reset_point = store_mark();
   header_size = 256;
-  next = store_get(sizeof(header_line));
-  next->text = store_get(header_size);
+  next = store_get(sizeof(header_line), FALSE);
+  next->text = store_get(header_size, TRUE);
   ptr = 0;
   had_zero = 0;
   prevlines_length = 0;
@@ -2501,7 +2541,7 @@ if (extract_recip)
         white space that follows the newline must not be removed - it is part
         of the header. */
 
-        pp = recipient = store_get(ss - s + 1);
+        pp = recipient = store_get(ss - s + 1, is_tainted(s));
         for (uschar * p = s; p < ss; p++) if (*p != '\n') *pp++ = *p;
         *pp = 0;
 
@@ -2532,7 +2572,7 @@ if (extract_recip)
         if (recipient == NULL && Ustrcmp(errmess, "empty address") != 0)
           {
           int len = Ustrlen(s);
-          error_block *b = store_get(sizeof(error_block));
+          error_block *b = store_get(sizeof(error_block), FALSE);
           while (len > 0 && isspace(s[len-1])) len--;
           b->next = NULL;
           b->text1 = string_printing(string_copyn(s, len));
@@ -2662,7 +2702,7 @@ it will fit. */
 to be the least significant base-62 digit of the time of arrival. Otherwise
 ensure that it is an empty string. */
 
-message_subdir[0] = split_spool_directory ? message_id[5] : 0;
+set_subdir_str(message_subdir, message_id, 0);
 
 /* Now that we have the message-id, if there is no message-id: header, generate
 one, but only for local (without suppress_local_fixups) or submission mode
@@ -2739,7 +2779,7 @@ function may mess with the real recipients. */
 
 if (LOGGING(received_recipients))
   {
-  raw_recipients = store_get(recipients_count * sizeof(uschar *));
+  raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE);
   for (int i = 0; i < recipients_count; i++)
     raw_recipients[i] = string_copy(recipients_list[i].address);
   raw_recipients_count = recipients_count;
@@ -2880,9 +2920,8 @@ if (  from_header
     uschar *at = domain ? from_address + domain - 1 : NULL;
 
     if (at) *at = 0;
-    from_address += route_check_prefix(from_address, local_from_prefix);
-    slen = route_check_suffix(from_address, local_from_suffix);
-    if (slen > 0)
+    from_address += route_check_prefix(from_address, local_from_prefix, NULL);
+    if ((slen = route_check_suffix(from_address, local_from_suffix, NULL)) > 0)
       {
       memmove(from_address+slen, from_address, Ustrlen(from_address)-slen);
       from_address += slen;
@@ -3060,7 +3099,7 @@ if ((data_fd = Uopen(spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE)) < 0)
 /* Make sure the file's group is the Exim gid, and double-check the mode
 because the group setting doesn't always get set automatically. */
 
-if (fchown(data_fd, exim_uid, exim_gid))
+if (0 != exim_fchown(data_fd, exim_uid, exim_gid, spool_name))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE,
     "Failed setting ownership on spool file %s: %s",
     spool_name, strerror(errno));
@@ -3467,9 +3506,9 @@ else
       goto TIDYUP;
 #endif /* WITH_CONTENT_SCAN */
 
-#ifdef EXPERIMENTAL_DMARC
-    dmarc_up = dmarc_store_data(from_header);
-#endif /* EXPERIMENTAL_DMARC */
+#ifdef SUPPORT_DMARC
+    dmarc_store_data(from_header);
+#endif
 
 #ifndef DISABLE_PRDR
     if (prdr_requested && recipients_count > 1 && acl_smtp_data_prdr)
@@ -3805,7 +3844,6 @@ else
     string_from_gstring(g), istemp, string_printing(errmsg));
 
   if (smtp_input)
-    {
     if (!smtp_batched_input)
       {
       smtp_respond(smtp_code, 3, TRUE, errmsg);
@@ -3816,7 +3854,6 @@ else
     else
       moan_smtp_batch(NULL, "%s %s", smtp_code, errmsg);
       /* Does not return */
-    }
   else
     {
     fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
@@ -3942,6 +3979,7 @@ it first! Include any message id that is in the message - since the syntax of a
 message id is actually an addr-spec, we can use the parse routine to canonicalize
 it. */
 
+rcvd_log_reset_point = store_mark();
 g = string_get(256);
 
 g = string_append(g, 2,
@@ -3952,9 +3990,15 @@ if (message_reference)
 
 g = add_host_info_for_log(g);
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 if (LOGGING(tls_cipher) && tls_in.cipher)
+  {
   g = string_append(g, 2, US" X=", tls_in.cipher);
+# ifdef EXPERIMENTAL_TLS_RESUME
+  if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED)
+    g = string_catn(g, US"*", 1);
+# endif
+  }
 if (LOGGING(tls_certificate_verified) && tls_in.cipher)
   g = string_append(g, 2, US" CV=", tls_in.certificate_verified ? "yes":"no");
 if (LOGGING(tls_peerdn) && tls_in.peerdn)
@@ -3987,18 +4031,14 @@ if (proxy_session && LOGGING(proxy))
 if (chunking_state > CHUNKING_OFFERED)
   g = string_catn(g, US" K", 2);
 
-sprintf(CS big_buffer, "%d", msg_size);
-g = string_append(g, 2, US" S=", big_buffer);
+g = string_fmt_append(g, " S=%d", msg_size);
 
 /* log 8BITMIME mode announced in MAIL_FROM
    0 ... no BODY= used
    7 ... 7BIT
    8 ... 8BITMIME */
 if (LOGGING(8bitmime))
-  {
-  sprintf(CS big_buffer, "%d", body_8bitmime);
-  g = string_append(g, 2, US" M8S=", big_buffer);
-  }
+  g = string_fmt_append(g, " M8S=%d", body_8bitmime);
 
 #ifndef DISABLE_DKIM
 if (LOGGING(dkim) && dkim_verify_overall)
@@ -4072,7 +4112,7 @@ if (message_logs && !blackholed_by)
   {
   int fd;
   uschar * m_name = spool_fname(US"msglog", message_subdir, message_id, US"");
-  
+
   if (  (fd = Uopen(m_name, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE)) < 0
      && errno == ENOENT
      )
@@ -4199,7 +4239,7 @@ if(cutthrough.cctx.sock >= 0 && cutthrough.delivery)
 
     case '4':  /* Temp-reject. Keep spoolfiles and accept, unless defer-pass mode.
                ... for which, pass back the exact error */
-      if (cutthrough.defer_pass) smtp_reply = string_copy_malloc(msg);
+      if (cutthrough.defer_pass) smtp_reply = string_copy_perm(msg, TRUE);
       cutthrough_done = TMP_REJ;               /* Avoid the usual immediate delivery attempt */
       break;                                   /* message_id needed for SMTP accept below */
 
@@ -4209,7 +4249,7 @@ if(cutthrough.cctx.sock >= 0 && cutthrough.delivery)
       break;                                   /* message_id needed for SMTP accept below */
 
     case '5':  /* Perm-reject.  Do the same to the source.  Dump any spoolfiles */
-      smtp_reply = string_copy_malloc(msg);            /* Pass on the exact error */
+      smtp_reply = string_copy_perm(msg, TRUE);                /* Pass on the exact error */
       cutthrough_done = PERM_REJ;
       break;
     }
@@ -4231,12 +4271,13 @@ if(!smtp_reply)
   if (f.deliver_freeze) log_write(0, LOG_MAIN, "frozen by %s", frozen_by);
   if (f.queue_only_policy) log_write(L_delay_delivery, LOG_MAIN,
     "no immediate delivery: queued%s%s by %s",
-    *queue_name ? " in " : "", *queue_name ? CS queue_name : "",       
+    *queue_name ? " in " : "", *queue_name ? CS queue_name : "",
     queued_by);
   }
 f.receive_call_bombout = FALSE;
 
-store_reset(g);   /* The store for the main log message can be reused */
+/* The store for the main log message can be reused */
+rcvd_log_reset_point = store_reset(rcvd_log_reset_point);
 
 /* If the message is frozen, and freeze_tell is set, do the telling. */