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 measure from '@ohos.measure';
17import window from '@ohos.window';
18import ability from '@ohos.ability.ability';
19import account_osAccount from '@ohos.account.osAccount';
20import emitter from '@ohos.events.emitter';
21import dlpPermission from '@ohos.dlpPermission';
22import bundleManager from '@ohos.bundle.bundleManager';
23import fs from '@ohos.file.fs';
24import zlib from '@ohos.zlib';
25import util from '@ohos.util';
26import fileUri from '@ohos.file.fileuri';
27import { BusinessError, Callback } from '@ohos.base'
28import Want from '@ohos.app.ability.Want';
29import common from '@ohos.app.ability.common';
30import connection from '@ohos.net.connection';
31import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession';
32import Constants from '../common/constant';
33import GlobalContext from './GlobalContext';
34import hiSysEvent from '@ohos.hiSysEvent';
35import { HiLog } from '../common/HiLog';
36
37const TAG = 'Utils';
38const HEAD_LENGTH_IN_BYTE = 28;
39const HEAD_LENGTH_IN_U32 = 7;
40const CERT_OFFSET = 5;
41const CERT_SIZE = 6;
42class ChangeOption {
43  public offset: number = 0
44  public length: number = 0
45}
46let defaultDlpProperty: dlpPermission.DLPProperty = {
47  ownerAccount: '',
48  ownerAccountType: dlpPermission.AccountType.DOMAIN_ACCOUNT,
49  authUserList: [],
50  contactAccount: '',
51  offlineAccess: true,
52  ownerAccountID: '',
53  everyoneAccessList: []
54}
55
56let defaultDlpFile: dlpPermission.DLPFile = {
57  dlpProperty: defaultDlpProperty,
58  recoverDLPFile: async () => {},
59  closeDLPFile: async () => {},
60  addDLPLinkFile: async () => {},
61  stopFuseLink: async () => {},
62  resumeFuseLink: async () => {},
63  replaceDLPLinkFile: async () => {},
64  deleteDLPLinkFile: async () => {}
65};
66
67interface AuthAccount {
68  authAccount: string;
69  textContent?: string;
70}
71
72interface PermissionType {
73  value: Resource;
74  data: string;
75  index: number;
76}
77
78interface DLPInfo {
79  name: string;
80  versionCode: string;
81}
82
83function getFileUriByPath(filePath: string): string {
84  try {
85    let uri = fileUri.getUriFromPath(filePath);
86    return uri;
87  } catch (err) {
88    HiLog.info(TAG, `getUriFromPath error: ${JSON.stringify(err)}`);
89    return '';
90  }
91}
92
93function getFileFd(uri: string, mode?: number): number {
94  mode = mode || fs.OpenMode.READ_ONLY;
95  try {
96    let file = fs.openSync(uri, mode);
97    HiLog.info(TAG, `open: ${uri}, as: ${file.fd}`);
98    return file.fd;
99  } catch (err) {
100    HiLog.info(TAG, `openSync error: ${JSON.stringify(err)}`);
101    return -1;
102  }
103}
104
105async function getOsAccountInfo(): Promise<account_osAccount.OsAccountInfo> {
106  let accountMgr = account_osAccount.getAccountManager();
107  return await accountMgr.queryOsAccount();
108}
109
110function checkDomainAccountInfo(accountInfo: account_osAccount.OsAccountInfo): number {
111  AppStorage.setOrCreate('hiAccountType', dlpPermission.AccountType.DOMAIN_ACCOUNT);
112  if (accountInfo.domainInfo.accountName === '' &&
113    accountInfo.domainInfo.accountId === '') {
114    AppStorage.setOrCreate('hiAccountStatus', 0);
115    return Constants.ERR_JS_APP_NO_ACCOUNT_ERROR;
116  }
117  if (!accountInfo.domainInfo.isAuthenticated) {
118    AppStorage.setOrCreate('hiAccountStatus', 0);
119    return Constants.ERR_JS_APP_SYSTEM_IS_AUTHENTICATED;
120  }
121  AppStorage.setOrCreate('hiAccountStatus', 1);
122  return Constants.ERR_JS_APP_ACCOUNT_INFO;
123}
124
125async function getUserId(): Promise<number> {
126  let accountMgr = account_osAccount.getAccountManager();
127  return await accountMgr.getOsAccountLocalId();
128}
129
130function getAuthPerm(accountName: string, dlpProperty: dlpPermission.DLPProperty): dlpPermission.DLPFileAccess {
131  let perm: dlpPermission.DLPFileAccess = dlpPermission.DLPFileAccess.NO_PERMISSION;
132  if (accountName === dlpProperty.ownerAccount) {
133    return dlpPermission.DLPFileAccess.FULL_CONTROL;
134  }
135  if ((dlpProperty.everyoneAccessList !== undefined) && (dlpProperty.everyoneAccessList.length > 0)) {
136    perm = Math.max(...dlpProperty.everyoneAccessList);
137  }
138  let authUserList = dlpProperty.authUserList ?? [];
139  for (let i = 0; i < authUserList.length; ++i) {
140    let authUser = authUserList[i];
141    if (authUser.authAccount === accountName) {
142      return authUser.dlpFileAccess;
143    }
144  }
145  return perm;
146}
147
148function terminateSelfWithResult(resultCode: number, result: string): void {
149  let abilityResult: ability.AbilityResult = {
150    resultCode: resultCode,
151    want: {
152      parameters: {
153        result: result
154      }
155    }
156  };
157  (getContext() as common.UIAbilityContext).terminateSelfWithResult(abilityResult);
158}
159
160function judgeIsSandBox(want: Want) {
161  return new Promise<boolean>(async resolve => {
162    let callerToken: number = want.parameters?.['ohos.aafwk.param.callerToken'] as number;
163    let callerBundleName: string = want.parameters?.['ohos.aafwk.param.callerBundleName'] as string;
164    let applicationInfo = await bundleManager.getApplicationInfo(
165      callerBundleName, bundleManager.ApplicationFlag.GET_APPLICATION_INFO_DEFAULT);
166    if (callerToken === applicationInfo.accessTokenId) {
167      resolve(false);
168    }
169    resolve(true);
170  })
171}
172
173let removeDuplicate = (arr: AuthAccount[], arg: string) => {
174  let map: Map<string, AuthAccount> = new Map();
175  for (let item of arr) {
176    if (!map.has(item.authAccount)) {
177      map.set(item.authAccount, item);
178    }
179  }
180  return Array.from<AuthAccount>(map.values());
181}
182
183
184let calculate = (newValue: Area, allNames: AuthAccount[]) => {
185  let editLength = allNames.length;
186  let screenWidth = Number(newValue['width']);
187  let nameChange = Constants.ENCRYPTION_STAFF_WIDTH_NAME;
188  let rowNamesLen = Math.floor(screenWidth / (nameChange + Constants.ENCRYPTION_ADD_STAFF_MARGIN_RIGHT));
189  let showNamesArr = editLength > Constants.ENCRYPTION_DOUBLED_NUMBER * rowNamesLen
190    ? allNames.slice(0, 2 * rowNamesLen - 1)
191    : allNames.slice(0, 2 * rowNamesLen);
192  let hideNamesNum = editLength - showNamesArr.length > 0
193    ? String(editLength - showNamesArr.length)
194    : '0';
195  return {
196    'rowNamesLen': rowNamesLen,
197    'showNamesArr': showNamesArr,
198    'hideNamesNum': hideNamesNum
199  } as Record<string, number | AuthAccount[] | string>
200}
201
202let toggleShow = (allNames: AuthAccount[], showNamesArr: AuthAccount[], editFlag: boolean, rowNamesLen: number) => {
203  if (showNamesArr.length < allNames.length) {
204    let showFlag = !editFlag;
205    let showNamesArr = allNames;
206    return {
207      'showNamesArr': showNamesArr,
208      'showFlag': showFlag
209    } as Record<string, AuthAccount[] | boolean>;
210  } else {
211    let showFlag = !editFlag;
212    let showNamesArr = allNames.length > Constants.ENCRYPTION_DOUBLED_NUMBER * rowNamesLen
213      ? allNames.slice(0, Constants.ENCRYPTION_DOUBLED_NUMBER * rowNamesLen - 1)
214      : allNames.slice(0, Constants.ENCRYPTION_DOUBLED_NUMBER * rowNamesLen);
215    return {
216      'showNamesArr': showNamesArr,
217      'showFlag': showFlag
218    } as Record<string, AuthAccount[] | boolean>;
219  }
220}
221
222function directionStatus(func: Callback<number>) {
223  let innerEvent: emitter.InnerEvent = {
224    eventId: Constants.ENCRYPTION_EMIT_DIRECTION_STATUS
225  };
226  emitter.on(innerEvent, (eventData: emitter.EventData) => {
227    func(eventData.data?.direction);
228  });
229}
230
231function getAppId(bundleName: string) {
232  return new Promise<string>(async (resolve, reject) => {
233    let bundleFlags: number = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO;
234    let userId = await getUserId();
235    try {
236      let data = await bundleManager.getBundleInfo(bundleName, bundleFlags, userId);
237      if (data.signatureInfo.appId) {
238        resolve(data.signatureInfo.appId);
239        return;
240      }
241      reject();
242    } catch (err) {
243      HiLog.error(TAG, `get appId failed: ${JSON.stringify(err)}`);
244      reject();
245    }
246  })
247}
248
249function getTime() {
250  let permanent: boolean | undefined = AppStorage.get('permanent');
251  if (permanent) {
252    return $r('app.string.permanently');
253  }
254  let dateTime: number | undefined = AppStorage.get('validity');
255  if (dateTime !== undefined) {
256    let date = new Date(dateTime);
257    let year = date.getFullYear();
258    let month = date.getMonth() + 1;
259    let day = date.getDate();
260    let hour = date.getHours();
261    let minute = date.getMinutes();
262    return `${year}/${month}/${day} ${hour}:${minute}`;
263  } else {
264    return '';
265  }
266}
267
268async function getFileSizeByUri(uri: string): Promise<number> {
269  let inFile: fs.File | undefined;
270  try {
271    inFile = await fs.open(uri, fs.OpenMode.READ_ONLY);
272    let stat = await fs.stat(inFile.fd);
273    HiLog.info(TAG, `get file info succeed, the size of file is: ${stat.size}`);
274    return stat.size;
275  } catch (err) {
276    HiLog.error(TAG, `open: ${uri}, failed: ${JSON.stringify(err)}`);
277    HiLog.info(TAG, `get file info failed with error message: ${JSON.stringify(err)}`);
278    return -1;
279  } finally {
280    if (inFile) {
281      fs.closeSync(inFile);
282    }
283  }
284}
285
286function getDLPInfo() {
287  return new Promise<DLPInfo>(async (resolve, reject) => {
288    let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT;
289    try {
290      bundleManager.getBundleInfoForSelf(bundleFlags, (err, data) => {
291        if (err) {
292          HiLog.error(TAG, `getBundleInfoForSelf failed. Cause: ${JSON.stringify(err)}`);
293          reject({name: '', versionCode: ''});
294        } else {
295          resolve({name: data.name, versionCode: data.versionCode.toString()});
296        }
297      });
298    } catch (err) {
299      HiLog.error(TAG, `getBundleInfoForSelf failed. Cause: ${JSON.stringify(err)}`);
300      reject({name: '', versionCode: ''});
301    }
302  })
303}
304
305function sendDlpManagerAccountLogin(errorCode: number) {
306  let event: hiSysEvent.SysEventInfo = {
307    domain: 'DLP_UE',
308    name: 'DLP_MANAGER_ACCOUNT_LOGIN',
309    eventType: hiSysEvent?.EventType?.BEHAVIOR,
310    params: {
311      'PNAMEID': AppStorage.get('hiPNameId') ?? '',
312      'PVERSIONID': AppStorage.get('hiPVersionId') ?? '',
313      'ACCOUNT_TYPE': AppStorage.get('hiAccountType') ?? '',
314      'ACCOUNT_STATUS': AppStorage.get('hiAccountStatus') ?? -1,
315      'LOGIN_FAIL_CODE': errorCode ?? -1,
316      'PKG_NAME': AppStorage.get('hiPkgName') ?? '',
317    } as Record<string, number>
318  };
319
320  try {
321    hiSysEvent.write(event);
322  } catch (err) {
323    HiLog.error(TAG, `sendDlpManagerAccountLogin failed`);
324  }
325}
326
327function sendDlpManagerFileConfiguration() {
328  let event: hiSysEvent.SysEventInfo = {
329    domain: 'DLP_UE',
330    name: 'DLP_MANAGER_FILE_CONFIGURATION',
331    eventType: hiSysEvent?.EventType?.BEHAVIOR,
332    params: {
333      'PNAMEID': AppStorage.get('hiPNameId') ?? '',
334      'PVERSIONID': AppStorage.get('hiPVersionId') ?? '',
335      'OPERATION': AppStorage.get('hiOperation') ?? '',
336      'READ_SCOPE': AppStorage.get('hiReadScope') ?? '',
337      'WRITE_SCOPE': AppStorage.get('hiWriteScope') ?? '',
338      'ADVANCED_SETTINGS': AppStorage.get('hiAdvancedSettings') ?? false,
339      'STORE_PATH': AppStorage.get('hiStorePath') ?? false,
340      'ACCOUNT_VERIFY_SUCC': AppStorage.get('hiAccountVerifySucc') ?? -1,
341      'ACCOUNT_VERIFY_FAIL': AppStorage.get('hiAccountVerifyFail') ?? -1,
342      'VALID_DATE': AppStorage.get('hiValidDate') ?? false,
343    } as Record<string, number>
344  };
345
346  try {
347    hiSysEvent.write(event);
348  } catch (err) {
349    HiLog.error(TAG, `sendDlpManagerFileConfiguration failed`);
350  }
351}
352
353function sendDlpFileCreateProperties(accountType: number) {
354  let event: hiSysEvent.SysEventInfo = {
355    domain: 'DLP_UE',
356    name: 'DLP_FILE_CREATE_EVENT',
357    eventType: hiSysEvent?.EventType?.BEHAVIOR,
358    params: {
359      'ACCOUNT_TYPE': accountType,
360      'PNAMEID': AppStorage.get('hiPNameId') ?? '',
361      'PVERSIONID': AppStorage.get('hiPVersionId') ?? '',
362      'CODE': AppStorage.get('hiCode') ?? -1,
363      'FILE_SIZE': AppStorage.get('hiFileSize') ?? -1,
364      'FILE_TYPE': AppStorage.get('hiFileType') ?? '',
365      'POLICY_SIZE_ENC': AppStorage.get('hiPolicySizeEnc') ?? -1,
366      'PKG_NAME': AppStorage.get('hiPkgName') ?? '',
367    } as Record<string, number>
368  };
369
370  try {
371    hiSysEvent.write(event);
372  } catch (err) {
373    HiLog.error(TAG, `sendDlpFileCreateProperties failed`);
374  }
375}
376
377function sendDlpFileOpenProperties() {
378  let event: hiSysEvent.SysEventInfo = {
379    domain: 'DLP_UE',
380    name: 'DLP_FILE_OPEN_EVENT',
381    eventType: hiSysEvent?.EventType?.BEHAVIOR,
382    params: {
383      'PNAMEID': AppStorage.get('hiPNameId') ?? '',
384      'PVERSIONID': AppStorage.get('hiPVersionId') ?? '',
385      'CODE': AppStorage.get('hiCode') ?? -1,
386      'SANDBOX_PKGNAME': AppStorage.get('hiSandboxPkgName') ?? '',
387      'SANDBOX_INDEX': AppStorage.get('hiSandboxIndex') ?? -1,
388      'ACCOUNT_TYPE': AppStorage.get('hiAccountType') ?? '',
389      'FILE_SIZE': AppStorage.get('hiFileSize') ?? -1,
390      'POLICY_SIZE_ENC': AppStorage.get('hiPolicySizeEnc') ?? -1,
391    } as Record<string, number>
392  };
393
394  try {
395    hiSysEvent.write(event);
396  } catch (err) {
397    HiLog.error(TAG, `sendDlpFileOpenProperties failed`);
398  }
399}
400
401function isValidPath(path: string): Boolean {
402  if (path.indexOf('/./') !== -1 || path.indexOf('/../') !== -1) {
403    return false;
404  }
405  return true;
406}
407
408function getAccountType(
409  context: common.ServiceExtensionContext | common.UIExtensionContext, fd: number): Promise<number> {
410  return new Promise(async (resolve, reject) => {
411    let z = new ArrayBuffer(HEAD_LENGTH_IN_BYTE);
412    let option: ChangeOption = { offset: 0, length: HEAD_LENGTH_IN_BYTE };
413    let num = fs.readSync(fd, z, option);
414    let buf = new Uint32Array(z, 0, HEAD_LENGTH_IN_U32);
415    if (buf && buf[0] === Constants.DLP_ZIP_MAGIC) {
416      let random = String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END);
417      let filePath = context.filesDir + '/saveAs' + random;
418      let dirPath = context.filesDir + '/saveAsUnzip' + random;
419      let fileName = dirPath + '/dlp_cert';
420      let ff: fs.File | undefined;
421      try {
422        fs.readSync(fd, z, option);
423        ff = await fs.open(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
424        await fs.copyFile(fd, ff.fd);
425        await fs.mkdir(dirPath);
426        await zlib.decompressFile(filePath, dirPath);
427        let dlpInfo = fs.readTextSync(fileName);
428        let infoArray = dlpInfo.split('accountType');
429        let type = infoArray[1].slice(Constants.TYPE_START, Constants.TYPE_END);
430        GlobalContext.store('accountType', Number(type));
431        resolve(Number(type));
432      } catch (err) {
433        HiLog.error(TAG, `decompressFile: ${JSON.stringify(err)}`);
434        reject();
435      } finally {
436        closeFile(ff);
437        deleteFile(filePath);
438        removeDir(dirPath);
439      }
440    } else {
441      let cert = new ArrayBuffer(buf[CERT_SIZE]);
442      option = { offset: buf[CERT_OFFSET], length: buf[CERT_SIZE] };
443      num = fs.readSync(fd, cert, option);
444      try {
445        let textDecoder: util.TextDecoder = util.TextDecoder.create('utf-8');
446        let fdString: string = textDecoder.decodeWithStream(new Uint8Array(cert), { stream: false });
447        let infoArray = fdString.split('accountType');
448        let type = infoArray[1].slice(Constants.TYPE_START, Constants.TYPE_END);
449        GlobalContext.store('accountType', Number(type));
450        resolve(Number(type));
451      } catch (err) {
452        HiLog.error(TAG, `getStringFromFd error: ${JSON.stringify(err)}`);
453        reject();
454      }
455    }
456  })
457}
458
459function closeFile(file: fs.File | undefined) {
460  try {
461    if (file) {
462      fs.closeSync(file);
463    }
464  } catch (err) {
465    HiLog.error(TAG, `closeSync: ${JSON.stringify(err)}`);
466  }
467}
468
469function deleteFile(file: string) {
470  try {
471    let res = fs.accessSync(file);
472    if (res) {
473      fs.unlinkSync(file);
474    }
475  } catch (err) {
476    HiLog.error(TAG, `deleteFile: ${JSON.stringify(err)}`);
477  }
478}
479
480function removeDir(file: string) {
481  try {
482    let res = fs.accessSync(file);
483    if (res) {
484      fs.rmdirSync(file);
485    }
486  } catch (err) {
487    HiLog.error(TAG, `rmdirSync: ${JSON.stringify(err)}`);
488  }
489}
490
491function checkNetworkStatus(): Promise<void> {
492  return new Promise((resolve, reject) => {
493    let netHandle = connection.getDefaultNetSync();
494    connection.getNetCapabilities(netHandle, (error: BusinessError, data: connection.NetCapabilities) => {
495      if (error) {
496        HiLog.error(TAG, `checkNetworkStatus failed: ${JSON.stringify(error)}`);
497        reject();
498        return;
499      };
500      HiLog.info(TAG, `network Succeeded to get data: ${JSON.stringify(data)}`);
501      const result = [connection.NetCap.NET_CAPABILITY_INTERNET, connection.NetCap.NET_CAPABILITY_VALIDATED]
502        .every(element => data.networkCap?.includes(element));
503      if (result) {
504        resolve();
505        return;
506      }
507      reject();
508    })
509  })
510}
511
512export {
513  AuthAccount,
514  PermissionType,
515  getOsAccountInfo,
516  checkDomainAccountInfo,
517  getUserId,
518  getAuthPerm,
519  terminateSelfWithResult,
520  judgeIsSandBox,
521  getFileFd,
522  getFileUriByPath,
523  removeDuplicate,
524  calculate,
525  toggleShow,
526  directionStatus,
527  getAppId,
528  getTime,
529  getFileSizeByUri,
530  getDLPInfo,
531  sendDlpManagerAccountLogin,
532  sendDlpManagerFileConfiguration,
533  sendDlpFileCreateProperties,
534  sendDlpFileOpenProperties,
535  DLPInfo,
536  isValidPath,
537  defaultDlpFile,
538  getAccountType,
539  checkNetworkStatus
540};
541