90a5aa4be6f3aa58d6d082247e9ea4a4bda06f48
[exim.git] / src / src / transports / autoreply.c
1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
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
15 order (note that "_" comes before the lower case letters). Those starting
16 with "*" are not settable by the user but are used by the option-reading
17 software for alternative value types. Some options are publicly visible and so
18 are stored in the driver instance block. These are flagged with opt_public. */
19 #define LOFF(field) OPT_OFF(autoreply_transport_options_block, field)
20
21 optionlist autoreply_transport_options[] = {
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) },
40 };
41
42 /* Size of the options list. An extern variable has to be used so that its
43 address can appear in the tables drtables.c. */
44
45 int autoreply_transport_options_count =
46 sizeof(autoreply_transport_options)/sizeof(optionlist);
47
48
49 #ifdef MACRO_PREDEF
50
51 /* Dummy values */
52 autoreply_transport_options_block autoreply_transport_option_defaults = {0};
53 void autoreply_transport_init(transport_instance *tblock) {}
54 BOOL autoreply_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
55
56 #else /*!MACRO_PREDEF*/
57
58
59 /* Default private options block for the autoreply transport. */
60
61 autoreply_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
86 enum { 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
95 enable consistency checks to be done, or anything else that needs
96 to be set up. */
97
98 void
99 autoreply_transport_init(transport_instance *tblock)
100 {
101 /*
102 autoreply_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
108 if (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
121 strings must be checked to ensure they contain only printing characters
122 and white space. If not, the function fails.
123
124 Arguments:
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
133 Returns: expanded string if expansion succeeds;
134 NULL otherwise
135 */
136
137 static uschar *
138 checkexpand(uschar *s, address_item *addr, uschar *name, int type)
139 {
140 uschar *ss = expand_string(s);
141
142 if (!ss)
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
150 if (type != cke_text) for (uschar * t = ss; *t != 0; t++)
151 {
152 int c = *t;
153 const uschar * sp;
154 if (mac_isprint(c)) continue;
155 if (type == cke_hdr && c == '\n' && (t[1] == ' ' || t[1] == '\t')) continue;
156 sp = string_printing(s);
157 addr->transport_return = FAIL;
158 addr->message = string_sprintf("Expansion of \"%s\" in %s transport "
159 "contains non-printing character %d", sp, name, c);
160 return NULL;
161 }
162
163 return 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
174 list. Any that are found are removed.
175
176 Arguments:
177 listptr points to the list of addresses
178 never_mail an address list, already expanded
179
180 Returns: nothing
181 */
182
183 static void
184 check_never_mail(uschar **listptr, const uschar *never_mail)
185 {
186 uschar *s = *listptr;
187
188 while (*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
205 if (!next) break;
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
233 that needs to be removed */
234
235 s = *listptr + Ustrlen(*listptr);
236 while (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
241 s = *listptr;
242 while (s != 0 && isspace(*s)) s++;
243 if (*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
253 FALSE, indicating that the top address has the status for all - though in fact
254 this transport can handle only one address at at time anyway. */
255
256 BOOL
257 autoreply_transport_entry(
258 transport_instance *tblock, /* data for this instantiation */
259 address_item *addr) /* address we are working on */
260 {
261 int fd, pid, rc;
262 int cache_fd = -1;
263 int cache_size = 0;
264 int add_size = 0;
265 EXIM_DB *dbm_file = NULL;
266 BOOL file_expand, return_message;
267 uschar *from, *reply_to, *to, *cc, *bcc, *subject, *headers, *text, *file;
268 uschar *logfile, *oncelog;
269 uschar *cache_buff = NULL;
270 uschar *cache_time = NULL;
271 uschar *message_id = NULL;
272 header_line *h;
273 time_t now = time(NULL);
274 time_t once_repeat_sec = 0;
275 FILE *fp;
276 FILE *ff = NULL;
277
278 autoreply_transport_options_block *ob =
279 (autoreply_transport_options_block *)(tblock->options_block);
280
281 DEBUG(D_transport) debug_printf("%s transport entered\n", tblock->name);
282
283 /* Set up for the good case */
284
285 addr->transport_return = OK;
286 addr->basic_errno = 0;
287
288 /* If the address is pointing to a reply block, then take all the data
289 from that block. It has typically been set up by a mail filter processing
290 router. Otherwise, the data must be supplied by this transport, and
291 it has to be expanded here. */
292
293 if (addr->reply)
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 }
312 else
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
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 )
344 return FALSE;
345
346 if (oncerepeat)
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
360 remove those that match. */
361
362 if (ob->never_mail)
363 {
364 const uschar *never_mail = expand_string(ob->never_mail);
365
366 if (!never_mail)
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
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);
377
378 if (!to && !cc && !bcc)
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
388 if (f.dont_deliver)
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
398 given recipient(s). This works only on the "To" field. If there is no "To"
399 field, the message is always sent. If the To: field contains more than one
400 recipient, the effect might not be quite as envisaged. If once_file_size is
401 set, instead of a dbm file, we use a regular file containing a circular buffer
402 recipient cache. */
403
404 if (oncelog && *oncelog && to)
405 {
406 time_t then = 0;
407
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
417 /* Handle fixed-size cache file. */
418
419 if (ob->once_file_size > 0)
420 {
421 uschar * nextp;
422 struct stat statbuf;
423
424 cache_fd = Uopen(oncelog, O_CREAT|O_RDWR, ob->mode);
425 if (cache_fd < 0 || fstat(cache_fd, &statbuf) != 0)
426 {
427 addr->transport_return = DEFER;
428 addr->basic_errno = errno;
429 addr->message = string_sprintf("Failed to %s \"once\" file %s when "
430 "sending message from %s transport: %s",
431 cache_fd < 0 ? "open" : "stat", oncelog, tblock->name, strerror(errno));
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;
441 cache_buff = store_get(cache_size + add_size, is_tainted(oncelog));
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
458 for (uschar * p = cache_buff; p < cache_buff + cache_size; p = nextp)
459 {
460 uschar *s = p + sizeof(time_t);
461 nextp = s + Ustrlen(s) + 1;
462 if (Ustrcmp(to, s) == 0)
463 {
464 memcpy(&then, p, sizeof(time_t));
465 cache_time = p;
466 break;
467 }
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;
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);
481 if (!dbm_file)
482 {
483 addr->transport_return = DEFER;
484 addr->basic_errno = errno;
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))
506 memcpy(&then, EXIM_DATUM_DATA(result_datum), sizeof(time_t));
507 else
508 then = now;
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 {
517 int log_fd;
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
527 DEBUG(D_transport) debug_printf("message previously sent to %s%s\n", to,
528 (once_repeat_sec > 0)? " and repeat time not reached" : "");
529 log_fd = logfile ? Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode) : -1;
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++;
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);
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. */
548 if (file)
549 {
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 }
566 }
567
568 /* Make a subprocess to send the message */
569
570 if ((pid = child_open_exim(&fd, US"autoreply")) < 0)
571 {
572 /* Creation of child failed; defer this delivery. */
573
574 addr->transport_return = DEFER;
575 addr->basic_errno = errno;
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);
579 if (dbm_file) EXIM_DBCLOSE(dbm_file);
580 return FALSE;
581 }
582
583 /* Create the message to be sent - recipients are taken from the headers,
584 as the -t option is used. The "headers" stuff *must* be last in case there
585 are newlines in it which might, if placed earlier, screw up other headers. */
586
587 fp = fdopen(fd, "wb");
588
589 if (from) fprintf(fp, "From: %s\n", from);
590 if (reply_to) fprintf(fp, "Reply-To: %s\n", reply_to);
591 if (to) fprintf(fp, "To: %s\n", to);
592 if (cc) fprintf(fp, "Cc: %s\n", cc);
593 if (bcc) fprintf(fp, "Bcc: %s\n", bcc);
594 if (subject) fprintf(fp, "Subject: %s\n", subject);
595
596 /* Generate In-Reply-To from the message_id header; there should
597 always be one, but code defensively. */
598
599 for (h = header_list; h; h = h->next)
600 if (h->type == htype_id) break;
601
602 if (h)
603 {
604 message_id = Ustrchr(h->text, ':') + 1;
605 while (isspace(*message_id)) message_id++;
606 fprintf(fp, "In-Reply-To: %s", message_id);
607 }
608
609 moan_write_references(fp, message_id);
610
611 /* Add an Auto-Submitted: header */
612
613 fprintf(fp, "Auto-Submitted: auto-replied\n");
614
615 /* Add any specially requested headers */
616
617 if (headers) fprintf(fp, "%s\n", headers);
618 fprintf(fp, "\n");
619
620 if (text)
621 {
622 fprintf(fp, "%s", CS text);
623 if (text[Ustrlen(text)-1] != '\n') fprintf(fp, "\n");
624 }
625
626 if (ff)
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 {
635 if (!s)
636 debug_printf("error while expanding line from file:\n %s\n %s\n",
637 big_buffer, expand_string_message);
638 }
639 fprintf(fp, "%s", s ? CS s : CS big_buffer);
640 }
641 else fprintf(fp, "%s", CS big_buffer);
642 }
643 (void) fclose(ff);
644 }
645
646 /* Copy the original message if required, observing the return size
647 limit if we are returning the body. */
648
649 if (return_message)
650 {
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";
657 transport_ctx tctx = {
658 .u = {.fd = fileno(fp)},
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
669 };
670
671 if (bounce_return_size_limit > 0 && !tblock->headers_only)
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 {
678 fprintf(fp, "\n%s"
679 "------ The body of the message is " OFF_T_FMT " characters long; only the first\n"
680 "------ %d or so are included here.\n\n", rubric, statbuf.st_size,
681 (max/1000)*1000);
682 }
683 else fprintf(fp, "\n%s\n", rubric);
684 }
685 else fprintf(fp, "\n%s\n", rubric);
686
687 fflush(fp);
688 transport_count = 0;
689 transport_write_message(&tctx, bounce_return_size_limit);
690 }
691
692 /* End the message and wait for the child process to end; no timeout. */
693
694 (void)fclose(fp);
695 rc = child_close(pid, 0);
696
697 /* Update the "sent to" log whatever the yield. This errs on the side of
698 missing out a message rather than risking sending more than one. We either have
699 cache_fd set to a fixed size, circular buffer file, or dbm_file set to an open
700 DBM file (or neither, if "once" is not set). */
701
702 /* Update fixed-size cache file. If cache_time is set, we found a previous
703 entry; that is the spot into which to put the current time. Otherwise we have
704 to add a new record; remove the first one in the file if the file is too big.
705 We 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
707 between multiple simultaneous deliveries. */
708
709 if (cache_fd >= 0)
710 {
711 uschar *from = cache_buff;
712 int size = cache_size;
713
714 if (lseek(cache_fd, 0, SEEK_SET) == 0)
715 {
716 if (!cache_time)
717 {
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 }
727 }
728
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 }
734 }
735
736 /* Update DBM file */
737
738 else if (dbm_file)
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
756 try will skip, of course. However, if there were no recipients in the
757 message, we do not fail. */
758
759 if (rc != 0)
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 }
772
773 /* Log the sending of the message if successful and required. If the file
774 fails to open, it's hard to know what to do. We cannot write to the Exim
775 log from here, since we may be running under an unprivileged uid. We don't
776 want to fail the delivery, since the message has been successfully sent. For
777 the moment, ignore open failures. Write the log entry as a single write() to a
778 file opened for appending, in order to avoid interleaving of output from
779 different processes. The log_buffer can be used exactly as for main log
780 writing. */
781
782 if (logfile)
783 {
784 int log_fd = Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode);
785 if (log_fd >= 0)
786 {
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
792 DEBUG(D_transport) debug_printf("logging message details\n");
793 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "%s\n", tod_stamp(tod_log));
794 if (from)
795 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " From: %s\n", from);
796 if (to)
797 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " To: %s\n", to);
798 if (cc)
799 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Cc: %s\n", cc);
800 if (bcc)
801 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Bcc: %s\n", bcc);
802 if (subject)
803 g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Subject: %s\n", subject);
804 if (headers)
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))
807 DEBUG(D_transport) debug_printf("Problem writing log file %s for %s "
808 "transport\n", logfile, tblock->name);
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
814 END_OFF:
815 if (dbm_file) EXIM_DBCLOSE(dbm_file);
816 if (cache_fd > 0) (void)close(cache_fd);
817
818 DEBUG(D_transport) debug_printf("%s transport succeeded\n", tblock->name);
819
820 return FALSE;
821 }
822
823 #endif /*!MACRO_PREDEF*/
824 /* End of transport/autoreply.c */