U
    ÔZjÚs  ã                   @   sz  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	 ddl
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mZ e e¡ZG dd	„ d	eƒZd
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Z%e&dd e%d!d"gƒZ'd#Z(d$d%e(gZ)d&d'„ Z*d(d)„ Z+d=d*d+„Z,d,d-„ Z-d.d/„ Z.d0d1„ Z/d2d3„ Z0d4d5„ Z1d6d7„ Z2d8d9„ Z3d:d;„ Z4dS )>z>NginxParser is a member object of the NginxConfigurator class.é    N)Úerrors)Úos)Úobj)Únginxparser)ÚUnionÚDictÚSetÚAnyÚListÚTuplec                   @   s¼   e Zd Z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„ Zd/dd„Zdd„ Zdd „ Zd0d!d"„Zd1d#d$„Zd2d&d'„Zd(d)„ Zd*d+„ Zd3d,d-„Zd%S )4ÚNginxParserzñClass handles the fine details of parsing the Nginx Configuration.

    :ivar str root: Normalized absolute path to the server root
        directory. Without trailing slash.
    :ivar dict parsed: Mapping of file paths to parsed trees

    c                 C   s*   i | _ tj |¡| _|  ¡ | _|  ¡  d S ©N)Úparsedr   ÚpathÚabspathÚrootÚ_find_config_rootÚconfig_rootÚload)Úselfr   © r   úW/var/www/html/TRUCKING_PROJECT/venv/lib/python3.8/site-packages/certbot_nginx/parser.pyÚ__init__   s    
zNginxParser.__init__c                 C   s   i | _ |  | j¡ dS )z/Loads Nginx files into a parsed tree.

        N)r   Ú_parse_recursivelyr   )r   r   r   r   r   (   s    zNginxParser.loadc                 C   sÆ   |   |¡}|  |¡}|D ]¨}|D ]ž}t|ƒr<|  |d ¡ q |d dgksX|d dgkr |d D ]\}t|ƒr||  |d ¡ q`|d dgkr`|d dgkr`|d D ]}t|ƒr |  |d ¡ q q`q qdS )a  Parses nginx config files recursively by looking at 'include'
        directives inside 'http' and 'server' blocks. Note that this only
        reads Nginx files that potentially declare a virtual host.

        :param str filepath: The path to the files to parse, as a glob

        é   r   ÚhttpÚserverN)Úabs_pathÚ_parse_filesÚ_is_include_directiver   )r   ÚfilepathÚtreesÚtreeÚentryZsubentryZserver_entryr   r   r   r   /   s    	

zNginxParser._parse_recursivelyc                 C   s0   t j |¡s$t j t j | j|¡¡S t j |¡S )záConverts a relative path to an absolute path relative to the root.
        Does nothing for paths that are already absolute.

        :param str path: The path
        :returns: The absolute path
        :rtype: str

        )r   r   ÚisabsÚnormpathÚjoinr   )r   r   r   r   r   r   K   s    	zNginxParser.abs_pathc           	      C   sn   |   ¡ }i }|D ]X}|| D ]J\}}t|ƒ}|d D ]0}| ¡ }||krR|j||< |jp^|| ||< q4qq|S )zSBuilds a map from address to whether it listens on ssl in any server block
        Úaddrs)Ú_get_raw_serversÚ_parse_server_rawÚnormalized_tupleÚssl)	r   ÚserversÚaddr_to_sslÚfilenamer   Ú_Úparsed_serverÚaddrZ
addr_tupler   r   r   Ú_build_addr_to_sslX   s    
zNginxParser._build_addr_to_sslc                    sz   i }| j D ]j}| j | }g ||< || ‰ t|dd„ ‡ fdd„ƒ t|| ƒD ]&\}\}}|  |¡}||f|| |< qLq
|S )z0Get a map of unparsed all server blocks
        c                 S   s   t | ƒdko| d dgkS )Né   r   r   ©Úlen)Úxr   r   r   Ú<lambda>u   ó    z.NginxParser._get_raw_servers.<locals>.<lambda>c                    s   ˆ   | d |f¡S )Nr   )Úappend)r6   Úy©Zsrvr   r   r7   v   r8   )r   Ú_do_for_subarrayÚ	enumerateÚ_get_included_directives)r   r,   r.   r"   Úir   r   Ú
new_serverr   r;   r   r(   i   s    



ÿ
zNginxParser._get_raw_serversc           	      C   sn   d}|   ¡ }g }|D ]J}|| D ]<\}}t|ƒ}t ||d |d ||d ||¡}| |¡ q q|  |¡ |S )a=  Gets list of all 'virtual hosts' found in Nginx configuration.
        Technically this is a misnomer because Nginx does not have virtual
        hosts, it has 'server blocks'.

        :returns: List of :class:`~certbot_nginx.obj.VirtualHost`
            objects found in configuration
        :rtype: list

        Tr'   r+   Únames)r(   r)   r   ZVirtualHostr9   Ú_update_vhosts_addrs_ssl)	r   Zenabledr,   Úvhostsr.   r   r   r0   Úvhostr   r   r   Ú
get_vhosts~   s"    ú
zNginxParser.get_vhostsc                 C   s<   |   ¡ }|D ]*}|jD ]}|| ¡  |_|jrd|_qqdS )zPUpdate a list of raw parsed vhosts to include global address sslishness
        TN)r2   r'   r*   r+   )r   rC   r-   rD   r1   r   r   r   rB   Ÿ   s    
z$NginxParser._update_vhosts_addrs_sslc              
   C   sh   t  |¡}|D ]T}t|ƒrt |  |d ¡¡}|D ].}z| | j| ¡ W q2 tk
r^   Y q2X q2q|S )z¼Returns array with the "include" directives expanded out by
        concatenating the contents of the included file to the block.

        :param list block:
        :rtype: list

        r   )ÚcopyÚdeepcopyr   Úglobr   Úextendr   ÚKeyError)r   ÚblockÚresultÚ	directiveZincluded_filesZinclr   r   r   r>   ©   s    
ÿ
z$NginxParser._get_included_directivesFc           	      C   s´   t   |¡}g }|D ]œ}|| jkr&|s&qz6t|ƒ�$}t |¡}|| j|< | |¡ W 5 Q R X W q tk
r|   t d|¡ Y q t	j
k
r¬ } zt d||¡ W 5 d}~X Y qX q|S )zçParse files from a glob

        :param str filepath: Nginx config file path
        :param bool override: Whether to parse a file that has been parsed
        :returns: list of parsed tree structures
        :rtype: list

        zCould not open file: %sú"Could not parse file: %s due to %sN)rH   r   Úopenr   r   r9   ÚIOErrorÚloggerÚwarningÚ	pyparsingZParseExceptionÚdebug)	r   r    ÚoverrideÚfilesr!   ÚitemÚ_filer   Úerrr   r   r   r   ½   s    	



"zNginxParser._parse_filesc                 C   sJ   dg}|D ]0}t j t j | j|¡¡r
t j | j|¡  S q
t d¡‚dS )z)Return the Nginx Configuration Root file.z
nginx.confz9Could not find Nginx root configuration file (nginx.conf)N)r   r   Úisfiler&   r   r   ZNoInstallationError)r   ÚlocationÚnamer   r   r   r   Ø   s    ÿzNginxParser._find_config_rootÚtmpTc              
   C   sœ   | j D ]�}| j | }|r(|tjj | }zL|r:| ¡ s:W qt |¡}t d||¡ t	|dƒ�}| 
|¡ W 5 Q R X W q tk
r”   t d|¡ Y qX qdS )zûDumps parsed configurations into files.

        :param str ext: The file extension to use for the dumped files. If
            empty, this overrides the existing conf files.
        :param bool lazy: Only write files that have been modified

        z!Writing nginx conf tree to %s:
%sÚwz#Could not open file for writing: %sN)r   r   r   ÚextsepZis_dirtyr   ÚdumpsrQ   rT   rO   ÚwriterP   Úerror)r   ÚextZlazyr.   r"   ÚoutrX   r   r   r   Úfiledumpã   s    	


zNginxParser.filedumpc                 C   s   |   ¡ }t|ƒ}t||ƒ |S )z­Parses a list of server directives, accounting for global address sslishness.

        :param list server: list of directives in a server block
        :rtype: dict
        )r2   r)   Ú_apply_global_addr_ssl)r   r   r-   r0   r   r   r   Úparse_serverû   s    
zNginxParser.parse_serverc                 C   s*   |j }|D ]}|sq
q
t|ƒr
 dS q
dS )zÓDoes vhost have ssl on for all ports?

        :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost in question

        :returns: True if 'ssl on' directive is included
        :rtype: bool

        TF)ÚrawÚ_is_ssl_on_directive)r   rD   r   rM   r   r   r   Úhas_ssl_on_directive  s    	z NginxParser.has_ssl_on_directivec                 C   s   |   |t t||¡¡ dS )aê  Add directives to the server block identified by vhost.

        This method modifies vhost to be fully consistent with the new directives.

        ..note :: It's an error to try and add a nonrepeatable directive that already
            exists in the config block with a conflicting value.

        ..todo :: Doesn't match server blocks whose server_name directives are
            split across multiple conf files.

        :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost
            whose information we use to match on
        :param list directives: The directives to add
        :param bool insert_at_top: True if the directives need to be inserted at the top
            of the server block instead of the bottom

        N)Ú_modify_server_directivesÚ	functoolsÚpartialÚ_add_directives©r   rD   Ú
directivesÚinsert_at_topr   r   r   Úadd_server_directives  s    ÿz!NginxParser.add_server_directivesc                 C   s   |   |t t||¡¡ dS )aR  Add or replace directives in the server block identified by vhost.

        This method modifies vhost to be fully consistent with the new directives.

        ..note :: When a directive with the same name already exists in the
        config block, the first instance will be replaced. Otherwise, the directive
        will be appended/prepended to the config block as in add_server_directives.

        ..todo :: Doesn't match server blocks whose server_name directives are
            split across multiple conf files.

        :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost
            whose information we use to match on
        :param list directives: The directives to add
        :param bool insert_at_top: True if the directives need to be inserted at the top
            of the server block instead of the bottom

        N)rk   rl   rm   Ú_update_or_add_directivesro   r   r   r   Úupdate_or_add_server_directives-  s    ÿz+NginxParser.update_or_add_server_directivesNc                 C   s   |   |t t||¡¡ dS )ab  Remove all directives of type directive_name.

        :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost
            to remove directives from
        :param string directive_name: The directive type to remove
        :param callable match_func: Function of the directive that returns true for directives
            to be deleted.
        N)rk   rl   rm   Ú_remove_directives)r   rD   Údirective_nameÚ
match_funcr   r   r   Úremove_server_directivesC  s    	ÿz$NginxParser.remove_server_directivesc                 C   s<   |   |¡}|  |¡}|d |_|d |_|d |_||_d S )Nr'   r+   rA   )r>   rg   r'   r+   rA   rh   )r   rD   Zdirectives_listr@   r0   r   r   r   Ú%_update_vhost_based_on_new_directivesO  s    




z1NginxParser._update_vhost_based_on_new_directivesc              
   C   s¤   |j }z^| j| }|jD ]}|| }qt|tƒr<t|ƒdkrFt d¡‚|d }||ƒ |  ||¡ W n: tjk
rž } zt d|t	|ƒf ¡‚W 5 d }~X Y nX d S )Nr3   zNot a server block.r   zProblem in %s: %s)
Úfilepr   r   Ú
isinstanceÚlistr5   r   ÚMisconfigurationErrorry   Ústr)r   rD   Z
block_funcr.   rL   ÚindexrY   r   r   r   rk   W  s    



z%NginxParser._modify_server_directivesc                 C   s2  t  |¡}| j|j }|jdd… D ]}|| }q$t  ||jd  ¡}|dk	r”t g ¡}|d D ]}	|	r`|	d |kr`| |	¡ q`||d< |  ||¡ | |¡ t	|ƒd |jd< |�r.|j
D ]}
d|
_d|
_q¼||jd  d D ]L}	|	rà|	d dkràtdƒ}|D ]*}dd	„ |	D ƒ}||k�r |	| |¡= �q qà|S )
ar  Duplicate the vhost in the configuration files.

        :param :class:`~certbot_nginx.obj.VirtualHost` vhost_template: The vhost
            whose information we copy
        :param bool remove_singleton_listen_params: If we should remove parameters
            from listen directives in the block that can only be used once per address
        :param list only_directives: If it exists, only duplicate the named directives. Only
            looks at first level of depth; does not expand includes.

        :returns: A vhost object for the newly created vhost
        :rtype: :class:`~certbot_nginx.obj.VirtualHost`
        Néÿÿÿÿr   r   FÚlisten)Zdefault_serverÚdefaultZsetfibZfastopenÚbacklogZrcvbufZsndbufZaccept_filterÚdeferredÚbindÚipv6onlyZ	reuseportZso_keepalivec                 S   s   g | ]}|  d ¡d ‘qS )ú=r   )Úsplit©Ú.0r6   r   r   r   Ú
<listcomp>–  s     z/NginxParser.duplicate_vhost.<locals>.<listcomp>)rF   rG   r   rz   r   r   ÚUnspacedListr9   ry   r5   r'   r‚   r†   Úsetr   )r   Zvhost_templateZremove_singleton_listen_paramsZonly_directivesZ	new_vhostZenclosing_blockr   Zraw_in_parsedZnew_directivesrM   r1   ÚexcludeÚparamÚkeysr   r   r   Úduplicate_vhostf  s4    





zNginxParser.duplicate_vhost)F)r]   T)F)F)N)FN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r   r   r2   r(   rE   rB   r>   r   r   re   rg   rj   rr   rt   rx   ry   rk   r‘   r   r   r   r   r      s.   
!





  ÿr   c              
   C   sŠ   | d k	r†z,t | ƒ�}t |¡W  5 Q R £ W S Q R X W nP tk
rT   t d| ¡ Y n2 tjk
r„ } zt d| |¡ W 5 d }~X Y nX g S )Nz"Missing NGINX TLS options file: %srN   )	rO   r   r   rP   rQ   rR   rS   ZParseBaseExceptionrT   )Zssl_optionsrX   rY   r   r   r   Ú_parse_ssl_optionsœ  s    
" r–   c                 C   sT   |dkrg }t | tƒrP|| ƒr*|| |ƒ n&t| ƒD ]\}}t|||||g ƒ q2dS )a(  Executes a function for a subarray of a nested array if it matches
    the given condition.

    :param list entry: The list to iterate over
    :param function condition: Returns true iff func should be executed on item
    :param function func: The function to call for each matching item

    N)r{   r|   r=   r<   )r#   Ú	conditionÚfuncr   r   rW   r   r   r   r<   §  s    	
r<   c                 C   sÔ   g }g }g }g }|D ]^}t | |ƒr.| |¡ qt| |dƒrF| |¡ qt| |dƒr^| |¡ qt| |ƒr| |¡ q|rŒt|td�}d|fS |r¤t|td�}d|fS |r¼t|td�}d|fS |rÐ|d }d|fS d	S )
ah  Finds the best match for target_name out of names using the Nginx
    name-matching rules (exact > longest wildcard starting with * >
    longest wildcard ending with * > regex).

    :param str target_name: The name to match
    :param set names: The candidate server names
    :returns: Tuple of (type of match, the name that matched)
    :rtype: tuple

    TF)ÚkeyÚexactÚwildcard_startÚwildcard_endr   Úregex)NN)Ú_exact_matchr9   Ú_wildcard_matchÚ_regex_matchÚminr5   Úmax)Útarget_namerA   rš   r›   rœ   r�   r\   Úmatchr   r   r   Úget_best_matchº  s4    

r¥   c                 C   s   | |kpd|  |kS )NÚ.r   )r£   r\   r   r   r   rž   è  s    rž   c                 C   st   |dkrdS |   d¡}|  d¡}|s4| ¡  | ¡  | d¡}|dkrR|dkrRdS d |¡} d |¡}|  d| ¡S )NÚ*Tr¦   r   Ú F)rˆ   ÚreverseÚpopr&   Úendswith)r£   r\   ÚstartÚpartsZmatch_partsÚfirstr   r   r   rŸ   ì  s    




rŸ   c                 C   sZ   t |ƒdk s|d dkrdS z t |dd … ¡}t || ¡W S  tjk
rT   Y dS X d S )Nr3   r   ú~Fr   )r5   ÚreÚcompiler¤   rb   )r£   r\   r�   r   r   r   r      s    r    c                 C   s2   t | tƒo0t| ƒdko0| d dko0t | d tjƒS )z¬Checks if an nginx parsed entry is an 'include' directive.

    :param list entry: the parsed entry
    :returns: Whether it's an 'include' directive
    :rtype: bool

    r3   r   Úincluder   )r{   r|   r5   ÚsixÚstring_types©r#   r   r   r   r     s    

ÿ
ÿþr   c                 C   s.   t | tƒo,t| ƒdko,| d dko,| d dkS )zªChecks if an nginx parsed entry is an 'ssl on' directive.

    :param list entry: the parsed entry
    :returns: Whether it's an 'ssl on' directive
    :rtype: bool

    r3   r   r+   r   Úon)r{   r|   r5   rµ   r   r   r   ri     s    

ÿ
ÿ
þri   c                 C   s:   | D ]}t |||ƒ q|r6d|d kr6| t d¡¡ dS )z"Adds directives to a config block.Ú
r€   N)Ú_add_directiver9   r   rŒ   ©rp   rq   rK   rM   r   r   r   rn   +  s    rn   c                 C   s:   | D ]}t |||ƒ q|r6d|d kr6| t d¡¡ dS )z.Adds or replaces directives in a config block.r·   r€   N)Ú_update_or_add_directiver9   r   rŒ   r¹   r   r   r   rs   2  s    rs   r²   Úserver_namer�   ZrewriteÚ
add_headerz managed by Certbotú ú#c                 C   s²   |d t | ƒk r| |d  nd}t|tƒrv|rvt |ƒdkrV|d dkrVt|d krVdS t|tjƒrn|jd }n|d }|  |d tdd… ¡ |dk	r®d|kr®|  |d d¡ dS )	zñAdd a ``#managed by Certbot`` comment to the end of the line at location.

    :param list block: The block containing the directive to be commented
    :param int location: The location within ``block`` of the directive to be commented
    r   Nr3   éþÿÿÿr¾   r€   r   r·   )	r5   r{   r|   ÚCOMMENTr   rŒ   ÚspacedÚinsertÚCOMMENT_BLOCK)rK   r[   Z
next_entryr   r   r   Úcomment_directive?  s     $rÄ   c           
      C   s®   d  |¡}| | }t g ¡}| |¡ t |¡}|d | }t |¡}d}	|d jd |d d krhd}	|d j |	d¡ |d j d¡ t |¡}t |¡}|d | |< dS )z=Comment out the line at location, with a note of explanation.z duplicated in {0}z #r   r   z# ú;N)Úformatr   rŒ   r9   r`   ÚloadsrÁ   rÂ   )
rK   r[   Zinclude_locationZcomment_messagerM   Znew_dir_blockZdumpedZ	commentedZnew_dirZinsert_locationr   r   r   Ú_comment_out_directiveR  s    






rÈ   c                    s   t ‡ ‡fdd„t| ƒD ƒdƒS )zeFinds the index of the first instance of directive_name in block.
       If no line exists, use None.c                 3   s6   | ].\}}|r|d  ˆ krˆdks*ˆ|ƒr|V  qdS )r   Nr   )rŠ   r   Úline©rv   rw   r   r   Ú	<genexpr>l  s       ÿz!_find_location.<locals>.<genexpr>N)Únextr=   )rK   rv   rw   r   rÊ   r   Ú_find_locationi  s    ÿrÍ   c                 C   s   t | ƒdkp| d dkS )z;Is this directive either a whitespace or comment directive?r   r¾   r4   ©rM   r   r   r   Ú_is_whitespace_or_commento  s    rÏ   c                 C   sJ  t |tjƒst |¡}t|ƒr,|  |¡ d S t| |d ƒ}|d }dd„ }d}|tkrÈt|d ƒ}|D ]`}t| |d ƒ}	|d }
t|ƒsf||	|
ƒsf| |	 |kr¶t 	| 
|| |	 ¡¡‚qft| |	|d ƒ qf|||ƒ�r"|�r|  dt d¡¡ |  d|¡ t| dƒ n|  |¡ t| t| ƒd ƒ n$| | |k�rFt 	| 
|| | ¡¡‚d S )Nr   c                 S   s   | dkpt |tjƒo|tkS )z, Can we append this directive to the block? N)r{   r³   r´   ÚREPEATABLE_DIRECTIVES)ÚlocÚdir_namer   r   r   Ú
can_append„  s    ÿz"_add_directive.<locals>.can_appendz<tried to insert directive "{0}" but found conflicting "{1}".r   r·   )r{   r   rŒ   rÏ   r9   rÍ   ÚINCLUDEr–   r   r}   rÆ   rÈ   rÂ   rÄ   r5   )rK   rM   rq   r[   rv   rÓ   Zerr_fmtZincluded_directivesZincluded_directiveZincluded_dir_locZincluded_dir_namer   r   r   r¸   s  s>    

ÿ
ÿ
r¸   c                 C   s   || |< t | |ƒ d S r   )rÄ   )rK   rM   r[   r   r   r   Ú_update_directiveª  s    rÕ   c                 C   sb   t |tjƒst |¡}t|ƒr,|  |¡ d S t| |d ƒ}|d k	rRt| ||ƒ d S t| ||ƒ d S )Nr   )r{   r   rŒ   rÏ   r9   rÍ   rÕ   r¸   )rK   rM   rq   r[   r   r   r   rº   ®  s    

rº   c                 C   s   d| kot | kS )Nr¾   )rÀ   rÎ   r   r   r   Ú_is_certbot_comment¿  s    rÖ   c                 C   sP   t || |d�}|dkrdS |d t|ƒk rDt||d  ƒrD||d = ||= q dS )zYRemoves directives of name directive_name from a config block if match_func matches.
    )rw   Nr   )rÍ   r5   rÖ   )rv   rw   rK   r[   r   r   r   ru   Â  s     
ru   c                 C   s.   |d D ] }| |  ¡  |_|jrd|d< qdS )zCApply global sslishness information to the parsed server block
    r'   Tr+   N)r*   r+   )r-   r0   r1   r   r   r   rf   Î  s    rf   c                 C   s¾   t ƒ }d}t ƒ }d}| D ]„}|s"q|d dkrbtj d |dd… ¡¡}|rœ| |¡ |jrœd}q|d dkrŒ| d	d
„ |dd… D ƒ¡ qt|ƒrd}d}q|r²|D ]
}d|_q¦|||dœS )zxParses a list of server directives.

    :param list server: list of directives in a server block
    :rtype: dict

    Fr   r�   r½   r   NTr»   c                 s   s   | ]}|  d ¡V  qdS )z"'N)Ústripr‰   r   r   r   rË   í  s     z$_parse_server_raw.<locals>.<genexpr>)r'   r+   rA   )	r�   r   ZAddrÚ
fromstringr&   Úaddr+   Úupdateri   )r   r'   r+   rA   Zapply_ssl_to_all_addrsrM   r1   r   r   r   r)   Ö  s2    
ýr)   )N)N)5r•   rF   rl   rH   Úloggingr°   rS   r³   Zcertbotr   Zcertbot.compatr   Zcertbot_nginxr   r   Zacme.magic_typingr   r   r   r	   r
   r   Ú	getLoggerr’   rQ   Úobjectr   r–   r<   r¥   rž   rŸ   r    r   ri   rn   rs   rÔ   r�   rÐ   rÀ   rÃ   rÄ   rÈ   rÍ   rÏ   r¸   rÕ   rº   rÖ   ru   rf   r)   r   r   r   r   Ú<module>   sT    
   

.

7