o
    .jo                     @   sz  d Z ddlZddlZddlmZ ddlmZm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 dd	lmZ dd
lmZmZ ddlmZ ddlmZ ddlmZ ddlmZ e	r^e
Ze e!Z"eG dd dZ#eG dd dZ$eG dd dZ%de&de&fddZ'de&dB ddfddZ(de&dB de)fddZ*deddfd d!Z+d,d%d&Z,d'ede
fd(d)Z-G d*d+ d+Z.dS )-zI
DAV response parsing: base class, result types and XML parse functions.
    N)Iterable)	dataclassfield)TYPE_CHECKINGAnycast)unquote)etree)_Element)FreeBusy)cdavdav)BaseElement)error)to_normal_str)URLc                   @   s@   e Zd ZU dZeed< eedZeee	f ed< dZ
eed< dS )PropfindResultz:Parsed result of a PROPFIND request for a single resource.hrefdefault_factory
properties   statusN)__name__
__module____qualname____doc__str__annotations__r   dictr   r   r   int r!   r!   D/home/thesage/.local/lib/python3.10/site-packages/caldav/response.pyr   !   s
   
 r   c                   @   sF   e Zd ZU dZeed< dZedB ed< dZedB ed< dZe	ed< dS )CalendarQueryResultzRParsed result of a calendar-query or calendar-multiget REPORT for a single object.r   Netagcalendar_datar   r   )
r   r   r   r   r   r   r$   r%   r   r    r!   r!   r!   r"   r#   *   s   
 r#   c                   @   sN   e Zd ZU dZeedZee ed< eedZ	ee
 ed< dZe
dB ed< dS )SyncCollectionResultz*Parsed result of a sync-collection REPORT.r   changeddeletedN
sync_token)r   r   r   r   r   listr'   r#   r   r(   r   r)   r!   r!   r!   r"   r&   4   s
   
 r&   textreturnc                 C   s6   d| v r
|  dd} t| }d|v rtt|j}|S )u   Normalize an href string from a DAV response element.

    Handles the Confluence double-encoding bug (%2540 → %40) and converts
    absolute URLs to path-only strings so callers always work with paths.
    z%2540z%40:)replacer   r   path)r+   r   r!   r!   r"   _normalize_hrefB   s   r0   r   c                    s0    du rdS t  fdddD st dS )z{Validate a status string like "HTTP/1.1 404 Not Found".

    200, 201, 207 and 404 are considered acceptable statuses.
    Nc                 3   s    | ]}| v V  qd S Nr!   ).0coder   r!   r"   	<genexpr>Y   s    z#_validate_status.<locals>.<genexpr>)z 200 z 201 z 207  404 )anyr   ResponseErrorr4   r!   r4   r"   _validate_statusR   s
   
r9   c                 C   sD   | sdS |   }t|dkr zt|d W S  ty   Y dS w dS )zHExtract integer status code from a status string like "HTTP/1.1 200 OK".r         )splitlenr    
ValueError)r   partsr!   r!   r"   _status_to_code]   s   r@   treez_Element | list[_Element]c                 C   sH   | j dkrt| dkr| d j tjj kr| d S | j tjj kr!| S | gS )u   Strip outer elements to reach the multistatus response children.

    The general format is <xml><multistatus><response>…</response>…</multistatus></xml>
    but sometimes the multistatus and/or xml wrapper is absent.
    xmlr   )tagr=   r   MultiStatus)rA   r!   r!   r"   _strip_to_multistatusj   s
   (rE   	propstatslist[_Element]dict[str, Any]c                 C   s   i }| D ];}| tjj}|dur|jrd|jv rq| tjj}|du r&q|D ]}t|dkr7|j||j< q(t|||j< q(q|S )z;Extract properties from propstat elements into a flat dict.Nr6   r   )findr   StatusrC   r+   Propr=   _element_to_value)rF   r   propstatstatus_elempropchildr!   r!   r"   _extract_propertiesw   s   rQ   elemc                 C   sF  t | dkr	| jS | j}|tjjkrdd | D S |tjjkr&dd | D S |tjjkr?dd | D }t |dkr=|d S |S |tjjkrLdd | D S |tj	jkrh| D ]}|jtj
jkre|jre|j  S qTdS g }| D ]&}|jrx||j ql|d	r||d	 qlt |dkr||j qlt |dkr|d S |r|S | S )
z0Convert a complex XML element to a Python value.r   c                 S   s    g | ]}| d r| d qS )name)getr2   rP   r!   r!   r"   
<listcomp>   s     z%_element_to_value.<locals>.<listcomp>c                 S   &   g | ]}|j tjj kr|jr|jqS r!   rC   r   Hrefr+   rU   r!   r!   r"   rV         & c                 S   rW   r!   rX   rU   r!   r!   r"   rV      rZ   r;   c                 S   s   g | ]}|j qS r!   )rC   rU   r!   r!   r"   rV      s    NrS   )r=   r+   rC   r   SupportedCalendarComponentSetCalendarUserAddressSetCalendarHomeSetr   ResourceTypeCurrentUserPrincipalrY   appendrT   )rR   rC   hrefsrP   children_textsr!   r!   r"   rL      s@   

rL   c                   @   s~  e Zd ZU dZdZedB ed< dZeed< dZ	e
ed< dZeed< d	Zeed
< dZeed< dZeed< dZeeeB  dB ed< dZedB ed< dGdddeddfddZe		dHdede
d
edd fddZdGdddeddfddZedefddZdeee B fddZdeddfdd Z	!	"		#dId$d%ZdJd'd(Z dKd*d+Z!dLd-d.Z"dede#eee edB f fd/d0Z$de%fd1d2Z&de%eef fd3d4Z'ed5d6 Z(de%ee%eef f fd7d8Z)de%ee%eef f fd9d:Z*			dMd;ed<e%eef d=ed>edB deee B dB f
d?d@Z+de%ee%eef f fdAdBZ,			dNdCe-e. dB dDe-e dB d>edB de%ee%eef f fdEdFZ/dS )ODAVResponsez
    Base class containing shared response parsing logic.

    This class provides the XML parsing and response extraction methods
    that are common to both sync and async DAV responses.
    NrA   headersr   r    _rawF	huge_treereason	davclientresults_sync_tokenresponseResponser,   c                 C   s   |  || d S r1   )_init_from_response)selfrl   ri   r!   r!   r"   __init__      zDAVResponse.__init__   bodystatus_codec                 C   sh   G dd d}| }||_ ||_|r|jdddnd|_| | }||_d|_d|_d|_|	| |S )u>   Construct a DAVResponse from raw bytes — intended for tests.c                   @   s*   e Zd ZU i Zeed< dZdZdZdZ	dS )z-DAVResponse.from_bytes.<locals>._FakeResponserd   r       re   OKN)
r   r   r   rd   r   r   rt   contentr+   rh   r!   r!   r!   r"   _FakeResponse   s   
 rx   zutf-8r.   )errorsre   N)
rt   rw   decoder+   __new__rg   ri   rj   rk   rn   )clsrs   rt   rg   rx   fakeobjr!   r!   r"   
from_bytes   s   

zDAVResponse.from_bytesc           	   
      sJ  |j | _ |j| _tdt| j   tdt| j  |j| _|| _|r)|j	| _	| j 
dd ddg}g d}t fdd	|D }t fd
d	|D } rd|sd|sd|jdk rd|jrdtd   z	t| j d }W n tttfy{   d}Y nw |dks| jsd| _d| _td nKztj| jtjd| j	dd| _W n( ty   |rtjtjkr|stj}ntj}|dt| j dd |rƂ Y nw tjtjkrttj| jdd t| drt| j t| jtr| j dd| _nt| jtr| j dd| _|j| _zt!|ddp|j"| _"W dS  t#y$   d| _"Y dS w )a  
        Initialize response from an HTTP response object.

        This shared method extracts headers, status, and parses XML content.
        Both DAVResponse and AsyncDAVResponse should call this from their __init__.

        Args:
            response: The HTTP response object from niquests
            davclient: Optional reference to the DAVClient for huge_tree setting
        zresponse headers: zresponse status: zContent-Typere   ztext/xmlzapplication/xml)z
text/plaintext/calendarzapplication/octet-streamc                 3       | ]}  |V  qd S r1   
startswithr2   xcontent_typer!   r"   r5          z2DAVResponse._init_from_response.<locals>.<genexpr>c                 3   r   r1   r   r   r   r!   r"   r5      r   i  zUnexpected content type: zContent-Lengthr   NzNo content deliveredT)remove_blank_textrg   )parserz8Expected some valid XML from the server, but got this: 
)exc_infopretty_printrf   s   
   
z

reason_phrase)$rd   rt   r   logdebugr   rw   rf   ri   rg   rT   r7   r+   r   	weirdnessr    KeyErrorr>   	TypeErrorrA   r	   XML	XMLParser	ExceptionlevelloggingDEBUGinfotostringhasattr
isinstancebytesr.   getattrrh   AttributeError)	ro   rl   ri   	xml_typesno_xml_types
expect_xmlexpect_no_xmlcontent_length_logr!   r   r"   rn      s   
zDAVResponse._init_from_responsec                 C   s,   t | dstjtt| jdd| _t| jS )z,Return the raw response content as a string.rf   Tr   )r   r	   r   r   r
   rA   rf   r   ro   r!   r!   r"   raw=  s   

zDAVResponse.rawc                 C   s
   t | jS )a  
        The general format of inbound data is something like this:

        <xml><multistatus>
            <response>(...)</response>
            <response>(...)</response>
            (...)
        </multistatus></xml>

        but sometimes the multistatus and/or xml element is missing in
        self.tree.  We don't want to bother with the multistatus and
        xml tags, we just want the response list.

        An "Element" in the lxml library is a list-like object, so we
        should typically return the element right above the responses.
        If there is nothing but a response, return it as a list with
        one element.

        (The equivalent of this method could probably be found with a
        simple XPath query, but I'm not much into XPath)
        )rE   rA   r   r!   r!   r"   rE   D  s   
z!DAVResponse._strip_to_multistatusc                 C   s   t | dS )a  
        status is a string like "HTTP/1.1 404 Not Found".  200, 207 and
        404 are considered good statuses.  The SOGo caldav server even
        returns "201 created" when doing a sync-report, to indicate
        that a resource was created after the last sync-token.  This
        makes sense to me, but I've only seen it from SOGo, and it's
        not in accordance with the examples in rfc6578.
        N)r9   )ro   r   r!   r!   r"   validate_status\  s   	zDAVResponse.validate_statusrF   rG   tuple[str | None, str | None]c                 C   sf   d}d}|D ](}| tjj}|du rq|D ]}|jtjjkr#|j}q|jtjjkr-|j}qq||fS )z?Extract (etag, calendar_data) from a list of propstat elements.N)rI   r   rK   rC   r   CalendarDatar+   GetEtag)ro   rF   r$   r%   rM   rO   rP   r!   r!   r"   _extract_calendar_query_propsg  s   z)DAVResponse._extract_calendar_query_propslist[PropfindResult]c                 C   s   | j dkrg S | j dvrtd| j  | jdu rg S g }|  D ]0}|jtjjkr0|j| _	q"|jtj
jkr8q"| |\}}}|t|t||rMt|ndd q"|S )z9Parse the response body as a PROPFIND multi-status reply.  r   rr   zPROPFIND failed with status Nr   )r   r   r   )r   r   r8   rA   rE   rC   r   	SyncTokenr+   rk   rm   _parse_responser`   r   rQ   r@   )ro   rj   rR   r   rF   r   r!   r!   r"   parse_propfindx  s,   


zDAVResponse.parse_propfindlist[CalendarQueryResult]c              	   C   s   | j dvrtd| j  | jdu rg S g }|  D ]*}|jtjjkr%q| |\}}}| 	|\}}|
t||||r@t|ndd q|S )zNParse the response body as a calendar-query or calendar-multiget REPORT reply.r   zREPORT failed with status Nr   r   r$   r%   r   )r   r   r8   rA   rE   rC   r   rm   r   r   r`   r#   r@   )ro   rj   rR   r   rF   r   r$   r%   r!   r!   r"   parse_calendar_query  s&   

z DAVResponse.parse_calendar_queryr&   c              	   C   s   | j dvrtd| j  | jdu rt S g }g }d}|  D ]E}|jtjjkr1|j	}|j	| _
q |jtjjkr9q | |\}}}|rGt|nd}|dkrS|| q | |\}	}
|t||	|
|d q t|||dS )z:Parse the response body as a sync-collection REPORT reply.r   z#sync-collection failed with status Nr   r   r   )r'   r(   r)   )r   r   r8   rA   r&   rE   rC   r   r   r+   rk   rm   r   r@   r`   r   r#   )ro   r'   r(   r)   rR   r   rF   
status_strrt   r$   r%   r!   r!   r"   parse_sync_collection  s:   


z!DAVResponse.parse_sync_collectionc                 C   s>  d}d}g }d}t |jtjjk |D ]t}|jtjjkr1t |  |j}t | | | q|jtjjkrFt |  t	|jpCd}q|jtj
jkrS|| q|jdkrct |jdk d}q|jdkr| }t t|dk t |d	 jd
k d}qt d| qt | |rt d|v  tt|||fS )z
        One response should contain one or zero status children, one
        href tag and zero or more propstats.  Find them, assert there
        isn't more in the response and return those three fields
        NFre   z{DAV:}responsedescriptionzNo resources foundTz{DAV:}errorr;   r   z&{https://purelymail.com}does-not-existz$unexpected element found in response404)r   assert_rC   r   rm   rJ   r+   r   rY   r0   PropStatr`   getchildrenr=   r   r   r   )ro   rl   r   r   rF   	check_404rR   childrenr!   r!   r"   r     s:   



zDAVResponse._parse_responsec                 C   s   i | _ i | j d< t| jjtjjk | jD ]4}t|jtjjk | |}|D ] }|	dr>|| | j d |dd < q)t
||| d| j |< q)q| j S )a  Parses an RFC6638 freebusy scheduling request response

        The response from the server is asserted to be a
        scheduling-response, with freebusy status for one or more wanted
        attendee - potentially with error status for all or some
        of the wanted attendees.

        Returns:
            Dict with:
              * email addresses -> FreeBusy status (raw data)
              * errors - dict with email addresses -> error messages

        ry   :errN)parentdata)objectsr   r   rA   rC   r   ScheduleResponserm   _parse_scheduling_responseendswithr   )ro   r   rl   parsed_responser   r!   r!   r"   "_parse_scheduling_response_objects  s   



z.DAVResponse._parse_scheduling_response_objectsc                 C   s   i }d}d}d}|D ]C}|j tjj kr/t|dkr+t|d j tjj k |d j}q
|j}q
|j tj	j kr:|j}q
|j tj
j krE|j}q
td|j  t| t| |dsd||| d< |rj|||< |S )a3  
        Parses one attendee response from a RFC6638 freebusy scheduling request

        Returns:
          * ``{ recipient => calendar_data }`` if everything is OK,
          * ``{f"{recipient}:err": status}`` if things are not OK,
          * a dict with both elements if things are partially OK
        Nr;   r   zunexpected attribute 2.0r   )rC   r   	Recipientr=   r   r   r   rY   r+   RequestStatusr   DAVErrorr   )ro   rl   ret	recipientr   r%   r   r!   r!   r"   r     s,   	


z&DAVResponse._parse_scheduling_responsec                 C   sb   z| j }W n ty   d }Y nw |d u r/| jd ur$| jdtjj ng }|r-|d jnd }|S )Nz.//r   )rk   r   rA   findallr   r   rC   r+   )ro   r)   tokensr!   r!   r"   r)   4  s   
"zDAVResponse.sync_tokenc           
      C   sJ  i | _ i | _d| jv r| jd | _|  }|D ]}|jtjjkr%|j| _	qt
|jtjjk | |\}}}|| j vrFi | j |< || j|< |D ]X}d}|tjj}t
|du |dur||jdur|t
t|dk |d7 }| |j d|jv r|qH|tjjD ]}|d7 }|D ]
}	|	| j | |	j< qqt
|t|k qHq| j S )zNInternal implementation of find_objects_and_props without deprecation warning.zSchedule-Tagr   Nr;   r6   )r   statusesrd   schedule_tagrE   rC   r   r   r+   rk   r   r   rm   r   rI   rJ   r=   r   iterfindrK   )
ro   	responsesrr   rF   r   rM   cntrO   thepropr!   r!   r"   _find_objects_and_propsF  s@   




z#DAVResponse._find_objects_and_propsc                 C   s   t jdtdd |  S )aN  Check the response from the server, check that it is on an expected format,
        find hrefs and props from it and check statuses delivered.

        The parsed data will be put into self.objects, a dict {href:
        {proptag: prop_element}}.  Further parsing of the prop_element
        has to be done by the caller.

        self.sync_token will be populated if found, self.objects will be populated.

        .. deprecated::
            Use ``response.results`` instead, which provides pre-parsed property values.
            This method will be removed in a future version.
        zfind_objects_and_props() is deprecated. Use response.results instead, which provides pre-parsed property values from the protocol layer.r:   )
stacklevel)warningswarnDeprecationWarningr   r   r!   r!   r"   find_objects_and_propsy  s   z"DAVResponse.find_objects_and_propsproptagprops_foundmulti_value_allowedxpathc              	   C   s2  g }||v r|| }|  D ]9}|tjjkr8|d  dr(|d  dkr(q|d  dr8|d dv r8qtd| dd	| d
 q|sZt	|dkrZ|j
rY||j
 n*|r^|nd}||}	g }|	D ]}
t|
    |
j
r}||
j
 qi||
j qi|r|S |sd S tt	|dk |d S )Nr   zcontent-typer;   r   version)2r   zIf you see this, please add a report at https://github.com/python-caldav/caldav/issues/209 - in _expand_simple_prop, dealing with z, extra item found: =.z.//*)itemsr   r   rC   lowerr   r   r   joinr=   r+   r`   r   r   )ro   r   r   r   r   valuesprop_xmlitem_xpathleafsleafr!   r!   r"   _expand_simple_prop  s@   
zDAVResponse._expand_simple_propc                 O   s   | j |i |S r1   )_expand_simple_props)ro   largskwargsr!   r!   r"   expand_simple_props  rq   zDAVResponse.expand_simple_propspropsmulti_value_propsc                 C   s   |pg }|pg }t | ds|   | jD ]6}| j| }|D ]}|jdu r%q| j|j||d||j< q|D ]}|jdu r<q4| j|j||dd||j< q4qttttttf f | jS )a  
        The find_objects_and_props() will stop at the xml element
        below the prop tag.  This method will expand those props into
        text.

        Executes find_objects_and_props if not run already, then
        modifies and returns self.objects.
        r   N)r   T)r   r   )r   r   r   rC   r   r   r   r   )ro   r   r   r   r   r   rO   r!   r!   r"   r     s$   





z DAVResponse._expand_simple_propsr1   )rr   F)rF   rG   r,   r   )r,   r   )r,   r   )r,   r&   )FN)NNN)0r   r   r   r   rA   r
   r   rd   r   r   r    rf   rg   boolrh   r   ri   rj   r*   r   r#   rk   rp   classmethodr   r   rn   propertyr   rE   r   r   r   r   r   tupler   r   r   r   r)   r   r   r   r   r   r   r   r!   r!   r!   r"   rc      s   
 [



$ /"
3

-

rc   )rF   rG   r,   rH   )/r   r   r   collections.abcr   dataclassesr   r   typingr   r   r   urllib.parser   lxmlr	   
lxml.etreer
   caldav.calendarobjectresourcer   caldav.elementsr   r   caldav.elements.baser   
caldav.libr   caldav.lib.python_utilitiesr   caldav.lib.urlr   rm   	getLoggerr   r   r   r#   r&   r   r0   r9   r    r@   rE   rQ   rL   rc   r!   r!   r!   r"   <module>   s>    
	
+