Commit | Line | Data |
---|---|---|
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 | ||
0e22dfd1 PH |
16 | /************************************************* |
17 | * Write From: line for DSN * | |
18 | *************************************************/ | |
19 | ||
20 | /* This function is called to write the From: line in automatically generated | |
21 | messages - bounces, warnings, etc. It expands a configuration item in order to | |
22 | get the text. If the expansion fails, a panic is logged and the default value | |
23 | for the option is used. | |
24 | ||
25 | Argument: the FILE to write to | |
26 | Returns: nothing | |
27 | */ | |
28 | ||
29 | void | |
30 | moan_write_from(FILE *f) | |
31 | { | |
d6c829b9 | 32 | uschar * s = expand_string(dsn_from); |
5455f548 | 33 | if (!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 | } | |
39 | fprintf(f, "From: %s\n", s); | |
40 | } | |
41 | ||
42 | ||
43 | ||
d6c829b9 JH |
44 | /************************************************* |
45 | * Write References: line for DSN * | |
46 | *************************************************/ | |
47 | ||
48 | /* Generate a References: header if there is in the header_list | |
49 | at least one of Message-ID:, References:, or In-Reply-To: (see RFC 2822). | |
50 | ||
51 | Arguments: f the FILE to write to | |
52 | message_id optional already-found message-id, or NULL | |
53 | ||
54 | Returns: nothing | |
55 | */ | |
56 | ||
57 | void | |
58 | moan_write_references(FILE * fp, uschar * message_id) | |
59 | { | |
60 | header_line * h; | |
61 | ||
62 | if (!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 | ||
70 | for (h = header_list; h; h = h->next) | |
71 | if (h->type != htype_old && strncmpic(US"References:", h->text, 11) == 0) | |
72 | break; | |
73 | ||
74 | if (!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 | |
80 | limit, some systems do not like headers growing beyond recognition. | |
81 | Keep the first message ID for the thread root and the last few for | |
82 | the position inside the thread, up to a maximum of 12 altogether. */ | |
83 | ||
84 | if (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 | ||
059ec3d9 PH |
118 | /************************************************* |
119 | * Send error message * | |
120 | *************************************************/ | |
121 | ||
122 | /* This function sends an error message by opening a pipe to a new process | |
123 | running Exim, and writing a message to it using the "-t" option. This is not | |
124 | used for delivery failures, which have their own code for handing failed | |
125 | addresses. | |
126 | ||
127 | Arguments: | |
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 | ||
136 | Returns: TRUE if message successfully sent | |
137 | */ | |
138 | ||
5455f548 | 139 | BOOL |
059ec3d9 PH |
140 | moan_send_message(uschar *recipient, int ident, error_block *eblock, |
141 | header_line *headers, FILE *message_file, uschar *firstline) | |
142 | { | |
143 | int written = 0; | |
144 | int fd; | |
145 | int status; | |
146 | int count = 0; | |
147 | int size_limit = bounce_return_size_limit; | |
8768d548 | 148 | FILE * fp; |
5455f548 | 149 | int pid; |
059ec3d9 | 150 | |
1a2e76e1 | 151 | #ifdef SUPPORT_DMARC |
5455f548 JH |
152 | uschar * s, * s2; |
153 | ||
154 | /* For DMARC if there is a specific sender set, expand the variable for the | |
155 | header From: and grab the address from that for the envelope FROM. */ | |
156 | ||
157 | if ( 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 |
166 | else |
167 | { | |
168 | s = NULL; | |
8e9fdd63 | 169 | pid = child_open_exim(&fd, US"moan_send_message"); |
5455f548 JH |
170 | } |
171 | ||
172 | #else | |
8e9fdd63 | 173 | pid = child_open_exim(&fd, US"moan_send_message"); |
5455f548 | 174 | #endif |
059ec3d9 PH |
175 | |
176 | if (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 | } | |
182 | else DEBUG(D_any) debug_printf("Child process %d for sending message\n", pid); | |
183 | ||
184 | /* Creation of child succeeded */ | |
185 | ||
8768d548 JH |
186 | fp = fdopen(fd, "wb"); |
187 | if (errors_reply_to) fprintf(fp, "Reply-To: %s\n", errors_reply_to); | |
188 | fprintf(fp, "Auto-Submitted: auto-replied\n"); | |
5455f548 | 189 | |
1a2e76e1 | 190 | #ifdef SUPPORT_DMARC |
5455f548 JH |
191 | if (s) |
192 | fprintf(fp, "From: %s\n", s); | |
193 | else | |
194 | #endif | |
195 | moan_write_from(fp); | |
196 | ||
8768d548 | 197 | fprintf(fp, "To: %s\n", recipient); |
d6c829b9 | 198 | moan_write_references(fp, NULL); |
059ec3d9 PH |
199 | |
200 | switch(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 |
336 | the input if available, up to the configured limit, if the option for including | |
337 | message bodies in bounces is set. */ | |
059ec3d9 | 338 | |
715ab376 | 339 | if (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 |
431 | that is receiving the message. Wait for it to finish, without a timeout. */ | |
432 | ||
8768d548 | 433 | (void)fclose(fp); |
059ec3d9 PH |
434 | status = child_close(pid, 0); /* Waits for child to close */ |
435 | if (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 | ||
446 | return 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 | |
456 | message. Delivery failures are handled separately in deliver.c. | |
457 | ||
458 | If there is a valid sender_address, and the failing message is not a local | |
459 | error message, then this function calls moan_send_message to send a message to | |
460 | that person. If the sender's address is null, then an error has occurred with a | |
461 | message that was generated by a mailer daemon. All we can do is to write | |
462 | information to log files. The same action is taken if local_error_message is | |
463 | set - this can happen for non null-senders in certain configurations where exim | |
464 | doesn't run setuid root. | |
465 | ||
466 | Arguments: | |
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 | ||
474 | Returns: FALSE if there is no sender_address to send to; | |
475 | else the return from moan_send_message() | |
476 | */ | |
477 | ||
478 | BOOL | |
479 | moan_to_sender(int ident, error_block *eblock, header_line *headers, | |
480 | FILE *message_file, BOOL check_sender) | |
481 | { | |
482 | uschar *firstline = NULL; | |
483 | uschar *msg = US"Error while reading message with no usable sender address"; | |
484 | ||
62b7cd08 | 485 | if (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 | 490 | if (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 | 502 | if (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 | ||
508 | switch(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 | ||
560 | return 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 | |
570 | mailmaster) about some incident. | |
571 | ||
572 | Arguments: | |
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 | ||
579 | Returns: nothing | |
580 | */ | |
581 | ||
582 | void | |
1ba28e2b PP |
583 | moan_tell_someone(uschar *who, address_item *addr, |
584 | const uschar *subject, const char *format, ...) | |
059ec3d9 PH |
585 | { |
586 | FILE *f; | |
587 | va_list ap; | |
588 | int fd; | |
8e9fdd63 | 589 | int pid = child_open_exim(&fd, US"moan_tell_someone"); |
059ec3d9 PH |
590 | |
591 | if (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 | ||
598 | f = fdopen(fd, "wb"); | |
456682f5 | 599 | fprintf(f, "Auto-Submitted: auto-replied\n"); |
0e22dfd1 | 600 | moan_write_from(f); |
059ec3d9 | 601 | fprintf(f, "To: %s\n", who); |
d6c829b9 | 602 | moan_write_references(f, NULL); |
059ec3d9 PH |
603 | fprintf(f, "Subject: %s\n\n", subject); |
604 | va_start(ap, format); | |
605 | vfprintf(f, format, ap); | |
606 | va_end(ap); | |
607 | ||
d7978c0f | 608 | if (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 |
623 | child_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. | |
633 | Information is written to stdout and/or stderr, and Exim exits with a non-zero | |
634 | completion code. BSMTP is almost always called by some other program, so it is | |
635 | up to that program to interpret the return code and do something with the error | |
636 | information, and also to preserve the batch input file for human analysis. | |
637 | ||
638 | Formerly, Exim used to attempt to continue after some errors, but this strategy | |
639 | has been abandoned as it can lead to loss of messages. | |
640 | ||
641 | Arguments: | |
642 | cmd_buffer the command causing the error, or NULL | |
643 | format a printf() format | |
644 | ... arguments for the format | |
645 | ||
646 | Returns: 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 | ||
651 | void | |
1ba28e2b | 652 | moan_smtp_batch(uschar *cmd_buffer, const char *format, ...) |
059ec3d9 PH |
653 | { |
654 | va_list ap; | |
655 | int yield = (receive_messagecount > 0)? 1 : 2; | |
656 | ||
657 | DEBUG(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 | ||
661 | va_start(ap, format); | |
662 | vfprintf(stdout, format, ap); | |
663 | va_end(ap); | |
664 | ||
665 | fprintf(stdout, "\nTransaction started in line %d\n", | |
666 | bsmtp_transaction_linecount); | |
667 | fprintf(stdout, "Error detected in line %d\n", receive_linecount); | |
668 | if (cmd_buffer != NULL) fprintf(stdout, "%s\n", cmd_buffer); | |
669 | ||
670 | /* On stderr, write stuff for human consumption */ | |
671 | ||
672 | fprintf(stderr, | |
673 | "An error was detected while processing a file of BSMTP input.\n" | |
674 | "The error message was:\n\n "); | |
675 | ||
676 | va_start(ap, format); | |
677 | vfprintf(stderr, format, ap); | |
678 | va_end(ap); | |
679 | ||
680 | fprintf(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 | ||
685 | if (cmd_buffer != NULL) | |
686 | { | |
687 | fprintf(stderr, "The SMTP command at fault was:\n\n %s\n\n", | |
688 | cmd_buffer); | |
689 | } | |
690 | ||
691 | fprintf(stderr, "%d previous message%s successfully processed.\n", | |
692 | receive_messagecount, (receive_messagecount == 1)? " was" : "s were"); | |
693 | ||
694 | fprintf(stderr, "The rest of the batch was abandoned.\n"); | |
695 | ||
81022793 | 696 | exim_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 | |
707 | the error_copies string to see whether there is an additional recipient list to | |
708 | which errors for this recipient must be bcc'd. The incoming recipient is always | |
709 | fully qualified. | |
710 | ||
711 | Argument: recipient address | |
712 | Returns: additional recipient list or NULL | |
713 | */ | |
714 | ||
715 | uschar * | |
716 | moan_check_errorcopy(uschar *recipient) | |
717 | { | |
718 | uschar *item, *localpart, *domain; | |
55414b25 | 719 | const uschar *listptr = errors_copy; |
059ec3d9 PH |
720 | uschar *yield = NULL; |
721 | uschar buffer[256]; | |
722 | int sep = 0; | |
723 | int llen; | |
724 | ||
725 | if (errors_copy == NULL) return NULL; | |
726 | ||
727 | /* Set up pointer to the local part and domain, and compute the | |
728 | length of the local part. */ | |
729 | ||
730 | localpart = recipient; | |
bc64a74d | 731 | domain = Ustrrchr(recipient, '@'); |
059ec3d9 PH |
732 | if (domain == NULL) return NULL; /* should not occur, but avoid crash */ |
733 | llen = domain++ - recipient; | |
734 | ||
735 | /* Scan through the configured items */ | |
736 | ||
d6c829b9 | 737 | while ((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 | ||
767 | DEBUG(D_any) debug_printf("errors_copy check returned %s\n", | |
768 | (yield == NULL)? US"NULL" : yield); | |
769 | ||
770 | expand_nmax = -1; | |
771 | return 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 | |
781 | or more syntax errors in the list of addresses. If there is an address to mail | |
782 | to, send a message, and always write the information to the log. In the case of | |
783 | a filter file, a "syntax error" might actually be something else, such as the | |
784 | inability to open a log file. Thus, the wording of the error message is | |
785 | general. | |
786 | ||
787 | Arguments: | |
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 | ||
794 | Returns: FALSE if string expansion failed; TRUE otherwise | |
795 | */ | |
796 | ||
797 | BOOL | |
798 | moan_skipped_syntax_errors(uschar *rname, error_block *eblock, | |
799 | uschar *syntax_errors_to, BOOL some, uschar *custom) | |
800 | { | |
801 | int pid, fd; | |
802 | uschar *s, *t; | |
803 | FILE *f; | |
059ec3d9 | 804 | |
d7978c0f | 805 | for (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 | 813 | if (!syntax_errors_to) return TRUE; |
059ec3d9 | 814 | |
d7978c0f | 815 | if (!(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 | |
823 | it. */ | |
824 | ||
8e9fdd63 | 825 | pid = child_open_exim(&fd, US"moan_skipped_syntax_errors"); |
059ec3d9 PH |
826 | |
827 | if (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 | ||
834 | f = fdopen(fd, "wb"); | |
456682f5 | 835 | fprintf(f, "Auto-Submitted: auto-replied\n"); |
0e22dfd1 | 836 | moan_write_from(f); |
059ec3d9 PH |
837 | fprintf(f, "To: %s\n", s); |
838 | fprintf(f, "Subject: error(s) in forwarding or filtering\n\n"); | |
d6c829b9 | 839 | moan_write_references(f, NULL); |
059ec3d9 | 840 | |
d7978c0f | 841 | if (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 | ||
852 | fprintf(f, "The %s router encountered the following error(s):\n\n", | |
853 | rname); | |
854 | ||
d7978c0f | 855 | for (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 | ||
863 | if (some) | |
864 | fprintf(f, "Other addresses were processed normally.\n"); | |
865 | else | |
866 | fprintf(f, "No valid addresses were generated.\n"); | |
867 | ||
f1e894f3 | 868 | (void)fclose(f); |
059ec3d9 PH |
869 | child_close(pid, 0); /* Waits for child to close; no timeout */ |
870 | ||
871 | return TRUE; | |
872 | } | |
873 | ||
874 | /* End of moan.c */ |