Skip to content

Commit eb53983

Browse files
committed
update
1 parent 1eb6409 commit eb53983

1 file changed

Lines changed: 99 additions & 84 deletions

File tree

Mailman/Handlers/SMTPDirect.py

Lines changed: 99 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -388,91 +388,106 @@ def verpdeliver(mlist, msg, msgdata, envsender, failures, conn):
388388

389389

390390
def bulkdeliver(mlist, msg, msgdata, envsender, failures, conn):
391-
# Convert email.message.Message to Mailman.Message if needed
392-
if isinstance(msg, email.message.Message) and not isinstance(msg, Message):
393-
mailman_msg = Message()
394-
# Copy all attributes from the original message
395-
for key, value in msg.items():
396-
mailman_msg[key] = value
397-
# Copy the payload with proper MIME handling
398-
if msg.is_multipart():
399-
for part in msg.get_payload():
400-
if isinstance(part, email.message.Message):
401-
mailman_msg.attach(part)
402-
else:
403-
newpart = Message()
404-
newpart.set_payload(part)
405-
mailman_msg.attach(newpart)
406-
else:
407-
mailman_msg.set_payload(msg.get_payload())
408-
msg = mailman_msg
409-
410-
# Do some final cleanup of the message header
411-
del msg['errors-to']
412-
msg['Errors-To'] = envsender
413-
if mlist.include_sender_header:
414-
del msg['sender']
415-
msg['Sender'] = '"%s" <%s>' % (mlist.real_name, envsender)
416-
417-
# Check for spam indicators
418-
if msg.get('X-Google-Group-Id'):
419-
mailman_log('smtp-failure', 'Message rejected: Contains X-Google-Group-Id header, likely spam')
420-
# Add all recipients to refused list
421-
for r in recips:
422-
refused[r] = (550, 'Message rejected: Contains X-Google-Group-Id header, likely spam')
423-
# Move message to bad queue
424-
badq = get_switchboard(Mailman.mm_cfg.BADQUEUE_DIR)
425-
badq.enqueue(msg, msgdata)
426-
failures.update(refused)
427-
return
428-
429-
# Get the plain, flattened text of the message
430-
msgtext = msg.as_string(mangle_from_=False)
431-
# Ensure the message text is properly encoded as UTF-8
432-
if isinstance(msgtext, str):
433-
msgtext = msgtext.encode('utf-8')
434-
435-
refused = {}
436-
recips = msgdata['recips']
437-
msgid = msg.get('Message-ID', 'n/a')
438-
# Ensure msgid is a string
439-
if isinstance(msgid, bytes):
440-
try:
441-
msgid = msgid.decode('utf-8', 'replace')
442-
except UnicodeDecodeError:
443-
msgid = msgid.decode('latin-1', 'replace')
444-
elif not isinstance(msgid, str):
445-
msgid = str(msgid)
391+
# Initialize recips at the start
392+
recips = []
446393
try:
447-
# Send the message
448-
refused = conn.sendmail(envsender, recips, msgtext)
449-
except smtplib.SMTPRecipientsRefused as e:
450-
mailman_log('smtp-failure', 'All recipients refused: %s, msgid: %s',
451-
e, msgid)
452-
refused = e.recipients
453-
# Move message to bad queue since all recipients were refused
454-
badq = get_switchboard(Mailman.mm_cfg.BADQUEUE_DIR)
455-
badq.enqueue(msg, msgdata)
456-
except smtplib.SMTPResponseException as e:
457-
mailman_log('smtp-failure', 'SMTP session failure: %s, %s, msgid: %s',
458-
e.smtp_code, e.smtp_error, msgid)
459-
# Properly handle permanent vs temporary failures
460-
if e.smtp_code >= 500 and e.smtp_code != 552:
461-
# Permanent failure - add to refused and move to bad queue
394+
# Check for spam headers first
395+
if msg.get('x-google-group-id'):
396+
mailman_log('error', 'Rejecting message with X-Google-Group-Id header')
397+
# Add all recipients to refused list with 550 error
398+
for r in msgdata.get('recipients', []):
399+
refused[r] = (550, 'Message rejected due to spam detection')
400+
# Move message to bad queue
401+
badq = get_switchboard(Mailman.mm_cfg.BADQUEUE_DIR)
402+
badq.enqueue(msg, msgdata)
403+
# Update failures dict
404+
failures = msgdata.get('failures', {})
405+
failures.update(refused)
406+
msgdata['failures'] = failures
407+
return
408+
409+
# Get the list of recipients
410+
recips = msgdata.get('recipients', [])
411+
if not recips:
412+
mailman_log('error', 'No recipients found in msgdata')
413+
return
414+
415+
# Convert email.message.Message to Mailman.Message if needed
416+
if isinstance(msg, email.message.Message) and not isinstance(msg, Message):
417+
mailman_msg = Message()
418+
# Copy all attributes from the original message
419+
for key, value in msg.items():
420+
mailman_msg[key] = value
421+
# Copy the payload with proper MIME handling
422+
if msg.is_multipart():
423+
for part in msg.get_payload():
424+
if isinstance(part, email.message.Message):
425+
mailman_msg.attach(part)
426+
else:
427+
newpart = Message()
428+
newpart.set_payload(part)
429+
mailman_msg.attach(newpart)
430+
else:
431+
mailman_msg.set_payload(msg.get_payload())
432+
msg = mailman_msg
433+
434+
# Do some final cleanup of the message header
435+
del msg['errors-to']
436+
msg['Errors-To'] = envsender
437+
if mlist.include_sender_header:
438+
del msg['sender']
439+
msg['Sender'] = '"%s" <%s>' % (mlist.real_name, envsender)
440+
441+
# Get the plain, flattened text of the message
442+
msgtext = msg.as_string(mangle_from_=False)
443+
# Ensure the message text is properly encoded as UTF-8
444+
if isinstance(msgtext, str):
445+
msgtext = msgtext.encode('utf-8')
446+
447+
refused = {}
448+
msgid = msg.get('Message-ID', 'n/a')
449+
# Ensure msgid is a string
450+
if isinstance(msgid, bytes):
451+
try:
452+
msgid = msgid.decode('utf-8', 'replace')
453+
except UnicodeDecodeError:
454+
msgid = msgid.decode('latin-1', 'replace')
455+
elif not isinstance(msgid, str):
456+
msgid = str(msgid)
457+
try:
458+
# Send the message
459+
refused = conn.sendmail(envsender, recips, msgtext)
460+
except smtplib.SMTPRecipientsRefused as e:
461+
mailman_log('smtp-failure', 'All recipients refused: %s, msgid: %s',
462+
e, msgid)
463+
refused = e.recipients
464+
# Move message to bad queue since all recipients were refused
465+
badq = get_switchboard(Mailman.mm_cfg.BADQUEUE_DIR)
466+
badq.enqueue(msg, msgdata)
467+
except smtplib.SMTPResponseException as e:
468+
mailman_log('smtp-failure', 'SMTP session failure: %s, %s, msgid: %s',
469+
e.smtp_code, e.smtp_error, msgid)
470+
# Properly handle permanent vs temporary failures
471+
if e.smtp_code >= 500 and e.smtp_code != 552:
472+
# Permanent failure - add to refused and move to bad queue
473+
for r in recips:
474+
refused[r] = (e.smtp_code, e.smtp_error)
475+
badq = get_switchboard(Mailman.mm_cfg.BADQUEUE_DIR)
476+
badq.enqueue(msg, msgdata)
477+
else:
478+
# Temporary failure - don't add to refused
479+
mailman_log('smtp-failure', 'Temporary SMTP failure, will retry: %s', e.smtp_error)
480+
except (socket.error, IOError, smtplib.SMTPException) as e:
481+
# MTA not responding or other socket problems
482+
mailman_log('smtp-failure', 'Low level smtp error: %s, msgid: %s', e, msgid)
483+
error = str(e)
462484
for r in recips:
463-
refused[r] = (e.smtp_code, e.smtp_error)
485+
refused[r] = (-1, error)
486+
# Move message to bad queue for low level errors
464487
badq = get_switchboard(Mailman.mm_cfg.BADQUEUE_DIR)
465488
badq.enqueue(msg, msgdata)
466-
else:
467-
# Temporary failure - don't add to refused
468-
mailman_log('smtp-failure', 'Temporary SMTP failure, will retry: %s', e.smtp_error)
469-
except (socket.error, IOError, smtplib.SMTPException) as e:
470-
# MTA not responding or other socket problems
471-
mailman_log('smtp-failure', 'Low level smtp error: %s, msgid: %s', e, msgid)
472-
error = str(e)
473-
for r in recips:
474-
refused[r] = (-1, error)
475-
# Move message to bad queue for low level errors
476-
badq = get_switchboard(Mailman.mm_cfg.BADQUEUE_DIR)
477-
badq.enqueue(msg, msgdata)
478-
failures.update(refused)
489+
failures.update(refused)
490+
except Exception as e:
491+
mailman_log('error', 'Error in bulkdeliver: %s\nTraceback:\n%s',
492+
str(e), traceback.format_exc())
493+
raise

0 commit comments

Comments
 (0)