-/* $Cambridge: exim/src/src/receive.c,v 1.2 2004/10/18 11:36:23 ph10 Exp $ */
+/* $Cambridge: exim/src/src/receive.c,v 1.9 2005/01/04 10:00:42 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2005 */
/* See the file NOTICE for conditions of use and distribution. */
/* Code for receiving a message and setting up spool files. */
/*************************************************
-* Check space on spool and log partitions *
+* Read space info for a partition *
*************************************************/
-/* This function is called before accepting a message; if any thresholds are
-set, it checks them. If a message_size is supplied, it checks that there is
-enough space for that size plus the threshold - i.e. that the message won't
-reduce the space to the threshold. Not all OS have statvfs(); for those that
-don't, this function always returns TRUE. For some OS the old function and
-struct name statfs is used; that is handled by a macro, defined in exim.h.
+/* This function is called by receive_check_fs() below, and also by string
+expansion for variables such as $spool_space. The field names for the statvfs
+structure are macros, because not all OS have F_FAVAIL and it seems tidier to
+have macros for F_BAVAIL and F_FILES as well. Some kinds of file system do not
+have inodes, and they return -1 for the number available.
-Arguments:
- msg_size the (estimated) size of an incoming message
+Later: It turns out that some file systems that do not have the concept of
+inodes return 0 rather than -1. Such systems should also return 0 for the total
+number of inodes, so we require that to be greater than zero before returning
+an inode count.
-Returns: FALSE if there isn't enough space, or if the information cannot
- be obtained
- TRUE if no check was done or there is enough space
+Arguments:
+ isspool TRUE for spool partition, FALSE for log partition
+ inodeptr address of int to receive inode count; -1 if there isn't one
+
+Returns: available on-root space, in kilobytes
+ -1 for log partition if there isn't one
+
+All values are -1 if the STATFS functions are not available.
*/
-BOOL
-receive_check_fs(int msg_size)
+int
+receive_statvfs(BOOL isspool, int *inodeptr)
{
#ifdef HAVE_STATFS
-BOOL rc = TRUE;
struct STATVFS statbuf;
+uschar *path;
+uschar *name;
+uschar buffer[1024];
-memset(&statbuf, 0, sizeof(statbuf));
-
-/* The field names are macros, because not all OS have F_FAVAIL and it seems
-tidier to have macros for F_BAVAIL and F_FILES as well. Some kinds of file
-server do not have inodes, and they return -1 for the number available, so we
-do the check only when this field is non-negative.
-
-Later: It turns out that some file systems that do not have the concept of
-inodes return 0 rather than -1. Such systems should also return 0 for the total
-number of inodes, so we require that to be greater than zero before doing the
-test. */
+/* The spool directory must always exist. */
-if (check_spool_space > 0 || msg_size > 0 || check_spool_inodes > 0)
+if (isspool)
{
- if (STATVFS(CS spool_directory, &statbuf) != 0)
- {
- log_write(0, LOG_MAIN|LOG_PANIC, "cannot accept message: failed to stat "
- "spool directory %s: %s", spool_directory, strerror(errno));
- smtp_closedown(US"spool directory problem");
- exim_exit(EXIT_FAILURE);
- }
-
- /* check_spool_space is held in K because disks are getting huge */
-
- if (statbuf.F_BAVAIL < (unsigned long)
- ((((double)check_spool_space) * 1024.0 + (double)msg_size) /
- (double)statbuf.F_FRSIZE)
- ||
- (statbuf.F_FILES > 0 &&
- statbuf.F_FAVAIL >= 0 &&
- statbuf.F_FAVAIL < check_spool_inodes))
- rc = FALSE;
-
- DEBUG(D_receive)
- debug_printf("spool directory %s space = %d blocks; inodes = %d; "
- "check_space = %dK (%d blocks); inodes = %d; msg_size = %d (%d blocks)\n",
- spool_directory, (int)statbuf.F_BAVAIL, (int)statbuf.F_FAVAIL,
- check_spool_space,
- (int)(((double)check_spool_space * 1024.0) / (double)statbuf.F_FRSIZE),
- check_spool_inodes, msg_size, (int)(msg_size / statbuf.F_FRSIZE));
-
- if (!rc)
- {
- log_write(0, LOG_MAIN, "spool directory space check failed: space=%d "
- "inodes=%d", (int)statbuf.F_BAVAIL, (int)statbuf.F_FAVAIL);
- return FALSE;
- }
- }
-
+ path = spool_directory;
+ name = US"spool";
+ }
+
/* Need to cut down the log file path to the directory, and to ignore any
appearance of "syslog" in it. */
-if (check_log_space > 0 || check_log_inodes > 0)
+else
{
- uschar *path;
int sep = ':'; /* Not variable - outside scripts use */
- uschar *cp;
uschar *p = log_file_path;
- uschar buffer[1024];
+ name = US"log";
/* An empty log_file_path means "use the default". This is the same as an
empty item in a list. */
if (Ustrcmp(path, "syslog") != 0) break;
}
- if (path == NULL) return TRUE; /* No log files, so no problem */
-
- /* An empty string means use the default */
+ if (path == NULL) /* No log files */
+ {
+ *inodeptr = -1;
+ return -1;
+ }
- if (path[0] == 0)
- path = string_sprintf("%s/log/%%slog", spool_directory);
+ /* An empty string means use the default, which is in the spool directory.
+ But don't just use the spool directory, as it is possible that the log
+ subdirectory has been symbolically linked elsewhere. */
- if ((cp = Ustrrchr(path, '/')) == NULL)
+ if (path[0] == 0)
{
- DEBUG(D_receive) debug_printf("cannot find slash in %s\n", path);
- return FALSE;
- }
- *cp = 0;
-
- if (STATVFS(CS path, &statbuf) != 0)
+ sprintf(CS buffer, CS"%s/log", CS spool_directory);
+ path = buffer;
+ }
+ else
{
- log_write(0, LOG_MAIN|LOG_PANIC, "cannot accept message: failed to stat "
- "log directory %s: %s", path, strerror(errno));
- smtp_closedown(US"log directory problem");
- exim_exit(EXIT_FAILURE);
- }
+ uschar *cp;
+ if ((cp = Ustrrchr(path, '/')) != NULL) *cp = 0;
+ }
+ }
+
+/* We now have the patch; do the business */
+
+memset(&statbuf, 0, sizeof(statbuf));
+
+if (STATVFS(CS path, &statbuf) != 0)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "cannot accept message: failed to stat "
+ "%s directory %s: %s", name, spool_directory, strerror(errno));
+ smtp_closedown(US"spool or log directory problem");
+ exim_exit(EXIT_FAILURE);
+ }
+
+*inodeptr = (statbuf.F_FILES > 0)? statbuf.F_FAVAIL : -1;
+
+/* Disks are getting huge. Take care with computing the size in kilobytes. */
+
+return (int)(((double)statbuf.F_BAVAIL * (double)statbuf.F_FRSIZE)/1024.0);
+
+/* Unable to find partition sizes in this environment. */
+
+#else
+*inodeptr = -1;
+return -1;
+#endif
+}
- if (statbuf.F_BAVAIL < (unsigned long)
- (((double)check_log_space * 1024.0) / (double)statbuf.F_FRSIZE)
- ||
- statbuf.F_FAVAIL < check_log_inodes) rc = FALSE;
+
+
+/*************************************************
+* Check space on spool and log partitions *
+*************************************************/
+
+/* This function is called before accepting a message; if any thresholds are
+set, it checks them. If a message_size is supplied, it checks that there is
+enough space for that size plus the threshold - i.e. that the message won't
+reduce the space to the threshold. Not all OS have statvfs(); for those that
+don't, this function always returns TRUE. For some OS the old function and
+struct name statfs is used; that is handled by a macro, defined in exim.h.
+
+Arguments:
+ msg_size the (estimated) size of an incoming message
+
+Returns: FALSE if there isn't enough space, or if the information cannot
+ be obtained
+ TRUE if no check was done or there is enough space
+*/
+
+BOOL
+receive_check_fs(int msg_size)
+{
+int space, inodes;
+
+if (check_spool_space > 0 || msg_size > 0 || check_spool_inodes > 0)
+ {
+ space = receive_statvfs(TRUE, &inodes);
+
DEBUG(D_receive)
- debug_printf("log directory %s space = %d blocks; inodes = %d; "
- "check_space = %dK (%d blocks); inodes = %d\n",
- path, (int)statbuf.F_BAVAIL, (int)statbuf.F_FAVAIL,
- check_log_space,
- (int)(((double)check_log_space * 1024.0) / (double)statbuf.F_FRSIZE),
- check_log_inodes);
-
- if (!rc)
- {
- log_write(0, LOG_MAIN, "log directory space check failed: space=%d "
- "inodes=%d", (int)statbuf.F_BAVAIL, (int)statbuf.F_FAVAIL);
+ debug_printf("spool directory space = %dK inodes = %d "
+ "check_space = %dK inodes = %d msg_size = %d\n",
+ space, inodes, check_spool_space, check_spool_inodes, msg_size);
+
+ if ((space >= 0 && space < check_spool_space) ||
+ (inodes >= 0 && inodes < check_spool_inodes))
+ {
+ log_write(0, LOG_MAIN, "spool directory space check failed: space=%d "
+ "inodes=%d", space, inodes);
return FALSE;
}
}
-#endif
+if (check_log_space > 0 || check_log_inodes > 0)
+ {
+ space = receive_statvfs(FALSE, &inodes);
+
+ DEBUG(D_receive)
+ debug_printf("log directory space = %dK inodes = %d "
+ "check_space = %dK inodes = %d\n",
+ space, inodes, check_log_space, check_log_inodes);
+
+ if ((space >= 0 && space < check_log_space) ||
+ (inodes >= 0 && inodes < check_log_inodes))
+ {
+ log_write(0, LOG_MAIN, "log directory space check failed: space=%d "
+ "inodes=%d", space, inodes);
+ return FALSE;
+ }
+ }
+
return TRUE;
}
}
else
{
- if (filter_test == NULL)
+ if (filter_test == FTEST_NONE)
{
fprintf(stderr, "\nexim: %s received - message abandoned\n",
(sig == SIGTERM)? "SIGTERM" : "SIGINT");
recipients_list[recipients_count].address = recipient;
recipients_list[recipients_count].pno = pno;
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+recipients_list[recipients_count].bmi_optin = bmi_current_optin;
+/* reset optin string pointer for next recipient */
+bmi_current_optin = NULL;
+#endif
recipients_list[recipients_count++].errors_to = NULL;
}
DEBUG(D_receive|D_acl) debug_printf(" (after Received:)");
break;
+ case htype_add_rfc:
+ /* add header before any header which is NOT Received: or Resent- */
+ last_received = header_list;
+ while ( (last_received->next != NULL) &&
+ ( (header_testname(last_received->next, US"Received", 8, FALSE)) ||
+ (header_testname_incomplete(last_received->next, US"Resent-", 7, FALSE)) ) )
+ last_received = last_received->next;
+ /* last_received now points to the last Received: or Resent-* header
+ in an uninterrupted chain of those header types (seen from the beginning
+ of all headers. Our current header must follow it. */
+ h->next = last_received->next;
+ last_received->next = h;
+ DEBUG(D_receive|D_acl) debug_printf(" (before any non-Received: or Resent-*: header)");
+ break;
+
default:
h->next = NULL;
header_last->next = h;
blocks.
. If there is a "sender:" header and the message is locally originated,
- throw it away, unless the caller is trusted, or unless local_sender_retain
- is set - which can only happen if local_from_check is false.
+ throw it away, unless the caller is trusted, or unless
+ active_local_sender_retain is set - which can only happen if
+ active_local_from_check is false.
. If recipients are to be extracted from the message, build the
recipients list from the headers, removing any that were on the
. If the sender is local, check that from: is correct, and if not, generate
a Sender: header, unless message comes from a trusted caller, or this
- feature is disabled by no_local_from_check.
+ feature is disabled by active_local_from_check being false.
. If there is no "date" header, generate one, for locally-originated
or submission mode messages only.
if (domain == 0 && newsender[0] != 0)
newsender = rewrite_address_qualify(newsender, FALSE);
- if (filter_test != NULL || receive_check_set_sender(newsender))
+ if (filter_test != FTEST_NONE || receive_check_set_sender(newsender))
{
sender_address = newsender;
- if (trusted_caller || filter_test != NULL)
+ if (trusted_caller || filter_test != FTEST_NONE)
{
authenticated_sender = NULL;
originator_name = US"";
sender_local = FALSE;
}
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
printf("Sender taken from \"From \" line\n");
}
}
/* If this is a filter test run and no headers were read, output a warning
in case there is a mistake in the test message. */
-if (filter_test != NULL && header_list->next == NULL)
+if (filter_test != FTEST_NONE && header_list->next == NULL)
printf("Warning: no message headers read\n");
otherwise set. However, remove any <> that surround the address
because the variable doesn't have these. */
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
uschar *start = h->text + 12;
uschar *end = start + Ustrlen(start);
/* 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 local_sender_retain is set. (This can only be true if
- local_from_check is false.) If there are any resent- headers in the
+ 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 = ((
- (sender_local && !trusted_caller && !local_sender_retain) ||
- submission_mode
+ h->type = ((!active_local_sender_retain &&
+ ((sender_local && !trusted_caller) || submission_mode)
) &&
(!resents_exist||is_resent))?
htype_old : htype_sender;
Sender: header is inserted, as required. */
if (from_header != NULL &&
- (
- (sender_local && local_from_check && !trusted_caller) ||
- (submission_mode && authenticated_id != NULL)
+ (active_local_from_check &&
+ ((sender_local && !trusted_caller) ||
+ (submission_mode && authenticated_id != NULL))
))
{
BOOL make_sender = TRUE;
testing mode, that is all this function does. Return TRUE if the message
ended with a dot. */
-if (filter_test != NULL)
+if (filter_test != FTEST_NONE)
{
process_info[process_info_len] = 0;
return message_ended == END_DOT;
if (smtp_input && !smtp_batched_input)
{
+
+#ifdef WITH_CONTENT_SCAN
+ /* MIME ACL hook */
+ if (acl_smtp_mime != NULL && recipients_count > 0)
+ {
+ FILE *mbox_file;
+ uschar rfc822_file_path[2048];
+ unsigned long mbox_size;
+ header_line *my_headerlist;
+ uschar *user_msg, *log_msg;
+ int mime_part_count_buffer = -1;
+
+ memset(CS rfc822_file_path,0,2048);
+
+ /* check if it is a MIME message */
+ my_headerlist = header_list;
+ while (my_headerlist != NULL) {
+ /* skip deleted headers */
+ if (my_headerlist->type == '*') {
+ my_headerlist = my_headerlist->next;
+ continue;
+ };
+ if (strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0) {
+ DEBUG(D_receive) debug_printf("Found Content-Type: header - executing acl_smtp_mime.\n");
+ goto DO_MIME_ACL;
+ };
+ my_headerlist = my_headerlist->next;
+ };
+
+ DEBUG(D_receive) debug_printf("No Content-Type: header - presumably not a MIME message.\n");
+ goto NO_MIME_ACL;
+
+ DO_MIME_ACL:
+ /* make sure the eml mbox file is spooled up */
+ mbox_file = spool_mbox(&mbox_size);
+ if (mbox_file == NULL) {
+ /* error while spooling */
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "acl_smtp_mime: error while creating mbox spool file, message temporarily rejected.");
+ Uunlink(spool_name);
+ unspool_mbox();
+ smtp_respond(451, TRUE, US"temporary local problem");
+ message_id[0] = 0; /* Indicate no message accepted */
+ smtp_reply = US""; /* Indicate reply already sent */
+ goto TIDYUP; /* Skip to end of function */
+ };
+
+ mime_is_rfc822 = 0;
+
+ MIME_ACL_CHECK:
+ mime_part_count = -1;
+ rc = mime_acl_check(mbox_file, NULL, &user_msg, &log_msg);
+ fclose(mbox_file);
+
+ if (Ustrlen(rfc822_file_path) > 0) {
+ mime_part_count = mime_part_count_buffer;
+
+ if (unlink(CS rfc822_file_path) == -1) {
+ log_write(0, LOG_PANIC,
+ "acl_smtp_mime: can't unlink RFC822 spool file, skipping.");
+ goto END_MIME_ACL;
+ };
+ };
+
+ /* check if we must check any message/rfc822 attachments */
+ if (rc == OK) {
+ uschar temp_path[1024];
+ int n;
+ struct dirent *entry;
+ DIR *tempdir;
+
+ snprintf(CS temp_path, 1024, "%s/scan/%s", spool_directory, message_id);
+
+ tempdir = opendir(CS temp_path);
+ n = 0;
+ do {
+ 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);
+ debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path);
+ break;
+ };
+ } while (1);
+ closedir(tempdir);
+
+ if (entry != NULL) {
+ mbox_file = Ufopen(rfc822_file_path,"r");
+ if (mbox_file == NULL) {
+ log_write(0, LOG_PANIC,
+ "acl_smtp_mime: can't open RFC822 spool file, skipping.");
+ unlink(CS rfc822_file_path);
+ goto END_MIME_ACL;
+ };
+ /* set RFC822 expansion variable */
+ mime_is_rfc822 = 1;
+ mime_part_count_buffer = mime_part_count;
+ goto MIME_ACL_CHECK;
+ };
+ };
+
+ END_MIME_ACL:
+ add_acl_headers(US"MIME");
+ if (rc == DISCARD)
+ {
+ recipients_count = 0;
+ blackholed_by = US"MIME ACL";
+ }
+ else if (rc != OK)
+ {
+ Uunlink(spool_name);
+ unspool_mbox();
+ if (smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0)
+ smtp_yield = FALSE; /* No more messsages after dropped connection */
+ smtp_reply = US""; /* Indicate reply already sent */
+ message_id[0] = 0; /* Indicate no message accepted */
+ goto TIDYUP; /* Skip to end of function */
+ };
+ }
+
+ NO_MIME_ACL:
+#endif /* WITH_CONTENT_SCAN */
+
+
if (acl_smtp_data != NULL && recipients_count > 0)
{
uschar *user_msg, *log_msg;
else if (rc != OK)
{
Uunlink(spool_name);
+#ifdef WITH_CONTENT_SCAN
+ unspool_mbox();
+#endif
if (smtp_handle_acl_fail(ACL_WHERE_DATA, rc, user_msg, log_msg) != 0)
smtp_yield = FALSE; /* No more messsages after dropped connection */
smtp_reply = US""; /* Indicate reply already sent */
enable_dollar_recipients = FALSE;
}
+#ifdef WITH_CONTENT_SCAN
+unspool_mbox();
+#endif
+
/* The final check on the message is to run the scan_local() function. The
version supplied with Exim always accepts, but this is a hook for sysadmins to
supply their own checking code. The local_scan() function is run even when all
deliver_firsttime = TRUE;
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+if (bmi_run == 1) {
+ /* rewind data file */
+ lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+ bmi_verdicts = bmi_process_message(header_list, data_fd);
+};
+#endif
+
/* Update the timstamp in our Received: header to account for any time taken by
an ACL or by local_scan(). The new time is the time that all reception
processing is complete. */
{
if (smtp_reply == NULL)
{
- smtp_printf("250 OK id=%s\r\n", message_id);
+ if (fake_reject)
+ smtp_respond(550,TRUE,fake_reject_text);
+ else
+ smtp_printf("250 OK id=%s\r\n", message_id);
if (host_checking)
fprintf(stdout,
"\n**** SMTP testing: that is not a real message id!\n\n");
}
- else if (smtp_reply[0] != 0) smtp_printf("%.1024s\r\n", smtp_reply);
+ else if (smtp_reply[0] != 0)
+ {
+ if (fake_reject && (smtp_reply[0] == '2'))
+ smtp_respond(550,TRUE,fake_reject_text);
+ else
+ smtp_printf("%.1024s\r\n", smtp_reply);
+ }
}
/* For batched SMTP, generate an error message on failure, and do