*/
void
-receive_add_recipient(uschar *recipient, int pno)
+receive_add_recipient(uschar * recipient, int pno)
{
if (recipients_count >= recipients_list_max)
{
*/
static uschar *
-handle_lost_connection(uschar *s)
+handle_lost_connection(uschar * s)
{
log_write(L_lost_incoming_connection | L_smtp_connection, LOG_MAIN,
"%s lost while reading message data%s", smtp_get_connection_info(), s);
}
if (sender_ident)
g = string_append(g, 2, US" U=", sender_ident);
+if (LOGGING(connection_id))
+ g = string_fmt_append(g, " Ci=%lu", connection_id);
if (received_protocol)
g = string_append(g, 2, US" P=", received_protocol);
if (LOGGING(pipelining) && f.smtp_in_pipelining_advertised)
int header_size = 256;
int had_zero = 0;
int prevlines_length = 0;
-const int id_resolution = BASE_62 == 62 ? 5000 : 10000;
+const int id_resolution = BASE_62 == 62 && !host_number_string ? 1
+ : BASE_62 != 62 && host_number_string ? 4
+ : 2;
int ptr = 0;
that this has happened, in order to give a better error if there are
no recipients left. */
- else if (recipient != NULL)
+ else if (recipient)
{
if (tree_search(tree_nonrecipients, recipient) == NULL)
receive_add_recipient(recipient, -1);
/* Move on past this address */
- s = ss + (*ss? 1:0);
+ s = ss + (*ss ? 1 : 0);
while (isspace(*s)) s++;
} /* Next address */
}
/* Now build the unique message id. This has changed several times over the
-lifetime of Exim. This description was rewritten for Exim 4.14 (February 2003).
-Retaining all the history in the comment has become too unwieldy - read
-previous release sources if you want it.
-
-The message ID has 3 parts: tttttt-pppppp-ss. Each part is a number in base 62.
-The first part is the current time, in seconds. The second part is the current
-pid. Both are large enough to hold 32-bit numbers in base 62. The third part
-can hold a number in the range 0-3843. It used to be a computed sequence
-number, but is now the fractional component of the current time in units of
-1/2000 of a second (i.e. a value in the range 0-1999). After a message has been
-received, Exim ensures that the timer has ticked at the appropriate level
-before proceeding, to avoid duplication if the pid happened to be re-used
-within the same time period. It seems likely that most messages will take at
-least half a millisecond to be received, so no delay will normally be
-necessary. At least for some time...
+lifetime of Exim, and is changing for Exim 4.97.
+The previous change was in about 2003.
+
+Detail for the pre-4.97 version is here in [square-brackets].
+
+The message ID has 3 parts: tttttt-ppppppppppp-ssss (6, 11, 4 - total 23 with
+the dashes). Each part is a number in base 62.
+[ tttttt-pppppp-ss 6, 6, 2 => 16 ]
-There is a modification when localhost_number is set. Formerly this was allowed
-to be as large as 255. Now it is restricted to the range 0-16, and the final
-component of the message id becomes (localhost_number * 200) + fractional time
-in units of 1/200 of a second (i.e. a value in the range 0-3399).
+The first part is the current time, in seconds. Six chars is enough until
+year 3700 with case-sensitive filesystes, but will run out in 2038 on
+case-insensitive ones (Cygwin, Darwin - where we have to use base-36.
+Both of those are in the "unsupported" bucket, so ignore for now).
-Some not-really-Unix operating systems use case-insensitive file names (Darwin,
-Cygwin). For these, we have to use base 36 instead of base 62. Luckily, this
-still allows the tttttt field to hold a large enough number to last for some
-more decades, and the final two-digit field can hold numbers up to 1295, which
-is enough for milliseconds (instead of 1/2000 of a second).
+The second part is the current pid, and supports 64b [31b] PIDs.
-However, the pppppp field cannot hold a 32-bit pid, but it can hold a 31-bit
-pid, so it is probably safe because pids have to be positive. The
-localhost_number is restricted to 0-10 for these hosts, and when it is set, the
-final field becomes (localhost_number * 100) + fractional time in centiseconds.
+The third part holds sub-second time, plus (when localhost_number is set)
+the host number multiplied by a number large enough to keep it away from
+the time portion. Host numbers are restricted to the range 0-16.
+The time resolution is variously 1, 2 or 4 microseconds [0.5 or 1 ms]
+depending on the use of localhost_nubmer and of case-insensitive filesystems.
+
+After a message has been received, Exim ensures that the timer has ticked at the
+appropriate level before proceeding, to avoid duplication if the pid happened to
+be re-used within the same time period. It seems likely that most messages will
+take at least half a millisecond to be received, so no delay will normally be
+necessary. At least for some time...
-Note that string_base62() returns its data in a static storage block, so it
-must be copied before calling string_base62() again. It always returns exactly
-6 characters.
+Note that string_base62_XX() returns its data in a static storage block, so it
+must be copied before calling string_base62_XXX) again. It always returns exactly
+11 (_64) or 6 (_32) characters.
There doesn't seem to be anything in the RFC which requires a message id to
start with a letter, but Smail was changed to ensure this. The external form of
way. It appears in the initializing code in exim.c. The macro MESSAGE_ID_LENGTH
must also be changed to reflect the correct string length. The queue-sort code
needs to know the layout. Then, of course, other programs that rely on the
-message id format will need updating too. */
+message id format will need updating too (inc. at least exim_msgdate). */
-Ustrncpy(message_id, string_base62((long int)(message_id_tv.tv_sec)), 6);
-message_id[6] = '-';
-Ustrncpy(message_id + 7, string_base62((long int)getpid()), 6);
+Ustrncpy(message_id, string_base62_32((long int)(message_id_tv.tv_sec)), MESSAGE_ID_TIME_LEN);
+message_id[MESSAGE_ID_TIME_LEN] = '-';
+Ustrncpy(message_id + MESSAGE_ID_TIME_LEN + 1,
+ string_base62_64((long int)getpid()),
+ MESSAGE_ID_PID_LEN
+ );
/* Deal with the case where the host number is set. The value of the number was
checked when it was read, to ensure it isn't too big. */
if (host_number_string)
- sprintf(CS(message_id + MESSAGE_ID_LENGTH - 3), "-%2s",
- string_base62((long int)(
- host_number * (1000000/id_resolution) +
- message_id_tv.tv_usec/id_resolution)) + 4);
+ sprintf(CS(message_id + MESSAGE_ID_TIME_LEN + 1 + MESSAGE_ID_PID_LEN),
+ "-%" str(MESSAGE_ID_SUBTIME_LEN) "s",
+ string_base62_32((long int)(
+ host_number * (1000000/id_resolution)
+ + message_id_tv.tv_usec/id_resolution))
+ + (6 - MESSAGE_ID_SUBTIME_LEN)
+ );
/* Host number not set: final field is just the fractional time at an
appropriate resolution. */
else
- sprintf(CS(message_id + MESSAGE_ID_LENGTH - 3), "-%2s",
- string_base62((long int)(message_id_tv.tv_usec/id_resolution)) + 4);
+ sprintf(CS(message_id + MESSAGE_ID_TIME_LEN + 1 + MESSAGE_ID_PID_LEN),
+ "-%" str(MESSAGE_ID_SUBTIME_LEN) "s",
+ string_base62_32((long int)(message_id_tv.tv_usec/id_resolution))
+ + (6 - MESSAGE_ID_SUBTIME_LEN));
/* Add the current message id onto the current process info string if
it will fit. */
lock_data.l_type = F_WRLCK;
lock_data.l_whence = SEEK_SET;
lock_data.l_start = 0;
-lock_data.l_len = SPOOL_DATA_START_OFFSET;
+lock_data.l_len = spool_data_start_offset(message_id);
if (fcntl(data_fd, F_SETLK, &lock_data) < 0)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Cannot lock %s (%d): %s", spool_name,
}
else
{
- fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+ fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET);
give_local_error(ERRMESS_TOOBIG,
string_sprintf("message too big (max=%d)", thismessage_size_limit),
US"message rejected: ", error_rc, spool_data_file, header_list);
else
{
- fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+ fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET);
give_local_error(ERRMESS_IOERR, msg, US"", error_rc, spool_data_file,
header_list);
/* Does not return */
log_write(0, LOG_MAIN|LOG_PANIC, "%s %s found in headers",
message_id, bad_addresses ? "bad addresses" : "no recipients");
- fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+ fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET);
/* If configured to send errors to the sender, but this fails, force
a failure error code. We use a special one for no recipients so that it
/* Set the value of message_body_size for the DATA ACL and for local_scan() */
message_body_size = (fstat(data_fd, &statbuf) == 0)?
- statbuf.st_size - SPOOL_DATA_START_OFFSET : -1;
+ statbuf.st_size - spool_data_start_offset(message_id) : -1;
/* If an ACL from any RCPT commands set up any warning headers to add, do so
now, before running the DATA ACL. */
}
else
message_body_size = (fstat(data_fd, &statbuf) == 0)?
- statbuf.st_size - SPOOL_DATA_START_OFFSET : -1;
+ statbuf.st_size - spool_data_start_offset(message_id) : -1;
/* If an ACL is specified for checking things at this stage of reception of a
message, run it, unless all the recipients were removed by "discard" in earlier
/* Does not return */
else
{
- fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+ fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET);
give_local_error(ERRMESS_LOCAL_ACL, user_msg,
US"message rejected by non-SMTP ACL: ", error_rc, spool_data_file,
header_list);
supply their own checking code. The local_scan() function is run even when all
the recipients have been discarded. */
-lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+lseek(data_fd, (long int)spool_data_start_offset(message_id), SEEK_SET);
/* Arrange to catch crashes in local_scan(), so that the -D file gets
deleted, and the incident gets logged. */
if (rc == LOCAL_SCAN_ACCEPT)
{
if (local_scan_data)
- for (uschar * s = local_scan_data; *s != 0; s++) if (*s == '\n') *s = ' ';
- for (int i = 0; i < recipients_count; i++)
+ for (uschar * s = local_scan_data; *s; s++) if (*s == '\n') *s = ' ';
+ for (recipient_item * r = recipients_list;
+ r < recipients_list + recipients_count; r++)
{
- recipient_item *r = recipients_list + i;
r->address = rewrite_address_qualify(r->address, TRUE);
if (r->errors_to)
r->errors_to = rewrite_address_qualify(r->errors_to, TRUE);
break;
}
- g = string_append(NULL, 2, US"F=",
- sender_address[0] == 0 ? US"<>" : sender_address);
+ g = string_append(NULL, 2, US"F=", *sender_address ? sender_address : US"<>");
g = add_host_info_for_log(g);
- log_write(0, LOG_MAIN|LOG_REJECT, "%s %srejected by local_scan(): %.256s",
- string_from_gstring(g), istemp, string_printing(errmsg));
+ log_write(0, LOG_MAIN|LOG_REJECT, "%Y %srejected by local_scan(): %.256s",
+ g, istemp, string_printing(errmsg));
if (smtp_input)
if (!smtp_batched_input)
/* Does not return */
else
{
- fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+ fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET);
give_local_error(ERRMESS_LOCAL_SCAN, errmsg,
US"message rejected by local scan code: ", error_rc, spool_data_file,
header_list);
signal(SIGINT, SIG_IGN);
#endif /* HAVE_LOCAL_SCAN */
+/* If we are faking a reject or defer, avoid sennding a DSN for the
+actually-accepted message */
+
+if (fake_response != OK)
+ for (recipient_item * r = recipients_list;
+ r < recipients_list + recipients_count; r++)
+ {
+ DEBUG(D_receive) if (r->dsn_flags & (rf_notify_success | rf_notify_delay))
+ debug_printf("DSN: clearing flags due to fake-response for message\n");
+ r->dsn_flags = r->dsn_flags & ~(rf_notify_success | rf_notify_delay)
+ | rf_notify_never;
+ }
+
/* Ensure the first time flag is set in the newly-received message. */
#ifdef EXPERIMENTAL_BRIGHTMAIL
if (bmi_run == 1)
{ /* rewind data file */
- lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+ lseek(data_fd, (long int)spool_data_start_offset(message_id), SEEK_SET);
bmi_verdicts = bmi_process_message(header_list, data_fd);
}
#endif
}
else
{
- fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+ fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET);
give_local_error(ERRMESS_IOERR, errmsg, US"", error_rc, spool_data_file,
header_list);
/* Does not return */
}
else
{
- fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+ fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET);
give_local_error(ERRMESS_IOERR, errmsg, US"", error_rc, spool_data_file,
header_list);
/* Does not return */
}
fstat(data_fd, &statbuf);
-msg_size += statbuf.st_size - SPOOL_DATA_START_OFFSET + 1;
+msg_size += statbuf.st_size - spool_data_start_offset(message_id) + 1;
/* Generate a "message received" log entry. We do this by building up a dynamic
string as required. We log the arrival of a new message while the
g = string_append(g, 2,
fake_response == FAIL ? US"(= " : US"<= ",
- sender_address[0] == 0 ? US"<>" : sender_address);
+ *sender_address ? sender_address : US"<>");
if (message_reference)
g = string_append(g, 2, US" R=", message_reference);
gstring_reset(g);
g = string_cat(g, US"SMTP connection lost after final dot");
g = add_host_info_for_log(g);
- log_write(0, LOG_MAIN, "%s", string_from_gstring(g));
+ log_write(0, LOG_MAIN, "%Y", g);
/* Delete the files for this aborted message. */
log_write(0, LOG_MAIN |
(LOGGING(received_recipients) ? LOG_RECIPIENTS : 0) |
(LOGGING(received_sender) ? LOG_SENDER : 0),
- "%s", g->s);
+ "%Y", g);
/* Log any control actions taken by an ACL or local_scan(). */