o
    ›¨Êh•P  ã                   @   s  d dl Z d dlZd dl mZ d dlmZmZ d dlmZ d dlmZ d dl	m
Z
mZmZ d dlmZ d dlZd dlmZ d d	lmZmZ d d
lmZ d dlmZ d dlmZmZ d dlmZmZ ddlm Z m!Z! ej"e#f Z$e%ƒ Z&dd„ Z'dd„ Z(e&fdd„Z)dd„ Z*dCdd„Z+dCdd„Z,G dd„ de%ƒZ-G dd„ de%ƒZ.d d!„ Z/e&ddd"fd#d$„Z0d%d&„ Z1e&fd'd(„Z2ed)dded*�Z3d+d,„ Z4d-d.„ Z5d/d0„ Z6d1d2„ Z7d3d4„ Z8d5d6„ Z9d7d8„ Z:d9d:„ Z;d;d<„ Z<zd d=l	m=Z= W n e>yû   d d>l	m?Z? d?d@„ Z=Y nw dAdB„ Z@dS )Dé    N)Ú	b64encode)ÚMappingÚMutableMapping)Údatetime)ÚMIMEBase)Ú
formatdateÚgetaddressesÚunquote)Úmktime)Úsettings)Úsanitize_addressÚDEFAULT_ATTACHMENT_MIME_TYPE©Ú
force_text)ÚPromise)ÚutcÚget_fixed_timezone)ÚurlsplitÚ
urlunsplité   )ÚAnymailConfigurationErrorÚAnymailInvalidAddressc               	   G   s~   t }| D ]8}|du rt }q|t ur<|t u r'z| ¡ }W q ty&   |}Y qw z| |¡ W q ty;   || }Y qw q|S )aq  
    Combines all non-UNSET args, by shallow merging mappings and concatenating sequences

    >>> combine({'a': 1, 'b': 2}, UNSET, {'b': 3, 'c': 4}, UNSET)
    {'a': 1, 'b': 3, 'c': 4}
    >>> combine([1, 2], UNSET, [3, 4], UNSET)
    [1, 2, 3, 4]
    >>> combine({'a': 1}, None, {'b': 2})  # None suppresses earlier args
    {'b': 2}
    >>> combine()
    UNSET

    N)ÚUNSETÚcopyÚAttributeErrorÚupdate)ÚargsÚresultÚvalue© r   ú?/var/www/html/env/lib/python3.10/site-packages/anymail/utils.pyÚcombine   s$   ÿÿ÷r!   c                  G   s2   t | ƒD ]}|du rt  S |tur|  S qtS )a  Returns the last of its args which is not UNSET.

    (Essentially `combine` without the merge behavior)

    >>> last(1, 2, UNSET, 3, UNSET, UNSET)
    3
    >>> last(1, 2, None, UNSET)  # None suppresses earlier args
    UNSET
    >>> last()
    UNSET

    N)Úreversedr   )r   r   r   r   r    Úlast<   s   ÿr#   c              	   C   sH   |D ]}z| | W   S  t y   Y qw |tu r"t dd |¡ ƒ‚|S )a  Returns the value of the first of keys found in dict dct.

    >>> getfirst({'a': 1, 'b': 2}, ['c', 'a'])
    1
    >>> getfirst({'a': 1, 'b': 2}, ['b', 'a'])
    2
    >>> getfirst({'a': 1, 'b': 2}, ['c'])
    KeyError
    >>> getfirst({'a': 1, 'b': 2}, ['c'], None)
    None
    zNone of %s found in dictú, )ÚKeyErrorr   Újoin)ÚdctÚkeysÚdefaultÚkeyr   r   r    ÚgetfirstR   s   ÿr+   c                 C   sN   |  ¡ D ] \}}|| v r t| | tƒr t|tƒr t| | |ƒ q|| |< qdS )z»Merge (recursively) keys and values from dict other into dict dct

    Works with dict-like objects: dct (and descendants) can be any MutableMapping,
    and other can be any Mapping
    N)ÚitemsÚ
isinstancer   r   Úupdate_deep)r'   Úotherr*   r   r   r   r    r.   i   s
    
ür.   c                 C   sÒ   t | tjƒs
t| ƒr| g} | du s| dgkrg S dd„ | D ƒ}t|ƒ}|g kr/|dgkr/dg}dd„ |D ƒ}|D ].}|jdksD|jdkrfdj|jd 	|¡|rRd	| ndd
�}t
|ƒt
| ƒkrb|d7 }t|ƒ‚q8|S )ay  Returns a list of EmailAddress objects from strings in address_list.

    Essentially wraps :func:`email.utils.getaddresses` with better error
    messaging and more-useful output objects

    Note that the returned list might be longer than the address_list param,
    if any individual string contains multiple comma-separated addresses.

    :param list[str]|str|None|list[None] address_list:
        the address or addresses to parse
    :param str|None field:
        optional description of the source of these addresses, for error message
    :return list[:class:`EmailAddress`]:
    :raises :exc:`AnymailInvalidAddress`:
    Nc                 S   ó   g | ]}t |ƒ‘qS r   r   )Ú.0Úaddressr   r   r    Ú
<listcomp>‘   ó    z&parse_address_list.<locals>.<listcomp>Ú )r5   r5   c                 S   s   g | ]
\}}t ||d �‘qS ))Údisplay_nameÚ	addr_spec)ÚEmailAddress)r1   ÚnameÚemailr   r   r    r3   •   s    ÿz@Invalid email address '{problem}' parsed from '{source}'{where}.r$   ú in `%s`)ÚproblemÚsourceÚwherez. (Maybe missing quotes around a display-name?))r-   ÚsixÚstring_typesÚis_lazyr   ÚusernameÚdomainÚformatr7   r&   Úlenr   )Úaddress_listÚfieldÚaddress_list_stringsÚname_email_pairsÚparsedr2   Úerrmsgr   r   r    Úparse_address_listw   s.   ÿý÷rL   c                 C   sL   t | g|d�}t|ƒ}|dkr"tdj|| |rd| d�ƒ‚dd�ƒ‚|d S )aŽ  Parses a single EmailAddress from str address, or raises AnymailInvalidAddress

    :param str address: the fully-formatted email str to parse
    :param str|None field: optional description of the source of this address, for error message
    :return :class:`EmailAddress`: if address contains a single email
    :raises :exc:`AnymailInvalidAddress`: if address contains no or multiple emails
    )rG   r   zGOnly one email address is allowed; found {count} in '{address}'{where}.r;   r5   )Úcountr2   r>   r   )rL   rE   r   rD   )r2   rG   rJ   rM   r   r   r    Úparse_single_address¨   s   ÿÿÿÿrN   c                   @   s8   e Zd ZdZddd„Zedd„ ƒZddd	„Zd
d„ ZdS )r8   ac  A sanitized, complete email address with easy access
    to display-name, addr-spec (email), etc.

    Similar to Python 3.6+ email.headerregistry.Address

    Instance properties, all read-only:
    :ivar str display_name:
        the address's display-name portion (unqouted, unescaped),
        e.g., 'Display Name, Inc.'
    :ivar str addr_spec:
        the address's addr-spec portion (unquoted, unescaped),
        e.g., 'user@example.com'
    :ivar str username:
        the local part (before the '@') of the addr-spec,
        e.g., 'user'
    :ivar str domain:
        the domain part (after the '@') of the addr-spec,
        e.g., 'example.com'

    :ivar str address:
        the fully-formatted address, with any necessary quoting and escaping,
        e.g., '"Display Name, Inc." <user@example.com>'
        (also available as `str(EmailAddress)`)
    r5   Nc                 C   sv   d | _ |d u rz|\}}W n	 ty   Y nw || _|| _z| dd¡\| _| _W d S  ty:   || _d| _Y d S w )Nú@r   r5   )Ú_addressÚ
ValueErrorr6   r7   ÚsplitrB   rC   )Úselfr6   r7   r   r   r    Ú__init__Ô   s   ÿþzEmailAddress.__init__c                 C   s   | j d u r
|  ¡ | _ | j S ©N)rP   Ú
formataddr©rS   r   r   r    r2   ä   s   

zEmailAddress.addressc                 C   s   t | j| jf|ƒS )ap  Return a fully-formatted email address, using encoding.

        This is essentially the same as :func:`email.utils.formataddr`
        on the EmailAddress's name and email properties, but uses
        Django's :func:`~django.core.mail.message.sanitize_address`
        for improved PY2/3 compatibility, consistent handling of
        encoding (a.k.a. charset), and proper handling of IDN
        domain portions.

        :param str|None encoding:
            the charset to use for the display-name portion;
            default None uses ascii if possible, else 'utf-8'
            (quoted-printable utf-8/base64)
        )r   r6   r7   )rS   Úencodingr   r   r    rV   í   s   zEmailAddress.formataddrc                 C   s   | j S rU   )r2   rW   r   r   r    Ú__str__þ   s   zEmailAddress.__str__)r5   NrU   )	Ú__name__Ú
__module__Ú__qualname__Ú__doc__rT   Úpropertyr2   rV   rY   r   r   r   r    r8   º   s    


r8   c                   @   s$   e Zd ZdZdd„ Zedd„ ƒZdS )Ú
Attachmenta¹  A normalized EmailMessage.attachments item with additional functionality

    Normalized to have these properties:
    name: attachment filename; may be None
    content: bytestream
    mimetype: the content type; guessed if not explicit
    inline: bool, True if attachment has a Content-ID header
    content_id: for inline, the Content-ID (*with* <>); may be None
    cid: for inline, the Content-ID *without* <>; may be empty string
    c                 C   s  || _ || _d| _d | _d| _t|tƒrX| ¡ | _|j	dd�| _
| j
d u r9t|dƒr0| ¡ | _
n	| ¡  | j¡| _
| ¡ | _t|ƒdkrWd| _|d | _| jd urWt| jƒ| _n|\| _| _
| _t| jƒ| _t| j
ƒ| _
| jd u r| jd urt | j¡\| _}| jd u r‰t| _d S d S )NFr5   T)ÚdecodeÚas_bytesÚinlinez
Content-ID)Ú_attachmentrX   rb   Ú
content_idÚcidr-   r   Úget_filenamer9   Úget_payloadÚcontentÚhasattrra   Ú	as_stringÚencodeÚget_content_typeÚmimetypeÚget_content_dispositionr	   Úforce_non_lazyÚ	mimetypesÚ
guess_typer   )rS   Ú
attachmentrX   Ú_r   r   r    rT     s6   






€

ÿzAttachment.__init__c                 C   s,   | j }t|tjƒr| | j¡}t|ƒ d¡S )z(Content encoded as a base64 ascii stringÚascii)rh   r-   r?   Ú	text_typerk   rX   r   r`   )rS   rh   r   r   r    Ú
b64content4  s   zAttachment.b64contentN)rZ   r[   r\   r]   rT   r^   rv   r   r   r   r    r_     s
    &r_   c                 C   s0   |   d¡}|du rdS t|ƒ d¡d  ¡  ¡ S )z’Return the message's content-disposition if it exists, or None.

    Backport of py3.5 :func:`~email.message.Message.get_content_disposition`
    zcontent-dispositionNú;r   )ÚgetÚstrÚ	partitionÚstripÚlower)Úmimeobjr   r   r   r    rn   =  s   
rn   Fc           	      C   s&  z|  | ¡}| dv r|dur|W S n|W S W n ttfy!   Y nw |dur5d | ¡  dd¡|  ¡ ¡}n|  ¡ }d| }ztj| W S  ttfy’   ztt|ƒW  Y S  ty‘   |rqz
tt|ƒW  Y  Y S  typ   Y nw |t	u r‹d||f }|rƒ|d| 7 }|d	7 }t
|ƒ‚| Y  Y S w w )
a¶  Returns an Anymail option from kwargs or Django settings.

    Returns first of:
    - kwargs[name] -- e.g., kwargs['api_key'] -- and name key will be popped from kwargs
    - settings.ANYMAIL['<ESP_NAME>_<NAME>'] -- e.g., settings.ANYMAIL['MAILGUN_API_KEY']
    - settings.ANYMAIL_<ESP_NAME>_<NAME> -- e.g., settings.ANYMAIL_MAILGUN_API_KEY
    - settings.<ESP_NAME>_<NAME> (only if allow_bare) -- e.g., settings.MAILGUN_API_KEY
    - default if provided; else raises AnymailConfigurationError

    If allow_bare, allows settings.<ESP_NAME>_<NAME> without the ANYMAIL_ prefix:
    ANYMAIL = { "MAILGUN_API_KEY": "xyz", ... }
    ANYMAIL_MAILGUN_API_KEY = "xyz"
    MAILGUN_API_KEY = "xyz"
    )rB   ÚpasswordNz{}_{}ú rs   z
ANYMAIL_%sz(You must set %s or ANYMAIL = {'%s': ...}z or %sz in your Django settings)Úpopr   r%   rD   ÚupperÚreplacer   ÚANYMAILÚgetattrr   r   )	r9   r)   Úesp_nameÚkwargsÚ
allow_barer   ÚsettingÚanymail_settingÚmessager   r   r    Úget_anymail_settingI  sH   
ÿýÿÿóýr‹   c              	   C   sH   g }| j D ]}zt||ƒ}W n	 ty   Y qw ||vr!| |¡ q|S )aI  Return list of all `method_name` methods for cls and its superclass chain.

    List is in MRO order, with no duplicates. Methods are unbound.

    (This is used to simplify mixins and subclasses that contribute to a method set,
    without requiring superclass chaining, and without requiring cooperating
    superclasses.)
    )Ú__mro__r„   r   Úappend)ÚclsÚmethod_nameÚmethodsÚancestorÚ	validatorr   r   r    Úcollect_all_methods‚  s   	
ÿ
€r“   c                 C   s2   |   |¡}t|ƒdkr|d S |tur|S | | S )a­  Like :func:`django.http.QueryDict.get`, but returns *first* value of multi-valued field.

    >>> from django.http import QueryDict
    >>> q = QueryDict('a=1&a=2&a=3')
    >>> querydict_getfirst(q, 'a')
    '1'
    >>> q.get('a')
    '3'
    >>> q['a']
    '3'

    You can bind this to a QueryDict instance using the "descriptor protocol":
    >>> q.getfirst = querydict_getfirst.__get__(q)
    >>> q.getfirst('a')
    '1'
    r   )ÚgetlistrE   r   )ÚqdictrG   r)   Úvaluesr   r   r    Úquerydict_getfirst—  s   
r—   i²  )Útzinfoc                 C   sD   z|   ¡ W S  ty!   | jdu rt|  ¡ ƒ Y S | t  ¡  Y S w )zBReturn the unix timestamp (seconds past the epoch) for datetime dtN)Ú	timestampr   r˜   r
   Ú	timetupleÚEPOCHÚtotal_seconds)Údtr   r   r    r™   ¶  s   

ür™   c                 C   s   t | ƒ}t|dd�S )z<Turn a datetime into a date string as specified in RFC 2822.T)Úusegmt)r™   r   )r�   Útimevalr   r   r    Úrfc2822dateÂ  s   r    c                 C   s8   t | ƒdkr| d dkrd|  } | d dkr| d } | S )z>Return s surrounded by angle brackets, added only if necessaryr   ú<éÿÿÿÿú>)rE   ©Úsr   r   r    Ú
angle_wrapÊ  s   r¦   c                 C   s
   t | tƒS )z+Return True if obj is a Django lazy object.)r-   r   ©Úobjr   r   r    rA   Ö  s   
rA   c                 C   s   t | ƒr	t | ¡S | S )z¶If obj is a Django lazy object, return it coerced to text; otherwise return it unchanged.

    (Similar to django.utils.encoding.force_text, but doesn't alter non-text objects.)
    )rA   r?   ru   r§   r   r   r    ro   Ý  s   
ro   c              	   C   s0   zdd„ | D ƒW S  t tfy   t| ƒ Y S w )zIReturn a (shallow) copy of sequence obj, with all values forced non-lazy.c                 S   r0   r   )ro   )r1   Úitemr   r   r    r3   ë  r4   z'force_non_lazy_list.<locals>.<listcomp>)r   Ú	TypeErrorro   r§   r   r   r    Úforce_non_lazy_listè  s
   ÿr«   c              	   C   s4   z
dd„ |   ¡ D ƒW S  ttfy   t| ƒ Y S w )zBReturn a (deep) copy of dict obj, with all values forced non-lazy.c                 S   s   i | ]	\}}|t |ƒ“qS r   )Úforce_non_lazy_dict)r1   r*   r   r   r   r    Ú
<dictcomp>ó  s    z'force_non_lazy_dict.<locals>.<dictcomp>)r,   r   rª   ro   r§   r   r   r    r¬   ð  s
   ÿr¬   c                 C   sT   z| j d  ¡ \}}| ¡ dkrt |¡ d¡W S W dS  tttt	fy)   Y dS w )zŠReturns HTTP basic auth string sent with request, or None.

    If request includes basic auth, result is string 'username:password'.
    ÚHTTP_AUTHORIZATIONÚbasiczutf-8N)
ÚMETArR   r|   Úbase64Ú	b64decoder`   Ú
IndexErrorr%   rª   rQ   )ÚrequestÚauthtypeÚauthdatar   r   r    Úget_request_basic_authø  s   ÿþþr·   c                 C   sH   |   ¡ }t| ƒ}|dur"t|ƒ}t|j|d |j |j|j|jfƒ}|S )zªReturns the "exact" url used to call request.

    Like :func:`django.http.request.HTTPRequest.build_absolute_uri`,
    but also inlines HTTP basic auth, if present.
    NrO   )	Úbuild_absolute_urir·   r   r   ÚschemeÚnetlocÚpathÚqueryÚfragment)r´   ÚurlÚ
basic_authÚpartsr   r   r    Úget_request_uri  s   ÿrÁ   )Úparsedate_to_datetime)Úparsedate_tzc                 C   sZ   t | ƒ}|d }|d u s|dkrd| v rt|d d… Ž S t|d ƒ}t|d d… d|iŽS )Nr¢   r   z-0000é   é<   r˜   )rÃ   r   r   )r¥   ÚdtupleÚtzr˜   r   r   r    rÂ     s   rÂ   c              
   C   s&   zt | ƒW S  tttfy   Y dS w )aN  Parses an RFC-2822 formatted date string into a datetime.datetime

    Returns None if string isn't parseable. Returned datetime will be naive
    if string doesn't include known timezone offset; aware if it does.

    (Same as Python 3 email.utils.parsedate_to_datetime, with improved
    handling for unparseable date strings.)
    N)rÂ   r³   rª   rQ   r¤   r   r   r    Úparse_rfc2822date+  s
   	
þrÈ   rU   )Ar±   rp   r   Úcollectionsr   r   r   Úemail.mime.baser   Úemail.utilsr   r   r	   Útimer
   r?   Údjango.confr   Údjango.core.mail.messager   r   Údjango.utils.encodingr   Údjango.utils.functionalr   Údjango.utils.timezoner   r   Úsix.moves.urllib.parser   r   Ú
exceptionsr   r   Úinteger_typesÚfloatÚBASIC_NUMERIC_TYPESÚobjectr   r!   r#   r+   r.   rL   rN   r8   r_   rn   r‹   r“   r—   r›   r™   r    r¦   rA   ro   r«   r¬   r·   rÁ   rÂ   ÚImportErrorrÃ   rÈ   r   r   r   r    Ú<module>   s^    !

1H;9ü