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