More checks on header line length during reception
[exim.git] / src / src / moan.c
CommitLineData
059ec3d9
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
f9ba5e22 5/* Copyright (c) University of Cambridge 1995 - 2018 */
059ec3d9
PH
6/* See the file NOTICE for conditions of use and distribution. */
7
8/* Functions for sending messages to sender or to mailmaster. */
9
10
11#include "exim.h"
12
13
14
0e22dfd1
PH
15/*************************************************
16* Write From: line for DSN *
17*************************************************/
18
19/* This function is called to write the From: line in automatically generated
20messages - bounces, warnings, etc. It expands a configuration item in order to
21get the text. If the expansion fails, a panic is logged and the default value
22for the option is used.
23
24Argument: the FILE to write to
25Returns: nothing
26*/
27
28void
29moan_write_from(FILE *f)
30{
31uschar *s = expand_string(dsn_from);
5455f548 32if (!s)
0e22dfd1
PH
33 {
34 log_write(0, LOG_MAIN|LOG_PANIC,
35 "Failed to expand dsn_from (using default): %s", expand_string_message);
36 s = expand_string(US DEFAULT_DSN_FROM);
37 }
38fprintf(f, "From: %s\n", s);
39}
40
41
42
059ec3d9
PH
43/*************************************************
44* Send error message *
45*************************************************/
46
47/* This function sends an error message by opening a pipe to a new process
48running Exim, and writing a message to it using the "-t" option. This is not
49used for delivery failures, which have their own code for handing failed
50addresses.
51
52Arguments:
53 recipient addressee for the message
54 ident identifies the type of error
55 eblock chain of error_blocks containing data about the error
56 headers the message's headers
57 message_file FILE containing the body of the message
58 firstline contains first line of file, if it was read to check for
59 "From ", but it turned out not to be
60
61Returns: TRUE if message successfully sent
62*/
63
5455f548 64BOOL
059ec3d9
PH
65moan_send_message(uschar *recipient, int ident, error_block *eblock,
66 header_line *headers, FILE *message_file, uschar *firstline)
67{
68int written = 0;
69int fd;
70int status;
71int count = 0;
72int size_limit = bounce_return_size_limit;
8768d548 73FILE * fp;
5455f548 74int pid;
059ec3d9 75
5455f548
JH
76#ifdef EXPERIMENTAL_DMARC
77uschar * s, * s2;
78
79/* For DMARC if there is a specific sender set, expand the variable for the
80header From: and grab the address from that for the envelope FROM. */
81
82if ( ident == ERRMESS_DMARC_FORENSIC
83 && dmarc_forensic_sender
84 && (s = expand_string(dmarc_forensic_sender))
85 && *s
86 && (s2 = expand_string(string_sprintf("${address:%s}", s)))
87 && *s2
88 )
89 pid = child_open_exim2(&fd, s2, bounce_sender_authentication);
90else
91 {
92 s = NULL;
93 pid = child_open_exim(&fd);
94 }
95
96#else
97pid = child_open_exim(&fd);
98#endif
059ec3d9
PH
99
100if (pid < 0)
101 {
102 DEBUG(D_any) debug_printf("Failed to create child to send message: %s\n",
103 strerror(errno));
104 return FALSE;
105 }
106else DEBUG(D_any) debug_printf("Child process %d for sending message\n", pid);
107
108/* Creation of child succeeded */
109
8768d548
JH
110fp = fdopen(fd, "wb");
111if (errors_reply_to) fprintf(fp, "Reply-To: %s\n", errors_reply_to);
112fprintf(fp, "Auto-Submitted: auto-replied\n");
5455f548
JH
113
114#ifdef EXPERIMENTAL_DMARC
115if (s)
116 fprintf(fp, "From: %s\n", s);
117else
118#endif
119 moan_write_from(fp);
120
8768d548 121fprintf(fp, "To: %s\n", recipient);
059ec3d9
PH
122
123switch(ident)
124 {
125 case ERRMESS_BADARGADDRESS:
8768d548 126 fprintf(fp,
62b7cd08 127 "Subject: Mail failure - malformed recipient address\n\n");
8768d548 128 fprintf(fp,
62b7cd08
JH
129 "A message that you sent contained a recipient address that was incorrectly\n"
130 "constructed:\n\n");
8768d548 131 fprintf(fp, " %s %s\n", eblock->text1, eblock->text2);
62b7cd08
JH
132 count = Ustrlen(eblock->text1);
133 if (count > 0 && eblock->text1[count-1] == '.')
8768d548 134 fprintf(fp,
62b7cd08 135 "\nRecipient addresses must not end with a '.' character.\n");
8768d548 136 fprintf(fp,
62b7cd08
JH
137 "\nThe message has not been delivered to any recipients.\n");
138 break;
059ec3d9
PH
139
140 case ERRMESS_BADNOADDRESS:
141 case ERRMESS_BADADDRESS:
8768d548 142 fprintf(fp,
62b7cd08 143 "Subject: Mail failure - malformed recipient address\n\n");
8768d548 144 fprintf(fp,
62b7cd08
JH
145 "A message that you sent contained one or more recipient addresses that were\n"
146 "incorrectly constructed:\n\n");
059ec3d9 147
62b7cd08
JH
148 while (eblock != NULL)
149 {
8768d548 150 fprintf(fp, " %s: %s\n", eblock->text1, eblock->text2);
62b7cd08
JH
151 count++;
152 eblock = eblock->next;
153 }
059ec3d9 154
8768d548 155 fprintf(fp, (count == 1)? "\nThis address has been ignored. " :
62b7cd08 156 "\nThese addresses have been ignored. ");
059ec3d9 157
8768d548 158 fprintf(fp, (ident == ERRMESS_BADADDRESS)?
62b7cd08
JH
159 "The other addresses in the message were\n"
160 "syntactically valid and have been passed on for an attempt at delivery.\n" :
059ec3d9 161
62b7cd08
JH
162 "There were no other addresses in your\n"
163 "message, and so no attempt at delivery was possible.\n");
164 break;
059ec3d9
PH
165
166 case ERRMESS_IGADDRESS:
8768d548
JH
167 fprintf(fp, "Subject: Mail failure - no recipient addresses\n\n");
168 fprintf(fp,
62b7cd08
JH
169 "A message that you sent using the -t command line option contained no\n"
170 "addresses that were not also on the command line, and were therefore\n"
171 "suppressed. This left no recipient addresses, and so no delivery could\n"
172 "be attempted.\n");
173 break;
059ec3d9
PH
174
175 case ERRMESS_NOADDRESS:
8768d548
JH
176 fprintf(fp, "Subject: Mail failure - no recipient addresses\n\n");
177 fprintf(fp,
62b7cd08
JH
178 "A message that you sent contained no recipient addresses, and therefore no\n"
179 "delivery could be attempted.\n");
180 break;
059ec3d9
PH
181
182 case ERRMESS_IOERR:
8768d548
JH
183 fprintf(fp, "Subject: Mail failure - system failure\n\n");
184 fprintf(fp,
62b7cd08
JH
185 "A system failure was encountered while processing a message that you sent,\n"
186 "so it has not been possible to deliver it. The error was:\n\n%s\n",
187 eblock->text1);
188 break;
059ec3d9
PH
189
190 case ERRMESS_VLONGHEADER:
8768d548
JH
191 fprintf(fp, "Subject: Mail failure - overlong header section\n\n");
192 fprintf(fp,
62b7cd08
JH
193 "A message that you sent contained a header section that was excessively\n"
194 "long and could not be handled by the mail transmission software. The\n"
195 "message has not been delivered to any recipients.\n");
196 break;
059ec3d9
PH
197
198 case ERRMESS_VLONGHDRLINE:
8768d548
JH
199 fprintf(fp, "Subject: Mail failure - overlong header line\n\n");
200 fprintf(fp,
62b7cd08
JH
201 "A message that you sent contained a header line that was excessively\n"
202 "long and could not be handled by the mail transmission software. The\n"
203 "message has not been delivered to any recipients.\n");
204 break;
059ec3d9
PH
205
206 case ERRMESS_TOOBIG:
8768d548
JH
207 fprintf(fp, "Subject: Mail failure - message too big\n\n");
208 fprintf(fp,
62b7cd08
JH
209 "A message that you sent was longer than the maximum size allowed on this\n"
210 "system. It was not delivered to any recipients.\n");
211 break;
059ec3d9
PH
212
213 case ERRMESS_TOOMANYRECIP:
8768d548
JH
214 fprintf(fp, "Subject: Mail failure - too many recipients\n\n");
215 fprintf(fp,
62b7cd08
JH
216 "A message that you sent contained more recipients than allowed on this\n"
217 "system. It was not delivered to any recipients.\n");
218 break;
059ec3d9
PH
219
220 case ERRMESS_LOCAL_SCAN:
221 case ERRMESS_LOCAL_ACL:
8768d548
JH
222 fprintf(fp, "Subject: Mail failure - rejected by local scanning code\n\n");
223 fprintf(fp,
62b7cd08
JH
224 "A message that you sent was rejected by the local scanning code that\n"
225 "checks incoming messages on this system.");
226 if (eblock->text1)
8768d548
JH
227 fprintf(fp, " The following error was given:\n\n %s", eblock->text1);
228 fprintf(fp, "\n");
059ec3d9
PH
229 break;
230
4840604e
TL
231#ifdef EXPERIMENTAL_DMARC
232 case ERRMESS_DMARC_FORENSIC:
62b7cd08
JH
233 bounce_return_message = TRUE;
234 bounce_return_body = FALSE;
5455f548
JH
235 fprintf(fp, "Subject: DMARC Forensic Report for %s from IP %s\n\n",
236 eblock ? eblock->text2 : US"Unknown",
4840604e 237 sender_host_address);
8768d548 238 fprintf(fp,
62b7cd08
JH
239 "A message claiming to be from you has failed the published DMARC\n"
240 "policy for your domain.\n\n");
5455f548 241 while (eblock)
62b7cd08 242 {
8768d548 243 fprintf(fp, " %s: %s\n", eblock->text1, eblock->text2);
62b7cd08
JH
244 count++;
245 eblock = eblock->next;
246 }
4840604e
TL
247 break;
248#endif
249
059ec3d9 250 default:
8768d548
JH
251 fprintf(fp, "Subject: Mail failure\n\n");
252 fprintf(fp,
62b7cd08
JH
253 "A message that you sent has caused the error routine to be entered with\n"
254 "an unknown error number (%d).\n", ident);
255 break;
059ec3d9
PH
256 }
257
715ab376
PH
258/* Now, if configured, copy the message; first the headers and then the rest of
259the input if available, up to the configured limit, if the option for including
260message bodies in bounces is set. */
059ec3d9 261
715ab376 262if (bounce_return_message)
059ec3d9 263 {
715ab376
PH
264 if (bounce_return_body)
265 {
8768d548 266 fprintf(fp, "\n"
715ab376
PH
267 "------ This is a copy of your message, including all the headers.");
268 if (size_limit == 0 || size_limit > thismessage_size_limit)
269 size_limit = thismessage_size_limit;
270 if (size_limit > 0 && size_limit < message_size)
271 {
272 int x = size_limit;
273 uschar *k = US"";
274 if ((x & 1023) == 0)
275 {
276 k = US"K";
277 x >>= 10;
278 }
8768d548 279 fprintf(fp, "\n"
715ab376
PH
280 "------ No more than %d%s characters of the body are included.\n\n",
281 x, k);
282 }
8768d548 283 else fprintf(fp, " ------\n\n");
715ab376
PH
284 }
285 else
059ec3d9 286 {
8768d548 287 fprintf(fp, "\n"
715ab376
PH
288 "------ This is a copy of the headers that were received before the "
289 "error\n was detected.\n\n");
059ec3d9 290 }
059ec3d9 291
715ab376
PH
292 /* If the error occurred before the Received: header was created, its text
293 field will still be NULL; just omit such a header line. */
059ec3d9 294
62b7cd08 295 while (headers)
715ab376 296 {
8768d548 297 if (headers->text != NULL) fprintf(fp, "%s", CS headers->text);
715ab376
PH
298 headers = headers->next;
299 }
059ec3d9 300
715ab376 301 if (ident != ERRMESS_VLONGHEADER && ident != ERRMESS_VLONGHDRLINE)
8768d548 302 fputc('\n', fp);
059ec3d9 303
715ab376
PH
304 /* After early detection of an error, the message file may be STDIN,
305 in which case we might have to terminate on a line containing just "."
306 as well as on EOF. We may already have the first line in memory. */
059ec3d9 307
62b7cd08 308 if (bounce_return_body && message_file)
059ec3d9 309 {
8768d548 310 BOOL enddot = f.dot_ends && message_file == stdin;
62b7cd08
JH
311 uschar * buf = store_get(bounce_return_linesize_limit+2);
312
8768d548 313 if (firstline) fprintf(fp, "%s", CS firstline);
62b7cd08 314
f2cb6292 315 while (fgets(CS buf, bounce_return_linesize_limit+2, message_file))
059ec3d9 316 {
62b7cd08
JH
317 int len;
318
319 if (enddot && *buf == '.' && buf[1] == '\n')
320 {
8768d548 321 fputc('.', fp);
62b7cd08
JH
322 break;
323 }
324
325 len = Ustrlen(buf);
326 if (buf[len-1] != '\n')
327 { /* eat rest of partial line */
328 int ch;
329 while ((ch = fgetc(message_file)) != EOF && ch != '\n') ;
330 }
331
332 if (size_limit > 0 && len > size_limit - written)
333 {
334 buf[size_limit - written] = '\0';
8768d548 335 fputs(CS buf, fp);
62b7cd08
JH
336 break;
337 }
338
8768d548 339 fputs(CS buf, fp);
059ec3d9
PH
340 }
341 }
4840604e
TL
342#ifdef EXPERIMENTAL_DMARC
343 /* Overkill, but use exact test in case future code gets inserted */
344 else if (bounce_return_body && message_file == NULL)
345 {
62b7cd08 346 /*XXX limit line length here? */
4840604e
TL
347 /* This doesn't print newlines, disable until can parse and fix
348 * output to be legible. */
8768d548 349 fprintf(fp, "%s", expand_string(US"$message_body"));
4840604e
TL
350 }
351#endif
059ec3d9 352 }
059ec3d9
PH
353/* Close the file, which should send an EOF to the child process
354that is receiving the message. Wait for it to finish, without a timeout. */
355
8768d548 356(void)fclose(fp);
059ec3d9
PH
357status = child_close(pid, 0); /* Waits for child to close */
358if (status != 0)
359 {
360 uschar *msg = US"Child mail process returned status";
361 if (status == -257)
362 log_write(0, LOG_MAIN, "%s %d: errno=%d: %s", msg, status, errno,
363 strerror(errno));
364 else
365 log_write(0, LOG_MAIN, "%s %d", msg, status);
366 return FALSE;
367 }
368
369return TRUE;
370}
371
372
373
374/*************************************************
375* Send message to sender *
376*************************************************/
377
378/* This function is called when errors are detected during the receipt of a
379message. Delivery failures are handled separately in deliver.c.
380
381If there is a valid sender_address, and the failing message is not a local
382error message, then this function calls moan_send_message to send a message to
383that person. If the sender's address is null, then an error has occurred with a
384message that was generated by a mailer daemon. All we can do is to write
385information to log files. The same action is taken if local_error_message is
386set - this can happen for non null-senders in certain configurations where exim
387doesn't run setuid root.
388
389Arguments:
390 ident identifies the particular error
391 eblock chain of error_blocks containing data about the error
392 headers message's headers (chain)
393 message_file a FILE where the body of the message can be read
394 check_sender if TRUE, read the first line of the file for a possible
395 "From " sender (if a trusted caller)
396
397Returns: FALSE if there is no sender_address to send to;
398 else the return from moan_send_message()
399*/
400
401BOOL
402moan_to_sender(int ident, error_block *eblock, header_line *headers,
403 FILE *message_file, BOOL check_sender)
404{
405uschar *firstline = NULL;
406uschar *msg = US"Error while reading message with no usable sender address";
407
62b7cd08 408if (message_reference)
059ec3d9
PH
409 msg = string_sprintf("%s (R=%s)", msg, message_reference);
410
411/* Find the sender from a From line if permitted and possible */
412
8768d548 413if (check_sender && message_file && f.trusted_caller &&
059ec3d9
PH
414 Ufgets(big_buffer, BIG_BUFFER_SIZE, message_file) != NULL)
415 {
416 uschar *new_sender = NULL;
417 if (regex_match_and_setup(regex_From, big_buffer, 0, -1))
418 new_sender = expand_string(uucp_from_sender);
62b7cd08 419 if (new_sender) sender_address = new_sender;
059ec3d9
PH
420 else firstline = big_buffer;
421 }
422
423/* If viable sender address, send a message */
424
8768d548 425if (sender_address && sender_address[0] && !f.local_error_message)
059ec3d9
PH
426 return moan_send_message(sender_address, ident, eblock, headers,
427 message_file, firstline);
428
429/* Otherwise, we can only log */
430
431switch(ident)
432 {
433 case ERRMESS_BADARGADDRESS:
434 case ERRMESS_BADNOADDRESS:
435 case ERRMESS_BADADDRESS:
436 log_write(0, LOG_MAIN, "%s: at least one malformed recipient address: "
437 "%s - %s", msg, eblock->text1, eblock->text2);
438 break;
439
440 case ERRMESS_IGADDRESS:
441 case ERRMESS_NOADDRESS:
442 log_write(0, LOG_MAIN, "%s: no recipient addresses", msg);
443 break;
444
445 /* This error has already been logged. */
446 case ERRMESS_IOERR:
447 break;
448
449 case ERRMESS_VLONGHEADER:
450 log_write(0, LOG_MAIN, "%s: excessively long message header section read "
451 "(more than %d characters)", msg, header_maxsize);
452 break;
453
454 case ERRMESS_VLONGHDRLINE:
455 log_write(0, LOG_MAIN, "%s: excessively long message header line read "
456 "(more than %d characters)", msg, header_line_maxsize);
457 break;
458
459 case ERRMESS_TOOBIG:
460 log_write(0, LOG_MAIN, "%s: message too big (limit set to %d)", msg,
461 thismessage_size_limit);
462 break;
463
464 case ERRMESS_TOOMANYRECIP:
465 log_write(0, LOG_MAIN, "%s: too many recipients (max set to %d)", msg,
466 recipients_max);
467 break;
468
469 case ERRMESS_LOCAL_SCAN:
470 log_write(0, LOG_MAIN, "%s: rejected by local_scan: %s", msg, eblock->text1);
471 break;
472
473 case ERRMESS_LOCAL_ACL:
474 log_write(0, LOG_MAIN, "%s: rejected by non-SMTP ACL: %s", msg, eblock->text1);
475 break;
476
477 default:
478 log_write(0, LOG_MAIN|LOG_PANIC, "%s: unknown error number %d", msg,
479 ident);
480 break;
481 }
482
483return FALSE;
484}
485
486
487
488/*************************************************
489* Send message to someone *
490*************************************************/
491
492/* This is called when exim is configured to tell someone (often the
493mailmaster) about some incident.
494
495Arguments:
496 who address to send mail to
497 addr chain of deferred addresses whose details are to be included
498 subject subject text for the message
499 format a printf() format for the body of the message
500 ... arguments for the format
501
502Returns: nothing
503*/
504
505void
1ba28e2b
PP
506moan_tell_someone(uschar *who, address_item *addr,
507 const uschar *subject, const char *format, ...)
059ec3d9
PH
508{
509FILE *f;
510va_list ap;
511int fd;
512int pid = child_open_exim(&fd);
513
514if (pid < 0)
515 {
516 DEBUG(D_any) debug_printf("Failed to create child to send message: %s\n",
517 strerror(errno));
518 return;
519 }
520
521f = fdopen(fd, "wb");
456682f5 522fprintf(f, "Auto-Submitted: auto-replied\n");
0e22dfd1 523moan_write_from(f);
059ec3d9
PH
524fprintf(f, "To: %s\n", who);
525fprintf(f, "Subject: %s\n\n", subject);
526va_start(ap, format);
527vfprintf(f, format, ap);
528va_end(ap);
529
530if (addr != NULL)
531 {
532 fprintf(f, "\nThe following address(es) have yet to be delivered:\n");
533 for (; addr != NULL; addr = addr->next)
534 {
535 uschar *parent = (addr->parent == NULL)? NULL : addr->parent->address;
536 fprintf(f, " %s", addr->address);
537 if (parent != NULL) fprintf(f, " <%s>", parent);
538 if (addr->basic_errno > 0) fprintf(f, ": %s", strerror(addr->basic_errno));
539 if (addr->message != NULL) fprintf(f, ": %s", addr->message);
540 fprintf(f, "\n");
541 }
542 }
543
f1e894f3 544(void)fclose(f);
059ec3d9
PH
545child_close(pid, 0); /* Waits for child to close; no timeout */
546}
547
548
549
550/*************************************************
551* Handle SMTP batch error *
552*************************************************/
553
554/* This is called when something goes wrong in batched (-bS) SMTP input.
555Information is written to stdout and/or stderr, and Exim exits with a non-zero
556completion code. BSMTP is almost always called by some other program, so it is
557up to that program to interpret the return code and do something with the error
558information, and also to preserve the batch input file for human analysis.
559
560Formerly, Exim used to attempt to continue after some errors, but this strategy
561has been abandoned as it can lead to loss of messages.
562
563Arguments:
564 cmd_buffer the command causing the error, or NULL
565 format a printf() format
566 ... arguments for the format
567
568Returns: does not return; exits from the program
569 exit code = 1 if some messages were accepted
570 exit code = 2 if no messages were accepted
571*/
572
573void
1ba28e2b 574moan_smtp_batch(uschar *cmd_buffer, const char *format, ...)
059ec3d9
PH
575{
576va_list ap;
577int yield = (receive_messagecount > 0)? 1 : 2;
578
579DEBUG(D_any) debug_printf("Handling error in batched SMTP input\n");
580
581/* On stdout, write stuff that a program could parse fairly easily. */
582
583va_start(ap, format);
584vfprintf(stdout, format, ap);
585va_end(ap);
586
587fprintf(stdout, "\nTransaction started in line %d\n",
588 bsmtp_transaction_linecount);
589fprintf(stdout, "Error detected in line %d\n", receive_linecount);
590if (cmd_buffer != NULL) fprintf(stdout, "%s\n", cmd_buffer);
591
592/* On stderr, write stuff for human consumption */
593
594fprintf(stderr,
595 "An error was detected while processing a file of BSMTP input.\n"
596 "The error message was:\n\n ");
597
598va_start(ap, format);
599vfprintf(stderr, format, ap);
600va_end(ap);
601
602fprintf(stderr,
603 "\n\nThe SMTP transaction started in line %d.\n"
604 "The error was detected in line %d.\n",
605 bsmtp_transaction_linecount, receive_linecount);
606
607if (cmd_buffer != NULL)
608 {
609 fprintf(stderr, "The SMTP command at fault was:\n\n %s\n\n",
610 cmd_buffer);
611 }
612
613fprintf(stderr, "%d previous message%s successfully processed.\n",
614 receive_messagecount, (receive_messagecount == 1)? " was" : "s were");
615
616fprintf(stderr, "The rest of the batch was abandoned.\n");
617
9bfb7e1b 618exim_exit(yield, US"batch");
059ec3d9
PH
619}
620
621
622
623
624/*************************************************
625* Check for error copies *
626*************************************************/
627
628/* This function is passed the recipient of an error message, and must check
629the error_copies string to see whether there is an additional recipient list to
630which errors for this recipient must be bcc'd. The incoming recipient is always
631fully qualified.
632
633Argument: recipient address
634Returns: additional recipient list or NULL
635*/
636
637uschar *
638moan_check_errorcopy(uschar *recipient)
639{
640uschar *item, *localpart, *domain;
55414b25 641const uschar *listptr = errors_copy;
059ec3d9
PH
642uschar *yield = NULL;
643uschar buffer[256];
644int sep = 0;
645int llen;
646
647if (errors_copy == NULL) return NULL;
648
649/* Set up pointer to the local part and domain, and compute the
650length of the local part. */
651
652localpart = recipient;
bc64a74d 653domain = Ustrrchr(recipient, '@');
059ec3d9
PH
654if (domain == NULL) return NULL; /* should not occur, but avoid crash */
655llen = domain++ - recipient;
656
657/* Scan through the configured items */
658
659while ((item = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))
660 != NULL)
661 {
55414b25
JH
662 const uschar *newaddress = item;
663 const uschar *pattern = string_dequote(&newaddress);
059ec3d9
PH
664
665 /* If no new address found, just skip this item. */
666
667 while (isspace(*newaddress)) newaddress++;
668 if (*newaddress == 0) continue;
669
670 /* We now have an item to match as an address in item, and the additional
671 address in newaddress. If the pattern matches, expand the new address string
672 and return it. During expansion, make local part and domain available for
673 insertion. This requires a copy to be made; we can't just temporarily
674 terminate it, as the whole address is required for $0. */
675
676 if (match_address_list(recipient, TRUE, TRUE, &pattern, NULL, 0, UCHAR_MAX+1,
677 NULL) == OK)
678 {
ce9f225c 679 deliver_localpart = string_copyn(localpart, llen);
059ec3d9
PH
680 deliver_domain = domain;
681 yield = expand_string_copy(newaddress);
682 deliver_domain = deliver_localpart = NULL;
683 if (yield == NULL)
684 log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand %s when processing "
685 "errors_copy: %s", newaddress, expand_string_message);
686 break;
687 }
688 }
689
690DEBUG(D_any) debug_printf("errors_copy check returned %s\n",
691 (yield == NULL)? US"NULL" : yield);
692
693expand_nmax = -1;
694return yield;
695}
696
697
698
699/************************************************
700* Handle skipped syntax errors *
701************************************************/
702
703/* This function is called by the redirect router when it has skipped over one
704or more syntax errors in the list of addresses. If there is an address to mail
705to, send a message, and always write the information to the log. In the case of
706a filter file, a "syntax error" might actually be something else, such as the
707inability to open a log file. Thus, the wording of the error message is
708general.
709
710Arguments:
711 rname the router name
712 eblock chain of error blocks
713 syntax_errors_to address to send mail to, or NULL
714 some TRUE if some addresses were generated; FALSE if none were
715 custom custom message text
716
717Returns: FALSE if string expansion failed; TRUE otherwise
718*/
719
720BOOL
721moan_skipped_syntax_errors(uschar *rname, error_block *eblock,
722 uschar *syntax_errors_to, BOOL some, uschar *custom)
723{
724int pid, fd;
725uschar *s, *t;
726FILE *f;
727error_block *e;
728
729for (e = eblock; e != NULL; e = e->next)
730 {
731 if (e->text2 != NULL)
732 log_write(0, LOG_MAIN, "%s router: skipped error: %s in \"%s\"",
733 rname, e->text1, e->text2);
734 else
735 log_write(0, LOG_MAIN, "%s router: skipped error: %s", rname,
736 e->text1);
737 }
738
739if (syntax_errors_to == NULL) return TRUE;
740
741s = expand_string(syntax_errors_to);
742if (s == NULL)
743 {
744 log_write(0, LOG_MAIN, "%s router failed to expand %s: %s", rname,
745 syntax_errors_to, expand_string_message);
746 return FALSE;
747 }
748
749/* If we can't create a process to send the message, just forget about
750it. */
751
752pid = child_open_exim(&fd);
753
754if (pid < 0)
755 {
756 DEBUG(D_any) debug_printf("Failed to create child to send message: %s\n",
757 strerror(errno));
758 return TRUE;
759 }
760
761f = fdopen(fd, "wb");
456682f5 762fprintf(f, "Auto-Submitted: auto-replied\n");
0e22dfd1 763moan_write_from(f);
059ec3d9
PH
764fprintf(f, "To: %s\n", s);
765fprintf(f, "Subject: error(s) in forwarding or filtering\n\n");
766
767if (custom != NULL)
768 {
769 t = expand_string(custom);
770 if (t == NULL)
771 {
772 log_write(0, LOG_MAIN, "%s router failed to expand %s: %s", rname,
773 custom, expand_string_message);
774 return FALSE;
775 }
776 fprintf(f, "%s\n\n", t);
777 }
778
779fprintf(f, "The %s router encountered the following error(s):\n\n",
780 rname);
781
782for (e = eblock; e != NULL; e = e->next)
783 {
784 fprintf(f, " %s", e->text1);
785 if (e->text2 != NULL)
786 fprintf(f, " in the address\n \"%s\"", e->text2);
787 fprintf(f, "\n\n");
788 }
789
790if (some)
791 fprintf(f, "Other addresses were processed normally.\n");
792else
793 fprintf(f, "No valid addresses were generated.\n");
794
f1e894f3 795(void)fclose(f);
059ec3d9
PH
796child_close(pid, 0); /* Waits for child to close; no timeout */
797
798return TRUE;
799}
800
801/* End of moan.c */