U
    d\                     @  s  d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	m
Z
mZmZmZmZmZmZmZ ddlmZ ddlmZ ddlmZmZmZmZmZ ddlmZmZ dd	lm Z m!Z! dd
l"m#Z# erddl$m%Z% dZ&e'e&Z(dZ)e'e)Z*dZ+dddddZ,dddddZ-ddddddZ.e+fddddddZ/d d!d"hZ0dd#d$d%d&d'Z1d$d$d(d)d*Z2d$d$d(d+d,Z3d$d$d(d-d.Z4dWd0dd1d2d3d4Z5dXddddd1d6d7d8Z6e+fddd9d:d;d<Z7e8d=e9d> d? Z:e;d@dAdBdCdDdEgZ<dFd0dGdHdIdJZ=e+d5d/d5dddfddddddKd#ddLdM	dNdOZ>dPdQdRdSdTZ?e@dUkrddlAZAzeAAe>ejBdV  W n, ek
r ZC zeDeC W 5 dZC[CX Y nX eEd dS )Yz*Tools to parse and validate a MongoDB URI.    )annotationsN)TYPE_CHECKINGAnyDictListMappingMutableMappingOptionalSizedTupleUnioncast)unquote_plus)_parse_ssl_options)INTERNAL_URI_OPTION_NAME_MAPSRV_SERVICE_NAMEURI_OPTIONS_DEPRECATION_MAP_CaseInsensitiveDictionaryget_validated_options)ConfigurationError
InvalidURI)_HAVE_DNSPYTHON_SrvResolver)_Address)
SSLContextz
mongodb://zmongodb+srv://ii  strbool)sreturnc                 C  sD   t t| D ]2}| | dkr| ||d  }t||kr dS qdS )zCheck for unescaped percent signs.

    :Parameters:
        - `s`: A string. `s` can have things like '%25', '%2525',
           and '%E2%85%A8' but cannot have unquoted percent like '%foo'.
    %   TF)rangelenr   )r   isub r%   6/tmp/pip-unpacked-wheel-oblwsawz/pymongo/uri_parser.py_unquoted_percent;   s    r'   zTuple[str, str])userinfor   c                 C  sR   d| ks|  ddkst| r&td| d\}}}|sBtdt|t|fS )al  Validates the format of user information in a MongoDB URI.
    Reserved characters that are gen-delimiters (":", "/", "?", "#", "[",
    "]", "@") as per RFC 3986 must be escaped.

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

    :Parameters:
        - `userinfo`: A string of the form <username>:<password>
    @:   zXUsername and password must be escaped according to RFC 3986, use urllib.parse.quote_plusz'The empty string is not valid username.)countr'   r   	partitionr   )r(   user_passwdr%   r%   r&   parse_userinfoL   s    r1   zOptional[int]z%Tuple[str, Optional[Union[str, int]]])entitydefault_portr   c                 C  sT   |  ddkrtd|  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.
    ]zNan IPv6 address literal must be enclosed in '[' and ']' according to RFC 2732.z]:r+      N)find
ValueError)r2   r3   r#   r%   r%   r&   parse_ipv6_literal_hoste   s    
r9   r   c                 C  s   | }|}| d dkr$t | |\}}nF| dr6| |fS | ddkrj| ddkrZtd|dd\}}t|tr| rt	|dkst	|dkrtd	|t	|}|
 |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   [.sockr*   r5   r+   zReserved 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: )r9   endswithr7   r,   r8   split
isinstancer   isdigitintlower)r2   r3   hostportr%   r%   r&   
parse_host}   s"    

 rD   tlsallowinvalidcertificatesZtlsallowinvalidhostnamestlsdisableocspendpointcheckzOptional[str]r   )optsdelimr   c                 C  s   t  }| |D ]n}|d\}}| dkrB||g | q||kr\td| d | dkrn|}nt|}|||< q|S )zHelper 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 'z'.Zauthmechanismproperties)r   r=   rA   
setdefaultappendwarningswarnr   )rG   rH   optionsZurioptkeyvaluevalr%   r%   r&   _parse_options   s    
rR   )rN   r   c                 C  s*  |  d}|dk	rDtD ],}|| krd}t|| d| |f q|  d}|dk	rd| krxd}t|d| df |dkrd| d< |  d}|dk	rdD ]$}|  |dkrd	}t||f qd
| kr&d| kr&ddddd}||  d
||  dkr&d}t|| d
| df | S )zRaise 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.rE   rF   T
tlscrlfile)rS   rE   rF   zDURI option %s=True cannot be specified when CRL checking is enabled.ssltlsr   )rQ   r   c                 S  s"   | dkr| dkS t | tr| S | S )N)truefalserW   )r>   r   )rQ   r%   r%   r&   truth_value   s
    
z-_handle_security_options.<locals>.truth_valuez=Can not specify conflicting values for URI options %s and %s.)get_IMPLICIT_TLSINSECURE_OPTSr   	cased_key)rN   rS   opterr_msgZtlsallowinvalidcertsrT   rY   r%   r%   r&   _handle_security_options   s>    	


r_   c                 C  s   t | D ]}|tkrt| \}}|dkr|}|| krhd}tj|| || |f tdd | | qd}tj|| ||f tdd q|dkrd}tj|| ||f tdd q| 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'.r6   )
stacklevelz,Option '%s' is deprecated, use '%s' instead.removedzOption '%s' is deprecated. %s.)listr   rL   rM   r\   DeprecationWarningpop)rN   optnamemodemessageZ
newoptnameZwarn_msgr%   r%   r&   _handle_option_deprecations   s8    	
rh   c                 C  sX   |  d}|dk	r$tD ]}|| |< qt| D ]&}t |d}|dk	r,| || |< q,| S )zNormalizes option names in the options dictionary by converting them to
    their internally-used names.

    :Parameters:
        - `options`: Instance of _CaseInsensitiveDictionary containing
          MongoDB URI options.
    rS   N)rZ   r[   rb   r   rd   )rN   rS   r]   re   Zintnamer%   r%   r&   _normalize_options   s    	

ri   FzMapping[str, Any]zMutableMapping[str, Any])rG   rM   r   c                 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   )rG   rM   r%   r%   r&   validate_options7  s    rj   T)rG   validaterM   	normalizer   c                 C  s   |  d}|  d}zd|dkr0|dkr0tdnF|dkrDt| d}n2|dkrXt| d}n|  ddkrrt| d}ntW n tk
r   tdY nX t|}t|}|rt|}|rttt	||}|
d	d
krtd|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.rI   r5   Nz(MongoDB URI options are key=value pairs.
authsource z1the authSource database cannot be an empty string)r7   r   rR   r8   r_   rh   ri   r   r   rj   rZ   )rG   rk   rM   rl   Zand_idxZsemi_idxrN   r%   r%   r&   split_optionsG  s.    


rq   zList[_Address])hostsr3   r   c                 C  sF   g }|  dD ]2}|std|}|dr0d}|t|| q|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).r;   N)r=   r   r<   rK   rD   )rr   r3   nodesr2   rC   r%   r%   r&   split_hostsu  s    
ru   r:   z/ "$r4   ro   Z
authSource
replicaset
replicaSetloadbalancedloadBalancedr
   None)rt   rN   r   c                 C  sd   t | dkr|drtd|dr`t | dkr<td|drNtd|dr`tdd S )	Nr+   Zdirectconnectionz8Cannot specify multiple hosts with directConnection=truerx   z4Cannot specify multiple hosts with loadBalanced=truez;Cannot specify directConnection=true with loadBalanced=truerv   z0Cannot specify replicaSet with loadBalanced=true)r"   rZ   r   )rt   rN   r%   r%   r&   _check_options  s    


r{   zOptional[float]zDict[str, Any])	urir3   rk   rM   rl   connect_timeoutsrv_service_namesrv_max_hostsr   c                 C  sJ  |  trd}| td }	nL|  trRts@tjp2d}
td|
 d}| td }	nt	dt dt d|	stt	d	d}d}d}d}t
 }|	d
\}}}|s|}d}|sd|krt	d|r.|d\}}}|rt|}d|kr|dd\}}t|rt	d| nd}|r.|t|||| |dkrD|dt}d|krl|d\}}}t|\}}n|}d
|krt	d| t|}d}|p|d}|r|drtdt dt|dd}t|dkrt	t d|d \}}|dk	rt	t d|p|d}t||||}| }| }|rt||||}t|t rhtd| D ]\}}||krp|||< qp|dr|rt	d|d r|rt	d!d"|kr,d#|kr,|rdnd$|d"< nB|s
|ddk	r
td%n"|s |r td&nt||d}t|| |||||||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.
        - 'srv_service_name` (optional): A custom SRV service name

    .. versionchanged:: 4.0
       To better follow RFC 3986, unquoted percent signs ("%") are no longer
       supported.

    .. 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.
    FNpythonzThe "dnspython" module must be installed to use mongodb+srv:// URIs. To fix this error install pymongo again:
 %s -m pip install pymongo>=4.3Tz)Invalid URI scheme: URI must begin with 'z' or ''z)Must provide at least one hostname or IP./rp   ?z8A '/' is required between the host list and any options..r+   zBad database name "%s"ZsrvServiceNamer)   z;Any '/' in a unix domain socket must be percent-encoded: %sZsrvMaxHostsZdirectConnectionz*Cannot specify directConnection=true with z URIs)r3   z. URIs must include one, and only one, hostnamer   z$ URIs must not include a port numberZconnectTimeoutMSzDOnly authSource, replicaSet, and loadBalanced are supported from DNSry   z0You cannot specify loadBalanced with srvMaxHostsrw   z.You cannot specify replicaSet with srvMaxHostsrV   rU   rW   zDThe srvServiceName option is only allowed with 'mongodb+srv://' URIszAThe srvMaxHosts option is only allowed with 'mongodb+srv://' URIs)ZnodelistusernamepasswordZdatabase
collectionrN   fqdn)
startswithSCHEME
SCHEME_LEN
SRV_SCHEMEr   sys
executabler   SRV_SCHEME_LENr   r   r-   r   r=   _BAD_DB_CHARSsearchupdaterq   rZ   r   
rpartitionr1   ru   r"   r   Z	get_hostsZget_optionsset_ALLOWED_TXT_OPTSitemsr{   )r|   r3   rk   rM   rl   r}   r~   r   Zis_srvZscheme_freeZpython_pathr.   r0   Zdbaser   rN   Z	host_partr/   Z	path_partrG   r(   rr   r   rt   rC   Zdns_resolverZdns_optionsZparsed_dns_optionsr]   rQ   r%   r%   r&   	parse_uri  s    <








r   zOptional[Mapping[str, Any]]zDict[str, SSLContext])kms_tls_optionsr   c                 C  s   | si S t | tstdi }|  D ]\}}t |tsHtd| d|dd t|}t|}t|}t|}t	|\}}|dkrt
d|rt
dd	D ]"}||krt
d
| |||< qq&|S )z!Parse KMS TLS connection options.zkms_tls_options must be a dictzkms_tls_options["z"] must be a dictrV   TNz!TLS is required for KMS providerszInsecure TLS options prohibited)ZtlsInsecureZtlsAllowInvalidCertificatesZtlsAllowInvalidHostnamesZ$tlsDisableCertificateRevocationCheckz!Insecure TLS options prohibited: )r>   dict	TypeErrorr   rJ   r   r_   ri   rj   r   r   )r   ZcontextsZproviderrG   ssl_contextZallow_invalid_hostnamesnr%   r%   r&   _parse_kms_tls_optionsU  s.    

r   __main__r+   )F)TFT)F__doc__
__future__r   rer   rL   typingr   r   r   r   r   r   r	   r
   r   r   r   urllib.parser   Zpymongo.client_optionsr   Zpymongo.commonr   r   r   r   r   Zpymongo.errorsr   r   Zpymongo.srv_resolverr   r   Zpymongo.typingsr   Zpymongo.pyopenssl_contextr   r   r"   r   r   r   ZDEFAULT_PORTr'   r1   r9   rD   r[   rR   r_   rh   ri   rj   rq   ru   compileescaper   	frozensetr   r{   r   r   __name__pprintargvexcprintexitr%   r%   r%   r&   <module>   st   4)6(     .  1!
