• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025 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 Result from '../../common/Result'
17import { ResultMsg } from '../../common/ResultMsg'
18import Constants from '../../common/constant';
19import fs, { ReadOptions } from '@ohos.file.fs';
20import { getFileFd } from '../../common/FileUtils/utils';
21import { HiLog } from '../../common/HiLog';
22import { zlib } from '@kit.BasicServicesKit';
23import { util } from '@kit.ArkTS';
24import FileUtil from '../../common/external/FileUtil';
25import { dlpPermission } from '@kit.DataProtectionKit';
26import FileMetaInfo from '../../bean/data/FileMetaInfo';
27import GlobalContext from '../../common/GlobalContext';
28import { FileParseType } from '../../bean/data/FileParseType';
29import { FileParseInfo } from '../../bean/data/FileParseInfo';
30import OpenDlpFileData from '../data/OpenDlpFileData';
31
32const TAG: string = 'FileParse';
33
34const NUM_TO_TYPE_MAP = new Map<number, string>([
35  [1, 'txt'],
36  [2, 'pdf'],
37  [3, 'doc'],
38  [4, 'docx'],
39  [5, 'ppt'],
40  [6, 'pptx'],
41  [7, 'xls'],
42  [8, 'xlsx'],
43  [9, 'bmp'],
44  [10, 'bm'],
45  [11, 'dng'],
46  [12, 'gif'],
47  [13, 'heic'],
48  [14, 'heics'],
49  [15, 'heif'],
50  [16, 'heifs'],
51  [17, 'hif'],
52  [18, 'jpg'],
53  [19, 'jpeg'],
54  [20, 'jpe'],
55  [21, 'png'],
56  [22, 'webp'],
57  [23, 'cur'],
58  [24, 'raf'],
59  [25, 'ico'],
60  [26, 'nrw'],
61  [27, 'rw2'],
62  [28, 'pef'],
63  [29, 'srw'],
64  [30, 'svg'],
65  [31, 'arw'],
66  [32, '3gpp2'],
67  [33, '3gp2'],
68  [34, '3g2'],
69  [35, '3gpp'],
70  [36, '3gp'],
71  [37, 'avi'],
72  [38, 'm4v'],
73  [39, 'f4v'],
74  [40, 'mp4v'],
75  [41, 'mpeg4'],
76  [42, 'mp4'],
77  [43, 'm2ts'],
78  [44, 'mts'],
79  [45, 'ts'],
80  [46, 'vt'],
81  [47, 'wrf'],
82  [48, 'mpeg'],
83  [49, 'mpeg2'],
84  [50, 'mpv2'],
85  [51, 'mp2v'],
86  [52, 'm2v'],
87  [53, 'm2t'],
88  [54, 'mpeg1'],
89  [55, 'mpv1'],
90  [56, 'mp1v'],
91  [57, 'm1v'],
92  [58, 'mpg'],
93  [59, 'mov'],
94  [60, 'mkv'],
95  [61, 'webm'],
96  [62, 'h264'],
97  [63, 'wbmp'],
98  [64, 'nef'],
99  [65, 'cr2'],
100]);
101
102abstract class FileParseBase {
103  protected metaInfo?: FileMetaInfo;
104  public fileSize: number = 0;
105  public parseType?: FileParseType;
106
107  constructor(fileSize: number) {
108    this.fileSize = fileSize;
109  }
110
111  public abstract parse(uri: string, ctxFilesDir: string): Promise<Result<FileMetaInfo>>;
112}
113
114class ZipParse extends FileParseBase {
115  constructor(fileSize: number) {
116    super(fileSize);
117    this.parseType = FileParseType.ZIP;
118  }
119
120  async parse(uri: string, ctxFilesDir: string): Promise<Result<FileMetaInfo>> {
121    const tempRandom = String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END);
122    const filePath = ctxFilesDir + '/saveAs' + tempRandom;
123    const dirPath = ctxFilesDir + '/saveAsUnzip' + tempRandom;
124    const fileName = dirPath + '/dlp_cert';
125    const generalInfoPath = dirPath + '/dlp_general_info';
126    let file: fs.File | undefined;
127    let ff: fs.File | undefined;
128    try {
129      file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
130      const fileInfo = fs.statSync(file.fd);
131      this.fileSize = fileInfo.size;
132      ff = await fs.open(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
133      await fs.copyFile(file.fd, ff.fd);
134      fs.mkdirSync(dirPath, true);
135      await zlib.decompressFile(filePath, dirPath);
136
137      let dlpInfo = fs.readTextSync(fileName);
138      let infoArray = dlpInfo.split('accountType');
139      let type = infoArray[Constants.NUMBER_ONE].slice(Constants.TYPE_START, Constants.TYPE_END);
140
141      let generalInfo = fs.readTextSync(generalInfoPath);
142      let generalInfoArray = generalInfo.split('realFileType');
143      let realFileType: string = '';
144      if (generalInfoArray.length === Constants.NUMBER_TWO) {
145        let realFileTypeStr = generalInfoArray[Constants.NUMBER_ONE].split('\"');
146        if (realFileTypeStr.length > Constants.NUMBER_TWO) {
147          realFileType = realFileTypeStr[Constants.NUMBER_TWO];
148        }
149      }
150
151      GlobalContext.store('accountType', Number(type));
152      this.metaInfo = {
153        accountType: Number(type),
154        fileType: realFileType,
155        ownerAccount: '',
156        fileSize: this.fileSize
157      };
158      return ResultMsg.buildSuccess(this.metaInfo);
159    } catch (error) {
160      HiLog.wrapError(TAG, error, 'Error parse zipFile');
161      return ResultMsg.getErrMsg(Constants.ERR_JS_NOT_DLP_FILE);
162    } finally {
163      FileUtil.closeSync(file);
164      FileUtil.closeSync(ff);
165      FileUtil.unlinkSync(filePath);
166      FileUtil.rmdirSync(dirPath);
167    }
168  }
169}
170
171class RawParse extends FileParseBase {
172  constructor(fileSize: number) {
173    super(fileSize);
174    this.parseType = FileParseType.RAW;
175  }
176
177  async parse(uri: string, ctxFilesDir: string): Promise<Result<FileMetaInfo>> {
178    let file: fs.File | undefined;
179    try {
180      file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
181      const fileInfo = fs.statSync(file.fd);
182      this.fileSize = fileInfo.size;
183      let data = new ArrayBuffer(Constants.HEAD_LENGTH_IN_BYTE);
184      let option: ReadOptions = { offset: 0, length: Constants.HEAD_LENGTH_IN_BYTE };
185      fs.readSync(file.fd, data, option);
186
187      let buf = new Uint32Array(data, 0, Constants.HEAD_LENGTH_IN_U32);
188      let cert = new ArrayBuffer(buf[Constants.CERT_SIZE]);
189      let certOffset = Constants.CERT_OFFSET_4GB * buf[Constants.CERT_OFFSET + 1] + buf[Constants.CERT_OFFSET];
190      option = { offset: certOffset, length: buf[Constants.CERT_SIZE] };
191      fs.readSync(file.fd, cert, option);
192
193      const textDecoder: util.TextDecoder = util.TextDecoder.create('utf-8');
194      const fdString: string = textDecoder.decodeToString(new Uint8Array(cert), { stream: false });
195      const infoArray = fdString.split('accountType');
196      const type = infoArray[1].slice(Constants.TYPE_START, Constants.TYPE_END);
197      const accountType = Number(type);
198      if (accountType !== dlpPermission.AccountType.CLOUD_ACCOUNT &&
199        accountType !== dlpPermission.AccountType.DOMAIN_ACCOUNT) {
200        HiLog.error(TAG, `Error accountType: ${accountType}`);
201        return ResultMsg.getErrMsg(Constants.ERR_JS_NOT_DLP_FILE);
202      }
203
204      GlobalContext.store('accountType', accountType);
205      this.metaInfo = {
206        accountType: accountType,
207        fileType: NUM_TO_TYPE_MAP.has(buf[Constants.NUMBER_THREE]) ?
208          NUM_TO_TYPE_MAP.get(buf[Constants.NUMBER_THREE]) as string : '',
209        ownerAccount: '',
210        fileSize: this.fileSize
211      };
212      return ResultMsg.buildSuccess(this.metaInfo);
213    } catch (error) {
214      HiLog.wrapError(TAG, error, 'Error parse rawfile');
215      return ResultMsg.getErrMsg(Constants.ERR_JS_NOT_DLP_FILE);
216    } finally {
217      FileUtil.closeSync(file);
218    }
219  }
220}
221
222class FileFormatDetector {
223  static async detect(fd: number): Promise<Result<FileParseInfo>> {
224    HiLog.info(TAG, 'start detect');
225    let data = new ArrayBuffer(Constants.HEAD_LENGTH_IN_BYTE);
226    let option: ReadOptions = { offset: 0, length: Constants.HEAD_LENGTH_IN_BYTE };
227    let fileSize = 0;
228    try {
229      fs.readSync(fd, data, option);
230      const fileInfo = fs.statSync(fd);
231      fileSize = fileInfo.size;
232    } catch (error) {
233      HiLog.wrapError(TAG, error, 'FileFormatDetector error');
234      return ResultMsg.getErrMsg(Constants.ERR_CODE_OPEN_FILE_ERROR);
235    }
236    let buf = new Uint32Array(data, 0, Constants.HEAD_LENGTH_IN_U32);
237    if (buf && buf[0] === Constants.DLP_ZIP_MAGIC) {
238      HiLog.debug(TAG, 'FileFormatDetector zip');
239      const parseInfo: FileParseInfo = { parseType: FileParseType.ZIP, fileSize: fileSize };
240      return ResultMsg.buildSuccess(parseInfo);
241    }
242    if (buf && (buf[0] === Constants.DLP_RAW_MAGIC || (buf.length >= 3 && buf[2] === Constants.DLP_RAW_MAGIC))) {
243      HiLog.debug(TAG, 'FileFormatDetector raw');
244      const parseInfo: FileParseInfo = { parseType: FileParseType.RAW, fileSize: fileSize };
245      return ResultMsg.buildSuccess(parseInfo);
246    }
247    HiLog.error(TAG, 'FileFormatDetector not dlp file');
248    return ResultMsg.getErrMsg(Constants.ERR_JS_NOT_DLP_FILE);
249  }
250}
251
252export class FileParseFactory {
253  static async createFileParse(openDlpFileData: OpenDlpFileData): Promise<Result<FileParseBase>> {
254    HiLog.debug(TAG, 'CreateFileParse');
255    const uri = openDlpFileData.uri;
256    let getFileFdRet = getFileFd(uri, fs.OpenMode.READ_WRITE);
257    if (getFileFdRet.errcode !== Constants.ERR_CODE_SUCCESS || !getFileFdRet.result) {
258      HiLog.info(TAG, 'getFileFd with READ_WRITE failed, try to READ_ONLY.');
259      getFileFdRet = getFileFd(uri, fs.OpenMode.READ_ONLY);
260      if (getFileFdRet.errcode !== Constants.ERR_CODE_SUCCESS || !getFileFdRet.result) {
261        HiLog.error(TAG, 'getFileFd with READ_ONLY error.');
262        return ResultMsg.buildMsg(getFileFdRet.errcode, getFileFdRet.errmsg);
263      }
264    }
265    let dlpFd = getFileFdRet.result;
266    const format = await FileFormatDetector.detect(dlpFd);
267    if (format.errcode !== Constants.ERR_CODE_SUCCESS || !format.result) {
268      HiLog.error(TAG, 'detect failed');
269      return ResultMsg.buildMsg(format.errcode, format.errmsg);
270    }
271    FileUtil.closeSync(dlpFd);
272    switch (format.result.parseType) {
273      case FileParseType.ZIP:
274        openDlpFileData.fileParse = FileParseType.ZIP;
275        return ResultMsg.buildSuccess(new ZipParse(format.result.fileSize));
276      case FileParseType.RAW:
277        openDlpFileData.fileParse = FileParseType.RAW;
278        return ResultMsg.buildSuccess(new RawParse(format.result.fileSize));
279      default:
280        HiLog.error(TAG, 'createFileParse error');
281        return ResultMsg.getErrMsg(Constants.ERR_JS_NOT_DLP_FILE);
282    }
283  }
284}