|
116 | 116 | from Mailman import Errors |
117 | 117 | from Mailman import LockFile |
118 | 118 | from Mailman.Queue.Runner import Runner |
| 119 | +from Mailman.Queue.Switchboard import Switchboard |
119 | 120 | from Mailman.Logging.Syslog import mailman_log |
120 | 121 | import Mailman.MailList as MailList |
121 | 122 | import Mailman.Message |
| 123 | +import threading |
| 124 | +import email.header |
122 | 125 |
|
123 | 126 |
|
124 | 127 | class PipelineError(Exception): |
@@ -192,6 +195,14 @@ def _dispose(self, mlist, msg, msgdata): |
192 | 195 | # Convert Python's Message to Mailman's Message if needed |
193 | 196 | msg = self._convert_message(msg) |
194 | 197 |
|
| 198 | + # Check if this is a bounce message |
| 199 | + if self._is_bounce(msg): |
| 200 | + mailman_log('debug', 'IncomingRunner._dispose: Message %s is a bounce, routing to bounce queue', msgid) |
| 201 | + # Route to bounce queue |
| 202 | + bounce_queue = Switchboard(mm_cfg.BOUNCEQUEUE_DIR) |
| 203 | + bounce_queue.enqueue(msg, msgdata) |
| 204 | + return False |
| 205 | + |
195 | 206 | # Get the pipeline |
196 | 207 | pipeline = self._get_pipeline(mlist, msg, msgdata) |
197 | 208 | if not pipeline: |
@@ -282,19 +293,26 @@ def _is_command(self, msg): |
282 | 293 | return False |
283 | 294 |
|
284 | 295 | def _is_bounce(self, msg): |
285 | | - """Check if the message is a bounce.""" |
286 | | - try: |
287 | | - # Check common bounce headers |
288 | | - bounce_headers = ['X-Failed-Recipients', 'X-Original-To', 'Return-Path'] |
289 | | - for header in bounce_headers: |
290 | | - if msg.get(header): |
291 | | - mailman_log('debug', 'IncomingRunner._is_bounce: Message has bounce header %s', header) |
292 | | - return True |
293 | | - return False |
294 | | - except Exception as e: |
295 | | - mailman_log('error', 'IncomingRunner._is_bounce: Error checking bounce: %s\nTraceback:\n%s', |
296 | | - str(e), traceback.format_exc()) |
297 | | - return False |
| 296 | + """Check if a message is a bounce message.""" |
| 297 | + # Check for common bounce headers |
| 298 | + if msg.get('x-failed-recipients'): |
| 299 | + return True |
| 300 | + if msg.get('x-original-to'): |
| 301 | + return True |
| 302 | + if msg.get('return-path', '').startswith('<>'): |
| 303 | + return True |
| 304 | + # Check content type for multipart/report |
| 305 | + if msg.get('content-type', '').startswith('multipart/report'): |
| 306 | + return True |
| 307 | + # Check for common bounce subjects |
| 308 | + subject = msg.get('subject', '').lower() |
| 309 | + bounce_subjects = ['delivery status', 'failure notice', 'mail delivery failed', |
| 310 | + 'mail delivery system', 'mail system error', 'returned mail', |
| 311 | + 'undeliverable', 'undelivered mail'] |
| 312 | + for bounce_subject in bounce_subjects: |
| 313 | + if bounce_subject in subject: |
| 314 | + return True |
| 315 | + return False |
298 | 316 |
|
299 | 317 | def _process_command(self, mlist, msg, msgdata): |
300 | 318 | """Process a command message.""" |
|
0 commit comments