3
]ð]ªF  ã               @   s¶  d Z ddlZddlZddlmZmZ er6ddlmZ nddlmZ ddl	m
Z
mZmZmZ ddlmZmZ ddlmZmZ dZeeƒZd	ZeeƒZd
Zdd„ Zdd„ Zefdd„ZddhZedd„ eD ƒB Zdd„ Zdd„ Z dd„ Z!dd„ Z"d0dd„Z#d1d!d"„Z$efd#d$„Z%ej&d%ej'd&ƒ d' ƒZ(e)d(d)d*d+gƒZ*ed dd dfd,d-„Z+e,d.k�r²ddl-Z-ddl.Z.ye-j-e+e.j/d/ ƒƒ W n, ek
�r¦ Z0 ze1e0ƒ W Y ddZ0[0X nX e.j2dƒ dS )2z*Tools to parse and validate a MongoDB URI.é    N)Ústring_typeÚPY3)Úunquote_plus)Úget_validated_optionsÚINTERNAL_URI_OPTION_NAME_MAPÚURI_OPTIONS_DEPRECATION_MAPÚ_CaseInsensitiveDictionary)ÚConfigurationErrorÚ
InvalidURI)Ú_HAVE_DNSPYTHONÚ_SrvResolverz
mongodb://zmongodb+srv://i‰i  c             C   s\   d| ks| j dƒdkr0tr d}nd}td| ƒ‚| jdƒ\}}}|sLtdƒ‚t|ƒt|ƒfS )a´  Validates the format of user information in a MongoDB URI.
    Reserved characters like ':', '/', '+' and '@' must be escaped
    following RFC 3986.

    Returns a 2-tuple containing the unescaped username followed
    by the unescaped password.

    :Paramaters:
        - `userinfo`: A string of the form <username>:<password>

    .. versionchanged:: 2.2
       Now uses `urllib.unquote_plus` so `+` characters must be escaped.
    ú@ú:é   zurllib.parse.quote_pluszurllib.quote_pluszFUsername and password must be escaped according to RFC 3986, use %s().z'The empty string is not valid username.)Úcountr   r
   Ú	partitionr   )ÚuserinfoZquote_fnÚuserÚ_Úpasswd© r   ú5/tmp/pip-build-20mum3z4/pymongo/pymongo/uri_parser.pyÚparse_userinfo)   s    r   c             C   sT   | j dƒdkrtdƒ‚| j dƒ}|dkr8| dd	… |fS | d|… | |d d… fS )
a°  Validates an IPv6 literal host:port string.

    Returns a 2-tuple of IPv6 literal followed by port where
    port is default_port if it wasn't specified in entity.

    :Parameters:
        - `entity`: A string that represents an IPv6 literal enclosed
                    in braces (e.g. '[::1]' or '[::1]:27017').
        - `default_port`: The port number to use when one wasn't
                          specified in entity.
    ú]r   zNan IPv6 address literal must be enclosed in '[' and ']' according to RFC 2732.z]:é   Néÿÿÿÿr   r   )ÚfindÚ
ValueError)ÚentityÚdefault_portÚir   r   r   Úparse_ipv6_literal_hostE   s    
r!   c             C   s¸   | }|}| d dkr$t | |ƒ\}}nF| jdƒr6| |fS | jdƒd	krj| jdƒdkrZtdƒ‚|jddƒ\}}t|tƒr¬|jƒ  s–t	|ƒdks–t	|ƒdkr¤td|f ƒ‚t	|ƒ}|j
ƒ |fS )
a„  Validates a host string

    Returns a 2-tuple of host followed by port where port is default_port
    if it wasn't specified in the string.

    :Parameters:
        - `entity`: A host or host:port string where host could be a
                    hostname or IP address.
        - `default_port`: The port number to use when one wasn't
                          specified in entity.
    r   ú[z.sockr   r   z’Reserved characters such as ':' must be escaped according RFC 2396. An IPv6 address literal must be enclosed in '[' and ']' according to RFC 2732.iÿÿ  z/Port must be an integer between 0 and 65535: %sr   )r!   Úendswithr   r   r   ÚsplitÚ
isinstancer   ÚisdigitÚintÚlower)r   r   ÚhostÚportr   r   r   Ú
parse_host[   s     

"
r+   ZtlsallowinvalidcertificatesZtlsallowinvalidhostnamesc             C   s   h | ]}t | ’qS r   )r   )Ú.0Úkr   r   r   ú	<setcomp>…   s    r.   c             C   sp   t ƒ }xd| j|ƒD ]V}|jdƒ\}}|jƒ dkrD|j|g ƒj|ƒ q||kr\tjd|f ƒ t|ƒ||< qW |S )zÆHelper method for split_options which creates the options dict.
    Also handles the creation of a list for the URI tag_sets/
    readpreferencetags portion, and the use of a unicode options string.ú=ZreadpreferencetagszDuplicate URI option '%s'.)r   r$   r(   Ú
setdefaultÚappendÚwarningsÚwarnr   )ÚoptsÚdelimÚoptionsZurioptÚkeyÚvaluer   r   r   Ú_parse_options‰   s    r9   c             C   s    | j dƒ}|dk	rHx4tD ],}|| krd}t|| jdƒ| j|ƒf ƒ‚qW d| krœd| krœdd„ }|| j dƒƒ|| j dƒƒkrœd}t|| jdƒ| jdƒf ƒ‚| S )	zÝRaise appropriate errors when conflicting TLS options are present in
    the options dictionary.

    :Parameters:
        - `options`: Instance of _CaseInsensitiveDictionary containing
          MongoDB URI options.
    ÚtlsinsecureNz9URI options %s and %s cannot be specified simultaneously.ÚsslZtlsc             S   s"   | dkr| dkS t | tƒr| S | S )NÚtrueÚfalse)r<   r=   )r%   Úbool)Úvalr   r   r   Útruth_value¬   s
    
z-_handle_security_options.<locals>.truth_valuez=Can not specify conflicting values for URI options %s and %s.)ÚgetÚ_TLSINSECURE_EXCLUDE_OPTSr
   Ú	cased_key)r6   r:   ÚoptÚerr_msgr@   r   r   r   Ú_handle_security_optionsš   s    

rF   c             C   sÀ   xºt | ƒD ]®}|tkr
t| \}}|dkrŽ|}|| krjd}tj|| j|ƒ| j|ƒf tdd� | j|ƒ q
d}tj|| j|ƒ|f tdd� q
|dkr
d}tj|| j|ƒ|f tdd� q
W | S )aM  Issue appropriate warnings when deprecated options are present in the
    options dictionary. Removes deprecated option key, value pairs if the
    options dictionary is found to also have the renamed option.

    :Parameters:
        - `options`: Instance of _CaseInsensitiveDictionary containing
          MongoDB URI options.
    Zrenamedz0Deprecated option '%s' ignored in favor of '%s'.r   )Ú
stacklevelz,Option '%s' is deprecated, use '%s' instead.ÚremovedzOption '%s' is deprecated. %s.)Úlistr   r2   r3   rC   ÚDeprecationWarningÚpop)r6   ÚoptnameÚmodeÚmessageZ
newoptnameZwarn_msgr   r   r   Ú_handle_option_deprecations»   s.    	


rO   c             C   sn   | j dƒ}|dk	r6x"tD ]}tj |dƒ}| | |< qW x2t| ƒD ]&}tj |dƒ}|dk	r@| j|ƒ| |< q@W | S )a  Normalizes option names in the options dictionary by converting them to
    their internally-used names. Also handles use of the tlsInsecure option.

    :Parameters:
        - `options`: Instance of _CaseInsensitiveDictionary containing
          MongoDB URI options.
    r:   N)rA   Ú_IMPLICIT_TLSINSECURE_OPTSr   rI   rK   )r6   r:   rD   ZintnamerL   r   r   r   Ú_normalize_optionsß   s    

rQ   Fc             C   s
   t | |ƒS )aö  Validates and normalizes options passed in a MongoDB URI.

    Returns a new dictionary of validated and normalized options. If warn is
    False then errors will be thrown for invalid options, otherwise they will
    be ignored and a warning will be issued.

    :Parameters:
        - `opts`: A dict of MongoDB URI options.
        - `warn` (optional): If ``True`` then warnings will be logged and
          invalid options will be ignored. Otherwise invalid options will
          cause errors.
    )r   )r4   r3   r   r   r   Úvalidate_optionsö   s    rR   Tc             C   sÄ   | j dƒ}| j dƒ}yd|dkr0|dkr0tdƒ‚nF|dkrDt| dƒ}n2|dkrXt| dƒ}n| j dƒd	krrt| dƒ}nt‚W n tk
r”   tdƒ‚Y nX t|ƒ}t|ƒ}|r´t||ƒ}|rÀt|ƒ}|S )
a  Takes the options portion of a MongoDB URI, validates each option
    and returns the options in a dictionary.

    :Parameters:
        - `opt`: A string representing MongoDB URI options.
        - `validate`: If ``True`` (the default), validate and normalize all
          options.
        - `warn`: If ``False`` (the default), suppress all warnings raised
          during validation of options.
        - `normalize`: If ``True`` (the default), renames all options to their
          internally-used names.
    ú&ú;r   z.Can not mix '&' and ';' for option separators.r/   r   Nz(MongoDB URI options are key=value pairs.r   )r   r
   r9   r   rF   rO   rR   rQ   )r4   Úvalidater3   Ú	normalizeZand_idxZsemi_idxr6   r   r   r   Úsplit_options  s*    



rW   c             C   sJ   g }x@| j dƒD ]2}|s tdƒ‚|}|jdƒr2d}|jt||ƒƒ qW |S )aµ  Takes a string of the form host1[:port],host2[:port]... and
    splits it into (host, port) tuples. If [:port] isn't present the
    default_port is used.

    Returns a set of 2-tuples containing the host name (or IP) followed by
    port number.

    :Parameters:
        - `hosts`: A string of the form host1[:port],host2[:port],...
        - `default_port`: The port number to use when one wasn't specified
          for a host.
    ú,z)Empty host (or extra comma in host list).z.sockN)r$   r	   r#   r1   r+   )Úhostsr   Únodesr   r*   r   r   r   Úsplit_hosts0  s    
r[   r"   z/ "$r   Z
authsourceZ
authSourceZ
replicasetZ
replicaSetc             C   s°  | j tƒrd}| td… }n8| j tƒrDts2tdƒ‚d}| td… }ntdttf ƒ‚|s`tdƒ‚d}d}	d}
d}tƒ }|j	dƒ\}}}|s’|}d}| r¨d	|kr¨td
ƒ‚|�r.|d d	krÌt
|dd… ƒ}nHtt
|j	d	ƒƒ\}
}}d|
k� rü|
jddƒ\}
}tj|
ƒ�rtd|
 ƒ‚|�r.|jt||||ƒƒ |
dk	�r@t
|
ƒ}
|dk	�rRt
|ƒ}d|k�rz|jdƒ\}}}t|ƒ\}}	n|}d|k�r”td| ƒ‚t
|ƒ}d}|�r�t|dd�}t|ƒdk�rÎtdtf ƒ‚|d \}}|dk	�ròtdtf ƒ‚|�p |jdƒ}t||d�}|jƒ }|jƒ }|�rrt||||ƒ}t|ƒt �rHtdƒ‚x(|jƒ D ]\}}||k�rR|||< �qRW d|k�rœ|�r†dnd|d< nt||d�}|||	|
|||dœS )aø  Parse and validate a MongoDB URI.

    Returns a dict of the form::

        {
            'nodelist': <list of (host, port) tuples>,
            'username': <username> or None,
            'password': <password> or None,
            'database': <database name> or None,
            'collection': <collection name> or None,
            'options': <dict of MongoDB URI options>,
            'fqdn': <fqdn of the MongoDB+SRV URI> or None
        }

    If the URI scheme is "mongodb+srv://" DNS SRV and TXT lookups will be done
    to build nodelist and options.

    :Parameters:
        - `uri`: The MongoDB URI to parse.
        - `default_port`: The port number to use when one wasn't specified
          for a host in the URI.
        - `validate` (optional): If ``True`` (the default), validate and
          normalize all options. Default: ``True``.
        - `warn` (optional): When validating, if ``True`` then will warn
          the user then ignore any invalid options or values. If ``False``,
          validation will error when options are unsupported or values are
          invalid. Default: ``False``.
        - `normalize` (optional): If ``True``, convert names of URI options
          to their internally-used names. Default: ``True``.
        - `connect_timeout` (optional): The maximum time in milliseconds to
          wait for a response from the DNS server.

    .. versionchanged:: 3.9
        Added the ``normalize`` parameter.

    .. versionchanged:: 3.6
        Added support for mongodb+srv:// URIs.

    .. versionchanged:: 3.5
        Return the original value of the ``readPreference`` MongoDB URI option
        instead of the validated read preference mode.

    .. versionchanged:: 3.1
        ``warn`` added so invalid options can be ignored.
    FNzCThe "dnspython" module must be installed to use mongodb+srv:// URIsTz4Invalid URI scheme: URI must begin with '%s' or '%s'z)Must provide at least one hostname or IP.ú/Ú ú?z8A '/' is required between the host list and any options.r   r   Ú.zBad database name "%s"r   z;Any '/' in a unix domain socket must be percent-encoded: %s)r   z0%s URIs must include one, and only one, hostnamez&%s URIs must not include a port numberZconnectTimeoutMS)Úconnect_timeoutz5Only authSource and replicaSet are supported from DNSr;   r<   )ZnodelistÚusernameÚpasswordZdatabaseÚ
collectionr6   Úfqdn)Ú
startswithÚSCHEMEÚ
SCHEME_LENÚ
SRV_SCHEMEr   r	   ÚSRV_SCHEME_LENr
   r   r   r   Úmapr$   Ú_BAD_DB_CHARSÚsearchÚupdaterW   Ú
rpartitionr   r[   ÚlenrA   r   Z	get_hostsZget_optionsÚsetÚ_ALLOWED_TXT_OPTSÚitems)Úurir   rU   r3   rV   r`   Zis_srvZscheme_freer   r   Zdbaserc   r6   Z	host_partr   Z	path_partr4   r   rY   rd   rZ   r*   Zdns_resolverZdns_optionsZparsed_dns_optionsrD   r?   r   r   r   Ú	parse_uriR  sž    /










rt   Ú__main__r   )F)TFT)3Ú__doc__Úrer2   Zbson.py3compatr   r   Úurllib.parser   ÚurllibZpymongo.commonr   r   r   r   Zpymongo.errorsr	   r
   Zpymongo.srv_resolverr   r   rf   ro   rg   rh   ri   ZDEFAULT_PORTr   r!   r+   rP   rB   r9   rF   rO   rQ   rR   rW   r[   ÚcompileÚescaperk   Ú	frozensetrq   rt   Ú__name__ÚpprintÚsysÚargvÚexcÚprintÚexitr   r   r   r   Ú<module>   sV   &
!$

* 
