1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import UIAbility from '@ohos.app.ability.UIAbility'; 17import picker from '@ohos.file.picker'; 18import Want from '@ohos.app.ability.Want'; 19import AbilityConstant from '@ohos.app.ability.AbilityConstant'; 20import window from '@ohos.window'; 21import { BusinessError } from '@ohos.base'; 22import ability from '@ohos.ability.ability'; 23import dlpPermission from '@ohos.dlpPermission'; 24import fs from '@ohos.file.fs'; 25import fileUri from '@ohos.file.fileuri'; 26import abilityManager from '@ohos.app.ability.abilityManager'; 27import { getFileUriByPath, getFileFd, getAppId, isValidPath, defaultDlpFile } from '../common/FileUtils/utils'; 28import Constants from '../common/constant'; 29import GlobalContext from '../common/GlobalContext'; 30import zlib from '@ohos.zlib'; 31import { GetAlertMessage } from '../common/AlertMessage/GetAlertMessage'; 32import { HiLog } from '../common/HiLog'; 33import dialogRequest from '@ohos.app.ability.dialogRequest'; 34import AccountManager from '../manager/AccountManager'; 35import FileUtil from '../common/external/FileUtil'; 36import { OpenDlpFileManager } from '../OpenDlpFile/manager/OpenDlpFileManager'; 37import App from '@system.app'; 38import DecryptContent from '../OpenDlpFile/data/DecryptContent'; 39import FileUtils from '../common/FileUtils/FileUtils'; 40 41const TAG = 'SaveAs'; 42 43class ChangeOption { 44 public offset: number = 0 45 public length: number = 0 46} 47 48const SUFFIX_INDEX = 2; 49const HEAD_LENGTH_IN_BYTE = 20; 50const HEAD_LENGTH_IN_U32 = 5; 51const TXT_OFFSET = 3; 52const SIZE_OFFSET = 4; 53const ARGS_ZERO = 0; 54const ARGS_ONE = 1; 55const ARGS_TWO = 2; 56const FILENAME_MAX_LEN = 255; 57const FOPEN_EXCEPTION_IS_DIR = 13900019; 58const SRC_URI_OFFSET = 4; 59const ACTION: Record<string, string> = { 60 'SELECT_ACTION': 'ohos.want.action.OPEN_FILE', 61 'SELECT_ACTION_MODAL': 'ohos.want.action.OPEN_FILE_SERVICE', 62 'SAVE_ACTION': 'ohos.want.action.CREATE_FILE', 63 'SAVE_ACTION_MODAL': 'ohos.want.action.CREATE_FILE_SERVICE', 64}; 65 66const errCode: Record<string, number> = { 67 'INVALID_ARGS': 13900020, 68 'RESULT_ERROR': 13900042, 69 'NAME_TOO_LONG': 13900030, 70}; 71 72const ERRCODE_MAP = new Map([ 73 [errCode.INVALID_ARGS, 'Invalid argument'], 74 [errCode.RESULT_ERROR, 'Unknown error'], 75 [errCode.NAME_TOO_LONG, 'File name too long'], 76]); 77 78export default class SaveAsAbility extends UIAbility { 79 private result: ability.AbilityResult = { 80 resultCode: -1, 81 want: { 82 bundleName: '', 83 abilityName: '', 84 parameters: { 85 pick_path_return: [], 86 pick_fd_return: 0 87 } 88 } 89 }; 90 private dlpFile: dlpPermission.DLPFile = defaultDlpFile; 91 private sandboxBundleName: string = ''; 92 private resultUri: string = ''; 93 private tokenId: number = -1; 94 private requestCode: number = -1; 95 private fileName: string = ''; 96 private suffix: string = ''; 97 private authPerm: dlpPermission.DLPFileAccess = dlpPermission.DLPFileAccess.READ_ONLY; 98 private isOK: boolean = true; // use with requestModalUIExtension 99 private zipFlag: boolean = false; 100 private random: string = String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END); 101 private filePath: string = ''; 102 private dirPath: string = ''; 103 private encFile: string = ''; 104 private zipPath: string = ''; 105 private zipName: string = ''; 106 private file: fs.File | undefined = undefined; 107 private dstFdPicker: number = -1; 108 private newDlpFile: dlpPermission.DLPFile = defaultDlpFile; 109 private srcFdPicker: number = -1; 110 private linkFileName: string = ''; 111 112 async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> { 113 HiLog.info(TAG, `onCreate`); 114 await this.prepareDlpFile(want); 115 try { 116 await abilityManager.notifySaveAsResult(this.result, this.requestCode); 117 } catch (err) { 118 HiLog.error(TAG, `notifySaveAsResult failed: ${JSON.stringify(err)}`); 119 } 120 if (this.isOK) { 121 this.context.terminateSelf(); 122 } 123 } 124 125 onDestroy(): void { 126 HiLog.info(TAG, `onDestroy`); 127 } 128 129 async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> { 130 // Main window is created, set main page for this ability 131 HiLog.info(TAG, `onWindowStageCreate`); 132 } 133 134 onWindowStageDestroy(): void { 135 // Main window is destroyed, release UI related resources 136 HiLog.info(TAG, `onWindowStageDestroy`); 137 } 138 139 onForeground(): void { 140 // Ability has brought to foreground 141 HiLog.info(TAG, `onForeground`); 142 } 143 144 onBackground(): void { 145 // Ability has back to background 146 HiLog.info(TAG, `onBackground`); 147 } 148 149 async parseParams(want: Want): Promise<boolean | void> { 150 if (want.parameters === undefined) { 151 HiLog.error(TAG, `invalid want`); 152 return false; 153 } 154 155 this.requestCode = want.parameters?.['requestCode'] as number; 156 if (this.requestCode === undefined) { 157 HiLog.error(TAG, `invalid requestCode`); 158 return false; 159 } 160 161 this.tokenId = want.parameters?.[Constants.PARAMS_CALLER_TOKEN] as number; 162 if (this.tokenId === undefined) { 163 HiLog.error(TAG, `invalid tokenId`); 164 return false; 165 } 166 try { 167 await this.checkParseParams(want); 168 return true; 169 } catch { 170 return false; 171 } 172 } 173 174 checkParseParams(want: Want): Promise<void> { 175 return new Promise(async (resolve, reject) => { 176 const manager = OpenDlpFileManager.getInstance(); 177 const getRet = manager.getHasDecryptedContentByTokenId(this.tokenId); 178 if (getRet.errcode !== Constants.ERR_CODE_SUCCESS || !getRet.result) { 179 HiLog.error(TAG, 'getHasDecryptedContentByTokenId error'); 180 reject(); 181 return; 182 } 183 const decryptedContent = getRet.result; 184 this.authPerm = decryptedContent.authPerm; 185 let dlpFileInfo = decryptedContent.dlpFile; 186 let contactAccount = dlpFileInfo?.dlpProperty?.contactAccount ?? ''; 187 if (this.authPerm !== dlpPermission.DLPFileAccess.CONTENT_EDIT && 188 this.authPerm !== dlpPermission.DLPFileAccess.FULL_CONTROL) { 189 HiLog.error(TAG, `invalid authPerm: ${this.authPerm}`); 190 this.isOK = false; 191 AppStorage.setOrCreate('contactAccount', contactAccount); 192 let accountFlag = await AccountManager.checkAccountInfo(contactAccount); 193 if (!accountFlag) { 194 await GetAlertMessage.requestModalUIExtension(this.context, { 195 code: Constants.ERR_JS_APP_INSIDE_ERROR 196 } as BusinessError); 197 reject(); 198 } 199 await GetAlertMessage.requestModalUIExtension(this.context, 200 { code: Constants.ERR_JS_DLP_FILE_READ_ONLY } as BusinessError); 201 reject(); 202 } 203 204 let newFileName = want.parameters?.['key_pick_file_name'] as []; 205 if (newFileName === undefined) { 206 HiLog.error(TAG, 'invalid newFileName'); 207 reject(); 208 } 209 this.fileName = newFileName.join(''); 210 let splitNames = this.fileName.split('.'); 211 HiLog.info(TAG, `splitNames: ${splitNames}`); 212 if (splitNames.length <= SUFFIX_INDEX) { 213 HiLog.error(TAG, 'get suffix failed'); 214 reject(); 215 } 216 this.suffix = splitNames[splitNames.length - SUFFIX_INDEX]; 217 HiLog.info(TAG, `suffix is: ${this.suffix}`); 218 resolve(); 219 }) 220 } 221 222 async copyDlpHead(srcFd: number, dstFd: number) { 223 return new Promise<boolean>(async (resolve, reject) => { 224 let appDir = this.context.filesDir + '/'; 225 this.filePath = appDir + 'saveAs' + this.random; 226 this.dirPath = appDir + 'saveAsUnzip' + this.random; 227 this.encFile = this.dirPath + '/encrypted_data'; 228 this.zipPath = appDir + 'saveAsZip' + this.random; 229 this.zipName = this.zipPath + '.zip'; 230 try { 231 let success = await this.copyDlpHeadData(srcFd, dstFd); 232 resolve(success); 233 } catch (err) { 234 HiLog.error(TAG, `copyDlpHead: ${JSON.stringify(err)}, message: ${JSON.stringify(err.message)}`); 235 resolve(false); 236 } 237 if (this.zipFlag) { 238 FileUtil.unlinkSync(this.dirPath + '/encrypted_data'); 239 FileUtil.unlinkSync(this.dirPath + '/dlp_general_info'); 240 FileUtil.unlinkSync(this.dirPath + '/dlp_cert'); 241 FileUtil.rmdirSync(this.dirPath); 242 FileUtil.unlinkSync(this.filePath); 243 FileUtil.unlinkSync(this.zipName); 244 } 245 }) 246 } 247 248 async copyDlpHeadData(srcFd: number, dstFd: number) { 249 return new Promise<boolean>(async (resolve, reject) => { 250 let z = new ArrayBuffer(HEAD_LENGTH_IN_BYTE); 251 let option: ChangeOption = { 252 offset: 0, 253 length: HEAD_LENGTH_IN_BYTE 254 }; 255 try { 256 fs.readSync(srcFd, z, option); 257 } catch (error) { 258 HiLog.error(TAG, `readSync exception, error is ${JSON.stringify(error)}`); 259 return; 260 } 261 let buf = new Uint32Array(z, 0, HEAD_LENGTH_IN_U32); 262 let magic = buf[0]; 263 264 if (magic !== Constants.DLP_ZIP_MAGIC) { 265 let txtOffset = buf[TXT_OFFSET]; 266 let head = new ArrayBuffer(txtOffset); 267 option = { 268 offset: 0, 269 length: txtOffset 270 }; 271 try { 272 fs.readSync(srcFd, head, option); 273 let buf2 = new Uint32Array(head, 0, HEAD_LENGTH_IN_U32); 274 buf2[SIZE_OFFSET] = 0; 275 fs.writeSync(dstFd, head, option); 276 resolve(true); 277 return; 278 } catch (error) { 279 HiLog.error(TAG, `readSync or writeSync exception, error is ${JSON.stringify(error)}`); 280 return; 281 } 282 } 283 284 const ret = await this.handleZipFile(srcFd, dstFd); 285 if (ret) { 286 resolve(true); 287 } else { 288 resolve(false); 289 } 290 }) 291 } 292 293 private async handleZipFile(srcFd: number, dstFd: number): Promise<boolean> { 294 let openFile: fs.File | undefined; 295 let openZipFile: fs.File | undefined; 296 try { 297 this.zipFlag = true; 298 openFile = await fs.open(this.filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 299 await fs.copyFile(srcFd, openFile.fd); 300 await fs.mkdir(this.dirPath); 301 let options: zlib.Options = { 302 level: zlib.CompressLevel.COMPRESS_LEVEL_NO_COMPRESSION, 303 memLevel: zlib.MemLevel.MEM_LEVEL_DEFAULT, 304 strategy: zlib.CompressStrategy.COMPRESS_STRATEGY_DEFAULT_STRATEGY, 305 }; 306 await zlib.decompressFile(this.filePath, this.dirPath, options); 307 await fs.truncate(this.encFile); 308 await zlib.compressFile(this.dirPath, this.zipPath, options); 309 openZipFile = await fs.open(this.zipName, fs.OpenMode.READ_WRITE); 310 await fs.copyFile(openZipFile.fd, dstFd); 311 return true; 312 } catch (err) { 313 HiLog.error(TAG, `open failed: ${JSON.stringify(err)}`); 314 return false; 315 } finally { 316 FileUtil.closeSync(openFile); 317 FileUtil.closeSync(openZipFile); 318 } 319 } 320 321 isValidDirUri(uri: string | undefined) { 322 if (uri == undefined) { 323 return true; 324 } 325 let file: fs.File | undefined = undefined; 326 try { 327 let uriObj = new fileUri.FileUri(uri); 328 let dirUri = uriObj.getFullDirectoryUri(); // get directory path of a uri 329 file = fs.openSync(dirUri, fs.OpenMode.READ_ONLY); 330 let stat = fs.statSync(file.fd); 331 let isDir = stat.isDirectory(); 332 HiLog.info(TAG, `test dir ${isDir ? 'is' : 'not'} a directory`); 333 return isDir; 334 } catch (e) { 335 let error = e as BusinessError; 336 if (error.code === FOPEN_EXCEPTION_IS_DIR) { 337 HiLog.info(TAG, `test dir is a directory`); 338 return true; 339 } 340 return false; 341 } finally { 342 FileUtil.closeSync(file); 343 } 344 } 345 346 async getSourceFileName() { 347 try { 348 const manager = OpenDlpFileManager.getInstance(); 349 const getRet = manager.getHasDecryptedContentByTokenId(this.tokenId); 350 if (getRet.errcode !== Constants.ERR_CODE_SUCCESS || !getRet.result) { 351 HiLog.error(TAG, 'getHasDecryptedContentByTokenId error'); 352 return ''; 353 } 354 const decryptedContent = getRet.result; 355 let srcUri: string = decryptedContent.openDlpFileData.uri; 356 let uriObj = new fileUri.FileUri(srcUri); 357 let dir = uriObj.getFullDirectoryUri(); // get directory path 358 let name = srcUri.replace(dir, '').replace('/', ''); // strip directory path and '/' 359 HiLog.info(TAG, 'getSourceFileName: ' + name); 360 return name; 361 } catch (e) { 362 let error = e as BusinessError; 363 HiLog.error(TAG, 'getSourceFileName error:' + error.message); 364 return ''; 365 } 366 } 367 368 async parseDocumentPickerSaveOption(args: picker.DocumentSaveOptions[], action: string) { 369 let config: Record<string, string | Record<string, Object>> = { 370 'action': action, 371 'parameters': { 372 'startMode': 'save', 373 } as Record<string, Object> 374 }; 375 376 if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') { 377 let option: picker.DocumentSaveOptions = args[ARGS_ZERO]; 378 if ((option.newFileNames !== undefined) && option.newFileNames.length > 0) { 379 if (option.newFileNames[0] == undefined) { // if option name not provided, default to empty string 380 option.newFileNames[0] = ''; 381 } 382 if (option.newFileNames[0].length > FILENAME_MAX_LEN) { 383 throw new Error('file name exceeds max length'); 384 } 385 let srcName = await this.getSourceFileName(); 386 if (option.newFileNames[0].length > 0 && srcName !== option.newFileNames[0]) { 387 HiLog.error(TAG, 'src file name is not same with newFileName provided'); 388 throw new Error('picker filename error'); 389 } 390 config.parameters['key_pick_file_name'] = option.newFileNames; 391 config.parameters['saveFile'] = option.newFileNames[0]; 392 } 393 394 let isDir = this.isValidDirUri(option.defaultFilePathUri); // undefined returns true 395 if (isDir) { 396 config.parameters['key_pick_dir_path'] = option.defaultFilePathUri; 397 } else { 398 throw new Error('defaultFilePathUri is not a valid directory uri'); 399 } 400 if ((option.fileSuffixChoices !== undefined) && option.fileSuffixChoices.length > 0) { 401 config.parameters['key_file_suffix_choices'] = option.fileSuffixChoices; 402 } 403 } 404 405 HiLog.info(TAG, `[picker] Save config: ${JSON.stringify(config)}`); 406 return config; 407 } 408 409 getDocumentPickerSaveResult(args: dialogRequest.RequestResult | ability.AbilityResult) { 410 let saveResult: Record<string, BusinessError | string[]> = { 411 'error': {} as BusinessError, 412 'data': [] 413 }; 414 415 if (((args as dialogRequest.RequestResult).result !== undefined && 416 (args as dialogRequest.RequestResult).result === 0) || 417 ((args as ability.AbilityResult).resultCode !== undefined && 418 (args as ability.AbilityResult).resultCode === 0)) { 419 if (args.want && args.want.parameters) { 420 if (args.want.parameters.pick_path_return) { 421 saveResult.data = args.want.parameters.pick_path_return as string[]; 422 } else { 423 saveResult.data = args.want.parameters[Constants.PARAMS_STREAM] as string[]; 424 } 425 } 426 } else if (((args as dialogRequest.RequestResult).result !== undefined && 427 (args as dialogRequest.RequestResult).result === 0) || 428 ((args as ability.AbilityResult).resultCode !== undefined && 429 (args as ability.AbilityResult).resultCode === -1)) { 430 saveResult.data = []; 431 } else { 432 saveResult.error = this.getErr(errCode.RESULT_ERROR) as BusinessError; 433 } 434 435 HiLog.info(TAG, `[picker] Save saveResult: ${JSON.stringify(saveResult)}`); 436 return saveResult; 437 } 438 439 getErr(errCode: number) { 440 return { code: errCode, message: ERRCODE_MAP.get(errCode) } as BusinessError; 441 } 442 443 async documentPickerSave(...args: Object[]): Promise<BusinessError | string[] | undefined> { 444 let config: Record<string, string | Record<string, Object>>; 445 let result: dialogRequest.RequestResult | ability.AbilityResult; 446 try { 447 config = await this.parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION); 448 result = await this.context.startAbilityForResult(config, { windowMode: 0 }); 449 } catch (error) { 450 HiLog.error(TAG, `startAbilityForResult error: ${JSON.stringify(error)}`); 451 return undefined; 452 } 453 HiLog.info(TAG, `[picker] Save result: ${JSON.stringify(result)}`); 454 try { 455 const saveResult: Record<string, BusinessError | string[]> = this.getDocumentPickerSaveResult(result); 456 if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') { 457 return (args[ARGS_ONE] as Function)(saveResult.error, saveResult.data); 458 } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') { 459 return (args[ARGS_ZERO] as Function)(saveResult.error, saveResult.data); 460 } 461 return new Promise<BusinessError | string[]>((resolve, reject) => { 462 if (saveResult.data !== undefined) { 463 resolve(saveResult.data); 464 } else { 465 reject(saveResult.error); 466 } 467 }) 468 } catch (resultError) { 469 HiLog.error(TAG, `[picker] Result error: ${resultError}`); 470 } 471 return undefined; 472 } 473 474 async prepareDlpFile(want: Want): Promise<void> { 475 HiLog.info(TAG, 'getFile start prepareDlpFile'); 476 let uri = ''; 477 let displayName = ''; 478 let ret = await this.parseParams(want); 479 if (!ret) { 480 HiLog.error(TAG, 'parseParams failed'); 481 return; 482 } 483 let documentSaveOptions = new picker.DocumentSaveOptions(); 484 displayName = this.fileName; 485 documentSaveOptions.newFileNames = [displayName]; 486 documentSaveOptions.fileSuffixChoices = [`.${this.suffix}.dlp`]; 487 try { 488 let saveRes: BusinessError | string[] | undefined = await this.documentPickerSave(documentSaveOptions); 489 if (saveRes == undefined || (saveRes instanceof Array && saveRes.length == 0)) { 490 HiLog.error(TAG, 'fail to get uri'); 491 return; 492 } 493 uri = saveRes[0] 494 if (!isValidPath(uri)) { 495 HiLog.error(TAG, 'invalid uri'); 496 return; 497 } 498 try { 499 await this.fileOpen(uri); 500 await this.pickerDialog(uri, want); 501 } catch { 502 HiLog.error(TAG, 'fileOpen or pickerDialog start failed'); 503 return; 504 } 505 } catch (err) { 506 HiLog.error(TAG, `DocumentViewPicker failed: ${JSON.stringify(err)}`); 507 FileUtil.closeSync(this.file); 508 FileUtil.unlinkSync(uri); 509 this.isOK = false; 510 await GetAlertMessage.requestModalUIExtension(this.context, 511 { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 512 return; 513 } 514 } 515 516 cleanupResources(uri: string): void { 517 FileUtil.closeSync(this.file); 518 FileUtil.unlinkSync(uri); 519 FileUtil.closeSync(this.srcFdPicker); 520 } 521 522 pickerDialog(uri: string, want: Want): Promise<void> { 523 return new Promise(async (resolve, reject) => { 524 const manager = OpenDlpFileManager.getInstance(); 525 const getRet = manager.getHasDecryptedContent(uri); 526 if (getRet.errcode !== Constants.ERR_CODE_SUCCESS || !getRet.result) { 527 HiLog.error(TAG, 'getHasDecryptedContent error'); 528 reject(); 529 return; 530 } 531 const decryptContent = getRet.result; 532 533 this.dlpFile = decryptContent.dlpFile; 534 this.sandboxBundleName = decryptContent.openDlpFileData.sandboxBundleName; 535 let appId: number = decryptContent.appInfo.appIndex; 536 537 let srcUri: string = decryptContent.openDlpFileData.uri; 538 let getFileFdRet = getFileFd(srcUri); 539 if (getFileFdRet.errcode !== Constants.ERR_CODE_SUCCESS || !getFileFdRet.result) { 540 HiLog.error(TAG, 'getFileFd error'); 541 reject(); 542 return; 543 } 544 this.srcFdPicker = getFileFdRet.result; 545 546 let success = await this.copyDlpHead(this.srcFdPicker, this.dstFdPicker); 547 if (!success) { 548 this.cleanupResources(uri); 549 this.isOK = false; 550 await GetAlertMessage.requestModalUIExtension(this.context, 551 { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 552 reject(); 553 } 554 try { 555 await this.openDLPFile(uri, want); 556 await this.addDLPLinkFile(appId, uri); 557 } catch { 558 this.cleanupResources(uri); 559 reject(); 560 } 561 const linkFilePath = Constants.FUSE_PATH + this.linkFileName; 562 const linkUri = getFileUriByPath(linkFilePath); 563 const copyRet = await this.copyNewDecryptContent(decryptContent, uri, appId, linkUri); 564 if (!copyRet) { 565 HiLog.error(TAG, 'copyNewDecryptContent failed'); 566 reject(); 567 } 568 this.resultUri = getFileUriByPath(linkFilePath); 569 (this.result.want?.parameters?.pick_path_return as string[]).push(this.resultUri); 570 this.result.resultCode = 0; 571 FileUtil.closeSync(this.srcFdPicker); 572 resolve(); 573 }) 574 } 575 576 private async copyNewDecryptContent(decryptContent: DecryptContent, uri: string, appId: number, linkUri: string): 577 Promise<boolean> { 578 let decryptContentNew = decryptContent.clone(); 579 decryptContentNew.dlpFile = this.newDlpFile; 580 decryptContentNew.openDlpFileData.sandboxBundleName = this.sandboxBundleName; 581 decryptContentNew.appInfo.appIndex = appId; 582 decryptContentNew.authPerm = this.authPerm; 583 decryptContentNew.openDlpFileData.uri = uri; 584 decryptContentNew.linkFileName = this.linkFileName; 585 decryptContentNew.dlpFd = this.dstFdPicker; 586 decryptContentNew.appInfo.tokenID = this.tokenId; 587 decryptContentNew.linkUri = linkUri; 588 589 const manager = OpenDlpFileManager.getInstance(); 590 const setRet = await manager.addDecryptContent(uri, decryptContentNew); 591 if (setRet.errcode !== Constants.ERR_CODE_SUCCESS) { 592 HiLog.error(TAG, 'addDecryptContent failed'); 593 return false; 594 } 595 return true; 596 } 597 598 fileOpen(uri: string): Promise<void> { 599 return new Promise(async (resolve, reject) => { 600 try { 601 this.file = await fs.open(uri, fs.OpenMode.READ_WRITE); 602 this.dstFdPicker = this.file.fd; 603 resolve(); 604 } catch (err) { 605 HiLog.error(TAG, `open filename: ${FileUtils.getFileNameByUri(uri)}, failed: ${JSON.stringify(err)}`); 606 FileUtil.unlinkSync(uri); 607 this.isOK = false; 608 await GetAlertMessage.requestModalUIExtension(this.context, 609 { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 610 reject(); 611 } 612 }) 613 } 614 615 openDLPFile(uri: string, want: Want): Promise<void> { 616 return new Promise(async (resolve, reject) => { 617 let callerAppId: string; 618 try { 619 let callerBundleName = want.parameters?.[Constants.PARAMS_CALLER_BUNDLE_NAME] as string; 620 callerAppId = await getAppId(callerBundleName); 621 HiLog.info(TAG, `get AppId: ${callerAppId}`); 622 } catch { 623 reject(); 624 return; 625 } 626 try { 627 this.newDlpFile = await dlpPermission.openDLPFile(this.dstFdPicker, callerAppId); 628 } catch (err) { 629 HiLog.error(TAG, `generateDlpFile: ${this.dstFdPicker}, failed: ${JSON.stringify(err)}`); 630 await GetAlertMessage.requestModalUIExtension(this.context, err); 631 FileUtil.closeSync(this.file); 632 FileUtil.unlinkSync(uri); 633 FileUtil.closeSync(this.srcFdPicker); 634 reject(); 635 } 636 resolve(); 637 }) 638 } 639 640 async addDLPLinkFile(appId: number, uri: string): Promise<void> { 641 return new Promise(async (resolve, reject) => { 642 let date = new Date(); 643 let timestamp = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 644 date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getMilliseconds()).getTime(); 645 646 this.linkFileName = String(this.sandboxBundleName).substring(0, Constants.BUNDLE_LEN) + '_' + appId + 647 '_' + timestamp + String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END) + '.dlp.link.' + 648 this.suffix; 649 650 try { 651 await this.newDlpFile.addDLPLinkFile(this.linkFileName); 652 } catch (err) { 653 HiLog.error(TAG, `addDlpLinkFile failed: ${JSON.stringify(err)}`); 654 try { 655 await this.newDlpFile.closeDLPFile(); 656 } catch (err) { 657 HiLog.error(TAG, `closeDlpFile failed: ${JSON.stringify(err)}`); 658 } 659 FileUtil.closeSync(this.file); 660 FileUtil.unlinkSync(uri); 661 FileUtil.closeSync(this.srcFdPicker); 662 this.isOK = false; 663 await GetAlertMessage.requestModalUIExtension(this.context, 664 { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 665 reject(); 666 } 667 resolve(); 668 }) 669 } 670}; 671