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