a
    Z,É_7  ã                   @   sª   d Z ddlZddlZddlZddlZddlZddlZddlmZ ddlm	Z	 ddl
mZ ddlmZ ddlmZ ddlmZ e e¡ZG d	d
„ d
eƒZdd„ Zdd„ ZdS )z@ApacheParser is a member object of the ApacheConfigurator class.é    N)ÚDict)ÚList)Úerrors)Úos)Úapache_util)Ú	constantsc                   @   s„  e Zd ZdZe d¡Zh d£Zd^dd„Zdd	„ Z	d
d„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zd d!„ Zd"d#„ Zd$d%„ Zd&d'„ Zd(d)„ Zd_d+d,„Zd`d-d.„Zd/d0„ Zd1d2„ Zd3d4„ Zdad5d6„Zdbd8d9„Z d:d;„ Z!d<d=„ Z"d>d?„ Z#d@dA„ Z$dBdC„ Z%dDdE„ Z&dFdG„ Z'dHdI„ Z(dJdK„ Z)dLdM„ Z*dNdO„ Z+dPdQ„ Z,dRdS„ Z-dTdU„ Z.dVdW„ Z/dXdY„ Z0dZd[„ Z1d\d]„ Z2dS )cÚApacheParseraÉ  Class handles the fine details of parsing the Apache Configuration.

    .. todo:: Make parsing general... remove sites-available etc...

    :ivar str root: Normalized absolute path to the server root
        directory. Without trailing slash.
    :ivar set modules: All module names that are currently enabled.
    :ivar dict loc: Location to place directives, root - configuration origin,
        default - user config file, name - NameVirtualHost,

    z\$\{[^ \}]*}>   ú?Ú*ú]ú\ú[N©é   é   c                 C   sì   || _ d | _|  ¡  |  ¡ s&t d¡‚i | _i | _i | _t	j
 |¡| _d|  ¡ i| _|  | jd ¡ |dkrt|  ¡  |  ¡  |  ¡  | j |  ¡ ¡ t | j¡| _|rÈ|  t	j
 |¡d | j  d¡ ¡ |dk rè| jdddrèt d	¡‚d S )
NzˆApache plugin support requires libaugeas0 and augeas-lenses version 1.2.0 or higher, please make sure you have you have those installed.Úrootr   ú/Zvhost_filesZDefineF)ÚexcludezError parsing runtime variables)ÚconfiguratorÚaugÚinit_augeasÚcheck_aug_versionr   ZNotSupportedErrorÚmodulesÚparser_pathsÚ	variablesr   ÚpathÚabspathr   Ú_find_config_rootÚlocÚ
parse_fileÚupdate_runtime_variablesÚstandardize_exclÚparse_modulesÚupdateÚ_set_locationsÚcopyÚdeepcopyÚexisting_pathsÚoptionÚfind_dirÚPluginError)Úselfr   Z	vhostrootÚversionr   © r-   úA/usr/lib/python3/dist-packages/certbot_apache/_internal/parser.pyÚ__init__#   s4    ÿ
ÿzApacheParser.__init__c                 C   sT   zddl }W n ty(   t d¡‚Y n0 |jtj|jj|jjB |jj	B d| _
dS )z' Initialize the actual Augeas instance r   NzProblem in Augeas installation)ZloadpathÚflags)ÚaugeasÚImportErrorr   ÚNoInstallationErrorZAugeasr   ZAUGEAS_LENS_DIRZNONEZNO_MODL_AUTOLOADZENABLE_SPANr   )r+   r1   r-   r-   r.   r   Y   s    ÿþûzApacheParser.init_augeasc              	   C   sv   | j  d¡}|D ]`}| j  |d ¡}|r||v rd |dt|ƒd … | j  |d ¡| j  |d ¡¡}t |¡‚qdS )	zÜVerify Augeas can parse all of the lens files.

        :param str lens: lens to check for errors

        :raises .errors.PluginError: If there has been an error in parsing with
            the specified lens.

        ú/augeas//errorz/lensz@There has been an error in parsing the file {0} on line {1}: {2}é   é   z/linez/messageN)r   ÚmatchÚgetÚformatÚlenr   r*   )r+   ZlensZerror_filesr   Z	lens_pathÚmsgr-   r-   r.   Úcheck_parsing_errorsj   s    	ûÿz!ApacheParser.check_parsing_errorsc                 C   sP   | j  dd¡ z| j  d¡}W n  ty>   | j  d¡ Y dS 0 | j  d¡ |S )z Checks that we have recent enough version of libaugeas.
        If augeas version is recent enough, it will support case insensitive
        regexp matchingz/test/path/testing/argZaRgUMeNTz,/test//*[self::arg=~regexp('argument', 'i')]z
/test/pathF)r   Úsetr7   ÚRuntimeErrorÚremove)r+   Úmatchesr-   r-   r.   r   „   s    ÿzApacheParser.check_aug_versionc              	   C   s´   | j  d¡}| j  dd¡ | j  d¡}z| j  ¡  W n2 ttfyf   |  |¡ d| j_	t
 d¡‚Y n0 | j  d|¡ | j  d¡}tƒ }|r°|D ]}| | j  |¡dd… ¡ q|S )	a™  Lists files that have modified Augeas DOM but the changes have not
        been written to the filesystem yet, used by `self.save()` and
        ApacheConfigurator to check the file state.

        :raises .errors.PluginError: If there was an error in Augeas, in
            an attempt to save the configuration, or an error creating a
            checkpoint

        :returns: `set` of unsaved files
        z/augeas/saveZnoopr4   Ú z-Error saving files, check logs for more info.z/augeas/events/savedr6   N)r   r8   r=   r7   Úsaver>   ÚIOErrorÚ_log_save_errorsr   Ú
save_notesr   r*   Úadd)r+   Z
save_stateÚex_errsZ
save_pathsÚ
save_filesr   r-   r-   r.   Úunsaved_files“   s$    
ÿ
zApacheParser.unsaved_filesc                 C   s&   |   ¡ r"| j jd7  _| j ¡  dS )zÙMakes sure that all Augeas dom changes are written to files to avoid
        loss of configuration directives when doing additional augeas parsing,
        causing a possible augeas.load() resulting dom reset
        z
(autosave)N)rI   r   rE   rB   ©r+   r-   r-   r.   Úensure_augeas_stateº   s    z ApacheParser.ensure_augeas_statec                 C   s>   d| j _| j ¡  |r:|D ]}| j d| ¡ q| j ¡  dS )zõSaves all changes to the configuration files.

        save() is called from ApacheConfigurator to handle the parser specific
        tasks of saving.

        :param list save_files: list of strings of file paths that we need to save.

        rA   z/files/N)r   rE   r   rB   r?   Úload)r+   rH   Zsfr-   r-   r.   rB   Ä   s    	
zApacheParser.savec                    s6   | j  d¡}t dd ‡ fdd„|D ƒ¡| jj¡ dS )zfLog errors due to bad Augeas save.

        :param list ex_errs: Existing errors before save

        r4   z2Unable to save files: %s. Attempted Save Notes: %sz, c                 3   s*   | ]"}|ˆ vr|d t |ƒd … V  qdS )r5   r6   N)r:   )Ú.0Úerr©rG   r-   r.   Ú	<genexpr>á   s   þz0ApacheParser._log_save_errors.<locals>.<genexpr>N)r   r7   ÚloggerÚerrorÚjoinr   rE   )r+   rG   Znew_errsr-   rO   r.   rD   ×   s
    ýzApacheParser._log_save_errorsc                 C   sd   |   tdƒ|¡s`t d|t|ƒ¡ |  t|ƒd|¡ tj |¡}tj 	|¡}| j
 |g ¡ |¡ dS )zÃAdd Include for a new configuration file if one does not exist

        :param str main_config: file path to main Apache config file
        :param str inc_path: path of file to include

        ÚIncludezAdding Include %s to %sN)r)   Úcase_irQ   ÚdebugÚget_aug_pathÚadd_dirr   r   ÚdirnameÚbasenamer'   Ú
setdefaultÚappend)r+   Zmain_configZinc_pathZnew_dirZnew_filer-   r-   r.   Úadd_includeå   s    ÿþzApacheParser.add_includec                 C   sD   |d | j vrd| j |d < d| d | j vr@d| j d| d < dS )z%Shortcut for updating parser modules.Ú_moduleNZmod_z.c)r   )r+   Úmod_namer-   r-   r.   Úadd_modø   s    zApacheParser.add_modc                 C   s   i | _ |  ¡  |  ¡  dS )zgReset the loaded modules list. This is called from cleanup to clear
        temporarily loaded modules.N)r   Úupdate_modulesr"   rJ   r-   r-   r.   Úreset_modulesÿ   s    zApacheParser.reset_modulesc           	      C   s®   i }|   d¡}t|ƒ}d}t|ƒ|kržt|ƒ}tj ||¡D ]^\}}|  |¡}|  |¡}|r†|r†|||< ||tj 	|¡dd… d < q<t
 d|dd… ¡ q<q| j |¡ dS )zãIterates on the configuration until no new modules are loaded.

        ..todo:: This should be attempted to be done with a binary to avoid
            the iteration issue.  Else... parse and enable mods at same time.

        Z
LoadModuleéÿÿÿÿNéþÿÿÿÚcz8Could not read LoadModule directive from Augeas path: %sr6   )r)   Úiterr:   ÚsixZmovesÚzipÚget_argr   r   rZ   rQ   rV   r   r#   )	r+   Zmodsr@   ÚiteratorZ	prev_sizeZ
match_nameZmatch_filenamer_   Zmod_filenamer-   r-   r.   r"     s$    
ÿ


ÿzApacheParser.parse_modulesc                 C   s   |   ¡  |  ¡  |  ¡  dS )zAUpdate Includes, Defines and Includes from httpd config dump dataN)Úupdate_definesÚupdate_includesra   rJ   r-   r-   r.   r    "  s    z%ApacheParser.update_runtime_variablesc                 C   s   t  | j d¡¡| _dS )z>Updates the dictionary of known variables in the configurationÚctlN)r   Zparse_definesr   r(   r   rJ   r-   r-   r.   rk   )  s    zApacheParser.update_definesc                 C   sB   |   d¡}t | j d¡¡}|r>|D ]}|  |¡s$|  |¡ q$dS )z>Get includes from httpd process, and add them to DOM if neededrT   rm   N)r)   r   Zparse_includesr   r(   Úparsed_in_currentr   )r+   Ú_r@   Úir-   r-   r.   rl   .  s    

zApacheParser.update_includesc                 C   s.   t  | j d¡¡}|D ]}|  | ¡ ¡ qdS )z:Get loaded modules from httpd process, and add them to DOMrm   N)r   r"   r   r(   r`   Ústrip)r+   r@   Úmodr-   r-   r.   ra   <  s    zApacheParser.update_modulesc                 C   s®   g }|dkr@t |ƒD ](\}}| d¡r| || dd… ¡ qnjt |ƒD ]`\}}| d| ¡rH|t|ƒd ksˆ||d   d|d  ¡sH| || dtd| ƒ … ¡ qH|S )a  Filter out directives with specific number of arguments.

        This function makes the assumption that all related arguments are given
        in order.  Thus /files/apache/directive[5]/arg[2] must come immediately
        after /files/apache/directive[5]/arg[1]. Runs in 1 linear pass.

        :param string matches: Matches of all directives with arg nodes
        :param int args: Number of args you would like to filter

        :returns: List of directives that contain # of arguments.
            (arg is stripped off)

        é   ú/argNéüÿÿÿú/arg[%d])Ú	enumerateÚendswithr\   r:   )r+   r@   ÚargsZfilteredrp   r7   r-   r-   r.   Úfilter_args_numC  s    
ÿÿ"zApacheParser.filter_args_numc                 C   sŠ   |   |d¡}| j |d dd¡ |d }| j ||¡ t|ƒdkrZ| j |d |d ¡ n,t|ƒD ]"\}}| j d	||d f |¡ qbd
S )aá  Adds directive and value to IfMod ssl block.

        Adds given directive and value along configuration path within
        an IfMod mod_ssl.c block.  If the IfMod block does not exist in
        the file, it is created.

        :param str aug_conf_path: Desired Augeas config path to add directive
        :param str directive: Directive you would like to add, e.g. Listen
        :param args: Values of the directive; str "443" or list of str
        :type args: list

        z	mod_ssl.cÚargÚ	directiveFzdirective[1]rs   rt   r   z
%s/arg[%d]N)Ú	get_ifmodr   Úinsertr=   r:   rw   )r+   Úaug_conf_pathr|   ry   Zif_mod_pathZnvh_pathrp   r{   r-   r-   r.   Úadd_dir_to_ifmodsslb  s    z ApacheParser.add_dir_to_ifmodsslFc                 C   s8   | j  d||f ¡}|s&|  |||¡S |d  d¡d S )a  Returns the path to <IfMod mod> and creates one if it doesn't exist.

        :param str aug_conf_path: Augeas configuration path
        :param str mod: module ie. mod_ssl.c
        :param bool beginning: If the IfModule should be created to the beginning
            of augeas path DOM tree.

        :returns: Augeas path of the requested IfModule directive that pre-existed
            or was created during the process. The path may be dynamic,
            i.e. .../IfModule[last()]
        :rtype: str

        z%s/IfModule/*[self::arg='%s']r   r{   )r   r7   Úcreate_ifmodÚ
rpartition)r+   r   rr   Ú	beginningZif_modsr-   r-   r.   r}   |  s    ÿzApacheParser.get_ifmodc                 C   sn   |r0d  |¡}| j d  |¡dd¡ d  |¡}n,d  |¡}d  |¡}| j |d¡ d	  |¡}| j ||¡ |S )
aµ  Creates a new <IfMod mod> and returns its path.

        :param str aug_conf_path: Augeas configuration path
        :param str mod: module ie. mod_ssl.c
        :param bool beginning: If the IfModule should be created to the beginning
            of augeas path DOM tree.

        :returns: Augeas path of the newly created IfModule directive.
            The path may be dynamic, i.e. .../IfModule[last()]
        :rtype: str

        z{}/IfModule[1]/argz{}/directive[1]ZIfModuleTz{}/IfModule[1]/z{}/IfModule[last() + 1]z{}/IfModule[last()]/argrA   z{}/IfModule[last()]/)r9   r   r~   r=   )r+   r   rr   rƒ   Z
c_path_argZretpathZc_pathr-   r-   r.   r   ’  s    
ÿ


zApacheParser.create_ifmodc                 C   s^   | j  |d |¡ t|tƒrHt|dƒD ]\}}| j  d||f |¡ q&n| j  |d |¡ dS )a  Appends directive to the end fo the file given by aug_conf_path.

        .. note:: Not added to AugeasConfigurator because it may depend
            on the lens

        :param str aug_conf_path: Augeas configuration path to add directive
        :param str directive: Directive to add
        :param args: Value of the directive. ie. Listen 443, 443 is arg
        :type args: list or str

        z/directive[last() + 1]rs   z%s/directive[last()]/arg[%d]z/directive[last()]/argN)r   r=   Ú
isinstanceÚlistrw   )r+   r   r|   ry   rp   Úvaluer-   r-   r.   rX   ­  s    
ÿzApacheParser.add_dirc                 C   sr   |d }| j  |dd¡ | j  ||¡ t|tƒr\t|dƒD ]\}}| j  |d|  |¡ q:n| j  |d |¡ dS )a)  Adds the directive to the beginning of defined aug_conf_path.

        :param str aug_conf_path: Augeas configuration path to add directive
        :param str dirname: Directive to add
        :param args: Value of the directive. ie. Listen 443, 443 is arg
        :type args: list or str
        z/directive[1]r|   Trs   rv   rt   N)r   r~   r=   r„   r…   rw   )r+   r   rY   ry   Z	first_dirrp   r†   r-   r-   r.   Úadd_dir_beginningÁ  s    
zApacheParser.add_dir_beginningc                 C   s   | j  |d |¡ dS )z§Adds the comment to the augeas path

        :param str aug_conf_path: Augeas configuration path to add directive
        :param str comment: Comment content

        z/#comment[last() + 1]N)r   r=   )r+   r   Úcommentr-   r-   r.   Úadd_commentÒ  s    zApacheParser.add_commentc                 C   sR   |st | jƒ}| j d| ¡}g }|D ]&}| j |¡}|r&||v r&| |¡ q&|S )a  Finds a comment with specified content from the provided DOM path

        :param str arg: Comment content to search
        :param str start: Beginning Augeas path to begin looking

        :returns: List of augeas paths containing the comment content
        :rtype: list

        z%s//*[label() = '#comment'])rW   r   r   r7   r8   r\   )r+   r{   ÚstartZcommentsZresultsrˆ   Z	c_contentr-   r-   r.   Úfind_commentsÛ  s    

zApacheParser.find_commentsTc                 C   sÚ   |st | jd ƒ}dt|ƒtdƒtdƒf }| j d||f ¡}|rN|  |¡}|du r\d}ndt|ƒ }g }|D ]d}	| j |	¡ ¡ }
|
d	v r²| |  	|||  
|  |	d ¡¡|¡¡ |
| ¡ krp| | j |	| ¡¡ qp|S )
a	  Finds directive in the configuration.

        Recursively searches through config files to find directives
        Directives should be in the form of a case insensitive regex currently

        .. todo:: arg should probably be a list
        .. todo:: arg search currently only supports direct matching. It does
            not handle the case of variables or quoted arguments. This should
            be adapted to use a generic search for the directive and then do a
            case-insensitive self.get_arg filter

        Note: Augeas is inherently case sensitive while Apache is case
        insensitive.  Augeas 1.0 allows case insensitive regexes like
        regexp(/Listen/, "i"), however the version currently supported
        by Ubuntu 0.10 does not.  Thus I have included my own case insensitive
        transformation by calling case_i() on everything to maintain
        compatibility.

        :param str directive: Directive to look for
        :param arg: Specific value directive must have, None if all should
                    be considered
        :type arg: str or None

        :param str start: Beginning Augeas path to begin looking
        :param bool exclude: Whether or not to exclude directives based on
            variables and enabled modules

        r   z(%s)|(%s)|(%s)rT   ZIncludeOptionalz$%s//*[self::directive=~regexp('%s')]Nrt   z/*[self::arg=~regexp('%s')])ZincludeZincludeoptional)rW   r   rU   r   r7   Úexclude_dirsr8   ÚlowerÚextendr)   Ú_get_include_pathri   )r+   r|   r{   rŠ   r   Zregexr@   Z
arg_suffixZordered_matchesr7   Zdir_r-   r-   r.   r)   ñ  s4    þ
ÿ
ýzApacheParser.find_dirc                    s6   |d dkr|d }ˆ j  |d ¡}‡ fdd„|D ƒS )zÑ
        Tries to fetch all arguments for a directive. See get_arg.

        Note that if match is an ancestor node, it returns all names of
        child directives as well as the list of arguments.

        rc   r   r
   c                    s   g | ]}ˆ   |¡‘qS r-   )ri   )rM   r{   rJ   r-   r.   Ú
<listcomp>I  ó    z-ApacheParser.get_all_args.<locals>.<listcomp>)r   r7   )r+   r7   Zallargsr-   rJ   r.   Úget_all_args=  s    	zApacheParser.get_all_argsc              	   C   sx   | j  |¡}|sdS | d¡}tj |¡}|D ]D}z| || j|dd…  ¡}W q. typ   t	 
d| ¡‚Y q.0 q.|S )zŽUses augeas.get to get argument value and interprets result.

        This also converts all variables and parameters appropriately.

        Nú'"r   rc   zError Parsing variable: %s)r   r8   rq   r   Úarg_var_interpreterÚfindallÚreplacer   ÚKeyErrorr   r*   )r+   r7   r†   r   Úvarr-   r-   r.   ri   K  s    
zApacheParser.get_argc                 C   s   t | jd ƒS )z@
        Returns the Augeas path of root configuration.
        r   )rW   r   rJ   r-   r-   r.   Úget_root_augpathg  s    zApacheParser.get_root_augpathc                 C   sN   d| j  ¡ fd| jfg}g }|D ](}|D ]}|  ||¡s( q q(| |¡ q |S )z>Exclude directives that are not loaded into the configuration.ZifmoduleZifdefine)r   Úkeysr   Ú_pass_filterr\   )r+   r@   ÚfiltersZvalid_matchesr7   Úfilter_r-   r-   r.   rŒ   m  s    zApacheParser.exclude_dirsc                 C   sŒ   |  ¡ }| |d ¡}|dkrˆ| d|¡}| j |d|… d ¡}| d¡rf|dd… |d v rvdS n||d vrvdS | |d |¡}qd	S )
zÐDetermine if directive passes a filter.

        :param str match: Augeas path
        :param list filter: list of tuples of form
            [("lowercase if directive", set of relevant parameters)]

        r   rc   r   Nrt   ú!rs   FT)r   Úfindr   r8   Ú
startswith)r+   r7   r   Zmatch_lZlast_match_idxZ	end_of_ifZ
expressionr-   r-   r.   r›   {  s    
zApacheParser._pass_filterc                 C   s>   |  d¡}| d¡s.tj tj | j|¡¡}ntj |¡}|S )z¢Ensure paths are consistent and absolute

        :param str arg: Argument of directive

        :returns: Standardized argument path
        :rtype: str
        r“   r   )rq   r    r   r   ÚnormpathrS   r   )r+   r{   r-   r-   r.   Ústandard_path_from_server_root˜  s
    	

z+ApacheParser.standard_path_from_server_rootc                 C   sˆ   |   |¡}tj |¡r,|  tj |d¡¡ n
|  |¡ | d¡}t|ƒD ],\}}tdd„ |D ƒƒrHd|  	|¡ ||< qHd |¡}t
|ƒS )a@  Converts an Apache Include directive into Augeas path.

        Converts an Apache Include directive argument into an Augeas
        searchable path

        .. todo:: convert to use os.path.join()

        :param str arg: Argument of Include directive

        :returns: Augeas path string
        :rtype: str

        r
   r   c                 s   s   | ]}|t jv V  qd S )N)r   Úfnmatch_chars)rM   Úcharr-   r-   r.   rP   Ì  r‘   z1ApacheParser._get_include_path.<locals>.<genexpr>z* [label()=~regexp('%s')])r¢   r   r   Úisdirr   rS   Úsplitrw   ÚanyÚfnmatch_to_rerW   )r+   r{   Z	split_argÚidxr¦   r-   r-   r.   r   «  s    


ÿ

zApacheParser._get_include_pathc                 C   s.   t jdk rt |¡dd… S t |¡dd… S )aˆ  Method converts Apache's basic fnmatch to regular expression.

        Assumption - Configs are assumed to be well-formed and only writable by
        privileged users.

        https://apr.apache.org/docs/apr/2.0/apr__fnmatch_8h_source.html

        :param str clean_fn_match: Apache style filename match, like globs

        :returns: regex suitable for augeas
        :rtype: str

        )é   r6   Niùÿÿÿr   éýÿÿÿ)ÚsysÚversion_infoÚfnmatchÚ	translate)r+   Zclean_fn_matchr-   r-   r.   r¨   ×  s    
zApacheParser.fnmatch_to_rec                 C   sT   |   |¡\}}|  ¡  |rP| j d| ¡}|sP|r<|  |¡ |  |¡ | j ¡  dS )zÙParse file with Augeas

        Checks to see if file_path is parsed by Augeas
        If filepath isn't parsed, the file is added and Augeas is reloaded

        :param str filepath: Apache config file path

        z&/augeas/load/Httpd['%s' =~ glob(incl)]N)Ú_check_path_actionsrK   r   r7   Ú_remove_httpd_transformÚ_add_httpd_transformrL   )r+   ÚfilepathÚuse_newÚ
remove_oldZinc_testr-   r-   r.   r   ë  s    	ÿ

zApacheParser.parse_filec                 C   s   |   || j¡S )a<  Checks if the file path is parsed by current Augeas parser config
        ie. returns True if the file is found on a path that's found in live
        Augeas configuration.

        :param str filep: Path to match

        :returns: True if file is parsed in existing configuration tree
        :rtype: bool
        )Ú_parsed_by_parser_pathsr   ©r+   Úfilepr-   r-   r.   rn     s    
zApacheParser.parsed_in_currentc                 C   s   |   || j¡S )a[  Checks if the file path is parsed by existing Apache config.
        ie. returns True if the file is found on a path that matches Include or
        IncludeOptional statement in the Apache configuration.

        :param str filep: Path to match

        :returns: True if file is parsed in existing configuration tree
        :rtype: bool
        )r¶   r'   r·   r-   r-   r.   Úparsed_in_original  s    
zApacheParser.parsed_in_originalc              	   C   s:   |D ]0}|| D ]"}t   |tj ||¡¡r  dS qqdS )znHelper function that searches through provided paths and returns
        True if file path is found in the setTF)r®   r   r   rS   )r+   r¸   ÚpathsZ	directoryÚfilenamer-   r-   r.   r¶     s
    z$ApacheParser._parsed_by_parser_pathsc                 C   s`   z<t j |¡}| jt j |¡ }d|v r.d}nd}|dk}W n tyV   d}d}Y n0 ||fS )aL  Determine actions to take with a new augeas path

        This helper function will return a tuple that defines
        if we should try to append the new filepath to augeas
        parser paths, and / or remove the old one with more
        narrow matching.

        :param str filepath: filepath to check the actions for

        r
   FT)r   r   rZ   r   rY   r—   )r+   r³   Znew_file_matchZexisting_matchesr´   rµ   r-   r-   r.   r°   '  s    
z ApacheParser._check_path_actionsc                 C   sd   | j tj |¡ }tj |¡}|D ]0}|d | }| j d| ¡}| j |d ¡ q"| j  |¡ dS )z[Remove path from Augeas transform

        :param str filepath: filepath to remove
        r   z!/augeas/load/Httpd/incl [. ='%s']r   N)r   r   r   rY   r   r7   r?   Úpop)r+   r³   Zremove_basenamesZremove_dirnameÚnameZremove_pathZ
remove_incr-   r-   r.   r±   @  s    ÿz$ApacheParser._remove_httpd_transformc                 C   s¨   | j  d¡}|r4| j  |d dd¡ | j  d|¡ n| j  dd¡ | j  d|¡ z$| jtj |¡  tj 	|¡¡ W n. t
y¢   tj 	|¡g| jtj |¡< Y n0 d	S )
a"  Add a transform to Augeas.

        This function will correctly add a transform to augeas
        The existing augeas.add_transform in python doesn't seem to work for
        Travis CI as it loads in libaugeas.so.0.10.0

        :param str incl: filepath to include for transform

        z /augeas/load/Httpd/incl [last()]r   ÚinclFz/augeas/load/Httpd/incl[last()]z/augeas/load/Httpd/lensz	Httpd.lnsz/augeas/load/Httpd/inclN)r   r7   r~   r=   r   r   r   rY   r\   rZ   r—   )r+   r¾   Zlast_includer-   r-   r.   r²   O  s    

ÿ
ÿz!ApacheParser._add_httpd_transformc                 C   sz   ddddddddd	| j d
 | j d | j d | j d | j d | j d g}t|dƒD ]\}}| j d| |¡ qP| j ¡  dS )aa  Standardize the excl arguments for the Httpd lens in Augeas.

        Note: Hack!
        Standardize the excl arguments for the Httpd lens in Augeas
        Servers sometimes give incorrect defaults
        Note: This problem should be fixed in Augeas 1.0.  Unfortunately,
        Augeas 0.10 appears to be the most popular version currently.

        z*.augnewz	*.augsavez*.dpkg-distz
*.dpkg-bakz
*.dpkg-newz
*.dpkg-oldz	*.rpmsavez*.rpmnewz*~z
/*.augsavez/*~z/*/*augsavez/*/*~z/*/*/*.augsavez/*/*/*~rs   z/augeas/load/Httpd/excl[%d]N)r   rw   r   r=   rL   )r+   Zexclrp   Zexcludedr-   r-   r.   r!   k  s    ø
zApacheParser.standardize_exclc                 C   sD   | j d }tj | jd¡}tj |¡r0|}|}n|}|}|||dœS )zSet default location for directives.

        Locations are given as file_paths
        .. todo:: Make sure that files are included

        r   z
ports.conf)ÚdefaultÚlistenr½   )r   r   r   rS   r   Úisfile)r+   r¿   ZtemprÀ   r½   r-   r-   r.   r$   ‹  s    
zApacheParser._set_locationsc                 C   sL   g d¢}|D ]0}t j t j | j|¡¡rt j | j|¡  S qt d¡‚dS )z(Find the Apache Configuration Root file.)zapache2.confz
httpd.confzconf/httpd.confz!Could not find configuration rootN)r   r   rÁ   rS   r   r   r3   )r+   Úlocationr½   r-   r-   r.   r   ž  s
    zApacheParser._find_config_root)Nr   N)F)F)N)NNT)3Ú__name__Ú
__module__Ú__qualname__Ú__doc__ÚreÚcompiler”   r£   r/   r   r<   r   rI   rK   rB   rD   r]   r`   rb   r"   r    rk   rl   ra   rz   r€   r}   r   rX   r‡   r‰   r‹   r)   r’   ri   r™   rŒ   r›   r¢   r   r¨   r   rn   r¹   r¶   r°   r±   r²   r!   r$   r   r-   r-   r-   r.   r      s`   
  ÿ
6'


	

L,	 r   c                 C   s   d  dd„ t | ¡D ƒ¡S )a`  Returns case insensitive regex.

    Returns a sloppy, but necessary version of a case insensitive regex.
    Any string should be able to be submitted and the string is
    escaped and then made case insensitive.
    May be replaced by a more proper /i once augeas 1.0 is widely
    supported.

    :param str string: string to make case i regex

    rA   c                 s   s2   | ]*}|  ¡ r&d | ¡  | ¡  d n|V  qdS )r   r   N)ÚisalphaÚupperr   )rM   re   r-   r-   r.   rP   ³  s   ÿzcase_i.<locals>.<genexpr>)rS   rÇ   Úescape)Ústringr-   r-   r.   rU   §  s    
ÿrU   c                 C   s   d|  S )zTReturn augeas path for full filepath.

    :param str file_path: Full filepath

    z/files%sr-   )Z	file_pathr-   r-   r.   rW   ·  s    rW   )rÆ   r%   r®   ZloggingrÇ   r¬   rg   Zacme.magic_typingr   r   Zcertbotr   Zcertbot.compatr   Zcertbot_apache._internalr   r   Z	getLoggerrÃ   rQ   Úobjectr   rU   rW   r-   r-   r-   r.   Ú<module>   s.   
       