Merge branch 'debug_fork'
[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 */