U
    ~fh^                     @  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 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Z-dd#d$d%d&d'Z.d$d$d(d)d*Z/d$d$d(d+d,Z0d$d$d(d-d.Z1dWd0dd1d2d3d4Z2dXddddd1d6d7d8Z3e(fddd9d:d;d<Z4e5d=e6d> d? Z7e8d@dAdBdCdDdEgZ9dFd0dGdH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l>Z>ze>>e;ej?dV  W n, ek
r Z@ zeAe@ W 5 dZ@[@X Y nX eBd dS )YzTools to parse and validate a MongoDB URI.

.. seealso:: This module is compatible with both the synchronous and asynchronous PyMongo APIs.
    )annotationsN)TYPE_CHECKINGAnyMappingMutableMappingOptionalSized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.

    :param 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-36gvocj8/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 )aZ  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.

    :param 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_userinfoK   s    
r.   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.

    :param entity: A string that represents an IPv6 literal enclosed
                    in braces (e.g. '[::1]' or '[::1]:27017').
    :param 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)r/   r0   r    r"   r"   r#   parse_ipv6_literal_hostc   s    
r6   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| st	dd	 |D r|D ]}|
 rtd
|qtdt|dkst|dkrtdt|}| |fS )aq  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.

    :param entity: A host or host:port string where host could be a
                    hostname or IP address.
    :param default_port: The port number to use when one wasn't
                          specified in entity.
    r   [.sockr'   r2   r(   zReserved characters such as ':' must be escaped according RFC 2396. An IPv6 address literal must be enclosed in '[' and ']' according to RFC 2732.c                 s  s   | ]}|  p| V  qd S )N)isspaceisdigit).0cr"   r"   r#   	<genexpr>   s     zparse_host.<locals>.<genexpr>z$Port contains whitespace character: zPort contains non-digit characters. Hint: username and password must be escaped according to RFC 3986, use urllib.parse.quote_plusi  z+Port must be an integer between 0 and 65535)r6   endswithr4   r)   r5   split
isinstancer   r:   allr9   intlower)r/   r0   hostportr<   r"   r"   r#   
parse_hostz   s2    

rF   tlsallowinvalidcertificatesZtlsallowinvalidhostnamestlsdisableocspendpointcheckzOptional[str]r   )optsdelimr   c                 C  s   t  }| |D ]r}|d\}}| dkrB||g | q||kr`tjd| ddd | dkrr|}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'.r3   
stacklevelZauthmechanismproperties)r   r?   rC   
setdefaultappendwarningswarnr   )rI   rJ   optionsZurioptkeyvaluevalr"   r"   r#   _parse_options   s    
rV   )rR   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.

    :param options: Instance of _CaseInsensitiveDictionary containing
          MongoDB URI options.
    tlsinsecureNz9URI options %s and %s cannot be specified simultaneously.rG   rH   T
tlscrlfile)rW   rG   rH   zDURI option %s=True cannot be specified when CRL checking is enabled.ssltlsr   )rU   r   c                 S  s"   | dkr| dkS t | tr| S | S )N)truefalser[   )r@   r   )rU   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)rR   rW   opterr_msgZtlsallowinvalidcertsrX   r]   r"   r"   r#   _handle_security_options   s>    


rc   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 )a;  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.

    :param options: Instance of _CaseInsensitiveDictionary containing
          MongoDB URI options.
    Zrenamedz0Deprecated option '%s' ignored in favor of '%s'.r3   rL   z,Option '%s' is deprecated, use '%s' instead.removedzOption '%s' is deprecated. %s.)listr   rP   rQ   r`   DeprecationWarningpop)rR   optnamemodemessageZ
newoptnameZwarn_msgr"   r"   r#   _handle_option_deprecations   s8    
rk   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.

    :param options: Instance of _CaseInsensitiveDictionary containing
          MongoDB URI options.
    rW   N)r^   r_   re   r   rg   )rR   rW   ra   rh   Zintnamer"   r"   r#   _normalize_options'  s    

rl   FzMapping[str, Any]zMutableMapping[str, Any])rI   rQ   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.

    :param opts: A dict of MongoDB URI options.
    :param warn: If ``True`` then warnings will be logged and
          invalid options will be ignored. Otherwise invalid options will
          cause errors.
    )r   )rI   rQ   r"   r"   r#   validate_options=  s    rm   T)rI   validaterQ   	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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.

    :param opt: A string representing MongoDB URI options.
    :param validate: If ``True`` (the default), validate and normalize all
          options.
    :param warn: If ``False`` (the default), suppress all warnings raised
          during validation of options.
    :param normalize: If ``True`` (the default), renames all options to their
          internally-used names.
    &;r   z.Can not mix '&' and ';' for option separators.rK   r2   Nz(MongoDB URI options are key=value pairs.
authsource z1the authSource database cannot be an empty string)r4   r   rV   r5   rc   rk   rl   r
   r   rm   r^   )rI   rn   rQ   ro   Zand_idxZsemi_idxrR   r"   r"   r#   split_optionsL  s.    


rt   zlist[_Address])hostsr0   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.

    :param hosts: A string of the form host1[:port],host2[:port],...
    :param default_port: The port number to use when one wasn't specified
          for a host.
    ,z)Empty host (or extra comma in host list).r8   N)r?   r   r>   rO   rF   )ru   r0   nodesr/   rE   r"   r"   r#   split_hostsy  s    
rx   r7   z/ "$r1   rr   Z
authSource
replicaset
replicaSetloadbalancedloadBalancedr   None)rw   rR   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=truer{   z4Cannot specify multiple hosts with loadBalanced=truez;Cannot specify directConnection=true with loadBalanced=truery   z0Cannot specify replicaSet with loadBalanced=true)r   r^   r   )rw   rR   r"   r"   r#   _check_options  s    


r~   zOptional[float]zdict[str, Any])	urir0   rn   rQ   ro   connect_timeoutsrv_service_namesrv_max_hostsr   c                 C  s0  |  trd}| td }	nN|  trTt sBtjp4d}
td|
 d}| td }	nt	dt dt d|	svt	d	d}d}d}d}t
 }|	d
\}}}d|kr|d\}}}n|}|rt|}d|kr|dd\}}t|rt	d| nd}|r|t|||| |dkr*|dt}d|krR|d\}}}t|\}}n|}d|krlt	d| t|}d}|p|d}|r|drtdt dt|dd}t|dkrt	t d|d \}}|dk	rt	t d|p|d}t||||}| }| }|rtt||||}t|t rNtd| D ]\}}||krV|||< qV|dr|rt	d|dr|rt	dd |krd!|kr|rdnd"|d < nB|s|ddk	rt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.

    :param uri: The MongoDB URI to parse.
    :param default_port: The port number to use when one wasn't specified
          for a host in the URI.
    :param validate: If ``True`` (the default), validate and
          normalize all options. Default: ``True``.
    :param warn: 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``.
    :param normalize: If ``True``, convert names of URI options
          to their internally-used names. Default: ``True``.
    :param connect_timeout: The maximum time in milliseconds to
          wait for a response from the DNS server.
    :param srv_service_name: A custom SRV service name

    .. versionchanged:: 4.6
       The delimiting slash (``/``) between hosts and connection options is now optional.
       For example, "mongodb://example.com?tls=true" is now a valid URI.

    .. 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.?/.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)r0   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 DNSr|   z0You cannot specify loadBalanced with srvMaxHostsrz   z.You cannot specify replicaSet with srvMaxHostsrZ   rY   r[   zDThe srvServiceName option is only allowed with 'mongodb+srv://' URIszAThe srvMaxHosts option is only allowed with 'mongodb+srv://' URIs)ZnodelistusernamepasswordZdatabase
collectionrR   fqdn)
startswithSCHEME
SCHEME_LEN
SRV_SCHEMEr   sys
executabler   SRV_SCHEME_LENr   r   r*   r   r?   _BAD_DB_CHARSsearchupdatert   r^   r   
rpartitionr.   rx   r   r   Z	get_hostsZget_optionsset_ALLOWED_TXT_OPTSitemsr~   )r   r0   rn   rQ   ro   r   r   r   Zis_srvZscheme_freeZpython_pathr+   r-   Zdbaser   rR   Zhost_plus_db_partr,   rI   Z	host_partr%   ru   r   rw   rE   Zdns_resolverZdns_optionsZparsed_dns_optionsra   rU   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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 dictrZ   TNz!TLS is required for KMS providerszInsecure TLS options prohibited)ZtlsInsecureZtlsAllowInvalidCertificatesZtlsAllowInvalidHostnamesZ$tlsDisableCertificateRevocationCheckz!Insecure TLS options prohibited: )r@   dict	TypeErrorr   rN   r   rc   rl   r
   rm   r   r   )r   ZcontextsZproviderrR   rI   ssl_contextZallow_invalid_hostnamesnr"   r"   r#   _parse_kms_tls_optionsW  s.    

r   __main__r(   )F)TFT)C__doc__
__future__r   rer   rP   typingr   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$   r.   r6   rF   r_   rV   rc   rk   rl   rm   rt   rx   compileescaper   	frozensetr   r~   r   r   __name__pprintargvexcprintexitr"   r"   r"   r#   <module>   st   (
55'     -  0!
