o
    cxR                     @   s.  d 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 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 ddlmZ ddlmZ ddlmZ ddlmZ G dd dejZG dd deZG dd deZG dd deZeje eje eje dS )aU  
Augeas implementation of the ParserNode interfaces.

Augeas works internally by using XPATH notation. The following is a short example
of how this all works internally, to better understand what's going on under the
hood.

A configuration file /etc/apache2/apache2.conf with the following content:

    # First comment line
    # Second comment line
    WhateverDirective whatevervalue
    <ABlock>
        DirectiveInABlock dirvalue
    </ABlock>
    SomeDirective somedirectivevalue
    <ABlock>
        AnotherDirectiveInABlock dirvalue
    </ABlock>
    # Yet another comment


Translates over to Augeas path notation (of immediate children), when calling
for example: aug.match("/files/etc/apache2/apache2.conf/*")

[
    "/files/etc/apache2/apache2.conf/#comment[1]",
    "/files/etc/apache2/apache2.conf/#comment[2]",
    "/files/etc/apache2/apache2.conf/directive[1]",
    "/files/etc/apache2/apache2.conf/ABlock[1]",
    "/files/etc/apache2/apache2.conf/directive[2]",
    "/files/etc/apache2/apache2.conf/ABlock[2]",
    "/files/etc/apache2/apache2.conf/#comment[3]"
]

Regardless of directives name, its key in the Augeas tree is always "directive",
with index where needed of course. Comments work similarly, while blocks
have their own key in the Augeas XPATH notation.

It's important to note that all of the unique keys have their own indices.

Augeas paths are case sensitive, while Apache configuration is case insensitive.
It looks like this:

    <block>
        directive value
    </block>
    <Block>
        Directive Value
    </Block>
    <block>
        directive value
    </block>
    <bLoCk>
        DiReCtiVe VaLuE
    </bLoCk>

Translates over to:

[
    "/files/etc/apache2/apache2.conf/block[1]",
    "/files/etc/apache2/apache2.conf/Block[1]",
    "/files/etc/apache2/apache2.conf/block[2]",
    "/files/etc/apache2/apache2.conf/bLoCk[1]",
]
    )Any)cast)Dict)Iterable)List)Optional)Set)Tuple)Union)errors)os)apache_util)
assertions)
interfaces)parser)parsernode_utilc                       sz   e Zd ZdZdeddf fddZdee ddfdd	Zd
ede	d  fddZ
deddfddZdedefddZ  ZS )AugeasParserNodez/ Augeas implementation of ParserNode interface kwargsreturnNc                    s   t |\}}}}t jdi | || _|| _|| _|| _tt	j
| jd| _	z| jd dr=td| jd W d S  tyK   tdw )Naugeasparser
augeaspath/z$Augeas path: {} has a trailing slashzAugeas path is required )utilparsernode_kwargssuper__init__ancestorfilepathdirtymetadatar   r   ApacheParsergetendswithr   PluginErrorformatKeyError)selfr   r   r   r   r    	__class__r   R/opt/certbot/lib/python3.10/site-packages/certbot_apache/_internal/augeasparser.pyr   Y   s(   

zAugeasParserNode.__init__msgc                 C   s   | j | d S N)r   save)r'   r+   r   r   r*   r-   m   s   zAugeasParserNode.savenamec                 C   sb   g }| j d }	 |dd }|r|dkr	 |S | |}|jdur0|j | kr0|| q)z
        Searches for ancestor BlockNodes with a given name.

        :param str name: Name of the BlockNode parent to search for

        :returns: List of matching ancestor nodes.
        :rtype: list of AugeasParserNode
        r   Tr   r   z/filesN)r    
rpartition_create_blocknoder.   lowerappend)r'   r.   	ancestorsparentancr   r   r*   find_ancestorsp   s   



zAugeasParserNode.find_ancestorspathAugeasBlockNodec                 C   sX   |  |}| j|d}t|}|du rtd| d| j|}t||tj||dS )z
        Helper function to create a BlockNode from Augeas path. This is used by
        AugeasParserNode.find_ancestors and AugeasBlockNode.
        and AugeasBlockNode.find_blocks

        r   r   NNo file path found for vhost: .)r.   enabledr   r   r    )	_aug_get_namer   r   get_file_path
ValueErrorparsed_in_originalr8   r   PASS)r'   r7   r.   r    	file_pathr<   r   r   r*   r0      s   

z"AugeasParserNode._create_blocknodec                 C   s4   |d dkr|dd }| dd }| dd S )z]
        Helper function to get name of a configuration block or variable from path.
        r   N[r   )split)r'   r7   r.   r   r   r*   r=      s   zAugeasParserNode._aug_get_name)__name__
__module____qualname____doc__r   r   r   strr-   r   r6   r0   r=   __classcell__r   r   r(   r*   r   V   s    r   c                       s<   e Zd ZdZdeddf fddZdedefdd	Z  ZS )
AugeasCommentNodez0 Augeas implementation of CommentNode interface r   r   Nc                    s*   t |\}}t jdi | || _d S Nr   )r   commentnode_kwargsr   r   comment)r'   r   rO   r(   r   r*   r      s   
zAugeasCommentNode.__init__otherc                 C   sL   t || jr$| j|jko#| j|jko#| j|jko#| j|jko#| j|jkS dS NF)
isinstancer)   rO   r   r   r   r    r'   rP   r   r   r*   __eq__   s   



zAugeasCommentNode.__eq__)	rF   rG   rH   rI   r   r   boolrT   rK   r   r   r(   r*   rL      s    rL   c                       s   e Zd ZdZdeddf fddZdedefdd	Zd
ee	 ddfddZ
edee	df fddZde	dee	 fddZ  ZS )AugeasDirectiveNodez2 Augeas implementation of DirectiveNode interface r   r   Nc                    sF   t |\}}}}t jdi | || _|| _|r!| | d S d S rM   )r   directivenode_kwargsr   r   r.   r<   set_parameters)r'   r   r.   
parametersr<   r(   r   r*   r      s   zAugeasDirectiveNode.__init__rP   c                 C   sd   t || jr0| j|jko/| j|jko/| j|jko/| j|jko/| j|jko/| j|jko/| j|jkS dS rQ   )	rR   r)   r.   r   rY   r<   r   r   r    rS   r   r   r*   rT      s   





zAugeasDirectiveNode.__eq__rY   c                 C   st   |  | jd }|D ]}d| jd }| jj| q
t|D ]\}}d| jd |d }| jj|| q dS )z
        Sets parameters of a DirectiveNode or BlockNode object.

        :param list parameters: List of all parameters for the node to set.
        r   z	{}/arg[1]z
{}/arg[{}]   N)_aug_get_paramsr    r%   r   augremove	enumerateset)r'   rY   orig_params_
param_pathpiparamr   r   r*   rX      s   z"AugeasDirectiveNode.set_parameters.c                 C   s   t | | jd S )z
        Fetches the parameters from Augeas tree, ensuring that the sequence always
        represents the current state

        :returns: Tuple of parameters for this DirectiveNode
        :rtype: tuple:
        r   )tupler[   r    r'   r   r   r*   rY      s   	zAugeasDirectiveNode.parametersr7   c                    s2    j j|d } fdd|D }dd |D S )zCHelper function to get parameters for DirectiveNodes and BlockNodes/argc                    s   g | ]} j |qS r   )r   get_arg).0apathrf   r   r*   
<listcomp>       z7AugeasDirectiveNode._aug_get_params.<locals>.<listcomp>c                 S   s   g | ]}|d ur|qS r,   r   )ri   argr   r   r*   rk      rl   )r   r\   match)r'   r7   	arg_pathsargsr   rf   r*   r[      s   z#AugeasDirectiveNode._aug_get_params)rF   rG   rH   rI   r   r   rU   rT   r   rJ   rX   propertyr	   rY   r   r[   rK   r   r   r(   r*   rV      s    
rV   c                	       s  e Zd ZdZdeddf fddZdedefdd	Z		d0d
ede	e
e  de	e dd fddZ		d0d
ede	e
e  de	e defddZ	d1dede	e ddfddZd2d
edede
d  fddZd2d
edede
d fddZdede
d fddZd3d!d"Zdee fd#d$Zde
e fd%d&Zd'eddfd(d)Zd'eddfd*d+Zd
edee fd,d-Zd
ede	e deeeef fd.d/Z  ZS )4r8   z. Augeas implementation of BlockNode interface r   r   Nc                    s   t  jdi | d| _d S rM   )r   r   children)r'   r   r(   r   r*   r     s   
zAugeasBlockNode.__init__rP   c                 C   sp   t || jr6| j|jko5| j|jko5| j|jko5| j|jko5| j|jko5| j|jko5| j|jko5| j	|j	kS dS rQ   )
rR   r)   r.   r   rY   rr   r<   r   r   r    rS   r   r   r*   rT     s"   






zAugeasBlockNode.__eq__r.   rY   positionc           
      C   st   |  ||\}}}| j|d}| jj||| t|}|du r)td| | j|}	t	|||	t
j||dS )z0Adds a new BlockNode to the sequence of childrenr9   Nr:   r.   rY   r<   r   r   r    )_aug_resolve_child_positionr   r\   insertr   r>   r   Errorr@   r8   r   rA   
r'   r.   rY   rs   
insertpathrealpathbeforenew_metadatarB   r<   r   r   r*   add_child_block  s$   

zAugeasBlockNode.add_child_blockc           
      C   s   |st d| d|\}}}| j|d}| jj|d| | jj|| t|}|du r8t 	d| | j
|}	t|||	tj||dS )z4Adds a new DirectiveNode to the sequence of childrenz0Directive requires parameters and none were set.	directiver9   Nr:   rt   )r   r$   ru   r   r\   rv   r_   r   r>   rw   r@   rV   r   rA   rx   r   r   r*   add_child_directive3  s*   


z#AugeasBlockNode.add_child_directive rO   rL   c                 C   sX   |  d|\}}}| j|d}| jj|d| | jj|| t|tjt	||dS )z2Adds a new CommentNode to the sequence of childrenz#commentr9   rO   r   r   r    )
ru   r   r\   rv   r_   rL   r   rA   r   r>   )r'   rO   rs   ry   rz   r{   r|   r   r   r*   add_child_commentT  s   
z!AugeasBlockNode.add_child_commentTexcludec                 C   s<   g }|  |}|r| j|}|D ]
}|| | q|S )z<Recursive search of BlockNodes from the sequence of children)_aug_find_blocksr   exclude_dirsr2   r0   )r'   r.   r   nodespathsr7   r   r   r*   find_blocksm  s   
zAugeasBlockNode.find_blocksrV   c                 C   sf   g }| j d}| jj|||d}t }|D ]}|dd }||vr0|| | || q|S )z@Recursive search of DirectiveNodes from the sequence of childrenr   )startr   rg   r   )	r    r"   r   find_dirr_   	partitionr2   _create_directivenodeadd)r'   r.   r   r   ownpath
directivesalready_parsedr~   r   r   r*   find_directivesy  s   
zAugeasBlockNode.find_directivesc                 C   s>   g }| j d}| jj||d}|D ]
}|| | q|S )z
        Recursive search of DirectiveNodes from the sequence of children.

        :param str comment: Comment content to search for.
        r   )r   )r    r"   r   find_commentsr2   _create_commentnode)r'   rO   r   r   commentscomr   r   r*   r     s   zAugeasBlockNode.find_commentschildr   c                 C   s.   | j j|jd std|jd dS )z
        Deletes a ParserNode from the sequence of children, and raises an
        exception if it's unable to do so.
        :param AugeasParserNode child: A node to delete.
        r   zGCould not delete child node, the Augeas path: {} doesn't seem to exist.N)r   r\   r]   r    r   r$   r%   )r'   r   r   r   r*   delete_child  s   zAugeasBlockNode.delete_childc                 C   s
   | j  S )z#Returns a list of unsaved filepaths)r   unsaved_filesrf   r   r   r*   r     s   
zAugeasBlockNode.unsaved_filesc                 C   s<   g }| j j}|D ]}|| D ]}|tj|| qq|S )an  
        Returns a list of file paths that have currently been parsed into the parser
        tree. The returned list may include paths with wildcard characters, for
        example: ['/etc/apache2/conf.d/*.load']

        This is typically called on the root node of the ParserNode tree.

        :returns: list of file paths of files that have been parsed
        )r   existing_pathsr2   r   r7   join)r'   	res_pathsr   	directoryfilenamer   r   r*   parsed_paths  s   zAugeasBlockNode.parsed_pathsr7   c                 C   s2   | j j|}| j |d}t|tjt||dS )z8Helper function to create a CommentNode from Augeas pathr9   r   )r   r\   r"   rL   r   rA   r   r>   )r'   r7   rO   r    r   r   r*   r     s   z#AugeasBlockNode._create_commentnodec                 C   sD   | j |}| j |d}| j t|}t|tj|t||dS )z:Helper function to create a DirectiveNode from Augeas pathr9   )r.   r   r<   r   r    )r   rh   r@   r   r>   rV   r   rA   )r'   r7   r.   r    r<   r   r   r*   r     s   z%AugeasBlockNode._create_directivenodec                    sP   t  }t| jjD ]}| jjd|t f }| fdd|D  q	|S )zoHelper function to perform a search to Augeas DOM tree to search
        configuration blocks with a given namez"/files%s//*[label()=~regexp('%s')]c                    s(   g | ]}   tj|  v r|qS r   )r1   r   r7   basename)ri   r7   r.   r   r*   rk     s    
z4AugeasBlockNode._aug_find_blocks.<locals>.<listcomp>)r_   listr   parser_pathsr\   rn   case_iupdate)r'   r.   	blk_paths
vhost_pathr   r   r   r*   r     s   z AugeasBlockNode._aug_find_blocksc                 C   s   d}| j jd| jd }d}t|D ]\}}|dur#||kr# n| |}||kr0|d7 }qd| jd ||}	| pG|du pG|t|k}
|
rSd| jd }n|dkr^|d }d	}n	d
| jd |}||	|fS )a2  
        Helper function that iterates through the immediate children and figures
        out the insertion path for a new AugeasParserNode.

        Augeas also generalizes indices for directives and comments, simply by
        using "directive" or "comment" respectively as their names.

        This function iterates over the existing children of the AugeasBlockNode,
        returning their insertion path, resulting Augeas path and if the new node
        should be inserted before or after the returned insertion path.

        Note: while Apache is case insensitive, Augeas is not, and blocks like
        Nameofablock and NameOfABlock have different indices.

        :param str name: Name of the AugeasBlockNode to insert, "directive" for
            AugeasDirectiveNode or "comment" for AugeasCommentNode
        :param int position: The position to insert the child AugeasParserNode to

        :returns: Tuple of insert path, resulting path and a boolean if the new
            node should be inserted before it.
        :rtype: tuple of str, str, bool
        Fz{}/*r   rZ   Nz	{}/{}[{}]z{}/*[last()]r   Tz{}/*[{}])r   r\   rn   r%   r    r^   r=   len)r'   r.   rs   r{   all_childrencounterir   	childnameresulting_pathr2   insert_pathr   r   r*   ru     s<   
	
z+AugeasBlockNode._aug_resolve_child_position)NN)r   N)T)r   r   r   N)rF   rG   rH   rI   r   r   rU   rT   rJ   r   r   intr}   rV   r   r   r   r   r   r   r   r   r   r   r   r   r	   ru   rK   r   r   r(   r*   r8     s\    



"

r8   N)rI   typingr   r   r   r   r   r   r   r	   r
   certbotr   certbot.compatr   certbot_apache._internalr   r   r   r   r   r   
ParserNoder   rL   rV   r8   CommentNoderegisterDirectiveNode	BlockNoder   r   r   r*   <module>   s4    B^;  6