@@ -381,10 +381,13 @@ def found_terminator(self):
381381 data .append (text )
382382 self .received_data = self ._newline .join (data )
383383 args = (self .peer , self .mailfrom , self .rcpttos , self .received_data )
384- if self .require_SMTPUTF8 :
385- status = self .smtp_server .process_smtputf8_message (* args )
386- else :
387- status = self .smtp_server .process_message (* args )
384+ kwargs = {}
385+ if not self ._decode_data :
386+ kwargs = {
387+ 'mail_options' : self .mail_options ,
388+ 'rcpt_options' : self .rcpt_options ,
389+ }
390+ status = self .smtp_server .process_message (* args , ** kwargs )
388391 self ._set_post_data_state ()
389392 if not status :
390393 self .push ('250 OK' )
@@ -419,8 +422,9 @@ def smtp_EHLO(self, arg):
419422 if self .data_size_limit :
420423 self .push ('250-SIZE %s' % self .data_size_limit )
421424 self .command_size_limits ['MAIL' ] += 26
422- if self .enable_SMTPUTF8 :
425+ if not self ._decode_data :
423426 self .push ('250-8BITMIME' )
427+ if self .enable_SMTPUTF8 :
424428 self .push ('250-SMTPUTF8' )
425429 self .command_size_limits ['MAIL' ] += 10
426430 self .push ('250 HELP' )
@@ -454,11 +458,15 @@ def _getaddr(self, arg):
454458 return address .addr_spec , rest
455459
456460 def _getparams (self , params ):
457- # Return any parameters that appear to be syntactically valid according
458- # to RFC 1869, ignore all others. (Postel rule: accept what we can.)
459- params = [param .split ('=' , 1 ) if '=' in param else (param , True )
460- for param in params .split ()]
461- return {k : v for k , v in params if k .isalnum ()}
461+ # Return params as dictionary. Return None if not all parameters
462+ # appear to be syntactically valid according to RFC 1869.
463+ result = {}
464+ for param in params :
465+ param , eq , value = param .partition ('=' )
466+ if not param .isalnum () or eq and not value :
467+ return None
468+ result [param ] = value if eq else True
469+ return result
462470
463471 def smtp_HELP (self , arg ):
464472 if arg :
@@ -508,7 +516,7 @@ def smtp_VRFY(self, arg):
508516
509517 def smtp_MAIL (self , arg ):
510518 if not self .seen_greeting :
511- self .push ('503 Error: send HELO first' );
519+ self .push ('503 Error: send HELO first' )
512520 return
513521 print ('===> MAIL' , arg , file = DEBUGSTREAM )
514522 syntaxerr = '501 Syntax: MAIL FROM: <address>'
@@ -528,18 +536,23 @@ def smtp_MAIL(self, arg):
528536 if self .mailfrom :
529537 self .push ('503 Error: nested MAIL command' )
530538 return
531- params = self ._getparams (params .upper ())
539+ self .mail_options = params .upper ().split ()
540+ params = self ._getparams (self .mail_options )
532541 if params is None :
533542 self .push (syntaxerr )
534543 return
535- body = params .pop ('BODY' , '7BIT' )
536- if self .enable_SMTPUTF8 and params .pop ('SMTPUTF8' , False ):
537- if body != '8BITMIME' :
538- self .push ('501 Syntax: MAIL FROM: <address>'
539- ' [BODY=8BITMIME SMTPUTF8]' )
544+ if not self ._decode_data :
545+ body = params .pop ('BODY' , '7BIT' )
546+ if body not in ['7BIT' , '8BITMIME' ]:
547+ self .push ('501 Error: BODY can only be one of 7BIT, 8BITMIME' )
540548 return
541- else :
549+ if self .enable_SMTPUTF8 :
550+ smtputf8 = params .pop ('SMTPUTF8' , False )
551+ if smtputf8 is True :
542552 self .require_SMTPUTF8 = True
553+ elif smtputf8 is not False :
554+ self .push ('501 Error: SMTPUTF8 takes no arguments' )
555+ return
543556 size = params .pop ('SIZE' , None )
544557 if size :
545558 if not size .isdigit ():
@@ -574,16 +587,16 @@ def smtp_RCPT(self, arg):
574587 if not address :
575588 self .push (syntaxerr )
576589 return
577- if params :
578- if self .extended_smtp :
579- params = self . _getparams ( params . upper ())
580- if params is None :
581- self .push ( syntaxerr )
582- return
583- else :
584- self . push ( syntaxerr )
585- return
586- if params and len (params .keys ()) > 0 :
590+ if not self . extended_smtp and params :
591+ self .push ( syntaxerr )
592+ return
593+ self . rcpt_options = params . upper (). split ()
594+ params = self ._getparams ( self . rcpt_options )
595+ if params is None :
596+ self . push ( syntaxerr )
597+ return
598+ # XXX currently there are no options we recognize.
599+ if len (params .keys ()) > 0 :
587600 self .push ('555 RCPT TO parameters not recognized or not implemented' )
588601 return
589602 self .rcpttos .append (address )
@@ -667,7 +680,7 @@ def handle_accepted(self, conn, addr):
667680 self ._decode_data )
668681
669682 # API for "doing something useful with the message"
670- def process_message (self , peer , mailfrom , rcpttos , data ):
683+ def process_message (self , peer , mailfrom , rcpttos , data , ** kwargs ):
671684 """Override this abstract method to handle messages from the client.
672685
673686 peer is a tuple containing (ipaddr, port) of the client that made the
@@ -685,21 +698,16 @@ def process_message(self, peer, mailfrom, rcpttos, data):
685698 containing a `.' followed by other text has had the leading dot
686699 removed.
687700
688- This function should return None for a normal `250 Ok' response;
689- otherwise, it should return the desired response string in RFC 821
690- format.
691-
692- """
693- raise NotImplementedError
694-
695- # API for processing messeges needing Unicode support (RFC 6531, RFC 6532).
696- def process_smtputf8_message (self , peer , mailfrom , rcpttos , data ):
697- """Same as ``process_message`` but for messages for which the client
698- has sent the SMTPUTF8 parameter with the MAIL command (see the
699- enable_SMTPUTF8 parameter of the constructor).
701+ kwargs is a dictionary containing additional information. It is empty
702+ unless decode_data=False or enable_SMTPUTF8=True was given as init
703+ parameter, in which case ut will contain the following keys:
704+ 'mail_options': list of parameters to the mail command. All
705+ elements are uppercase strings. Example:
706+ ['BODY=8BITMIME', 'SMTPUTF8'].
707+ 'rcpt_options': same, for the rcpt command.
700708
701709 This function should return None for a normal `250 Ok' response;
702- otherwise, it should return the desired response string in RFC 6531
710+ otherwise, it should return the desired response string in RFC 821
703711 format.
704712
705713 """
@@ -725,13 +733,13 @@ def _print_message_content(self, peer, data):
725733 line = repr (line )
726734 print (line )
727735
728- def process_message (self , peer , mailfrom , rcpttos , data ):
736+ def process_message (self , peer , mailfrom , rcpttos , data , ** kwargs ):
729737 print ('---------- MESSAGE FOLLOWS ----------' )
730- self . _print_message_content ( peer , data )
731- print ( '------------ END MESSAGE ------------' )
732-
733- def process_smtputf8_message ( self , peer , mailfrom , rcpttos , data ):
734- print ('----- SMTPUTF8 MESSAGE FOLLOWS ------' )
738+ if kwargs :
739+ if kwargs . get ( 'mail_options' ):
740+ print ( 'mail options: %s' % kwargs [ 'mail_options' ])
741+ if kwargs . get ( 'rcpt_options' ):
742+ print ('rcpt options: %s \n ' % kwargs [ 'rcpt_options' ] )
735743 self ._print_message_content (peer , data )
736744 print ('------------ END MESSAGE ------------' )
737745
0 commit comments