
    ]jc              	          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mZ ddl	m
Z ddlmZmZ ddlZddlmZ erddlmZ  ej(                  e      Z G d d	e      Z G d
 d      ZddedededefdZdedefdZdej>                  de e   fdZ!dej>                  dedefdZ" G d d      Z# G d de#      Z$ G d de#      Z%y)zOAPIRequestContext for making HTTP requests with browser cookie synchronization.    N)parsedate_to_datetime)	responses)TYPE_CHECKINGAny)Retry)	WebDriverc                   $     e Zd ZdZd fdZ xZS )APIRequestFailurezRaised when an API request returns a non-2xx status and fail_on_status_code is True.

    Attributes:
        response: The APIResponse that triggered the failure.
    c                 ~    || _         t        | 	  |j                   d|j                   d|j
                          y )N : )responsesuper__init__statusstatus_texturl)selfr   	__class__s     a/home/ubuntu/.local/lib/python3.12/site-packages/selenium/webdriver/common/api_request_context.pyr   zAPIRequestFailure.__init__-   s8     HOO,Ah.B.B-C2hll^TU    )r   APIResponsereturnN)__name__
__module____qualname____doc__r   __classcell__r   s   @r   r
   r
   &   s    V Vr   r
   c                   |    e Zd ZdZdededeeef   dededdfd	Ze	de
fd
       ZdefdZdefdZdefdZddZy)r   zRepresents an HTTP response from an API request.

    Attributes:
        status: HTTP status code.
        status_text: HTTP status text.
        headers: Response headers as a dict.
        url: The request URL.
    r   r   headersr   bodyr   Nc                 J    || _         || _        || _        || _        || _        y N)r   r   r!   r   _body)r   r   r   r!   r   r"   s         r   r   zAPIResponse.__init__<   s&    &
r   c                 6    d| j                   cxk  xr dk  S c S )z4Whether the response status is in the 200-299 range.   i+  )r   r   s    r   okzAPIResponse.okC   s     dkk(S((((r   c                 @    t        j                  | j                        S )z_Parse the response body as JSON.

        Returns:
            The parsed JSON object.
        )jsonloadsr%   r(   s    r   r+   zAPIResponse.jsonH   s     zz$**%%r   c                 8    | j                   j                  d      S )zmDecode the response body as UTF-8 text.

        Returns:
            The response body as a string.
        utf-8)r%   decoder(   s    r   textzAPIResponse.textP   s     zz  ))r   c                     | j                   S )zfReturn the raw response body bytes.

        Returns:
            The response body as bytes.
        r%   r(   s    r   r"   zAPIResponse.bodyX   s     zzr   c                     d| _         y)zFree the response body memory.r   Nr2   r(   s    r   disposezAPIResponse.dispose`   s	    
r   r   N)r   r   r   r   intstrdictbytesr   propertyboolr)   r   r+   r0   r"   r4    r   r   r   r   2   s    s  tCH~ TW _d im  )D ) )&c &*c *e r   r   cookier   default_domainr   c                 8   | j                  d      }|!|t        t        j                               k  ryt        j                  j                  |      }|j                  xs d}|j                  xs d}|j                  xs d}| j                  dd      }|s|r||k7  r2y|j                  d      r||d	d k(  s|j                  |      sy||k7  ry| j                  d
d      }	|	dk(  rn||	k7  r|j                  |	dz         sy| j                  dd      r|dk7  ryy)aA  Check if a browser cookie should be sent with a request to the given URL.

    Evaluates expiry, domain, path, and secure attribute matching per RFC 6265.

    Args:
        cookie: A cookie dict from driver.get_cookies().
        url: The target request URL.
        default_domain: Fallback domain for host-only cookies (no domain attribute).
            When a cookie has no domain, it only matches if the request hostname
            equals this value. If empty and cookie has no domain, the cookie is skipped.

    Returns:
        True if the cookie matches the URL.
    expiryNF /httpdomain.   pathsecurehttpsT)getr6   timeurllibparseurlparsehostnamerG   scheme
startswithendswith)
r=   r   r>   r@   parsedrO   rG   rP   cookie_domaincookie_paths
             r   _cookie_matchesrV   e   s     ZZ!FfDIIK(88\\""3'F$"H;;#D]]$fF JJx,M^!;		!	!#	&M!"--1B1B=1Q}$ **VS)Kc		T__[35F%G zz(E"v'8r   header_valuec                 v   | j                  d      }|d   j                         }|j                  d      }|dk(  ri S |d| j                         }||dz   d j                         }||d}d}|dd D ]  }|j                         }|sd|v rD|j                  dd      \  }	}
|	j                         j                         }	|
j                         }
n |j                         j                         }	d	}
|	d
k(  r|
|d
<   |	dk(  r|
|d<   |	dk(  rd|d<   |	dk(  rd|d<   |	dk(  r|
|d<   |	dk(  r2	 t	        |
      }t	        t        j
                               |z   |d<   d}|	dk(  s|r	 t        |
      }t	        |j                               |d<     |S # t        $ r Y /w xY w# t        t        f$ r Y Ew xY w)aM  Parse a single Set-Cookie header value into a cookie dict.

    Uses manual parsing instead of http.cookies.SimpleCookie which is too
    strict for real-world Set-Cookie headers.

    Args:
        header_value: The Set-Cookie header string.

    Returns:
        A dict with cookie attributes suitable for driver.add_cookie().
    ;r   =NrF   )namevalueFrA   rD   rG   rH   ThttponlyhttpOnlysamesitesameSitezmax-ager@   expires)
splitstripfindlowerr6   rK   
ValueErrorr   	timestamp	TypeError)rW   parts
name_valueeq_idxr\   r]   r=   has_max_agepart	attr_name
attr_valuemax_agedts                r   _parse_set_cookiers      s    s#Eq!J__S!F|	gv$$&Dvz|$**,E&*U;FKab	 #zz|$;$(JJsA$6!Iz!)//1I#))+J

**,IJ )F8& 'F6N("#F8*$!%F:*$!+F:)#j/#&tyy{#3g#=x " )#K*:6#&r||~#6x C#J M   	* s$   .0F)'F%	F"!F"%F87F8respc                     t        | j                  d      r| j                  j                  d      }|r|S | j                  j                  d      }|r|gS g S )zExtract all Set-Cookie header values from a urllib3 response.

    Args:
        resp: The urllib3 HTTP response.

    Returns:
        A list of Set-Cookie header strings.
    getlistz
Set-Cookie)hasattrr!   rv   rJ   )rt   r!   scs      r   _get_set_cookie_headersry      sQ     t||Y',,&&|4N			,	'BB42r   original_urlc                     | j                   r| j                   j                  nd}|rQ|d   }|j                  r@|j                  r4t        j
                  j                  |j                  |j                        S |S )a)  Return the final URL after any redirects.

    urllib3's retry history records each hop.  When redirects occurred,
    the last entry's redirect_location resolved against its URL gives
    the final destination.  When no redirects occurred, the original
    request URL is returned unchanged.
    r<   r[   )retrieshistoryr   redirect_locationrL   rM   urljoin)rt   rz   r}   lasts       r   _resolve_redirect_urlr      s]     '+lldll""Gr{88..<<''$2H2HIIr   c                   $   e Zd ZdZ	 	 	 	 	 d"dedeeef   dz  dedededdfd	Z	d
ede
defdZd
ede
defdZd
ede
defdZd
ede
defdZd
ede
defdZd
ede
defdZd#d
edede
defdZd$dZd
edefdZdeee
f   deeef   fdZdeeef   deee
f   dedz  fdZd
edeee
f   defdZded
edeeef   dedz  deee
f   dej4                  fdZdej4                  d
edefdZd
edee   fdZdee   d
eddfd Zd
edede
defd!Z y)%_BaseRequestContextzCBase class with shared HTTP request logic for API request contexts.Nbase_urlextra_headerstimeoutmax_redirectsfail_on_status_coder   c                     || _         |xs i | _        || _        || _        || _        t        j                         | _        y r$   )	_base_url_extra_headers_timeout_max_redirects_fail_on_status_codeurllib3PoolManager_pool)r   r   r   r   r   r   s         r   r   z_BaseRequestContext.__init__   s?     "+1r+$7!((*
r   r   kwargsc                 *     | j                   |dfi |S )a  Send a GET request.

        Args:
            url: The request URL (absolute or relative to base_url).
            **kwargs: Optional arguments: headers, params, timeout, max_redirects, fail_on_status_code.

        Returns:
            An APIResponse object.
        GET_fetchr   r   r   s      r   rJ   z_BaseRequestContext.get  s     t{{3000r   c                 *     | j                   |dfi |S )a5  Send a POST request.

        Args:
            url: The request URL (absolute or relative to base_url).
            **kwargs: Optional arguments: headers, params, data, form,
                json_data, timeout, max_redirects, fail_on_status_code.

        Returns:
            An APIResponse object.
        POSTr   r   s      r   postz_BaseRequestContext.post       t{{31&11r   c                 *     | j                   |dfi |S )a4  Send a PUT request.

        Args:
            url: The request URL (absolute or relative to base_url).
            **kwargs: Optional arguments: headers, params, data, form,
                json_data, timeout, max_redirects, fail_on_status_code.

        Returns:
            An APIResponse object.
        PUTr   r   s      r   putz_BaseRequestContext.put%  s     t{{3000r   c                 *     | j                   |dfi |S )a6  Send a PATCH request.

        Args:
            url: The request URL (absolute or relative to base_url).
            **kwargs: Optional arguments: headers, params, data, form,
                json_data, timeout, max_redirects, fail_on_status_code.

        Returns:
            An APIResponse object.
        PATCHr   r   s      r   patchz_BaseRequestContext.patch2  s     t{{32622r   c                 *     | j                   |dfi |S )a7  Send a DELETE request.

        Args:
            url: The request URL (absolute or relative to base_url).
            **kwargs: Optional arguments: headers, params, data, form,
                json_data, timeout, max_redirects, fail_on_status_code.

        Returns:
            An APIResponse object.
        DELETEr   r   s      r   deletez_BaseRequestContext.delete?  s     t{{33F33r   c                 *     | j                   |dfi |S )a  Send a HEAD request.

        Args:
            url: The request URL (absolute or relative to base_url).
            **kwargs: Optional arguments: headers, params, timeout,
                max_redirects, fail_on_status_code.

        Returns:
            An APIResponse object.
        HEADr   r   s      r   headz_BaseRequestContext.headL  r   r   methodc                 *     | j                   ||fi |S )aw  Send an HTTP request with a custom method.

        Args:
            url: The request URL (absolute or relative to base_url).
            method: The HTTP method to use.
            **kwargs: Optional arguments: headers, params, data, form,
                json_data, timeout, max_redirects, fail_on_status_code.

        Returns:
            An APIResponse object.
        r   )r   r   r   r   s       r   fetchz_BaseRequestContext.fetchY  s     t{{31&11r   c                 8    | j                   j                          y)z%Close the underlying connection pool.N)r   clearr(   s    r   r4   z_BaseRequestContext.disposeg  s    

r   c                     |j                  d      s0| j                  j                  d      dz   |j                  d      z   S |S )z6Resolve a URL, prepending base_url for relative paths.)zhttp://zhttps://rB   )rQ   r   rstriplstripr   r   s     r   _resolve_urlz _BaseRequestContext._resolve_urlk  s;    ~~56>>((-3cjjoEE
r   c                 z    t        | j                        }|j                  d      r|j                  |d          |S )z-Merge extra_headers with per-request headers.r!   )r8   r   rJ   update)r   r   r!   s      r   _build_headersz"_BaseRequestContext._build_headersq  s4    t**+::i NN6),-r   r!   c                 h   |j                  d      }|j                  d      }|j                  d      }|6|j                  dd       t        j                  |      j	                  d      S |@|j                  dd       t
        j                  j                  |      j	                  d      S |t        |t              r@|j                  dd       t
        j                  j                  |      j	                  d      S t        |t              r|j	                  d      S t        |t              r|S y)	zPrepare the request body from json_data, form, or data kwargs.

        Priority: json_data > form > data. Only one should be provided.
        	json_dataformdataNzContent-Typezapplication/jsonr.   z!application/x-www-form-urlencoded)rJ   
setdefaultr+   dumpsencoderL   rM   	urlencode
isinstancer8   r7   r9   )r   r!   r   r   r   r   s         r   _prepare_bodyz!_BaseRequestContext._prepare_bodyx  s   
 JJ{+	zz&!zz&! ~/AB::i(//88~/RS<<))$/66w??$%"">3VW||--d3::7CCD#&{{7++D%(r   c                     |j                  d      }|r-d|v rdnd}||z   t        j                  j                  |      z   S |S )z#Append query parameters to the URL.params?&)rJ   rL   rM   r   )r   r   r   r   	separators        r   _append_paramsz"_BaseRequestContext._append_params  sC    H%"czsI?V\\%;%;F%CCC
r   r"   c           
          |j                  d| j                        }|j                  d| j                        }|dkD  }t        dddd|r|ndd      }	| j                  j                  |||||||	d      S )z%Execute the HTTP request via urllib3.r   r   r   F)connectreadr   otherredirectraise_on_redirectT)r!   r"   r   r   r|   preload_content)rJ   r   r   r   r   request)
r   r   r   r!   r"   r   r   r   followr|   s
             r   _execute_requestz$_BaseRequestContext._execute_request  s     **Y6

?D4G4GH"&,]!#
 zz!!  " 	
 		
r   rt   c                 D   i }|j                   j                         D ]-  \  }}|j                         }||v r||   dz   |z   ||<   )|||<   / t        |dd      }|xs  t	        j
                  |j                  d      }t        |j                  ||||j                        S )z-Build an APIResponse from a urllib3 response.z, reasonNrA   )r   r   r!   r   r"   )	r!   itemsrf   getattrhttp_status_phrasesrJ   r   r   r   )	r   rt   r   resp_headerskvkeyr   r   s	            r   _build_responsez#_BaseRequestContext._build_response  s     (*LL&&( 	&DAq'')Cl"$0$5$<q$@S!$%S!	& x.H 3 7 7R H;;# 
 	
r   c                     g S )zKGet cookies that should be sent with the request. Overridden by subclasses.r<   r   s     r   _get_cookies_for_requestz,_BaseRequestContext._get_cookies_for_request  s    	r   set_cookie_headersc                      y)zGProcess Set-Cookie headers from the response. Overridden by subclasses.Nr<   )r   r   r   s      r   _handle_response_cookiesz,_BaseRequestContext._handle_response_cookies  s    r   c                 &   | j                  |      }| j                  |      }| j                  |      }|r0dj                  d |D              }d|v r|d   dz   |z   |d<   n||d<   | j	                  ||      }| j                  ||      }| j                  |||||      }t        ||      }	t        |      }
|
r| j                  |
|	       | j                  ||	      }|j                  d| j                        }|r|j                  st        |      |S )zExecute an HTTP request with cookie handling.

        Args:
            url: The request URL.
            method: The HTTP method.
            **kwargs: Optional arguments.

        Returns:
            An APIResponse object.
        z; c              3   6   K   | ]  }|d     d|d      yw)r\   rZ   r]   Nr<   ).0cs     r   	<genexpr>z-_BaseRequestContext._fetch.<locals>.<genexpr>  s#     %\a61QwZL&A%\s   Cookier   )r   r   r   joinr   r   r   r   ry   r   r   rJ   r   r)   r
   )r   r   r   r   r!   matching_cookiescookie_headerr"   rt   	final_urlr   r   fails                r   r   z_BaseRequestContext._fetch  s'    $%%f-  88= II%\K[%\\M7"$+H$5$<}$L!$1!!!'62!!#v.$$VS'4H *$4	 5T:))*<iH''i8zz/1J1JK#H--r   rA   N      >@
   F)r   r5   )!r   r   r   r   r7   r8   floatr6   r;   r   r   r   rJ   r   r   r   r   r   r   r4   r   r   r9   r   r   r   BaseHTTPResponser   r   listr   r   r   r<   r   r   r   r      s{   M /3$)++ CH~,+ 	+
 + "+ 
+
1s 
1c 
1k 
12 2s 2{ 21s 1c 1k 13 3 3 34# 4 4 42 2s 2{ 22 2c 2S 2[ 2  T#s(^ S#X T#s(^ T#s(^ PUX\P\ 2# tCH~ # 

 #
.238n
DIDL
Z^_bdg_gZh
		!	!
8
G$<$< 
3 
; 
*C DJ V49 V3 VSW V*# *s *c *k *r   r   c                   $    e Zd ZdZ	 	 	 	 	 ddddedeeef   dz  deded	ed
df fdZ		 	 	 	 ddedeeef   dz  deez  e
j                  z  dz  d	ed
df
dZddee
j                  z  dz  d
eeef   fdZded
ee   fdZdee   ded
dfdZ xZS )APIRequestContextah  Makes HTTP requests with automatic browser cookie synchronization.

    Cookies from the browser session are sent with API requests, and cookies
    from API responses are synced back to the browser.

    Args:
        driver: The WebDriver instance to sync cookies with.
        base_url: Optional base URL prepended to relative request paths.
        extra_headers: Optional headers included in every request.
        timeout: Default request timeout in seconds.
        max_redirects: Maximum number of redirects to follow.
        fail_on_status_code: If True, raise APIRequestFailure for non-2xx responses.
    Ndriverr   r   r   r   r   r   r   c                 <    t         |   |||||       || _        y N)r   r   r   r   r   )r   r   _driver)r   r   r   r   r   r   r   r   s          r   r   zAPIRequestContext.__init__  s/     	'' 3 	 	
 r   storage_state_IsolatedAPIRequestContextc                 8   g }|t        |t        t        j                  f      r^t        j                  |      }|j	                         st        d|       	 t        |      5 }t        j                  |      }ddd       n|}t        j                  dg             }t        |||| j                  | j                   |      S # 1 sw Y   LxY w# t        j                  $ r}	t        d| d|	       |	d}	~	wt        $ r}	t        d| d|	       |	d}	~	ww xY w)a  Create an isolated API request context that does not sync with the browser.

        Args:
            base_url: Optional base URL for this context.
            extra_headers: Optional headers for this context.
            storage_state: Optional cookies to pre-load, as a dict, JSON file path, or Path.
            fail_on_status_code: If True, raise APIRequestFailure for non-2xx responses.

        Returns:
            An _IsolatedAPIRequestContext instance.
        NzStorage state file not found: z#Invalid JSON in storage state file r   zCannot read storage state file cookies)r   r   r   r   r   r   )r   r7   pathlibPathexistsFileNotFoundErroropenr+   loadJSONDecodeErrorrg   OSErrorr   rJ   r   r   r   )
r   r   r   r   r   r   	file_pathfstatees
             r   new_contextzAPIRequestContext.new_context  s$   $ !$-#w||)<=#LL7	 '')+.LYK,XYY]i -A $		!- &599Y34G)'MM-- 3
 	
- -++ d$'J9+UWXYWZ%[\bcc ]!$CI;bQRPS"TU[\\]s<   C $C:C C
C D$C66DDDrG   c                 0   | j                   j                         }d|i}|Et        j                  |      }	 t	        |d      5 }t        j                  ||d       ddd       |S |S # 1 sw Y   |S xY w# t        $ r}t        d| d|       |d}~ww xY w)zExport the current browser cookies as a storage state dict.

        Args:
            path: Optional file path to save the storage state as JSON.

        Returns:
            A dict with a "cookies" key containing the browser cookies.
        r   Nw   )indentzCannot write storage state to r   )r   get_cookiesr   r   r   r+   dumpr   )r   rG   r   r  r  r  r  s          r   get_storage_statez#APIRequestContext.get_storage_stateK  s     ,,**,!*G 4T*IX)S) 2QIIeQq12 u	2   X >ykA3OPVWWXs5   A5 A(A5 (A2-A5 2A5 5	B>BBr   c                    	 | j                   j                         }d}	 | j                   j
                  }|r-t        j                  j                  |      j                  xs d}|D cg c]  }t        |||      s| c}S # t        $ r t        j	                  dd       g cY S w xY w# t        $ r t        j	                  dd       Y hw xY wc c}w )z1Get matching browser cookies for the request URL.z"Could not retrieve browser cookiesTexc_inforA   z7Could not get current URL for host-only cookie matching)r   r
  	Exceptionloggerdebugcurrent_urlrL   rM   rN   rO   rV   )r   r   browser_cookiesr>   currentr   s         r   r   z*APIRequestContext._get_cookies_for_request_  s    	"ll668O
 	cll..G!'!6!6w!?!H!H!NB +Vaoan.UVV  	LL=LMI	  	cLLR]aLb	cVs/   B AB* (C;C"B'&B'* CCr   c                 X   t         j                  j                  |      }|D ]  }t        |      }|j	                  d      s |j                  d|j                  xs d       |j                  dd       |j	                  d      }|@|t        t        j                               k  r 	 | j                  j                  |d          	 | j                  j                  |        y# t        $ r Y +w xY w# t        $ r+ t        j                  d|j	                  d      d	
       Y w xY w)z,Sync Set-Cookie headers back to the browser.r\   rD   rA   rG   rB   r@   NzICould not sync cookie '%s' to browser (domain mismatch with current page)Tr  )rL   rM   rN   rs   rJ   r   rO   r6   rK   r   delete_cookier  
add_cookier  warning)r   r   r   
parsed_url	sc_headerr=   r@   s          r   r   z*APIRequestContext._handle_response_cookiesp  s   \\**3/
+ 	I&y1F::f%h
(;(;(ArBfc*ZZ)F!fDIIK0@&@LL..vf~> ''/	 ! 
  _JJv&!  s$   (C&C5&	C21C250D)(D)r   )rA   NNFr$   )r   r   r   r   r7   r8   r   r6   r;   r   r   r   r  r   r  r   r   r   r   r   s   @r   r   r     s7   " /3$)  CH~,	
   " 
( /3:>$)*
*
 CH~,*
 czGLL047	*

 "*
 
&*
XcGLL&84&? 4PSUXPX> (WC WDJ W"49 3 SW r   r   c                        e Zd ZdZ	 	 	 	 	 	 ddedeeef   dz  dee   dz  dedede	d	df fd
Z
d	eeef   fdZded	ee   fdZdee   ded	dfdZ xZS )r   zAn isolated API request context that maintains its own cookie jar.

    Does not synchronize cookies with any browser session.
    Nr   r   r   r   r   r   r   c                 D    t         |   |||||       |xs g | _        y r   )r   r   _cookies)r   r   r   r   r   r   r   r   s          r   r   z#_IsolatedAPIRequestContext.__init__  s3     	'' 3 	 	
 %,Mrr   c                 0    dt        | j                        iS )z3Return the current cookies as a storage state dict.r   )r   r  r(   s    r   r  z,_IsolatedAPIRequestContext.get_storage_state  s    4.//r   r   c                     t         j                  j                  |      j                  xs d}| j                  D cg c]  }t        |||      s| c}S c c}w )z+Get matching cookies from the internal jar.rA   )rL   rM   rN   rO   r  rV   )r   r   r>   r   s       r   r   z3_IsolatedAPIRequestContext._get_cookies_for_request  sF      ..s3<<B==TaOAsN,STTTs   AAr   c           
         t         j                  j                  |      }t        t	        j                               }|D ]  }t        |      }|j                  d      s!|j                  d|j                  xs d       |j                  dd       |d   |j                  dd      |j                  dd      f}| j                  D cg c]<  }|j                  d      |j                  dd      |j                  dd      f|k7  s;|> c}| _	        |j                  d      }	|	|	|k  r| j                  j                  |        yc c}w )z-Store Set-Cookie headers in the internal jar.r\   rD   rA   rG   rB   r@   N)rL   rM   rN   r6   rK   rs   rJ   r   rO   r  append)
r   r   r   r  nowr  r=   r   r   r@   s
             r   r   z3_IsolatedAPIRequestContext._handle_response_cookies  s%   \\**3/
$))++ 	)I&y1F::f%h
(;(;(ArBfc*&>6::h#;VZZPS=TUC  ==QUU6]AEE(B<OQRQVQVW]_bQc,dhk,kDM ZZ)F!fmMM  (!	)s   <E	E	)rA   NNr   r   F)r   r   r   r   r7   r8   r   r   r6   r;   r   r   r  r   r   r   r   s   @r   r   r     s     /3%)$)22 CH~,2 dd"	2
 2 2 "2 
2$04S> 0UC UDJ U)49 )3 )SW )r   r   )rA   )&r   r+   loggingr   rK   urllib.parserL   email.utilsr   http.clientr   r   typingr   r   r   urllib3.util.retryr   #selenium.webdriver.remote.webdriverr   	getLoggerr   r  r  r
   r   r8   r7   r;   rV   rs   r   r   ry   r   r   r   r   r<   r   r   <module>r,     s   $ V      - 8 %  $=			8	$	V	 	V0 0f2D 2s 2C 2 2j<C <D <~'":": tCy " 8 8  PS  A AHI+ IX6)!4 6)r   