Fix segfault on bad cmdline -f (sender) argument. Bug 2541
[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 pid = child_open_exim(&fd);
571
572 /* Creation of child failed; defer this delivery. */
573
574 if (pid < 0)
575   {
576   addr->transport_return = DEFER;
577   addr->basic_errno = errno;
578   addr->message = string_sprintf("Failed to create child process to send "
579     "message from %s transport: %s", tblock->name, strerror(errno));
580   DEBUG(D_transport) debug_printf("%s\n", addr->message);
581   if (dbm_file) EXIM_DBCLOSE(dbm_file);
582   return FALSE;
583   }
584
585 /* Create the message to be sent - recipients are taken from the headers,
586 as the -t option is used. The "headers" stuff *must* be last in case there
587 are newlines in it which might, if placed earlier, screw up other headers. */
588
589 fp = fdopen(fd, "wb");
590
591 if (from) fprintf(fp, "From: %s\n", from);
592 if (reply_to) fprintf(fp, "Reply-To: %s\n", reply_to);
593 if (to) fprintf(fp, "To: %s\n", to);
594 if (cc) fprintf(fp, "Cc: %s\n", cc);
595 if (bcc) fprintf(fp, "Bcc: %s\n", bcc);
596 if (subject) fprintf(fp, "Subject: %s\n", subject);
597
598 /* Generate In-Reply-To from the message_id header; there should
599 always be one, but code defensively. */
600
601 for (h = header_list; h; h = h->next)
602   if (h->type == htype_id) break;
603
604 if (h)
605   {
606   message_id = Ustrchr(h->text, ':') + 1;
607   while (isspace(*message_id)) message_id++;
608   fprintf(fp, "In-Reply-To: %s", message_id);
609   }
610
611 moan_write_references(fp, message_id);
612
613 /* Add an Auto-Submitted: header */
614
615 fprintf(fp, "Auto-Submitted: auto-replied\n");
616
617 /* Add any specially requested headers */
618
619 if (headers) fprintf(fp, "%s\n", headers);
620 fprintf(fp, "\n");
621
622 if (text)
623   {
624   fprintf(fp, "%s", CS text);
625   if (text[Ustrlen(text)-1] != '\n') fprintf(fp, "\n");
626   }
627
628 if (ff)
629   {
630   while (Ufgets(big_buffer, big_buffer_size, ff) != NULL)
631     {
632     if (file_expand)
633       {
634       uschar *s = expand_string(big_buffer);
635       DEBUG(D_transport)
636         {
637         if (!s)
638           debug_printf("error while expanding line from file:\n  %s\n  %s\n",
639             big_buffer, expand_string_message);
640         }
641       fprintf(fp, "%s", s ? CS s : CS big_buffer);
642       }
643     else fprintf(fp, "%s", CS big_buffer);
644     }
645   (void) fclose(ff);
646   }
647
648 /* Copy the original message if required, observing the return size
649 limit if we are returning the body. */
650
651 if (return_message)
652   {
653   uschar *rubric = (tblock->headers_only)?
654     US"------ This is a copy of the message's header lines.\n"
655     : (tblock->body_only)?
656     US"------ This is a copy of the body of the message, without the headers.\n"
657     :
658     US"------ This is a copy of the message, including all the headers.\n";
659   transport_ctx tctx = {
660     .u = {.fd = fileno(fp)},
661     .tblock = tblock,
662     .addr = addr,
663     .check_string = NULL,
664     .escape_string =  NULL,
665     .options = (tblock->body_only ? topt_no_headers : 0)
666         | (tblock->headers_only ? topt_no_body : 0)
667         | (tblock->return_path_add ? topt_add_return_path : 0)
668         | (tblock->delivery_date_add ? topt_add_delivery_date : 0)
669         | (tblock->envelope_to_add ? topt_add_envelope_to : 0)
670         | topt_not_socket
671   };
672
673   if (bounce_return_size_limit > 0 && !tblock->headers_only)
674     {
675     struct stat statbuf;
676     int max = (bounce_return_size_limit/DELIVER_IN_BUFFER_SIZE + 1) *
677       DELIVER_IN_BUFFER_SIZE;
678     if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
679       {
680       fprintf(fp, "\n%s"
681 "------ The body of the message is " OFF_T_FMT " characters long; only the first\n"
682 "------ %d or so are included here.\n\n", rubric, statbuf.st_size,
683         (max/1000)*1000);
684       }
685     else fprintf(fp, "\n%s\n", rubric);
686     }
687   else fprintf(fp, "\n%s\n", rubric);
688
689   fflush(fp);
690   transport_count = 0;
691   transport_write_message(&tctx, bounce_return_size_limit);
692   }
693
694 /* End the message and wait for the child process to end; no timeout. */
695
696 (void)fclose(fp);
697 rc = child_close(pid, 0);
698
699 /* Update the "sent to" log whatever the yield. This errs on the side of
700 missing out a message rather than risking sending more than one. We either have
701 cache_fd set to a fixed size, circular buffer file, or dbm_file set to an open
702 DBM file (or neither, if "once" is not set). */
703
704 /* Update fixed-size cache file. If cache_time is set, we found a previous
705 entry; that is the spot into which to put the current time. Otherwise we have
706 to add a new record; remove the first one in the file if the file is too big.
707 We always rewrite the entire file in a single write operation. This is
708 (hopefully) going to be the safest thing because there is no interlocking
709 between multiple simultaneous deliveries. */
710
711 if (cache_fd >= 0)
712   {
713   uschar *from = cache_buff;
714   int size = cache_size;
715
716   if (lseek(cache_fd, 0, SEEK_SET) == 0)
717     {
718     if (!cache_time)
719       {
720       cache_time = from + size;
721       memcpy(cache_time + sizeof(time_t), to, add_size - sizeof(time_t));
722       size += add_size;
723
724       if (cache_size > 0 && size > ob->once_file_size)
725         {
726         from += sizeof(time_t) + Ustrlen(from + sizeof(time_t)) + 1;
727         size -= (from - cache_buff);
728         }
729       }
730
731     memcpy(cache_time, &now, sizeof(time_t));
732     if(write(cache_fd, from, size) != size)
733       DEBUG(D_transport) debug_printf("Problem writing cache file %s for %s "
734         "transport\n", oncelog, tblock->name);
735     }
736   }
737
738 /* Update DBM file */
739
740 else if (dbm_file)
741   {
742   EXIM_DATUM key_datum, value_datum;
743   EXIM_DATUM_INIT(key_datum);          /* Some DBM libraries need to have */
744   EXIM_DATUM_INIT(value_datum);        /* cleared datums. */
745   EXIM_DATUM_DATA(key_datum) = CS to;
746   EXIM_DATUM_SIZE(key_datum) = Ustrlen(to) + 1;
747
748   /* Many OS define the datum value, sensibly, as a void *. However, there
749   are some which still have char *. By casting this address to a char * we
750   can avoid warning messages from the char * systems. */
751
752   EXIM_DATUM_DATA(value_datum) = CS (&now);
753   EXIM_DATUM_SIZE(value_datum) = (int)sizeof(time_t);
754   EXIM_DBPUT(dbm_file, key_datum, value_datum);
755   }
756
757 /* If sending failed, defer to try again - but if once is set the next
758 try will skip, of course. However, if there were no recipients in the
759 message, we do not fail. */
760
761 if (rc != 0)
762   if (rc == EXIT_NORECIPIENTS)
763     {
764     DEBUG(D_any) debug_printf("%s transport: message contained no recipients\n",
765       tblock->name);
766     }
767   else
768     {
769     addr->transport_return = DEFER;
770     addr->message = string_sprintf("Failed to send message from %s "
771       "transport (%d)", tblock->name, rc);
772     goto END_OFF;
773     }
774
775 /* Log the sending of the message if successful and required. If the file
776 fails to open, it's hard to know what to do. We cannot write to the Exim
777 log from here, since we may be running under an unprivileged uid. We don't
778 want to fail the delivery, since the message has been successfully sent. For
779 the moment, ignore open failures. Write the log entry as a single write() to a
780 file opened for appending, in order to avoid interleaving of output from
781 different processes. The log_buffer can be used exactly as for main log
782 writing. */
783
784 if (logfile)
785   {
786   int log_fd = Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode);
787   if (log_fd >= 0)
788     {
789     gstring gs = { .size = LOG_BUFFER_SIZE, .ptr = 0, .s = log_buffer }, *g = &gs;
790
791     /* Use taint-unchecked routines for writing into log_buffer, trusting
792     that we'll never expand it. */
793
794     DEBUG(D_transport) debug_printf("logging message details\n");
795     g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "%s\n", tod_stamp(tod_log));
796     if (from)
797       g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "  From: %s\n", from);
798     if (to)
799       g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "  To: %s\n", to);
800     if (cc)
801       g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "  Cc: %s\n", cc);
802     if (bcc)
803       g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "  Bcc: %s\n", bcc);
804     if (subject)
805       g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "  Subject: %s\n", subject);
806     if (headers)
807       g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "  %s\n", headers);
808     if(write(log_fd, g->s, g->ptr) != g->ptr || close(log_fd))
809       DEBUG(D_transport) debug_printf("Problem writing log file %s for %s "
810         "transport\n", logfile, tblock->name);
811     }
812   else DEBUG(D_transport) debug_printf("Failed to open log file %s for %s "
813     "transport: %s\n", logfile, tblock->name, strerror(errno));
814   }
815
816 END_OFF:
817 if (dbm_file) EXIM_DBCLOSE(dbm_file);
818 if (cache_fd > 0) (void)close(cache_fd);
819
820 DEBUG(D_transport) debug_printf("%s transport succeeded\n", tblock->name);
821
822 return FALSE;
823 }
824
825 #endif  /*!MACRO_PREDEF*/
826 /* End of transport/autoreply.c */