U
    ˜­“]•  ã                   @   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Zddlm	Z	m
Z
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ÚSet)Úerrors)Úos)Ú	constantsc                   @   sŽ  e Zd ZdZe d¡ZedddddgƒZdbd
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/„ Zd0d1„ Zdcd3d4„Zddd5d6„Zd7d8„ Zd9d:„ Z d;d<„ Z!ded=d>„Z"dfd@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„ Z/dZd[„ Z0d\d]„ Z1d^d_„ Z2d`da„ Z3dS )gÚ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¡‚tƒ | _i | _i | _	t
j |¡| _d|  ¡ i| _|  | jd ¡ |dkrv|  ¡  |  ¡  |  ¡  | 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ÚsetÚ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.   ú7/usr/lib/python3/dist-packages/certbot_apache/parser.pyÚ__init__%   s4    ÿ
ÿzApacheParser.__init__c                 C   sV   zddl }W n tk
r*   t d¡‚Y nX |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,   r2   r.   r.   r/   r   [   s    ÿþûzApacheParser.init_augeasc              	   C   sv   | j  d¡}|D ]`}| j  |d ¡}|r||k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_errorsl   s    	ûÿz!ApacheParser.check_parsing_errorsc                 C   sR   | j  dd¡ z| j  d¡}W n" tk
r@   | j  d¡ Y dS X | 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   r   r8   Ú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 n4 ttfk
rh   |  |¡ d| j_	t
 d¡‚Y nX | 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noopr5   Ú z-Error saving files, check logs for more info.z/augeas/events/savedr7   N)r   r9   r   r8   Ú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

        r5   z2Unable to save files: %s. Attempted Save Notes: %sz, c                 3   s*   | ]"}|ˆ kr|d t |ƒd … V  qdS )r6   r7   N)r;   )Ú.0Úerr©rG   r.   r/   Ú	<genexpr>ã   s    þz0ApacheParser._log_save_errors.<locals>.<genexpr>N)r   r8   Ú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   sH   |d | j kr| j  |d ¡ d| d | j krD| j  d| d ¡ dS )z%Shortcut for updating parser modules.Ú_moduleZmod_z.cN)r   rF   )r,   Úmod_namer.   r.   r/   Úadd_modú   s    zApacheParser.add_modc                 C   s   t ƒ | _|  ¡  |  ¡  dS )zgReset the loaded modules list. This is called from cleanup to clear
        temporarily loaded modules.N)r   r   Úupdate_modulesr#   rJ   r.   r.   r/   Úreset_modules  s    zApacheParser.reset_modulesc           	      C   s´   t ƒ }|  d¡}t|ƒ}d}t|ƒ|kr¤t|ƒ}tj ||¡D ]b\}}|  |¡}|  |¡}|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: %sr7   )r   r*   Úiterr;   ÚsixZmovesÚzipÚget_argrF   r   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ddg}|  |d¡}z| d¡ W n tk
rJ   Y dS X |D ]@}| d¡dkrvt d	¡ t	 
d
¡‚| d¡}|d ||d < qP|| _dS )zGet Defines from httpd processÚctlú-tú-DZDUMP_RUN_CFGzDefine: ([^ \n]*)Nú=é   z8Unexpected number of equal signs in runtime config dump.z&Error parsing Apache runtime variablesr   r   )Údictr   r)   Úparse_from_subprocessr?   Ú
ValueErrorÚcountrQ   rR   r   r+   Ú	partitionr   )r,   r   Z
define_cmdr@   r8   Úpartsr.   r.   r/   rk   *  s$    ÿ
ÿ
zApacheParser.update_definesc                 C   sP   |   d¡}| j d¡dddg}|  |d¡}|rL|D ]}|  |¡s2|  |¡ q2dS )z>Get includes from httpd process, and add them to DOM if neededrT   rm   rn   ro   ZDUMP_INCLUDESz\(.*\) (.*)N)r*   r   r)   rs   Úparsed_in_currentr    )r,   Ú_Zinc_cmdr@   Úir.   r.   r/   rl   A  s    
ÿ
zApacheParser.update_includesc                 C   s<   | j  d¡dddg}|  |d¡}|D ]}|  | ¡ ¡ q$dS )z:Get loaded modules from httpd process, and add them to DOMrm   rn   ro   ZDUMP_MODULESz(.*)_moduleN)r   r)   rs   r`   Ústrip)r,   Zmod_cmdr@   Úmodr.   r.   r/   ra   Q  s    ÿzApacheParser.update_modulesc                 C   s   |   |¡}t |¡ |¡S )zÙGet values from stdout of subprocess command

        :param list command: Command to run
        :param str regexp: Regexp for parsing

        :returns: list parsed from command output
        :rtype: list

        )Ú_get_runtime_cfgÚreÚcompileÚfindall)r,   ÚcommandZregexpÚstdoutr.   r.   r/   rs   Z  s    

z"ApacheParser.parse_from_subprocessc              	   C   s„   z&t j|t jt jdd}| ¡ \}}W n8 ttfk
r^   t d|tj	¡ t
 d |¡¡‚Y nX |jdkr€t d|¡ t
 d¡‚|S )zwGet runtime configuration info.
        :param command: Command to run

        :returns: stdout from command

        T)r‚   ÚstderrZuniversal_newlinesz2Error running command %s for runtime parameters!%sz-Error accessing loaded Apache parameters: {0}r   z$Error in checking parameter list: %sz^Apache is unable to check whether or not the module is loaded because Apache is misconfigured.)Ú
subprocessÚPopenÚPIPEZcommunicateÚOSErrorrt   rQ   rR   r   Úlinesepr   ZMisconfigurationErrorr:   Ú
returncodeZwarning)r,   r   Úprocr‚   rƒ   r.   r.   r/   r}   g  s2    ü þÿÿ

ÿzApacheParser._get_runtime_cfgc                 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)

        rq   ú/argNéüÿÿÿú/arg[%d])Ú	enumerateÚendswithr\   r;   )r,   r@   ÚargsZfilteredrz   r8   r.   r.   r/   Úfilter_args_num†  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]rq   r‹   r   z
%s/arg[%d]N)Ú	get_ifmodr   Úinsertr   r;   rŽ   )r,   Úaug_conf_pathr“   r   Zif_mod_pathZnvh_pathrz   r’   r.   r.   r/   Úadd_dir_to_ifmodssl¥  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   r8   Úcreate_ifmodÚ
rpartition)r,   r–   r|   Ú	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()]/)r:   r   r•   r   )r,   r–   r|   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]rq   z%s/directive[last()]/arg[%d]z/directive[last()]/argN)r   r   Ú
isinstanceÚlistrŽ   )r,   r–   r“   r   rz   Ú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“   Trq   r   r‹   N)r   r•   r   r›   rœ   rŽ   )r,   r–   rY   r   Z	first_dirrz   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&||k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   r8   r9   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kr\d}ndt|ƒ }g }|D ]l}	| j |	¡ ¡ }
|
d	ks”|
d
k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')]Nr‹   z/*[self::arg=~regexp('%s')]ZincludeZincludeoptional)rW   r   rU   r   r8   Ú_exclude_dirsr9   ÚlowerÚextendr*   Ú_get_include_pathri   )r,   r“   r’   r¡   r   Zregexr@   Z
arg_suffixZordered_matchesr8   Zdir_r.   r.   r/   r*   4  s6    þ
ÿ
 ý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>Œ  s     z-ApacheParser.get_all_args.<locals>.<listcomp>)r   r8   )r,   r8   Zallargsr.   rJ   r/   Úget_all_args€  s    	zApacheParser.get_all_argsc              	   C   sz   | j  |¡}|sdS | d¡}tj |¡}|D ]F}z| || j|dd…  ¡}W q. tk
rr   t	 
d| ¡‚Y q.X 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   r9   r{   r   Úarg_var_interpreterr€   Úreplacer   ÚKeyErrorr   r+   )r,   r8   r   r   Úvarr.   r.   r/   ri   Ž  s    
zApacheParser.get_argc                 C   sJ   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   r   Ú_pass_filterr\   )r,   r@   ÚfiltersZvalid_matchesr8   Úfilter_r.   r.   r/   r£   «  s    zApacheParser._exclude_dirsc                 C   sŒ   |  ¡ }| |d ¡}|dkrˆ| d|¡}| j |d|… d ¡}| d¡rf|dd… |d krvdS n||d k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   Nr‹   ú!rq   FT)r¤   Úfindr   r9   Ú
startswith)r,   r8   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 |¡}tj |¡r\|  tj |d¡¡ n
|  |¡ | 	d¡}t
|ƒD ],\}}tdd„ |D ƒƒrxd|  |¡ ||< qxd |¡}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   r	   c                 s   s   | ]}|t jkV  qd S )N)r   Úfnmatch_chars)rM   Úcharr.   r.   r/   rP      s     z1ApacheParser._get_include_path.<locals>.<genexpr>z* [label()=~regexp('%s')])r{   r³   r   r   ÚnormpathrS   r   Úisdirr    ÚsplitrŽ   Ú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
        http://apache2.sourcearchive.com/documentation/2.2.16-6/apr__fnmatch_8h_source.html

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

        :returns: regex suitable for augeas
        :rtype: str

        )é   r7   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   r8   Ú_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/   rx   ;  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_originalG  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)ÚkeysrÀ   r   r   rS   )r,   rÊ   ÚpathsZ	directoryÚfilenamer.   r.   r/   rÈ   S  s
    z$ApacheParser._parsed_by_parser_pathsc                 C   sb   z<t j |¡}| jt j |¡ }d|kr.d}nd}|dk}W n tk
rX   d}d}Y nX ||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   r8   r?   Úpop)r,   rÅ   Zremove_basenamesZremove_dirnameÚnameZremove_pathZ
remove_incr.   r.   r/   rÃ   u  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 n0 t
k
r¤   tj 	|¡g| jtj |¡< Y nX 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   r8   r•   r   r   r   r   rY   r\   rZ   r¬   )r,   rÑ   Zlast_includer.   r.   r/   rÄ   „  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/*/*/*~rq   z/augeas/load/Httpd/excl[%d]N)r   rŽ   r   r   rL   )r,   Zexclrz   Zexcludedr.   r.   r/   r"      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   sN   dddg}|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   r4   )r,   ÚlocationrÐ   r.   r.   r/   r   Ó  s
    
zApacheParser._find_config_root)Nr   N)F)F)N)NNT)4Ú__name__Ú
__module__Ú__qualname__Ú__doc__r~   r   rª   r   r´   r0   r   r=   r   rI   rK   rB   rD   r]   r`   rb   r#   r!   rk   rl   ra   rs   r}   r‘   r—   r”   r˜   rX   rž   r    r¢   r*   r¨   ri   r£   r®   r¦   rº   r    rx   rË   rÈ   rÂ   rÃ   rÄ   r"   r%   r   r.   r.   r.   r/   r      s`   
  ÿ
6'
	

	

L5	 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   s0   g | ](}|  ¡ r(d | ¡  | ¡  d n|‘qS )r   r   )ÚisalphaÚupperr¤   )rM   re   r.   r.   r/   r§   è  s    ÿzcase_i.<locals>.<listcomp>)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„   r¾   rg   Zacme.magic_typingr   r   r   Zcertbotr   Zcertbot.compatr   Zcertbot_apacher   Z	getLoggerrÖ   rQ   Úobjectr   rU   rW   r.   r.   r.   r/   Ú<module>   s,   
       N