U
    i7giQ                     @  sH  U d dl mZ d dlmZ d dlZd dlZd dlZd dlZ	d dl
Z
d dlZd dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d d	lmZ d d
lmZ d dlmZ d dlmZ d dlmZ d dlmZ d dl m!Z! d dl"m#Z# d dl$Z$d dl%m&Z& d dl%m'Z' e	j(r"d dl)Z*e+dej,dd G dd de-Z.dDdddddddZ/dEddddddZ0dFdddd d!d"Z1dGd#dd$d%d&d'Z2dd(d)d*d+Z3G d,d- d-Z4G d.d/ d/e5Z6G d0d1 d1Z7G d2d3 d3Z8G d4d5 d5Z9G d6d7 d7e9Z:G d8d9 d9e9Z;e$< Z=d:e>d;< e=j?d<d=d>Z@d?e>d@< dddAdBdCZAdS )H    )annotationsN)contextmanager)charset)policy)encode_base64)Header)MIMEBase)MIMEMultipart)MIMEText)
formataddr)
formatdate)
make_msgid)	parseaddr)
guess_type)TracebackType)current_app)Flaskutf-8c                      s8   e Zd Zdddd fddZdd fdd	Z  ZS )
FlaskMailUnicodeDecodeErrort.AnyNone)objargsreturnc                   s   || _ t j|  d S N)r   super__init__)selfr   r   	__class__ 7/tmp/pip-unpacked-wheel-bsxxmmbx/flask_mail/__init__.pyr   $   s    z$FlaskMailUnicodeDecodeError.__init__strr   c                   s(   t   }| d| jdt| j dS )Nz. You passed in z ())r   __str__r   type)r   originalr   r    r!   r%   (   s    
z#FlaskMailUnicodeDecodeError.__str__)__name__
__module____qualname__r   r%   __classcell__r    r    r   r!   r   #   s   r   strictr   r"   )sencodingerrorsr   c              
     s   t | tr| S z$t | tr(t|  }nt| }W nZ tk
r } z<t | ts`t| f|j |d fdd| jD }W 5 d}~X Y nX |S )zz
    Similar to smart_text, except that lazy instances are resolved to
    strings, rather than kept as lazy objects.
     c                   s   g | ]}t | qS r    )
force_text).0argr.   r/   r    r!   
<listcomp>>   s     zforce_text.<locals>.<listcomp>N)
isinstancer"   bytesUnicodeDecodeError	Exceptionr   r   join)r-   r.   r/   outer    r4   r!   r1   -   s    


.r1   )subjectr.   r   c                 C  s^   z|  d W nJ tk
rX   zt| |  } W n" tk
rR   t| d  } Y nX Y nX | S )Nasciir   )encodeUnicodeEncodeErrorr   )r=   r.   r    r    r!   sanitize_subjectC   s    rA   str | tuple[str, str])addrr.   r   c                 C  s   t | trtt| } | \}} zt|| }W n" tk
rR   t|d }Y nX z| d W nh tk
r   d| kr| dd\}}tt||}|dd}d	||g} nt| | } Y nX t
|| fS )Nr   r>   @   idna)r6   r"   r   r1   r   r?   r@   splitdecoder:   r   )rC   r.   nmZ	localpartdomainr    r    r!   sanitize_addressO   s"    
rK   z!c.Iterable[str | tuple[str, str]]z	list[str])	addressesr.   r   c                   s    fdd| D S )Nc                   s   g | ]}t | qS r    )rK   )r2   r<   r.   r    r!   r5   k   s     z&sanitize_addresses.<locals>.<listcomp>r    )rL   r.   r    rM   r!   sanitize_addressesh   s    rN   bool)liner   c                 C  s   d| kpd| kS )z,Used by has_bad_header to check for \r or \n
r    )rP   r    r    r!   _has_newlinen   s    rS   c                   @  sv   e Zd ZdZdddddZddd	d
ZdddddddZddddZdddddddZddddddZ	dS )
ConnectionzHandles connection to host.Mailr   )mailr   c                 C  s   || _ d | _d| _d S Nr   )rV   host
num_emails)r   rV   r    r    r!   r   v   s    zConnection.__init__zte.Selfr#   c                 C  s$   | j jrd | _n
|  | _d| _| S rW   )rV   suppressrX   configure_hostrY   r   r    r    r!   	__enter__{   s
    
zConnection.__enter__ztype[BaseException]BaseExceptionr   )exc_type	exc_valuetbr   c                 C  s   | j d k	r| j   d S r   )rX   quit)r   r_   r`   ra   r    r    r!   __exit__   s    
zConnection.__exit__zsmtplib.SMTP | smtplib.SMTP_SSLc                 C  s|   | j jrt| j j| j j}nt| j j| j j}|t| j j	 | j j
rT|  | j jrx| j jrx|| j j| j j |S r   )rV   use_sslsmtplibZSMTP_SSLserverportSMTPset_debuglevelintdebuguse_tlsstarttlsusernamepasswordlogin)r   rX   r    r    r!   r[      s    zConnection.configure_hostNMessagestr | tuple[str, str] | None)messageenvelope_fromr   c                 C  s   |j std|jstd| r(t|jdkr<t |_| jdk	rv| jt	|pV|jt
t|j | |j|j t }tj||d |  jd7  _| j| jjkrd| _| jr| j  |  | _dS )zVerifies and sends message.

        :param message: Message instance.
        :param envelope_from: Email address to be used in MAIL FROM command.
        zNo recipients have been addedzRThe message does not specify a sender and a default sender has not been configuredN)rs   rE   r   )send_toAssertionErrorsenderhas_bad_headersBadHeaderErrordatetimerX   ZsendmailrK   listrN   as_bytesmail_optionsrcpt_optionsr   Z_get_current_objectemail_dispatchedsendrY   rV   
max_emailsrb   r[   )r   rs   rt   appr    r    r!   r      s0    



zConnection.sendr   r   kwargsr   c                 O  s   |  t|| dS ztShortcut for send(msg).

        Takes same arguments as Message constructor.

        :versionadded: 0.3.5
        Nr   rq   r   r   r   r    r    r!   send_message   s    zConnection.send_message)N)
r(   r)   r*   __doc__r   r]   rc   r[   r   r   r    r    r    r!   rT   s   s   	 (rT   c                   @  s   e Zd ZdS )ry   N)r(   r)   r*   r    r    r    r!   ry      s   ry   c                   @  s(   e Zd ZdZd	ddddddddZdS )

Attachmenta?  Encapsulates file attachment information.

    :param filename: filename of attachment
    :param content_type: file mimetype
    :param data: the raw file data
    :param disposition: content-disposition (if any)

    .. versionchanged:: 0.10.0
        The `data` argument is required.

    .. versionadded: 0.3.5
    N
str | Nonestr | bytes | Nonedict[str, str] | None)filenamecontent_typedatadispositionheadersc                 C  sz   |d krt d|| _|d kr2|d k	r2t|d }|d krNt|trJd}nd}|| _|| _|p`d| _|d krpi }|| _d S )Nz The 'data' argument is required.r   z
text/plainzapplication/octet-stream
attachment)	
ValueErrorr   r   r6   r"   r   r   r   r   r   r   r   r   r   r   r    r    r!   r      s    

zAttachment.__init__)NNNNN)r(   r)   r*   r   r   r    r    r    r!   r      s        r   c                   @  s&  e Zd ZdZd;ddddddddd	dd
dddddddZeddddZeddddZejdddddZd<ddddddZ	dddd Z
ddd!d"Zd#dd$d%Zddd&d'Zd#dd(d)Zd*dd+d,Zd*dd-d.Zd/dd0d1d2Zd3dd4d5d6Zd=ddd7dddd8d9d:ZdS )>rq   a;  Encapsulates an email message.

    :param subject: email subject header
    :param recipients: list of email addresses
    :param body: plain text message
    :param html: HTML message
    :param alts: A dict or an iterable to go through dict() that contains multipart
                 alternatives
    :param sender: email sender address, or **MAIL_DEFAULT_SENDER** by default
    :param cc: CC list
    :param bcc: BCC list
    :param attachments: list of Attachment instances
    :param reply_to: reply-to address
    :param date: send date
    :param charset: message character set
    :param extra_headers: A dictionary of additional headers for the message
    :param mail_options: A list of ESMTP options to be used in MAIL FROM command
    :param rcpt_options:  A list of ESMTP options to be used in RCPT commands
     Nr"   z"list[str | tuple[str, str]] | Noner   z3dict[str, str] | c.Iterable[tuple[str, str]] | Nonerr   zlist[Attachment] | Nonezfloat | Noner   zlist[str] | None)r=   
recipientsbodyhtmlaltsrw   ccbccattachmentsreply_torz   r   extra_headersr~   r   c                 C  s   |pt jd j}t|tr2|d  d|d  d}|p8g | _|| _|| _|
| _|pTg | _	|p^g | _
|| _t|ppi | _|| _|| _t | _|| _|| _|pg | _|pg | _|	pg | _d S )NrV   r    <rE   >)r   
extensionsdefault_senderr6   tupler   r=   rw   r   r   r   r   dictr   r   rz   r   msgIdr   r   r~   r   r   )r   r=   r   r   r   r   rw   r   r   r   r   rz   r   r   r~   r   r    r    r!   r     s&    





zMessage.__init__zset[str | tuple[str, str]]r#   c                 C  s2   t | j}| jr|| j | jr.|| j |S r   )setr   r   updater   )r   r;   r    r    r!   ru   =  s    
zMessage.send_toc                 C  s   | j dS Nr   )r   getr\   r    r    r!   r   I  s    zMessage.htmlr   )valuer   c                 C  s&   |d kr| j dd  n
|| j d< d S r   )r   pop)r   r   r    r    r!   r   M  s    plainr
   )textsubtyper   c                 C  s   | j pd}t|||dS )zCreates a MIMEText object with the given subtype (default: 'plain')
        If the text is unicode, the utf-8 charset is used.
        r   )_subtype_charset)r   r
   )r   r   r   r   r    r    r!   	_mimetextT  s    
zMessage._mimetextr   c              
   C  s  t jd j}| jpd}| jpg }t|dkr@| js@| | j}n~t|dkrl| jslt	 }|
| | j nRt	 }t	d}|
| | jd | j D ]\}}|
| || q|
| | jrtt| j||d< t| j||d< dttt| j||d	< t| jd
d|d< | j|d< | jrHdttt| j||d< | jr`t| j||d< | jr| j D ]\}}	|	||< qrtdtj}
|D ]}t|j !d }|"|j# t$| |j%dk	rX|j%}|rt&'d|j%}|(dd)d}|
*d|+ }z|(d W n. t,k
rD   |j-d|j.dd|fd Y nX |j-d|j.|d |j/ D ]\}}|-|| qb|
| qt0j1|_0|S )zCreates the emailrV   r   r   alternativer   SubjectFromz, ToT)	localtimeDatez
Message-IDCczReply-Toz[\s]+/NZNFKDr>   ignorer0   zContent-DispositionUTF8r   )r   )2r   r   ascii_attachmentsr   r   lenr   r   r   r	   attachitemsr=   rA   r1   rK   rw   r:   r|   r   rN   r   r   rz   r   r   r   r   recompileUNICODEr   r   rG   set_payloadr   r   r   unicodedata	normalizer?   rH   substripr@   
add_headerr   r   r   rh   )r   r   r.   r   msgr   mimetypecontentkvZSPACESr   fr   keyr   r    r    r!   _message[  sp    



  zMessage._messagec                 C  s   |    S r   )r   	as_stringr\   r    r    r!   r     s    zMessage.as_stringr7   c                 C  s   |    S r   )r   r}   r\   r    r    r!   r}     s    zMessage.as_bytesc                 C  s   |   S r   )r   r\   r    r    r!   r%     s    zMessage.__str__c                 C  s   |   S r   )r}   r\   r    r    r!   	__bytes__  s    zMessage.__bytes__rO   c                 C  s   | j f| j}| jr || j |D ]4}t|trJ|d  d|d  d}t|r$ dS q$| jrt| jrt| j	dD ]P\}}|s dS |dkr|d dkr dS t|r dS t
| dkrz dS qzdS )	zChecks for bad headers i.e. newlines in subject, sender or recipients.
        RFC5322: Allows multiline CRLF with trailing whitespace (FWS) in headers
        r   r   rE   r   Tz
z	 F)rw   r   r   appendr6   r   rS   r=   	enumeraterG   r   r   )r   r   headerlinenumrP   r    r    r!   rx     s(    

zMessage.has_bad_headersc                 C  s   t jdtdd |  S )Nzs'is_bad_headers' is renamed to 'has_bad_headers'. The old name is deprecated and will be removed in Flask-Mail 1.0.   
stacklevel)warningswarnDeprecationWarningrx   r\   r    r    r!   is_bad_headers  s    zMessage.is_bad_headersrT   )
connectionr   c                 C  s   | |  dS )zVerifies and sends the message.N)r   )r   r   r    r    r!   r     s    zMessage.sendrB   )	recipientr   c                 C  s   | j | dS )zfAdds another recipient to the message.

        :param recipient: email address of recipient.
        N)r   r   )r   r   r    r    r!   add_recipient  s    zMessage.add_recipientr   )r   r   r   r   r   r   c                 C  s   | j t||||| dS )zAdds an attachment to the message.

        :param filename: filename of attachment
        :param content_type: file mimetype
        :param data: the raw file data
        :param disposition: content-disposition (if any)
        N)r   r   r   r   r    r    r!   r     s    zMessage.attach)r   NNNNNNNNNNNNNN)r   )NNNNN)r(   r)   r*   r   r   propertyru   r   setterr   r   r   r}   r%   r   rx   r   r   r   r   r    r    r    r!   rq      sN                  ,(Q	
     rq   c                   @  sN   e Zd ZeddddZddddd	Zd
d
ddddZddddZdS )
_MailMixinzc.Iterator[list[Message]]r#   c              	   #  s:   g  dddd fdd}t |  V  W 5 Q R X dS )a3  Records all messages. Use in unit tests for example::

            with mail.record_messages() as outbox:
                response = app.test_client.get("/email-sending-view/")
                assert len(outbox) == 1
                assert outbox[0].subject == "testing"

        :versionadded: 0.4
        r   rq   r   )r   rs   r   c                   s     | d S r   )r   )r   rs   Zoutboxr    r!   record  s    z*_MailMixin.record_messages.<locals>.recordN)r   Zconnected_to)r   r   r    r   r!   record_messages  s    z_MailMixin.record_messagesrq   r   )rs   r   c              	   C  s"   |   }|| W 5 Q R X dS )zSends a single message instance. If TESTING is True the message will
        not actually be sent.

        :param message: a Message instance.
        N)connectr   )r   rs   r   r    r    r!   r     s    
z_MailMixin.sendr   r   c                 O  s   |  t|| dS r   r   r   r    r    r!   r     s    z_MailMixin.send_messagerT   c              
   C  sR   t | ddpt}zt|jd W S  tk
rL } ztd|W 5 d}~X Y nX dS )z$Opens a connection to the mail host.r   NrV   z:The current application was not configured with Flask-Mail)getattrr   rT   r   KeyErrorRuntimeError)r   r   errr    r    r!   r   (  s    z_MailMixin.connectN)r(   r)   r*   r   r   r   r   r   r    r    r    r!   r      s
   

r   c                   @  s.   e Zd ZddddddddddddddZd	S )
_Mailr"   r   z
int | NonerO   rj   rf   rn   ro   rg   rl   rd   r   rk   r   rZ   r   c                 C  sF   || _ || _|| _|| _|| _|| _|| _|| _|	| _|
| _	|| _
d S r   r   )r   rf   rn   ro   rg   rl   rd   r   rk   r   rZ   r   r    r    r!   r   5  s    z_Mail.__init__N)r(   r)   r*   r   r    r    r    r!   r   4  s   r   c                   @  sX   e Zd ZdZddddddZdd	d
dddddZdddddZdddddZdS )rU   zManages email messaging.NzFlask | Noner   )r   r   c                 C  s&   || _ |d k	r| || _nd | _d S r   )r   init_appstate)r   r   r    r    r!   r   S  s    zMail.__init__Fzdict[str, t.Any]z
bool | intrO   r   )configrk   testingr   c                 C  sp   t |dd|d|d|dd|dd|d	d|d
t|d||d|d||ddS )NZMAIL_SERVERz	127.0.0.1ZMAIL_USERNAMEZMAIL_PASSWORDZ	MAIL_PORT   ZMAIL_USE_TLSFZMAIL_USE_SSLZMAIL_DEFAULT_SENDERZ
MAIL_DEBUGZMAIL_MAX_EMAILSZMAIL_SUPPRESS_SENDZMAIL_ASCII_ATTACHMENTS)r   r   rj   )r   r   rk   r   r    r    r!   	init_mail[  s    





zMail.init_mailr   c                 C  s0   |  |j|j|j}t|di |_||jd< |S )zInitializes your mail settings from the application settings.

        You can use this if you want to set up your Mail instance
        at configuration time.
        r   rV   )r   r   rk   r   r   r   )r   r   r   r    r    r!   r   l  s    
zMail.init_appr"   r   namer   c                 C  s   t | j|d S r   )r   r   )r   r   r    r    r!   __getattr__y  s    zMail.__getattr__)N)FF)r(   r)   r*   r   r   r   r   r   r    r    r    r!   rU   P  s   	   rU   zblinker.Namespacesignalszemail-dispatchedz
Signal sent when an email is dispatched. This signal will also be sent
in testing mode, even though the email will not actually be sent.
)doczblinker.NamedSignalr   r   c                 C  s8   | dkr,dd l }tjdtdd |jdS t| d S )N__version__r   zThe '__version__' attribute is deprecated and will be removed in Flask-Mail 1.0. Use feature detection or 'importlib.metadata.version("flask-mail")' instead.r   r   z
flask-mail)importlib.metadatar   r   r   metadataversionAttributeError)r   	importlibr    r    r!   r     s    r   )r   r,   )r   )r   )r   )B
__future__r   collections.abcabccr   re   r{   typingtr   r   
contextlibr   emailr   r   email.encodersr   Zemail.headerr   Zemail.mime.baser   Zemail.mime.multipartr	   Zemail.mime.textr
   email.utilsr   r   r   r   	mimetypesr   typesr   ZblinkerZflaskr   r   TYPE_CHECKINGZtyping_extensionsteadd_charsetSHORTESTr8   r   r1   rA   rK   rN   rS   rT   r9   ry   r   rq   r   r   rU   	Namespacer   __annotations__signalr   r   r    r    r    r!   <module>   sd    
 [.  4-	