a ‚oeJdã@sVUdZddlZddlZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl m Z ddl m Z ddl mZddl mZddl mZddl mZdd l mZdd l mZdd l mZdd l mZddlZdd lmZddlmZddlmZddlmZddlmZe j d¡Z e �r,ddl!Z!e "e#¡Z$Gdd„deƒZ%Gdd„deƒZ&Gdd„dƒZ'dZ(dZ)dZ*ej+ ,d¡Z-e .¡Z/iZ0ee1ej2fe3d<e 4dej5¡Z6ee1e1fdœd d!„Z7e$j8fee1e e1gdfee1e1fd"œd#d$„Z9e1e:d%œd&d'„Z;e1dd(œd)d*„Ze:dd-œd.d/„Z?dxe1e>e:dd-œd2d3„Z@dye1e1ee>ed5œd6d7„ZAe1e e>ge1fe>e>e1eee1fd8œd9d:„ZBdze1e>e1eee1fd<œd=d>„ZCd{e1e1e>e1eee1fd@œdAdB„ZDe1ddCœdDdE„ZEee1ee1dFœdGdH„ZFee1e1fdœdIdJ„ZGe1dœdKdL„ZHee1dœdMdN„ZId|e1e1e1dPœdQdR„ZJe1e1dSœdTdU„ZKd}e:ee1e1fdVœdWdX„ZLe 4dY¡ZMe1e:dZœd[d\„ZNGd]d^„d^ejOƒZPe d_e1ee1e>fdd`œdadb„ZQe1e1dcœddde„ZRee1eSfe1dcœdfdg„ZTe1e:dhœdidj„ZUee1eSfe:dcœdkdl„ZVe1e:dmœdndo„ZWe e e ddpœdqdr„ZXe1eee>e1fdsœdtdu„ZYe e e ddpœdvdw„ZZdS)~zUtilities for all Certbot.éN)ÚAny)ÚCallable)ÚDict)ÚIO)ÚList)Ú NamedTuple)ÚOptional)ÚSet)ÚTuple)ÚUnion)Úerrors)Ú constants)Úlock)Ú filesystem)ÚosÚlinuxc@s&eZdZUdZeeed<eed<dS)ÚKeyzPContainer for an optional file path and contents for a PEM-formated private key.ÚfileZpemN©Ú__name__Ú __module__Ú __qualname__Ú__doc__rÚstrÚ__annotations__Úbytes©rrú-C:\Program Files\Certbot\pkgs\certbot\util.pyr&s  rc@s.eZdZUdZeeed<eed<eed<dS)ÚCSRzPContainer for an optional file path and contents for a PEM or DER-formatted CSR.rÚdataZformNrrrrrr,s  rc@s0eZdZdZeddœdd„Zdedœdd„ZdS) Ú LooseVersionaÆA version with loose rules, i.e. any given string is a valid version number. but regular comparison is not supported. Instead, the `try_risky_comparison` method is provided, which may return an error if two LooseVersions are 'incomparible'. For example when integer and string version components are present in the same position. Differences with old distutils.version.LooseVersion: (https://github.com/python/cpython/blob/v3.10.0/Lib/distutils/version.py#L269) Most version comparisons should give the same result. However, if a version has multiple trailing zeroes, not all of them are used in the comparison. This ensure that, for example, "2.0" and "2.0.0" are equal. N©Úversion_stringÚreturnc CsTdd„t |¡Dƒ}t|ƒD],\}}zt|ƒ||<WqtyFYq0q||_dS)zhParses a version string into its components. :param str version_string: version string cSsg|]}|r|dkr|‘qS)Ú.r©Ú.0ÚxrrrÚ Hs ÿz)LooseVersion.__init__..N)Ú_VERSION_COMPONENT_REÚsplitÚ enumerateÚintÚ ValueErrorÚversion_components)Úselfr"Ú componentsÚiÚobjrrrÚ__init__Bs zLooseVersion.__init__)Úotherr#cCspzDtj|j|jdd�D](\}}||kr.WdS||krWdSqWdStyjtd |j|j¡ƒ‚Yn0dS)a¿Compares the LooseVersion to another value. If the other value is another LooseVersion, the version components are compared. Otherwise, an exception is raised. Comparison is performed element-wise. If the version components being compared are of different types, the two versions are considered incomparible. Otherwise, if either of the components is not equal to the other, less or greater is returned based on the comparison's result. In case the two versions are of different lengths, some elements in the longer version have not yet been compared. If these are all equal to zero, the two versions are equal. Otherwise, the longer version is greater. If the two versions are incomparible, an exception is raised. Otherwise, the returned integer indicates the result of the comparison. If self == other, 0 is returned. If self > other, 1 is returned. If self < other -1 is returned. Examples: Equality: - LooseVersion('1.0').try_risky_comparison(LooseVersion('1.0')) -> 0 - LooseVersion('2.0.0a').try_risky_comparison(LooseVersion('2.0.0a')) -> 0 Inequality: - LooseVersion('2.0.0').try_risky_comparison(LooseVersion('1.0')) -> 1 - LooseVersion('1.0.1').try_risky_comparison(LooseVersion('2.0a')) -> -1 Incomparability: - LooseVersion('1a').try_risky_comparison(LooseVersion('1.0')) -> ValueError r)Ú fillvalueéÿÿÿÿéz~Cannot meaningfully compare LooseVersion {} with LooseVersion {} due to comparison of version components with different types.N)Ú itertoolsÚ zip_longestr.Ú TypeErrorr-Úformat)r/r4Zself_vcZother_vcrrrÚtry_risky_comparisonRsþ  þz!LooseVersion.try_risky_comparison)rrrrrr3r,r<rrrrr 4s r zzz)z$The following error was encountered:z{0}zWEither run as root, or set --config-dir, --work-dir, and --logs-dir to writeable paths.Ú_LOCKSz(\d+ | [a-z]+ | \.))r#csZtj ¡‰dˆvsdˆvrˆSdD]2}|ˆvr"d ‡fdd„ˆ| d¡Dƒ¡ˆ|<q"ˆS)a‡ When Certbot is run inside a Snap, certain environment variables are modified. But Certbot sometimes calls out to external programs, since it uses classic confinement. When we do that, we must modify the env to remove our modifications so it will use the system's libraries, since they may be incompatible with the versions of libraries included in the Snap. For example, apachectl, Nginx, and anything run from inside a hook should call this function and pass the results into the ``env`` argument of ``subprocess.Popen``. :returns: A modified copy of os.environ ready to pass to Popen :rtype: dict ÚSNAPZCERTBOT_SNAPPED)ÚPATHZLD_LIBRARY_PATHú:c3s|]}ˆd|vr|VqdS)r>Nrr%©ÚenvrrÚ ªóz1env_no_snap_for_external_calls..)rÚenvironÚcopyÚjoinr*)Ú path_namerrArÚenv_no_snap_for_external_calls•s (rI)ÚparamsÚlogr#c Cs˜z tj|dtjtjdtƒd�}Wn6ttfyVdd |¡}||ƒt |¡‚Yn0|j dkrŒdd |¡|j |j f}||ƒt |¡‚|j |j fS)zªRun the script with the given params. :param list params: List of parameters to pass to subprocess.run :param callable log: Logger method to use for errors FT)ÚcheckÚstdoutÚstderrÚuniversal_newlinesrBzUnable to run the command: %sú rzError while running %s. %s %s) Ú subprocessÚrunÚPIPErIÚOSErrorr-rGr ZSubprocessErrorÚ returncoderMrN)rJrKÚprocÚmsgrrrÚ run_script®s&û  ÿ rX)Úexer#cCsTtj |¡\}}|rt |¡Stjd tj¡D]}t tj ||¡¡r0dSq0dS)z¤Determine whether path/name refers to an executable. :param str exe: Executable path or name :returns: If exe is a valid executable :rtype: bool r?TF)rÚpathr*rZ is_executablerEÚpathseprG)rYrZÚ_rrrÚ exe_existsÌs  r])Údir_pathr#cCs&ts ttƒ|tvr"t |¡t|<dS)zªLock the directory at dir_path until program exit. :param str dir_path: path to directory :raises errors.LockError: if the lock is held by another process N)r=Úatexit_registerÚ_release_locksrZlock_dir)r^rrrÚlock_dir_until_exitßsracCsLt ¡D]6}z | ¡Wqd |¡}tj|dd�Yq0qt ¡dS)Nz(Exception occurred releasing lock: {0!r}T©Úexc_info)r=ÚvaluesÚreleaser;ÚloggerÚdebugÚclear)Zdir_lockrWrrrr`îs   r`)Ú directoryÚmodeÚstrictr#c Cs`zt|||ƒt|ƒWnBtyZ}z*tjddd�t t |¡¡‚WYd}~n d}~00dS)ahEnsure directory exists with proper permissions and is locked. :param str directory: Path to a directory. :param int mode: Directory mode. :param bool strict: require directory to be owned by current user :raises .errors.LockError: if the directory cannot be locked :raises .errors.Error: if the directory cannot be made or verified zException was:TrbN) Úmake_or_verify_dirrarTrfrgr ÚErrorÚ PERM_ERR_FMTr;)rirjrkÚerrorrrrÚset_up_core_dirøs   rpéíFc Cspzt ||¡WnZtyj}zB|jtjkrT|rVt ||¡sVt d|t|ƒf¡‚n‚WYd}~n d}~00dS)aîMake sure directory exists with proper permissions. :param str directory: Path to a directory. :param int mode: Directory mode. :param bool strict: require directory to be owned by current user :raises .errors.Error: if a directory already exists, but has wrong permissions or owner :raises OSError: if invalid or inaccessible file names and paths, or other arguments that have the correct type, but are not accepted by the operating system. zE%s exists, but it should be owned by current user with permissions %sN) rÚmakedirsrTÚerrnoÚEEXISTZcheck_permissionsr rmÚoct)rirjrkÚ exceptionrrrrl s  ÿÿrlÚw)rZrjÚchmodr#cCsLd}|dur|f}d}tj|tjtjBtjBg|¢RŽ}tj||g|¢RŽS)zÕSafely open a file. :param str path: Path to a file. :param str mode: Same os `mode` for `open`. :param int chmod: Same as `mode` for `filesystem.open`, uses Python defaults if ``None``. rN)rÚopenrÚO_CREATÚO_EXCLÚO_RDWRÚfdopen)rZrjrxZ open_argsZ fdopen_argsÚfdrrrÚ safe_open&s "r)rZÚ filename_patÚcountrxrjr#c Csptj |||ƒ¡}zt|||d�tj |¡fWSty`}z|jtjkrL‚WYd}~n d}~00|d7}qdS)N)rxrjr7)rrZrGrÚabspathrTrsrt)rZr€r�rxrjZ current_pathÚerrrrrÚ _unique_file7s r„éÿ)rZrxrjr#cs*tj |¡\}‰t|‡fdd„d||d�S)zºSafely finds a unique file. :param str path: path/filename.ext :param int chmod: File mode :param str mode: Open mode :returns: tuple of file object and file name cs d|ˆfS)Nz%04d_%sr©r�©ÚtailrrÚPrDzunique_file..r©r€r�rxrj)rrZr*r„)rZrxrjrr‡rÚ unique_fileDs  þr‹é¤)rZÚfilenamerxrjr#c srtj |dˆ¡}zt||d�|fWStyV}z|jtjkrB‚WYd}~n d}~00t|‡fdd„d||d�S)aSafely finds a unique file using lineage convention. :param str path: directory path :param str filename: proposed filename :param int chmod: file mode :param str mode: open mode :returns: tuple of file object and file name (which may be modified from the requested one by appending digits to ensure uniqueness) :raises OSError: if writing files fails for an unanticipated reason, such as a full disk or a lack of permission to write to specified location. z%s.conf)rxNcs dˆ|fS)Nz %s-%04d.confrr†©r�rrr‰lrDz%unique_lineage_name..r7rŠ)rrZrGrrTrsrtr„)rZr�rxrjZpreferred_pathrƒrrŽrÚunique_lineage_nameTs  þr�)rZr#c CsFzt |¡Wn2ty@}z|jtjkr,‚WYd}~n d}~00dS)z!Remove a file that may not exist.N)rÚremoverTrsÚENOENT)rZrƒrrrÚ safely_removeps  r’)Ú all_namesr#c CsLtƒ}|D]<}z| t|ƒ¡Wq tjyDtjd|dd�Yq 0q |S)zÑRemoves names that aren't considered valid by Let's Encrypt. :param set all_names: all names found in the configuration :returns: all found names that are considered valid by LE :rtype: set zNot suggesting name "%s"Trb)ÚsetÚaddÚenforce_le_validityr ÚConfigurationErrorrfrg)r“Zfiltered_namesÚnamerrrÚget_filtered_namesys r™cCs tdd�S)zc Get OS name and version :returns: (os_name, os_version) :rtype: `tuple` of `str` F©Úpretty)Úget_python_os_inforrrrÚ get_os_infoŠsr�cCs,trtjdd�}tr|s(d tdd�¡S|S)z^ Get OS name and version string for User Agent :returns: os_ua :rtype: `str` TršrP)Ú _USE_DISTROÚdistror˜rGrœ)Zos_inforrrÚget_os_info_ua”s  r cCstrt ¡ d¡SgS)z¬ Get a list of strings that indicate the distribution likeness to other distributions. :returns: List of distribution acronyms :rtype: `list` of `str` rP)ržrŸZliker*rrrrÚget_systemd_os_like¢s r¡ú/etc/os-release)ÚvarnameÚfilepathr#cCs‚|d}tj |¡sdSt|dƒ�}| ¡}Wdƒn1s@0Y|D].}| ¡ |¡rNt| ¡t|ƒd…ƒSqNdS)zæ Get single value from a file formatted like systemd /etc/os-release :param str varname: Name of variable to fetch :param str filepath: File path of os-release file :returns: requested value :rtype: `str` ú=ÚÚrN) rrZÚisfileryÚ readlinesÚstripÚ startswithÚ_normalize_stringÚlen)r£r¤Z var_stringZfhÚcontentsÚlinerrrÚget_var_from_file¯s   &r°)Úorigr#cCs| dd¡ dd¡ ¡S)zV Helper function for get_var_from_file() to remove quotes and whitespaces ú"r¦ú')Úreplacerª)r±rrrr¬Åsr¬)r›r#c Cs,t t ¡t ¡t ¡¡}|\}}}| ¡}| d¡rjtrj|rFt  ¡nt  ¡t ¡}}|r`|}|rh|}nº| d¡rÚz$t j ddgt j t j ddtƒd�}Wn2tyÊt j ddgt j t j ddtƒd�}Yn0|j d ¡}nJ| d ¡�r| d ¡d }| d ¡d }n t ¡d�r t ¡d}nd}||fS)zø Get Operating System type/distribution and major version using python platform module :param bool pretty: If the returned OS name should be in longer (pretty) form :returns: (os_name, os_version) :rtype: `tuple` of `str` rÚdarwinz/usr/bin/sw_versz-productVersionFT)rMrNrLrOrBZsw_versÚ Zfreebsdú-rr$r7r¦)ÚplatformZ system_aliasÚsystemreÚversionÚlowerr«ržrŸr˜ÚidrQrRrSrIrTrMÚrstripÚ partitionZ win32_ver)r›ÚinfoZos_typeZos_verr\Z distro_nameZdistro_versionrVrrrrœÌsH ý  ü  ü  rœz![a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+$)Úemailr#cCs2t |¡dur"| d¡ o d|vSt d|¡dS)z$Scrub email address before using it.Nr$z..zInvalid email address: %s.F)Ú EMAIL_REGEXÚmatchr«rfro)rÀrrrÚ safe_emails rÃc@s,eZdZdZdeeeeeddœdd„ZdS)ÚDeprecatedArgumentActionz1Action to log a warning when an argument is used.N)Úunused1Úunused2Úunused3Ú option_stringr#cCst d|¡dS)NzUse of %s is deprecated.)rfZwarning)r/rÅrÆrÇrÈrrrÚ__call__sz!DeprecatedArgumentAction.__call__)N)rrrrrrrrÉrrrrrÄs ÿÿrÄ).N)Ú add_argumentÚ argument_nameÚnargsr#cCsJttjvr4ttjtƒr$tj t¡ntjtf7_||ttj|d�dS)aáAdds a deprecated argument with the name argument_name. Deprecated arguments are not shown in the help. If they are used on the command line, a warning is shown stating that the argument is deprecated and no other action is taken. :param callable add_argument: Function that adds arguments to an argument parser/group. :param str argument_name: Name of deprecated argument. :param nargs: Value for nargs when adding the argument to argparse. )ÚactionÚhelprÌN)rÄÚconfigargparseZ#ACTION_TYPES_THAT_DONT_NEED_A_VALUEÚ isinstancer”r•ÚargparseÚSUPPRESS)rÊrËrÌrrrÚadd_deprecated_arguments  ÿÿÿrÓ)Údomainr#cCs�t|ƒ}t d|¡s$t d |¡¡‚| d¡}t|ƒdkrJt d |¡¡‚|D]<}| d¡rnt d ||¡¡‚|  d¡rNt d ||¡¡‚qN|S) ahChecks that Let's Encrypt will consider domain to be valid. :param str domain: FQDN to check :type domain: `str` :returns: The domain cast to `str`, with ASCII-only contents :rtype: str :raises ConfigurationError: for invalid domains and cases where Let's Encrypt currently will not issue certificates z^[A-Za-z0-9.-]*$zP{0} contains an invalid character. Valid characters are A-Z, a-z, 0-9, ., and -.r$éz{0} needs at least two labelsr·z1label "{0}" in domain "{1}" cannot start with "-"z/label "{0}" in domain "{1}" cannot end with "-") Úenforce_domain_sanityÚrerÂr r—r;r*r­r«Úendswith)rÔÚlabelsZlabelrrrr–1s4  ÿÿ  ÿ ÿÿ ÿÿr–cCsz"t|tƒr| d¡}| d¡Wnty>t d¡‚Yn0| ¡}| d¡r^|dd…n|}dD]&}|  d  |¡¡rft d   ||¡¡‚qft |ƒr¦t d   |¡¡‚d   |¡}t |ƒd krÌt d   |¡¡‚|  d¡}|D]6}|sòt d  |¡¡‚t |ƒdkrÚt d  ||¡¡‚qÚ|S)a�Method which validates domain value and errors out if the requirements are not met. :param domain: Domain to check :type domain: `str` or `bytes` :raises ConfigurationError: for invalid domains and cases where Let's Encrypt currently will not issue certificates :returns: The domain cast to `str`, with ASCII-only contents :rtype: str zutf-8ÚasciizbNon-ASCII domain names not supported. To issue for an Internationalized Domain Name, use Punycode.r$Nr6)ÚhttpÚhttpsz{0}://z[Requested name {0} appears to be a URL, not a FQDN. Try again without the leading "{1}://".zRequested name {0} is an IP address. The Let's Encrypt certificate authority will not issue certificates for a bare IP address.z*Requested domain {0} is not a FQDN becauseéÿz{0} it is too long.z{0} it contains an empty label.é?z{0} label {1} is too long.)rÐrÚdecodeÚencodeÚ UnicodeErrorr r—r»rØr«r;Ú is_ipaddressr­r*)rÔÚschemerWrÙÚlrrrrÖSs>    þÿþÿ    rÖ)Úaddressr#c Cs^zt tj|¡WdStjyXzt tj|¡WYdStjyRYYdS0Yn0dS)z×Is given address string form of IP(v4 or v6) address? :param address: address to check :type address: `str` :returns: True if address is valid IP address, otherwise return False. :rtype: bool TFN)ÚsocketZ inet_ptonZAF_INETroZAF_INET6)rårrrrâŽs râcCst|tƒr| d¡S| d¡S)z¹"Is domain a wildcard domain? :param domain: domain to check :type domain: `bytes` or `str` :returns: True if domain is a wildcard, otherwise, False :rtype: bool z*.s*.)rÐrr«)rÔrrrÚis_wildcard_domain¥s  rç)Úsrvr#cCs|tjkpd|vS)zÏ Determine whether a given ACME server is a known test / staging server. :param str srv: the URI for the ACME server :returns: True iff srv is a known test / staging server :rtype bool: Zstaging)r Z STAGING_URI)rèrrrÚ is_staging´sré)ÚfuncÚargsÚkwargsr#cOstjt|g|¢Ri|¤ŽdS)aSets func to be called before the program exits. Special care is taken to ensure func is only called when the process that first imports this module exits rather than any child processes. :param function func: function to be called in case of an error N)ÚatexitÚregisterÚ _atexit_call©rêrërìrrrr_¿s r_r!cCst|ƒ}|jS)aýParses a version string into its components. This code and the returned tuple is based on the now deprecated distutils.version.LooseVersion class from the Python standard library. Two LooseVersion classes and two lists as returned by this function should compare in the same way. See https://github.com/python/cpython/blob/v3.10.0/Lib/distutils/version.py#L205-L347. :param str version_string: version string :returns: list of parsed version string components :rtype: list )r r.)r"Z loose_versionrrrÚparse_loose_versionËs rñcOstt ¡kr||i|¤ŽdS)N)Ú _INITIAL_PIDrÚgetpidrðrrrrïÚs rï)rqF)rwN)r…rw)rŒrw)r¢)F)[rrÑrírsr8Zloggingr¸r×rærQÚsysÚtypingrrrrrrrr r r rÏZcertbotr Zcertbot._internalr rZcertbot.compatrrr«ržrŸZ getLoggerrrfrrr Z ANSI_SGR_BOLDZ ANSI_SGR_REDZANSI_SGR_RESETÚlineseprGrnróròr=rZLockFilerÚcompileÚVERBOSEr)rIrorXÚboolr]rar`r,rprlrr„r‹r�r’r™r�r r¡r°r¬rœrÁrÃÚActionrÄrÓr–rrÖrârçrér_rñrïrrrrÚsž                 K . ÿ ÿ ÿ    8  ÿ ";