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