OpenSSL: fix bulid on older library versions
[exim.git] / src / src / transports / autoreply.c
CommitLineData
0756eb3c
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
f9ba5e22 5/* Copyright (c) University of Cambridge 1995 - 2018 */
0756eb3c
PH
6/* See the file NOTICE for conditions of use and distribution. */
7
8
9#include "../exim.h"
10#include "autoreply.h"
11
12
13
14/* Options specific to the autoreply transport. They must be in alphabetic
15order (note that "_" comes before the lower case letters). Those starting
16with "*" are not settable by the user but are used by the option-reading
17software for alternative value types. Some options are publicly visible and so
18are stored in the driver instance block. These are flagged with opt_public. */
13a4b4c1 19#define LOFF(field) OPT_OFF(autoreply_transport_options_block, field)
0756eb3c
PH
20
21optionlist autoreply_transport_options[] = {
13a4b4c1
JH
22 { "bcc", opt_stringptr, LOFF(bcc) },
23 { "cc", opt_stringptr, LOFF(cc) },
24 { "file", opt_stringptr, LOFF(file) },
25 { "file_expand", opt_bool, LOFF(file_expand) },
26 { "file_optional", opt_bool, LOFF(file_optional) },
27 { "from", opt_stringptr, LOFF(from) },
28 { "headers", opt_stringptr, LOFF(headers) },
29 { "log", opt_stringptr, LOFF(logfile) },
30 { "mode", opt_octint, LOFF(mode) },
31 { "never_mail", opt_stringptr, LOFF(never_mail) },
32 { "once", opt_stringptr, LOFF(oncelog) },
33 { "once_file_size", opt_int, LOFF(once_file_size) },
34 { "once_repeat", opt_stringptr, LOFF(once_repeat) },
35 { "reply_to", opt_stringptr, LOFF(reply_to) },
36 { "return_message", opt_bool, LOFF(return_message) },
37 { "subject", opt_stringptr, LOFF(subject) },
38 { "text", opt_stringptr, LOFF(text) },
39 { "to", opt_stringptr, LOFF(to) },
0756eb3c
PH
40};
41
42/* Size of the options list. An extern variable has to be used so that its
43address can appear in the tables drtables.c. */
44
45int autoreply_transport_options_count =
46 sizeof(autoreply_transport_options)/sizeof(optionlist);
47
d185889f
JH
48
49#ifdef MACRO_PREDEF
50
51/* Dummy values */
52autoreply_transport_options_block autoreply_transport_option_defaults = {0};
53void autoreply_transport_init(transport_instance *tblock) {}
54BOOL autoreply_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
55
56#else /*!MACRO_PREDEF*/
57
58
0756eb3c
PH
59/* Default private options block for the autoreply transport. */
60
61autoreply_transport_options_block autoreply_transport_option_defaults = {
62 NULL, /* from */
63 NULL, /* reply_to */
64 NULL, /* to */
65 NULL, /* cc */
66 NULL, /* bcc */
67 NULL, /* subject */
68 NULL, /* headers */
69 NULL, /* text */
70 NULL, /* file */
71 NULL, /* logfile */
72 NULL, /* oncelog */
73 NULL, /* once_repeat */
74 NULL, /* never_mail */
75 0600, /* mode */
76 0, /* once_file_size */
77 FALSE, /* file_expand */
78 FALSE, /* file_optional */
79 FALSE /* return message */
80};
81
82
83
84/* Type of text for the checkexpand() function */
85
86enum { cke_text, cke_hdr, cke_file };
87
88
89
90/*************************************************
91* Initialization entry point *
92*************************************************/
93
94/* Called for each instance, after its options have been read, to
95enable consistency checks to be done, or anything else that needs
96to be set up. */
97
98void
99autoreply_transport_init(transport_instance *tblock)
100{
101/*
102autoreply_transport_options_block *ob =
103 (autoreply_transport_options_block *)(tblock->options_block);
104*/
105
106/* If a fixed uid field is set, then a gid field must also be set. */
107
108if (tblock->uid_set && !tblock->gid_set && tblock->expand_gid == NULL)
109 log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
110 "user set without group for the %s transport", tblock->name);
111}
112
113
114
115
116/*************************************************
117* Expand string and check *
118*************************************************/
119
120/* If the expansion fails, the error is set up in the address. Expanded
121strings must be checked to ensure they contain only printing characters
122and white space. If not, the function fails.
123
124Arguments:
125 s string to expand
126 addr address that is being worked on
127 name transport name, for error text
128 type type, for checking content:
129 cke_text => no check
130 cke_hdr => header, allow \n + whitespace
131 cke_file => file name, no non-printers allowed
132
133Returns: expanded string if expansion succeeds;
134 NULL otherwise
135*/
136
137static uschar *
138checkexpand(uschar *s, address_item *addr, uschar *name, int type)
139{
0756eb3c
PH
140uschar *ss = expand_string(s);
141
481e63ca 142if (!ss)
0756eb3c
PH
143 {
144 addr->transport_return = FAIL;
145 addr->message = string_sprintf("Expansion of \"%s\" failed in %s transport: "
146 "%s", s, name, expand_string_message);
147 return NULL;
148 }
149
d7978c0f 150if (type != cke_text) for (uschar * t = ss; *t != 0; t++)
0756eb3c
PH
151 {
152 int c = *t;
55414b25 153 const uschar * sp;
0756eb3c
PH
154 if (mac_isprint(c)) continue;
155 if (type == cke_hdr && c == '\n' && (t[1] == ' ' || t[1] == '\t')) continue;
55414b25 156 sp = string_printing(s);
0756eb3c
PH
157 addr->transport_return = FAIL;
158 addr->message = string_sprintf("Expansion of \"%s\" in %s transport "
55414b25 159 "contains non-printing character %d", sp, name, c);
0756eb3c
PH
160 return NULL;
161 }
162
163return ss;
164}
165
166
167
168
169/*************************************************
170* Check a header line for never_mail *
171*************************************************/
172
173/* This is called to check to, cc, and bcc for addresses in the never_mail
174list. Any that are found are removed.
175
176Arguments:
177 listptr points to the list of addresses
178 never_mail an address list, already expanded
179
180Returns: nothing
181*/
182
183static void
55414b25 184check_never_mail(uschar **listptr, const uschar *never_mail)
0756eb3c
PH
185{
186uschar *s = *listptr;
187
188while (*s != 0)
189 {
190 uschar *error, *next;
191 uschar *e = parse_find_address_end(s, FALSE);
192 int terminator = *e;
193 int start, end, domain, rc;
194
195 /* Temporarily terminate the string at the address end while extracting
196 the operative address within. */
197
198 *e = 0;
199 next = parse_extract_address(s, &error, &start, &end, &domain, FALSE);
200 *e = terminator;
201
202 /* If there is some kind of syntax error, just give up on this header
203 line. */
204
5fcc791a 205 if (!next) break;
0756eb3c
PH
206
207 /* See if the address is on the never_mail list */
208
209 rc = match_address_list(next, /* address to check */
210 TRUE, /* start caseless */
211 FALSE, /* don't expand the list */
212 &never_mail, /* the list */
213 NULL, /* no caching */
214 -1, /* no expand setup */
215 0, /* separator from list */
216 NULL); /* no lookup value return */
217
218 if (rc == OK) /* Remove this address */
219 {
220 DEBUG(D_transport)
221 debug_printf("discarding recipient %s (matched never_mail)\n", next);
222 if (terminator == ',') e++;
223 memmove(s, e, Ustrlen(e) + 1);
224 }
225 else /* Skip over this address */
226 {
227 s = e;
228 if (terminator == ',') s++;
229 }
230 }
231
232/* Check to see if we removed the last address, leaving a terminating comma
233that needs to be removed */
234
235s = *listptr + Ustrlen(*listptr);
236while (s > *listptr && (isspace(s[-1]) || s[-1] == ',')) s--;
237*s = 0;
238
239/* Check to see if there any addresses left; if not, set NULL */
240
241s = *listptr;
242while (s != 0 && isspace(*s)) s++;
243if (*s == 0) *listptr = NULL;
244}
245
246
247
248/*************************************************
249* Main entry point *
250*************************************************/
251
252/* See local README for interface details. This transport always returns
253FALSE, indicating that the top address has the status for all - though in fact
254this transport can handle only one address at at time anyway. */
255
256BOOL
257autoreply_transport_entry(
258 transport_instance *tblock, /* data for this instantiation */
259 address_item *addr) /* address we are working on */
260{
261int fd, pid, rc;
262int cache_fd = -1;
0756eb3c
PH
263int cache_size = 0;
264int add_size = 0;
265EXIM_DB *dbm_file = NULL;
266BOOL file_expand, return_message;
267uschar *from, *reply_to, *to, *cc, *bcc, *subject, *headers, *text, *file;
268uschar *logfile, *oncelog;
269uschar *cache_buff = NULL;
270uschar *cache_time = NULL;
30dba1e6 271uschar *message_id = NULL;
0756eb3c
PH
272header_line *h;
273time_t now = time(NULL);
274time_t once_repeat_sec = 0;
8768d548 275FILE *fp;
0756eb3c
PH
276FILE *ff = NULL;
277
278autoreply_transport_options_block *ob =
279 (autoreply_transport_options_block *)(tblock->options_block);
280
281DEBUG(D_transport) debug_printf("%s transport entered\n", tblock->name);
282
283/* Set up for the good case */
284
285addr->transport_return = OK;
286addr->basic_errno = 0;
287
288/* If the address is pointing to a reply block, then take all the data
289from that block. It has typically been set up by a mail filter processing
290router. Otherwise, the data must be supplied by this transport, and
291it has to be expanded here. */
292
481e63ca 293if (addr->reply)
0756eb3c
PH
294 {
295 DEBUG(D_transport) debug_printf("taking data from address\n");
296 from = addr->reply->from;
297 reply_to = addr->reply->reply_to;
298 to = addr->reply->to;
299 cc = addr->reply->cc;
300 bcc = addr->reply->bcc;
301 subject = addr->reply->subject;
302 headers = addr->reply->headers;
303 text = addr->reply->text;
304 file = addr->reply->file;
305 logfile = addr->reply->logfile;
306 oncelog = addr->reply->oncelog;
307 once_repeat_sec = addr->reply->once_repeat;
308 file_expand = addr->reply->file_expand;
309 expand_forbid = addr->reply->expand_forbid;
310 return_message = addr->reply->return_message;
311 }
312else
313 {
314 uschar *oncerepeat = ob->once_repeat;
315
316 DEBUG(D_transport) debug_printf("taking data from transport\n");
317 from = ob->from;
318 reply_to = ob->reply_to;
319 to = ob->to;
320 cc = ob->cc;
321 bcc = ob->bcc;
322 subject = ob->subject;
323 headers = ob->headers;
324 text = ob->text;
325 file = ob->file;
326 logfile = ob->logfile;
327 oncelog = ob->oncelog;
328 file_expand = ob->file_expand;
329 return_message = ob->return_message;
330
0a6c178c
JH
331 if ( from && !(from = checkexpand(from, addr, tblock->name, cke_hdr))
332 || reply_to && !(reply_to = checkexpand(reply_to, addr, tblock->name, cke_hdr))
333 || to && !(to = checkexpand(to, addr, tblock->name, cke_hdr))
334 || cc && !(cc = checkexpand(cc, addr, tblock->name, cke_hdr))
335 || bcc && !(bcc = checkexpand(bcc, addr, tblock->name, cke_hdr))
336 || subject && !(subject = checkexpand(subject, addr, tblock->name, cke_hdr))
337 || headers && !(headers = checkexpand(headers, addr, tblock->name, cke_text))
338 || text && !(text = checkexpand(text, addr, tblock->name, cke_text))
339 || file && !(file = checkexpand(file, addr, tblock->name, cke_file))
340 || logfile && !(logfile = checkexpand(logfile, addr, tblock->name, cke_file))
341 || oncelog && !(oncelog = checkexpand(oncelog, addr, tblock->name, cke_file))
342 || oncerepeat && !(oncerepeat = checkexpand(oncerepeat, addr, tblock->name, cke_file))
343 )
0756eb3c
PH
344 return FALSE;
345
0a6c178c 346 if (oncerepeat)
0756eb3c
PH
347 {
348 once_repeat_sec = readconf_readtime(oncerepeat, 0, FALSE);
349 if (once_repeat_sec < 0)
350 {
351 addr->transport_return = FAIL;
352 addr->message = string_sprintf("Invalid time value \"%s\" for "
353 "\"once_repeat\" in %s transport", oncerepeat, tblock->name);
354 return FALSE;
355 }
356 }
357 }
358
359/* If the never_mail option is set, we have to scan all the recipients and
360remove those that match. */
361
0a6c178c 362if (ob->never_mail)
0756eb3c 363 {
55414b25 364 const uschar *never_mail = expand_string(ob->never_mail);
0756eb3c 365
0a6c178c 366 if (!never_mail)
0756eb3c
PH
367 {
368 addr->transport_return = FAIL;
369 addr->message = string_sprintf("Failed to expand \"%s\" for "
370 "\"never_mail\" in %s transport", ob->never_mail, tblock->name);
371 return FALSE;
372 }
373
0a6c178c
JH
374 if (to) check_never_mail(&to, never_mail);
375 if (cc) check_never_mail(&cc, never_mail);
376 if (bcc) check_never_mail(&bcc, never_mail);
0756eb3c 377
0a6c178c 378 if (!to && !cc && !bcc)
0756eb3c
PH
379 {
380 DEBUG(D_transport)
381 debug_printf("*** all recipients removed by never_mail\n");
382 return OK;
383 }
384 }
385
386/* If the -N option is set, can't do any more. */
387
8768d548 388if (f.dont_deliver)
0756eb3c
PH
389 {
390 DEBUG(D_transport)
391 debug_printf("*** delivery by %s transport bypassed by -N option\n",
392 tblock->name);
393 return FALSE;
394 }
395
396
397/* If the oncelog field is set, we send want to send only one message to the
398given recipient(s). This works only on the "To" field. If there is no "To"
399field, the message is always sent. If the To: field contains more than one
400recipient, the effect might not be quite as envisaged. If once_file_size is
401set, instead of a dbm file, we use a regular file containing a circular buffer
402recipient cache. */
403
481e63ca 404if (oncelog && *oncelog && to)
0756eb3c
PH
405 {
406 time_t then = 0;
407
481e63ca
JH
408 if (is_tainted(oncelog))
409 {
410 addr->transport_return = DEFER;
411 addr->basic_errno = EACCES;
412 addr->message = string_sprintf("Tainted '%s' (once file for %s transport)"
413 " not permitted", oncelog, tblock->name);
414 goto END_OFF;
415 }
416
0756eb3c
PH
417 /* Handle fixed-size cache file. */
418
419 if (ob->once_file_size > 0)
420 {
d7978c0f 421 uschar * nextp;
0756eb3c 422 struct stat statbuf;
0756eb3c 423
481e63ca 424 cache_fd = Uopen(oncelog, O_CREAT|O_RDWR, ob->mode);
0756eb3c
PH
425 if (cache_fd < 0 || fstat(cache_fd, &statbuf) != 0)
426 {
427 addr->transport_return = DEFER;
6e0fddef 428 addr->basic_errno = errno;
0756eb3c
PH
429 addr->message = string_sprintf("Failed to %s \"once\" file %s when "
430 "sending message from %s transport: %s",
6e0fddef 431 cache_fd < 0 ? "open" : "stat", oncelog, tblock->name, strerror(errno));
0756eb3c
PH
432 goto END_OFF;
433 }
434
435 /* Get store in the temporary pool and read the entire file into it. We get
436 an amount of store that is big enough to add the new entry on the end if we
437 need to do that. */
438
439 cache_size = statbuf.st_size;
440 add_size = sizeof(time_t) + Ustrlen(to) + 1;
f3ebb786 441 cache_buff = store_get(cache_size + add_size, is_tainted(oncelog));
0756eb3c
PH
442
443 if (read(cache_fd, cache_buff, cache_size) != cache_size)
444 {
445 addr->transport_return = DEFER;
446 addr->basic_errno = errno;
447 addr->message = US"error while reading \"once\" file";
448 goto END_OFF;
449 }
450
451 DEBUG(D_transport) debug_printf("%d bytes read from %s\n", cache_size, oncelog);
452
453 /* Scan the data for this recipient. Each entry in the file starts with
454 a time_t sized time value, followed by the address, followed by a binary
455 zero. If we find a match, put the time into "then", and the place where it
456 was found into "cache_time". Otherwise, "then" is left at zero. */
457
d7978c0f 458 for (uschar * p = cache_buff; p < cache_buff + cache_size; p = nextp)
0756eb3c
PH
459 {
460 uschar *s = p + sizeof(time_t);
0a6c178c 461 nextp = s + Ustrlen(s) + 1;
0756eb3c
PH
462 if (Ustrcmp(to, s) == 0)
463 {
464 memcpy(&then, p, sizeof(time_t));
465 cache_time = p;
466 break;
467 }
0756eb3c
PH
468 }
469 }
470
471 /* Use a DBM file for the list of previous recipients. */
472
473 else
474 {
475 EXIM_DATUM key_datum, result_datum;
cfb9cf20
JH
476 uschar * dirname = string_copy(oncelog);
477 uschar * s;
478
479 if ((s = Ustrrchr(dirname, '/'))) *s = '\0';
480 EXIM_DBOPEN(oncelog, dirname, O_RDWR|O_CREAT, ob->mode, &dbm_file);
d315eda1 481 if (!dbm_file)
0756eb3c
PH
482 {
483 addr->transport_return = DEFER;
6e0fddef 484 addr->basic_errno = errno;
0756eb3c
PH
485 addr->message = string_sprintf("Failed to open %s file %s when sending "
486 "message from %s transport: %s", EXIM_DBTYPE, oncelog, tblock->name,
487 strerror(errno));
488 goto END_OFF;
489 }
490
491 EXIM_DATUM_INIT(key_datum); /* Some DBM libraries need datums */
492 EXIM_DATUM_INIT(result_datum); /* to be cleared */
493 EXIM_DATUM_DATA(key_datum) = CS to;
494 EXIM_DATUM_SIZE(key_datum) = Ustrlen(to) + 1;
495
496 if (EXIM_DBGET(dbm_file, key_datum, result_datum))
497 {
498 /* If the datum size is that of a binary time, we are in the new world
499 where messages are sent periodically. Otherwise the file is an old one,
500 where the datum was filled with a tod_log time, which is assumed to be
501 different in size. For that, only one message is ever sent. This change
502 introduced at Exim 3.00. In a couple of years' time the test on the size
503 can be abolished. */
504
505 if (EXIM_DATUM_SIZE(result_datum) == sizeof(time_t))
0756eb3c 506 memcpy(&then, EXIM_DATUM_DATA(result_datum), sizeof(time_t));
0a6c178c
JH
507 else
508 then = now;
0756eb3c
PH
509 }
510 }
511
512 /* Either "then" is set zero, if no message has yet been sent, or it
513 is set to the time of the last sending. */
514
515 if (then != 0 && (once_repeat_sec <= 0 || now - then < once_repeat_sec))
516 {
13559da6 517 int log_fd;
481e63ca
JH
518 if (is_tainted(logfile))
519 {
520 addr->transport_return = DEFER;
521 addr->basic_errno = EACCES;
522 addr->message = string_sprintf("Tainted '%s' (logfile for %s transport)"
523 " not permitted", logfile, tblock->name);
524 goto END_OFF;
525 }
526
0756eb3c
PH
527 DEBUG(D_transport) debug_printf("message previously sent to %s%s\n", to,
528 (once_repeat_sec > 0)? " and repeat time not reached" : "");
13559da6 529 log_fd = logfile ? Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode) : -1;
0756eb3c
PH
530 if (log_fd >= 0)
531 {
532 uschar *ptr = log_buffer;
533 sprintf(CS ptr, "%s\n previously sent to %.200s\n", tod_stamp(tod_log), to);
534 while(*ptr) ptr++;
1ac6b2e7
JH
535 if(write(log_fd, log_buffer, ptr - log_buffer) != ptr-log_buffer
536 || close(log_fd))
537 DEBUG(D_transport) debug_printf("Problem writing log file %s for %s "
538 "transport\n", logfile, tblock->name);
0756eb3c
PH
539 }
540 goto END_OFF;
541 }
542
543 DEBUG(D_transport) debug_printf("%s %s\n", (then <= 0)?
544 "no previous message sent to" : "repeat time reached for", to);
545 }
546
547/* We are going to send a message. Ensure any requested file is available. */
481e63ca 548if (file)
0756eb3c 549 {
481e63ca
JH
550 if (is_tainted(file))
551 {
552 addr->transport_return = DEFER;
553 addr->basic_errno = EACCES;
554 addr->message = string_sprintf("Tainted '%s' (file for %s transport)"
555 " not permitted", file, tblock->name);
556 return FALSE;
557 }
558 if (!(ff = Ufopen(file, "rb")) && !ob->file_optional)
559 {
560 addr->transport_return = DEFER;
561 addr->basic_errno = errno;
562 addr->message = string_sprintf("Failed to open file %s when sending "
563 "message from %s transport: %s", file, tblock->name, strerror(errno));
564 return FALSE;
565 }
0756eb3c
PH
566 }
567
568/* Make a subprocess to send the message */
569
8e9fdd63 570if ((pid = child_open_exim(&fd, US"autoreply")) < 0)
0756eb3c 571 {
8e9fdd63
JH
572 /* Creation of child failed; defer this delivery. */
573
0756eb3c 574 addr->transport_return = DEFER;
6e0fddef 575 addr->basic_errno = errno;
0756eb3c
PH
576 addr->message = string_sprintf("Failed to create child process to send "
577 "message from %s transport: %s", tblock->name, strerror(errno));
578 DEBUG(D_transport) debug_printf("%s\n", addr->message);
0a6c178c 579 if (dbm_file) EXIM_DBCLOSE(dbm_file);
0756eb3c
PH
580 return FALSE;
581 }
582
583/* Create the message to be sent - recipients are taken from the headers,
584as the -t option is used. The "headers" stuff *must* be last in case there
585are newlines in it which might, if placed earlier, screw up other headers. */
586
8768d548 587fp = fdopen(fd, "wb");
0756eb3c 588
8768d548
JH
589if (from) fprintf(fp, "From: %s\n", from);
590if (reply_to) fprintf(fp, "Reply-To: %s\n", reply_to);
591if (to) fprintf(fp, "To: %s\n", to);
592if (cc) fprintf(fp, "Cc: %s\n", cc);
593if (bcc) fprintf(fp, "Bcc: %s\n", bcc);
594if (subject) fprintf(fp, "Subject: %s\n", subject);
0756eb3c
PH
595
596/* Generate In-Reply-To from the message_id header; there should
597always be one, but code defensively. */
598
0a6c178c 599for (h = header_list; h; h = h->next)
0756eb3c
PH
600 if (h->type == htype_id) break;
601
0a6c178c 602if (h)
0756eb3c 603 {
30dba1e6
PH
604 message_id = Ustrchr(h->text, ':') + 1;
605 while (isspace(*message_id)) message_id++;
8768d548 606 fprintf(fp, "In-Reply-To: %s", message_id);
30dba1e6
PH
607 }
608
d6c829b9 609moan_write_references(fp, message_id);
0756eb3c
PH
610
611/* Add an Auto-Submitted: header */
612
8768d548 613fprintf(fp, "Auto-Submitted: auto-replied\n");
0756eb3c
PH
614
615/* Add any specially requested headers */
616
8768d548
JH
617if (headers) fprintf(fp, "%s\n", headers);
618fprintf(fp, "\n");
0756eb3c 619
0a6c178c 620if (text)
0756eb3c 621 {
8768d548
JH
622 fprintf(fp, "%s", CS text);
623 if (text[Ustrlen(text)-1] != '\n') fprintf(fp, "\n");
0756eb3c
PH
624 }
625
0a6c178c 626if (ff)
0756eb3c
PH
627 {
628 while (Ufgets(big_buffer, big_buffer_size, ff) != NULL)
629 {
630 if (file_expand)
631 {
632 uschar *s = expand_string(big_buffer);
633 DEBUG(D_transport)
634 {
0a6c178c 635 if (!s)
0756eb3c
PH
636 debug_printf("error while expanding line from file:\n %s\n %s\n",
637 big_buffer, expand_string_message);
638 }
8768d548 639 fprintf(fp, "%s", s ? CS s : CS big_buffer);
0756eb3c 640 }
8768d548 641 else fprintf(fp, "%s", CS big_buffer);
0756eb3c 642 }
e0eb00cd 643 (void) fclose(ff);
0756eb3c
PH
644 }
645
646/* Copy the original message if required, observing the return size
9f526266 647limit if we are returning the body. */
0756eb3c
PH
648
649if (return_message)
650 {
9f526266
PH
651 uschar *rubric = (tblock->headers_only)?
652 US"------ This is a copy of the message's header lines.\n"
653 : (tblock->body_only)?
654 US"------ This is a copy of the body of the message, without the headers.\n"
655 :
656 US"------ This is a copy of the message, including all the headers.\n";
65de12cc 657 transport_ctx tctx = {
8768d548 658 .u = {.fd = fileno(fp)},
b3b37076
JH
659 .tblock = tblock,
660 .addr = addr,
661 .check_string = NULL,
662 .escape_string = NULL,
663 .options = (tblock->body_only ? topt_no_headers : 0)
664 | (tblock->headers_only ? topt_no_body : 0)
665 | (tblock->return_path_add ? topt_add_return_path : 0)
666 | (tblock->delivery_date_add ? topt_add_delivery_date : 0)
667 | (tblock->envelope_to_add ? topt_add_envelope_to : 0)
668 | topt_not_socket
65de12cc 669 };
9f526266
PH
670
671 if (bounce_return_size_limit > 0 && !tblock->headers_only)
0756eb3c
PH
672 {
673 struct stat statbuf;
674 int max = (bounce_return_size_limit/DELIVER_IN_BUFFER_SIZE + 1) *
675 DELIVER_IN_BUFFER_SIZE;
676 if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
677 {
8768d548 678 fprintf(fp, "\n%s"
b1c749bb 679"------ The body of the message is " OFF_T_FMT " characters long; only the first\n"
9f526266 680"------ %d or so are included here.\n\n", rubric, statbuf.st_size,
0d7eb84a 681 (max/1000)*1000);
0756eb3c 682 }
8768d548 683 else fprintf(fp, "\n%s\n", rubric);
0756eb3c 684 }
8768d548 685 else fprintf(fp, "\n%s\n", rubric);
0756eb3c 686
8768d548 687 fflush(fp);
0756eb3c 688 transport_count = 0;
42055a33 689 transport_write_message(&tctx, bounce_return_size_limit);
0756eb3c
PH
690 }
691
692/* End the message and wait for the child process to end; no timeout. */
693
8768d548 694(void)fclose(fp);
0756eb3c
PH
695rc = child_close(pid, 0);
696
697/* Update the "sent to" log whatever the yield. This errs on the side of
698missing out a message rather than risking sending more than one. We either have
699cache_fd set to a fixed size, circular buffer file, or dbm_file set to an open
700DBM file (or neither, if "once" is not set). */
701
702/* Update fixed-size cache file. If cache_time is set, we found a previous
703entry; that is the spot into which to put the current time. Otherwise we have
704to add a new record; remove the first one in the file if the file is too big.
705We always rewrite the entire file in a single write operation. This is
706(hopefully) going to be the safest thing because there is no interlocking
707between multiple simultaneous deliveries. */
708
709if (cache_fd >= 0)
710 {
711 uschar *from = cache_buff;
712 int size = cache_size;
0756eb3c 713
d315eda1 714 if (lseek(cache_fd, 0, SEEK_SET) == 0)
0756eb3c 715 {
d315eda1 716 if (!cache_time)
0756eb3c 717 {
d315eda1
JH
718 cache_time = from + size;
719 memcpy(cache_time + sizeof(time_t), to, add_size - sizeof(time_t));
720 size += add_size;
721
722 if (cache_size > 0 && size > ob->once_file_size)
723 {
724 from += sizeof(time_t) + Ustrlen(from + sizeof(time_t)) + 1;
725 size -= (from - cache_buff);
726 }
0756eb3c 727 }
0756eb3c 728
d315eda1
JH
729 memcpy(cache_time, &now, sizeof(time_t));
730 if(write(cache_fd, from, size) != size)
731 DEBUG(D_transport) debug_printf("Problem writing cache file %s for %s "
732 "transport\n", oncelog, tblock->name);
733 }
0756eb3c
PH
734 }
735
736/* Update DBM file */
737
d315eda1 738else if (dbm_file)
0756eb3c
PH
739 {
740 EXIM_DATUM key_datum, value_datum;
741 EXIM_DATUM_INIT(key_datum); /* Some DBM libraries need to have */
742 EXIM_DATUM_INIT(value_datum); /* cleared datums. */
743 EXIM_DATUM_DATA(key_datum) = CS to;
744 EXIM_DATUM_SIZE(key_datum) = Ustrlen(to) + 1;
745
746 /* Many OS define the datum value, sensibly, as a void *. However, there
747 are some which still have char *. By casting this address to a char * we
748 can avoid warning messages from the char * systems. */
749
750 EXIM_DATUM_DATA(value_datum) = CS (&now);
751 EXIM_DATUM_SIZE(value_datum) = (int)sizeof(time_t);
752 EXIM_DBPUT(dbm_file, key_datum, value_datum);
753 }
754
755/* If sending failed, defer to try again - but if once is set the next
756try will skip, of course. However, if there were no recipients in the
757message, we do not fail. */
758
759if (rc != 0)
0756eb3c
PH
760 if (rc == EXIT_NORECIPIENTS)
761 {
762 DEBUG(D_any) debug_printf("%s transport: message contained no recipients\n",
763 tblock->name);
764 }
765 else
766 {
767 addr->transport_return = DEFER;
768 addr->message = string_sprintf("Failed to send message from %s "
769 "transport (%d)", tblock->name, rc);
770 goto END_OFF;
771 }
0756eb3c
PH
772
773/* Log the sending of the message if successful and required. If the file
774fails to open, it's hard to know what to do. We cannot write to the Exim
775log from here, since we may be running under an unprivileged uid. We don't
776want to fail the delivery, since the message has been successfully sent. For
777the moment, ignore open failures. Write the log entry as a single write() to a
778file opened for appending, in order to avoid interleaving of output from
779different processes. The log_buffer can be used exactly as for main log
780writing. */
781
0a6c178c 782if (logfile)
0756eb3c
PH
783 {
784 int log_fd = Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode);
785 if (log_fd >= 0)
786 {
f3ebb786
JH
787 gstring gs = { .size = LOG_BUFFER_SIZE, .ptr = 0, .s = log_buffer }, *g = &gs;
788
789 /* Use taint-unchecked routines for writing into log_buffer, trusting
790 that we'll never expand it. */
791
0756eb3c 792 DEBUG(D_transport) debug_printf("logging message details\n");
f3ebb786 793 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "%s\n", tod_stamp(tod_log));
0a6c178c 794 if (from)
f3ebb786 795 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " From: %s\n", from);
0a6c178c 796 if (to)
f3ebb786 797 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " To: %s\n", to);
0a6c178c 798 if (cc)
f3ebb786 799 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Cc: %s\n", cc);
0a6c178c 800 if (bcc)
f3ebb786 801 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Bcc: %s\n", bcc);
0a6c178c 802 if (subject)
f3ebb786 803 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Subject: %s\n", subject);
0a6c178c 804 if (headers)
f3ebb786
JH
805 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s\n", headers);
806 if(write(log_fd, g->s, g->ptr) != g->ptr || close(log_fd))
1ac6b2e7
JH
807 DEBUG(D_transport) debug_printf("Problem writing log file %s for %s "
808 "transport\n", logfile, tblock->name);
0756eb3c
PH
809 }
810 else DEBUG(D_transport) debug_printf("Failed to open log file %s for %s "
811 "transport: %s\n", logfile, tblock->name, strerror(errno));
812 }
813
814END_OFF:
d315eda1 815if (dbm_file) EXIM_DBCLOSE(dbm_file);
f1e894f3 816if (cache_fd > 0) (void)close(cache_fd);
0756eb3c
PH
817
818DEBUG(D_transport) debug_printf("%s transport succeeded\n", tblock->name);
819
820return FALSE;
821}
822
d185889f 823#endif /*!MACRO_PREDEF*/
0756eb3c 824/* End of transport/autoreply.c */