
    iT                    >   d Z ddlmZ ddlZddlmZmZ ddlmZ ddl	m
Z
mZmZ ddlmZ ddlmZmZ dd	lmZ dd
lmZ e
rddlmZ  ej        d          Z G d de          Z G d de          Z G d d          Zd*dZ	 	 	 	 	 	 	 	 	 d+d,d(Z	 	 	 	 	 	 d-d.d)Z dS )/z
Base class for DAV clients.

This module contains the BaseDAVClient class which provides shared
functionality for both sync (DAVClient) and async (AsyncDAVClient) clients.
    )annotationsN)ABCabstractmethod)Mapping)TYPE_CHECKINGAnyNoReturn)error)extract_auth_typesselect_auth_type)to_normal_str)URL)
FeatureSetcaldavc                      e Zd ZU dZdgZg dZdZded<   dZded<   dZ	ded	<   dZ
ded
<   dZded<   dZded<   d8dZd9dZd:d;dZd<dZd=d!Zd>d$Zd?d(Zd@d+ZdAd/ZdBdCd3ZdDdEd6Zed:dFd7            ZdS )GBaseDAVClienta  
    Base class for DAV clients providing shared authentication and configuration logic.

    This abstract base class contains common functionality used by both
    DAVClient (sync) and AsyncDAVClient (async). Subclasses must implement
    the abstract methods for their specific HTTP library.

    Shared functionality:
    - Authentication type extraction and selection
    - Feature set management
    - Common properties (username, password, auth_type, etc.)
    z0{urn:ietf:params:xml:ns:caldav}calendar-home-set)z{DAV:}resourcetypez{DAV:}displaynamez?{urn:ietf:params:xml:ns:caldav}supported-calendar-component-setz){http://apple.com/ns/ical/}calendar-colorz&{http://calendarserver.org/ns/}getctagN
str | Noneusernamepassword
Any | Noneauth	auth_typezFeatureSet | Nonefeaturesr   urlstrreturnc                    |r<|                     d          s't          | j                            |                    S |S )zMake a URL absolute by joining with the client's base URL if needed.

        Args:
            url: URL string, possibly relative (e.g., "/calendars/user/")

        Returns:
            Absolute URL string.
        http)
startswithr   r   join)selfr   s     M/root/projects/butler/venv/lib/python3.11/site-packages/caldav/base_client.py_make_absolute_urlz BaseDAVClient._make_absolute_url:   s@      	+s~~f-- 	+tx}}S))***
    headerset[str]c                     t          |          S )a  Extract authentication types from WWW-Authenticate header.

        Parses the WWW-Authenticate header value and extracts the
        authentication scheme names (e.g., "basic", "digest", "bearer").

        Args:
            header: WWW-Authenticate header value from server response.

        Returns:
            Set of lowercase auth type strings.

        Example:
            >>> client.extract_auth_types('Basic realm="test", Digest realm="test"')
            {'basic', 'digest'}
        )r   )r!   r%   s     r"   r   z BaseDAVClient.extract_auth_typesG   s      "&)))r$   
auth_typeslist[str] | Nonec                B   | j         }|s|st          j        d          |r!|r||vrt          j        d| d|           |sZ|rXt          |t	          | j                  t	          | j                            }|s d|v r| j        st          j        d          |S )a  
        Select the best authentication type from available options.

        This method implements the shared logic for choosing an auth type
        based on configured credentials and server-supported types.

        Args:
            auth_types: List of acceptable auth types from server.

        Returns:
            Selected auth type string, or None if no suitable type found.

        Raises:
            AuthorizationError: If configuration conflicts with server capabilities.
        zlNo auth-type given. This shouldn't happen. Raise an issue at https://github.com/python-caldav/caldav/issues/zConfiguration specifies to use z, but server only accepts )reason)has_usernamehas_passwordbearerzeServer provides bearer auth, but no password given. The bearer token should be configured as password)r   r
   AuthorizationErrorr   boolr   r   )r!   r(   r   s      r"   _select_auth_typezBaseDAVClient._select_auth_typeY   s     N	 	 	*T  
  	) 		(C(C*8 8 8+58 8   
  	Z 	(!$-00!$-00  I  Z!7!7!7.H   
 r$   methodbodyheadersMapping[str, str] | Nonetuple[URL, dict]c                4   |pi }| j                                         }|                    |           ||dk    rd|v r|d= t          j        |          }t
                              d| dt          |           d| dt          |                      ||fS )zCombine headers, strip Content-Type for empty bodies, objectify URL, and log.

        Returns:
            (url_obj, combined_headers) ready to pass to the HTTP library.
        N zContent-Typezsending request - method=z, url=z
, headers=z
body:
)	r4   copyupdater   	objectifylogdebugr   r   )r!   r   r2   r3   r4   combined_headersurl_objs          r"   _prepare_requestzBaseDAVClient._prepare_request   s     -R<,,..(((LDBJJN>N,N,N 0-$$		H H Hc'll H H'H H2?2E2EH H	
 	
 	
 (((r$   status_codeintr0   c                J    |dk    od|v o| j          o| j        duo| j        duS )zReturn True when a 401 response warrants auth negotiation.

        True when: status is 401, WWW-Authenticate header present, no auth
        object yet, and credentials are configured.
        i  zWWW-AuthenticateN)r   r   r   )r!   rA   r4   s      r"   _should_negotiate_authz$BaseDAVClient._should_negotiate_auth   sL     3 *"g-*I* T)* T)	
r$   www_authenticateNonec                    |                      |          }|                     |           | j        st          d          dS )zBuild auth object from a WWW-Authenticate header value.

        Raises:
            NotImplementedError: If the server offers no supported auth method.
        zhThe server does not provide any of the currently supported authentication methods: basic, digest, bearerN)r   build_auth_objectr   NotImplementedError)r!   rE   r(   s      r"   _build_auth_from_401z"BaseDAVClient._build_auth_from_401   sX     ,,-=>>
z***y 	%J  	 	r$   url_strreason_sourcer	   c                d    	 |j         }n# t          $ r d}Y nw xY wt          j        ||          )zFRaise AuthorizationError, extracting reason from reason_source.reason.z
None given)r   r+   )r+   AttributeErrorr
   r/   )r!   rK   rL   r+   s       r"   _raise_authorization_errorz(BaseDAVClient._raise_authorization_error   sK    	"")FF 	" 	" 	"!FFF	"&76BBBBs   
 namebytesc                   ddl m} ddlm}m} |rX|                                |                                |                                gz   gz   |                    |          z   gng }|	                                |z   |                                |
                                |                                gz   }|                    |                                          S )z:Build the XML body for a principal-property-search REPORT.r   )etreecdavdav)value)lxmlrS   caldav.elementsrU   rV   PropertySearchPropDisplayNameMatchPrincipalPropertySearchCalendarHomeSettostring
xmlelement)r!   rP   rS   rU   rV   name_filterquerys          r"   _build_principal_search_queryz+BaseDAVClient._build_principal_search_query   s    -------- S!!SXXZZ3??3D3D2E%E$FFY]I^I^^__ 	 ''))xxzz4//113??3D3DEF 	
 ~~e..00111r$   principal_dictdictlistc           	        ddl m}m} ddlm}m} g }|D ]}||         }|j        j        |vr||j        j                 j        }	t          j
        ||j        j                                                             t          j
        ||j        j                                                             ||j        j                 }
t          j
        |
                                            t          j
        |
j                    |
                                }t          j
        t          |          dk               t          j
        |d                                                     t          j
        |d                                                     |d         j        } || |          }|                     || ||	|                     |S )zFParse principal-property-search REPORT results into Principal objects.r   )CalendarSet	PrincipalrT      )clientr   )rl   r   rP   calendar_home_set)caldav.collectionri   rj   rY   rU   rV   r\   tagtextr
   assert_getchildrenitemsr_   lenappend)r!   re   ri   rj   rU   rV   retxprP   chschs_hrefchs_urlrm   s                 r"    _parse_principal_search_responsez.BaseDAVClient._parse_principal_search_response   s   <<<<<<<<-------- 	 	Aq!A"!++S_().DMa 34@@BBBCCCMa 34::<<<===D(,-CMciikk/***Mch,'''((HM#h--1,---Mhqk//111222Mhqk55777888qk&G +4W E E EJJ	14K\]]]    
r$   calendarstartendc                4    |                      |d||          S )zGet events from a calendar, optionally filtered by date range.

        For sync clients returns a list directly.
        For async clients returns a coroutine that must be awaited.
        T)eventr~   r   search_calendar)r!   r}   r~   r   s       r"   
get_eventszBaseDAVClient.get_events   s!     ##HD3#OOOr$   Finclude_completedc                2    |                      |d|          S )zGet todos from a calendar.

        For sync clients returns a list directly.
        For async clients returns a coroutine that must be awaited.
        T)todor   r   )r!   r}   r   s      r"   	get_todoszBaseDAVClient.get_todos   s      ##H4K\#]]]r$   c                    dS )a5  
        Build authentication object based on configured credentials.

        This method must be implemented by subclasses to create the
        appropriate auth object for their HTTP library (requests, httpx, etc.).

        Args:
            auth_types: List of acceptable auth types from server.
        N )r!   r(   s     r"   rH   zBaseDAVClient.build_auth_object  s	     	r$   )r   r   r   r   )r%   r   r   r&   N)r(   r)   r   r   )
r   r   r2   r   r3   r   r4   r5   r   r6   )rA   rB   r4   r   r   r0   )rE   r   r   rF   )rK   r   rL   r   r   r	   )rP   r   r   rQ   )re   rf   r   rg   NN)r}   r   r~   r   r   r   r   r   )F)r}   r   r   r0   r   r   )r(   r)   r   rF   )__name__
__module____qualname____doc__CALENDAR_HOME_SET_PROPSCALENDAR_LIST_PROPSr   __annotations__r   r   r   r   r   r#   r   r1   r@   rD   rJ   rO   rd   r|   r   r   r   rH   r   r$   r"   r   r      s           RR    HHD I    "&H&&&&COOOO   * * * *$- - - - -^) ) ) )0
 
 
 
   C C C C2 2 2 2$   6P P P P P^ ^ ^ ^ ^ 
 
 
 
 ^
 
 
r$   r   c                  N     e Zd ZdZdd fdZed             Zd	 Zd
 Zd Z	 xZ
S )CalendarCollectiona  
    A list of calendars that can be used as a context manager.

    This class extends list to provide automatic cleanup of the underlying
    DAV client connection when used with a `with` statement.

    Example::

        from caldav import get_calendars

        # As context manager (recommended) - auto-closes connection
        with get_calendars(url="...", username="...", password="...") as calendars:
            for cal in calendars:
                print(cal.get_display_name())

        # Without context manager - must close manually
        calendars = get_calendars(url="...", username="...", password="...")
        # ... use calendars ...
        if calendars:
            calendars[0].client.close()
    N	calendarslist | Nonerl   r   c                \    t                                          |pg            || _        d S r   )super__init___client)r!   r   rl   	__class__s      r"   r   zCalendarCollection.__init__&  s*    b)))r$   c                @    | j         r| j         S | r| d         j        S dS )z(The underlying DAV client, if available.r   N)r   rl   r!   s    r"   rl   zCalendarCollection.client*  s/     < 	 < 	"7>!tr$   c                    | S r   r   r   s    r"   	__enter__zCalendarCollection.__enter__4  s    r$   c                .    |                                   dS NFcloser!   exc_typeexc_valexc_tbs       r"   __exit__zCalendarCollection.__exit__7      

ur$   c                    | j         r| j                                          dS | r!| d         j                                         dS dS )+Close the underlying DAV client connection.r   N)r   r   rl   r   s    r"   r   zCalendarCollection.close;  sV    < 	#L      	#GN  """""	# 	#r$   r   )r   r   rl   r   )r   r   r   r   r   propertyrl   r   r   r   __classcell__)r   s   @r"   r   r     s         ,         X    # # # # # # #r$   r   c                  f    e Zd ZdZdddZed             Zed             Zd	 Zd
 Z	d Z
d Zd ZdS )CalendarResulta  
    A single calendar result that can be used as a context manager.

    This wrapper holds a single Calendar (or None) and provides automatic
    cleanup of the underlying DAV client connection when used with a
    `with` statement.

    Example::

        from caldav import get_calendar

        # As context manager (recommended) - auto-closes connection
        with get_calendar(calendar_name="Work", url="...") as calendar:
            if calendar:
                events = calendar.date_search(start=..., end=...)

        # Without context manager
        result = get_calendar(calendar_name="Work", url="...")
        calendar = result.calendar  # or just use result directly
        # ... use calendar ...
        result.close()
    Nr}   r   rl   c                "    || _         || _        d S r   )	_calendarr   )r!   r}   rl   s      r"   r   zCalendarResult.__init__[  s    !r$   c                    | j         S )z#The calendar, or None if not found.r   r   s    r"   r}   zCalendarResult.calendar_  s     ~r$   c                H    | j         r| j         S | j        r| j        j        S dS )zThe underlying DAV client.N)r   r   rl   r   s    r"   rl   zCalendarResult.clientd  s0     < 	 <> 	)>((tr$   c                    | j         S r   r   r   s    r"   r   zCalendarResult.__enter__m  s
    ~r$   c                .    |                                   dS r   r   r   s       r"   r   zCalendarResult.__exit__p  r   r$   c                D    | j         }|r|                                 dS dS )r   N)rl   r   )r!   rl   s     r"   r   zCalendarResult.closet  s,     	LLNNNNN	 	r$   c                    | j         d uS r   r   r   s    r"   __bool__zCalendarResult.__bool__{  s    ~T))r$   c                `    | j         t          d| d          t          | j         |          S )Nz"No calendar found, cannot access '')r   rN   getattr)r!   rP   s     r"   __getattr__zCalendarResult.__getattr__~  s6    >! !Md!M!M!MNNNt~t,,,r$   r   )r}   r   rl   r   )r   r   r   r   r   r   r}   rl   r   r   r   r   r   r   r$   r"   r   r   C  s         .       X   X      * * *- - - - -r$   r   objr   r   rg   c                f    | sg S t          | t          t          f          r| gS t          |           S )z8Convert a string or None to a list for uniform handling.)
isinstancer   rQ   rg   )r   s    r"   _normalize_to_listr     s8     	#U|$$ u99r$   TFclient_classtypecalendar_urlr   calendar_namecheck_config_filer0   config_filer   config_section
testconfigenvironmentrP   raise_errorsc
                  	 ddl } |j        d          	fd}t          d| ||||||d|
}|	rt          d          t	                      S  ||j        i d          }|st	          |          S g }t          |          }t          |          }|D ]j}d	t          |          v r|                    |
          }n|                    |          } ||j	        i d|           r|
                    |           k|D ]1} ||j        d|id| d          }|r|
                    |           2|s|s|s ||j        i d          }|r|}t	          ||          S )a  
    Get calendars from a CalDAV server with configuration from multiple sources.

    This function creates a client, connects to the server, and returns
    calendar objects based on the specified criteria. Configuration is read
    from various sources (explicit parameters, environment variables, config files).

    The returned CalendarCollection can be used as a context manager to ensure
    the underlying connection is properly closed.

    Args:
        client_class: The client class to use (DAVClient or AsyncDAVClient).
        calendar_url: URL(s) or ID(s) of specific calendars to fetch.
            Can be a string or list of strings. If the value contains '/',
            it's treated as a URL; otherwise as a calendar ID.
        calendar_name: Name(s) of specific calendars to fetch by display name.
            Can be a string or list of strings.
        check_config_file: Whether to look for config files (default: True).
        config_file: Explicit path to config file.
        config_section: Section name in config file (default: "default").
        testconfig: Whether to use test server configuration.
        environment: Whether to read from environment variables (default: True).
        name: Name of test server to use (for testconfig).
        raise_errors: If True, raise exceptions on errors; if False, log and skip.
        **config_data: Connection parameters (url, username, password, etc.)

    Returns:
        CalendarCollection of Calendar objects matching the criteria.
        If no calendar_url or calendar_name specified, returns all calendars.

    Example::

        from caldav import get_calendars

        # As context manager (recommended)
        with get_calendars(url="https://...", username="...", password="...") as calendars:
            for cal in calendars:
                print(cal.get_display_name())

        # Without context manager - connection closed on garbage collection
        calendars = get_calendars(url="https://...", username="...", password="...")
    r   Nr   c                    	  | di |}|t          d|           |S # t          $ r)}                    d| d|            r Y d}~dS d}~ww xY w)z>Try a method call, handling errors based on raise_errors flag.NzMethod returned None: z(Problems fetching calendar information: z - r   )
ValueError	Exceptionr
   )methkwargserrmsgrv   er<   r   s        r"   _tryzget_calendars.<locals>._try  s    		$....C{ !B&!B!BCCCJ 	 	 	IIOOOAOOPPP 44444		s   ! 
AAA)r   r   r   r   r   r   rP   z4Could not create DAV client - no configuration foundzgetting principal)rl   /)cal_url)cal_idz	calendar rP   zcalendar by name 'r   zgetting all calendarsr   )logging	getLoggerget_davclientr   r   	principalr   r   r}   get_display_nameru   get_calendars)r   r   r   r   r   r   r   r   rP   r   config_datar   r   rl   r   r   calendar_urlscalendar_namesr   r}   cal_nameall_calsr<   s            `            @r"   r   r     s+   n NNN
'
H
%
%C       	!+%	 	 	 	F ~ 	USTTT!### V%r+>??I 1!0000I&|44M'66N ! ' '#g,, ))')::HH )))99H 4)2/D7/D/DEE 	'X&&& # ' '4X,,,,
 

  	'X&&&  !] !> !4	/5LMM 	! Ii7777r$   c           
     8   ddl m}  |j        d	||||||d|}	|	dS |	                    dd          }
|	                    dd          }|	                    dd          }|	                    dd            | d	i |	}|
|
|_        |||_        |||_        |S )
aS  
    Get a DAV client instance with configuration from multiple sources.

    This is the canonical implementation used by both sync and async clients.
    Configuration is read from various sources in priority order:

    1. Explicit parameters (url=, username=, password=, etc.)
    2. Test server config (if testconfig=True or PYTHON_CALDAV_USE_TEST_SERVER env var)
    3. Environment variables (CALDAV_URL, CALDAV_USERNAME, CALDAV_PASSWORD)
    4. Config file (CALDAV_CONFIG_FILE env var or ~/.config/caldav/)

    Args:
        client_class: The client class to instantiate (DAVClient or AsyncDAVClient).
        check_config_file: Whether to look for config files (default: True).
        config_file: Explicit path to config file.
        config_section: Section name in config file (default: "default").
        testconfig: Whether to use test server configuration.
        environment: Whether to read from environment variables (default: True).
        name: Name of test server to use (for testconfig).
        **config_data: Explicit connection parameters passed to client constructor.
            Common parameters include:
            - url: CalDAV server URL, domain, or email address
            - username: Username for authentication
            - password: Password for authentication
            - ssl_verify_cert: Whether to verify SSL certificates
            - auth_type: Authentication type ("basic", "digest", "bearer")

    Returns:
        Client instance, or None if no configuration is found.

    Example (sync)::

        from caldav import get_davclient
        client = get_davclient(url="https://caldav.example.com", username="user", password="pass")

    Example (async)::

        from caldav.async_davclient import get_davclient
        client = await get_davclient(url="https://caldav.example.com", username="user", password="pass")
    r   )config)r   r   r   r   r   rP   N_setup	_teardown_server_nameprotocolr   )r   r   get_connection_paramspopsetupteardownserver_name)r   r   r   r   r   r   rP   r   r   conn_params
setup_functeardown_funcr   rl   s                 r"   r   r     s    d  /&. +%   K t 400JOOK66M//.$77K OOJ%%% \((K((F ! '(Mr$   )r   r   r   rg   )	NNTNNFTNF)r   r   r   r   r   r   r   r0   r   r   r   r   r   r0   r   r0   rP   r   r   r0   r   r   )TNNFTN)r   r   r   r0   r   r   r   r   r   r0   r   r0   rP   r   r   r   )!r   
__future__r   r   abcr   r   collections.abcr   typingr   r   r	   
caldav.libr
   caldav.lib.authr   r   caldav.lib.python_utilitiesr   caldav.lib.urlr   caldav.compatibility_hintsr   r   r<   r   rg   r   r   r   r   r   r   r$   r"   <module>r      s    # " " " " "  # # # # # # # # # # # # # # / / / / / / / / / /       @ @ @ @ @ @ @ @ 5 5 5 5 5 5       6555555g!!r r r r rC r r rj1# 1# 1# 1# 1# 1# 1# 1#h>- >- >- >- >- >- >- >-B     $ $""!%}8 }8 }8 }8 }8D #"!%U U U U U U Ur$   