* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) The Exim Maintainers 2020 - 2023 */
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* SPDX-License-Identifier: GPL-2.0-or-later */
*/
BOOL
-write_chunk(transport_ctx * tctx, uschar *chunk, int len)
+write_chunk(transport_ctx * tctx, const uschar * chunk, int len)
{
-uschar *start = chunk;
-uschar *end = chunk + len;
+const uschar * start = chunk;
+const uschar * end = chunk + len;
int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2;
/* The assumption is made that the check string will never stretch over move
for possible escaping. The code for the non-NL route should be as fast as
possible. */
-for (uschar * ptr = start; ptr < end; ptr++)
+for (const uschar * ptr = start; ptr < end; ptr++)
{
int ch, len;
Returns: a string
*/
-uschar *
+const uschar *
transport_rcpt_address(address_item *addr, BOOL include_affixes)
{
uschar *at;
*/
BOOL
transport_headers_send(transport_ctx * tctx,
- BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len))
+ BOOL (*sendfn)(transport_ctx * tctx, const uschar * s, int len))
{
const uschar * list;
transport_instance * tblock = tctx ? tctx->tblock : NULL;
void
transport_update_waiting(host_item * hostlist, uschar * tpname)
{
-const uschar *prevname = US"";
-open_db dbblock;
-open_db *dbm_file;
+const uschar * prevname = US"";
+open_db dbblock, * dbp;
if (!is_new_message_id(message_id))
{
DEBUG(D_transport) debug_printf("message_id %s is not new format; "
- "skipping wait-%s database update\n", tpname);
+ "skipping wait-%s database update\n", message_id, tpname);
return;
}
/* Open the database for this transport */
-if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", tpname),
+if (!(dbp = dbfn_open(string_sprintf("wait-%.200s", tpname),
O_RDWR, &dbblock, TRUE, TRUE)))
return;
/* Look up the host record; if there isn't one, make an empty one. */
- if (!(host_record = dbfn_read(dbm_file, host->name)))
+ if (!(host_record = dbfn_read(dbp, host->name)))
{
host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH, GET_UNTAINTED);
host_record->count = host_record->sequence = 0;
debug_printf_indent("NOTE: old or corrupt message-id found in wait=%.200s"
" hints DB; deleting records for %s\n", tpname, host->name);
- (void) dbfn_delete(dbm_file, host->name);
+ (void) dbfn_delete(dbp, host->name);
for (int i = host_record->sequence - 1; i >= 0; i--)
- (void) dbfn_delete(dbm_file,
+ (void) dbfn_delete(dbp,
(sprintf(CS buffer, "%.200s:%d", host->name, i), buffer));
host_record->count = host_record->sequence = 0;
{
dbdata_wait *cont;
sprintf(CS buffer, "%.200s:%d", host->name, i);
- if ((cont = dbfn_read(dbm_file, buffer)))
+ if ((cont = dbfn_read(dbp, buffer)))
{
int clen = cont->count * MESSAGE_ID_LENGTH;
for (uschar * s = cont->text; s < cont->text + clen; s += MESSAGE_ID_LENGTH)
if (host_record->count >= WAIT_NAME_MAX)
{
sprintf(CS buffer, "%.200s:%d", host->name, host_record->sequence);
- dbfn_write(dbm_file, buffer, host_record, sizeof(dbdata_wait) + host_length);
+ dbfn_write(dbp, buffer, host_record, sizeof(dbdata_wait) + host_length);
#ifndef DISABLE_QUEUE_RAMP
if (f.queue_2stage && queue_fast_ramp && !queue_run_in_order)
queue_notify_daemon(message_id);
/* Update the database */
- dbfn_write(dbm_file, host->name, host_record, sizeof(dbdata_wait) + host_length);
+ dbfn_write(dbp, host->name, host_record, sizeof(dbdata_wait) + host_length);
DEBUG(D_transport) debug_printf("added %.*s to queue for %s\n",
MESSAGE_ID_LENGTH, message_id, host->name);
}
/* All now done */
-dbfn_close(dbm_file);
+dbfn_close(dbp);
}
current continue sequence is greater than the maximum supplied as an argument,
or greater than the global connection_max_messages, which, if set, overrides.
+It is also called if conditions are otherwise right for pipelining a QUIT after
+the message data, since if there is another message waiting we do not want to
+send that QUIT.
+
Arguments:
transport_name name of the transport
hostname name of the host
{
dbdata_wait * host_record;
int host_length;
-open_db dbblock;
-open_db * dbm_file;
+open_db dbblock, * dbp;
int i;
struct stat statbuf;
/* Open the waiting information database. */
-if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name),
+if (!(dbp = dbfn_open(string_sprintf("wait-%.200s", transport_name),
O_RDWR, &dbblock, TRUE, TRUE)))
goto retfalse;
/* See if there is a record for this host; if not, there's nothing to do. */
-if (!(host_record = dbfn_read(dbm_file, hostname)))
+if (!(host_record = dbfn_read(dbp, hostname)))
{
- dbfn_close(dbm_file);
DEBUG(D_transport) debug_printf_indent("no messages waiting for %s\n", hostname);
- goto retfalse;
+ goto dbclose_false;
}
/* If the data in the record looks corrupt, just log something and
if (host_record->count > WAIT_NAME_MAX)
{
- dbfn_close(dbm_file);
log_write(0, LOG_MAIN|LOG_PANIC, "smtp-wait database entry for %s has bad "
"count=%d (max=%d)", hostname, host_record->count, WAIT_NAME_MAX);
- goto retfalse;
+ goto dbclose_false;
}
/* Scan the message ids in the record from the end towards the beginning,
DEBUG(D_hints_lookup)
debug_printf_indent("NOTE: old or corrupt message-id found in wait=%.200s"
" hints DB; deleting records for %s\n", transport_name, hostname);
- (void) dbfn_delete(dbm_file, hostname);
+ (void) dbfn_delete(dbp, hostname);
for (int i = host_record->sequence - 1; i >= 0; i--)
- (void) dbfn_delete(dbm_file,
+ (void) dbfn_delete(dbp,
(sprintf(CS buffer, "%.200s:%d", hostname, i), buffer));
- dbfn_close(dbm_file);
- goto retfalse;
+ goto dbclose_false;
}
msgq[i].bKeep = TRUE;
for (int i = host_record->sequence - 1; i >= 0 && !newr; i--)
{
sprintf(CS buffer, "%.200s:%d", hostname, i);
- newr = dbfn_read(dbm_file, buffer);
+ newr = dbfn_read(dbp, buffer);
}
/* If no continuation, delete the current and break the loop */
if (!newr)
{
- dbfn_delete(dbm_file, hostname);
+ dbfn_delete(dbp, hostname);
break;
}
/* Else replace the current with the continuation */
- dbfn_delete(dbm_file, buffer);
+ dbfn_delete(dbp, buffer);
host_record = newr;
host_length = host_record->count * MESSAGE_ID_LENGTH;
if (host_length <= 0)
{
- dbfn_close(dbm_file);
DEBUG(D_transport) debug_printf_indent("waiting messages already delivered\n");
- goto retfalse;
+ goto dbclose_false;
}
/* we were not able to find an acceptable message, nor was there a
- * continuation record. So bug out, outer logic will clean this up.
- */
+ continuation record. So bug out, outer logic will clean this up.
+ */
if (!bContinuation)
{
Ustrcpy(new_message_id, message_id);
- dbfn_close(dbm_file);
- goto retfalse;
+ goto dbclose_false;
}
} /* we need to process a continuation record */
if (host_length > 0)
{
host_record->count = host_length/MESSAGE_ID_LENGTH;
- dbfn_write(dbm_file, hostname, host_record, (int)sizeof(dbdata_wait) + host_length);
+ dbfn_write(dbp, hostname, host_record, (int)sizeof(dbdata_wait) + host_length);
}
-dbfn_close(dbm_file);
-DEBUG(D_transport) {acl_level--; debug_printf("transport_check_waiting: TRUE\n"); }
+dbfn_close(dbp);
+
+DEBUG(D_transport)
+ {
+ acl_level--;
+ debug_printf("transport_check_waiting: TRUE (found %s)\n", new_message_id);
+ }
return TRUE;
+dbclose_false:
+ dbfn_close(dbp);
+
retfalse:
-DEBUG(D_transport) {acl_level--; debug_printf("transport_check_waiting: FALSE\n"); }
-return FALSE;
+ DEBUG(D_transport)
+ {acl_level--; debug_printf("transport_check_waiting: FALSE\n"); }
+ return FALSE;
}
/*************************************************
#ifndef DISABLE_TLS
if (smtp_peer_options & OPTION_TLS) i += 6;
#endif
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
if (continue_limit_mail || continue_limit_rcpt || continue_limit_rcptdom)
i += 4;
#endif
argv[i++] = US"-MCT";
#endif
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
if (continue_limit_rcpt || continue_limit_rcptdom)
{
argv[i++] = US"-MCL";
BOOL
transport_pass_socket(const uschar *transport_name, const uschar *hostname,
const uschar *hostaddress, uschar *id, int socket_fd
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
, unsigned peer_limit_mail, unsigned peer_limit_rcpt, unsigned peer_limit_rcptdom
#endif
)
DEBUG(D_transport) debug_printf("transport_pass_socket entered\n");
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
continue_limit_mail = peer_limit_mail;
continue_limit_rcpt = peer_limit_rcpt;
continue_limit_rcptdom = peer_limit_rcptdom;
#endif
-if ((pid = exim_fork(US"continued-transport-interproc")) == 0)
+if ((pid = exim_fork(US"continued-transport")) == 0)
{
- /* Disconnect entirely from the parent process. If we are running in the
- test harness, wait for a bit to allow the previous process time to finish,
- write the log, etc., so that the output is always in the same order for
- automatic comparison. */
-
- if ((pid = exim_fork(US"continued-transport")) != 0)
- _exit(EXIT_SUCCESS);
- testharness_pause_ms(1000);
+ /* If we are running in the test harness, wait for a bit to allow the
+ previous process time to finish, write the log, etc., so that the output is
+ always in the same order for automatic comparison. */
+ testharness_pause_ms(500);
transport_do_pass_socket(transport_name, hostname, hostaddress,
id, socket_fd);
+ /*NOTREACHED*/
}
-/* If the process creation succeeded, wait for the first-level child, which
-immediately exits, leaving the second level process entirely disconnected from
-this one. */
-
if (pid > 0)
- {
- int rc;
- while ((rc = wait(&status)) != pid && (rc >= 0 || errno != ECHILD));
return TRUE;
- }
-else
- {
- DEBUG(D_transport) debug_printf("transport_pass_socket failed to fork: %s\n",
+
+DEBUG(D_transport) debug_printf("transport_pass_socket failed to fork: %s\n",
strerror(errno));
- return FALSE;
- }
+return FALSE;
}
address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), GET_UNTAINTED);
/* +1 because addr->local_part[0] == '|' since af_force_command is set */
- s = expand_string(addr->local_part + 1);
+ s = expand_cstring(addr->local_part + 1);
if (!s || !*s)
{