exipick version 20060307.0
[exim.git] / src / src / receive.c
index c83bca664b7b00f0fed90d0c0e466f79c33c3366..f711e9ca85a61328721770c0e86c29bbc477befd 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/receive.c,v 1.15 2005/04/07 15:40:50 ph10 Exp $ */
+/* $Cambridge: exim/src/src/receive.c,v 1.27 2006/03/06 16:05:12 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2006 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Code for receiving a message and setting up spool files. */
@@ -303,8 +303,8 @@ if (spool_name[0] != 0)
 
 /* Now close the file if it is open, either as a fd or a stream. */
 
-if (data_file != NULL) fclose(data_file);
-  else if (data_fd >= 0) close(data_fd);
+if (data_file != NULL) (void)fclose(data_file);
+  else if (data_fd >= 0) (void)close(data_fd);
 
 /* Attempt to close down an SMTP connection tidily. */
 
@@ -865,7 +865,7 @@ if (error_handling == ERRORS_SENDER)
     error_rc = EXIT_FAILURE;
   }
 else fprintf(stderr, "exim: %s%s\n", text2, text1);  /* Sic */
-fclose(f);
+(void)fclose(f);
 exim_exit(error_rc);
 }
 
@@ -899,10 +899,10 @@ add_acl_headers(uschar *acl_name)
 header_line *h, *next;
 header_line *last_received = NULL;
 
-if (acl_warn_headers == NULL) return;
+if (acl_added_headers == NULL) return;
 DEBUG(D_receive|D_acl) debug_printf(">>Headers added by %s ACL:\n", acl_name);
 
-for (h = acl_warn_headers; h != NULL; h = next)
+for (h = acl_added_headers; h != NULL; h = next)
   {
   next = h->next;
 
@@ -964,7 +964,7 @@ for (h = acl_warn_headers; h != NULL; h = next)
   DEBUG(D_receive|D_acl) debug_printf("  %s", header_last->text);
   }
 
-acl_warn_headers = NULL;
+acl_added_headers = NULL;
 DEBUG(D_receive|D_acl) debug_printf(">>\n");
 }
 
@@ -1078,7 +1078,7 @@ mime_is_rfc822 = 0;
 MIME_ACL_CHECK:
 mime_part_count = -1;
 rc = mime_acl_check(acl, mbox_file, NULL, &user_msg, &log_msg);
-fclose(mbox_file);
+(void)fclose(mbox_file);
 
 if (Ustrlen(rfc822_file_path) > 0) {
   mime_part_count = mime_part_count_buffer;
@@ -1097,7 +1097,8 @@ if (rc == OK) {
   struct dirent *entry;
   DIR *tempdir;
 
-  snprintf(CS temp_path, 1024, "%s/scan/%s", spool_directory, message_id);
+  (void)string_format(temp_path, 1024, "%s/scan/%s", spool_directory,
+    message_id);
 
  tempdir = opendir(CS temp_path);
  n = 0;
@@ -1105,7 +1106,7 @@ if (rc == OK) {
    entry = readdir(tempdir);
    if (entry == NULL) break;
     if (strncmpic(US entry->d_name,US"__rfc822_",9) == 0) {
-      snprintf(CS rfc822_file_path, 2048,"%s/scan/%s/%s", spool_directory, message_id, entry->d_name);
+      (void)string_format(rfc822_file_path, 2048,"%s/scan/%s/%s", spool_directory, message_id, entry->d_name);
      debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path);
      break;
     };
@@ -1113,7 +1114,7 @@ if (rc == OK) {
  closedir(tempdir);
 
   if (entry != NULL) {
-    mbox_file = Ufopen(rfc822_file_path,"r");
+    mbox_file = Ufopen(rfc822_file_path,"rb");
     if (mbox_file == NULL) {
       log_write(0, LOG_PANIC,
          "acl_smtp_mime: can't open RFC822 spool file, skipping.");
@@ -1159,9 +1160,10 @@ return TRUE;
 Either a non-null list of recipients, or the extract flag will be true, or
 both. The flag sender_local is true for locally generated messages. The flag
 submission_mode is true if an ACL has obeyed "control = submission". The flag
-smtp_input is true if the message is to be handled using SMTP conventions about
-termination and lines starting with dots. For non-SMTP messages, dot_ends is
-true for dot-terminated messages.
+suppress_local_fixups is true if an ACL has obeyed "control =
+suppress_local_fixups". The flag smtp_input is true if the message is to be
+handled using SMTP conventions about termination and lines starting with dots.
+For non-SMTP messages, dot_ends is true for dot-terminated messages.
 
 If a message was successfully read, message_id[0] will be non-zero.
 
@@ -1282,12 +1284,7 @@ uschar *smtp_reply = NULL;
 
 header_line *h, *next;
 
-/* Flags for noting the existence of certain headers */
-
-/**** No longer check for these (Nov 2003)
-BOOL to_or_cc_header_exists = FALSE;
-BOOL bcc_header_exists = FALSE;
-****/
+/* Flags for noting the existence of certain headers (only one left) */
 
 BOOL date_header_exists = FALSE;
 
@@ -1339,11 +1336,9 @@ received_count = 1;     /* For the one we will add */
 
 if (thismessage_size_limit <= 0) thismessage_size_limit = INT_MAX;
 
-/* While reading the message, body_linecount and body_zerocount is computed.
-The full message_ linecount is set up only when the headers are read back in
-from the spool for delivery. */
+/* While reading the message, the following counts are computed. */
 
-body_linecount = body_zerocount = 0;
+message_linecount = body_linecount = body_zerocount = 0;
 
 #ifdef EXPERIMENTAL_DOMAINKEYS
 /* Call into DK to set up the context. Check if DK is to be run are carried out
@@ -1573,7 +1568,11 @@ for (;;)
   /* End of header line reached */
 
   EOL:
-  receive_linecount++;          /* For BSMTP errors */
+
+  /* Keep track of lines for BSMTP errors and overall message_linecount. */
+
+  receive_linecount++;
+  message_linecount++;
 
   /* Now put in the terminating newline. There is always space for
   at least two more characters. */
@@ -1848,24 +1847,12 @@ for (h = header_list->next; h != NULL; h = h->next)
 
   switch (header_checkname(h, is_resent))
     {
-    /* "Bcc:" gets flagged, and its existence noted, whether it's resent- or
-    not. */
-
     case htype_bcc:
-    h->type = htype_bcc;
-    /****
-    bcc_header_exists = TRUE;
-    ****/
+    h->type = htype_bcc;        /* Both Bcc: and Resent-Bcc: */
     break;
 
-    /* "Cc:" gets flagged, and the existence of a recipient header is noted,
-    whether it's resent- or not. */
-
     case htype_cc:
-    h->type = htype_cc;
-    /****
-    to_or_cc_header_exists = TRUE;
-    ****/
+    h->type = htype_cc;         /* Both Cc: and Resent-Cc: */
     break;
 
     /* Record whether a Date: or Resent-Date: header exists, as appropriate. */
@@ -1973,18 +1960,22 @@ for (h = header_list->next; h != NULL; h = h->next)
     break;
 
     /* If there is a "Sender:" header and the message is locally originated,
-    and from an untrusted caller, or if we are in submission mode for a remote
-    message, mark it "old" so that it will not be transmitted with the message,
-    unless active_local_sender_retain is set. (This can only be true if
-    active_local_from_check is false.) If there are any resent- headers in the
-    message, apply this rule to Resent-Sender: instead of Sender:. Messages
-    with multiple resent- header sets cannot be tidily handled. (For this
-    reason, at least one MUA - Pine - turns old resent- headers into X-resent-
-    headers when resending, leaving just one set.) */
+    and from an untrusted caller and suppress_local_fixups is not set, or if we
+    are in submission mode for a remote message, mark it "old" so that it will
+    not be transmitted with the message, unless active_local_sender_retain is
+    set. (This can only be true if active_local_from_check is false.) If there
+    are any resent- headers in the message, apply this rule to Resent-Sender:
+    instead of Sender:. Messages with multiple resent- header sets cannot be
+    tidily handled. (For this reason, at least one MUA - Pine - turns old
+    resent- headers into X-resent- headers when resending, leaving just one
+    set.) */
 
     case htype_sender:
     h->type = ((!active_local_sender_retain &&
-                ((sender_local && !trusted_caller) || submission_mode)
+                (
+                (sender_local && !trusted_caller && !suppress_local_fixups)
+                  || submission_mode
+                )
                ) &&
                (!resents_exist||is_resent))?
       htype_old : htype_sender;
@@ -2142,13 +2133,7 @@ if (extract_recip)
       will be kept on the spool, but not transmitted as part of the
       message. */
 
-      if (h->type == htype_bcc)
-        {
-        h->type = htype_old;
-        /****
-        bcc_header_exists = FALSE;
-        ****/
-        }
+      if (h->type == htype_bcc) h->type = htype_old;
       }   /* For appropriate header line */
     }     /* For each header line */
 
@@ -2246,11 +2231,13 @@ ensure that it is an empty string. */
 message_subdir[0] = split_spool_directory? message_id[5] : 0;
 
 /* Now that we have the message-id, if there is no message-id: header, generate
-one, but only for local or submission mode messages. This can be
-user-configured if required, but we had better flatten any illegal characters
-therein. */
+one, but only for local (without suppress_local_fixups) or submission mode
+messages. This can be user-configured if required, but we had better flatten
+any illegal characters therein. */
 
-if (msgid_header == NULL && (sender_host_address == NULL || submission_mode))
+if (msgid_header == NULL &&
+      ((sender_host_address == NULL && !suppress_local_fixups)
+        || submission_mode))
   {
   uschar *p;
   uschar *id_text = US"";
@@ -2324,46 +2311,75 @@ for (i = 0; i < recipients_count; i++)
     rewrite_address(recipients_list[i].address, TRUE, TRUE,
       global_rewrite_rules, rewrite_existflags);
 
-/* If there is no From: header, generate one for local or submission_mode
-messages. If there is no sender address, but the sender is local or this is a
-local delivery error, use the originator login. This shouldn't happen for
-genuine bounces, but might happen for autoreplies. The addition of From: must
-be done *before* checking for the possible addition of a Sender: header,
-because untrusted_set_sender allows an untrusted user to set anything in the
-envelope (which might then get info From:) but we still want to ensure a valid
-Sender: if it is required. */
-
-if (from_header == NULL && (sender_host_address == NULL || submission_mode))
+/* If there is no From: header, generate one for local (without
+suppress_local_fixups) or submission_mode messages. If there is no sender
+address, but the sender is local or this is a local delivery error, use the
+originator login. This shouldn't happen for genuine bounces, but might happen
+for autoreplies. The addition of From: must be done *before* checking for the
+possible addition of a Sender: header, because untrusted_set_sender allows an
+untrusted user to set anything in the envelope (which might then get info
+From:) but we still want to ensure a valid Sender: if it is required. */
+
+if (from_header == NULL &&
+    ((sender_host_address == NULL && !suppress_local_fixups)
+      || submission_mode))
   {
+  uschar *oname = US"";
+
+  /* Use the originator_name if this is a locally submitted message and the
+  caller is not trusted. For trusted callers, use it only if -F was used to
+  force its value or if we have a non-SMTP message for which -f was not used
+  to set the sender. */
+
+  if (sender_host_address == NULL)
+    {
+    if (!trusted_caller || sender_name_forced ||
+         (!smtp_input && !sender_address_forced))
+      oname = originator_name;
+    }
+
+  /* For non-locally submitted messages, the only time we use the originator
+  name is when it was forced by the /name= option on control=submission. */
+
+  else
+    {
+    if (submission_name != NULL) oname = submission_name;
+    }
+
   /* Envelope sender is empty */
 
   if (sender_address[0] == 0)
     {
+    uschar *fromstart, *fromend;
+
+    fromstart = string_sprintf("%sFrom: %s%s", resent_prefix,
+      oname, (oname[0] == 0)? "" : " <");
+    fromend = (oname[0] == 0)? US"" : US">";
+
     if (sender_local || local_error_message)
       {
-      header_add(htype_from, "%sFrom: %s%s%s@%s%s\n", resent_prefix,
-        originator_name,
-        (originator_name[0] == 0)? "" : " <",
-        local_part_quote(originator_login),
-        qualify_domain_sender,
-        (originator_name[0] == 0)? "" : ">");
+      header_add(htype_from, "%s%s@%s%s\n", fromstart,
+        local_part_quote(originator_login), qualify_domain_sender,
+        fromend);
       }
     else if (submission_mode && authenticated_id != NULL)
       {
       if (submission_domain == NULL)
         {
-        header_add(htype_from, "%sFrom: %s@%s\n", resent_prefix,
-          local_part_quote(authenticated_id), qualify_domain_sender);
+        header_add(htype_from, "%s%s@%s%s\n", fromstart,
+          local_part_quote(authenticated_id), qualify_domain_sender,
+          fromend);
         }
       else if (submission_domain[0] == 0)  /* empty => whole address set */
         {
-        header_add(htype_from, "%sFrom: %s\n", resent_prefix,
-          authenticated_id);
+        header_add(htype_from, "%s%s%s\n", fromstart, authenticated_id,
+          fromend);
         }
       else
         {
-        header_add(htype_from, "%sFrom: %s@%s\n", resent_prefix,
-          local_part_quote(authenticated_id), submission_domain);
+        header_add(htype_from, "%s%s@%s%s\n", fromstart,
+          local_part_quote(authenticated_id), submission_domain,
+          fromend);
         }
       from_header = header_last;    /* To get it checked for Sender: */
       }
@@ -2375,34 +2391,31 @@ if (from_header == NULL && (sender_host_address == NULL || submission_mode))
 
   else
     {
-    if (!smtp_input || sender_local)
-      header_add(htype_from, "%sFrom: %s%s%s%s\n",
-        resent_prefix, originator_name,
-        (originator_name[0] == 0)? "" : " <",
-        (sender_address_unrewritten == NULL)?
-          sender_address : sender_address_unrewritten,
-        (originator_name[0] == 0)? "" : ">");
-    else
-      header_add(htype_from, "%sFrom: %s\n", resent_prefix, sender_address);
+    header_add(htype_from, "%sFrom: %s%s%s%s\n", resent_prefix,
+      oname,
+      (oname[0] == 0)? "" : " <",
+      (sender_address_unrewritten == NULL)?
+        sender_address : sender_address_unrewritten,
+      (oname[0] == 0)? "" : ">");
 
     from_header = header_last;    /* To get it checked for Sender: */
     }
   }
 
 
-/* If the sender is local, or if we are in submission mode and there is an
-authenticated_id, check that an existing From: is correct, and if not, generate
-a Sender: header, unless disabled. Any previously-existing Sender: header was
-removed above. Note that sender_local, as well as being TRUE if the caller of
-exim is not trusted, is also true if a trusted caller did not supply a -f
-argument for non-smtp input. To allow trusted callers to forge From: without
-supplying -f, we have to test explicitly here. If the From: header contains
-more than one address, then the call to parse_extract_address fails, and a
-Sender: header is inserted, as required. */
+/* If the sender is local (without suppress_local_fixups), or if we are in
+submission mode and there is an authenticated_id, check that an existing From:
+is correct, and if not, generate a Sender: header, unless disabled. Any
+previously-existing Sender: header was removed above. Note that sender_local,
+as well as being TRUE if the caller of exim is not trusted, is also true if a
+trusted caller did not supply a -f argument for non-smtp input. To allow
+trusted callers to forge From: without supplying -f, we have to test explicitly
+here. If the From: header contains more than one address, then the call to
+parse_extract_address fails, and a Sender: header is inserted, as required. */
 
 if (from_header != NULL &&
      (active_local_from_check &&
-       ((sender_local && !trusted_caller) ||
+       ((sender_local && !trusted_caller && !suppress_local_fixups) ||
         (submission_mode && authenticated_id != NULL))
      ))
   {
@@ -2464,12 +2477,27 @@ if (from_header != NULL &&
 
   if (make_sender)
     {
-    if (submission_mode)
+    if (submission_mode && submission_name == NULL)
       header_add(htype_sender, "%sSender: %s\n", resent_prefix,
         generated_sender_address);
     else
       header_add(htype_sender, "%sSender: %s <%s>\n",
-        resent_prefix, originator_name, generated_sender_address);
+        resent_prefix,
+        submission_mode? submission_name : originator_name,
+        generated_sender_address);
+    }
+
+  /* Ensure that a non-null envelope sender address corresponds to the
+  submission mode sender address. */
+
+  if (submission_mode && sender_address[0] != 0)
+    {
+    if (sender_address_unrewritten == NULL)
+      sender_address_unrewritten = sender_address;
+    sender_address = generated_sender_address;
+    log_write(L_address_rewrite, LOG_MAIN,
+      "\"%s\" from env-from rewritten as \"%s\" by submission mode",
+      sender_address_unrewritten, generated_sender_address);
     }
   }
 
@@ -2510,30 +2538,24 @@ for (h = header_list->next; h != NULL; h = h->next)
 
 
 /* An RFC 822 (sic) message is not legal unless it has at least one of "to",
-"cc", or "bcc". Note that although the minimal examples in RFC822 show just
+"cc", or "bcc". Note that although the minimal examples in RFC 822 show just
 "to" or "bcc", the full syntax spec allows "cc" as well. If any resent- header
 exists, this applies to the set of resent- headers rather than the normal set.
 
-The requirement for a recipient header has been removed in RFC 2822. Earlier
-versions of Exim added a To: header for locally submitted messages, and an
-empty Bcc: header for others or when always_bcc was set. In the light of the
-changes in RFC 2822, we now always add Bcc: just in case there are still MTAs
-out there that insist on the RFC 822 syntax.
-
-November 2003: While generally revising what Exim does to fix up headers, it
-seems like a good time to remove this altogether. */
+The requirement for a recipient header has been removed in RFC 2822. At this
+point in the code, earlier versions of Exim added a To: header for locally
+submitted messages, and an empty Bcc: header for others. In the light of the
+changes in RFC 2822, this was dropped in November 2003. */
 
-/******
-if (!to_or_cc_header_exists && !bcc_header_exists)
-  header_add(htype_bcc, "Bcc:\n");
-******/
 
 /* If there is no date header, generate one if the message originates locally
-(i.e. not over TCP/IP) or the submission mode flag is set. Messages without
-Date: are not valid, but it seems to be more confusing if Exim adds one to
-all remotely-originated messages. */
+(i.e. not over TCP/IP) and suppress_local_fixups is not set, or if the
+submission mode flag is set. Messages without Date: are not valid, but it seems
+to be more confusing if Exim adds one to all remotely-originated messages. */
 
-if (!date_header_exists && (sender_host_address == NULL || submission_mode))
+if (!date_header_exists &&
+      ((sender_host_address == NULL && !suppress_local_fixups)
+        || submission_mode))
   header_add(htype_other, "%sDate: %s\n", resent_prefix, tod_stamp(tod_full));
 
 search_tidyup();    /* Free any cached resources */
@@ -2585,8 +2607,8 @@ if (data_fd < 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. */
 
-fchown(data_fd, exim_uid, exim_gid);
-fchmod(data_fd, SPOOL_MODE);
+(void)fchown(data_fd, exim_uid, exim_gid);
+(void)fchmod(data_fd, SPOOL_MODE);
 
 /* We now have data file open. Build a stream for it and lock it. We lock only
 the first line of the file (containing the message ID) because otherwise there
@@ -2615,7 +2637,7 @@ if (next != NULL)
   {
   uschar *s = next->text;
   int len = next->slen;
-  fwrite(s, 1, len, data_file);
+  (void)fwrite(s, 1, len, data_file);
   body_linecount++;                 /* Assumes only 1 line */
   }
 
@@ -2633,6 +2655,7 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT)
   else message_ended = read_message_data(data_file);
 
   receive_linecount += body_linecount;  /* For BSMTP errors mainly */
+  message_linecount += body_linecount;
 
   /* Handle premature termination of SMTP */
 
@@ -2805,7 +2828,7 @@ if (extract_recip && (bad_addresses != NULL || recipients_count == 0))
   if (recipients_count == 0 || error_handling == ERRORS_STDERR)
     {
     Uunlink(spool_name);
-    fclose(data_file);
+    (void)fclose(data_file);
     exim_exit(error_rc);
     }
   }
@@ -3391,7 +3414,7 @@ if (message_logs && blackholed_by == NULL)
       {
       log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't fdopen message log %s: %s",
         spool_name, strerror(errno));
-      close(fd);
+      (void)close(fd);
       }
     else
       {
@@ -3401,7 +3424,7 @@ if (message_logs && blackholed_by == NULL)
         frozen_by);
       if (queue_only_policy) fprintf(message_log,
         "%s no immediate delivery: queued by %s\n", now, queued_by);
-      fclose(message_log);
+      (void)fclose(message_log);
       }
     }
   }
@@ -3433,8 +3456,8 @@ possible for fclose() to fail - but what to do? What has happened to the lock
 if this happens? */
 
 TIDYUP:
-process_info[process_info_len] = 0;          /* Remove message id */
-if (data_file != NULL) fclose(data_file);    /* Frees the lock */
+process_info[process_info_len] = 0;                /* Remove message id */
+if (data_file != NULL) (void)fclose(data_file);    /* Frees the lock */
 
 /* Now reset signal handlers to their defaults */
 
@@ -3459,8 +3482,9 @@ if (smtp_input)
     {
     if (smtp_reply == NULL)
       {
-      if (fake_reject)
-        smtp_respond(550,TRUE,fake_reject_text);
+      if (fake_response != OK)
+        smtp_respond(fake_response == DEFER ? 450 : 550,
+                     TRUE, fake_response_text);
       else
         smtp_printf("250 OK id=%s\r\n", message_id);
       if (host_checking)
@@ -3469,8 +3493,9 @@ if (smtp_input)
       }
     else if (smtp_reply[0] != 0)
       {
-      if (fake_reject && (smtp_reply[0] == '2'))
-        smtp_respond(550,TRUE,fake_reject_text);
+      if (fake_response != OK && (smtp_reply[0] == '2'))
+        smtp_respond(fake_response == DEFER ? 450 : 550,
+                     TRUE, fake_response_text);
       else
         smtp_printf("%.1024s\r\n", smtp_reply);
       }