@@ -388,91 +388,106 @@ def verpdeliver(mlist, msg, msgdata, envsender, failures, conn):
388388
389389
390390def 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\n Traceback:\n %s' ,
492+ str (e ), traceback .format_exc ())
493+ raise
0 commit comments