
    b                        d Z ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlm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 ddlmZ ddlmZ ddlmZ ddlmc mZ ddlmZmZ ddlmZ ddlm Z  ddlm!Z! ddlm"Z" ddl#mc m$Z% ddl&m'Z' ddl(m)Z) ddl*m+Z+m,Z,  e+ e,ddd             ddl-m.Z. e
j^                  ja                  dd      Z1dZ2 e"jf                  ed       ed       G d d ejh                                      Z5 e"jf                  ed       G d! d"ejl                               Z7 e"jf                  ed       G d# d$ejp                               Z9y)%zTests for signurl command.    )absolute_import)print_function)division)unicode_literalsdatetime)	timedeltaN)HAVE_OPENSSL)HAVE_CRYPTO)CommandException)
GcsJsonApi)IamcredentailsApi)ImpersonationCredentials)	SkipForS3
SkipForXML)ObjectToURI)SetBotoConfigForTest)SetEnvironmentForTest)unittest)client)ServiceAccountCredentials)add_moveMovedModulemockzunittest.mock)r   GSUtil test_impersonate_service_accountz$test%40developer.gserviceaccount.comzsignurl requires pyopenssl.z.Signed URLs are only supported for gs:// URLs.c                   
   e Zd ZdZd Zd Zd Zd Z ej                  e
 d      d        Z ej                  e
d      d	        Zd
 Zd Zd Zd Z ej                  ed       ed       ed      d                      Zd Zy)TestSignUrlz&Integration tests for signurl command.c                     t        | d      s-t        j                  dd      }| j                  |      | _        | j                  S )Njson_ks_filegslibtests/test_data/test.jsoncontents)hasattrpkgutilget_dataCreateTempFiler    selfr$   s     +platform/gsutil/gslib/tests/test_signurl.py_GetJSONKsFilezTestSignUrl._GetJSONKsFile>   sA    4(!!'+FGh--x-@d    c                     t        | d      s-t        j                  dd      }| j                  |      | _        | j                  S )Nks_filer!   tests/test_data/test.p12r#   )r%   r&   r'   r(   r/   r)   s     r+   
_GetKsFilezTestSignUrl._GetKsFileE   s@    4#  !!'+EFh((((;dl<<r-   c                     | j                   rd}nd}| j                  g dd|      }| j                   r| j                  d|       y| j                  d|       y)	z@Tests signurl fails with out of bounds value for valid duration.      )signurl-d123dr/   gs://uriTreturn_stderrexpected_statusz&value must be less than or equal to 7dz6CommandException: Max valid duration allowed is 7 daysN)_use_gcloud_storage	RunGsUtilassertIn)r*   r;   stderrs      r+   testSignUrlInvalidDurationz&TestSignUrl.testSignUrlInvalidDurationZ   s]    oo^^L*.,;  =F 
mm<fE
mmLr-   c                 T    | j                  g ddd      }| j                  d|       y)z5Tests signurl with -u flag fails duration > 12 hours.)r5   r6   13h-ur8   Tr4   r9   z8CommandException: Max valid duration allowed is 12:00:00N)r=   r>   )r*   r?   s     r+   /testSignUrlInvalidDurationWithUseServiceAccountz;TestSignUrl.testSignUrlInvalidDurationWithUseServiceAccounti   s2    ^^F*.,-  /F 	MMLr-   z+signurl requires crypto to decode .p12 keysc           	          | j                         }| j                  |d      }ddddd| j                         t        |      g}| j	                  |dd	      }| j                  d
|       y )N   z
bucket_urir$   r5   -p
notasecret-mPUTr4   r9   zsCommandException: pyca/cryptography is not available. Either install it, or please consider using the .json keyfileCreateBucketCreateObjectr1   surir=   r>   )r*   rH   
object_uricmdr?   s        r+   ,testSignUrlRaiseErrorForP12KeysWithoutCryptoz8TestSignUrl.testSignUrlRaiseErrorForP12KeysWithoutCryptoq   sy    ""$J""j4"HJ4tUZC
 ^^Cq!^DFMM  H  JP  Qr-   z,signurl requires crypto to decode .p12 keys.c           	      @   | j                         }| j                  |d      }ddddd| j                         t        |      g}| j	                  |d	      }| j                  d
|       | j                  d|       | j                  d|       | j                  d|       y)z=Tests signurl output of a sample object with pkcs12 keystore.rF   rG   r5   rI   rJ   rK   rL   Treturn_stdoutz1x-goog-credential=test.apps.googleusercontent.comx-goog-expires=3600%2Fus-central1%2F	PUT	NrM   r*   rH   rQ   rR   stdouts        r+   testSignUrlOutputP12z TestSignUrl.testSignUrlOutputP12}   s     ""$J""j4"HJ4tUZC
 ^^Ct^4FMMEvNMM'0MM%v.MM)V$r-   c                 J   | j                         }| j                  |d      }ddd| j                         t        |      g}| j	                  |d      }| j                  dt        z   |       | j                  d	|       | j                  d
|       | j                  d|       y)z;Tests signurl output of a sample object with JSON keystore.rF   rG   r5   rK   rL   TrU   x-goog-credential=rW   rX   rY   N)rN   rO   r,   rP   r=   r>   
TEST_EMAILrZ   s        r+   testSignUrlOutputJSONz!TestSignUrl.testSignUrlOutputJSON   s    ""$J""j4"HJdE4#6#6#8$z:J
KC^^Ct^4FMM&3V<MM'0MM%v.MM)V$r-   c                    | j                  d      }| j                  |d      }d| j                         |j                  g}| j	                  |d      }| j                  dt        z   |       | j                  d|j                  z   |       y	)
zCTests signurl output of a sample object version with JSON keystore.T)versioning_enabledrF   rG   r5   rU   r^   zgeneration=N)rN   rO   r,   version_specific_urir=   r>   r_   
generationrZ   s        r+   -testSignUrlWithJSONKeyFileAndObjectGenerationz9TestSignUrl.testSignUrlWithJSONKeyFileAndObjectGeneration   s    ""d";J""j4"HJd))+Z-L-L
MC^^Ct^4FMM&3V<MM-*"7"77@r-   c                    g d}g d}| j                  t        |      t        |             ddddd| j                         g}|j                  |       | j	                  |d	      }|j                  d
      }| j                  t        |      t        |      dz          |dd  }t        |||      D ]C  \  }}}| j                  ||       | j                  ||       | j                  dt        z   |       E | j                  d|       y )N)zgs://example.org/test 1zgs://example.org/test/test 2u'   gs://example.org/Аудиоарi хив)zEhttps://storage.googleapis.com/example.org/test%201?x-goog-signature=zJhttps://storage.googleapis.com/example.org/test/test%202?x-goog-signature=z}https://storage.googleapis.com/example.org/%D0%90%D1%83%D0%B4%D0%B8%D0%BE%D0%B0%D1%80i%20%D1%85%D0%B8%D0%B2?x-goog-signature=r5   rK   rL   -rusTrU   
r3   r4   r^   z%2Fus%2F)	assertEquallenr,   extendr=   splitzipr>   r_   )	r*   objsexpected_partial_urlscmd_argsr[   linesobjlinepartial_urls	            r+   %testSignUrlWithURLEncodeRequiredCharsz1TestSignUrl.testSignUrlWithURLEncodeRequiredChars   s    D 	SY$9 :; 	4dH OOD^^HD^9FLLESZTQ/ !"IE"%dE3H"IT;
mmC
mmK&
mm(3T: #J 	MM*f%r-   c                 |   g d}g }| j                         }|D ]%  }|j                  | j                  ||d             ' | j                  d| j	                         t        |      dz   gd      }| j                  t        |j                  d            d	       |D ]  }| j                  t        |      |        y )
N)test1test2test3r-   )rH   object_namer$   r5   z/*TrU   ri      )
rN   appendrO   r=   r,   rP   rj   rk   rm   r>   )r*   ro   obj_urlsbucketobj_namer[   obj_urls          r+   testSignUrlWithWildcardz#TestSignUrl.testSignUrlWithWildcard   s    &DH Foo


v(0%(  *+  ^^						f		 	  F 	Sd+,a0
mmDM6* r-   z/Test requires test_impersonate_service_account.Tests only uses gs credentials.Tests only run on JSON API.c                     t        ddt        fg      5  | j                  g ddd      \  }}d d d        | j                  d       | j                  dt        z         y # 1 sw Y   5xY w)NCredentialsgs_impersonate_service_account)r5   rg   us-east1rC   zgs://pubT)rV   r:   z"https://storage.googleapis.com/pubz&All API calls will be executed as [%s])r   SERVICE_ACCOUNTr=   r>   )r*   r[   r?   s      r+   testSignUrlWithServiceAccountz)TestSignUrl.testSignUrlWithServiceAccount   sv    
 
/O / 1 2 
3~~
9 & nff
3 	MM6?MM:_L
3 
3s   A""A+c                 v    | j                  d| j                         dgd       | j                  g dd       y)z0Tests the signurl output of a non-existent file.r5   zgs://r4   )r;   )r5   zfile://tmp/abczgs://bucketN)r=   r,   r*   s    r+   testSignUrlOfNonObjectUrlz%TestSignUrl.testSignUrlOfNonObjectUrl   s?    NNIt224g>#$  &NN?#$  &r-   N)__name__
__module____qualname____doc__r,   r1   r@   rD   r   
skipUnlessr   rS   r\   r`   re   rv   r   r   r   r   r   r    r-   r+   r   r   9   s     /* 8;(UV	Q W	Q 8{$RS% T%	%A"&H+. 8HJ./+, - 0J&r-   r   c                   @    e Zd ZdZdZ fdZd Zd Zd Z e	d       e
d      d	               Z e	d       e
d      d
               Z e	d       e
d       ej                  d       ej                  d      d                             Zd Zd Zd Zd Zd Z xZS )UnitTestSignUrlz#Unit tests for the signurl command.Nc                    t         t        |           t        j                  dd      }t
        j                  j                  j                  |d      \  | _	        | _
        d }|t
        j                  j                  _        y )Nr!   r0   rJ   c                  "    t        dddddd      S )Nil  r4   r   r|   7   r   r   r-   r+   fake_nowz'UnitTestSignUrl.setUp.<locals>.fake_now  s    dAq!Q++r-   )superr   setUpr&   r'   r!   commandsr5   _ReadKeystorekeyclient_emailutilssignurl_helper_NowUTC)r*   ks_contentsr   	__class__s      r+   r   zUnitTestSignUrl.setUp   se    	/4&(""7,FGK"'.."8"8"F"F\##DHd, *2EKK&r-   c                 X    | j                         }t        |j                  d   d   d<   |S )NapiclassgsJSON)MakeGsUtilApir   api_map)r*   mock_api_delegators     r+   _get_mock_api_delegatorz'UnitTestSignUrl._get_mock_api_delegator	  s5    ++- <Fz*408r-   c           
         dt        d      fdt        d      fdt        d      fd	t        d
      fdt        d      fdt        d      fdt        d      fddg	}|D ]A  \  }}	 t        j                  j                  j	                  |      }| j                  ||       C y # t        $ r || j                  d       Y cw xY w)N1hr4   )hours2dr3   )days5Dr|   35s#   seconds33!   22m   )minutes)z3.7N)27ZNz{0} failed to parse)r	   r!   r   r5   _DurationToTimeDeltarj   r   fail)r*   testsinpexpectedtds        r+   testDurationSpecz UnitTestSignUrl.testDurationSpec  s    	yq!"	ya !	ya !		"%&	yq!"	yr"#		"%&
E X+^^##88=X&   +
)))
*+s   );B''CCc                 F   t         j                  }t        d      }t        dg      5  t        j
                  j                  j                  | j                  ddd| j                  dd|| j                  d	d
      }ddd       | j                  |       y# 1 sw Y   xY w)zBTests the _GenSignedUrl function with a PUT method using Key file.  r   r   gs_hostzstorage.googleapis.comNFr   	RESUMABLEtest/test.txtus-east 
apiuse_service_accountprovider	client_idmethodgcs_pathdurationloggerregioncontent_type)sigsTEST_SIGN_PUT_SIGr	   r   r!   r   r5   _GenSignedUrlr   r   r   rj   r*   r   r   
signed_urls       r+   testSignPutUsingKeyFilez'UnitTestSignUrl.testSignPutUsingKeyFile(  s    %%H&H	  : ; 
<>>))77
((#%%" 8 j
< 	Xz*
< 
<   ABB r   r   c                    t         j                  }t        d      }| j                         }|j	                  d      }t        j                  t              }d|_        d|j                  _
        ||_        t        dg      5  t        j                  j                  j!                  d|d	d| j"                  d
d|| j$                  dd      }ddd       | j'                  |       |j                  j)                  d       y# 1 sw Y   7xY w)zATests the _GenSignedUrl function PUT method with service account.r   r   r   specfake_service_account_email)fake_key   fake_signaturer   NTrL   r   r   r   r      GOOG4-RSA-SHA256
19000101T000555Z
19000101/us-east1/storage/goog4_request
7f110b30eeca7fdd8846e876bceee85384d8e4c7388b3596544b1b503f9e2320)r   &TEST_SIGN_URL_PUT_WITH_SERVICE_ACCOUNTr	   r   _GetApir   Mockr   service_account_email	sign_blobreturn_valuecredentialsr   r!   r   r5   r   r   r   rj   assert_called_once_with)r*   r   r   r   json_apimock_credentialsr   s          r+   testSignPutUsingServiceAccountz.UnitTestSignUrl.testSignPutUsingServiceAccount=  s     ::H&H557!))$/Hyy&?@-I*.M++H	  : ; 
<>>))77
 "%%" 8 j
< 	Xz*66	
< 
<s   A	DDc                    t         j                  }t        d      }| j                         }|j	                  d      }t        j                  t        j                        }d|_	        ||_
        t        dg      5  | j                  t        t        j                  j                   j"                  d|dd| j$                  d	d
|| j&                  dd       ddd       y# 1 sw Y   yxY w)zTests the _GenSignedUrl with incorrect account type.

    Test that GenSignedUrl function with 'use_service_account' set to True
    and a service account not used for credentials raises an error.
    r   r   r   r   r   r   NTrL   r   r   r   r   )r   r   r	   r   r   r   r   r   OAuth2Credentialsr   r   r   assertRaisesr   r!   r   r5   r   r   r   )r*   r   r   r   r   r   s         r+   #testSignUrlWithIncorrectAccountTypez3UnitTestSignUrl.testSignUrlWithIncorrectAccountType_  s     ::H&H557!))$/Hyyf&>&>?-I*+H	  : ; 
<
(..<<.,0!%"&"3"3$!0!)#{{)%'  )
< 
< 
<s   <ACC'z(gslib.iamcredentials_api.apitools_clientz*gslib.iamcredentials_api.apitools_messagesc                    t         j                  }t        d      }| j                         }|j	                  d      }t        j                  t              }t        j                         }||j                  _	        t        t        j                               }	|	|_        d|_        t        j                         }
d|
_        |
|j                  j                  _	        ||_        t#        dg      5  t$        j&                  j(                  j+                  d	|d
d| j,                  dd|| j.                  dd      }d	d	d	       | j1                  |       |j2                  j5                  d       y	# 1 sw Y   8xY w)zTests the _GenSignedUrl function PUT method with impersonation.

    Test _GenSignedUrl function using an impersonated service account.
    r   r   r   r   )r   r   r   r   NTrL   r   r   r   r   r   )payload)r   r   r	   r   r   r   r   r   IamcredentialsV1r   r   r   service_account_id
signedBlobprojects_serviceAccountsSignBlobr   r   r!   r   r5   r   r   r   rj   SignBlobRequestr   )r*   mock_api_messagesmock_apiclientr   r   r   r   r   api_client_objmock_iam_cred_api	mock_respr   s               r+   )testSignPutUsingImersonatedServiceAccountz9UnitTestSignUrl.testSignPutUsingImersonatedServiceAccount  sU    ::H&H557!))$/H yy&>?YY[N3AN##0 *diikB,*F' 		I,IDMN++44A+H	  : ; 
<>>))77
 "%%" 8 j
< 	Xz*%%==/ > 0
< 
<s   2A	E22E;c                    t         j                  } G d dt              } |       }t        d      }t	        dg      5  t
        j                  j                  j                  | j                  ddd| j                  d	d
||dd      }ddd       | j                  |       | j                  |j                          |       }t	        dg      5  t
        j                  j                  j                  | j                  ddd| j                  d	d
||dd      }ddd       | j                  |j                         y# 1 sw Y   xY w# 1 sw Y   1xY w)z;Tests _GenSignedUrl using key file with a RESUMABLE method.c                       e Zd Zd Zd Zy)@UnitTestSignUrl.testSignResumableWithKeyFile.<locals>.MockLoggerc                     d| _         y )NFwarning_issuedr   s    r+   __init__zIUnitTestSignUrl.testSignResumableWithKeyFile.<locals>.MockLogger.__init__  s
    #r-   c                     d| _         y )NTr  )r*   
unused_msgs     r+   warnzEUnitTestSignUrl.testSignResumableWithKeyFile.<locals>.MockLogger.warn  s
    "r-   N)r   r   r   r  r  r   r-   r+   
MockLoggerr    s    $#r-   r  r   r   r   NFr   r   r   r   r   r   z
image/jpeg)r   TEST_SIGN_RESUMABLEobjectr	   r   r!   r   r5   r   r   r   rj   
assertTruer  assertFalse)r*   r   r  mock_loggerr   r   mock_logger2s          r+   testSignResumableWithKeyFilez,UnitTestSignUrl.testSignResumableWithKeyFile  sF   ''H#V # ,K&H	  : ; 
<>>))77
((#%%" 8 j
< 	Xz*OOK../<L	  : ; 
<>>))77
((#%%"# 8 %j
< 	\001E
< 
<&
< 
<s   A	D=A	E	=E	Ec                 F   t         j                  }t        d      }t        dg      5  t        j
                  j                  j                  | j                  ddd| j                  dd|| j                  d	d
      }ddd       | j                  |       y# 1 sw Y   xY w)zFTests _GenSignedUrl using key file with a PUT method and content type.r   r   r   NFr   rL   r   euz
text/plainr   )r   TEST_SIGN_URL_PUT_CONTENTr	   r   r!   r   r5   r   r   r   r   rj   r   s       r+   $testSignurlPutContentypeUsingKeyFilez4UnitTestSignUrl.testSignurlPutContentypeUsingKeyFile  s    --H&H	  : ; 
<>>))77
((#%%"# 8 %j
< 	Xz*
< 
<r   c                 F   t         j                  }t        d      }t        dg      5  t        j
                  j                  j                  | j                  ddd| j                  dd|| j                  d	d
      }ddd       | j                  |       y# 1 sw Y   xY w)zBTests the _GenSignedUrl function using key file with a GET method.r   r   r   NFr   GETr   asiar   r   )r   TEST_SIGN_URL_GETr	   r   r!   r   r5   r   r   r   r   rj   r   s       r+   testSignurlGetUsingKeyFilez*UnitTestSignUrl.testSignurlGetUsingKeyFile  s    %%H#H	  : ; 
<>>))77
((#%%" 8 j
< 	Xz*
< 
<r   c                    t         j                  }t        j                  dd      j	                         }t
        j                  j                  j                  |      \  }}t        d      }t        dg      5  t
        j                  j                  j                  |ddd|d	d
|| j                  dd      }ddd       | j                  |       y# 1 sw Y   xY w)zDTests _GenSignedUrl with a GET method and the test JSON private key.r!   r"   r   r   r   NFr   r  r   r  r   r   )r   TEST_SIGN_URL_GET_WITH_JSON_KEYr&   r'   decoder!   r   r5   _ReadJSONKeystorer	   r   r   r   rj   )r*   r   json_contentsr   r   r   r   s          r+   %testSignurlGetWithJSONKeyUsingKeyFilez5UnitTestSignUrl.testSignurlGetWithJSONKeyUsingKeyFile  s    33H$$W%@BBH&( ..@@OC#H	  : ; 
<>>))77
# " 8 j
< 	Xz*
< 
<s   9?CCc                 H   t         j                  }t        d      }t        dg      5  t        j
                  j                  j                  | j                  ddd| j                  dd|| j                  d	d
d      }ddd       | j                  |       y# 1 sw Y   xY w)z4Tests the _GenSignedUrl function with a userproject.r   r   r   NFr   r  r   r  r   	myproject)r   r   r   r   r   r   r   r   r   r   billing_project)r   TEST_SIGN_URL_GET_USERPROJECTr	   r   r!   r   r5   r   r   r   r   rj   r   s       r+   testSignurlGetWithUserProjectz-UnitTestSignUrl.testSignurlGetWithUserProject+  s    11H#H	  : ; 
<>>))77
((#%%"% 8 'j
< 	Xz*
< 
<s   ABB!)r   r   r   r   maxDiffr   r   r   r   r   r   r   r   r   patchr  r  r  r  r"  r'  __classcell__)r   s   @r+   r   r      s    + '	2+*+* ./+, - 0@ ./+,) - 0)> ./+,4::894:::;00 < : - 000d02d+*+*+2+r-   r   c                       e Zd Zd Zd Zy)UnitTestSignUrlWithShimc                    t        j                  dd      }| j                  |      }t        ddg      5  t	        ddd      5  | j                  d	d
ddddddddd|dgd      }dj                  |j                  d         }| j                  dj                  |      |       d d d        d d d        y # 1 sw Y   xY w# 1 sw Y   y xY w)Nr!   r"   r#   r   use_gcloud_storageTruer   hidden_shim_modedry_runr0  fake_dir(CLOUDSDK_CORE_PASS_CREDENTIALS_TO_GSUTILCLOUDSDK_ROOT_DIRr5   r6   2mrK   r   rg   US-bproject-capplication/octet-streamgs://bucket/objectTreturn_log_handlerri   infoaj  storage sign-url --format=csv[separator="\t"](resource:label="URL", http_verb:label="HTTP Method", expiration:label="Expiration", signed_url:label="Signed URL") --private-key-file={} --headers=x-goog-resumable=start --duration 120s --http-verb POST --region US --query-params userProject=project --headers content-type=application/octet-stream gs://bucket/object
r&   r'   r(   r   r   
RunCommandjoinmessagesr>   format)r*   key_contentskey_pathmock_log_handler
info_liness        r+   testShimTranslatesFlagsz/UnitTestSignUrlWithShim.testShimTranslatesFlagsE  s    ##G-HIL""L"9H	GHJ 
K 6<)" 	  ??9$k4tY&2F7
 ?C	 + D
 YY/88@A
	" #)&"2J
	@	
K 
K	 	
K 
Ks$   CA B6%C6B?	;CCc                    t        j                  dd      }| j                  |      }d}t        ddg      5  t	        ddd	      5  | j                  d
ddddd|dddddd|dgd      }dj                  |j                  d         }| j                  dj                  ||      |       d d d        d d d        y # 1 sw Y   xY w# 1 sw Y   y xY w)Nr!   r0   r#   rJ   r.  r1  r0  r4  r5  r5   r6   r8  rK   r   rI   rg   r9  r:  r;  r<  r=  r>  Tr?  ri   rA  a  storage sign-url --format=csv[separator="\t"](resource:label="URL", http_verb:label="HTTP Method", expiration:label="Expiration", signed_url:label="Signed URL") --private-key-file={} --headers=x-goog-resumable=start --duration 120s --http-verb POST --private-key-password {} --region US --query-params userProject=project --headers content-type=application/octet-stream gs://bucket/objectrB  )r*   rG  rH  key_passwordrI  rJ  s         r+   !testShimTranslatesFlagsWithP12Keyz9UnitTestSignUrlWithShim.testShimTranslatesFlagsWithP12Keya  s   ##G-GHL""L"9HL	GHJ 
K 6<)" 	  ??9$k4tT4QZ\`&2F7
 ?C	 + D
 YY/88@A

" #)&<"@*	N	
K 
K	 	
K 
Ks$   CA#B;*C;C	 CCN)r   r   r   rK  rN  r   r-   r+   r,  r,  B  s    @8Nr-   r,  ):r   
__future__r   r   r   r   r   r	   osr&   botogslib.commands.signurlr!   r
   r   gslib.exceptionr   gslib.gcs_json_apir   gslib.iamcredentials_apir   gslib.impersonation_credentialsr   gslib.tests.testcaser   testcase)gslib.tests.testcase.integration_testcaser   r   gslib.tests.utilr   rP   r   r   r   gslib.tests.signurl_signaturessignurl_signaturesr   oauth2clientr   oauth2client.service_accountr   sixr   r   	six.movesr   config	get_valuer   r_   r   GsUtilIntegrationTestCaser   GsUtilUnitTestCaser   ShimUnitTestBaser,  r   r-   r+   <module>rf     s@   ! & %  '   	    / . , ) 6 D ' ' M 0 1 2 % - -  B % VV_	5 6 ++''(JL3
 \#@A
;<x&(44 x& = Bx&v \#@AH+h11 H+ BH+V
 \#@A:Nh77 :N B:Nr-   