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