ó
    ÏnajjQ  ã                  ó„  • S r SSKJr  SSKrSSKrSSKJrJr  SSKJ	r	  SSK
r
SSKrSSKrSSKrSSKrSSKrSSKrSSKrSSKJr  SSKrSSKrSSKrSSKrSSKJr  SSKJrJrJr  \" \5      RA                  5       RB                  S	   r"S
r#Sr$Sr%Sr&Sr'Sr(Sr)Sr*\RV                  " S5      r,S+S jr-S,S jr.\'S.S-S jjr/\	" SS9 " S S5      5       r0 " S S5      r1 " S S5      r2S.S jr3S S!.S/S" jjr4 " S# S$\Rj                  Rl                  5      r7\(SS%.         S0S& jjr8S1S' jr9S2S( jr:S2S) jr;\<S*:X  a  \;" 5         gg)3a  Password-protected static server for the V3 Face review viewer.

The server intentionally uses only Python's standard library.  It keeps the
existing byte-range support required by audio, video, VRM, and FBX assets,
while putting the whole origin behind a server-side password check.
é    )ÚannotationsN)ÚdefaultdictÚdeque)Ú	dataclass)ÚPath)ÚDeque)Úparse_qsÚquoteÚurlsplité   z%/tools/blendshape-player-natural.htmlz__Host-face_sessioni   Úpbkdf2_sha256iÀ'	 i€p  i,  é   zbytes=(\d*)-(\d*)\Zc                ój   • [         R                  " U 5      R                  S5      R                  S5      $ )Nó   =Úascii)Úbase64Úurlsafe_b64encodeÚrstripÚdecode©Úvalues    Ú<package/face/animasync-face-v3/scripts/face_viewer_server.pyÚ
_b64encoder   ,   s)   € Ü×#Ò# EÓ*×1Ñ1°$Ó7×>Ñ>¸wÓGÐGó    c                óT   • [         R                  " U S[        U 5      * S-  -  -   5      $ )NÚ=é   )r   Úurlsafe_b64decodeÚlenr   s    r   Ú
_b64decoder    0   s'   € Ü×#Ò# E¨C´C¸³J°;À±?Ñ,CÑ$CÓDÐDr   )Ú
iterationsc          	     óö   • [        U 5      S:  a  [        S5      e[        R                  " S5      n[        R
                  " SU R                  S5      X!5      n[         SU S[        U5       S[        U5       3$ )zFReturn a salted PBKDF2-SHA256 password hash suitable for the env file.é   z,password must contain at least 12 charactersé   Úsha256úutf-8Ú$)	r   Ú
ValueErrorÚsecretsÚtoken_bytesÚhashlibÚpbkdf2_hmacÚencodeÚPASSWORD_HASH_SCHEMEr   )Úpasswordr!   ÚsaltÚdigests       r   Úmake_password_hashr2   4   sr   € ä
ˆ8ƒ}�rÓÜÐGÓHÐHÜ×Ò˜rÓ"€DÜ× Ò  ¨8¯?©?¸7Ó+CÀTÓV€FÜ"Ð# 1 Z L°´*¸TÓ2BÐ1CÀ1ÄZÐPVÓEWÐDXÐYÐYr   T)Úfrozenc                  óR   • \ rS rSr% S\S'   S\S'   S\S'   \SS j5       rSS jrS	rg
)ÚPasswordVerifieré=   Úintr!   Úbytesr0   Úexpectedc                ó¶  •  UR                  SS5      u  p#pE[        U5      n[        U5      n[        U5      nU[
        :w  a  [	        SU 35      eSUs=::  a  S::  d  O  [	        S5      e[        U5      S:  d,  [        U5      [        R                  " 5       R                  :w  a  [	        S	5      eU " XgUS
9$ ! [        [        4 a  n	[	        S5      U	eS n	A	ff = f)Nr'   é   z(invalid FACE_VIEWER_PASSWORD_HASH formatz"unsupported password hash scheme: i † i@KL z;password hash iterations must be between 100000 and 5000000r$   z+invalid password hash salt or digest length)r!   r0   r9   )
Úsplitr7   r    Ú	TypeErrorr(   r.   r   r+   r%   Údigest_size)
ÚclsÚencodedÚschemeÚraw_iterationsÚraw_saltÚraw_expectedr!   r0   r9   Úexcs
             r   Úfrom_encodedÚPasswordVerifier.from_encodedC   sÖ   € ð	RØ=D¿]¹]È3ÐPQÓ=RÑ:ˆF HÜ˜^Ó,ˆJÜ˜hÓ'ˆDÜ! ,Ó/ˆHð Ô)Ó)ÜÐAÀ&ÀÐJÓKÐKØ˜*Õ1¨	Õ1ÜÐZÓ[Ð[Üˆt‹9�r‹>œS ›]¬g¯nªnÓ.>×.JÑ.JÓJÜÐJÓKÐKÙ˜j¸hÑGÐGøô œ:Ð&ó 	RÜÐGÓHÈcÐQûð	Rús   ‚6B7 Â7CÃCÃCc                óº   • [         R                  " SUR                  S5      U R                  U R                  5      n[
        R                  " X R                  5      $ )Nr%   r&   )r+   r,   r-   r0   r!   ÚhmacÚcompare_digestr9   )Úselfr/   Úactuals      r   ÚverifyÚPasswordVerifier.verifyT   sC   € Ü×$Ò$Ø�h—o‘o gÓ.°·	±	¸4¿?¹?ó
ˆô ×"Ò" 6¯=©=Ó9Ð9r   © N)r@   ÚstrÚreturnz'PasswordVerifier')r/   rP   rQ   Úbool)	Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__annotations__ÚclassmethodrF   rM   Ú__static_attributes__rO   r   r   r5   r5   =   s*   ‡ àƒOØ
ƒKØƒOàóHó ðH÷ :r   r5   c                  óL   • \ rS rSr\4S	S jjrS
S jrSS jrSS jrSS jr	Sr
g)ÚSessionStoreé[   c                óR   • Xl         0 U l        [        R                  " 5       U l        g ©N)Úttl_secondsÚ	_sessionsÚ	threadingÚLockÚ_lock)rK   r_   s     r   Ú__init__ÚSessionStore.__init__\   s   € Ø&ÔØ+-ˆŒÜ—^’^Ó%ˆ�
r   c                ó&  • [         R                  " S5      nU R                     U R                  [        R                  " 5       5        [        R                  " 5       U R
                  -   U R                  U'   S S S 5        U$ ! , (       d  f       U$ = f)Né    )r)   Útoken_urlsaferc   Ú_purge_lockedÚtimer_   r`   ©rK   Útokens     r   ÚcreateÚSessionStore.createa   sg   € Ü×%Ò% bÓ)ˆØ�Z‹ZØ×ÑœtŸyšy›{Ô+Ü$(§I¢I£K°$×2BÑ2BÑ$BˆD�N‰N˜5Ñ!÷ ð ˆ÷ ŒZð ˆús   £ABÂ
Bc                ó   • U(       d  g[         R                   " 5       nU R                     U R                  U5        U R                  R	                  U5      nUS L=(       a    X2:„  sS S S 5        $ ! , (       d  f       g = f)NF)rj   rc   ri   r`   Úget)rK   rl   ÚnowÚ
expires_ats       r   ÚvalidÚSessionStore.validh   sV   € ÞØÜ�iŠi‹kˆØ�Z‹ZØ×Ñ˜sÔ#ØŸ™×+Ñ+¨EÓ2ˆJØ TÐ)×>¨jÑ.>÷ �Z�Zús   ª;A/Á/
A=c                ó˜   • U(       d  g U R                      U R                  R                  US 5        S S S 5        g ! , (       d  f       g = fr^   )rc   r`   Úpoprk   s     r   ÚrevokeÚSessionStore.revokeq   s-   € ÞØØ�Z‹ZØ�N‰N×Ñ˜u dÔ+÷ �ZŽZús	   •;»
A	c                óÄ   • U R                   R                  5        VVs/ s H  u  p#X1::  d  M  UPM     nnnU H  nU R                   R                  US 5        M!     g s  snnf r^   )r`   Úitemsrv   )rK   rq   rl   rr   Úexpireds        r   ri   ÚSessionStore._purge_lockedw   sQ   € Ø26·.±.×2FÑ2FÔ2HÔ^Ò2HÑ.˜UÈJÑL]—5Ñ2HˆÑ^ÛˆEØ�N‰N×Ñ˜u dÖ+ò ùó _s
   žA­A)rc   r`   r_   N)r_   r7   rQ   ÚNone©rQ   rP   )rl   ú
str | NonerQ   rR   )rl   r   rQ   r}   )rq   ÚfloatrQ   r}   )rS   rT   rU   rV   ÚSESSION_TTL_SECONDSrd   rm   rs   rw   ri   rY   rO   r   r   r[   r[   [   s   † Ø*=ö &ô
ô?ô,÷,r   r[   c                  óZ   • \ rS rSr\\S.     S
S jjrSS jrSS jrSS jr	SS jr
Srg	)ÚLoginRateLimiteré}   )Úmax_failuresÚwindow_secondsc               óx   • Xl         X l        [        [        5      U l        [
        R                  " 5       U l        g r^   )r…   r†   r   r   Ú	_failuresra   rb   rc   )rK   r…   r†   s      r   rd   ÚLoginRateLimiter.__init__~   s*   € ð )ÔØ,ÔÜ2=¼eÓ2DˆŒÜ—^’^Ó%ˆ�
r   c                óÊ   • U R                      U R                  U[        R                  " 5       5      n[        U5      U R                  :¬  sS S S 5        $ ! , (       d  f       g = fr^   )rc   Ú_active_failures_lockedrj   r   r…   ©rK   Ú	client_idÚfailuress      r   Ú
is_limitedÚLoginRateLimiter.is_limited‰   s=   € Ø�Z‹ZØ×3Ñ3°I¼t¿yºy»{ÓKˆHÜ�x“= D×$5Ñ$5Ñ5÷ �Z�Zús   �=AÁ
A"c                óâ   • U R                      U R                  U[        R                  " 5       5      nUR                  [        R                  " 5       5        S S S 5        g ! , (       d  f       g = fr^   )rc   r‹   rj   ÚappendrŒ   s      r   Úrecord_failureÚLoginRateLimiter.record_failureŽ   s=   € Ø�Z‹ZØ×3Ñ3°I¼t¿yºy»{ÓKˆHØ�O‰OœDŸIšI›KÔ(÷ �ZŽZús   �A
A Á 
A.c                óˆ   • U R                      U R                  R                  US 5        S S S 5        g ! , (       d  f       g = fr^   )rc   rˆ   rv   )rK   r�   s     r   ÚclearÚLoginRateLimiter.clear“   s'   € Ø�Z‹ZØ�N‰N×Ñ˜y¨$Ô/÷ �ZŽZús	   �3³
Ac                ó\  • U R                   U   nX R                  -
  nU(       a+  US   U::  a"  UR                  5         U(       a  US   U::  a  M"  U(       dU  [        U R                   5       H<  nXQ:w  d  M
  U R                   U   (       a  M   U R                   R	                  US 5        M>     U$ )Nr   )rˆ   r†   ÚpopleftÚlistrv   )rK   r�   rq   rŽ   ÚcutoffÚkeys         r   r‹   Ú(LoginRateLimiter._active_failures_locked—   s�   € Ø—>‘> )Ñ,ˆØ×*Ñ*Ñ*ˆÞ˜8 A™;¨&Ó0Ø×ÑÔö ˜8 A™;¨&Õ0æô ˜DŸN™NÖ+�ØÕ#¨D¯N©N¸3×,?Ñ,?Ø—N‘N×&Ñ& s¨DÖ1ñ ,ð ˆr   )rˆ   rc   r…   r†   N)r…   r7   r†   r7   rQ   r}   )r�   rP   rQ   rR   )r�   rP   rQ   r}   )r�   rP   rq   r€   rQ   zDeque[float])rS   rT   rU   rV   ÚMAX_FAILURES_PER_WINDOWÚFAILURE_WINDOW_SECONDSrd   r�   r“   r–   r‹   rY   rO   r   r   rƒ   rƒ   }   sA   † ð 4Ø4ñ		&ð ð	&ð ð		&ð
 
õ	&ô6ô
)ô
0÷r   rƒ   c                ód  • U (       d  [         $ [        U 5      nUR                  (       dQ  UR                  (       d@  UR                  R                  S5      (       a   UR                  R                  S5      (       a  [         $ UR                  nUR                  (       a  USUR                  -   -  nU$ )z>Allow only same-origin absolute paths in post-login redirects.Ú/z//Ú?)ÚDEFAULT_TARGETr   rA   ÚnetlocÚpathÚ
startswithÚquery)r   ÚpartsÚresults      r   Ú	safe_nextrª   ¥   sx   € æÜÐÜ�U‹O€EØ‡|‡|�u—|—|¨5¯:©:×+@Ñ+@À×+EÑ+EÈÏÉ×I^ÑI^Ð_c×IdÑIdÜÐØ�Z‰Z€FØ‡{‡{Ø�#˜Ÿ™Ñ#Ñ#ˆØ€Mr   F©Ú
show_errorc               óˆ   • [         R                  " [        U 5      SS9nU(       a  SOSnSU SU S3nUR                  S5      $ )	NT)r
   uL   <p class="error" role="alert">ë¹„ë°€ë²ˆí˜¸ê°€ ì˜¬ë°”ë¥´ì§€ ì•ŠìŠµë‹ˆë‹¤.</p>Ú uN  <!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>V3 Face â€” Review Access</title>
  <style>
    * { box-sizing: border-box; }
    html, body { min-height: 100%; }
    body {
      margin: 0; display: grid; place-items: center; padding: 24px;
      color: #1e293b; background: #f8fafc;
      font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    }
    main {
      width: min(100%, 400px); padding: 34px; background: #fff;
      border: 1px solid #e2e8f0; border-radius: 16px;
      box-shadow: 0 20px 45px rgba(15, 23, 42, .08);
    }
    .mark { width: 34px; height: 34px; border-radius: 10px; margin-bottom: 22px;
      background: linear-gradient(135deg, #0052ff, #4d7cff); }
    h1 { margin: 0 0 8px; font-size: 22px; letter-spacing: -.02em; }
    .sub { margin: 0 0 24px; color: #64748b; font-size: 14px; line-height: 1.55; }
    label { display: block; margin-bottom: 8px; font-size: 13px; font-weight: 650; }
    input { width: 100%; height: 46px; border: 1px solid #cbd5e1; border-radius: 9px;
      padding: 0 13px; font: inherit; outline: none; }
    input:focus { border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37, 99, 235, .14); }
    button { width: 100%; height: 46px; margin-top: 14px; border: 0; border-radius: 9px;
      color: white; background: #155eef; font: inherit; font-weight: 700; cursor: pointer; }
    button:hover { background: #0b4fd6; }
    .error { margin: 0 0 14px; padding: 10px 12px; border-radius: 8px;
      color: #b42318; background: #fef3f2; font-size: 13px; }
  </style>
</head>
<body>
  <main>
    <div class="mark" aria-hidden="true"></div>
    <h1>V3 Face Review</h1>
    <p class="sub">GoodGangLabs ì‚¬ë‚´ ê²€í† ìš© íŽ˜ì�´ì§€ìž…ë‹ˆë‹¤.<br>ê³µìœ ë°›ì�€ ë¹„ë°€ë²ˆí˜¸ë¥¼ ìž…ë ¥í•´ ì£¼ì„¸ìš”.</p>
    z]
    <form method="post" action="/auth/login">
      <input type="hidden" name="next" value="u  ">
      <label for="password">ë¹„ë°€ë²ˆí˜¸</label>
      <input id="password" name="password" type="password" required autofocus
             autocomplete="current-password" maxlength="256">
      <button type="submit">ë“¤ì–´ê°€ê¸°</button>
    </form>
  </main>
</body>
</html>
r&   )ÚhtmlÚescaperª   r-   )Únext_urlr¬   Úescaped_nextÚerrorÚdocuments        r   Ú_login_documentrµ   ²   s`   € Ü—;’;œy¨Ó2¸$Ñ?€Lö ñ 	Wàð 
ð
'ðN €Gð /à/;¨nð 	=ðS2€Hðf �?‰?˜7Ó#Ð#r   c                  óF  ^ • \ rS rSr% SrS\S'   S\S'   S\S'   S	\S
'   SrS\S'   S$U 4S jjrS$U 4S jjrS$S jr	S$S jr
S$S jrS%S jrS$S jrS$S jrSSSS.         S&S jjrS'S jrS(S jrS)S jrS%U 4S jjr\S*S j5       rS+S jrS,S  jrS-S! jrS.S" jrS#rU =r$ )/ÚFaceViewerHandleréï   zFaceViewer/2r5   Úpassword_verifierr[   Úsessionsrƒ   Úrate_limiterr   ÚrootTrR   Útrust_proxyc                óP   >• [         TU ]  " US[        U R                  5      0UD6  g )NÚ	directory)Úsuperrd   rP   r¼   )rK   ÚargsÚkwargsÚ	__class__s      €r   rd   ÚFaceViewerHandler.__init__÷   s"   ø€ Ü‰Ò˜$ÐC¬#¨d¯i©i«.ÐC¸FÓCr   c                óÖ   >• U R                  SS5        U R                  SS5        U R                  SS5        U R                  SS5        U R                  S	S
5        [        TU ]	  5         g )NzCache-Controlzprivate, no-store, max-age=0ÚPragmazno-cachezX-Content-Type-OptionsÚnosniffzReferrer-Policyzsame-originzX-Frame-OptionsÚDENY)Úsend_headerrÀ   Úend_headers)rK   rÃ   s    €r   rÊ   ÚFaceViewerHandler.end_headersú   sd   ø€ ð 	×Ñ˜Ð*HÔIØ×Ñ˜ :Ô.Ø×ÑÐ1°9Ô=Ø×ÑÐ*¨MÔ:Ø×ÑÐ*¨FÔ3Ü‰ÑÕr   c                ó"   • U R                  SS9  g )NT©Ú	send_body©Ú_route_get_or_head©rK   s    r   Údo_GETÚFaceViewerHandler.do_GET  s   € Ø×Ñ¨$ÐÒ/r   c                ó"   • U R                  SS9  g )NFrÍ   rÏ   rÑ   s    r   Údo_HEADÚFaceViewerHandler.do_HEAD  s   € Ø×Ñ¨%ÐÒ0r   c                óÀ   • [        U R                  5      R                  nUS:X  a  U R                  5         g US:X  a  U R                  5         g U R	                  S5        g )Nz/auth/loginz/auth/logouté”  )r   r¥   Ú_handle_loginÚ_handle_logoutÚ
send_error)rK   r¥   s     r   Údo_POSTÚFaceViewerHandler.do_POST  sJ   € Ü˜Ÿ	™	Ó"×'Ñ'ˆØ�=Ó Ø×ÑÕ Ø�^Ó#Ø×ÑÕ!à�O‰O˜CÕ r   c               óŠ  • [        U R                  5      nUR                  nUS:X  a  U R                  SSSUS9  g US:X  am  [        [	        UR
                  5      R                  S[        /5      S   5      nU R                  5       (       a  U R                  U5        g U R                  XAS9  g UR                  S	5      (       a  U R                  S
5        g U R                  5       (       dJ  X2R
                  (       a  SUR
                  -   OS-   nU R                  S[        [        U5      SS9-   5        g U R                  US9  g )Nz/healthzéÈ   s   ok
ztext/plain; charset=utf-8rÍ   ú/loginÚnextr   z/auth/i•  r¢   r®   z/login?next=)Úsafe)r   r¥   Ú_send_bytesrª   r	   r§   rp   r£   Ú_is_authenticatedÚ	_redirectÚ_send_loginr¦   rÛ   r
   Ú_serve_static)rK   rÎ   r¨   r¥   r±   Ú	requesteds         r   rÐ   Ú$FaceViewerHandler._route_get_or_head  s  € Ü˜Ÿ™Ó#ˆØ�z‰zˆØ�:ÓØ×Ñ˜S 'Ð+FÐR[ÐÑ\ØØ�8ÓÜ ¤¨%¯+©+Ó!6×!:Ñ!:¸6ÄNÐCSÓ!TÐUVÑ!WÓXˆHØ×%Ñ%×'Ñ'Ø—‘˜xÔ(ð ð × Ñ  Ð Ñ?ØØ�?‰?˜8×$Ñ$Ø�O‰O˜CÔ ØØ×%Ñ%×'Ñ'Ø·{·{  u§{¡{Ò!2ÈÑKˆIØ�N‰N˜>¬E´)¸IÓ2FÈRÑ,PÑPÔQØØ×Ñ YÐÒ/r   c           	     ó¼  • U R                  5       nU R                  R                  U5      (       aS  U R                  S5        U R	                  S[        [        5      5        U R	                  SS5        U R                  5         g U R                  R                  SS5      n [        U5      nUS:  d
  U[        :”  a  U R                  S	5        g U R                  R                  S
S5      R                  SS5      S   R                  5       R!                  5       nUS:w  a  U R                  S5        g  [#        U R$                  R'                  U5      R)                  S5      SS9nUR                  SS/5      S   S S n[-        UR                  S[.        /5      S   5      nU R0                  R3                  U5      (       d-  U R                  R5                  U5        U R7                  USSS9  g U R                  R9                  U5        U R:                  R=                  5       nU R                  S5        U R	                  SU5        U R	                  S[>         SU SU R:                  R@                   S35        U R	                  SS5        U R                  5         g ! [         a    U R                  SS5         g f = f! [*         a    U R                  S5         g f = f)Ni­  zRetry-AfterúContent-LengthÚ0r®   i�  zInvalid Content-Lengthr   i�  úContent-TypeÚ;r   z!application/x-www-form-urlencodediŸ  r&   T)Úkeep_blank_valuesr/   é   rá   i‘  )Ústatusr¬   é/  ÚLocationú
Set-Cookier   z; Path=/; Max-Age=z ; Secure; HttpOnly; SameSite=Lax)!Ú
_client_idr»   r�   Úsend_responserÉ   rP   rŸ   rÊ   Úheadersrp   r7   r(   rÛ   ÚMAX_FORM_BYTESr<   ÚstripÚlowerr	   ÚrfileÚreadr   ÚUnicodeDecodeErrorrª   r£   r¹   rM   r“   ræ   r–   rº   rm   ÚCOOKIE_NAMEr_   )	rK   r�   Ú
raw_lengthÚlengthÚcontent_typeÚfieldsr/   r±   rl   s	            r   rÙ   ÚFaceViewerHandler._handle_login*  s‡  € Ø—O‘OÓ%ˆ	Ø×Ñ×'Ñ'¨	×2Ñ2Ø×Ñ˜sÔ#Ø×Ñ˜]¬CÔ0FÓ,GÔHØ×ÑÐ-¨sÔ3Ø×ÑÔØØ—\‘\×%Ñ%Ð&6¸Ó;ˆ
ð	Ü˜“_ˆFð �A‹:˜¤.Ó0Ø�O‰O˜CÔ ØØ—|‘|×'Ñ'¨¸Ó;×AÑAÀ#ÀqÓIÈ!ÑL×RÑRÓT×ZÑZÓ\ˆØÐ>Ó>Ø�O‰O˜CÔ Øð	Ü˜dŸj™jŸo™o¨fÓ5×<Ñ<¸WÓEÐY]Ñ^ˆFð —:‘:˜j¨2¨$Ó/°Ñ2°4°CÐ8ˆÜ˜VŸZ™Z¨´Ð0@ÓAÀ!ÑDÓEˆØ×%Ñ%×,Ñ,¨X×6Ñ6Ø×Ñ×,Ñ,¨YÔ7Ø×Ñ˜X¨c¸dÐÑCØØ×Ñ×Ñ 	Ô*Ø—‘×$Ñ$Ó&ˆØ×Ñ˜3ÔØ×Ñ˜ XÔ.Ø×ÑØÜˆm˜1˜U˜GÐ#5°d·m±m×6OÑ6OÐ5Pð Q-ð -ô	
ð
 	×ÑÐ)¨3Ô/Ø×ÑÕøô? ó 	Ø�O‰O˜CÐ!9Ô:Ùð	ûô "ó 	Ø�O‰O˜CÔ Ùð	ús$   Â!J Ä22J= ÊJ:Ê9J:Ê=KËKc                ó  • U R                   R                  U R                  5       5        U R                  S5        U R	                  SS5        U R	                  S[
         S35        U R	                  SS5        U R                  5         g )Nrò   ró   rà   rô   z4=; Path=/; Max-Age=0; Secure; HttpOnly; SameSite=Laxrë   rì   )rº   rw   Ú_session_tokenrö   rÉ   rþ   rÊ   rÑ   s    r   rÚ   Ú FaceViewerHandler._handle_logoutV  sv   € Ø�‰×Ñ˜T×0Ñ0Ó2Ô3Ø×Ñ˜3ÔØ×Ñ˜ XÔ.Ø×ÑØÜˆmÐOÐPô	
ð 	×ÑÐ)¨3Ô/Ø×ÑÕr   rß   F)rñ   r¬   rÎ   c               ó~   • [        XS9nU R                  U5        U R                  SS5        U R                  USUS9  g )Nr«   zContent-Security-Policyzjdefault-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'ztext/html; charset=utf-8rÍ   )rµ   rö   rÉ   Ú_finish_bytes)rK   r±   rñ   r¬   rÎ   Úbodys         r   ræ   ÚFaceViewerHandler._send_logina  sJ   € ô ˜xÑ?ˆØ×Ñ˜6Ô"Ø×ÑÐ2ð  5aô  	bØ×Ñ˜4Ð!;ÀyÐÒQr   c               óF   • U R                  U5        U R                  X#US9  g )NrÍ   )rö   r  )rK   rñ   r	  r  rÎ   s        r   rã   ÚFaceViewerHandler._send_bytesn  s#   € Ø×Ñ˜6Ô"Ø×Ñ˜4¸ÐÒCr   c               óÖ   • U R                  SU5        U R                  S[        [        U5      5      5        U R                  5         U(       a  U R                  R                  U5        g g )Nrí   rë   )rÉ   rP   r   rÊ   ÚwfileÚwrite)rK   r	  r  rÎ   s       r   r  ÚFaceViewerHandler._finish_bytesr  sQ   € Ø×Ñ˜¨Ô6Ø×ÑÐ)¬3¬s°4«y«>Ô:Ø×ÑÔÞØ�J‰J×Ñ˜TÕ"ð r   c                óŽ   • U R                  S5        U R                  SU5        U R                  SS5        U R                  5         g )Nrò   ró   rë   rì   )rö   rÉ   rÊ   )rK   Úlocations     r   rå   ÚFaceViewerHandler._redirecty  s=   € Ø×Ñ˜3ÔØ×Ñ˜ XÔ.Ø×ÑÐ)¨3Ô/Ø×ÑÕr   c          	     óè  >• U R                  U R                  5      n[        R                  R                  U5      (       a%  U(       a  [        T	U ]  5         g [        T	U ]  5         g [        R                  R                  U5      (       d  U R                  S5        g [        R                  R                  U5      nU R                  R                  S5      nU(       dŠ  U R                  S5        U R                  SU R                  U5      5        U R                  SS5        U R                  S[        U5      5        U R!                  5         U(       a  U R#                  USU5        g U R%                  XC5      nUcI  U R                  S	5        U R                  S
SU 35        U R                  SS5        U R!                  5         g Uu  pgXv-
  S-   nU R                  S5        U R                  SU R                  U5      5        U R                  SS5        U R                  S
SU SU SU 35        U R                  S[        U5      5        U R!                  5         U(       a  U R#                  X&U5        g g )NrØ   ÚRangerß   rí   zAccept-Rangesr8   rë   r   i   zContent-Rangezbytes */rì   r   éÎ   zbytes Ú-r¡   )Útranslate_pathr¥   ÚosÚisdirrÀ   rÒ   rÕ   ÚisfilerÛ   Úgetsizer÷   rp   rö   rÉ   Ú
guess_typerP   rÊ   Ú_copy_file_rangeÚ_parse_range)
rK   rÎ   r¥   ÚsizeÚrange_headerÚ
byte_rangeÚstartÚendr   rÃ   s
            €r   rç   ÚFaceViewerHandler._serve_static  s  ø€ Ø×"Ñ" 4§9¡9Ó-ˆÜ�7‰7�=‰=˜×ÑÞÜ‘‘Ô ð ô ‘‘Ô!ØÜ�w‰w�~‰~˜d×#Ñ#Ø�O‰O˜CÔ ØÜ�w‰w�‰˜tÓ$ˆØ—|‘|×'Ñ'¨Ó0ˆÞØ×Ñ˜sÔ#Ø×Ñ˜^¨T¯_©_¸TÓ-BÔCØ×Ñ˜_¨gÔ6Ø×ÑÐ-¬s°4«yÔ9Ø×ÑÔÞØ×%Ñ% d¨A¨tÔ4ØØ×&Ñ& |Ó:ˆ
ØÑØ×Ñ˜sÔ#Ø×Ñ˜_°¸¸Ð.?Ô@Ø×ÑÐ-¨sÔ3Ø×ÑÔØØ‰
ˆØ‘˜q‘ˆØ×Ñ˜3ÔØ×Ñ˜¨¯©¸Ó)>Ô?Ø×Ñ˜¨'Ô2Ø×Ñ˜¨F°5°'¸¸3¸%¸qÀÀÐ*GÔHØ×ÑÐ)¬3¨v«;Ô7Ø×ÑÔÞØ×!Ñ! $¨vÕ6ð r   c                óz  • [         R                  U R                  5       5      nU(       a  US::  a  g UR                  5       u  p4U(       d  U(       d  g U(       d%  [	        U5      nUS::  a  g [        SX-
  5      US-
  4$ [	        U5      nU(       a  [	        U5      OUS-
  n[        XqS-
  5      nXa:¼  d  Xg:”  a  g Xg4$ )Nr   r   )ÚRANGE_REÚ	fullmatchrù   Úgroupsr7   ÚmaxÚmin)r   r   ÚmatchÚ	raw_startÚraw_endÚsuffix_lengthr#  r$  s           r   r  ÚFaceViewerHandler._parse_range§  s¦   € ä×"Ñ" 5§;¡;£=Ó1ˆÞ˜ ›	ØØ"Ÿ\™\›^Ñˆ	Þ¦ØÞÜ ›LˆMØ Ó!ØÜ�q˜$Ñ.Ó/°¸±Ð9Ð9Ü�I“ˆÞ%Œc�'Œl¨4°!©8ˆÜ�#˜a‘xÓ ˆØ‹=˜E›KØØˆzÐr   c                ó\  •  [        US5       nUR                  U5        UnU(       aU  UR                  [        SU5      5      nU(       d  O2U R                  R                  U5        U[        U5      -  nU(       a  MU  S S S 5        g ! , (       d  f       g = f! [        [        4 a     g f = f)NÚrbi   )	ÚopenÚseekrü   r+  r  r  r   ÚBrokenPipeErrorÚConnectionResetError)rK   r¥   r#  r   ÚsourceÚ	remainingÚchunks          r   r  Ú"FaceViewerHandler._copy_file_range»  sŽ   € ð	Ü�d˜DÔ! VØ—‘˜EÔ"Ø"�	ÞØ"ŸK™K¬¨I°yÓ(AÓB�EÞ ØØ—J‘J×$Ñ$ UÔ+Ø¤ U£Ñ+�I÷  �i÷ "×!Ö!ûô  Ô!5Ð6ó 	Ùð	ús/   ‚B ŽA.BÁ>B Â
BÂB ÂB ÂB+Â*B+c                óT   • U R                   R                  U R                  5       5      $ r^   )rº   rs   r  rÑ   s    r   rä   Ú#FaceViewerHandler._is_authenticatedÉ  s    € Ø�}‰}×"Ñ" 4×#6Ñ#6Ó#8Ó9Ð9r   c                óD  • U R                   R                  S5      nU(       d  g [        R                  R	                  5       n UR                  U5        UR                  [        5      nU(       a  UR                  $ S $ ! [        R                  R                   a     g f = f)NÚCookie)	r÷   rp   ÚhttpÚcookiesÚSimpleCookieÚloadÚCookieErrorrþ   r   )rK   Ú
raw_cookieÚcookieÚmorsels       r   r  Ú FaceViewerHandler._session_tokenÌ  s~   € Ø—\‘\×%Ñ% hÓ/ˆ
ÞØÜ—‘×*Ñ*Ó,ˆð	Ø�K‰K˜
Ô#ð —‘œKÓ(ˆÞ%ˆv�|‰|Ð/¨4Ð/øô �|‰|×'Ñ'ó 	Ùð	ús   ÁA> Á>BÂBc                óö   • U R                   (       aJ  U R                  R                  SS5      R                  5       n [	        [
        R                  " U5      5      $ U R                  S   $ ! [         a     Nf = f)NzCF-Connecting-IPr®   r   )	r½   r÷   rp   rù   rP   Ú	ipaddressÚ
ip_addressr(   Úclient_address)rK   Ú	forwardeds     r   rõ   ÚFaceViewerHandler._client_idØ  sm   € Ø××ØŸ™×(Ñ(Ð);¸RÓ@×FÑFÓHˆIðÜœ9×/Ò/°	Ó:Ó;Ð;ð ×"Ñ" 1Ñ%Ð%øô ó Ùðús   ½A+ Á+
A8Á7A8rO   ©rQ   r}   )rÎ   rR   rQ   r}   )
r±   rP   rñ   r7   r¬   rR   rÎ   rR   rQ   r}   )
rñ   r7   r	  r8   r  rP   rÎ   rR   rQ   r}   )r	  r8   r  rP   rÎ   rR   rQ   r}   )r  rP   rQ   r}   )r   rP   r   r7   rQ   ztuple[int, int] | None)r¥   rP   r#  r7   r   r7   rQ   r}   )rQ   rR   )rQ   r   r~   )rS   rT   rU   rV   Úserver_versionrW   r½   rd   rÊ   rÒ   rÕ   rÜ   rÐ   rÙ   rÚ   ræ   rã   r  rå   rç   Ústaticmethodr  r  rä   r  rõ   rY   Ú__classcell__)rÃ   s   @r   r·   r·   ï   sÜ   ø‡ Ø#€NØ'Ó'ØÓØ"Ó"Ø
ƒJØ€K�Ó÷D÷	ô0ô1ô!ô0ô,*ôX	ð Ø ØñRàðRð ð	Rð
 ðRð ðRð 
õRôDô#ô÷&7ðP óó ðô&ô:ô
0÷&ò &r   r·   ©Úsession_ttlr½   c               ó¤   •  " S S[         5      nU R                  5       Ul        Xl        [	        U5      Ul        [        5       Ul        X4l        U$ )Nc                  ó   • \ rS rSrSrg)Ú2build_handler.<locals>.ConfiguredFaceViewerHandlerié  rO   N)rS   rT   rU   rV   rY   rO   r   r   ÚConfiguredFaceViewerHandlerrV  é  s   † Úr   rW  )	r·   Úresolver¼   r¹   r[   rº   rƒ   r»   r½   )r¼   ÚverifierrS  r½   rW  s        r   Úbuild_handlerrZ  â  sL   € ôÔ&7ô ð (,§|¡|£~ÐÔ$Ø4<Ô1Ü+7¸Ó+DÐÔ(Ü/?Ó/AÐÔ,Ø.9Ô+Ø&Ð&r   c                óˆ   •  [        U 5      nUS::  a  [        U S35      eU$ ! [         a  n[        U S35      UeS nAff = f)Nz must be an integerr   z must be positive)r7   r(   )Ú	raw_valueÚnamer   rE   s       r   Ú_positive_intr^  ô  s[   € ð@Ü�I“ˆð �ƒzÜ˜D˜6Ð!2Ð3Ó4Ð4Ø€Løô	 ó @Ü˜D˜6Ð!4Ð5Ó6¸CÐ?ûð@ús   ‚# £
A­<¼Ac                 ó^  • [         R                  R                  SS5      n U (       d  [        S5      e [        R                  U 5      n[        [         R                  R                  SS5      S5      n[        [         R                  R                  S[        [        5      5      S5      n[        [         R                  R                  S[        [        5      5      5      R                  5       nUR                  5       (       d  [        SU 35      e[         R                  R                  S	S
5      S;  n[        XQX6S9n[        R                   R#                  SU4U5      nSUl        ['        SU SU 3SS9  UR)                  5         g ! [         a  n[        [        U5      5      UeS nAff = f)NÚFACE_VIEWER_PASSWORD_HASHr®   zAFACE_VIEWER_PASSWORD_HASH is required; refusing to start unlockedÚFACE_VIEWER_PORTÚ8095ÚFACE_VIEWER_SESSION_TTLÚFACE_VIEWER_ROOTz%FACE_VIEWER_ROOT is not a directory: ÚFACE_VIEWER_TRUST_PROXYÚ1>   rì   ÚFalseÚfalserR  z	127.0.0.1Tzserving password-protected z on 127.0.0.1:)Úflush)r  Úenvironrp   Ú
SystemExitr5   rF   r^  rP   r�   r(   r   ÚDEFAULT_ROOTrX  Úis_dirrZ  r?  ÚserverÚThreadingHTTPServerÚdaemon_threadsÚprintÚserve_forever)	Úencoded_hashrY  ÚportrS  rE   r¼   r½   Úhandlerrn  s	            r   Úserverv  þ  sW  € Ü—:‘:—>‘>Ð"=¸rÓB€LÞÜÐ\Ó]Ð]ð,Ü#×0Ñ0°Ó>ˆÜœRŸZ™ZŸ^™^Ð,>ÀÓGÐI[Ó\ˆÜ#Ü�J‰J�N‰NÐ4´cÔ:MÓ6NÓOØ%ó
ˆô ”—
‘
—‘Ð1´3´|Ó3DÓEÓF×NÑNÓP€DØ�;‰;�=‰=ÜÐ@ÀÀÐGÓHÐHÜ—*‘*—.‘.Ð!:¸CÓ@ÐH_Ñ_€KÜ˜D¸Ñ]€GÜ�[‰[×,Ñ,¨k¸4Ð-@À'ÓJ€FØ €FÔÜ	Ð'¨ v¨^¸D¸6Ð
BÈ$ÒOØ
×ÑÕøô ó ,Üœ˜S›Ó"¨Ð+ûð,ús   ´A6F Æ
F,ÆF'Æ'F,c                 óž  • [         R                  " [        S9n U R                  SSSS9  U R	                  5       nUR
                  (       aR  [        R                  " S5      n[        R                  " S5      nX#:w  a  [        S5      e [        [        U5      5        g [        5         g ! [         a  n[        [        U5      5      UeS nAff = f)	N)Údescriptionz--hash-passwordÚ
store_truez8prompt for a password and print a salted hash, then exit)ÚactionÚhelpz
Password: zConfirm password: zpasswords do not match)ÚargparseÚArgumentParserÚ__doc__Úadd_argumentÚ
parse_argsÚhash_passwordÚgetpassrk  rq  r2   r(   rP   rv  )ÚparserrÁ   r/   ÚconfirmationrE   s        r   Úmainr…    s¶   € Ü×$Ò$´Ñ9€FØ
×ÑØØØGð ñ ð
 ×ÑÓ€DØ××Ü—?’? <Ó0ˆÜ—’Ð';Ó<ˆØÓ#ÜÐ5Ó6Ð6ð	0ÜÔ$ XÓ.Ô/ð 	Ü	…Gøô ó 	0ÜœS ›XÓ&¨CÐ/ûð	0ús   ÂB( Â(
CÂ2CÃCÚ__main__)r   r8   rQ   rP   )r   rP   rQ   r8   )r/   rP   r!   r7   rQ   rP   )r   r   rQ   rP   )r±   rP   r¬   rR   rQ   r8   )
r¼   r   rY  r5   rS  r7   r½   rR   rQ   ztype[FaceViewerHandler])r\  rP   r]  rP   rQ   r7   rN  )=r~  Ú
__future__r   r|  r   Úcollectionsr   r   Údataclassesr   r‚  r+   rI   r¯   Úhttp.cookiesr?  Úhttp.serverrI  r  Úpathlibr   Úrer)   ra   rj   Útypingr   Úurllib.parser	   r
   r   Ú__file__rX  Úparentsrl  r£   rþ   rø   r.   ÚPASSWORD_HASH_ITERATIONSr�   rŸ   rž   Úcompiler'  r   r    r2   r5   r[   rƒ   rª   rµ   rn  ÚSimpleHTTPRequestHandlerr·   rZ  r^  rv  r…  rS   rO   r   r   Ú<module>r•     sy  ðñõ #ã Û ß *Ý !Û Û Û Û Û Û Û Û 	Ý Û 	Û Û Û Ý ß 2Ñ 2ñ �H‹~×%Ñ%Ó'×/Ñ/°Ñ2€Ø8€Ø#€Ø€Ø&Ð Ø"Ð Ø!Ð ØÐ ØÐ Ø�:Š:Ð,Ó-€ôHôEð <T÷ Zñ �$Ñ÷:ð :ó ð:÷:,ñ ,÷D%ñ %ôP
ð :?÷ :$ôzp&˜Ÿ™×<Ñ<ô p&ðn +Øñ'Ø
ð'àð'ð ð	'ð
 ð'ð õ'ô$ôô0ð* ˆzÓÙ…Fð r   