* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
int autoreply_transport_options_count =
sizeof(autoreply_transport_options)/sizeof(optionlist);
+
+#ifdef MACRO_PREDEF
+
+/* Dummy values */
+autoreply_transport_options_block autoreply_transport_option_defaults = {0};
+void autoreply_transport_init(transport_instance *tblock) {}
+BOOL autoreply_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
+
+#else /*!MACRO_PREDEF*/
+
+
/* Default private options block for the autoreply transport. */
autoreply_transport_options_block autoreply_transport_option_defaults = {
static uschar *
checkexpand(uschar *s, address_item *addr, uschar *name, int type)
{
-uschar *t;
uschar *ss = expand_string(s);
if (ss == NULL)
return NULL;
}
-if (type != cke_text) for (t = ss; *t != 0; t++)
+if (type != cke_text) for (uschar * t = ss; *t != 0; t++)
{
int c = *t;
const uschar * sp;
header_line *h;
time_t now = time(NULL);
time_t once_repeat_sec = 0;
-FILE *f;
+FILE *fp;
FILE *ff = NULL;
autoreply_transport_options_block *ob =
file_expand = ob->file_expand;
return_message = ob->return_message;
- if ((from != NULL &&
- (from = checkexpand(from, addr, tblock->name, cke_hdr)) == NULL) ||
- (reply_to != NULL &&
- (reply_to = checkexpand(reply_to, addr, tblock->name, cke_hdr)) == NULL) ||
- (to != NULL &&
- (to = checkexpand(to, addr, tblock->name, cke_hdr)) == NULL) ||
- (cc != NULL &&
- (cc = checkexpand(cc, addr, tblock->name, cke_hdr)) == NULL) ||
- (bcc != NULL &&
- (bcc = checkexpand(bcc, addr, tblock->name, cke_hdr)) == NULL) ||
- (subject != NULL &&
- (subject = checkexpand(subject, addr, tblock->name, cke_hdr)) == NULL) ||
- (headers != NULL &&
- (headers = checkexpand(headers, addr, tblock->name, cke_text)) == NULL) ||
- (text != NULL &&
- (text = checkexpand(text, addr, tblock->name, cke_text)) == NULL) ||
- (file != NULL &&
- (file = checkexpand(file, addr, tblock->name, cke_file)) == NULL) ||
- (logfile != NULL &&
- (logfile = checkexpand(logfile, addr, tblock->name, cke_file)) == NULL) ||
- (oncelog != NULL &&
- (oncelog = checkexpand(oncelog, addr, tblock->name, cke_file)) == NULL) ||
- (oncerepeat != NULL &&
- (oncerepeat = checkexpand(oncerepeat, addr, tblock->name, cke_file)) == NULL))
+ if ( from && !(from = checkexpand(from, addr, tblock->name, cke_hdr))
+ || reply_to && !(reply_to = checkexpand(reply_to, addr, tblock->name, cke_hdr))
+ || to && !(to = checkexpand(to, addr, tblock->name, cke_hdr))
+ || cc && !(cc = checkexpand(cc, addr, tblock->name, cke_hdr))
+ || bcc && !(bcc = checkexpand(bcc, addr, tblock->name, cke_hdr))
+ || subject && !(subject = checkexpand(subject, addr, tblock->name, cke_hdr))
+ || headers && !(headers = checkexpand(headers, addr, tblock->name, cke_text))
+ || text && !(text = checkexpand(text, addr, tblock->name, cke_text))
+ || file && !(file = checkexpand(file, addr, tblock->name, cke_file))
+ || logfile && !(logfile = checkexpand(logfile, addr, tblock->name, cke_file))
+ || oncelog && !(oncelog = checkexpand(oncelog, addr, tblock->name, cke_file))
+ || oncerepeat && !(oncerepeat = checkexpand(oncerepeat, addr, tblock->name, cke_file))
+ )
return FALSE;
- if (oncerepeat != NULL)
+ if (oncerepeat)
{
once_repeat_sec = readconf_readtime(oncerepeat, 0, FALSE);
if (once_repeat_sec < 0)
/* If the never_mail option is set, we have to scan all the recipients and
remove those that match. */
-if (ob->never_mail != NULL)
+if (ob->never_mail)
{
const uschar *never_mail = expand_string(ob->never_mail);
- if (never_mail == NULL)
+ if (!never_mail)
{
addr->transport_return = FAIL;
addr->message = string_sprintf("Failed to expand \"%s\" for "
return FALSE;
}
- if (to != NULL) check_never_mail(&to, never_mail);
- if (cc != NULL) check_never_mail(&cc, never_mail);
- if (bcc != NULL) check_never_mail(&bcc, never_mail);
+ if (to) check_never_mail(&to, never_mail);
+ if (cc) check_never_mail(&cc, never_mail);
+ if (bcc) check_never_mail(&bcc, never_mail);
- if (to == NULL && cc == NULL && bcc == NULL)
+ if (!to && !cc && !bcc)
{
DEBUG(D_transport)
debug_printf("*** all recipients removed by never_mail\n");
/* If the -N option is set, can't do any more. */
-if (dont_deliver)
+if (f.dont_deliver)
{
DEBUG(D_transport)
debug_printf("*** delivery by %s transport bypassed by -N option\n",
set, instead of a dbm file, we use a regular file containing a circular buffer
recipient cache. */
-if (oncelog != NULL && *oncelog != 0 && to != NULL)
+if (oncelog && *oncelog != 0 && to)
{
time_t then = 0;
if (ob->once_file_size > 0)
{
- uschar *p;
+ uschar * nextp;
struct stat statbuf;
cache_fd = Uopen(oncelog, O_CREAT|O_RDWR, ob->mode);
cache_size = statbuf.st_size;
add_size = sizeof(time_t) + Ustrlen(to) + 1;
- cache_buff = store_get(cache_size + add_size);
+ cache_buff = store_get(cache_size + add_size, is_tainted(oncelog));
if (read(cache_fd, cache_buff, cache_size) != cache_size)
{
zero. If we find a match, put the time into "then", and the place where it
was found into "cache_time". Otherwise, "then" is left at zero. */
- p = cache_buff;
- while (p < cache_buff + cache_size)
+ for (uschar * p = cache_buff; p < cache_buff + cache_size; p = nextp)
{
uschar *s = p + sizeof(time_t);
- uschar *nextp = s + Ustrlen(s) + 1;
+ nextp = s + Ustrlen(s) + 1;
if (Ustrcmp(to, s) == 0)
{
memcpy(&then, p, sizeof(time_t));
cache_time = p;
break;
}
- p = nextp;
}
}
else
{
EXIM_DATUM key_datum, result_datum;
- EXIM_DBOPEN(oncelog, O_RDWR|O_CREAT, ob->mode, &dbm_file);
+ uschar * dirname = string_copy(oncelog);
+ uschar * s;
+
+ if ((s = Ustrrchr(dirname, '/'))) *s = '\0';
+ EXIM_DBOPEN(oncelog, dirname, O_RDWR|O_CREAT, ob->mode, &dbm_file);
if (!dbm_file)
{
addr->transport_return = DEFER;
can be abolished. */
if (EXIM_DATUM_SIZE(result_datum) == sizeof(time_t))
- {
memcpy(&then, EXIM_DATUM_DATA(result_datum), sizeof(time_t));
- }
- else then = now;
+ else
+ then = now;
}
}
/* We are going to send a message. Ensure any requested file is available. */
-if (file != NULL)
+if (file)
{
ff = Ufopen(file, "rb");
- if (ff == NULL && !ob->file_optional)
+ if (!ff && !ob->file_optional)
{
addr->transport_return = DEFER;
addr->message = string_sprintf("Failed to open file %s when sending "
addr->message = string_sprintf("Failed to create child process to send "
"message from %s transport: %s", tblock->name, strerror(errno));
DEBUG(D_transport) debug_printf("%s\n", addr->message);
+ if (dbm_file) EXIM_DBCLOSE(dbm_file);
return FALSE;
}
as the -t option is used. The "headers" stuff *must* be last in case there
are newlines in it which might, if placed earlier, screw up other headers. */
-f = fdopen(fd, "wb");
+fp = fdopen(fd, "wb");
-if (from != NULL) fprintf(f, "From: %s\n", from);
-if (reply_to != NULL) fprintf(f, "Reply-To: %s\n", reply_to);
-if (to != NULL) fprintf(f, "To: %s\n", to);
-if (cc != NULL) fprintf(f, "Cc: %s\n", cc);
-if (bcc != NULL) fprintf(f, "Bcc: %s\n", bcc);
-if (subject != NULL) fprintf(f, "Subject: %s\n", subject);
+if (from) fprintf(fp, "From: %s\n", from);
+if (reply_to) fprintf(fp, "Reply-To: %s\n", reply_to);
+if (to) fprintf(fp, "To: %s\n", to);
+if (cc) fprintf(fp, "Cc: %s\n", cc);
+if (bcc) fprintf(fp, "Bcc: %s\n", bcc);
+if (subject) fprintf(fp, "Subject: %s\n", subject);
/* Generate In-Reply-To from the message_id header; there should
always be one, but code defensively. */
-for (h = header_list; h != NULL; h = h->next)
+for (h = header_list; h; h = h->next)
if (h->type == htype_id) break;
-if (h != NULL)
+if (h)
{
message_id = Ustrchr(h->text, ':') + 1;
while (isspace(*message_id)) message_id++;
- fprintf(f, "In-Reply-To: %s", message_id);
+ fprintf(fp, "In-Reply-To: %s", message_id);
}
-/* Generate a References header if there is at least one of Message-ID:,
-References:, or In-Reply-To: (see RFC 2822). */
-
-for (h = header_list; h != NULL; h = h->next)
- if (h->type != htype_old && strncmpic(US"References:", h->text, 11) == 0)
- break;
-
-if (h == NULL)
- for (h = header_list; h != NULL; h = h->next)
- if (h->type != htype_old && strncmpic(US"In-Reply-To:", h->text, 12) == 0)
- break;
-
-/* We limit the total length of references. Although there is no fixed
-limit, some systems do not like headers growing beyond recognition.
-Keep the first message ID for the thread root and the last few for
-the position inside the thread, up to a maximum of 12 altogether. */
-
-if (h != NULL || message_id != NULL)
- {
- fprintf(f, "References:");
- if (h != NULL)
- {
- uschar *s, *id, *error;
- uschar *referenced_ids[12];
- int reference_count = 0;
- int i;
-
- s = Ustrchr(h->text, ':') + 1;
- parse_allow_group = FALSE;
- while (*s != 0 && (s = parse_message_id(s, &id, &error)) != NULL)
- {
- if (reference_count == sizeof(referenced_ids)/sizeof(uschar *))
- {
- memmove(referenced_ids + 1, referenced_ids + 2,
- sizeof(referenced_ids) - 2*sizeof(uschar *));
- referenced_ids[reference_count - 1] = id;
- }
- else referenced_ids[reference_count++] = id;
- }
- for (i = 0; i < reference_count; ++i) fprintf(f, " %s", referenced_ids[i]);
- }
-
- /* The message id will have a newline on the end of it. */
-
- if (message_id != NULL) fprintf(f, " %s", message_id);
- else fprintf(f, "\n");
- }
+moan_write_references(fp, message_id);
/* Add an Auto-Submitted: header */
-fprintf(f, "Auto-Submitted: auto-replied\n");
+fprintf(fp, "Auto-Submitted: auto-replied\n");
/* Add any specially requested headers */
-if (headers != NULL) fprintf(f, "%s\n", headers);
-fprintf(f, "\n");
+if (headers) fprintf(fp, "%s\n", headers);
+fprintf(fp, "\n");
-if (text != NULL)
+if (text)
{
- fprintf(f, "%s", CS text);
- if (text[Ustrlen(text)-1] != '\n') fprintf(f, "\n");
+ fprintf(fp, "%s", CS text);
+ if (text[Ustrlen(text)-1] != '\n') fprintf(fp, "\n");
}
-if (ff != NULL)
+if (ff)
{
while (Ufgets(big_buffer, big_buffer_size, ff) != NULL)
{
uschar *s = expand_string(big_buffer);
DEBUG(D_transport)
{
- if (s == NULL)
+ if (!s)
debug_printf("error while expanding line from file:\n %s\n %s\n",
big_buffer, expand_string_message);
}
- fprintf(f, "%s", (s == NULL)? CS big_buffer : CS s);
+ fprintf(fp, "%s", s ? CS s : CS big_buffer);
}
- else fprintf(f, "%s", CS big_buffer);
+ else fprintf(fp, "%s", CS big_buffer);
}
(void) fclose(ff);
}
:
US"------ This is a copy of the message, including all the headers.\n";
transport_ctx tctx = {
- fileno(f),
- tblock,
- addr,
- NULL, NULL,
- (tblock->body_only ? topt_no_headers : 0) |
- (tblock->headers_only ? topt_no_body : 0) |
- (tblock->return_path_add ? topt_add_return_path : 0) |
- (tblock->delivery_date_add ? topt_add_delivery_date : 0) |
- (tblock->envelope_to_add ? topt_add_envelope_to : 0)
+ .u = {.fd = fileno(fp)},
+ .tblock = tblock,
+ .addr = addr,
+ .check_string = NULL,
+ .escape_string = NULL,
+ .options = (tblock->body_only ? topt_no_headers : 0)
+ | (tblock->headers_only ? topt_no_body : 0)
+ | (tblock->return_path_add ? topt_add_return_path : 0)
+ | (tblock->delivery_date_add ? topt_add_delivery_date : 0)
+ | (tblock->envelope_to_add ? topt_add_envelope_to : 0)
+ | topt_not_socket
};
if (bounce_return_size_limit > 0 && !tblock->headers_only)
DELIVER_IN_BUFFER_SIZE;
if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
{
- fprintf(f, "\n%s"
+ fprintf(fp, "\n%s"
"------ The body of the message is " OFF_T_FMT " characters long; only the first\n"
"------ %d or so are included here.\n\n", rubric, statbuf.st_size,
(max/1000)*1000);
}
- else fprintf(f, "\n%s\n", rubric);
+ else fprintf(fp, "\n%s\n", rubric);
}
- else fprintf(f, "\n%s\n", rubric);
+ else fprintf(fp, "\n%s\n", rubric);
- fflush(f);
+ fflush(fp);
transport_count = 0;
transport_write_message(&tctx, bounce_return_size_limit);
}
/* End the message and wait for the child process to end; no timeout. */
-(void)fclose(f);
+(void)fclose(fp);
rc = child_close(pid, 0);
/* Update the "sent to" log whatever the yield. This errs on the side of
message, we do not fail. */
if (rc != 0)
- {
if (rc == EXIT_NORECIPIENTS)
{
DEBUG(D_any) debug_printf("%s transport: message contained no recipients\n",
"transport (%d)", tblock->name, rc);
goto END_OFF;
}
- }
/* Log the sending of the message if successful and required. If the file
fails to open, it's hard to know what to do. We cannot write to the Exim
different processes. The log_buffer can be used exactly as for main log
writing. */
-if (logfile != NULL)
+if (logfile)
{
int log_fd = Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode);
if (log_fd >= 0)
{
- uschar *ptr = log_buffer;
+ gstring gs = { .size = LOG_BUFFER_SIZE, .ptr = 0, .s = log_buffer }, *g = &gs;
+
+ /* Use taint-unchecked routines for writing into log_buffer, trusting
+ that we'll never expand it. */
+
DEBUG(D_transport) debug_printf("logging message details\n");
- sprintf(CS ptr, "%s\n", tod_stamp(tod_log));
- while(*ptr) ptr++;
- if (from != NULL)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " From: %s\n", from);
- while(*ptr) ptr++;
- }
- if (to != NULL)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " To: %s\n", to);
- while(*ptr) ptr++;
- }
- if (cc != NULL)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " Cc: %s\n", cc);
- while(*ptr) ptr++;
- }
- if (bcc != NULL)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " Bcc: %s\n", bcc);
- while(*ptr) ptr++;
- }
- if (subject != NULL)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " Subject: %s\n", subject);
- while(*ptr) ptr++;
- }
- if (headers != NULL)
- {
- (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
- " %s\n", headers);
- while(*ptr) ptr++;
- }
- if(write(log_fd, log_buffer, ptr - log_buffer) != ptr-log_buffer
- || close(log_fd))
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "%s\n", tod_stamp(tod_log));
+ if (from)
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " From: %s\n", from);
+ if (to)
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " To: %s\n", to);
+ if (cc)
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Cc: %s\n", cc);
+ if (bcc)
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Bcc: %s\n", bcc);
+ if (subject)
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Subject: %s\n", subject);
+ if (headers)
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s\n", headers);
+ if(write(log_fd, g->s, g->ptr) != g->ptr || close(log_fd))
DEBUG(D_transport) debug_printf("Problem writing log file %s for %s "
"transport\n", logfile, tblock->name);
}
return FALSE;
}
+#endif /*!MACRO_PREDEF*/
/* End of transport/autoreply.c */