import { SendData } from "../models/data/sendData" ; import { SendRequest } from "../models/request/sendRequest" ; import { ErrorResponse } from "../models/response/errorResponse" ; import { SendResponse } from "../models/response/sendResponse" ; import { EncArrayBuffer } from "../models/domain/encArrayBuffer" ; import { EncString } from "../models/domain/encString" ; import { Send } from "../models/domain/send" ; import { SendFile } from "../models/domain/sendFile" ; import { SendText } from "../models/domain/sendText" ; import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey" ; import { SendType } from "../enums/sendType" ; import { SendView } from "../models/view/sendView" ; import { ApiService } from "../abstractions/api.service" ; import { CryptoService } from "../abstractions/crypto.service" ; import { CryptoFunctionService } from "../abstractions/cryptoFunction.service" ; import { FileUploadService } from "../abstractions/fileUpload.service" ; import { I18nService } from "../abstractions/i18n.service" ; import { SendService as SendServiceAbstraction } from "../abstractions/send.service" ; import { StateService } from "../abstractions/state.service" ; import { Utils } from "../misc/utils" ; export class SendService implements SendServiceAbstraction { constructor ( private cryptoService , private apiService , private fileUploadService , private i18nService , private cryptoFunctionService , private stateService ) { } async clearCache ( ) { await this . stateService . setDecryptedSends ( null ) ; } async encrypt ( model , file , password , key ? ) { let fileData = null ; const send = new Send ( ) ; send . id = model . id ; send . type = model . type ; send . disabled = model . disabled ; send . hideEmail = model . hideEmail ; send . maxAccessCount = model . maxAccessCount ; if ( model . key == null ) { model . key = await this . cryptoFunctionService . randomBytes ( 16 ) ; model . cryptoKey = await this . cryptoService . makeSendKey ( model . key ) ; } if ( password != null ) { const passwordHash = await this . cryptoFunctionService . pbkdf2 ( password , model . key , "sha256" , 100000 ) ; send . password = Utils . fromBufferToB64 ( passwordHash ) ; } send . key = await this . cryptoService . encrypt ( model . key , key ) ; send . name = await this . cryptoService . encrypt ( model . name , model . cryptoKey ) ; send . notes = await this . cryptoService . encrypt ( model . notes , model . cryptoKey ) ; if ( send . type === SendType . Text ) { send . text = new SendText ( ) ; send . text . text = await this . cryptoService . encrypt ( model . text . text , model . cryptoKey ) ; send . text . hidden = model . text . hidden ; } else if ( send . type === SendType . File ) { send . file = new SendFile ( ) ; if ( file != null ) { if ( file instanceof ArrayBuffer ) { const [ name , data ] = await this . encryptFileData ( model . file . fileName , file , model . cryptoKey ) ; send . file . fileName = name ; fileData = data ; } else { fileData = await this . parseFile ( send , file , model . cryptoKey ) ; } } } return [ send , fileData ] ; } async get ( id ) { const sends = await this . stateService . getEncryptedSends ( ) ; if ( sends == null || ! sends . hasOwnProperty ( id ) ) { return null ; } return new Send ( sends [ id ] ) ; } async getAll ( ) { const sends = await this . stateService . getEncryptedSends ( ) ; const response = [ ] ; for ( const id in sends ) { if ( sends . hasOwnProperty ( id ) ) { response . push ( new Send ( sends [ id ] ) ) ; } } return response ; } async getAllDecrypted ( ) { let decSends = await this . stateService . getDecryptedSends ( ) ; if ( decSends != null ) { return decSends ; } decSends = [ ] ; const hasKey = await this . cryptoService . hasKey ( ) ; if ( ! hasKey ) { throw new Error ( "No key." ) ; } const promises = [ ] ; const sends = await this . getAll ( ) ; sends . forEach ( ( send ) => { promises . push ( send . decrypt ( ) . then ( ( f ) => decSends . push ( f ) ) ) ; } ) ; await Promise . all ( promises ) ; decSends . sort ( Utils . getSortFunction ( this . i18nService , "name" ) ) ; await this . stateService . setDecryptedSends ( decSends ) ; return decSends ; } async saveWithServer ( sendData ) { const request = new SendRequest ( sendData [ 0 ] , sendData [ 1 ] ?. buffer . byteLength ) ; let response ; if ( sendData [ 0 ] . id == null ) { if ( sendData [ 0 ] . type === SendType . Text ) { response = await this . apiService . postSend ( request ) ; } else { try { const uploadDataResponse = await this . apiService . postFileTypeSend ( request ) ; response = uploadDataResponse . sendResponse ; await this . fileUploadService . uploadSendFile ( uploadDataResponse , sendData [ 0 ] . file . fileName , sendData [ 1 ] ) ; } catch ( e ) { if ( e instanceof ErrorResponse && ( e as ErrorResponse ) . statusCode === 404 ) { response = await this . legacyServerSendFileUpload ( sendData , request ) ; } else if ( e instanceof ErrorResponse ) { throw new Error ( ( e as ErrorResponse ) . getSingleMessage ( ) ) ; } else { throw e ; } } } sendData [ 0 ] . id = response . id ; sendData [ 0 ] . accessId = response . accessId ; } else { response = await this . apiService . putSend ( sendData [ 0 ] . id , request ) ; } const userId = await this . stateService . getUserId ( ) ; const data = new SendData ( response , userId ) ; await this . upsert ( data ) ; } deprecated async legacyServerSendFileUpload ( sendData , request ) { const fd = new FormData ( ) ; try { const blob = new Blob ( [ sendData [ 1 ] . buffer ] , { type : "application/octet-stream" } ) ; fd . append ( "model" , JSON . stringify ( request ) ) ; fd . append ( "data" , blob , sendData [ 0 ] . file . fileName . encryptedString ) ; } catch ( e ) { if ( Utils . isNode && ! Utils . isBrowser ) { fd . append ( "model" , JSON . stringify ( request ) ) ; fd . append ( "data" , Buffer . from ( sendData [ 1 ] . buffer ) as any , { filepath : sendData [ 0 ] . file . fileName . encryptedString , contentType : "application/octet-stream" , } as any ) ; } else { throw e ; } } return await this . apiService . postSendFileLegacy ( fd ) ; } async upsert ( send ) { let sends = await this . stateService . getEncryptedSends ( ) ; if ( sends == null ) { sends = { } ; } if ( send instanceof SendData ) { const s = send as SendData ; sends [ s . id ] = s ; } else { ( send as SendData [ ] ) . forEach ( ( s ) => { sends [ s . id ] = s ; } ) ; } await this . replace ( sends ) ; } async replace ( sends ) { await this . stateService . setDecryptedSends ( null ) ; await this . stateService . setEncryptedSends ( sends ) ; } async clear ( ) { await this . stateService . setDecryptedSends ( null ) ; await this . stateService . setEncryptedSends ( null ) ; } async delete ( id ) { const sends = await this . stateService . getEncryptedSends ( ) ; if ( sends == null ) { return ; } if ( typeof id === "string" ) { if ( sends [ id ] == null ) { return ; } delete sends [ id ] ; } else { ( id as string [ ] ) . forEach ( ( i ) => { delete sends [ i ] ; } ) ; } await this . replace ( sends ) ; } async deleteWithServer ( id ) { await this . apiService . deleteSend ( id ) ; await this . delete ( id ) ; } async removePasswordWithServer ( id ) { const response = await this . apiService . putSendRemovePassword ( id ) ; const userId = await this . stateService . getUserId ( ) ; const data = new SendData ( response , userId ) ; await this . upsert ( data ) ; } private parseFile ( send , file , key ) { return new Promise ( ( resolve , reject ) => { const reader = new FileReader ( ) ; reader . readAsArrayBuffer ( file ) ; reader . onload = async ( evt ) => { try { const [ name , data ] = await this . encryptFileData ( file . name , evt . target . result as ArrayBuffer , key ) ; send . file . fileName = name ; resolve ( data ) ; } catch ( e ) { reject ( e ) ; } } ; reader . onerror = ( ) => { reject ( "Error reading file." ) ; } ; } ) ; } private async encryptFileData ( fileName , data , key ) { const encFileName = await this . cryptoService . encrypt ( fileName , key ) ; const encFileData = await this . cryptoService . encryptToBytes ( data , key ) ; return [ encFileName , encFileData ] ; } }