|  |  | @ -39,6 +39,7 @@ interface State { | 
			
		
	
		
		
			
				
					
					|  |  |  |   passwordErrorString: string; |  |  |  |   passwordErrorString: string; | 
			
		
	
		
		
			
				
					
					|  |  |  |   passwordFieldsMatch: boolean; |  |  |  |   passwordFieldsMatch: boolean; | 
			
		
	
		
		
			
				
					
					|  |  |  |   mnemonicSeed: string; |  |  |  |   mnemonicSeed: string; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   generatedMnemonicSeed: string; | 
			
		
	
		
		
			
				
					
					|  |  |  |   hexGeneratedPubKey: string; |  |  |  |   hexGeneratedPubKey: string; | 
			
		
	
		
		
			
				
					
					|  |  |  |   primaryDevicePubKey: string; |  |  |  |   primaryDevicePubKey: string; | 
			
		
	
		
		
			
				
					
					|  |  |  |   mnemonicError: string | undefined; |  |  |  |   mnemonicError: string | undefined; | 
			
		
	
	
		
		
			
				
					|  |  | @ -113,6 +114,7 @@ export class RegistrationTabs extends React.Component<{}, State> { | 
			
		
	
		
		
			
				
					
					|  |  |  |       passwordErrorString: '', |  |  |  |       passwordErrorString: '', | 
			
		
	
		
		
			
				
					
					|  |  |  |       passwordFieldsMatch: false, |  |  |  |       passwordFieldsMatch: false, | 
			
		
	
		
		
			
				
					
					|  |  |  |       mnemonicSeed: '', |  |  |  |       mnemonicSeed: '', | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       generatedMnemonicSeed: '', | 
			
		
	
		
		
			
				
					
					|  |  |  |       hexGeneratedPubKey: '', |  |  |  |       hexGeneratedPubKey: '', | 
			
		
	
		
		
			
				
					
					|  |  |  |       primaryDevicePubKey: '', |  |  |  |       primaryDevicePubKey: '', | 
			
		
	
		
		
			
				
					
					|  |  |  |       mnemonicError: undefined, |  |  |  |       mnemonicError: undefined, | 
			
		
	
	
		
		
			
				
					|  |  | @ -125,39 +127,11 @@ export class RegistrationTabs extends React.Component<{}, State> { | 
			
		
	
		
		
			
				
					
					|  |  |  |     window.textsecure.storage.remove('secondaryDeviceStatus'); |  |  |  |     window.textsecure.storage.remove('secondaryDeviceStatus'); | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   public render() { |  |  |  |   public componentDidMount() { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     this.generateMnemonicAndKeyPair().ignore(); |  |  |  |     this.generateMnemonicAndKeyPair().ignore(); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     return this.renderTabs(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   private async generateMnemonicAndKeyPair() { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (this.state.mnemonicSeed === '') { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       const language = 'english'; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       const mnemonic = await this.accountManager.generateMnemonic(language); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       let seedHex = window.mnemonic.mn_decode(mnemonic, language); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       // handle shorter than 32 bytes seeds
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       const privKeyHexLength = 32 * 2; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       if (seedHex.length !== privKeyHexLength) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         seedHex = seedHex.concat(seedHex); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         seedHex = seedHex.substring(0, privKeyHexLength); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       const seed = window.dcodeIO.ByteBuffer.wrap( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         seedHex, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         'hex' |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       ).toArrayBuffer(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       const keyPair = await window.libsignal.Curve.async.createKeyPair(seed); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       const hexGeneratedPubKey = Buffer.from(keyPair.pubKey).toString('hex'); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       this.setState({ |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         mnemonicSeed: mnemonic, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         hexGeneratedPubKey, // our 'frontend' sessionID
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       }); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   private renderTabs() { |  |  |  |   public render() { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     const { selectedTab } = this.state; |  |  |  |     const { selectedTab } = this.state; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     const createAccount = window.i18n('createAccount'); |  |  |  |     const createAccount = window.i18n('createAccount'); | 
			
		
	
	
		
		
			
				
					|  |  | @ -186,6 +160,32 @@ export class RegistrationTabs extends React.Component<{}, State> { | 
			
		
	
		
		
			
				
					
					|  |  |  |     ); |  |  |  |     ); | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   private async generateMnemonicAndKeyPair() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     if (this.state.generatedMnemonicSeed === '') { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       const language = 'english'; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       const mnemonic = await this.accountManager.generateMnemonic(language); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       let seedHex = window.mnemonic.mn_decode(mnemonic, language); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       // handle shorter than 32 bytes seeds
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       const privKeyHexLength = 32 * 2; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       if (seedHex.length !== privKeyHexLength) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         seedHex = seedHex.concat(seedHex); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         seedHex = seedHex.substring(0, privKeyHexLength); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       const seed = window.dcodeIO.ByteBuffer.wrap( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         seedHex, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         'hex' | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       ).toArrayBuffer(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       const keyPair = await window.libsignal.Curve.async.createKeyPair(seed); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       const hexGeneratedPubKey = Buffer.from(keyPair.pubKey).toString('hex'); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       this.setState({ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         generatedMnemonicSeed: mnemonic, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         hexGeneratedPubKey, // our 'frontend' sessionID
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       }); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   private readonly handleTabSelect = (tabType: TabType): void => { |  |  |  |   private readonly handleTabSelect = (tabType: TabType): void => { | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (tabType !== TabType.SignIn) { |  |  |  |     if (tabType !== TabType.SignIn) { | 
			
		
	
		
		
			
				
					
					|  |  |  |       this.cancelSecondaryDevice().ignore(); |  |  |  |       this.cancelSecondaryDevice().ignore(); | 
			
		
	
	
		
		
			
				
					|  |  | @ -200,7 +200,6 @@ export class RegistrationTabs extends React.Component<{}, State> { | 
			
		
	
		
		
			
				
					
					|  |  |  |       passwordErrorString: '', |  |  |  |       passwordErrorString: '', | 
			
		
	
		
		
			
				
					
					|  |  |  |       passwordFieldsMatch: false, |  |  |  |       passwordFieldsMatch: false, | 
			
		
	
		
		
			
				
					
					|  |  |  |       mnemonicSeed: '', |  |  |  |       mnemonicSeed: '', | 
			
		
	
		
		
			
				
					
					|  |  |  |       hexGeneratedPubKey: '', |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       primaryDevicePubKey: '', |  |  |  |       primaryDevicePubKey: '', | 
			
		
	
		
		
			
				
					
					|  |  |  |       mnemonicError: undefined, |  |  |  |       mnemonicError: undefined, | 
			
		
	
		
		
			
				
					
					|  |  |  |       displayNameError: undefined, |  |  |  |       displayNameError: undefined, | 
			
		
	
	
		
		
			
				
					|  |  | @ -731,15 +730,19 @@ export class RegistrationTabs extends React.Component<{}, State> { | 
			
		
	
		
		
			
				
					
					|  |  |  |     const { |  |  |  |     const { | 
			
		
	
		
		
			
				
					
					|  |  |  |       password, |  |  |  |       password, | 
			
		
	
		
		
			
				
					
					|  |  |  |       mnemonicSeed, |  |  |  |       mnemonicSeed, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       generatedMnemonicSeed, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       signInMode, | 
			
		
	
		
		
			
				
					
					|  |  |  |       displayName, |  |  |  |       displayName, | 
			
		
	
		
		
			
				
					
					|  |  |  |       passwordErrorString, |  |  |  |       passwordErrorString, | 
			
		
	
		
		
			
				
					
					|  |  |  |       passwordFieldsMatch, |  |  |  |       passwordFieldsMatch, | 
			
		
	
		
		
			
				
					
					|  |  |  |     } = this.state; |  |  |  |     } = this.state; | 
			
		
	
		
		
			
				
					
					|  |  |  |     // Make sure the password is valid
 |  |  |  |     // Make sure the password is valid
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     window.log.info('starting registration'); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     const trimName = displayName.trim(); |  |  |  |     const trimName = displayName.trim(); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (!trimName) { |  |  |  |     if (!trimName) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       window.log.warn('invalid trimmed name for registration'); | 
			
		
	
		
		
			
				
					
					|  |  |  |       window.pushToast({ |  |  |  |       window.pushToast({ | 
			
		
	
		
		
			
				
					
					|  |  |  |         title: window.i18n('displayNameEmpty'), |  |  |  |         title: window.i18n('displayNameEmpty'), | 
			
		
	
		
		
			
				
					
					|  |  |  |         type: 'error', |  |  |  |         type: 'error', | 
			
		
	
	
		
		
			
				
					|  |  | @ -750,6 +753,7 @@ export class RegistrationTabs extends React.Component<{}, State> { | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (passwordErrorString) { |  |  |  |     if (passwordErrorString) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       window.log.warn('invalid password for registration'); | 
			
		
	
		
		
			
				
					
					|  |  |  |       window.pushToast({ |  |  |  |       window.pushToast({ | 
			
		
	
		
		
			
				
					
					|  |  |  |         title: window.i18n('invalidPassword'), |  |  |  |         title: window.i18n('invalidPassword'), | 
			
		
	
		
		
			
				
					
					|  |  |  |         type: 'error', |  |  |  |         type: 'error', | 
			
		
	
	
		
		
			
				
					|  |  | @ -760,6 +764,8 @@ export class RegistrationTabs extends React.Component<{}, State> { | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (!!password && !passwordFieldsMatch) { |  |  |  |     if (!!password && !passwordFieldsMatch) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       window.log.warn('passwords does not match for registration'); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       window.pushToast({ |  |  |  |       window.pushToast({ | 
			
		
	
		
		
			
				
					
					|  |  |  |         title: window.i18n('passwordsDoNotMatch'), |  |  |  |         title: window.i18n('passwordsDoNotMatch'), | 
			
		
	
		
		
			
				
					
					|  |  |  |         type: 'error', |  |  |  |         type: 'error', | 
			
		
	
	
		
		
			
				
					|  |  | @ -769,28 +775,48 @@ export class RegistrationTabs extends React.Component<{}, State> { | 
			
		
	
		
		
			
				
					
					|  |  |  |       return; |  |  |  |       return; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (!mnemonicSeed) { |  |  |  |     if (signInMode === SignInMode.UsingSeed && !mnemonicSeed) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       window.log.warn('empty mnemonic seed passed in seed restoration mode'); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       return; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } else if (!generatedMnemonicSeed) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       window.log.warn('empty generated seed'); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       return; |  |  |  |       return; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     // Ensure we clear the secondary device registration status
 |  |  |  |     // Ensure we clear the secondary device registration status
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     window.textsecure.storage.remove('secondaryDeviceStatus'); |  |  |  |     window.textsecure.storage.remove('secondaryDeviceStatus'); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     const seedToUse = | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       signInMode === SignInMode.UsingSeed | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         ? mnemonicSeed | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         : generatedMnemonicSeed; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     try { |  |  |  |     try { | 
			
		
	
		
		
			
				
					
					|  |  |  |       await this.resetRegistration(); |  |  |  |       await this.resetRegistration(); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       await window.setPassword(password); |  |  |  |       await window.setPassword(password); | 
			
		
	
		
		
			
				
					
					|  |  |  |       await this.accountManager.registerSingleDevice( |  |  |  |       await this.accountManager.registerSingleDevice( | 
			
		
	
		
		
			
				
					
					|  |  |  |         mnemonicSeed, |  |  |  |         seedToUse, | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         language, |  |  |  |         language, | 
			
		
	
		
		
			
				
					
					|  |  |  |         trimName |  |  |  |         trimName | 
			
		
	
		
		
			
				
					
					|  |  |  |       ); |  |  |  |       ); | 
			
		
	
		
		
			
				
					
					|  |  |  |       trigger('openInbox'); |  |  |  |       trigger('openInbox'); | 
			
		
	
		
		
			
				
					
					|  |  |  |     } catch (e) { |  |  |  |     } catch (e) { | 
			
		
	
		
		
			
				
					
					|  |  |  |       if (typeof e === 'string') { |  |  |  |       window.pushToast({ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         //this.showToast(e);
 |  |  |  |         title: `Error: ${e.message || 'Something went wrong'}`, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         type: 'error', | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         id: 'registrationError', | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       }); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       let exmsg = ''; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       if (e.message) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         exmsg += e.message; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       if (e.stack) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         exmsg += ` | stack:  + ${e.stack}`; | 
			
		
	
		
		
			
				
					
					|  |  |  |       } |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  |       //this.log(e);
 |  |  |  |       window.log.warn('exception during registration:', exmsg); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -804,8 +830,12 @@ export class RegistrationTabs extends React.Component<{}, State> { | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   private async registerSecondaryDevice() { |  |  |  |   private async registerSecondaryDevice() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     window.log.warn('starting registerSecondaryDevice'); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     // tslint:disable-next-line: no-backbone-get-set-outside-model
 |  |  |  |     // tslint:disable-next-line: no-backbone-get-set-outside-model
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (window.textsecure.storage.get('secondaryDeviceStatus') === 'ongoing') { |  |  |  |     if (window.textsecure.storage.get('secondaryDeviceStatus') === 'ongoing') { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       window.log.warn('registering secondary device already ongoing'); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |       return; |  |  |  |       return; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |     this.setState({ |  |  |  |     this.setState({ | 
			
		
	
	
		
		
			
				
					|  |  | 
 |