o
    l>j                     @   s  U 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	m
Z
mZmZmZ ddlmZ ddlmZmZmZmZmZm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"m#Z#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. ddl/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9 ddl:m;Z;m<Z<m=Z=m>Z> ddl?m@Z@mAZAmBZBmCZCmDZDmEZEmFZFmGZGmHZHmIZI ddlJmKZK ddlLmMZM ddlNmOZO ddlPmQZQmRZR ddlSmTZTmUZUmVZV eWeXZYejZe[d< dZ\e]e[d< dZ^e]e[d< ddiZ_e`eaeaf e[d< e- de-d d!d"d#d$d%d&d ejbe_d d'd(d)d&d*d+d,d-Zce`eaeaf e[d.< d/d0d1Zde`eaeaf e[d2< d?d3eea d4eafd5d6Zeed7d8G d9d: d:Zfed7d8G d;d< d<ZgG d=d> d>ZhdS )@zLibrary base file.    N)	dataclass)chmodenvironmakedirspathumask)
gettempdir)AnyDictListMappingOptionalcast)uuid1)DefaultClientDataCollectorFido2Client)CtapHidDevice)AuthenticationResponsePublicKeyCredentialDescriptor!PublicKeyCredentialRequestOptionsPublicKeyCredentialTypeUserVerificationRequirement)	HTTPError)Response)CloudKitExtraMode)ACCOUNT_NAMECONTENT_TYPE_JSONCONTENT_TYPE_TEXT)
PyiCloud2FARequiredExceptionPyiCloudAcceptTermsExceptionPyiCloudAPIResponseExceptionPyiCloudFailedLoginException PyiCloudNoTrustedNumberAvailablePyiCloudPasswordException$PyiCloudServiceNotActivatedExceptionPyiCloudServiceUnavailable$PyiCloudTrustedDevicePromptException*PyiCloudTrustedDeviceVerificationException)Hsa2BootContextTrustedDeviceBridgeBootstrapperTrustedDeviceBridgeStateparse_boot_args_html)
AccountServiceAppleDeviceCalendarServiceContactsServiceDriveServiceFindMyiPhoneServiceManagerHideMyEmailServicePhotosServiceRemindersServiceUbiquityService)InvitesService)NotesService)PyiCloudSession)SrpPasswordSrpProtocolType)
b64_encodeb64url_decodeget_password_from_keyringLOGGER   PCS_SLEEP_TIME
   PCS_MAX_RETRIES
User-AgentzwMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3.1 Safari/605.1.15_HEADERSz, text/javascript@d39ba9916b7251055b22c7f910e2ea796ee65e98b2ddecea8f5dde8d9d1a815dfirstPartyAuthhttps://www.icloud.comtrueweb_messagecode zen-USz	GMT+00:00z1.1)ULZVF),:)
separators)AcceptContent-TypezX-Apple-OAuth-Client-IdzX-Apple-OAuth-Client-TypeX-Apple-OAuth-Redirect-URIz X-Apple-OAuth-Require-Grant-CodeX-Apple-OAuth-Response-ModeX-Apple-OAuth-Response-TypeX-Apple-OAuth-StateX-Apple-Widget-KeyzX-Apple-FD-Client-Info_AUTH_HEADERS_JSON2534Project662534B22)clientBuildNumberclientMasteringNumber_PARAMScookie_directoryreturnc                 C   s2   | r
t t | S t t d}t |t S )zAResolve the directory used for persisted session and cookie data.pyicloud)r   normpath
expanduserjoinr   getpassgetuser)ra   topdir rj   B/home/thesage/.local/lib/python3.10/site-packages/pyicloud/base.pyresolve_cookie_directorym   s   rl   T)frozenc                   @   sz   e Zd ZU dZeeB ed< dZee	 ed< dZ
ee ed< edeeeef  ded  fdd	Zdeeef fd
dZdS )TrustedPhoneNumberz-Typed view of Apple's trusted-phone metadata.	device_idNnon_fteu	push_modevaluerb   c                 C   sj   t |tsdS |d}t |ttfsdS |d}t |ts!d}|d}|dur.t|}| |||dS )z>Return a typed phone record when Apple's payload includes one.NidnonFTEUpushMode)ro   rp   rq   )
isinstancer   getintstrbool)clsrr   ro   rp   rq   rj   rj   rk   from_mapping   s    




zTrustedPhoneNumber.from_mappingc                 C   s"   d| j i}| jdur| j|d< |S )zHReturn the nested phoneNumber payload expected by Apple's SMS endpoints.rs   Nrt   )ro   rp   )selfpayloadrj   rj   rk   as_phone_number_payload   s   


z*TrustedPhoneNumber.as_phone_number_payload)__name__
__module____qualname____doc__rx   ry   __annotations__rp   r   rz   rq   classmethodr   r	   r|   dictr   rj   rj   rj   rk   rn   w   s   
 rn   c                   @   sj   e Zd ZU dZdZee ed< dZe	edf ed< e
deeeef  dd fd	d
Zdee fddZdS )PhoneNumberVerificationz9Typed view of Apple's phone verification wrapper payload.Ntrusted_phone_numberrj   .trusted_phone_numbersrr   rb   c                 C   sn   t |ts|  S t|d}|d}g }t |tr/|D ]}t|}|dur.|| q| |t|dS )zDReturn the parsed phone verification payload when Apple exposes one.trustedPhoneNumbertrustedPhoneNumbersNr   r   )rv   r   rn   r|   rw   listappendtuple)r{   rr   r   trusted_phone_numbers_rawr   entryphone_numberrj   rj   rk   r|      s"   




z$PhoneNumberVerification.from_mappingc                 C   s$   | j dur| j S | jr| jd S dS )zBReturn the first usable trusted phone number from Apple's payload.Nr   r   r}   rj   rj   rk   best_trusted_phone_number   s
   

z1PhoneNumberVerification.best_trusted_phone_number)r   r   r   r   r   r   rn   r   r   r   r   r   ry   r	   r|   r   rj   rj   rj   rk   r      s   
 r   c                   @   s4  e Zd ZdZdddZddee defddZ									
	dd	dddedee dee dedee dedee dede	dB dedee
 ddfddZedefddZ	ddedee ddfddZdeddfdd Zdd!d"Zdd#d$Zdd%d&Zdeeef fd'd(Zd
d
d	d)d*ed+ed,edeeef fd-d.Zdd/d0Zdd1d2Zdd3d4Zdee ddfd5d6Zdefd7d8Z	dd9eeeef  deeef fd:d;Zedefd<d=Zdefd>d?Zedefd@dAZ edefdBdCZ!edefdDdEZ"ede#eeef  fdFdGZ$dHeeef defdIdJZ%dHeeef dKedefdLdMZ&de'fdNdOZ(ddPdQZ)	ddRedSee ddfdTdUZ*de+fdVdWZ,defdXdYZ-defdZd[Z.ddSee defd\d]Z/edefd^d_Z0edee fd`daZ1edee2e  fdbdcZ3dde'ddfdedfZ4de5fdgdhZ6dee7 fdidjZ8dee fdkdlZ9defdmdnZ:ede2e; fdodpZ<ddHee; ddfdqdrZ=deeef fdsdtZ>duedvedeeef fdwdxZ?dueddfdydzZ@dKedefd{d|ZAdKeddfd}d~ZBdKeddfddZCdefddZDdedefddZEedeFfddZGedeHfddZIedeJfddZKedeLfddZMedeNfddZOedePfddZQedeRfddZSedeTfddZUedeVfddZWedeXfddZYedeZfddZ[ede\fddZ]edefddZ^defddZ_defddZ`dS )PyiCloudServicea!  
    A base authentication class for the iCloud service. Handles the
    authentication required to access iCloud services.

    Usage:
        from pyicloud import PyiCloudService
        pyicloud = PyiCloudService('username@apple.com', 'password')
        pyicloud.iphone.location()
    rb   Nc                 C   sF   | j rdnd}d| | _| j d| _d| | _d| d| _dS )	z%Set up the endpoints for the service.z.cnrK   zhttps://idmsa.apple.comz/appleauth/authrG   zhttps://setup.icloud.comz/setup/ws/1N)_is_china_mainland_idmsa_endpoint_auth_endpoint_home_endpoint_setup_endpoint)r}   icloud_chinarj   rj   rk   _setup_endpoints   s
   z PyiCloudService._setup_endpointsra   c                 C   sZ   t |}|st|}t|dd t|d td}zt|dd W t| |S t| w )z,Set up the cookie directory for the service.T)exist_oki  ?   )rl   r   dirnamer   r   r   )r}   ra   _cookie_directoryri   	old_umaskrj   rj   rk   _setup_cookie_directory   s   


z'PyiCloudService._setup_cookie_directoryTF)authenticatecloudkit_validation_extraapple_idpasswordverify	client_idwith_familychina_mainlandaccept_termsrefresh_intervalr   r   c
                C   s~  |du rt dddkn|| _|   || _|| _|| _|	| _| jdu r+|
r+t|| _i | _	i | _
d| _d| _t | _d| _d| _i | _|pLtt  | _|| _|| _| |}t }|| j| j dd t| ||| j|d| _| jj	d	| j| _t  }|d
| ji || _d| _!d| _"d| _#d| _$d| _%d| _&d| _'d| _(d| _)d| _*d| _+d| _,d| _-|
r| .  dS dS )z6Initialize a service session for one Apple ID account.Nr   01unknown/)OriginReferer)r   headersr   ra   r   clientIdF)/r   rw   r   r   _password_raw	_apple_id_accept_terms_refresh_intervalr=   data
_auth_data_hsa2_boot_context_trusted_device_bridge_stater)   _trusted_device_bridge_two_factor_delivery_method_two_factor_delivery_noticeparamsry   r   lower
_client_id_with_family_cloudkit_validation_extrar   rD   copyupdater   r8   _sessionsessionr`   _webservices_account	_calendar	_contacts_devices_drive_files_hidemyemail_photos
_reminders_notes_invites_requires_mfar   )r}   r   r   ra   r   r   r   r   r   r   r   r   r   _headers_paramsrj   rj   rk   __init__   sv   


zPyiCloudService.__init__c                 C      | j S )zAReturn whether the current service uses China mainland endpoints.)r   r   rj   rj   rk   is_china_mainlandL     z!PyiCloudService.is_china_mainlandforce_refreshservicec                 C   s  d}| j jdr#|s#z	|  | _d}W n ty"   td Y nw |se|dure| jdre|| jd v re| jd | }d|v re|d retd| j| z	| | d}W n t	yd   td	 Y nw |sz| 
  td
 W n ty   d| _td Y nw |   dS )z
        Handles authentication, and persists cookies so that
        subsequent logins will not cause additional e-mails from Apple.
        Fsession_tokenTz7Invalid authentication token, will log in from scratch.NappscanLaunchWithOneFactorzAuthenticating as %s for %sz7Could not log into service. Attempting brand new login.z%Authentication completed successfullyz2FA is required)r   r   rw   _validate_tokenr    r>   debugaccount_name&_authenticate_with_credentials_servicer!   _authenticater   r   _update_state)r}   r   r   login_successfulapprj   rj   rk   r   R  sD   


zPyiCloudService.authenticate
login_datac                 C   s   | j drj| jstd| jj| j d| jd| j di ddid}|  | }|d	i d
}|du r?td| jj| j d| jd|id}|  | jj	| j d|d}|  | | _ dS dS )z*Handle accepting updated terms of service.termsUpdateNeededz\You must accept the updated terms of service to continue. Set --accept-terms to accept them.z	/getTermslocaledsInfolanguageCodeen_USr   jsoniCloudTermsversionNzCould not get terms versionz/repairDoneacceptedICloudTerms/accountLoginr   )
r   rw   r   r   r   r   r   raise_for_statusr   post)r}   r   resp
terms_infor   rj   rj   rk   _handle_accept_terms}  s8   

z$PyiCloudService._handle_accept_termsc                 C   s`   d| j v r!t| j d tr!d| j d v r!| jd| j d d i d| j v r.| j d | _dS dS )z Update the state of the service.r   dsidwebservicesN)r   rv   r   r   r   r   r   rj   rj   rk   r     s   

zPyiCloudService._update_statec                 C   s   i | _ i | _d| _|   d| _d| _d| _d| _d| _d| _	d| _
d| _d| _d| _d| _d| _d| _d| _| jdd dS )z#Clear in-memory auth-derived state.Nr   Fr   )r   r   r   "_clear_trusted_device_bridge_stater   r   r   r   r   r   r   r   r   r   r   r   r   r   r   popr   rj   rj   rk   _clear_authenticated_state  s&   z*PyiCloudService._clear_authenticated_statec                 C   s"   | j dur| j| j  d| _ dS )zIClose any active trusted-device bridge session and clear in-memory state.N)r   r   closer   rj   rj   rk   r     s   

z2PyiCloudService._clear_trusted_device_bridge_statec                 C   s   ddddd}| j jds|   |S | j jds!|   |S z|  | _|   W n ty;   |   | Y S w |d| j	| j
| jd |S )z?Probe current authentication state without prompting for login.F)authenticatedtrusted_sessionrequires_2farequires_2sar   X-APPLE-WEBAUTH-TOKENT)r   r   rw   r   cookiesr   r   r    r   is_trusted_sessionr  r  )r}   statusrj   rj   rk   get_auth_status  s4   
zPyiCloudService.get_auth_status)keep_trustedall_sessionsclear_local_sessionr
  r  r  c             	   C   s   ||d}d}| j drH| jjdrHz!| jj| j dt| j t|ddid}t	| d	}W n t
tfyG   tjd
dd Y nw d}|rY| jjdd |   d}|||dS )zFLog out of the current session and optionally clear local persistence.)trustBrowserallBrowsersFr   r  z/logoutrU   ztext/plain;charset=UTF-8)r   r   r   successz Remote logout was not confirmed.Texc_info)remove_files)r~   remote_logout_confirmedlocal_session_cleared)r   rw   r   r  r   r   r   r   dumpsrz   r    
ValueErrorr>   r   clear_persistencer   )r}   r
  r  r  r~   r  responser  rj   rj   rk   logout  s6   

zPyiCloudService.logoutc              	   C   sF   t d| j z|   W dS  ttfy"   |   |   Y dS w )zGAuthenticate with either the cached session token or fresh credentials.zAuthenticating as %sN)r>   r   r   _authenticate_with_tokenr!   r   _srp_authenticationr   rj   rj   rk   r     s   zPyiCloudService._authenticatec                 C   sX  | j du r	td|  }| jj| j d|d d|d |d |d |d |d	 |d d
d	d}|  t| j }t	  t
  z6tj| j|tjtjd}| \}}dt|t|ddd tD i}| jj| j d||  d}|  W n tttfy } zd}	t|	||d}~ww | }
t|
d }t|
d }|
d }|
d }t|
d }d}||||| |||}|j}|r|rt|d|dt|dt|dddg i}| jjd r| jjd g|d< z| jj| j d!d"d#i||  d$ W dS  ty   t d% | ! | _"| #  Y dS  ty+ } zd&}	t|	|d}~ww )'zSRP authentication.NzNo password setz/authorize/signinrY   7rZ   rX   rV   rW   latest)	frame_id	skVersioniframeidr   response_typeredirect_uriresponse_modestateauthVersionr   )hash_algng_typea	protocolsc                 S   s   g | ]}|j qS rj   )rr   ).0protocolrj   rj   rk   
<listcomp>D  s    z7PyiCloudService._srp_authentication.<locals>.<listcomp>z/signin/initr   r   z&Failed to initiate srp authentication.saltbc	iterationr,      m1m2
rememberMeTtrustTokenstrust_tokenz/signin/completeisRememberMeEnabledrH   )r   r   r   z(2FA required to complete authentication.#Invalid email/password combination.)$r   r!   _get_auth_headersr   rw   r   r   r9   srprfc5054_enableno_username_in_xUserr   SHA256NG_2048start_authenticationr;   r   r:   r   r    r   r#   r   base64	b64decodeset_encrypt_infoprocess_challengeH_AMKr   r   r>   r   _get_mfa_auth_optionsr   _request_2fa_code)r}   auth_headersr  srp_passwordusrunameAr   errormsgbodyr/  r0  r1  
iterationsr,  
key_lengthr4  r5  rj   rj   rk   r    s   







z#PyiCloudService._srp_authenticationc              
   C   s   | j jdstdzL| j jd| j jdd| j jddd}| j j| j d|d	}|  | | _| | | j	sFt
| j|i | _d
| _|   | d W d
S  ttfym } zd}t|||d
}~ww )z!Authenticate using session token.r   zNo session token availableaccount_countryTr8  rK   )accountCountryCodedsWebAuthTokenextended_login
trustTokenr   r   Nr   zInvalid authentication token.)r   r   rw   r!   r   r   r   r   r   r  r   r   r   r   r   _set_two_factor_delivery_stater    r   )r}   r   r   rO  rP  rj   rj   rk   r  ~  s2   

z(PyiCloudService._authenticate_with_tokenc              
   C   sl   || j | jd}z| jj| j d|d | | |  | _W dS  ty5 } zd}t	|||d}~ww )z5Authenticate to a specific service using credentials.)appNamer   r   r   r   r:  N)
r   r   r   r   r   r   r   r   r    r!   )r}   r   r   rO  rP  rj   rj   rk   r     s   
z6PyiCloudService._authenticate_with_credentials_servicec                 C   sj   t d | jjdstddz| jj| j ddd}t d | W S  ty4   t d	  w )
z2Checks if the current access token is still valid.zChecking session token validityr  z$Missing X-APPLE-WEBAUTH-TOKEN cookieNz	/validatenull)r   zSession token is still validzInvalid authentication token)	r>   r   r   r  rw   r    r   r   r   )r}   reqrj   rj   rk   r     s   



zPyiCloudService._validate_token	overridesc                 C   s   t  }|| j| j| jd | jjdr| jjd |d< | jjdr.| jjd |d< | jjdr=| jjd |d< |rD|| |S )zEBuild Apple auth headers for IDMS, bridge, and verification requests.)r   rY   zX-Apple-Frame-Idscnt
session_idzX-Apple-ID-Session-Idauth_attributeszX-Apple-Auth-Attributes)r[   r   r   r   r   r   r   rw   )r}   r]  r   rj   rj   rk   r;    s    
z!PyiCloudService._get_auth_headersc                 C   r   )zReturn the session.)r   r   rj   rj   rk   r     s   zPyiCloudService.sessionc                 C   s   | j ddp| j p| jS )zDReturn whether the current auth state still requires MFA completion.hsaChallengeRequiredF)r   rw   r  r   r   rj   rj   rk   _is_mfa_required  s
   z PyiCloudService._is_mfa_requiredc                 C   s"   |   o| jdi dddkS )z4Returns True if two-step authentication is required.r   
hsaVersionr      rb  r   rw   r   rj   rj   rk   r       zPyiCloudService.requires_2sac                 C   s"   |   o| jdi dddkS )z6Returns True if two-factor authentication is required.r   rc  r      re  r   rj   rj   rk   r    rf  zPyiCloudService.requires_2fac                 C   s   | j ddS )z'Returns True if the session is trusted.hsaTrustedBrowserF)r   rw   r   rj   rj   rk   r    s   z"PyiCloudService.is_trusted_sessionc                 C   s(   | j j| j d| jd}| dS )z4Returns devices trusted for two-step authentication.z/listDevicesr&  devices)r   rw   r   r   r   )r}   requestrj   rj   rk   trusted_devices  s   zPyiCloudService.trusted_devicesdevicec                 C   s,   | j j| j d| j|d}| ddS )z>Requests that a verification code is sent to the given device.z/sendVerificationCoder   r  F)r   r   r   r   r   rw   )r}   rl  rj  rj   rj   rk   send_verification_code  s   
z&PyiCloudService.send_verification_coderJ   c              
   C   st   | |dd z| jj| j d| j|d W n ty1 } z|jdkr,W Y d}~dS  d}~ww |   | j S )z:Verifies a verification code received on a trusted device.T)verificationCoder  z/validateVerificationCoder   i[NF)	r   r   r   r   r   r    rJ   trust_sessionr  )r}   rl  rJ   rO  rj   rj   rk   validate_verification_code  s   


z*PyiCloudService.validate_verification_codec              
   C   s   |  ddi}| jj| j|d}i }z| }W n tttfy&   d}Y nw t|t	r7|
| t|}ntt|dd}| }|
| || _|   | d |S )z,Retrieve auth request options for assertion.rT   z	text/htmlr   NtextrK   r   )r;  r   rw   r   r   AttributeError	TypeErrorr  rv   r   r   r(   from_auth_optionsr+   getattras_auth_datar   r   rY  )r}   r   r  auth_optionsresponse_jsonboot_contextboot_auth_datarj   rj   rk   rH  $  s$   



z%PyiCloudService._get_mfa_auth_optionsc                 C   s   |  dti}z| jj| j d|d td W n ty(   td Y nw |  }|dur]z| jj	| j d|
 dd	|d
 td|j W dS  ty\   td Y dS w dS )a9  Request a 2FA code delivery after SRP authentication requires MFA.

        Apple does not automatically push a verification code for API-based
        (non-browser) sessions after SRP. This method explicitly triggers the
        push notification to trusted devices and falls back to SMS when available.
        rT   z/verify/trusteddevicerq  z*Requested 2FA code via trusted device pushz8Could not request 2FA device push; will try SMS fallbackN/verify/phonesmsphoneNumbermoder.  z(Requested 2FA code via SMS (phone id %s)zCould not request 2FA SMS code)r;  r   r   rw   r   r>   r   	Exception_trusted_phone_numberputr   ro   )r}   r   r   rj   rj   rk   rI  >  s8   


z!PyiCloudService._request_2fa_codemethodnoticec                 C   s   || _ || _dS )zCTrack the active MFA delivery route for the current auth challenge.N)r   r   )r}   r  r  rj   rj   rk   rY  b  s   
z.PyiCloudService._set_two_factor_delivery_statec                 C   s&   | j dur| j S t| j}|| _ |S )zEReturn the best available HSA2 boot context for the active challenge.N)r   r(   ru  r   r}   rz  rj   rj   rk   _current_hsa2_boot_contextj  s
   
z*PyiCloudService._current_hsa2_boot_contextc                 C   s"   |   }|jdko|jot|jS )z>Return whether Apple's HSA2 boot data prefers the bridge flow.zauth/bridge/step)r  auth_initial_routehas_trusted_devicesrz   bridge_initiate_datar  rj   rj   rk   _supports_trusted_device_bridget  s   
z/PyiCloudService._supports_trusted_device_bridgec                 C   s   |   dko|  duS )z3Return whether SMS delivery is currently available.r}  N)_two_factor_moder  r   rj   rj   rk   _can_request_sms_2fa_code~  s   
z)PyiCloudService._can_request_sms_2fa_codec                 C   s`   |   }|s	t | dd}| dti}| jj| j d||d |   | 	d| dS )z4Trigger SMS delivery for the current HSA2 challenge.r}  r~  rT   r|  r.  T)
r  r"   r   r;  r   r   r  r   r   rY  )r}   r  r   r   r   rj   rj   rk   _request_sms_2fa_code  s   
z%PyiCloudService._request_sms_2fa_codec                 C   sF   | j dkr| j S | jds| jrdS |  rdS |  dkr!dS dS )zHReturn the current HSA2 delivery method without exposing auth internals.r   fsaChallengesecurity_keytrusted_devicer}  )r   r   rw   security_key_namesr  r  r   rj   rj   rk   two_factor_delivery_method  s   
z*PyiCloudService.two_factor_delivery_methodc                 C   r   )zGReturn an optional user-facing note about the active 2FA delivery path.)r   r   rj   rj   rk   two_factor_delivery_notice  r   z*PyiCloudService.two_factor_delivery_noticec                 C   s   | j dS )z@Security key names which can be used for the WebAuthn assertion.keyNames)r   rw   r   rj   rj   rk   r       z"PyiCloudService.security_key_namesr   c                 C   s,   |  dti}| jj| j d||d dS )z:Submit the WebAuthn assertion response for authentication.rT   z/verify/security/keyr.  Nr;  r   r   r   r   )r}   r   r   rj   rj   rk   #_submit_webauthn_assertion_response  s   
z3PyiCloudService._submit_webauthn_assertion_responsec                 C   s   | j d}t|S )z>Return Apple's nested phone verification payload when present.phoneNumberVerification)r   rw   r   r|   )r}   phone_verificationrj   rj   rk   _phone_number_verification  s   
z*PyiCloudService._phone_number_verificationc                 C   s*   t | jd}|dur|S |   S )z;Return the best available trusted phone number description.r   N)rn   r|   r   rw   r  r   )r}   r   rj   rj   rk   r    s   
z%PyiCloudService._trusted_phone_numberc                 C   s4   | j d}t|tr|S |  }|du rdS |jS )z7Return the current 2FA delivery mode reported by Apple.r  N)r   rw   rv   ry   r  rq   )r}   r  r   rj   rj   rk   r    s   
z PyiCloudService._two_factor_modec                 C   s   | j ds	| jr| d dS |   |  r\z(| jj| j| j	| 
dti|  tt| jjdtd d| _| d W dS  ty[   tjd	dd
 |  rZ| jdd Y S  w |  rd|  S dS )zATrigger the active HSA2 delivery route for the current challenge.r  r  FrT   rC   )r   auth_endpointr   rz  
user_agentr  TzKTrusted-device bridge bootstrap failed; falling back to SMS when available.r  z2Trusted-device prompt failed; falling back to SMS.)r  )r   rw   r  rY  r   r  r   startr   r   r;  r   r  r   ry   r   rD   r   r&   r>   r   r  r  r   rj   rj   rk   request_2fa_code  s@   



z PyiCloudService.request_2fa_codec                 C   s   t t S )z!List the available FIDO2 devices.)r   r   list_devicesr   rj   rj   rk   fido2_devices  r  zPyiCloudService.fido2_devicesc              
   C   s  | j di }z|d }|d }|d }W n ty& } ztd|d}~ww |s9tt }|s5td|d }t|t	d	d
}dd |D }	t
t|||	tdd}
||
d}| |t|jjt|jjt|jj|jjrxt|jjndt|j|d |   dS )zAConduct the WebAuthn assertion ceremony with user's FIDO2 device.r  	challenge
keyHandlesrpIdzMissing WebAuthn challenge dataNzNo FIDO2 devices foundr   zhttps://apple.com)client_data_collectorc                 S   s    g | ]}t t|td dqS )z
public-key)rs   type)r   r<   r   )r+  cred_idrj   rj   rk   r-  #  s    z8PyiCloudService.confirm_security_key.<locals>.<listcomp>discouraged)r  rp_idallow_credentialsuser_verification)r  
clientDatasignatureDataauthenticatorData
userHandlecredentialIDr  )r   rw   KeyErrorr    r   r   r  RuntimeErrorr   r   r   r<   r   get_assertionget_responser  r;   r  client_data	signatureauthenticator_datauser_handleraw_idro  )r}   rl  fsar  allowed_credentialsr  rO  ri  clientcredentialsassertion_optionsresultrj   rj   rk   confirm_security_key  sb   


z$PyiCloudService.confirm_security_keyc                 C   s,   t d | jj| j d| jd }|S )z.Check if the user has consented to PCS access.zQuerying web access statez/requestWebAccessStater&  r>   r   r   r   r   r   r   )r}   r   rj   rj   rk   _check_pcs_consentC  s   
z"PyiCloudService._check_pcs_consentapp_namederived_from_user_actionc                 C   s0   t d | jj| j d||d| jd S )zESend a request to the PCS endpoint to check the status of PCS access.zQuerying PCS statusz/requestPCS)rZ  derivedFromUserAction)r   r   r  )r}   r  r  rj   rj   rk   _send_pcs_requestL  s   

z!PyiCloudService._send_pcs_requestc                 C   s4  |   }|ddstd dS |dds4td | jj| j d| jd	 }|d
s4t	dtd t
tD ]}|ddrLtd  ntd tt |   }q=t
tD ]8}| j||dkd}|d dkrxtd  dS |d dv rtd|d  tt q_td|d  t	ddS )z*Request PCS access for a specific service.isICDRSDisabledFz;Skipping PCS request because Apple reports ICDRS is enabledNisDeviceConsentedForPCSTzRequesting PCS consentz/enableDeviceConsentForPCSr&  isDeviceConsentNotificationSentzUnable to request PCS access!zWaiting for PCS consentzPCS consent grantedz'PCS consent not granted yet, waiting...r   )r  r  r  zPCS access was grantedmessage)z'Requested the device to upload cookies.z$Cookies not available yet on server.z#PCS access couldn't be obtained: %szUnknown PCS state: %s)r  rw   r>   r   r   r   r   r   r   r    rangerB   timesleepr@   r  rO  )r}   r  _check_pcs_respr   _attemptrj   rj   rk   _request_pcs_for_service[  sF   








z(PyiCloudService._request_pcs_for_servicec                 C   s  | j }zkz?| jdkr| | n2|dur=|js=| jj| j| j| dt	i||ds<t
d W W |dur:|   dS dS n| | W n! tyK     tyd   t
d Y W |durb|   dS dS w W |durn|   n
|durx|   w w t
d |   | j S )zDVerifies a verification code received via Apple's 2FA system (HSA2).r}  NrT   )r   r  r   bridge_staterJ   zCode verification failed.FzCode verification successful.)r   r  _validate_sms_code#uses_legacy_trusted_device_verifierr   validate_coder   r   r;  r   r>   rO  r   _validate_trusted_device_coder'   r    r   ro  r  )r}   rJ   r  rj   rj   rk   validate_2fa_code  sR   







z!PyiCloudService.validate_2fa_codec                 C   s8   dd|ii}|  dti}| jj| j d||d dS )zIVerifies a verification code received via Apple's legacy device endpoint.securityCoderJ   rT   z"/verify/trusteddevice/securitycoder.  Nr  )r}   rJ   r   r   rj   rj   rk   r    s   

z-PyiCloudService._validate_trusted_device_codec                 C   sb   |   }|s	t | d|i|jpdd}| dt dt i}| jj| j	 d||d dS )	z=Verifies a verification code received via Apple's SMS system.rJ   r}  )r  r  r  rT   z, z/verify/phone/securitycoder.  N)
r  r"   r   rq   r;  r   r   r   r   r   )r}   rJ   r   r   r   rj   rj   rk   r    s   

z"PyiCloudService._validate_sms_codec              	   C   sb   d| _ |  }z| jj| j d|d |   td W dS  tt	fy0   t
d Y dS w )z9Request session trust to avoid user log in going forward.Fz
/2sv/trustrq  zSession trust successful.TzSession trust failed.)r   r;  r   rw   r   r  r>   r   r    r   rO  )r}   r   rj   rj   rk   ro    s   


zPyiCloudService.trust_sessionws_keyc                 C   s6   | j du s| j |du rtd| | j | d S )z5Get webservice URL, raise an exception if not exists.NzWebservice not available: url)r   rw   r$   )r}   r  rj   rj   rk   get_webservice_url  s
   z"PyiCloudService.get_webservice_urlc              
   C   sb   | j s.z| d}t|| j| j| j| j| jd| _ W | j S  ty- } zt	d|d}~ww | j S )zReturns all devices.findme)service_roottoken_endpointr   r   r   r   z$Find My iPhone service not availableN)
r   r  r1   r   r   r   r   r   r$   r%   r}   r  rO  rj   rj   rk   ri    s*   

zPyiCloudService.devicesc              
   C   V   | j s(| d}zt|| j| jd| _ W | j S  ty' } ztd|d}~ww | j S )zGets the 'HME' service.premiummailsettingsr  r   r   z#Hide My Email service not availableN)r   r  r2   r   r   r    r%   r  rj   rj   rk   hidemyemail  s$   

	zPyiCloudService.hidemyemailc                 C   s
   | j d S )zReturns the iPhone.r   )ri  r   rj   rj   rk   iphone  s   
zPyiCloudService.iphonec              
   C   sZ   | j s*| d}zt|| j| j| jd| _ W | j S  ty) } ztd|d}~ww | j S )zGets the 'Account' service.account)r  r   r   r   zAccount service not availableN)r   r  r,   r   r   r   r    r%   r  rj   rj   rk   r    s&   


zPyiCloudService.accountc              
   C   sj   | j s2| d}zt|| j| jd| _ W | j S  ty1 } zd|jkr(td|td|d}~ww | j S )zGets the 'File' service.ubiquityr  zAccount migratedz3Files service not available use `api.drive` insteadzFiles service not availableN)r   r  r5   r   r   r    reasonr%   r  rj   rj   rk   files&  s0   


zPyiCloudService.filesc              
   C   s   |  d | jsC| d}| d}| d}| jd d | jd< zt|| j| j||d| _W | jS  tyB } ztd|d	}~ww | jS )
zGets the 'Photo' service.photosckdatabasewsuploadimagewssharedstreamsr   r   )r  r   r   
upload_urlshared_streams_urlzPhotos service not availableN)	r  r   r  r   r   r3   r   r    r%   )r}   r  r  r  rO  rj   rj   rk   r  ;  s0   




zPyiCloudService.photosc              
   C   r  )zGets the 'Calendar' service.calendarr  zCalendar service not availableN)r   r  r.   r   r   r    r%   r  rj   rj   rk   r  T      


zPyiCloudService.calendarc              
   C   r  )zGets the 'Contacts' service.contactsr  zContacts service not availableN)r   r  r/   r   r   r    r%   r  rj   rj   rk   r  c  r  zPyiCloudService.contactsc              
   C   Z   | j s*z| d}t|| j| j| jd| _ W | j S  ty) } ztd|d}~ww | j S )zGets the 'Reminders' service.r  r  r   r   r   zReminders service not availableN)r   r  r4   r   r   r   r    r%   r  rj   rj   rk   	remindersr  &   


zPyiCloudService.remindersc              
   C   sd   |  d | js/zt| d| d| j| jd| _W | jS  ty. } ztd|d}~ww | jS )zGets the 'Drive' service.iclouddrivedrivewsdocws)r  document_rootr   r   zDrive service not availableN)r  r   r0   r  r   r   r    r%   )r}   rO  rj   rj   rk   drive  s&   


zPyiCloudService.drivec              
   C   r  )zGets the 'Notes' service.r  r  zNotes service not availableN)r   r  r7   r   r   r   r    r%   r  rj   rj   rk   notes  r  zPyiCloudService.notesc              
   C   s^   | j s,z| d}t|| j| j| jd| _ W | j S  ttfy+ } ztd|d}~ww | j S )zGets the 'Invites' service.r  r  zInvites service not availableN)	r   r  r6   r   r   r   r    r$   r%   r  rj   rj   rk   invites  s,   

zPyiCloudService.invitesc                 C   r   )z8Retrieves the account name associated with the Apple ID.)r   r   rj   rj   rk   r     r   zPyiCloudService.account_namec                 C   s   d| j  S )z4Return a concise human-readable service description.ziCloud API: )r   r   rj   rj   rk   __str__     zPyiCloudService.__str__c                 C   s   d|  dS )z.Mirror ``__str__`` for interactive inspection.<>rj   r   rj   rj   rk   __repr__  r  zPyiCloudService.__repr__)rb   NN)NNTNTNFN)FN)ar   r   r   r   r   r   ry   r   rz   floatr   r   propertyr   r   r   r   r   r   r   r	   r	  r  r   r  r  r   r   r;  r8   r   rb  r  r  r  r   rk  rm  rp  r
   rH  rI  rY  r(   r  r  r  r  r  r  r   r  r  r   r  rn   r  r  r  r   r  r  r  r  r  r  r  r  ro  r  r1   ri  r2   r  r-   r  r,   r  r5   r  r3   r  r.   r  r/   r  r4   r  r0   r  r7   r  r6   r  r   r  r  rj   rj   rj   rk   r      sB   


	

[
+
"

&


)


_!

	
%


'8	

0$	r   r  )ir   rC  rg   r   loggingr  dataclassesr   osr   r   r   r   r   tempfiler   typingr	   r
   r   r   r   r   uuidr   r<  fido2.clientr   r   	fido2.hidr   fido2.webauthnr   r   r   r   r   requestsr   requests.modelsr   pyicloud.common.cloudkit.baser   pyicloud.constr   r   r   pyicloud.exceptionsr   r   r    r!   r"   r#   r$   r%   r&   r'   pyicloud.hsa2_bridger(   r)   r*   r+   pyicloud.servicesr,   r-   r.   r/   r0   r1   r2   r3   r4   r5   pyicloud.services.invitesr6   pyicloud.services.notesr7   pyicloud.sessionr8   pyicloud.srp_passwordr9   r:   pyicloud.utilsr;   r<   r=   	getLoggerr   r>   Loggerr   r@   rx   rB   rD   r   ry   r  r[   r`   rl   rn   r   r   rj   rj   rj   rk   <module>   s|     00
**