*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for reading spool files. When compiling for a utility (eximon),
uschar * fname;
int save_errno;
- message_subdir[0] = split_spool_directory == i ? '\0' : id[5];
+ set_subdir_str(message_subdir, id, i);
fname = spool_fname(US"input", message_subdir, id, US"-D");
- DEBUG(D_deliver) debug_printf("Trying spool file %s\n", fname);
+ DEBUG(D_deliver) debug_printf_indent("Trying spool file %s\n", fname);
/* We protect against symlink attacks both in not propagating the
* file-descriptor to other processes as we exec, and also ensuring that we
if (fcntl(fd, F_SETLK, &lock_data) < 0)
{
- log_write(L_skip_delivery,
- LOG_MAIN,
- "Spool file is locked (another process is handling this message)");
+ log_write(L_skip_delivery, LOG_MAIN,
+ "Spool file for %s is locked (another process is handling this message)",
+ id);
(void)close(fd);
errno = 0;
return -1;
if (n < 5) return FALSE; /* malformed line */
buffer[n-1] = 0; /* Remove \n */
-node = store_get(sizeof(tree_node) + n - 3);
+node = store_get(sizeof(tree_node) + n - 3, TRUE); /* rcpt names tainted */
*connect = node;
Ustrcpy(node->name, buffer + 3);
node->data.ptr = NULL;
sender_host_address = NULL;
sender_host_name = NULL;
sender_host_port = 0;
-sender_host_authenticated = NULL;
+sender_host_authenticated = sender_host_auth_pubname = NULL;
sender_ident = NULL;
f.sender_local = FALSE;
f.sender_set_untrusted = FALSE;
# ifdef SUPPORT_DANE
tls_in.dane_verified = FALSE;
# endif
-tls_in.cipher = NULL;
+tls_in.ver = tls_in.cipher = NULL;
# ifndef COMPILE_UTILITY /* tls support fns not built in */
tls_free_cert(&tls_in.ourcert);
tls_free_cert(&tls_in.peercert);
dsn_envid = NULL;
}
+static void *
+fgets_big_buffer(FILE *fp)
+{
+int len = 0;
+
+big_buffer[0] = 0;
+if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) return NULL;
+
+while ((len = Ustrlen(big_buffer)) == big_buffer_size-1
+ && big_buffer[len-1] != '\n')
+ {
+ uschar *newbuffer;
+ int newsize;
+
+ if (big_buffer_size >= BIG_BUFFER_SIZE * 4) return NULL;
+ newsize = big_buffer_size * 2;
+ newbuffer = store_get_perm(newsize, FALSE);
+ memcpy(newbuffer, big_buffer, len);
+
+ big_buffer = newbuffer;
+ big_buffer_size = newsize;
+ if (Ufgets(big_buffer + len, big_buffer_size - len, fp) == NULL) return NULL;
+ }
+
+if (len <= 0 || big_buffer[len-1] != '\n') return NULL;
+return big_buffer;
+}
+
+
/*************************************************
* Read spool header file *
int rcount = 0;
long int uid, gid;
BOOL inheader = FALSE;
-uschar *p;
/* Reset all the global variables to their default values. However, there is
one exception. DO NOT change the default value of dont_deliver, because it may
for (int n = 0; n < 2; n++)
{
if (!subdir_set)
- message_subdir[0] = split_spool_directory == (n == 0) ? name[5] : 0;
+ set_subdir_str(message_subdir, name, n);
if ((fp = Ufopen(spool_fname(US"input", message_subdir, name, US""), "rb")))
break;
errno = 0;
#ifndef COMPILE_UTILITY
-DEBUG(D_deliver) debug_printf("reading spool file %s\n", name);
+DEBUG(D_deliver) debug_printf_indent("reading spool file %s\n", name);
#endif /* COMPILE_UTILITY */
/* The first line of a spool file contains the message id followed by -H (i.e.
if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR;
-p = big_buffer + Ustrlen(big_buffer);
-while (p > big_buffer && isspace(p[-1])) p--;
-*p = 0;
-if (!isdigit(p[-1])) goto SPOOL_FORMAT_ERROR;
-while (p > big_buffer && (isdigit(p[-1]) || '-' == p[-1])) p--;
-gid = Uatoi(p);
-if (p <= big_buffer || *(--p) != ' ') goto SPOOL_FORMAT_ERROR;
-*p = 0;
-if (!isdigit(p[-1])) goto SPOOL_FORMAT_ERROR;
-while (p > big_buffer && (isdigit(p[-1]) || '-' == p[-1])) p--;
-uid = Uatoi(p);
-if (p <= big_buffer || *(--p) != ' ') goto SPOOL_FORMAT_ERROR;
-*p = 0;
+ {
+ uschar *p = big_buffer + Ustrlen(big_buffer);
+ while (p > big_buffer && isspace(p[-1])) p--;
+ *p = 0;
+ if (!isdigit(p[-1])) goto SPOOL_FORMAT_ERROR;
+ while (p > big_buffer && (isdigit(p[-1]) || '-' == p[-1])) p--;
+ gid = Uatoi(p);
+ if (p <= big_buffer || *(--p) != ' ') goto SPOOL_FORMAT_ERROR;
+ *p = 0;
+ if (!isdigit(p[-1])) goto SPOOL_FORMAT_ERROR;
+ while (p > big_buffer && (isdigit(p[-1]) || '-' == p[-1])) p--;
+ uid = Uatoi(p);
+ if (p <= big_buffer || *(--p) != ' ') goto SPOOL_FORMAT_ERROR;
+ *p = 0;
+ }
originator_login = string_copy(big_buffer);
originator_uid = (uid_t)uid;
if (n < 3 || big_buffer[0] != '<' || big_buffer[n-2] != '>')
goto SPOOL_FORMAT_ERROR;
-sender_address = store_get(n-2);
+sender_address = store_get(n-2, TRUE); /* tainted */
Ustrncpy(sender_address, big_buffer+1, n-3);
sender_address[n-3] = 0;
if (sscanf(CS big_buffer, TIME_T_FMT " %d", &received_time.tv_sec, &warning_count) != 2)
goto SPOOL_FORMAT_ERROR;
received_time.tv_usec = 0;
+received_time_complete = received_time;
+
message_age = time(NULL) - received_time.tv_sec;
+#ifndef COMPILE_UTILITY
+if (f.running_in_test_harness)
+ message_age = test_harness_fudged_queue_time(message_age);
+#endif
#ifndef COMPILE_UTILITY
-DEBUG(D_deliver) debug_printf("user=%s uid=%ld gid=%ld sender=%s\n",
+DEBUG(D_deliver) debug_printf_indent("user=%s uid=%ld gid=%ld sender=%s\n",
originator_login, (long int)originator_uid, (long int)originator_gid,
sender_address);
-#endif /* COMPILE_UTILITY */
+#endif
/* Now there may be a number of optional lines, each starting with "-". If you
add a new setting here, make sure you set the default above.
To allow new versions of Exim that add additional flags to interwork with older
versions that do not understand them, just ignore any lines starting with "-"
that we don't recognize. Otherwise it wouldn't be possible to back off a new
-version that left new-style flags written on the spool. */
+version that left new-style flags written on the spool.
+
+If the line starts with "--" the content of the variable is tainted. */
-p = big_buffer + 2;
for (;;)
{
- int len;
- if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR;
+ BOOL tainted;
+ uschar * var;
+ const uschar * p;
+
+ if (fgets_big_buffer(fp) == NULL) goto SPOOL_READ_ERROR;
if (big_buffer[0] != '-') break;
- while ( (len = Ustrlen(big_buffer)) == big_buffer_size-1
- && big_buffer[len-1] != '\n'
- )
- { /* buffer not big enough for line; certs make this possible */
- uschar * buf;
- if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR;
- buf = store_get_perm(big_buffer_size *= 2);
- memcpy(buf, big_buffer, --len);
- big_buffer = buf;
- if (Ufgets(big_buffer+len, big_buffer_size-len, fp) == NULL)
- goto SPOOL_READ_ERROR;
- }
- big_buffer[len-1] = 0;
+ big_buffer[Ustrlen(big_buffer)-1] = 0;
+
+ tainted = big_buffer[1] == '-';
+ var = big_buffer + (tainted ? 2 : 1);
+ p = var + 1;
- switch(big_buffer[1])
+ switch(*var)
{
case 'a':
uschar *name, *endptr;
int count;
tree_node *node;
- endptr = Ustrchr(big_buffer + 6, ' ');
+ endptr = Ustrchr(var + 5, ' ');
if (endptr == NULL) goto SPOOL_FORMAT_ERROR;
- name = string_sprintf("%c%.*s", big_buffer[4],
- (int)(endptr - big_buffer - 6), big_buffer + 6);
+ name = string_sprintf("%c%.*s", var[3],
+ (int)(endptr - var - 5), var + 5);
if (sscanf(CS endptr, " %d", &count) != 1) goto SPOOL_FORMAT_ERROR;
node = acl_var_create(name);
- node->data.ptr = store_get(count + 1);
+ node->data.ptr = store_get(count + 1, tainted);
if (fread(node->data.ptr, 1, count+1, fp) < count) goto SPOOL_READ_ERROR;
((uschar*)node->data.ptr)[count] = 0;
}
f.allow_unqualified_sender = TRUE;
else if (Ustrncmp(p, "uth_id", 6) == 0)
- authenticated_id = string_copy(big_buffer + 9);
+ authenticated_id = string_copy_taint(var + 8, tainted);
else if (Ustrncmp(p, "uth_sender", 10) == 0)
- authenticated_sender = string_copy(big_buffer + 13);
+ authenticated_sender = string_copy_taint(var + 12, tainted);
else if (Ustrncmp(p, "ctive_hostname", 14) == 0)
- smtp_active_hostname = string_copy(big_buffer + 17);
+ smtp_active_hostname = string_copy_taint(var + 16, tainted);
/* For long-term backward compatibility, we recognize "-acl", which was
used before the number of ACL variables changed from 10 to 20. This was
unsigned index, count;
uschar name[20]; /* Need plenty of space for %u format */
tree_node * node;
- if ( sscanf(CS big_buffer + 5, "%u %u", &index, &count) != 2
+ if ( sscanf(CS var + 4, "%u %u", &index, &count) != 2
|| index >= 20
|| count > 16384 /* arbitrary limit on variable size */
)
else
(void) string_format(name, sizeof(name), "%c%u", 'm', index - 10);
node = acl_var_create(name);
- node->data.ptr = store_get(count + 1);
+ node->data.ptr = store_get(count + 1, tainted);
/* We sanity-checked the count, so disable the Coverity error */
/* coverity[tainted_data] */
if (fread(node->data.ptr, 1, count+1, fp) < count) goto SPOOL_READ_ERROR;
case 'b':
if (Ustrncmp(p, "ody_linecount", 13) == 0)
- body_linecount = Uatoi(big_buffer + 15);
+ body_linecount = Uatoi(var + 14);
else if (Ustrncmp(p, "ody_zerocount", 13) == 0)
- body_zerocount = Uatoi(big_buffer + 15);
+ body_zerocount = Uatoi(var + 14);
#ifdef EXPERIMENTAL_BRIGHTMAIL
else if (Ustrncmp(p, "mi_verdicts ", 12) == 0)
- bmi_verdicts = string_copy(big_buffer + 14);
+ bmi_verdicts = string_copy_taint(var + 13, tainted);
#endif
break;
f.deliver_firsttime = TRUE;
/* Check if the dsn flags have been set in the header file */
else if (Ustrncmp(p, "sn_ret", 6) == 0)
- dsn_ret= atoi(CS big_buffer + 8);
+ dsn_ret= atoi(CS var + 7);
else if (Ustrncmp(p, "sn_envid", 8) == 0)
- dsn_envid = string_copy(big_buffer + 11);
+ dsn_envid = string_copy_taint(var + 10, tainted);
break;
case 'f':
if (Ustrncmp(p, "rozen", 5) == 0)
{
f.deliver_freeze = TRUE;
- if (sscanf(CS big_buffer+7, TIME_T_FMT, &deliver_frozen_at) != 1)
+ if (sscanf(CS var+6, TIME_T_FMT, &deliver_frozen_at) != 1)
goto SPOOL_READ_ERROR;
}
break;
host_lookup_deferred = TRUE;
else if (Ustrcmp(p, "ost_lookup_failed") == 0)
host_lookup_failed = TRUE;
+ else if (Ustrncmp(p, "ost_auth_pubname", 16) == 0)
+ sender_host_auth_pubname = string_copy_taint(var + 18, tainted);
else if (Ustrncmp(p, "ost_auth", 8) == 0)
- sender_host_authenticated = string_copy(big_buffer + 11);
+ sender_host_authenticated = string_copy_taint(var + 10, tainted);
else if (Ustrncmp(p, "ost_name", 8) == 0)
- sender_host_name = string_copy(big_buffer + 11);
+ sender_host_name = string_copy_taint(var + 10, tainted);
else if (Ustrncmp(p, "elo_name", 8) == 0)
- sender_helo_name = string_copy(big_buffer + 11);
+ sender_helo_name = string_copy_taint(var + 10, tainted);
/* We now record the port number after the address, separated by a
dot. For compatibility during upgrading, do nothing if there
else if (Ustrncmp(p, "ost_address", 11) == 0)
{
- sender_host_port = host_address_extract_port(big_buffer + 14);
- sender_host_address = string_copy(big_buffer + 14);
+ sender_host_port = host_address_extract_port(var + 13);
+ sender_host_address = string_copy_taint(var + 13, tainted);
}
break;
case 'i':
if (Ustrncmp(p, "nterface_address", 16) == 0)
{
- interface_port = host_address_extract_port(big_buffer + 19);
- interface_address = string_copy(big_buffer + 19);
+ interface_port = host_address_extract_port(var + 18);
+ interface_address = string_copy_taint(var + 18, tainted);
}
else if (Ustrncmp(p, "dent", 4) == 0)
- sender_ident = string_copy(big_buffer + 7);
+ sender_ident = string_copy_taint(var + 6, tainted);
break;
case 'l':
if (Ustrcmp(p, "ocal") == 0)
f.sender_local = TRUE;
- else if (Ustrcmp(big_buffer, "-localerror") == 0)
+ else if (Ustrcmp(var, "localerror") == 0)
f.local_error_message = TRUE;
#ifdef HAVE_LOCAL_SCAN
else if (Ustrncmp(p, "ocal_scan ", 10) == 0)
- local_scan_data = string_copy(big_buffer + 12);
+ local_scan_data = string_copy_taint(var + 11, tainted);
#endif
break;
case 'm':
- if (Ustrcmp(p, "anual_thaw") == 0) f.deliver_manual_thaw = TRUE;
+ if (Ustrcmp(p, "anual_thaw") == 0)
+ f.deliver_manual_thaw = TRUE;
else if (Ustrncmp(p, "ax_received_linelength", 22) == 0)
- max_received_linelength = Uatoi(big_buffer + 24);
+ max_received_linelength = Uatoi(var + 23);
break;
case 'N':
case 'r':
if (Ustrncmp(p, "eceived_protocol", 16) == 0)
- received_protocol = string_copy(big_buffer + 19);
+ received_protocol = string_copy_taint(var + 18, tainted);
else if (Ustrncmp(p, "eceived_time_usec", 17) == 0)
{
unsigned usec;
- if (sscanf(CS big_buffer + 21, "%u", &usec) == 1)
+ if (sscanf(CS var + 20, "%u", &usec) == 1)
+ {
received_time.tv_usec = usec;
+ if (!received_time_complete.tv_sec) received_time_complete.tv_usec = usec;
+ }
+ }
+ else if (Ustrncmp(p, "eceived_time_complete", 21) == 0)
+ {
+ unsigned sec, usec;
+ if (sscanf(CS var + 23, "%u.%u", &sec, &usec) == 2)
+ {
+ received_time_complete.tv_sec = sec;
+ received_time_complete.tv_usec = usec;
+ }
}
break;
f.sender_set_untrusted = TRUE;
#ifdef WITH_CONTENT_SCAN
else if (Ustrncmp(p, "pam_bar ", 8) == 0)
- spam_bar = string_copy(big_buffer + 10);
+ spam_bar = string_copy_taint(var + 9, tainted);
else if (Ustrncmp(p, "pam_score ", 10) == 0)
- spam_score = string_copy(big_buffer + 12);
+ spam_score = string_copy_taint(var + 11, tainted);
else if (Ustrncmp(p, "pam_score_int ", 14) == 0)
- spam_score_int = string_copy(big_buffer + 16);
+ spam_score_int = string_copy_taint(var + 15, tainted);
#endif
#ifndef COMPILE_UTILITY
else if (Ustrncmp(p, "pool_file_wireformat", 20) == 0)
case 't':
if (Ustrncmp(p, "ls_", 3) == 0)
{
- uschar * q = p + 3;
+ const uschar * q = p + 3;
if (Ustrncmp(q, "certificate_verified", 20) == 0)
tls_in.certificate_verified = TRUE;
else if (Ustrncmp(q, "cipher", 6) == 0)
- tls_in.cipher = string_copy(big_buffer + 12);
+ tls_in.cipher = string_copy_taint(q+7, tainted);
# ifndef COMPILE_UTILITY /* tls support fns not built in */
else if (Ustrncmp(q, "ourcert", 7) == 0)
- (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert);
+ (void) tls_import_cert(q+8, &tls_in.ourcert);
else if (Ustrncmp(q, "peercert", 8) == 0)
- (void) tls_import_cert(big_buffer + 14, &tls_in.peercert);
+ (void) tls_import_cert(q+9, &tls_in.peercert);
# endif
else if (Ustrncmp(q, "peerdn", 6) == 0)
- tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
+ tls_in.peerdn = string_unprinting(string_copy_taint(q+7, tainted));
else if (Ustrncmp(q, "sni", 3) == 0)
- tls_in.sni = string_unprinting(string_copy(big_buffer + 9));
+ tls_in.sni = string_unprinting(string_copy_taint(q+4, tainted));
else if (Ustrncmp(q, "ocsp", 4) == 0)
- tls_in.ocsp = big_buffer[10] - '0';
-# ifdef EXPERIMENTAL_TLS_RESUME
+ tls_in.ocsp = q[5] - '0';
+# ifndef DISABLE_TLS_RESUME
else if (Ustrncmp(q, "resumption", 10) == 0)
- tls_in.resumption = big_buffer[16] - 'A';
+ tls_in.resumption = q[11] - 'A';
# endif
-
+ else if (Ustrncmp(q, "ver", 3) == 0)
+ tls_in.ver = string_copy_taint(q+4, tainted);
}
break;
#endif
#ifndef COMPILE_UTILITY
DEBUG(D_deliver)
- debug_printf("sender_local=%d ident=%s\n", f.sender_local,
- (sender_ident == NULL)? US"unset" : sender_ident);
+ debug_printf_indent("sender_local=%d ident=%s\n", f.sender_local,
+ sender_ident ? sender_ident : US"unset");
#endif /* COMPILE_UTILITY */
/* We now have the tree of addresses NOT to deliver to, or a line
goto SPOOL_FORMAT_ERROR;
#ifndef COMPILE_UTILITY
-DEBUG(D_deliver)
- {
- debug_printf("Non-recipients:\n");
- debug_print_tree(tree_nonrecipients);
- }
+DEBUG(D_deliver) debug_print_tree("Non-recipients", tree_nonrecipients);
#endif /* COMPILE_UTILITY */
/* After reading the tree, the next line has not yet been read into the
goto SPOOL_FORMAT_ERROR;
#ifndef COMPILE_UTILITY
-DEBUG(D_deliver) debug_printf("recipients_count=%d\n", rcount);
+DEBUG(D_deliver) debug_printf_indent("recipients_count=%d\n", rcount);
#endif /* COMPILE_UTILITY */
recipients_list_max = rcount;
-recipients_list = store_get(rcount * sizeof(recipient_item));
+recipients_list = store_get(rcount * sizeof(recipient_item), FALSE);
/* We sanitised the count and know we have enough memory, so disable
the Coverity error on recipients_count */
uschar *errors_to = NULL;
uschar *p;
- if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR;
+ if (fgets_big_buffer(fp) == NULL) goto SPOOL_READ_ERROR;
nn = Ustrlen(big_buffer);
if (nn < 2) goto SPOOL_FORMAT_ERROR;
if (*p == ',')
{
int dummy;
+#if !defined (COMPILE_UTILITY)
+ DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - Exim 3 spool file\n");
+#endif
while (isdigit(*(--p)) || *p == ',');
if (*p == ' ')
{
else if (*p == ' ')
{
+#if !defined (COMPILE_UTILITY)
+ DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - early Exim 4 spool file\n");
+#endif
*p++ = 0;
(void)sscanf(CS p, "%d", &pno);
}
int flags;
#if !defined (COMPILE_UTILITY)
- DEBUG(D_deliver) debug_printf("**** SPOOL_IN - Exim standard format spoolfile\n");
+ DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - Exim standard format spoolfile\n");
#endif
(void)sscanf(CS p+1, "%d", &flags);
if (len > 0)
{
p -= len;
- errors_to = string_copy(p);
+ errors_to = string_copy_taint(p, TRUE);
}
}
if (len > 0)
{
p -= len;
- orcpt = string_copy(p);
+ orcpt = string_copy_taint(p, TRUE);
}
}
}
#if !defined(COMPILE_UTILITY)
else
- { DEBUG(D_deliver) debug_printf("**** SPOOL_IN - No additional fields\n"); }
+ { DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - No additional fields\n"); }
if (orcpt || dsn_flags)
- DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: <%s> orcpt: <%s> dsn_flags: 0x%x\n",
+ DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - address: <%s> orcpt: <%s> dsn_flags: 0x%x\n",
big_buffer, orcpt, dsn_flags);
if (errors_to)
- DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: <%s> errorsto: <%s>\n",
+ DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - address: <%s> errorsto: <%s>\n",
big_buffer, errors_to);
#endif
- recipients_list[recipients_count].address = string_copy(big_buffer);
+ recipients_list[recipients_count].address = string_copy_taint(big_buffer, TRUE);
recipients_list[recipients_count].pno = pno;
recipients_list[recipients_count].errors_to = errors_to;
recipients_list[recipients_count].orcpt = orcpt;
if (read_headers)
{
- h = store_get(sizeof(header_line));
+ h = store_get(sizeof(header_line), FALSE);
h->next = NULL;
h->type = flag[0];
h->slen = n;
- h->text = store_get(n+1);
+ h->text = store_get(n+1, TRUE); /* tainted */
if (h->type == htype_received) received_count++;
- if (header_list == NULL) header_list = h;
- else header_last->next = h;
+ if (header_list) header_last->next = h;
+ else header_list = h;
header_last = h;
for (i = 0; i < n; i++)
and give a positive response. */
#ifndef COMPILE_UTILITY
-DEBUG(D_deliver) debug_printf("body_linecount=%d message_linecount=%d\n",
+DEBUG(D_deliver) debug_printf_indent("body_linecount=%d message_linecount=%d\n",
body_linecount, message_linecount);
#endif /* COMPILE_UTILITY */
fclose(fp);
errno = n;
- return inheader? spool_read_hdrerror : spool_read_enverror;
+ return inheader ? spool_read_hdrerror : spool_read_enverror;
}
SPOOL_FORMAT_ERROR:
return inheader? spool_read_hdrerror : spool_read_enverror;
}
+
+#ifndef COMPILE_UTILITY
+/* Read out just the (envelope) sender string from the spool -H file.
+Remove the <> wrap and return it in allocated store. Return NULL on error.
+
+We assume that message_subdir is already set.
+*/
+
+uschar *
+spool_sender_from_msgid(const uschar * id)
+{
+uschar * name = string_sprintf("%s-H", id);
+FILE * fp;
+int n;
+uschar * yield = NULL;
+
+if (!(fp = Ufopen(spool_fname(US"input", message_subdir, name, US""), "rb")))
+ return NULL;
+
+DEBUG(D_deliver) debug_printf_indent("reading spool file %s\n", name);
+
+/* Skip the line with the copy of the filename, then the line with login/uid/gid.
+Read the next line, which should be the envelope sender.
+Do basic validation on that. */
+
+if ( Ufgets(big_buffer, big_buffer_size, fp) != NULL
+ && Ufgets(big_buffer, big_buffer_size, fp) != NULL
+ && Ufgets(big_buffer, big_buffer_size, fp) != NULL
+ && (n = Ustrlen(big_buffer)) >= 3
+ && big_buffer[0] == '<' && big_buffer[n-2] == '>'
+ )
+ {
+ yield = store_get(n-2, TRUE); /* tainted */
+ Ustrncpy(yield, big_buffer+1, n-3);
+ yield[n-3] = 0;
+ }
+fclose(fp);
+return yield;
+}
+#endif /* COMPILE_UTILITY */
+
/* vi: aw ai sw=2
*/
/* End of spool_in.c */