{"version":3,"file":"worker-lazy-e482062c.js","sources":["../../../src/api/worker/facades/lazy/BookingFacade.ts","../../../src/api/worker/facades/lazy/CounterFacade.ts","../../../src/api/worker/facades/lazy/GroupManagementFacade.ts","../../../src/api/worker/facades/lazy/UserManagementFacade.ts","../../../src/api/worker/facades/lazy/CustomerFacade.ts","../../../src/api/worker/facades/lazy/BlobFacade.ts","../../../src/api/worker/facades/lazy/FileFacade.ts","../../../src/api/worker/facades/lazy/MailFacade.ts","../../../src/api/worker/facades/lazy/CalendarFacade.ts","../../../src/api/worker/facades/lazy/MailAddressFacade.ts","../../../src/api/worker/facades/lazy/ShareFacade.ts","../../../src/api/worker/facades/lazy/GiftCardFacade.ts","../../../src/api/worker/facades/lazy/ConfigurationDatabase.ts","../../../src/api/worker/facades/lazy/ContactFormFacade.ts"],"sourcesContent":["import type { BookingItemFeatureType } from \"../../../common/TutanotaConstants.js\"\nimport { Const } from \"../../../common/TutanotaConstants.js\"\nimport type { PriceData, PriceItemData, PriceServiceReturn } from \"../../../entities/sys/TypeRefs.js\"\nimport { createPriceRequestData, createPriceServiceData } from \"../../../entities/sys/TypeRefs.js\"\nimport { neverNull } from \"@tutao/tutanota-utils\"\nimport { assertWorkerOrNode } from \"../../../common/Env.js\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { PriceService } from \"../../../entities/sys/Services.js\"\n\nassertWorkerOrNode()\n\nexport class BookingFacade {\n\tconstructor(private readonly serviceExecutor: IServiceExecutor) {}\n\n\t/**\n\t * Provides the price for a given feature type and count.\n\t * @param  type The booking feature type, one of tutao.entity.tutanota.TutanotaConstants.BOOKING_ITEM_FEATURE_TYPE_*.\n\t * @param  count Number of items, may be negative.\n\t * @param  reactivate  If true a user or group is reactivated instead of created - not used for aliases, storage or branding\n\t * @return Resolves to PriceServiceReturn or an exception if the loading failed.\n\t */\n\tgetPrice(type: BookingItemFeatureType, count: number, reactivate: boolean): Promise<PriceServiceReturn> {\n\t\tconst priceRequestData = createPriceRequestData({\n\t\t\tfeatureType: type,\n\t\t\tcount: String(count),\n\t\t\treactivate,\n\t\t\tpaymentInterval: null,\n\t\t\taccountType: null,\n\t\t\tbusiness: null,\n\t\t})\n\t\tconst serviceData = createPriceServiceData({\n\t\t\tdate: Const.CURRENT_DATE,\n\t\t\tpriceRequest: priceRequestData,\n\t\t})\n\t\treturn this.serviceExecutor.get(PriceService, serviceData)\n\t}\n\n\t/**\n\t * Provides the price for a given feature type and count.\n\t * @return Resolves to PriceServiceReturn or an exception if the loading failed.\n\t */\n\tgetCurrentPrice(): Promise<PriceServiceReturn> {\n\t\tconst serviceData = createPriceServiceData()\n\t\treturn this.serviceExecutor.get(PriceService, serviceData)\n\t}\n\n\t/**\n\t * Provides the price item from the given priceData for the given featureType. Returns null if no such item is available.\n\t * @param  priceData The given price data.\n\t * @param  featureType The booking item feature type\n\t * @return The price item or null\n\t */\n\tgetPriceItem(priceData: PriceData | null, featureType: NumberString): PriceItemData | null {\n\t\tif (priceData != null) {\n\t\t\treturn neverNull(priceData).items.find((p) => p.featureType === featureType) ?? null\n\t\t}\n\n\t\treturn null\n\t}\n}\n","import { CounterValue, createReadCounterData } from \"../../../entities/monitor/TypeRefs.js\"\nimport { assertWorkerOrNode } from \"../../../common/Env.js\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { CounterService } from \"../../../entities/monitor/Services.js\"\nimport { CounterType } from \"../../../common/TutanotaConstants.js\"\n\nassertWorkerOrNode()\n\nexport class CounterFacade {\n\tconstructor(private readonly serviceExecutor: IServiceExecutor) {}\n\n\tasync readCounterValue(counterType: CounterType, rowName: string, columnName: Id): Promise<number> {\n\t\tconst counterData = createReadCounterData({\n\t\t\tcounterType,\n\t\t\trowName,\n\t\t\tcolumnName,\n\t\t})\n\t\tconst counterReturn = await this.serviceExecutor.get(CounterService, counterData)\n\t\treturn Number(counterReturn.counterValues[0].value)\n\t}\n\n\tasync readAllCustomerCounterValues(counterType: CounterType, customerId: Id): Promise<CounterValue[]> {\n\t\tconst counterData = createReadCounterData({\n\t\t\tcounterType,\n\t\t\trowName: customerId,\n\t\t})\n\t\tconst counterReturn = await this.serviceExecutor.get(CounterService, counterData)\n\t\treturn counterReturn.counterValues\n\t}\n}\n","import { Const, CounterType, GroupType } from \"../../../common/TutanotaConstants.js\"\nimport type { InternalGroupData, UserAreaGroupData } from \"../../../entities/tutanota/TypeRefs.js\"\nimport {\n\tcreateCreateMailGroupData,\n\tcreateDeleteGroupData,\n\tcreateInternalGroupData,\n\tcreateUserAreaGroupData,\n\tcreateUserAreaGroupPostData,\n} from \"../../../entities/tutanota/TypeRefs.js\"\nimport { assertNotNull, hexToUint8Array, neverNull } from \"@tutao/tutanota-utils\"\nimport type { Group, User } from \"../../../entities/sys/TypeRefs.js\"\nimport { createMembershipAddData, createMembershipRemoveData, GroupTypeRef, UserTypeRef } from \"../../../entities/sys/TypeRefs.js\"\nimport { CounterFacade } from \"./CounterFacade.js\"\nimport { EntityClient } from \"../../../common/EntityClient.js\"\nimport { assertWorkerOrNode } from \"../../../common/Env.js\"\nimport { encryptString } from \"../../crypto/CryptoFacade.js\"\nimport type { RsaImplementation } from \"../../crypto/RsaImplementation.js\"\nimport { aes128RandomKey, decryptKey, encryptKey, encryptRsaKey, publicKeyToHex, RsaKeyPair } from \"@tutao/tutanota-crypto\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { LocalAdminGroupService, MailGroupService, TemplateGroupService } from \"../../../entities/tutanota/Services.js\"\nimport { MembershipService } from \"../../../entities/sys/Services.js\"\nimport { UserFacade } from \"../UserFacade.js\"\nimport { ProgrammingError } from \"../../../common/error/ProgrammingError.js\"\n\nassertWorkerOrNode()\n\nexport class GroupManagementFacade {\n\tconstructor(\n\t\tprivate readonly user: UserFacade,\n\t\tprivate readonly counters: CounterFacade,\n\t\tprivate readonly entityClient: EntityClient,\n\t\tprivate readonly rsa: RsaImplementation,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t) {}\n\n\tasync readUsedSharedMailGroupStorage(group: Group): Promise<number> {\n\t\treturn this.counters.readCounterValue(CounterType.UserStorageLegacy, neverNull(group.customer), group._id)\n\t}\n\n\tasync createMailGroup(name: string, mailAddress: string): Promise<void> {\n\t\tlet adminGroupIds = this.user.getGroupIds(GroupType.Admin)\n\n\t\tif (adminGroupIds.length === 0) {\n\t\t\tadminGroupIds = this.user.getGroupIds(GroupType.LocalAdmin)\n\t\t}\n\n\t\tlet adminGroupKey = this.user.getGroupKey(adminGroupIds[0])\n\n\t\tlet customerGroupKey = this.user.getGroupKey(this.user.getGroupId(GroupType.Customer))\n\n\t\tlet mailGroupKey = aes128RandomKey()\n\t\tlet mailGroupInfoSessionKey = aes128RandomKey()\n\t\tlet mailboxSessionKey = aes128RandomKey()\n\t\tconst keyPair = await this.rsa.generateKey()\n\t\tconst mailGroupData = await this.generateInternalGroupData(\n\t\t\tkeyPair,\n\t\t\tmailGroupKey,\n\t\t\tmailGroupInfoSessionKey,\n\t\t\tadminGroupIds[0],\n\t\t\tadminGroupKey,\n\t\t\tcustomerGroupKey,\n\t\t)\n\t\tconst data = createCreateMailGroupData({\n\t\t\tmailAddress,\n\t\t\tencryptedName: encryptString(mailGroupInfoSessionKey, name),\n\t\t\tmailEncMailboxSessionKey: encryptKey(mailGroupKey, mailboxSessionKey),\n\t\t\tgroupData: mailGroupData,\n\t\t})\n\t\tawait this.serviceExecutor.post(MailGroupService, data)\n\t}\n\n\t/**\n\t * Generates keys for the new group and prepares the group data object to create the group.\n\t *\n\t * @param adminGroup Is not set when generating new customer, then the admin group will be the admin of the customer\n\t * @param adminGroupKey Is not set when generating calendar as normal user\n\t * @param customerGroupKey Group key of the customer\n\t * @param userGroupKey user group key\n\t * @param name Name of the group\n\t */\n\tgenerateUserAreaGroupData(name: string): Promise<UserAreaGroupData> {\n\t\treturn this.entityClient.load(GroupTypeRef, this.user.getUserGroupId()).then((userGroup) => {\n\t\t\tconst adminGroupId = neverNull(userGroup.admin) // user group has always admin group\n\n\t\t\tlet adminGroupKey: BitArray | null = null\n\n\t\t\tif (this.user.getAllGroupIds().indexOf(adminGroupId) !== -1) {\n\t\t\t\t// getGroupKey throws an error if user is not member of that group - so check first\n\t\t\t\tadminGroupKey = this.user.getGroupKey(adminGroupId)\n\t\t\t}\n\n\t\t\tconst customerGroupKey = this.user.getGroupKey(this.user.getGroupId(GroupType.Customer))\n\n\t\t\tconst userGroupKey = this.user.getUserGroupKey()\n\n\t\t\tconst groupRootSessionKey = aes128RandomKey()\n\t\t\tconst groupInfoSessionKey = aes128RandomKey()\n\t\t\tconst groupKey = aes128RandomKey()\n\t\t\treturn createUserAreaGroupData({\n\t\t\t\tgroupEncGroupRootSessionKey: encryptKey(groupKey, groupRootSessionKey),\n\t\t\t\tcustomerEncGroupInfoSessionKey: encryptKey(customerGroupKey, groupInfoSessionKey),\n\t\t\t\tuserEncGroupKey: encryptKey(userGroupKey, groupKey),\n\t\t\t\tgroupInfoEncName: encryptString(groupInfoSessionKey, name),\n\t\t\t\tadminEncGroupKey: adminGroupKey ? encryptKey(adminGroupKey, groupKey) : null,\n\t\t\t\tadminGroup: adminGroupId,\n\t\t\t})\n\t\t})\n\t}\n\n\tcreateTemplateGroup(name: string): Promise<Id> {\n\t\treturn this.generateUserAreaGroupData(name).then((groupData) => {\n\t\t\tconst serviceData = createUserAreaGroupPostData({\n\t\t\t\tgroupData: groupData,\n\t\t\t})\n\t\t\treturn this.serviceExecutor.post(TemplateGroupService, serviceData).then((returnValue) => returnValue.group)\n\t\t})\n\t}\n\n\tgenerateInternalGroupData(\n\t\tkeyPair: RsaKeyPair,\n\t\tgroupKey: Aes128Key,\n\t\tgroupInfoSessionKey: Aes128Key,\n\t\tadminGroupId: Id | null,\n\t\tadminGroupKey: Aes128Key,\n\t\townerGroupKey: Aes128Key,\n\t): InternalGroupData {\n\t\tlet groupData = createInternalGroupData()\n\t\tgroupData.publicKey = hexToUint8Array(publicKeyToHex(keyPair.publicKey))\n\t\tgroupData.groupEncPrivateKey = encryptRsaKey(groupKey, keyPair.privateKey)\n\t\tgroupData.adminGroup = adminGroupId\n\t\tgroupData.adminEncGroupKey = encryptKey(adminGroupKey, groupKey)\n\t\tgroupData.ownerEncGroupInfoSessionKey = encryptKey(ownerGroupKey, groupInfoSessionKey)\n\t\treturn groupData\n\t}\n\n\tasync addUserToGroup(user: User, groupId: Id): Promise<void> {\n\t\tconst userGroupKey = await this.getGroupKeyViaAdminEncGKey(user.userGroup.group)\n\t\tconst groupKey = await this.getGroupKeyViaAdminEncGKey(groupId)\n\t\tconst data = createMembershipAddData({\n\t\t\tuser: user._id,\n\t\t\tgroup: groupId,\n\t\t\tsymEncGKey: encryptKey(userGroupKey, groupKey),\n\t\t})\n\t\tawait this.serviceExecutor.post(MembershipService, data)\n\t}\n\n\tasync removeUserFromGroup(userId: Id, groupId: Id): Promise<void> {\n\t\tconst data = createMembershipRemoveData({\n\t\t\tuser: userId,\n\t\t\tgroup: groupId,\n\t\t})\n\t\tawait this.serviceExecutor.delete(MembershipService, data)\n\t}\n\n\tasync deactivateGroup(group: Group, restore: boolean): Promise<void> {\n\t\tconst data = createDeleteGroupData({\n\t\t\tgroup: group._id,\n\t\t\trestore,\n\t\t})\n\n\t\tif (group.type === GroupType.Mail) {\n\t\t\tawait this.serviceExecutor.delete(MailGroupService, data)\n\t\t} else if (group.type === GroupType.LocalAdmin) {\n\t\t\tawait this.serviceExecutor.delete(LocalAdminGroupService, data)\n\t\t} else {\n\t\t\tthrow new Error(\"invalid group type for deactivation\")\n\t\t}\n\t}\n\n\t/**\n\t * Get a group key for any group we are admin and know some member of.\n\t *\n\t * Unlike {@link getGroupKeyViaAdminEncGKey} this should work for any group because we will actually go a \"long\" route of decrypting userGroupKey of the\n\t * member and decrypting group key with that.\n\t */\n\tasync getGroupKeyViaUser(groupId: Id, viaUser: Id): Promise<Aes128Key> {\n\t\tconst user = await this.entityClient.load(UserTypeRef, viaUser)\n\t\tconst userGroupKey = await this.getGroupKeyViaAdminEncGKey(user.userGroup.group)\n\t\tconst ship = user.memberships.find((m) => m.group === groupId)\n\t\tif (ship == null) {\n\t\t\tthrow new Error(`User doesn't have this group membership! User: ${viaUser} groupId: ${groupId}`)\n\t\t}\n\t\treturn decryptKey(userGroupKey, ship.symEncGKey)\n\t}\n\n\t/**\n\t * Get a group key for certain group types.\n\t *\n\t * Some groups (e.g. user groups or shared mailboxes) have adminGroupEncGKey set on creation. For those groups we can fairly easy get a group key without\n\t * decrypting userGroupKey of some member of that group.\n\t */\n\tgetGroupKeyViaAdminEncGKey(groupId: Id): Promise<Aes128Key> {\n\t\tif (this.user.hasGroup(groupId)) {\n\t\t\t// e.g. I am a global admin and want to add another user to the global admin group\n\t\t\treturn Promise.resolve(this.user.getGroupKey(groupId))\n\t\t} else {\n\t\t\treturn this.entityClient.load(GroupTypeRef, groupId).then((group) => {\n\t\t\t\tif (group.adminGroupEncGKey == null || group.adminGroupEncGKey.length === 0) {\n\t\t\t\t\tthrow new ProgrammingError(\"Group doesn't have adminGroupEncGKey, you can't get group key this way\")\n\t\t\t\t}\n\t\t\t\treturn Promise.resolve()\n\t\t\t\t\t.then(() => {\n\t\t\t\t\t\tif (group.admin && this.user.hasGroup(group.admin)) {\n\t\t\t\t\t\t\t// e.g. I am a member of the group that administrates group G and want to add a new member to G\n\t\t\t\t\t\t\treturn this.user.getGroupKey(assertNotNull(group.admin))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// e.g. I am a global admin but group G is administrated by a local admin group and want to add a new member to G\n\t\t\t\t\t\t\tlet globalAdminGroupId = this.user.getGroupId(GroupType.Admin)\n\n\t\t\t\t\t\t\tlet globalAdminGroupKey = this.user.getGroupKey(globalAdminGroupId)\n\n\t\t\t\t\t\t\treturn this.entityClient.load(GroupTypeRef, assertNotNull(group.admin)).then((localAdminGroup) => {\n\t\t\t\t\t\t\t\tif (localAdminGroup.admin === globalAdminGroupId) {\n\t\t\t\t\t\t\t\t\treturn decryptKey(globalAdminGroupKey, assertNotNull(localAdminGroup.adminGroupEncGKey))\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tthrow new Error(`local admin group ${localAdminGroup._id} is not administrated by global admin group ${globalAdminGroupId}`)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.then((adminGroupKey) => {\n\t\t\t\t\t\treturn decryptKey(adminGroupKey, assertNotNull(group.adminGroupEncGKey))\n\t\t\t\t\t})\n\t\t\t})\n\t\t}\n\t}\n}\n","import { AccountType, Const, CounterType, GroupType } from \"../../../common/TutanotaConstants.js\"\nimport type { User } from \"../../../entities/sys/TypeRefs.js\"\nimport {\n\tcreateMembershipAddData,\n\tcreateRecoverCode,\n\tcreateResetPasswordData,\n\tcreateUpdateAdminshipData,\n\tcreateUserDataDelete,\n\tGroupTypeRef,\n\tRecoverCodeTypeRef,\n} from \"../../../entities/sys/TypeRefs.js\"\nimport { encryptBytes, encryptString } from \"../../crypto/CryptoFacade.js\"\nimport { assertNotNull, neverNull, uint8ArrayToHex } from \"@tutao/tutanota-utils\"\nimport type { ContactFormUserData, UserAccountUserData } from \"../../../entities/tutanota/TypeRefs.js\"\nimport { createContactFormUserData, createUserAccountCreateData, createUserAccountUserData } from \"../../../entities/tutanota/TypeRefs.js\"\nimport type { GroupManagementFacade } from \"./GroupManagementFacade.js\"\nimport type { RecoverData } from \"../LoginFacade.js\"\nimport { CounterFacade } from \"./CounterFacade.js\"\nimport { assertWorkerOrNode } from \"../../../common/Env.js\"\nimport {\n\taes128RandomKey,\n\taes256EncryptKey,\n\taes256RandomKey,\n\tbitArrayToUint8Array,\n\tcreateAuthVerifier,\n\tcreateAuthVerifierAsBase64Url,\n\tdecrypt256Key,\n\tdecryptKey,\n\tencrypt256Key,\n\tencryptKey,\n\tgenerateKeyFromPassphrase,\n\tgenerateRandomSalt,\n\tKeyLength,\n\trandom,\n} from \"@tutao/tutanota-crypto\"\nimport type { RsaImplementation } from \"../../crypto/RsaImplementation.js\"\nimport { EntityClient } from \"../../../common/EntityClient.js\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { MembershipService, ResetPasswordService, SystemKeysService, UpdateAdminshipService, UserService } from \"../../../entities/sys/Services.js\"\nimport { UserAccountService } from \"../../../entities/tutanota/Services.js\"\nimport { UserFacade } from \"../UserFacade.js\"\nimport { ExposedOperationProgressTracker, OperationId } from \"../../../main/OperationProgressTracker.js\"\n\nassertWorkerOrNode()\n\nexport class UserManagementFacade {\n\tconstructor(\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly groupManagement: GroupManagementFacade,\n\t\tprivate readonly counters: CounterFacade,\n\t\tprivate readonly rsa: RsaImplementation,\n\t\tprivate readonly entityClient: EntityClient,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly operationProgressTracker: ExposedOperationProgressTracker,\n\t) {}\n\n\tasync changeUserPassword(user: User, newPassword: string): Promise<void> {\n\t\tconst userGroupKey = await this.groupManagement.getGroupKeyViaAdminEncGKey(user.userGroup.group)\n\t\tconst salt = generateRandomSalt()\n\t\tconst passwordKey = generateKeyFromPassphrase(newPassword, salt, KeyLength.b128)\n\t\tconst pwEncUserGroupKey = encryptKey(passwordKey, userGroupKey)\n\t\tconst passwordVerifier = createAuthVerifier(passwordKey)\n\t\tconst data = createResetPasswordData({\n\t\t\tuser: user._id,\n\t\t\tsalt,\n\t\t\tverifier: passwordVerifier,\n\t\t\tpwEncUserGroupKey,\n\t\t})\n\t\tawait this.serviceExecutor.post(ResetPasswordService, data)\n\t}\n\n\tasync changeAdminFlag(user: User, admin: boolean): Promise<void> {\n\t\tlet adminGroupId = this.userFacade.getGroupId(GroupType.Admin)\n\n\t\tlet adminGroupKey = this.userFacade.getGroupKey(adminGroupId)\n\n\t\tconst userGroup = await this.entityClient.load(GroupTypeRef, user.userGroup.group)\n\t\tlet userGroupKey = decryptKey(adminGroupKey, neverNull(userGroup.adminGroupEncGKey))\n\n\t\tif (admin) {\n\t\t\tawait this.groupManagement.addUserToGroup(user, adminGroupId)\n\n\t\t\tif (user.accountType !== AccountType.SYSTEM) {\n\t\t\t\tconst keyData = await this._getAccountKeyData()\n\t\t\t\t// we can not use addUserToGroup here because the admin is not admin of the account group\n\t\t\t\tconst addAccountGroup = createMembershipAddData({\n\t\t\t\t\tuser: user._id,\n\t\t\t\t\tgroup: keyData.group,\n\t\t\t\t\tsymEncGKey: encryptKey(userGroupKey, decryptKey(this.userFacade.getUserGroupKey(), keyData.symEncGKey)),\n\t\t\t\t})\n\t\t\t\tawait this.serviceExecutor.post(MembershipService, addAccountGroup)\n\t\t\t}\n\t\t} else {\n\t\t\tawait this.groupManagement.removeUserFromGroup(user._id, adminGroupId)\n\n\t\t\tif (user.accountType !== AccountType.SYSTEM) {\n\t\t\t\tconst keyData = await this._getAccountKeyData()\n\t\t\t\treturn this.groupManagement.removeUserFromGroup(user._id, keyData.group)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get key and id of premium or starter group.\n\t * @throws Error if account type is not premium or starter\n\t *\n\t * @private\n\t */\n\tasync _getAccountKeyData(): Promise<{ group: Id; symEncGKey: Uint8Array }> {\n\t\tconst keysReturn = await this.serviceExecutor.get(SystemKeysService, null)\n\t\tconst user = this.userFacade.getLoggedInUser()\n\n\t\tif (user.accountType === AccountType.PREMIUM) {\n\t\t\treturn {\n\t\t\t\tgroup: neverNull(keysReturn.premiumGroup),\n\t\t\t\tsymEncGKey: keysReturn.premiumGroupKey,\n\t\t\t}\n\t\t} else if (user.accountType === AccountType.STARTER) {\n\t\t\t// We don't have starterGroup on SystemKeyReturn so we hardcode it for now.\n\t\t\treturn {\n\t\t\t\tgroup: \"JDpWrwG----0\",\n\t\t\t\tsymEncGKey: keysReturn.starterGroupKey,\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new Error(`Trying to get keyData for user with account type ${user.accountType}`)\n\t\t}\n\t}\n\n\tasync updateAdminship(groupId: Id, newAdminGroupId: Id): Promise<void> {\n\t\tlet adminGroupId = this.userFacade.getGroupId(GroupType.Admin)\n\t\tconst newAdminGroup = await this.entityClient.load(GroupTypeRef, newAdminGroupId)\n\t\tconst group = await this.entityClient.load(GroupTypeRef, groupId)\n\t\tconst oldAdminGroup = await this.entityClient.load(GroupTypeRef, neverNull(group.admin))\n\n\t\tconst adminGroupKey = this.userFacade.getGroupKey(adminGroupId)\n\n\t\tlet groupKey\n\t\tif (oldAdminGroup._id === adminGroupId) {\n\t\t\tgroupKey = decryptKey(adminGroupKey, neverNull(group.adminGroupEncGKey))\n\t\t} else {\n\t\t\tlet localAdminGroupKey = decryptKey(adminGroupKey, neverNull(oldAdminGroup.adminGroupEncGKey))\n\t\t\tgroupKey = decryptKey(localAdminGroupKey, neverNull(group.adminGroupEncGKey))\n\t\t}\n\n\t\tlet newAdminGroupEncGKey\n\t\tif (newAdminGroup._id === adminGroupId) {\n\t\t\tnewAdminGroupEncGKey = encryptKey(adminGroupKey, groupKey)\n\t\t} else {\n\t\t\tlet localAdminGroupKey = decryptKey(adminGroupKey, neverNull(newAdminGroup.adminGroupEncGKey))\n\t\t\tnewAdminGroupEncGKey = encryptKey(localAdminGroupKey, groupKey)\n\t\t}\n\n\t\tconst data = createUpdateAdminshipData({\n\t\t\tgroup: group._id,\n\t\t\tnewAdminGroup: newAdminGroup._id,\n\t\t\tnewAdminGroupEncGKey,\n\t\t})\n\t\tawait this.serviceExecutor.post(UpdateAdminshipService, data)\n\t}\n\n\tasync readUsedUserStorage(user: User): Promise<number> {\n\t\tconst counterValue = await this.counters.readCounterValue(CounterType.UserStorageLegacy, neverNull(user.customer), user.userGroup.group)\n\t\treturn Number(counterValue)\n\t}\n\n\tasync deleteUser(user: User, restore: boolean): Promise<void> {\n\t\tconst data = createUserDataDelete({\n\t\t\tuser: user._id,\n\t\t\trestore,\n\t\t\tdate: Const.CURRENT_DATE,\n\t\t})\n\t\tawait this.serviceExecutor.delete(UserService, data)\n\t}\n\n\t_getGroupId(user: User, groupType: GroupType): Id {\n\t\tif (groupType === GroupType.User) {\n\t\t\treturn user.userGroup.group\n\t\t} else {\n\t\t\tlet membership = user.memberships.find((m) => m.groupType === groupType)\n\n\t\t\tif (!membership) {\n\t\t\t\tthrow new Error(\"could not find groupType \" + groupType + \" for user \" + user._id)\n\t\t\t}\n\n\t\t\treturn membership.group\n\t\t}\n\t}\n\n\tcreateUser(\n\t\tname: string,\n\t\tmailAddress: string,\n\t\tpassword: string,\n\t\tuserIndex: number,\n\t\toverallNbrOfUsersToCreate: number,\n\t\toperationId: OperationId,\n\t): Promise<void> {\n\t\tlet adminGroupIds = this.userFacade.getGroupIds(GroupType.Admin)\n\n\t\tif (adminGroupIds.length === 0) {\n\t\t\tadminGroupIds = this.userFacade.getGroupIds(GroupType.LocalAdmin)\n\t\t}\n\n\t\tconst adminGroupId = adminGroupIds[0]\n\n\t\tconst adminGroupKey = this.userFacade.getGroupKey(adminGroupId)\n\n\t\tlet customerGroupKey = this.userFacade.getGroupKey(this.userFacade.getGroupId(GroupType.Customer))\n\n\t\tlet userGroupKey = aes128RandomKey()\n\t\tlet userGroupInfoSessionKey = aes128RandomKey()\n\t\treturn this.rsa\n\t\t\t.generateKey()\n\t\t\t.then((keyPair) =>\n\t\t\t\tthis.groupManagement.generateInternalGroupData(keyPair, userGroupKey, userGroupInfoSessionKey, adminGroupId, adminGroupKey, customerGroupKey),\n\t\t\t)\n\t\t\t.then((userGroupData) => {\n\t\t\t\treturn this.operationProgressTracker.onProgress(operationId, ((userIndex + 0.8) / overallNbrOfUsersToCreate) * 100).then(() => {\n\t\t\t\t\tlet data = createUserAccountCreateData()\n\t\t\t\t\tdata.date = Const.CURRENT_DATE\n\t\t\t\t\tdata.userGroupData = userGroupData\n\t\t\t\t\tdata.userData = this.generateUserAccountData(\n\t\t\t\t\t\tuserGroupKey,\n\t\t\t\t\t\tuserGroupInfoSessionKey,\n\t\t\t\t\t\tcustomerGroupKey,\n\t\t\t\t\t\tmailAddress,\n\t\t\t\t\t\tpassword,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tthis.generateRecoveryCode(userGroupKey),\n\t\t\t\t\t)\n\t\t\t\t\treturn this.serviceExecutor.post(UserAccountService, data).then(() => {\n\t\t\t\t\t\treturn this.operationProgressTracker.onProgress(operationId, ((userIndex + 1) / overallNbrOfUsersToCreate) * 100)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t}\n\n\tgenerateUserAccountData(\n\t\tuserGroupKey: Aes128Key,\n\t\tuserGroupInfoSessionKey: Aes128Key,\n\t\tcustomerGroupKey: Aes128Key,\n\t\tmailAddress: string,\n\t\tpassword: string,\n\t\tuserName: string,\n\t\trecoverData: RecoverData,\n\t): UserAccountUserData {\n\t\tlet salt = generateRandomSalt()\n\t\tlet userPassphraseKey = generateKeyFromPassphrase(password, salt, KeyLength.b128)\n\t\tlet mailGroupKey = aes128RandomKey()\n\t\tlet contactGroupKey = aes128RandomKey()\n\t\tlet fileGroupKey = aes128RandomKey()\n\t\tlet clientKey = aes128RandomKey()\n\t\tlet mailboxSessionKey = aes128RandomKey()\n\t\tlet contactListSessionKey = aes128RandomKey()\n\t\tlet fileSystemSessionKey = aes128RandomKey()\n\t\tlet mailGroupInfoSessionKey = aes128RandomKey()\n\t\tlet contactGroupInfoSessionKey = aes128RandomKey()\n\t\tlet fileGroupInfoSessionKey = aes128RandomKey()\n\t\tlet tutanotaPropertiesSessionKey = aes128RandomKey()\n\t\tlet userEncEntropy = encryptBytes(userGroupKey, random.generateRandomData(32))\n\t\tlet userData = createUserAccountUserData()\n\t\tuserData.mailAddress = mailAddress\n\t\tuserData.encryptedName = encryptString(userGroupInfoSessionKey, userName)\n\t\tuserData.salt = salt\n\t\tuserData.verifier = createAuthVerifier(userPassphraseKey)\n\t\tuserData.userEncClientKey = encryptKey(userGroupKey, clientKey)\n\t\tuserData.pwEncUserGroupKey = encryptKey(userPassphraseKey, userGroupKey)\n\t\tuserData.userEncCustomerGroupKey = encryptKey(userGroupKey, customerGroupKey)\n\t\tuserData.userEncMailGroupKey = encryptKey(userGroupKey, mailGroupKey)\n\t\tuserData.userEncContactGroupKey = encryptKey(userGroupKey, contactGroupKey)\n\t\tuserData.userEncFileGroupKey = encryptKey(userGroupKey, fileGroupKey)\n\t\tuserData.userEncEntropy = userEncEntropy\n\t\tuserData.userEncTutanotaPropertiesSessionKey = encryptKey(userGroupKey, tutanotaPropertiesSessionKey)\n\t\tuserData.mailEncMailBoxSessionKey = encryptKey(mailGroupKey, mailboxSessionKey)\n\t\tuserData.contactEncContactListSessionKey = encryptKey(contactGroupKey, contactListSessionKey)\n\t\tuserData.fileEncFileSystemSessionKey = encryptKey(fileGroupKey, fileSystemSessionKey)\n\t\tuserData.customerEncMailGroupInfoSessionKey = encryptKey(customerGroupKey, mailGroupInfoSessionKey)\n\t\tuserData.customerEncContactGroupInfoSessionKey = encryptKey(customerGroupKey, contactGroupInfoSessionKey)\n\t\tuserData.customerEncFileGroupInfoSessionKey = encryptKey(customerGroupKey, fileGroupInfoSessionKey)\n\t\tuserData.userEncRecoverCode = recoverData.userEncRecoverCode\n\t\tuserData.recoverCodeEncUserGroupKey = recoverData.recoverCodeEncUserGroupKey\n\t\tuserData.recoverCodeVerifier = recoverData.recoveryCodeVerifier\n\t\treturn userData\n\t}\n\n\tgenerateContactFormUserAccountData(userGroupKey: Aes128Key, password: string): ContactFormUserData {\n\t\tlet salt = generateRandomSalt()\n\t\tlet userPassphraseKey = generateKeyFromPassphrase(password, salt, KeyLength.b128)\n\t\tlet mailGroupKey = aes128RandomKey()\n\t\tlet clientKey = aes128RandomKey()\n\t\tlet mailboxSessionKey = aes128RandomKey()\n\t\tlet mailGroupInfoSessionKey = aes128RandomKey()\n\t\tlet tutanotaPropertiesSessionKey = aes128RandomKey()\n\t\tlet userEncEntropy = encryptBytes(userGroupKey, random.generateRandomData(32))\n\t\tlet userData = createContactFormUserData()\n\t\tuserData.salt = salt\n\t\tuserData.verifier = createAuthVerifier(userPassphraseKey)\n\t\tuserData.userEncClientKey = encryptKey(userGroupKey, clientKey)\n\t\tuserData.pwEncUserGroupKey = encryptKey(userPassphraseKey, userGroupKey)\n\t\tuserData.userEncMailGroupKey = encryptKey(userGroupKey, mailGroupKey)\n\t\tuserData.userEncEntropy = userEncEntropy\n\t\tuserData.userEncTutanotaPropertiesSessionKey = encryptKey(userGroupKey, tutanotaPropertiesSessionKey)\n\t\tuserData.mailEncMailBoxSessionKey = encryptKey(mailGroupKey, mailboxSessionKey)\n\t\tuserData.ownerEncMailGroupInfoSessionKey = encryptKey(mailGroupKey, mailGroupInfoSessionKey)\n\t\treturn userData\n\t}\n\n\tgenerateRecoveryCode(userGroupKey: Aes128Key): RecoverData {\n\t\tconst recoveryCode = aes256RandomKey()\n\t\tconst userEncRecoverCode = encrypt256Key(userGroupKey, recoveryCode)\n\t\tconst recoverCodeEncUserGroupKey = aes256EncryptKey(recoveryCode, userGroupKey)\n\t\tconst recoveryCodeVerifier = createAuthVerifier(recoveryCode)\n\t\treturn {\n\t\t\tuserEncRecoverCode,\n\t\t\trecoverCodeEncUserGroupKey,\n\t\t\thexCode: uint8ArrayToHex(bitArrayToUint8Array(recoveryCode)),\n\t\t\trecoveryCodeVerifier,\n\t\t}\n\t}\n\n\tgetRecoverCode(password: string): Promise<string> {\n\t\tconst user = this.userFacade.getLoggedInUser()\n\t\tconst recoverCodeId = user.auth?.recoverCode\n\t\tif (recoverCodeId == null) {\n\t\t\treturn Promise.reject(new Error(\"Auth is missing\"))\n\t\t}\n\n\t\tconst key = generateKeyFromPassphrase(password, assertNotNull(user.salt), KeyLength.b128)\n\t\tconst extraHeaders = {\n\t\t\tauthVerifier: createAuthVerifierAsBase64Url(key),\n\t\t}\n\t\treturn this.entityClient.load(RecoverCodeTypeRef, recoverCodeId, undefined, extraHeaders).then((result) => {\n\t\t\treturn uint8ArrayToHex(bitArrayToUint8Array(decrypt256Key(this.userFacade.getUserGroupKey(), result.userEncRecoverCode)))\n\t\t})\n\t}\n\n\tcreateRecoveryCode(password: string): Promise<string> {\n\t\tconst user = this.userFacade.getUser()\n\n\t\tif (user == null || user.auth == null) {\n\t\t\tthrow new Error(\"Invalid state: no user or no user.auth\")\n\t\t}\n\n\t\tconst { userEncRecoverCode, recoverCodeEncUserGroupKey, hexCode, recoveryCodeVerifier } = this.generateRecoveryCode(this.userFacade.getUserGroupKey())\n\t\tconst recoverPasswordEntity = createRecoverCode()\n\t\trecoverPasswordEntity.userEncRecoverCode = userEncRecoverCode\n\t\trecoverPasswordEntity.recoverCodeEncUserGroupKey = recoverCodeEncUserGroupKey\n\t\trecoverPasswordEntity._ownerGroup = this.userFacade.getUserGroupId()\n\t\trecoverPasswordEntity.verifier = recoveryCodeVerifier\n\t\tconst pwKey = generateKeyFromPassphrase(password, neverNull(user.salt), KeyLength.b128)\n\t\tconst authVerifier = createAuthVerifierAsBase64Url(pwKey)\n\t\treturn this.entityClient\n\t\t\t.setup(null, recoverPasswordEntity, {\n\t\t\t\tauthVerifier,\n\t\t\t})\n\t\t\t.then(() => hexCode)\n\t}\n}\n","import type { InvoiceData, PaymentData, SpamRuleFieldType, SpamRuleType } from \"../../../common/TutanotaConstants.js\"\nimport { AccountType, BookingItemFeatureType, Const, CounterType, GroupType } from \"../../../common/TutanotaConstants.js\"\nimport type {\n\tAccountingInfo,\n\tCustomDomainReturn,\n\tCustomerServerProperties,\n\tEmailSenderListElement,\n\tPaymentDataServicePutReturn,\n} from \"../../../entities/sys/TypeRefs.js\"\nimport {\n\tAccountingInfoTypeRef,\n\tcreateBrandingDomainData,\n\tcreateBrandingDomainDeleteData,\n\tcreateCreateCustomerServerPropertiesData,\n\tcreateCustomDomainData,\n\tcreateEmailSenderListElement,\n\tcreateMembershipAddData,\n\tcreateMembershipRemoveData,\n\tcreatePaymentDataServicePutData,\n\tcreatePdfInvoiceServiceData,\n\tCustomerInfoTypeRef,\n\tCustomerServerPropertiesTypeRef,\n\tCustomerTypeRef,\n} from \"../../../entities/sys/TypeRefs.js\"\nimport { assertWorkerOrNode } from \"../../../common/Env.js\"\nimport type { Hex } from \"@tutao/tutanota-utils\"\nimport { assertNotNull, downcast, neverNull, noOp, ofClass, stringToUtf8Uint8Array, uint8ArrayToBase64, uint8ArrayToHex } from \"@tutao/tutanota-utils\"\nimport { getWhitelabelDomain } from \"../../../common/utils/Utils.js\"\nimport { CryptoFacade } from \"../../crypto/CryptoFacade.js\"\nimport {\n\tBrandingDomainService,\n\tCreateCustomerServerProperties,\n\tCustomDomainService,\n\tMembershipService,\n\tPaymentDataService,\n\tPdfInvoiceService,\n\tSystemKeysService,\n} from \"../../../entities/sys/Services.js\"\nimport type { ContactFormAccountReturn, InternalGroupData } from \"../../../entities/tutanota/TypeRefs.js\"\nimport { createContactFormAccountData, createCustomerAccountCreateData } from \"../../../entities/tutanota/TypeRefs.js\"\nimport type { UserManagementFacade } from \"./UserManagementFacade.js\"\nimport type { GroupManagementFacade } from \"./GroupManagementFacade.js\"\nimport { CounterFacade } from \"./CounterFacade.js\"\nimport type { Country } from \"../../../common/CountryList.js\"\nimport { getByAbbreviation } from \"../../../common/CountryList.js\"\nimport { LockedError } from \"../../../common/error/RestError.js\"\nimport type { RsaKeyPair } from \"@tutao/tutanota-crypto\"\nimport { aes128RandomKey, bitArrayToUint8Array, encryptKey, hexToPublicKey, sha256Hash, uint8ArrayToBitArray } from \"@tutao/tutanota-crypto\"\nimport type { RsaImplementation } from \"../../crypto/RsaImplementation.js\"\nimport { EntityClient } from \"../../../common/EntityClient.js\"\nimport { DataFile } from \"../../../common/DataFile.js\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { ContactFormAccountService, CustomerAccountService } from \"../../../entities/tutanota/Services.js\"\nimport { BookingFacade } from \"./BookingFacade.js\"\nimport { UserFacade } from \"../UserFacade.js\"\nimport { PaymentInterval } from \"../../../../subscription/PriceUtils.js\"\nimport { ExposedOperationProgressTracker, OperationId } from \"../../../main/OperationProgressTracker.js\"\nimport { formatNameAndAddress } from \"../../../common/utils/CommonFormatter.js\"\n\nassertWorkerOrNode()\n\ninterface ContactFormUserGroupData {\n\tuserGroupKey: Aes128Key\n\tuserGroupData: InternalGroupData\n}\n\nexport class CustomerFacade {\n\tprivate contactFormUserGroupData: Promise<ContactFormUserGroupData> | null\n\n\tconstructor(\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly groupManagement: GroupManagementFacade,\n\t\tprivate readonly userManagement: UserManagementFacade,\n\t\tprivate readonly counters: CounterFacade,\n\t\tprivate readonly rsa: RsaImplementation,\n\t\tprivate readonly entityClient: EntityClient,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly bookingFacade: BookingFacade,\n\t\tprivate readonly cryptoFacade: CryptoFacade,\n\t\tprivate readonly operationProgressTracker: ExposedOperationProgressTracker,\n\t) {\n\t\tthis.contactFormUserGroupData = null\n\t}\n\n\tasync getDomainValidationRecord(domainName: string): Promise<string> {\n\t\tconst customer = this.getCustomerId()\n\t\tconst baseString = domainName.trim().toLowerCase() + customer\n\t\tconst hash = sha256Hash(stringToUtf8Uint8Array(baseString)).slice(0, 16)\n\t\treturn \"t-verify=\" + uint8ArrayToHex(hash)\n\t}\n\n\taddDomain(domainName: string): Promise<CustomDomainReturn> {\n\t\tconst data = createCustomDomainData({\n\t\t\tdomain: domainName.trim().toLowerCase(),\n\t\t})\n\t\treturn this.serviceExecutor.post(CustomDomainService, data)\n\t}\n\n\tasync removeDomain(domainName: string): Promise<void> {\n\t\tconst data = createCustomDomainData({\n\t\t\tdomain: domainName.trim().toLowerCase(),\n\t\t})\n\t\tawait this.serviceExecutor.delete(CustomDomainService, data)\n\t}\n\n\tasync setCatchAllGroup(domainName: string, mailGroupId: Id | null): Promise<void> {\n\t\tconst data = createCustomDomainData({\n\t\t\tdomain: domainName.trim().toLowerCase(),\n\t\t\tcatchAllMailGroup: mailGroupId,\n\t\t})\n\t\tawait this.serviceExecutor.put(CustomDomainService, data)\n\t}\n\n\tasync orderWhitelabelCertificate(domainName: string): Promise<void> {\n\t\tconst customerId = this.getCustomerId()\n\t\tconst customer = await this.entityClient.load(CustomerTypeRef, customerId)\n\t\tconst customerInfo = await this.entityClient.load(CustomerInfoTypeRef, customer.customerInfo)\n\t\tlet existingBrandingDomain = getWhitelabelDomain(customerInfo, domainName)\n\t\tconst keyData = await this.serviceExecutor.get(SystemKeysService, null)\n\t\tlet systemAdminPubKey = hexToPublicKey(uint8ArrayToHex(keyData.systemAdminPubKey))\n\t\tlet sessionKey = aes128RandomKey()\n\t\tconst systemAdminPubEncAccountingInfoSessionKey = await this.rsa.encrypt(systemAdminPubKey, bitArrayToUint8Array(sessionKey))\n\n\t\tconst data = createBrandingDomainData({\n\t\t\tdomain: domainName,\n\t\t\tsystemAdminPubEncSessionKey: systemAdminPubEncAccountingInfoSessionKey,\n\t\t})\n\t\tif (existingBrandingDomain) {\n\t\t\tawait this.serviceExecutor.put(BrandingDomainService, data)\n\t\t} else {\n\t\t\tawait this.serviceExecutor.post(BrandingDomainService, data)\n\t\t}\n\t}\n\n\tprivate getCustomerId() {\n\t\treturn assertNotNull(this.userFacade.getLoggedInUser().customer)\n\t}\n\n\tasync deleteCertificate(domainName: string): Promise<void> {\n\t\tconst data = createBrandingDomainDeleteData({\n\t\t\tdomain: domainName,\n\t\t})\n\t\tawait this.serviceExecutor.delete(BrandingDomainService, data)\n\t}\n\n\t/**\n\t * Reads the used storage of a customer in bytes.\n\t * @return The amount of used storage in byte.\n\t */\n\tasync readUsedCustomerStorage(customerId: Id): Promise<number> {\n\t\tconst customerCounters = await this.counters.readAllCustomerCounterValues(CounterType.UserStorageLegacy, customerId)\n\t\treturn customerCounters.reduce((sum, counterValue) => sum + Number(counterValue.value), 0)\n\t}\n\n\t/**\n\t * Reads the available storage capacity of a customer in bytes.\n\t * @return The amount of available storage capacity in byte.\n\t */\n\treadAvailableCustomerStorage(customerId: Id): Promise<number> {\n\t\treturn this.entityClient.load(CustomerTypeRef, customerId).then((customer) => {\n\t\t\treturn this.entityClient.load(CustomerInfoTypeRef, customer.customerInfo).then((customerInfo) => {\n\t\t\t\tlet includedStorage = Number(customerInfo.includedStorageCapacity)\n\t\t\t\tlet promotionStorage = Number(customerInfo.promotionStorageCapacity)\n\t\t\t\tlet availableStorage = Math.max(includedStorage, promotionStorage)\n\t\t\t\tlet bookedStorage = 0\n\n\t\t\t\tif (customer.type === AccountType.PREMIUM) {\n\t\t\t\t\treturn this.bookingFacade.getCurrentPrice().then((price) => {\n\t\t\t\t\t\tlet currentStorageItem = this.bookingFacade.getPriceItem(price.currentPriceNextPeriod, BookingItemFeatureType.Storage)\n\n\t\t\t\t\t\tif (currentStorageItem != null) {\n\t\t\t\t\t\t\tbookedStorage = Number(currentStorageItem.count)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tavailableStorage = Math.max(bookedStorage, availableStorage)\n\t\t\t\t\t\treturn availableStorage * Const.MEMORY_GB_FACTOR\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\treturn availableStorage * Const.MEMORY_GB_FACTOR\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n\n\tasync loadCustomerServerProperties(): Promise<CustomerServerProperties> {\n\t\tconst customer = await this.entityClient.load(CustomerTypeRef, this.getCustomerId())\n\t\tlet cspId\n\t\tif (customer.serverProperties) {\n\t\t\tcspId = customer.serverProperties\n\t\t} else {\n\t\t\t// create properties\n\t\t\tconst sessionKey = aes128RandomKey()\n\t\t\tconst adminGroupKey = this.userFacade.getGroupKey(this.userFacade.getGroupId(GroupType.Admin))\n\n\t\t\tconst groupEncSessionKey = encryptKey(adminGroupKey, sessionKey)\n\t\t\tconst data = createCreateCustomerServerPropertiesData({\n\t\t\t\tadminGroupEncSessionKey: groupEncSessionKey,\n\t\t\t})\n\t\t\tconst returnData = await this.serviceExecutor.post(CreateCustomerServerProperties, data)\n\t\t\tcspId = returnData.id\n\t\t}\n\t\treturn this.entityClient.load(CustomerServerPropertiesTypeRef, cspId)\n\t}\n\n\taddSpamRule(field: SpamRuleFieldType, type: SpamRuleType, value: string): Promise<void> {\n\t\treturn this.loadCustomerServerProperties().then((props) => {\n\t\t\tvalue = value.toLowerCase().trim()\n\t\t\tlet newListEntry = createEmailSenderListElement({\n\t\t\t\tvalue,\n\t\t\t\thashedValue: uint8ArrayToBase64(sha256Hash(stringToUtf8Uint8Array(value))),\n\t\t\t\ttype,\n\t\t\t\tfield,\n\t\t\t})\n\t\t\tprops.emailSenderList.push(newListEntry)\n\t\t\treturn this.entityClient.update(props).catch(ofClass(LockedError, noOp))\n\t\t})\n\t}\n\n\teditSpamRule(spamRule: EmailSenderListElement): Promise<void> {\n\t\treturn this.loadCustomerServerProperties().then((props) => {\n\t\t\tspamRule.value = spamRule.value.toLowerCase().trim()\n\t\t\tconst index = props.emailSenderList.findIndex((item) => spamRule._id === item._id)\n\n\t\t\tif (index === -1) {\n\t\t\t\tthrow new Error(\"spam rule does not exist \" + JSON.stringify(spamRule))\n\t\t\t}\n\n\t\t\tprops.emailSenderList[index] = spamRule\n\t\t\treturn this.entityClient.update(props).catch(ofClass(LockedError, noOp))\n\t\t})\n\t}\n\n\tasync generateSignupKeys(operationId: OperationId): Promise<[RsaKeyPair, RsaKeyPair, RsaKeyPair]> {\n\t\tconst key1 = await this.rsa.generateKey()\n\t\tawait this.operationProgressTracker.onProgress(operationId, 33)\n\t\tconst key2 = await this.rsa.generateKey()\n\t\tawait this.operationProgressTracker.onProgress(operationId, 66)\n\t\tconst key3 = await this.rsa.generateKey()\n\t\tawait this.operationProgressTracker.onProgress(operationId, 100)\n\t\treturn [key1, key2, key3]\n\t}\n\n\tasync signup(\n\t\tkeyPairs: [RsaKeyPair, RsaKeyPair, RsaKeyPair],\n\t\taccountType: AccountType,\n\t\tauthToken: string,\n\t\tmailAddress: string,\n\t\tpassword: string,\n\t\tregistrationCode: string,\n\t\tcurrentLanguage: string,\n\t): Promise<Hex> {\n\t\tconst keyData = await this.serviceExecutor.get(SystemKeysService, null)\n\t\tconst systemAdminPubKey = hexToPublicKey(uint8ArrayToHex(keyData.systemAdminPubKey))\n\t\tconst userGroupKey = aes128RandomKey()\n\t\tconst adminGroupKey = aes128RandomKey()\n\t\tconst customerGroupKey = aes128RandomKey()\n\t\tconst userGroupInfoSessionKey = aes128RandomKey()\n\t\tconst adminGroupInfoSessionKey = aes128RandomKey()\n\t\tconst customerGroupInfoSessionKey = aes128RandomKey()\n\t\tconst accountingInfoSessionKey = aes128RandomKey()\n\t\tconst customerServerPropertiesSessionKey = aes128RandomKey()\n\t\tconst systemAdminPubEncAccountingInfoSessionKey = await this.rsa.encrypt(systemAdminPubKey, bitArrayToUint8Array(accountingInfoSessionKey))\n\t\tconst userGroupData = this.groupManagement.generateInternalGroupData(\n\t\t\tkeyPairs[0],\n\t\t\tuserGroupKey,\n\t\t\tuserGroupInfoSessionKey,\n\t\t\tnull,\n\t\t\tadminGroupKey,\n\t\t\tcustomerGroupKey,\n\t\t)\n\n\t\tconst adminGroupData = this.groupManagement.generateInternalGroupData(\n\t\t\tkeyPairs[1],\n\t\t\tadminGroupKey,\n\t\t\tadminGroupInfoSessionKey,\n\t\t\tnull,\n\t\t\tadminGroupKey,\n\t\t\tcustomerGroupKey,\n\t\t)\n\n\t\tconst customerGroupData = this.groupManagement.generateInternalGroupData(\n\t\t\tkeyPairs[2],\n\t\t\tcustomerGroupKey,\n\t\t\tcustomerGroupInfoSessionKey,\n\t\t\tnull,\n\t\t\tadminGroupKey,\n\t\t\tcustomerGroupKey,\n\t\t)\n\n\t\tconst recoverData = this.userManagement.generateRecoveryCode(userGroupKey)\n\n\t\tconst data = createCustomerAccountCreateData({\n\t\t\tauthToken,\n\t\t\tdate: Const.CURRENT_DATE,\n\t\t\tlang: currentLanguage,\n\t\t\tcode: registrationCode,\n\t\t\tuserData: this.userManagement.generateUserAccountData(\n\t\t\t\tuserGroupKey,\n\t\t\t\tuserGroupInfoSessionKey,\n\t\t\t\tcustomerGroupKey,\n\t\t\t\tmailAddress,\n\t\t\t\tpassword,\n\t\t\t\t\"\",\n\t\t\t\trecoverData,\n\t\t\t),\n\t\t\tuserEncAdminGroupKey: encryptKey(userGroupKey, adminGroupKey),\n\t\t\tuserGroupData,\n\t\t\tadminGroupData,\n\t\t\tcustomerGroupData,\n\t\t\tadminEncAccountingInfoSessionKey: encryptKey(adminGroupKey, accountingInfoSessionKey),\n\t\t\tsystemAdminPubEncAccountingInfoSessionKey,\n\t\t\tadminEncCustomerServerPropertiesSessionKey: encryptKey(adminGroupKey, customerServerPropertiesSessionKey),\n\t\t})\n\t\tawait this.serviceExecutor.post(CustomerAccountService, data)\n\t\treturn recoverData.hexCode\n\t}\n\n\tcreateContactFormUserGroupData(): Promise<void> {\n\t\tlet userGroupKey = aes128RandomKey()\n\t\tlet userGroupInfoSessionKey = aes128RandomKey()\n\t\tthis.contactFormUserGroupData = this.rsa\n\t\t\t.generateKey()\n\t\t\t.then((keyPair) => this.groupManagement.generateInternalGroupData(keyPair, userGroupKey, userGroupInfoSessionKey, null, userGroupKey, userGroupKey))\n\t\t\t.then((userGroupData) => {\n\t\t\t\treturn {\n\t\t\t\t\tuserGroupKey,\n\t\t\t\t\tuserGroupData,\n\t\t\t\t}\n\t\t\t})\n\t\treturn Promise.resolve()\n\t}\n\n\tprivate async getContactFormUserGroupData(): Promise<ContactFormUserGroupData> {\n\t\tif (this.contactFormUserGroupData) {\n\t\t\treturn this.contactFormUserGroupData\n\t\t} else {\n\t\t\tawait this.createContactFormUserGroupData()\n\t\t\treturn downcast(this.contactFormUserGroupData)\n\t\t}\n\t}\n\n\t/**\n\t * @pre CustomerFacade#createContactFormUserGroupData has been invoked before\n\t */\n\tasync createContactFormUser(password: string, contactFormId: IdTuple, operationId: OperationId): Promise<ContactFormAccountReturn> {\n\t\tconst contactFormUserGroupData = await this.getContactFormUserGroupData()\n\t\tlet { userGroupKey, userGroupData } = contactFormUserGroupData\n\t\tawait this.operationProgressTracker.onProgress(operationId, 35)\n\t\tlet data = createContactFormAccountData()\n\t\tdata.userData = this.userManagement.generateContactFormUserAccountData(userGroupKey, password)\n\t\tawait this.operationProgressTracker.onProgress(operationId, 95)\n\t\tdata.userGroupData = userGroupData\n\t\tdata.contactForm = contactFormId\n\t\tconst result = this.serviceExecutor.post(ContactFormAccountService, data)\n\t\tthis.contactFormUserGroupData = null\n\t\treturn result\n\t}\n\n\tasync switchFreeToPremiumGroup(): Promise<void> {\n\t\ttry {\n\t\t\tconst keyData = await this.serviceExecutor.get(SystemKeysService, null)\n\t\t\tconst membershipAddData = createMembershipAddData({\n\t\t\t\tuser: this.userFacade.getLoggedInUser()._id,\n\t\t\t\tgroup: neverNull(keyData.premiumGroup),\n\t\t\t\tsymEncGKey: encryptKey(this.userFacade.getUserGroupKey(), uint8ArrayToBitArray(keyData.premiumGroupKey)),\n\t\t\t})\n\t\t\tawait this.serviceExecutor.post(MembershipService, membershipAddData)\n\t\t\tconst membershipRemoveData = createMembershipRemoveData({\n\t\t\t\tuser: this.userFacade.getLoggedInUser()._id,\n\t\t\t\tgroup: neverNull(keyData.freeGroup),\n\t\t\t})\n\t\t\tawait this.serviceExecutor.delete(MembershipService, membershipRemoveData)\n\t\t} catch (e) {\n\t\t\te.message = e.message + \" error switching free to premium group\"\n\t\t\tconsole.log(e)\n\t\t\tthrow e\n\t\t}\n\t}\n\n\tasync switchPremiumToFreeGroup(): Promise<void> {\n\t\ttry {\n\t\t\tconst keyData = await this.serviceExecutor.get(SystemKeysService, null)\n\t\t\tconst membershipAddData = createMembershipAddData({\n\t\t\t\tuser: this.userFacade.getLoggedInUser()._id,\n\t\t\t\tgroup: neverNull(keyData.freeGroup),\n\t\t\t\tsymEncGKey: encryptKey(this.userFacade.getUserGroupKey(), uint8ArrayToBitArray(keyData.freeGroupKey)),\n\t\t\t})\n\t\t\tawait this.serviceExecutor.post(MembershipService, membershipAddData)\n\t\t\tconst membershipRemoveData = createMembershipRemoveData({\n\t\t\t\tuser: this.userFacade.getLoggedInUser()._id,\n\t\t\t\tgroup: neverNull(keyData.premiumGroup),\n\t\t\t})\n\t\t\tawait this.serviceExecutor.delete(MembershipService, membershipRemoveData)\n\t\t} catch (e) {\n\t\t\te.message = e.message + \" error switching premium to free group\"\n\t\t\tconsole.log(e)\n\t\t\tthrow e\n\t\t}\n\t}\n\n\tasync updatePaymentData(\n\t\tpaymentInterval: PaymentInterval,\n\t\tinvoiceData: InvoiceData,\n\t\tpaymentData: PaymentData | null,\n\t\tconfirmedInvoiceCountry: Country | null,\n\t): Promise<PaymentDataServicePutReturn> {\n\t\tlet customer = await this.entityClient.load(CustomerTypeRef, assertNotNull(this.userFacade.getLoggedInUser().customer))\n\t\tlet customerInfo = await this.entityClient.load(CustomerInfoTypeRef, customer.customerInfo)\n\t\tlet accountingInfo = await this.entityClient.load(AccountingInfoTypeRef, customerInfo.accountingInfo)\n\t\tlet accountingInfoSessionKey = await this.cryptoFacade.resolveSessionKeyForInstance(accountingInfo)\n\t\tconst service = createPaymentDataServicePutData()\n\t\tservice.paymentInterval = paymentInterval.toString()\n\t\tservice.invoiceName = \"\"\n\t\tservice.invoiceAddress = invoiceData.invoiceAddress\n\t\tservice.invoiceCountry = invoiceData.country ? invoiceData.country.a : \"\"\n\t\tservice.invoiceVatIdNo = invoiceData.vatNumber ? invoiceData.vatNumber : \"\"\n\t\tservice.paymentMethod = paymentData ? paymentData.paymentMethod : accountingInfo.paymentMethod ? accountingInfo.paymentMethod : \"\"\n\t\tservice.paymentMethodInfo = null\n\t\tservice.paymentToken = null\n\t\tif (paymentData && paymentData.creditCardData) {\n\t\t\tservice.creditCard = paymentData.creditCardData\n\t\t}\n\t\tservice.confirmedCountry = confirmedInvoiceCountry ? confirmedInvoiceCountry.a : null\n\t\treturn this.serviceExecutor.put(PaymentDataService, service, { sessionKey: accountingInfoSessionKey ?? undefined })\n\t}\n\n\t/**\n\t * Convenience function to change the payment interval for the current subscription\n\t * @param accountingInfo accounting info\n\t * @param newPaymentInterval new payment interval\n\t */\n\tasync changePaymentInterval(accountingInfo: AccountingInfo, newPaymentInterval: PaymentInterval): Promise<PaymentDataServicePutReturn> {\n\t\tconst invoiceCountry = neverNull(getByAbbreviation(neverNull(accountingInfo.invoiceCountry)))\n\n\t\treturn this.updatePaymentData(\n\t\t\tnewPaymentInterval,\n\t\t\t{\n\t\t\t\tinvoiceAddress: formatNameAndAddress(accountingInfo.invoiceName, accountingInfo.invoiceAddress),\n\t\t\t\tcountry: invoiceCountry,\n\t\t\t\tvatNumber: accountingInfo.invoiceVatIdNo,\n\t\t\t},\n\t\t\tnull,\n\t\t\tinvoiceCountry,\n\t\t)\n\t}\n\n\tasync downloadInvoice(invoiceNumber: string): Promise<DataFile> {\n\t\tconst data = createPdfInvoiceServiceData({\n\t\t\tinvoiceNumber,\n\t\t})\n\t\treturn this.serviceExecutor.get(PdfInvoiceService, data).then((returnData) => {\n\t\t\treturn {\n\t\t\t\t_type: \"DataFile\",\n\t\t\t\tname: String(invoiceNumber) + \".pdf\",\n\t\t\t\tmimeType: \"application/pdf\",\n\t\t\t\tdata: returnData.data,\n\t\t\t\tsize: returnData.data.byteLength,\n\t\t\t\tid: undefined,\n\t\t\t}\n\t\t})\n\t}\n\n\tasync loadAccountingInfo(): Promise<AccountingInfo> {\n\t\tconst customer = await this.entityClient.load(CustomerTypeRef, assertNotNull(this.userFacade.getUser()?.customer))\n\t\tconst customerInfo = await this.entityClient.load(CustomerInfoTypeRef, customer.customerInfo)\n\t\treturn this.entityClient.load(AccountingInfoTypeRef, customerInfo.accountingInfo)\n\t}\n}\n","import { addParamsToUrl, isSuspensionResponse, RestClient } from \"../../rest/RestClient.js\"\nimport { CryptoFacade, encryptBytes } from \"../../crypto/CryptoFacade.js\"\nimport { clear, concat, neverNull, promiseMap, splitUint8ArrayInChunks, uint8ArrayToBase64, uint8ArrayToString } from \"@tutao/tutanota-utils\"\nimport { ArchiveDataType, MAX_BLOB_SIZE_BYTES } from \"../../../common/TutanotaConstants.js\"\n\nimport { HttpMethod, MediaType, resolveTypeReference } from \"../../../common/EntityFunctions.js\"\nimport { assertWorkerOrNode, isApp, isDesktop } from \"../../../common/Env.js\"\nimport type { SuspensionHandler } from \"../../SuspensionHandler.js\"\nimport { BlobService } from \"../../../entities/storage/Services.js\"\nimport { aes128Decrypt, sha256Hash } from \"@tutao/tutanota-crypto\"\nimport type { FileUri, NativeFileApp } from \"../../../../native/common/FileApp.js\"\nimport type { AesApp } from \"../../../../native/worker/AesApp.js\"\nimport { InstanceMapper } from \"../../crypto/InstanceMapper.js\"\nimport { Aes128Key } from \"@tutao/tutanota-crypto/dist/encryption/Aes.js\"\nimport { Blob, BlobReferenceTokenWrapper, createBlobReferenceTokenWrapper } from \"../../../entities/sys/TypeRefs.js\"\nimport { FileReference } from \"../../../common/utils/FileUtils.js\"\nimport { handleRestError } from \"../../../common/error/RestError.js\"\nimport { ProgrammingError } from \"../../../common/error/ProgrammingError.js\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { BlobGetInTypeRef, BlobPostOut, BlobPostOutTypeRef, BlobServerAccessInfo, createBlobGetIn } from \"../../../entities/storage/TypeRefs.js\"\nimport { AuthDataProvider } from \"../UserFacade.js\"\nimport { doBlobRequestWithRetry, tryServers } from \"../../rest/EntityRestClient.js\"\nimport { BlobAccessTokenFacade, BlobReferencingInstance } from \"../BlobAccessTokenFacade.js\"\n\nassertWorkerOrNode()\nexport const BLOB_SERVICE_REST_PATH = `/rest/${BlobService.app}/${BlobService.name.toLowerCase()}`\n\n/**\n * The BlobFacade uploads and downloads blobs to/from the blob store.\n *\n * It requests tokens from the BlobAccessTokenService and download and uploads the blobs to/from the BlobService.\n *\n * In case of upload it is necessary to make a request to the BlobReferenceService or use the referenceTokens returned by the BlobService PUT in some other service call.\n * Otherwise, the blobs will automatically be deleted after some time. It is not allowed to reference blobs manually in some instance.\n */\nexport class BlobFacade {\n\tconstructor(\n\t\tprivate readonly authDataProvider: AuthDataProvider,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly restClient: RestClient,\n\t\tprivate readonly suspensionHandler: SuspensionHandler,\n\t\tprivate readonly fileApp: NativeFileApp,\n\t\tprivate readonly aesApp: AesApp,\n\t\tprivate readonly instanceMapper: InstanceMapper,\n\t\tprivate readonly cryptoFacade: CryptoFacade,\n\t\tprivate readonly blobAccessTokenFacade: BlobAccessTokenFacade,\n\t) {}\n\n\t/**\n\t * Encrypts and uploads binary data to the blob store. The binary data is split into multiple blobs in case it\n\t * is too big.\n\t *\n\t * @returns blobReferenceToken that must be used to reference a blobs from an instance. Only to be used once.\n\t */\n\tasync encryptAndUpload(\n\t\tarchiveDataType: ArchiveDataType,\n\t\tblobData: Uint8Array,\n\t\townerGroupId: Id,\n\t\tsessionKey: Aes128Key,\n\t): Promise<BlobReferenceTokenWrapper[]> {\n\t\tconst chunks = splitUint8ArrayInChunks(MAX_BLOB_SIZE_BYTES, blobData)\n\t\tconst doBlobRequest = async () => {\n\t\t\tconst blobServerAccessInfo = await this.blobAccessTokenFacade.requestWriteToken(archiveDataType, ownerGroupId)\n\t\t\treturn promiseMap(chunks, async (chunk) => await this.encryptAndUploadChunk(chunk, blobServerAccessInfo, sessionKey))\n\t\t}\n\t\tconst doEvictToken = () => this.blobAccessTokenFacade.evictWriteToken(archiveDataType, ownerGroupId)\n\n\t\treturn doBlobRequestWithRetry(doBlobRequest, doEvictToken)\n\t}\n\n\t/**\n\t * Encrypts and uploads binary data stored as a file to the blob store. The binary data is split into multiple blobs in case it\n\t * is too big.\n\t *\n\t * @returns blobReferenceToken that must be used to reference a blobs from an instance. Only to be used once.\n\t */\n\tasync encryptAndUploadNative(\n\t\tarchiveDataType: ArchiveDataType,\n\t\tfileUri: FileUri,\n\t\townerGroupId: Id,\n\t\tsessionKey: Aes128Key,\n\t): Promise<BlobReferenceTokenWrapper[]> {\n\t\tif (!isApp() && !isDesktop()) {\n\t\t\tthrow new ProgrammingError(\"Environment is not app or Desktop!\")\n\t\t}\n\t\tconst chunkUris = await this.fileApp.splitFile(fileUri, MAX_BLOB_SIZE_BYTES)\n\n\t\tconst doEvictToken = () => this.blobAccessTokenFacade.evictWriteToken(archiveDataType, ownerGroupId)\n\t\tconst doBlobRequest = async () => {\n\t\t\tconst blobServerAccessInfo = await this.blobAccessTokenFacade.requestWriteToken(archiveDataType, ownerGroupId)\n\t\t\treturn promiseMap(chunkUris, async (chunkUri) => {\n\t\t\t\treturn this.encryptAndUploadNativeChunk(chunkUri, blobServerAccessInfo, sessionKey)\n\t\t\t})\n\t\t}\n\t\treturn doBlobRequestWithRetry(doBlobRequest, doEvictToken)\n\t}\n\n\t/**\n\t * Downloads multiple blobs, decrypts and joins them to unencrypted binary data.\n\t *\n\t * @param archiveDataType\n\t * @param referencingInstance that directly references the blobs\n\t * @returns Uint8Array unencrypted binary data\n\t */\n\tasync downloadAndDecrypt(archiveDataType: ArchiveDataType, referencingInstance: BlobReferencingInstance): Promise<Uint8Array> {\n\t\tconst sessionKey = neverNull(await this.cryptoFacade.resolveSessionKeyForInstance(referencingInstance.entity))\n\t\tconst doBlobRequest = async () => {\n\t\t\tconst blobServerAccessInfo = await this.blobAccessTokenFacade.requestReadTokenBlobs(archiveDataType, referencingInstance)\n\t\t\treturn promiseMap(referencingInstance.blobs, (blob) => this.downloadAndDecryptChunk(blob, blobServerAccessInfo, sessionKey))\n\t\t}\n\t\tconst doEvictToken = () => this.blobAccessTokenFacade.evictReadBlobsToken(referencingInstance)\n\n\t\tconst blobData = await doBlobRequestWithRetry(doBlobRequest, doEvictToken)\n\t\treturn concat(...blobData)\n\t}\n\n\t/**\n\t * Downloads multiple blobs, decrypts and joins them to unencrypted binary data which will be stored as a file on the\n\t * device.\n\t *\n\t * @param archiveDataType\n\t * @param referencingInstance that directly references the blobs\n\t * @param fileName is written to the returned FileReference\n\t * @param mimeType is written to the returned FileReference\n\t * @returns FileReference to the unencrypted binary data\n\t */\n\tasync downloadAndDecryptNative(\n\t\tarchiveDataType: ArchiveDataType,\n\t\treferencingInstance: BlobReferencingInstance,\n\t\tfileName: string,\n\t\tmimeType: string,\n\t): Promise<FileReference> {\n\t\tif (!isApp() && !isDesktop()) {\n\t\t\tthrow new ProgrammingError(\"Environment is not app or Desktop!\")\n\t\t}\n\t\tconst sessionKey = neverNull(await this.cryptoFacade.resolveSessionKeyForInstance(referencingInstance.entity))\n\t\tconst decryptedChunkFileUris: FileUri[] = []\n\t\tconst doBlobRequest = async () => {\n\t\t\tclear(decryptedChunkFileUris) // ensure that the decrypted file uris are emtpy in case we retry because of NotAuthorized error\n\t\t\tconst blobServerAccessInfo = await this.blobAccessTokenFacade.requestReadTokenBlobs(archiveDataType, referencingInstance)\n\t\t\treturn promiseMap(referencingInstance.blobs, async (blob) => {\n\t\t\t\tdecryptedChunkFileUris.push(await this.downloadAndDecryptChunkNative(blob, blobServerAccessInfo, sessionKey))\n\t\t\t}).catch(async (e: Error) => {\n\t\t\t\t// cleanup every temporary file in the native part in case an error occured when downloading chun\n\t\t\t\tfor (const decryptedChunkFileUri of decryptedChunkFileUris) {\n\t\t\t\t\tawait this.fileApp.deleteFile(decryptedChunkFileUri)\n\t\t\t\t}\n\t\t\t\tthrow e\n\t\t\t})\n\t\t}\n\t\tconst doEvictToken = () => this.blobAccessTokenFacade.evictReadBlobsToken(referencingInstance)\n\n\t\tawait doBlobRequestWithRetry(doBlobRequest, doEvictToken)\n\n\t\t// now decryptedChunkFileUris has the correct order of downloaded blobs, and we need to tell native to join them\n\t\t// check if output already exists and return cached?\n\t\ttry {\n\t\t\tconst decryptedFileUri = await this.fileApp.joinFiles(fileName, decryptedChunkFileUris)\n\t\t\tconst size = await this.fileApp.getSize(decryptedFileUri)\n\t\t\treturn {\n\t\t\t\t_type: \"FileReference\",\n\t\t\t\tname: fileName,\n\t\t\t\tmimeType,\n\t\t\t\tsize,\n\t\t\t\tlocation: decryptedFileUri,\n\t\t\t}\n\t\t} finally {\n\t\t\tfor (const tmpBlobFile of decryptedChunkFileUris) {\n\t\t\t\tawait this.fileApp.deleteFile(tmpBlobFile)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async encryptAndUploadChunk(\n\t\tchunk: Uint8Array,\n\t\tblobServerAccessInfo: BlobServerAccessInfo,\n\t\tsessionKey: Aes128Key,\n\t): Promise<BlobReferenceTokenWrapper> {\n\t\tconst encryptedData = encryptBytes(sessionKey, chunk)\n\t\tconst blobHash = uint8ArrayToBase64(sha256Hash(encryptedData).slice(0, 6))\n\t\tconst queryParams = await this.blobAccessTokenFacade.createQueryParams(blobServerAccessInfo, { blobHash }, BlobGetInTypeRef)\n\n\t\treturn tryServers(\n\t\t\tblobServerAccessInfo.servers,\n\t\t\tasync (serverUrl) => {\n\t\t\t\tconst response = await this.restClient.request(BLOB_SERVICE_REST_PATH, HttpMethod.POST, {\n\t\t\t\t\tqueryParams: queryParams,\n\t\t\t\t\tbody: encryptedData,\n\t\t\t\t\tresponseType: MediaType.Json,\n\t\t\t\t\tbaseUrl: serverUrl,\n\t\t\t\t})\n\t\t\t\treturn await this.parseBlobPostOutResponse(response)\n\t\t\t},\n\t\t\t`can't upload to server`,\n\t\t)\n\t}\n\n\tprivate async encryptAndUploadNativeChunk(\n\t\tfileUri: FileUri,\n\t\tblobServerAccessInfo: BlobServerAccessInfo,\n\t\tsessionKey: Aes128Key,\n\t): Promise<BlobReferenceTokenWrapper> {\n\t\tconst encryptedFileInfo = await this.aesApp.aesEncryptFile(sessionKey, fileUri)\n\t\tconst encryptedChunkUri = encryptedFileInfo.uri\n\t\tconst blobHash = await this.fileApp.hashFile(encryptedChunkUri)\n\n\t\treturn tryServers(\n\t\t\tblobServerAccessInfo.servers,\n\t\t\tasync (serverUrl) => {\n\t\t\t\treturn await this.uploadNative(encryptedChunkUri, blobServerAccessInfo, serverUrl, blobHash)\n\t\t\t},\n\t\t\t`can't upload to server from native`,\n\t\t)\n\t}\n\n\tprivate async uploadNative(\n\t\tlocation: string,\n\t\tblobServerAccessInfo: BlobServerAccessInfo,\n\t\tserverUrl: string,\n\t\tblobHash: string,\n\t): Promise<BlobReferenceTokenWrapper> {\n\t\tif (this.suspensionHandler.isSuspended()) {\n\t\t\treturn this.suspensionHandler.deferRequest(() => this.uploadNative(location, blobServerAccessInfo, serverUrl, blobHash))\n\t\t}\n\t\tconst queryParams = await this.blobAccessTokenFacade.createQueryParams(blobServerAccessInfo, { blobHash }, BlobGetInTypeRef)\n\t\tconst serviceUrl = new URL(BLOB_SERVICE_REST_PATH, serverUrl)\n\t\tconst fullUrl = addParamsToUrl(serviceUrl, queryParams)\n\t\tconst { suspensionTime, responseBody, statusCode, errorId, precondition } = await this.fileApp.upload(location, fullUrl.toString(), HttpMethod.POST, {}) // blobReferenceToken in the response body\n\n\t\tif (statusCode === 201 && responseBody != null) {\n\t\t\treturn this.parseBlobPostOutResponse(uint8ArrayToString(\"utf-8\", responseBody))\n\t\t} else if (responseBody == null) {\n\t\t\tthrow new Error(\"no response body\")\n\t\t} else if (isSuspensionResponse(statusCode, suspensionTime)) {\n\t\t\tthis.suspensionHandler.activateSuspensionIfInactive(Number(suspensionTime))\n\t\t\treturn this.suspensionHandler.deferRequest(() => this.uploadNative(location, blobServerAccessInfo, serverUrl, blobHash))\n\t\t} else {\n\t\t\tthrow handleRestError(statusCode, ` | ${HttpMethod.POST} ${fullUrl.toString()} failed to natively upload blob`, errorId, precondition)\n\t\t}\n\t}\n\n\tprivate async parseBlobPostOutResponse(jsonData: string): Promise<BlobReferenceTokenWrapper> {\n\t\tconst responseTypeModel = await resolveTypeReference(BlobPostOutTypeRef)\n\t\tconst instance = JSON.parse(jsonData)\n\t\tconst { blobReferenceToken } = await this.instanceMapper.decryptAndMapToInstance<BlobPostOut>(responseTypeModel, instance, null)\n\t\treturn createBlobReferenceTokenWrapper({ blobReferenceToken })\n\t}\n\n\tprivate async downloadAndDecryptChunk(blob: Blob, blobServerAccessInfo: BlobServerAccessInfo, sessionKey: Aes128Key): Promise<Uint8Array> {\n\t\tconst { archiveId, blobId } = blob\n\t\tconst getData = createBlobGetIn({\n\t\t\tarchiveId,\n\t\t\tblobId,\n\t\t})\n\t\tconst BlobGetInTypeModel = await resolveTypeReference(BlobGetInTypeRef)\n\t\tconst literalGetData = await this.instanceMapper.encryptAndMapToLiteral(BlobGetInTypeModel, getData, null)\n\t\tconst body = JSON.stringify(literalGetData)\n\t\tconst queryParams = await this.blobAccessTokenFacade.createQueryParams(blobServerAccessInfo, {}, BlobGetInTypeRef)\n\t\treturn tryServers(\n\t\t\tblobServerAccessInfo.servers,\n\t\t\tasync (serverUrl) => {\n\t\t\t\tconst data = await this.restClient.request(BLOB_SERVICE_REST_PATH, HttpMethod.GET, {\n\t\t\t\t\tqueryParams: queryParams,\n\t\t\t\t\tbody,\n\t\t\t\t\tresponseType: MediaType.Binary,\n\t\t\t\t\tbaseUrl: serverUrl,\n\t\t\t\t\tnoCORS: true,\n\t\t\t\t})\n\t\t\t\treturn aes128Decrypt(sessionKey, data)\n\t\t\t},\n\t\t\t`can't download from server `,\n\t\t)\n\t}\n\n\tprivate async downloadAndDecryptChunkNative(blob: Blob, blobServerAccessInfo: BlobServerAccessInfo, sessionKey: Aes128Key): Promise<FileUri> {\n\t\tconst { archiveId, blobId } = blob\n\t\tconst getData = createBlobGetIn({\n\t\t\tarchiveId,\n\t\t\tblobId,\n\t\t})\n\t\tconst BlobGetInTypeModel = await resolveTypeReference(BlobGetInTypeRef)\n\t\tconst literalGetData = await this.instanceMapper.encryptAndMapToLiteral(BlobGetInTypeModel, getData, null)\n\t\tconst _body = JSON.stringify(literalGetData)\n\n\t\tconst blobFilename = blobId + \".blob\"\n\n\t\treturn tryServers(\n\t\t\tblobServerAccessInfo.servers,\n\t\t\tasync (serverUrl) => {\n\t\t\t\treturn await this.downloadNative(serverUrl, blobServerAccessInfo, sessionKey, blobFilename, { _body })\n\t\t\t},\n\t\t\t`can't download native from server `,\n\t\t)\n\t}\n\n\t/**\n\t * @return the uri of the decrypted blob\n\t */\n\tprivate async downloadNative(\n\t\tserverUrl: string,\n\t\tblobServerAccessInfo: BlobServerAccessInfo,\n\t\tsessionKey: Aes128Key,\n\t\tfileName: string,\n\t\tadditionalParams: Dict,\n\t): Promise<FileUri> {\n\t\tif (this.suspensionHandler.isSuspended()) {\n\t\t\treturn this.suspensionHandler.deferRequest(() => this.downloadNative(serverUrl, blobServerAccessInfo, sessionKey, fileName, additionalParams))\n\t\t}\n\t\tconst serviceUrl = new URL(BLOB_SERVICE_REST_PATH, serverUrl)\n\t\tconst url = addParamsToUrl(serviceUrl, await this.blobAccessTokenFacade.createQueryParams(blobServerAccessInfo, additionalParams, BlobGetInTypeRef))\n\t\tconst { statusCode, encryptedFileUri, suspensionTime, errorId, precondition } = await this.fileApp.download(url.toString(), fileName, {})\n\t\tif (statusCode == 200 && encryptedFileUri != null) {\n\t\t\tconst decryptedFileUrl = await this.aesApp.aesDecryptFile(sessionKey, encryptedFileUri)\n\t\t\ttry {\n\t\t\t\tawait this.fileApp.deleteFile(encryptedFileUri)\n\t\t\t} catch {\n\t\t\t\tconsole.log(\"Failed to delete encrypted file\", encryptedFileUri)\n\t\t\t}\n\t\t\treturn decryptedFileUrl\n\t\t} else if (isSuspensionResponse(statusCode, suspensionTime)) {\n\t\t\tthis.suspensionHandler.activateSuspensionIfInactive(Number(suspensionTime))\n\t\t\treturn this.suspensionHandler.deferRequest(() => this.downloadNative(serverUrl, blobServerAccessInfo, sessionKey, fileName, additionalParams))\n\t\t} else {\n\t\t\tthrow handleRestError(statusCode, ` | ${HttpMethod.GET} failed to natively download attachment`, errorId, precondition)\n\t\t}\n\t}\n}\n","import { addParamsToUrl, isSuspensionResponse, RestClient } from \"../../rest/RestClient.js\"\nimport { CryptoFacade } from \"../../crypto/CryptoFacade.js\"\nimport type { File as TutanotaFile } from \"../../../entities/tutanota/TypeRefs.js\"\nimport { createFileDataDataGet, FileDataDataGetTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\nimport { assert, assertNotNull, filterInt, neverNull } from \"@tutao/tutanota-utils\"\n\nimport { HttpMethod, MediaType, resolveTypeReference } from \"../../../common/EntityFunctions.js\"\nimport { assertWorkerOrNode, getApiOrigin, Mode } from \"../../../common/Env.js\"\nimport { handleRestError } from \"../../../common/error/RestError.js\"\nimport { convertToDataFile, DataFile } from \"../../../common/DataFile.js\"\nimport type { SuspensionHandler } from \"../../SuspensionHandler.js\"\nimport { aes128Decrypt } from \"@tutao/tutanota-crypto\"\nimport type { NativeFileApp } from \"../../../../native/common/FileApp.js\"\nimport type { AesApp } from \"../../../../native/worker/AesApp.js\"\nimport { InstanceMapper } from \"../../crypto/InstanceMapper.js\"\nimport { FileReference } from \"../../../common/utils/FileUtils.js\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport modelInfo from \"../../../entities/tutanota/ModelInfo.js\"\nimport { UserFacade } from \"../UserFacade.js\"\n\nassertWorkerOrNode()\nconst REST_PATH = \"/rest/tutanota/filedataservice\"\n\nexport class FileFacade {\n\tconstructor(\n\t\tprivate readonly user: UserFacade,\n\t\tprivate readonly restClient: RestClient,\n\t\tprivate readonly suspensionHandler: SuspensionHandler,\n\t\tprivate readonly fileApp: NativeFileApp,\n\t\tprivate readonly aesApp: AesApp,\n\t\tprivate readonly instanceMapper: InstanceMapper,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly cryptoFacade: CryptoFacade,\n\t) {}\n\n\tclearFileData(): Promise<void> {\n\t\treturn this.fileApp.clearFileData()\n\t}\n\n\tasync downloadFileContent(file: TutanotaFile): Promise<DataFile> {\n\t\tlet requestData = createFileDataDataGet()\n\t\trequestData.file = file._id\n\t\trequestData.base64 = false\n\t\tconst sessionKey = await this.cryptoFacade.resolveSessionKeyForInstance(file)\n\t\tconst entityToSend = await this.instanceMapper.encryptAndMapToLiteral(await resolveTypeReference(FileDataDataGetTypeRef), requestData, null)\n\t\tlet headers = this.user.createAuthHeaders()\n\n\t\theaders[\"v\"] = String(modelInfo.version)\n\t\tlet body = JSON.stringify(entityToSend)\n\t\tconst data = await this.restClient.request(REST_PATH, HttpMethod.GET, { body, responseType: MediaType.Binary, headers })\n\t\treturn convertToDataFile(file, aes128Decrypt(neverNull(sessionKey), data))\n\t}\n\n\tasync downloadFileContentNative(file: TutanotaFile): Promise<FileReference> {\n\t\tassert(env.mode === Mode.App || env.mode === Mode.Desktop, \"Environment is not app or Desktop!\")\n\n\t\tif (this.suspensionHandler.isSuspended()) {\n\t\t\treturn this.suspensionHandler.deferRequest(() => this.downloadFileContentNative(file))\n\t\t}\n\n\t\tconst sessionKey = assertNotNull(await this.cryptoFacade.resolveSessionKeyForInstance(file), \"Session key for TutanotaFile is null\")\n\n\t\tconst requestData = createFileDataDataGet({\n\t\t\tfile: file._id,\n\t\t\tbase64: false,\n\t\t})\n\n\t\tconst FileDataDataGetTypModel = await resolveTypeReference(FileDataDataGetTypeRef)\n\t\tconst entityToSend = await this.instanceMapper.encryptAndMapToLiteral(FileDataDataGetTypModel, requestData, null)\n\n\t\tconst headers = this.user.createAuthHeaders()\n\n\t\theaders[\"v\"] = String(modelInfo.version)\n\t\tconst body = JSON.stringify(entityToSend)\n\t\tconst queryParams = {\n\t\t\t_body: body,\n\t\t}\n\t\tconst url = addParamsToUrl(new URL(getApiOrigin() + REST_PATH), queryParams)\n\t\tconst { statusCode, encryptedFileUri, errorId, precondition, suspensionTime } = await this.fileApp.download(url.toString(), file.name, headers)\n\n\t\tif (suspensionTime && isSuspensionResponse(statusCode, suspensionTime)) {\n\t\t\tthis.suspensionHandler.activateSuspensionIfInactive(Number(suspensionTime))\n\n\t\t\treturn this.suspensionHandler.deferRequest(() => this.downloadFileContentNative(file))\n\t\t} else if (statusCode === 200 && encryptedFileUri != null) {\n\t\t\tconst decryptedFileUri = await this.aesApp.aesDecryptFile(neverNull(sessionKey), encryptedFileUri)\n\n\t\t\ttry {\n\t\t\t\tawait this.fileApp.deleteFile(encryptedFileUri)\n\t\t\t} catch (e) {\n\t\t\t\tconsole.warn(\"Failed to delete encrypted file\", encryptedFileUri)\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t_type: \"FileReference\",\n\t\t\t\tname: file.name,\n\t\t\t\tmimeType: file.mimeType ?? MediaType.Binary,\n\t\t\t\tlocation: decryptedFileUri,\n\t\t\t\tsize: filterInt(file.size),\n\t\t\t}\n\t\t} else {\n\t\t\tthrow handleRestError(statusCode, ` | GET ${url.toString()} failed to natively download attachment`, errorId, precondition)\n\t\t}\n\t}\n}\n","import type { CryptoFacade } from \"../../crypto/CryptoFacade.js\"\nimport { encryptBytes, encryptString } from \"../../crypto/CryptoFacade.js\"\nimport {\n\tDraftService,\n\tExternalUserService,\n\tListUnsubscribeService,\n\tMailFolderService,\n\tMailService,\n\tMoveMailService,\n\tReportMailService,\n\tSendDraftService,\n} from \"../../../entities/tutanota/Services.js\"\nimport type { ConversationType } from \"../../../common/TutanotaConstants.js\"\nimport {\n\tArchiveDataType,\n\tCounterType,\n\tGroupType,\n\tMailAuthenticationStatus as MailAuthStatus,\n\tMailMethod,\n\tMailReportType,\n\tOperationType,\n\tPhishingMarkerStatus,\n\tReportedMailFieldType,\n} from \"../../../common/TutanotaConstants.js\"\nimport type {\n\tContact,\n\tDraftAttachment,\n\tDraftRecipient,\n\tEncryptedMailAddress,\n\tFile as TutanotaFile,\n\tMail,\n\tMailFolder,\n\tPhishingMarker,\n\tSendDraftData,\n} from \"../../../entities/tutanota/TypeRefs.js\"\nimport {\n\tcreateAttachmentKeyData,\n\tcreateCreateExternalUserGroupData,\n\tcreateCreateMailFolderData,\n\tcreateDeleteMailData,\n\tcreateDeleteMailFolderData,\n\tcreateDraftAttachment,\n\tcreateDraftCreateData,\n\tcreateDraftData,\n\tcreateDraftRecipient,\n\tcreateDraftUpdateData,\n\tcreateEncryptedMailAddress,\n\tcreateExternalUserData,\n\tcreateListUnsubscribeData,\n\tcreateMoveMailData,\n\tcreateNewDraftAttachment,\n\tcreateReportMailPostData,\n\tcreateSecureExternalRecipientKeyData,\n\tcreateSendDraftData,\n\tcreateUpdateMailFolderData,\n\tFileTypeRef,\n\tMailDetailsDraftTypeRef,\n\tMailTypeRef,\n\tTutanotaPropertiesTypeRef,\n} from \"../../../entities/tutanota/TypeRefs.js\"\nimport { RecipientsNotFoundError } from \"../../../common/error/RecipientsNotFoundError.js\"\nimport { NotFoundError } from \"../../../common/error/RestError.js\"\nimport type { EntityUpdate, PublicKeyReturn, User } from \"../../../entities/sys/TypeRefs.js\"\nimport {\n\tBlobReferenceTokenWrapper,\n\tcreatePublicKeyData,\n\tExternalUserReferenceTypeRef,\n\tGroupInfoTypeRef,\n\tGroupRootTypeRef,\n\tGroupTypeRef,\n\tUserTypeRef,\n} from \"../../../entities/sys/TypeRefs.js\"\nimport {\n\taddressDomain,\n\tassertNotNull,\n\tbyteLength,\n\tcontains,\n\tdefer,\n\tisNotNull,\n\tisSameTypeRefByAttr,\n\tneverNull,\n\tnoOp,\n\tofClass,\n\tpromiseFilter,\n\tpromiseMap,\n} from \"@tutao/tutanota-utils\"\nimport { BlobFacade } from \"./BlobFacade.js\"\nimport { FileFacade } from \"./FileFacade.js\"\nimport { assertWorkerOrNode, isApp, isDesktop } from \"../../../common/Env.js\"\nimport { EntityClient } from \"../../../common/EntityClient.js\"\nimport { getEnabledMailAddressesForGroupInfo, getUserGroupMemberships } from \"../../../common/utils/GroupUtils.js\"\nimport { containsId, getLetId, isSameId, stringToCustomId } from \"../../../common/utils/EntityUtils.js\"\nimport { htmlToText } from \"../../search/IndexUtils.js\"\nimport { MailBodyTooLargeError } from \"../../../common/error/MailBodyTooLargeError.js\"\nimport { UNCOMPRESSED_MAX_SIZE } from \"../../Compression.js\"\nimport {\n\taes128RandomKey,\n\tbitArrayToUint8Array,\n\tcreateAuthVerifier,\n\tdecryptKey,\n\tencryptKey,\n\tgenerateKeyFromPassphrase,\n\tgenerateRandomSalt,\n\tKeyLength,\n\tkeyToUint8Array,\n\tmurmurHash,\n\trandom,\n\tsha256Hash,\n} from \"@tutao/tutanota-crypto\"\nimport { DataFile } from \"../../../common/DataFile.js\"\nimport { FileReference, isDataFile, isFileReference } from \"../../../common/utils/FileUtils.js\"\nimport { CounterService } from \"../../../entities/monitor/Services.js\"\nimport { PublicKeyService } from \"../../../entities/sys/Services.js\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { createWriteCounterData } from \"../../../entities/monitor/TypeRefs.js\"\nimport { UserFacade } from \"../UserFacade.js\"\nimport { PartialRecipient, Recipient, RecipientList, RecipientType } from \"../../../common/recipients/Recipient.js\"\nimport { NativeFileApp } from \"../../../../native/common/FileApp.js\"\nimport { isLegacyMail } from \"../../../common/MailWrapper.js\"\n\nassertWorkerOrNode()\ntype Attachments = ReadonlyArray<TutanotaFile | DataFile | FileReference>\n\ninterface CreateDraftParams {\n\tsubject: string\n\tbodyText: string\n\tsenderMailAddress: string\n\tsenderName: string\n\ttoRecipients: RecipientList\n\tccRecipients: RecipientList\n\tbccRecipients: RecipientList\n\tconversationType: ConversationType\n\tpreviousMessageId: Id | null\n\tattachments: Attachments | null\n\tconfidential: boolean\n\treplyTos: RecipientList\n\tmethod: MailMethod\n}\n\ninterface UpdateDraftParams {\n\tsubject: string\n\tbody: string\n\tsenderMailAddress: string\n\tsenderName: string\n\ttoRecipients: RecipientList\n\tccRecipients: RecipientList\n\tbccRecipients: RecipientList\n\tattachments: Attachments | null\n\tconfidential: boolean\n\tdraft: Mail\n}\n\nexport class MailFacade {\n\tprivate phishingMarkers: Set<string> = new Set()\n\tprivate deferredDraftId: IdTuple | null = null // the mail id of the draft that we are waiting for to be updated via websocket\n\tprivate deferredDraftUpdate: Record<string, any> | null = null // this deferred promise is resolved as soon as the update of the draft is received\n\n\tconstructor(\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly fileFacade: FileFacade,\n\t\tprivate readonly entityClient: EntityClient,\n\t\tprivate readonly crypto: CryptoFacade,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly blobFacade: BlobFacade,\n\t\tprivate readonly fileApp: NativeFileApp,\n\t) {}\n\n\tasync createMailFolder(name: string, parent: IdTuple | null, ownerGroupId: Id): Promise<void> {\n\t\tconst mailGroupKey = this.userFacade.getGroupKey(ownerGroupId)\n\n\t\tconst sk = aes128RandomKey()\n\t\tconst newFolder = createCreateMailFolderData({\n\t\t\tfolderName: name,\n\t\t\tparentFolder: parent,\n\t\t\townerEncSessionKey: encryptKey(mailGroupKey, sk),\n\t\t\townerGroup: ownerGroupId,\n\t\t})\n\t\tawait this.serviceExecutor.post(MailFolderService, newFolder, { sessionKey: sk })\n\t}\n\n\t/**\n\t * Updates a mail folder's name, if needed\n\t * @param newName - if this is the same as the folder's current name, nothing is done\n\t */\n\tasync updateMailFolderName(folder: MailFolder, newName: string): Promise<void> {\n\t\tif (newName !== folder.name) {\n\t\t\tfolder.name = newName\n\t\t\tawait this.entityClient.update(folder)\n\t\t}\n\t}\n\n\t/**\n\t * Updates a mail folder's parent, if needed\n\t * @param newParent - if this is the same as the folder's current parent, nothing is done\n\t */\n\tasync updateMailFolderParent(folder: MailFolder, newParent: IdTuple | null): Promise<void> {\n\t\tif (\n\t\t\t(folder.parentFolder != null && newParent != null && !isSameId(folder.parentFolder, newParent)) ||\n\t\t\t(folder.parentFolder == null && newParent != null) ||\n\t\t\t(folder.parentFolder != null && newParent == null)\n\t\t) {\n\t\t\tconst updateFolder = createUpdateMailFolderData({\n\t\t\t\tfolder: folder._id,\n\t\t\t\tnewParent: newParent,\n\t\t\t})\n\t\t\tawait this.serviceExecutor.put(MailFolderService, updateFolder)\n\t\t}\n\t}\n\n\t/**\n\t * Creates a draft mail.\n\t * @param bodyText The bodyText of the mail formatted as HTML.\n\t * @param previousMessageId The id of the message that this mail is a reply or forward to. Null if this is a new mail.\n\t * @param attachments The files that shall be attached to this mail or null if no files shall be attached. TutanotaFiles are already exising on the server, DataFiles are files from the local file system. Attention: the DataFile class information is lost\n\t * @param confidential True if the mail shall be sent end-to-end encrypted, false otherwise.\n\t */\n\tasync createDraft({\n\t\tsubject,\n\t\tbodyText,\n\t\tsenderMailAddress,\n\t\tsenderName,\n\t\ttoRecipients,\n\t\tccRecipients,\n\t\tbccRecipients,\n\t\tconversationType,\n\t\tpreviousMessageId,\n\t\tattachments,\n\t\tconfidential,\n\t\treplyTos,\n\t\tmethod,\n\t}: CreateDraftParams): Promise<Mail> {\n\t\tif (byteLength(bodyText) > UNCOMPRESSED_MAX_SIZE) {\n\t\t\tthrow new MailBodyTooLargeError(`Can't update draft, mail body too large (${byteLength(bodyText)})`)\n\t\t}\n\n\t\tconst senderMailGroupId = await this._getMailGroupIdForMailAddress(this.userFacade.getLoggedInUser(), senderMailAddress)\n\n\t\tconst userGroupKey = this.userFacade.getUserGroupKey()\n\n\t\tconst mailGroupKey = this.userFacade.getGroupKey(senderMailGroupId)\n\n\t\tconst sk = aes128RandomKey()\n\t\tconst service = createDraftCreateData()\n\t\tservice.previousMessageId = previousMessageId\n\t\tservice.conversationType = conversationType\n\t\tservice.ownerEncSessionKey = encryptKey(mailGroupKey, sk)\n\t\tservice.symEncSessionKey = encryptKey(userGroupKey, sk) // legacy\n\n\t\tservice.draftData = createDraftData({\n\t\t\tsubject,\n\t\t\tcompressedBodyText: bodyText,\n\t\t\tsenderMailAddress,\n\t\t\tsenderName,\n\t\t\tconfidential,\n\t\t\tmethod,\n\t\t\ttoRecipients: toRecipients.map(recipientToDraftRecipient),\n\t\t\tccRecipients: ccRecipients.map(recipientToDraftRecipient),\n\t\t\tbccRecipients: bccRecipients.map(recipientToDraftRecipient),\n\t\t\treplyTos: replyTos.map(recipientToEncryptedMailAddress),\n\t\t\taddedAttachments: await this._createAddedAttachments(attachments, [], senderMailGroupId, mailGroupKey),\n\t\t})\n\t\tconst createDraftReturn = await this.serviceExecutor.post(DraftService, service, { sessionKey: sk })\n\t\treturn this.entityClient.load(MailTypeRef, createDraftReturn.draft)\n\t}\n\n\t/**\n\t * Updates a draft mail.\n\t * @param subject The subject of the mail.\n\t * @param body The body text of the mail.\n\t * @param senderMailAddress The senders mail address.\n\t * @param senderName The name of the sender that is sent together with the mail address of the sender.\n\t * @param toRecipients The recipients the mail shall be sent to.\n\t * @param ccRecipients The recipients the mail shall be sent to in cc.\n\t * @param bccRecipients The recipients the mail shall be sent to in bcc.\n\t * @param attachments The files that shall be attached to this mail or null if the current attachments shall not be changed.\n\t * @param confidential True if the mail shall be sent end-to-end encrypted, false otherwise.\n\t * @param draft The draft to update.\n\t * @return The updated draft. Rejected with TooManyRequestsError if the number allowed mails was exceeded, AccessBlockedError if the customer is not allowed to send emails currently because he is marked for approval.\n\t */\n\tasync updateDraft({\n\t\tsubject,\n\t\tbody,\n\t\tsenderMailAddress,\n\t\tsenderName,\n\t\ttoRecipients,\n\t\tccRecipients,\n\t\tbccRecipients,\n\t\tattachments,\n\t\tconfidential,\n\t\tdraft,\n\t}: UpdateDraftParams): Promise<Mail> {\n\t\tif (byteLength(body) > UNCOMPRESSED_MAX_SIZE) {\n\t\t\tthrow new MailBodyTooLargeError(`Can't update draft, mail body too large (${byteLength(body)})`)\n\t\t}\n\n\t\tconst senderMailGroupId = await this._getMailGroupIdForMailAddress(this.userFacade.getLoggedInUser(), senderMailAddress)\n\n\t\tconst mailGroupKey = this.userFacade.getGroupKey(senderMailGroupId)\n\t\tconst currentAttachments = await this.getAttachmentIds(draft)\n\t\tconst replyTos = await this.getReplyTos(draft)\n\n\t\tconst sk = decryptKey(mailGroupKey, draft._ownerEncSessionKey as any)\n\t\tconst service = createDraftUpdateData()\n\t\tservice.draft = draft._id\n\t\tservice.draftData = createDraftData({\n\t\t\tsubject: subject,\n\t\t\tcompressedBodyText: body,\n\t\t\tsenderMailAddress: senderMailAddress,\n\t\t\tsenderName: senderName,\n\t\t\tconfidential: confidential,\n\t\t\tmethod: draft.method,\n\t\t\ttoRecipients: toRecipients.map(recipientToDraftRecipient),\n\t\t\tccRecipients: ccRecipients.map(recipientToDraftRecipient),\n\t\t\tbccRecipients: bccRecipients.map(recipientToDraftRecipient),\n\t\t\treplyTos: replyTos,\n\t\t\tremovedAttachments: this._getRemovedAttachments(attachments, currentAttachments),\n\t\t\taddedAttachments: await this._createAddedAttachments(attachments, currentAttachments, senderMailGroupId, mailGroupKey),\n\t\t})\n\t\tthis.deferredDraftId = draft._id\n\t\t// we have to wait for the updated mail because sendMail() might be called right after this update\n\t\tthis.deferredDraftUpdate = defer()\n\t\t// use a local reference here because this._deferredDraftUpdate is set to null when the event is received async\n\t\tconst deferredUpdatePromiseWrapper = this.deferredDraftUpdate\n\t\tawait this.serviceExecutor.put(DraftService, service, { sessionKey: sk })\n\t\treturn deferredUpdatePromiseWrapper.promise\n\t}\n\n\tasync moveMails(mails: IdTuple[], targetFolder: IdTuple): Promise<void> {\n\t\tawait this.serviceExecutor.post(MoveMailService, createMoveMailData({ mails, targetFolder }))\n\t}\n\n\tasync reportMail(mail: Mail, reportType: MailReportType): Promise<void> {\n\t\tconst mailSessionKey: Aes128Key = assertNotNull(await this.crypto.resolveSessionKeyForInstance(mail))\n\t\tconst postData = createReportMailPostData({\n\t\t\tmailId: mail._id,\n\t\t\tmailSessionKey: bitArrayToUint8Array(mailSessionKey),\n\t\t\treportType,\n\t\t})\n\t\tawait this.serviceExecutor.post(ReportMailService, postData)\n\t}\n\n\tasync deleteMails(mails: IdTuple[], folder: IdTuple): Promise<void> {\n\t\tconst deleteMailData = createDeleteMailData({\n\t\t\tmails,\n\t\t\tfolder,\n\t\t})\n\t\tawait this.serviceExecutor.delete(MailService, deleteMailData)\n\t}\n\n\t/**\n\t * Returns all ids of the files that have been removed, i.e. that are contained in the existingFileIds but not in the provided files\n\t */\n\t_getRemovedAttachments(providedFiles: Attachments | null, existingFileIds: IdTuple[]): IdTuple[] {\n\t\tlet removedAttachmentIds: IdTuple[] = []\n\n\t\tif (providedFiles) {\n\t\t\tlet attachments = neverNull(providedFiles)\n\t\t\t// check which attachments have been removed\n\t\t\texistingFileIds.forEach((fileId) => {\n\t\t\t\tif (\n\t\t\t\t\t!attachments.find(\n\t\t\t\t\t\t(attachment) => attachment._type !== \"DataFile\" && attachment._type !== \"FileReference\" && isSameId(getLetId(attachment), fileId),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tremovedAttachmentIds.push(fileId)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\treturn removedAttachmentIds\n\t}\n\n\t/**\n\t * Uploads the given data files or sets the file if it is already existing files (e.g. forwarded files) and returns all DraftAttachments\n\t */\n\tasync _createAddedAttachments(\n\t\tprovidedFiles: Attachments | null,\n\t\texistingFileIds: ReadonlyArray<IdTuple>,\n\t\tsenderMailGroupId: Id,\n\t\tmailGroupKey: Aes128Key,\n\t): Promise<DraftAttachment[]> {\n\t\tif (providedFiles == null || providedFiles.length === 0) return []\n\n\t\treturn promiseMap(providedFiles, async (providedFile) => {\n\t\t\t// check if this is a new attachment or an existing one\n\t\t\tif (isDataFile(providedFile)) {\n\t\t\t\t// user added attachment\n\t\t\t\tconst fileSessionKey = aes128RandomKey()\n\t\t\t\tlet referenceTokens: Array<BlobReferenceTokenWrapper>\n\t\t\t\tif (isApp() || isDesktop()) {\n\t\t\t\t\tconst { location } = await this.fileApp.writeDataFile(providedFile)\n\t\t\t\t\treferenceTokens = await this.blobFacade.encryptAndUploadNative(ArchiveDataType.Attachments, location, senderMailGroupId, fileSessionKey)\n\t\t\t\t\tawait this.fileApp.deleteFile(location)\n\t\t\t\t} else {\n\t\t\t\t\treferenceTokens = await this.blobFacade.encryptAndUpload(ArchiveDataType.Attachments, providedFile.data, senderMailGroupId, fileSessionKey)\n\t\t\t\t}\n\t\t\t\treturn this.createAndEncryptDraftAttachment(referenceTokens, fileSessionKey, providedFile, mailGroupKey)\n\t\t\t} else if (isFileReference(providedFile)) {\n\t\t\t\tconst fileSessionKey = aes128RandomKey()\n\t\t\t\tconst referenceTokens = await this.blobFacade.encryptAndUploadNative(\n\t\t\t\t\tArchiveDataType.Attachments,\n\t\t\t\t\tprovidedFile.location,\n\t\t\t\t\tsenderMailGroupId,\n\t\t\t\t\tfileSessionKey,\n\t\t\t\t)\n\t\t\t\treturn this.createAndEncryptDraftAttachment(referenceTokens, fileSessionKey, providedFile, mailGroupKey)\n\t\t\t} else if (!containsId(existingFileIds, getLetId(providedFile))) {\n\t\t\t\t// forwarded attachment which was not in the draft before\n\t\t\t\treturn this.crypto.resolveSessionKeyForInstance(providedFile).then((fileSessionKey) => {\n\t\t\t\t\tconst attachment = createDraftAttachment()\n\t\t\t\t\tattachment.existingFile = getLetId(providedFile)\n\t\t\t\t\tattachment.ownerEncFileSessionKey = encryptKey(mailGroupKey, neverNull(fileSessionKey))\n\t\t\t\t\treturn attachment\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\treturn null\n\t\t\t}\n\t\t}) // disable concurrent file upload to avoid timeout because of missing progress events on Firefox.\n\t\t\t.then((attachments) => attachments.filter(isNotNull))\n\t\t\t.then((it) => {\n\t\t\t\t// only delete the temporary files after all attachments have been uploaded\n\t\t\t\tif (isApp()) {\n\t\t\t\t\tthis.fileFacade.clearFileData().catch((e) => console.warn(\"Failed to clear files\", e))\n\t\t\t\t}\n\n\t\t\t\treturn it\n\t\t\t})\n\t}\n\n\tprivate createAndEncryptDraftAttachment(\n\t\treferenceTokens: BlobReferenceTokenWrapper[],\n\t\tfileSessionKey: Aes128Key,\n\t\tprovidedFile: DataFile | FileReference,\n\t\tmailGroupKey: Aes128Key,\n\t): DraftAttachment {\n\t\tlet attachment = createDraftAttachment()\n\t\tlet newAttachmentData = createNewDraftAttachment()\n\t\tnewAttachmentData.encFileName = encryptString(fileSessionKey, providedFile.name)\n\t\tnewAttachmentData.encMimeType = encryptString(fileSessionKey, providedFile.mimeType)\n\t\tnewAttachmentData.fileData = null\n\t\tnewAttachmentData.referenceTokens = referenceTokens\n\t\tnewAttachmentData.encCid = providedFile.cid == null ? null : encryptString(fileSessionKey, providedFile.cid)\n\t\tattachment.newFile = newAttachmentData\n\t\tattachment.ownerEncFileSessionKey = encryptKey(mailGroupKey, fileSessionKey)\n\t\treturn attachment\n\t}\n\n\tasync sendDraft(draft: Mail, recipients: Array<Recipient>, language: string): Promise<void> {\n\t\tconst senderMailGroupId = await this._getMailGroupIdForMailAddress(this.userFacade.getLoggedInUser(), draft.sender.address)\n\t\tconst bucketKey = aes128RandomKey()\n\t\tconst sendDraftData = createSendDraftData()\n\t\tsendDraftData.language = language\n\t\tsendDraftData.mail = draft._id\n\n\t\tconst attachments = await this.getAttachmentIds(draft)\n\t\tfor (let fileId of attachments) {\n\t\t\tconst file = await this.entityClient.load(FileTypeRef, fileId)\n\t\t\tconst fileSessionKey = await this.crypto.resolveSessionKeyForInstance(file)\n\t\t\tconst data = createAttachmentKeyData({\n\t\t\t\tfile: fileId,\n\t\t\t})\n\n\t\t\tif (draft.confidential) {\n\t\t\t\tdata.bucketEncFileSessionKey = encryptKey(bucketKey, neverNull(fileSessionKey))\n\t\t\t} else {\n\t\t\t\tdata.fileSessionKey = keyToUint8Array(neverNull(fileSessionKey))\n\t\t\t}\n\n\t\t\tsendDraftData.attachmentKeyData.push(data)\n\t\t}\n\n\t\tawait Promise.all([\n\t\t\tthis.entityClient.loadRoot(TutanotaPropertiesTypeRef, this.userFacade.getUserGroupId()).then((tutanotaProperties) => {\n\t\t\t\tsendDraftData.plaintext = tutanotaProperties.sendPlaintextOnly\n\t\t\t}),\n\t\t\tthis.crypto.resolveSessionKeyForInstance(draft).then((mailSessionkey) => {\n\t\t\t\tlet sk = neverNull(mailSessionkey)\n\t\t\t\tsendDraftData.calendarMethod = draft.method !== MailMethod.NONE\n\n\t\t\t\tif (draft.confidential) {\n\t\t\t\t\tsendDraftData.bucketEncMailSessionKey = encryptKey(bucketKey, sk)\n\t\t\t\t\tconst hasExternalSecureRecipient = recipients.some((r) => r.type === RecipientType.EXTERNAL && !!this.getContactPassword(r.contact)?.trim())\n\n\t\t\t\t\tif (hasExternalSecureRecipient) {\n\t\t\t\t\t\tsendDraftData.senderNameUnencrypted = draft.sender.name // needed for notification mail\n\t\t\t\t\t}\n\n\t\t\t\t\treturn this._addRecipientKeyData(bucketKey, sendDraftData, recipients, senderMailGroupId)\n\t\t\t\t} else {\n\t\t\t\t\tsendDraftData.mailSessionKey = bitArrayToUint8Array(sk)\n\t\t\t\t}\n\t\t\t}),\n\t\t])\n\t\tawait this.serviceExecutor.post(SendDraftService, sendDraftData)\n\t}\n\n\tasync getAttachmentIds(draft: Mail): Promise<IdTuple[]> {\n\t\treturn draft.attachments\n\t}\n\n\tasync getReplyTos(draft: Mail): Promise<EncryptedMailAddress[]> {\n\t\tif (isLegacyMail(draft)) {\n\t\t\treturn draft.replyTos\n\t\t} else {\n\t\t\tconst mailDetails = await this.entityClient.load(MailDetailsDraftTypeRef, neverNull(draft.mailDetailsDraft))\n\t\t\treturn mailDetails.details.replyTos\n\t\t}\n\t}\n\n\tcheckMailForPhishing(\n\t\tmail: Mail,\n\t\tlinks: Array<{\n\t\t\thref: string\n\t\t\tinnerHTML: string\n\t\t}>,\n\t): Promise<boolean> {\n\t\tlet score = 0\n\t\tconst senderAddress = mail.sender.address\n\t\tconst senderAuthenticated = mail.authStatus === MailAuthStatus.AUTHENTICATED\n\n\t\tif (senderAuthenticated) {\n\t\t\tif (this._checkFieldForPhishing(ReportedMailFieldType.FROM_ADDRESS, senderAddress)) {\n\t\t\t\tscore += 6\n\t\t\t} else {\n\t\t\t\tconst senderDomain = addressDomain(senderAddress)\n\n\t\t\t\tif (this._checkFieldForPhishing(ReportedMailFieldType.FROM_DOMAIN, senderDomain)) {\n\t\t\t\t\tscore += 6\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (this._checkFieldForPhishing(ReportedMailFieldType.FROM_ADDRESS_NON_AUTH, senderAddress)) {\n\t\t\t\tscore += 6\n\t\t\t} else {\n\t\t\t\tconst senderDomain = addressDomain(senderAddress)\n\n\t\t\t\tif (this._checkFieldForPhishing(ReportedMailFieldType.FROM_DOMAIN_NON_AUTH, senderDomain)) {\n\t\t\t\t\tscore += 6\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// We check that subject exists because when there's an encryption error it will be missing\n\t\tif (mail.subject && this._checkFieldForPhishing(ReportedMailFieldType.SUBJECT, mail.subject)) {\n\t\t\tscore += 3\n\t\t}\n\n\t\tfor (const link of links) {\n\t\t\tif (this._checkFieldForPhishing(ReportedMailFieldType.LINK, link.href)) {\n\t\t\t\tscore += 6\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\tconst domain = getUrlDomain(link.href)\n\n\t\t\t\tif (domain && this._checkFieldForPhishing(ReportedMailFieldType.LINK_DOMAIN, domain)) {\n\t\t\t\t\tscore += 6\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst hasSuspiciousLink = links.some(({ href, innerHTML }) => {\n\t\t\tconst innerText = htmlToText(innerHTML)\n\t\t\tconst textUrl = parseUrl(innerText)\n\t\t\tconst hrefUrl = parseUrl(href)\n\t\t\treturn textUrl && hrefUrl && textUrl.hostname !== hrefUrl.hostname\n\t\t})\n\n\t\tif (hasSuspiciousLink) {\n\t\t\tscore += 6\n\t\t}\n\n\t\treturn Promise.resolve(7 < score)\n\t}\n\n\tasync deleteFolder(id: IdTuple): Promise<void> {\n\t\tconst deleteMailFolderData = createDeleteMailFolderData({\n\t\t\tfolders: [id],\n\t\t})\n\t\t// TODO make DeleteMailFolderData unencrypted in next model version\n\t\tawait this.serviceExecutor.delete(MailFolderService, deleteMailFolderData, { sessionKey: \"dummy\" as any })\n\t}\n\n\tasync fixupCounterForMailList(groupId: Id, listId: Id, unreadMails: number): Promise<void> {\n\t\tconst data = createWriteCounterData({\n\t\t\tcounterType: CounterType.UnreadMails,\n\t\t\trow: groupId,\n\t\t\tcolumn: listId,\n\t\t\tvalue: String(unreadMails),\n\t\t})\n\t\tawait this.serviceExecutor.post(CounterService, data)\n\t}\n\n\t_checkFieldForPhishing(type: ReportedMailFieldType, value: string): boolean {\n\t\tconst hash = phishingMarkerValue(type, value)\n\t\treturn this.phishingMarkers.has(hash)\n\t}\n\n\tasync _addRecipientKeyData(bucketKey: Aes128Key, service: SendDraftData, recipients: Array<Recipient>, senderMailGroupId: Id): Promise<void> {\n\t\tconst notFoundRecipients: string[] = []\n\n\t\tfor (let recipient of recipients) {\n\t\t\tif (recipient.address === \"system@tutanota.de\" || !recipient) {\n\t\t\t\tnotFoundRecipients.push(recipient.address)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// copy password information if this is an external contact\n\t\t\t// otherwise load the key information from the server\n\t\t\tif (recipient.type === RecipientType.EXTERNAL) {\n\t\t\t\tconst password = this.getContactPassword(recipient.contact)\n\n\t\t\t\tif (password == null || !isSameId(this.userFacade.getGroupId(GroupType.Mail), senderMailGroupId)) {\n\t\t\t\t\t// no password given and prevent sending to secure externals from shared group\n\t\t\t\t\tnotFoundRecipients.push(recipient.address)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tconst salt = generateRandomSalt()\n\t\t\t\tconst passwordKey = generateKeyFromPassphrase(password, salt, KeyLength.b128)\n\t\t\t\tconst passwordVerifier = createAuthVerifier(passwordKey)\n\t\t\t\tconst externalGroupKeys = await this._getExternalGroupKey(recipient.address, passwordKey, passwordVerifier)\n\t\t\t\tconst data = createSecureExternalRecipientKeyData()\n\t\t\t\tdata.mailAddress = recipient.address\n\t\t\t\tdata.symEncBucketKey = null // legacy for old permission system, not used any more\n\n\t\t\t\tdata.ownerEncBucketKey = encryptKey(externalGroupKeys.externalMailGroupKey, bucketKey)\n\t\t\t\tdata.passwordVerifier = passwordVerifier\n\t\t\t\tdata.salt = salt\n\t\t\t\tdata.saltHash = sha256Hash(salt)\n\t\t\t\tdata.pwEncCommunicationKey = encryptKey(passwordKey, externalGroupKeys.externalUserGroupKey)\n\t\t\t\tservice.secureExternalRecipientKeyData.push(data)\n\t\t\t} else {\n\t\t\t\tconst keyData = await this.crypto.encryptBucketKeyForInternalRecipient(bucketKey, recipient.address, notFoundRecipients)\n\n\t\t\t\tif (keyData) {\n\t\t\t\t\tservice.internalRecipientKeyData.push(keyData)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (notFoundRecipients.length > 0) {\n\t\t\tthrow new RecipientsNotFoundError(notFoundRecipients.join(\"\\n\"))\n\t\t}\n\t}\n\n\tprivate getContactPassword(contact: Contact | null): string | null {\n\t\treturn contact?.presharedPassword ?? contact?.autoTransmitPassword ?? null\n\t}\n\n\t/**\n\t * Checks that an external user instance with a mail box exists for the given recipient. If it does not exist, it is created.\n\t * Returns the user group key and the user mail group key of the external recipient.\n\t * @param recipientMailAddress\n\t * @param externalUserPwKey The external user's password key.\n\t * @param verifier The external user's verifier, base64 encoded.\n\t * @return Resolves to the the external user's group key and the external user's mail group key, rejected if an error occured\n\t */\n\t_getExternalGroupKey(\n\t\trecipientMailAddress: string,\n\t\texternalUserPwKey: Aes128Key,\n\t\tverifier: Uint8Array,\n\t): Promise<{\n\t\texternalUserGroupKey: Aes128Key\n\t\texternalMailGroupKey: Aes128Key\n\t}> {\n\t\treturn this.entityClient.loadRoot(GroupRootTypeRef, this.userFacade.getUserGroupId()).then((groupRoot) => {\n\t\t\tlet cleanedMailAddress = recipientMailAddress.trim().toLocaleLowerCase()\n\t\t\tlet mailAddressId = stringToCustomId(cleanedMailAddress)\n\t\t\treturn this.entityClient\n\t\t\t\t.load(ExternalUserReferenceTypeRef, [groupRoot.externalUserReferences, mailAddressId])\n\t\t\t\t.then((externalUserReference) => {\n\t\t\t\t\treturn this.entityClient.load(UserTypeRef, externalUserReference.user).then((externalUser) => {\n\t\t\t\t\t\tlet mailGroupId = neverNull(externalUser.memberships.find((m) => m.groupType === GroupType.Mail)).group\n\t\t\t\t\t\treturn Promise.all([\n\t\t\t\t\t\t\tthis.entityClient.load(GroupTypeRef, mailGroupId),\n\t\t\t\t\t\t\tthis.entityClient.load(GroupTypeRef, externalUserReference.userGroup),\n\t\t\t\t\t\t]).then(([externalMailGroup, externalUserGroup]) => {\n\t\t\t\t\t\t\tlet externalUserGroupKey = decryptKey(this.userFacade.getUserGroupKey(), neverNull(externalUserGroup.adminGroupEncGKey))\n\t\t\t\t\t\t\tlet externalMailGroupKey = decryptKey(externalUserGroupKey, neverNull(externalMailGroup.adminGroupEncGKey))\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\texternalUserGroupKey,\n\t\t\t\t\t\t\t\texternalMailGroupKey,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\t.catch(\n\t\t\t\t\tofClass(NotFoundError, (e) => {\n\t\t\t\t\t\t// it does not exist, so create it\n\t\t\t\t\t\tlet internalMailGroupKey = this.userFacade.getGroupKey(this.userFacade.getGroupId(GroupType.Mail))\n\n\t\t\t\t\t\tlet externalUserGroupKey = aes128RandomKey()\n\t\t\t\t\t\tlet externalMailGroupKey = aes128RandomKey()\n\t\t\t\t\t\tlet externalUserGroupInfoSessionKey = aes128RandomKey()\n\t\t\t\t\t\tlet externalMailGroupInfoSessionKey = aes128RandomKey()\n\t\t\t\t\t\tlet clientKey = aes128RandomKey()\n\t\t\t\t\t\tlet tutanotaPropertiesSessionKey = aes128RandomKey()\n\t\t\t\t\t\tlet mailboxSessionKey = aes128RandomKey()\n\t\t\t\t\t\tlet userEncEntropy = encryptBytes(externalUserGroupKey, random.generateRandomData(32))\n\t\t\t\t\t\tlet d = createExternalUserData()\n\t\t\t\t\t\td.verifier = verifier\n\t\t\t\t\t\td.userEncClientKey = encryptKey(externalUserGroupKey, clientKey)\n\t\t\t\t\t\td.externalUserEncUserGroupInfoSessionKey = encryptKey(externalUserGroupKey, externalUserGroupInfoSessionKey)\n\t\t\t\t\t\td.internalMailEncUserGroupInfoSessionKey = encryptKey(internalMailGroupKey, externalUserGroupInfoSessionKey)\n\t\t\t\t\t\td.externalUserEncMailGroupKey = encryptKey(externalUserGroupKey, externalMailGroupKey)\n\t\t\t\t\t\td.externalMailEncMailGroupInfoSessionKey = encryptKey(externalMailGroupKey, externalMailGroupInfoSessionKey)\n\t\t\t\t\t\td.internalMailEncMailGroupInfoSessionKey = encryptKey(internalMailGroupKey, externalMailGroupInfoSessionKey)\n\t\t\t\t\t\td.externalUserEncEntropy = userEncEntropy\n\t\t\t\t\t\td.externalUserEncTutanotaPropertiesSessionKey = encryptKey(externalUserGroupKey, tutanotaPropertiesSessionKey)\n\t\t\t\t\t\td.externalMailEncMailBoxSessionKey = encryptKey(externalMailGroupKey, mailboxSessionKey)\n\t\t\t\t\t\tlet userGroupData = createCreateExternalUserGroupData()\n\t\t\t\t\t\tuserGroupData.mailAddress = cleanedMailAddress\n\t\t\t\t\t\tuserGroupData.externalPwEncUserGroupKey = encryptKey(externalUserPwKey, externalUserGroupKey)\n\t\t\t\t\t\tuserGroupData.internalUserEncUserGroupKey = encryptKey(this.userFacade.getUserGroupKey(), externalUserGroupKey)\n\t\t\t\t\t\td.userGroupData = userGroupData\n\t\t\t\t\t\treturn this.serviceExecutor.post(ExternalUserService, d).then(() => {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\texternalUserGroupKey: externalUserGroupKey,\n\t\t\t\t\t\t\t\texternalMailGroupKey: externalMailGroupKey,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t})\n\t}\n\n\tentityEventsReceived(data: EntityUpdate[]): Promise<void> {\n\t\treturn promiseMap(data, (update) => {\n\t\t\tif (\n\t\t\t\tthis.deferredDraftUpdate &&\n\t\t\t\tthis.deferredDraftId &&\n\t\t\t\tupdate.operation === OperationType.UPDATE &&\n\t\t\t\tisSameTypeRefByAttr(MailTypeRef, update.application, update.type) &&\n\t\t\t\tisSameId(this.deferredDraftId, [update.instanceListId, update.instanceId])\n\t\t\t) {\n\t\t\t\treturn this.entityClient.load(MailTypeRef, neverNull(this.deferredDraftId)).then((mail) => {\n\t\t\t\t\tlet deferredPromiseWrapper = neverNull(this.deferredDraftUpdate)\n\t\t\t\t\tthis.deferredDraftUpdate = null\n\t\t\t\t\tdeferredPromiseWrapper.resolve(mail)\n\t\t\t\t})\n\t\t\t}\n\t\t}).then(noOp)\n\t}\n\n\tphishingMarkersUpdateReceived(markers: Array<PhishingMarker>) {\n\t\tmarkers.forEach((marker) => {\n\t\t\tif (marker.status === PhishingMarkerStatus.INACTIVE) {\n\t\t\t\tthis.phishingMarkers.delete(marker.marker)\n\t\t\t} else {\n\t\t\t\tthis.phishingMarkers.add(marker.marker)\n\t\t\t}\n\t\t})\n\t}\n\n\tgetRecipientKeyData(mailAddress: string): Promise<PublicKeyReturn | null> {\n\t\treturn this.serviceExecutor\n\t\t\t.get(\n\t\t\t\tPublicKeyService,\n\t\t\t\tcreatePublicKeyData({\n\t\t\t\t\tmailAddress,\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.catch(ofClass(NotFoundError, () => null))\n\t}\n\n\t_getMailGroupIdForMailAddress(user: User, mailAddress: string): Promise<Id> {\n\t\treturn promiseFilter(getUserGroupMemberships(user, GroupType.Mail), (groupMembership) => {\n\t\t\treturn this.entityClient.load(GroupTypeRef, groupMembership.group).then((mailGroup) => {\n\t\t\t\tif (mailGroup.user == null) {\n\t\t\t\t\treturn this.entityClient.load(GroupInfoTypeRef, groupMembership.groupInfo).then((mailGroupInfo) => {\n\t\t\t\t\t\treturn contains(getEnabledMailAddressesForGroupInfo(mailGroupInfo), mailAddress)\n\t\t\t\t\t})\n\t\t\t\t} else if (isSameId(mailGroup.user, user._id)) {\n\t\t\t\t\treturn this.entityClient.load(GroupInfoTypeRef, user.userGroup.groupInfo).then((userGroupInfo) => {\n\t\t\t\t\t\treturn contains(getEnabledMailAddressesForGroupInfo(userGroupInfo), mailAddress)\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\t// not supported\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t})\n\t\t}).then((filteredMemberships) => {\n\t\t\tif (filteredMemberships.length === 1) {\n\t\t\t\treturn filteredMemberships[0].group\n\t\t\t} else {\n\t\t\t\tthrow new NotFoundError(\"group for mail address not found \" + mailAddress)\n\t\t\t}\n\t\t})\n\t}\n\n\tasync clearFolder(folderId: IdTuple) {\n\t\tconst deleteMailData = createDeleteMailData({\n\t\t\tfolder: folderId,\n\t\t})\n\t\tawait this.serviceExecutor.delete(MailService, deleteMailData)\n\t}\n\n\tasync unsubscribe(mailId: IdTuple, recipient: string, headers: string[]) {\n\t\tconst postData = createListUnsubscribeData({\n\t\t\tmail: mailId,\n\t\t\trecipient,\n\t\t\theaders: headers.join(\"\\n\"),\n\t\t})\n\t\tawait this.serviceExecutor.post(ListUnsubscribeService, postData)\n\t}\n}\n\nexport function phishingMarkerValue(type: ReportedMailFieldType, value: string): string {\n\treturn type + murmurHash(value.replace(/\\s/g, \"\"))\n}\n\nfunction parseUrl(link: string): URL | null {\n\ttry {\n\t\treturn new URL(link)\n\t} catch (e) {\n\t\treturn null\n\t}\n}\n\nfunction getUrlDomain(link: string): string | null {\n\tconst url = parseUrl(link)\n\treturn url && url.hostname\n}\n\nfunction recipientToDraftRecipient(recipient: PartialRecipient): DraftRecipient {\n\treturn createDraftRecipient({\n\t\tname: recipient.name ?? \"\",\n\t\tmailAddress: recipient.address,\n\t})\n}\n\nfunction recipientToEncryptedMailAddress(recipient: PartialRecipient): EncryptedMailAddress {\n\treturn createEncryptedMailAddress({\n\t\tname: recipient.name ?? \"\",\n\t\taddress: recipient.address,\n\t})\n}\n","import { assertWorkerOrNode } from \"../../../common/Env.js\"\nimport type { AlarmInfo, AlarmNotification, Group, PushIdentifier, RepeatRule, User, UserAlarmInfo } from \"../../../entities/sys/TypeRefs.js\"\nimport {\n\tAlarmServicePostTypeRef,\n\tcreateAlarmInfo,\n\tcreateAlarmNotification,\n\tcreateAlarmServicePost,\n\tcreateCalendarEventRef,\n\tcreateDateWrapper,\n\tcreateNotificationSessionKey,\n\tcreateRepeatRule,\n\tcreateUserAlarmInfo,\n\tGroupTypeRef,\n\tPushIdentifierTypeRef,\n\tUserAlarmInfoTypeRef,\n\tUserTypeRef,\n} from \"../../../entities/sys/TypeRefs.js\"\nimport {\n\tassertNotNull,\n\tdowncast,\n\tflat,\n\tflatMap,\n\tgetFromMap,\n\tgroupBy,\n\tgroupByAndMapUniquely,\n\tisNotNull,\n\tneverNull,\n\tofClass,\n\tpromiseMap,\n\tstringToUtf8Uint8Array,\n} from \"@tutao/tutanota-utils\"\nimport { CryptoFacade } from \"../../crypto/CryptoFacade.js\"\nimport { GroupType, OperationType } from \"../../../common/TutanotaConstants.js\"\nimport type { CalendarEvent, CalendarEventUidIndex, CalendarRepeatRule } from \"../../../entities/tutanota/TypeRefs.js\"\nimport {\n\tCalendarEventTypeRef,\n\tCalendarEventUidIndexTypeRef,\n\tCalendarGroupRootTypeRef,\n\tcreateCalendarDeleteData,\n\tcreateUserAreaGroupPostData,\n} from \"../../../entities/tutanota/TypeRefs.js\"\nimport { DefaultEntityRestCache } from \"../../rest/DefaultEntityRestCache.js\"\nimport { ConnectionError, NotAuthorizedError, NotFoundError, PayloadTooLargeError } from \"../../../common/error/RestError.js\"\nimport { EntityClient } from \"../../../common/EntityClient.js\"\nimport { elementIdPart, getLetId, getListId, isSameId, listIdPart, uint8arrayToCustomId } from \"../../../common/utils/EntityUtils.js\"\nimport { GroupManagementFacade } from \"./GroupManagementFacade.js\"\nimport { SetupMultipleError } from \"../../../common/error/SetupMultipleError.js\"\nimport { ImportError } from \"../../../common/error/ImportError.js\"\nimport { aes128RandomKey, encryptKey, sha256Hash } from \"@tutao/tutanota-crypto\"\nimport { InstanceMapper } from \"../../crypto/InstanceMapper.js\"\nimport { TutanotaError } from \"../../../common/error/TutanotaError.js\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { AlarmService } from \"../../../entities/sys/Services.js\"\nimport { CalendarService } from \"../../../entities/tutanota/Services.js\"\nimport { resolveTypeReference } from \"../../../common/EntityFunctions.js\"\nimport { UserFacade } from \"../UserFacade.js\"\nimport { isOfflineError } from \"../../../common/utils/ErrorCheckUtils.js\"\nimport { EncryptedAlarmNotification } from \"../../../../native/common/EncryptedAlarmNotification.js\"\nimport { NativePushFacade } from \"../../../../native/common/generatedipc/NativePushFacade.js\"\nimport { ExposedOperationProgressTracker, OperationId } from \"../../../main/OperationProgressTracker.js\"\nimport { InfoMessageHandler } from \"../../../../gui/InfoMessageHandler.js\"\n\nassertWorkerOrNode()\n\ntype AlarmNotificationsPerEvent = {\n\tevent: CalendarEvent\n\talarmInfoIds: IdTuple[]\n\talarmNotifications: AlarmNotification[]\n}\n\nexport class CalendarFacade {\n\t// visible for testing\n\treadonly entityClient: EntityClient\n\n\tconstructor(\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly groupManagementFacade: GroupManagementFacade,\n\t\t// We inject cache directly because we need to delete user from it for a hack\n\t\tprivate readonly entityRestCache: DefaultEntityRestCache,\n\t\tprivate readonly nativePushFacade: NativePushFacade,\n\t\tprivate readonly operationProgressTracker: ExposedOperationProgressTracker,\n\t\tprivate readonly instanceMapper: InstanceMapper,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly cryptoFacade: CryptoFacade,\n\t\tprivate readonly infoMessageHandler: InfoMessageHandler,\n\t) {\n\t\tthis.entityClient = new EntityClient(this.entityRestCache)\n\t}\n\n\tasync saveImportedCalendarEvents(\n\t\teventsWrapper: Array<{\n\t\t\tevent: CalendarEvent\n\t\t\talarms: Array<AlarmInfo>\n\t\t}>,\n\t\toperationId: OperationId,\n\t): Promise<void> {\n\t\t// it is safe to assume that all event uids are set here\n\t\treturn this.saveCalendarEvents(eventsWrapper, (percent) => this.operationProgressTracker.onProgress(operationId, percent))\n\t}\n\n\t/**\n\t * We try to create as many events as possible and only throw the error at the end.\n\t * If alarmNotifications are created for an event that will later fail to be created we ignore them.\n\t * This function does not perform any checks on the event so it should only be called internally when\n\t * we can be sure that those checks have already been performed.\n\t * @param eventsWrapper the events and alarmNotifications to be created.\n\t * @param onProgress\n\t */\n\tprivate async saveCalendarEvents(\n\t\teventsWrapper: Array<{\n\t\t\tevent: CalendarEvent\n\t\t\talarms: ReadonlyArray<AlarmInfo>\n\t\t}>,\n\t\tonProgress: (percent: number) => Promise<void>,\n\t): Promise<void> {\n\t\tlet currentProgress = 10\n\t\tawait onProgress(currentProgress)\n\n\t\tfor (const { event } of eventsWrapper) {\n\t\t\tevent.hashedUid = hashUid(assertNotNull(event.uid, \"tried to save calendar event without uid.\"))\n\t\t}\n\n\t\tconst user = this.userFacade.getLoggedInUser()\n\n\t\tconst numEvents = eventsWrapper.length\n\t\tlet eventsWithAlarms: Array<AlarmNotificationsPerEvent>\n\t\ttry {\n\t\t\teventsWithAlarms = await this.saveMultipleAlarms(user, eventsWrapper)\n\t\t} catch (e) {\n\t\t\tif (e instanceof SetupMultipleError) {\n\t\t\t\tconsole.log(\"Saving alarms failed.\", e)\n\t\t\t\tthrow new ImportError(e.errors[0], \"Could not save alarms.\", numEvents)\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t\teventsWithAlarms.forEach(({ event, alarmInfoIds }) => (event.alarmInfos = alarmInfoIds))\n\t\tcurrentProgress = 33\n\t\tawait onProgress(currentProgress)\n\t\tconst eventsWithAlarmsByEventListId = groupBy(eventsWithAlarms, (eventWrapper) => getListId(eventWrapper.event))\n\t\tlet collectedAlarmNotifications: AlarmNotification[] = []\n\t\t//we have different lists for short and long events so this is 1 or 2\n\t\tconst size = eventsWithAlarmsByEventListId.size\n\t\tlet failed = 0\n\t\tlet errors = [] as Array<TutanotaError>\n\n\t\tfor (const [listId, eventsWithAlarmsOfOneList] of eventsWithAlarmsByEventListId) {\n\t\t\tlet successfulEvents = eventsWithAlarmsOfOneList\n\t\t\tawait this.entityClient\n\t\t\t\t.setupMultipleEntities(\n\t\t\t\t\tlistId,\n\t\t\t\t\teventsWithAlarmsOfOneList.map((e) => e.event),\n\t\t\t\t)\n\t\t\t\t.catch(\n\t\t\t\t\tofClass(SetupMultipleError, (e) => {\n\t\t\t\t\t\tfailed += e.failedInstances.length\n\t\t\t\t\t\terrors = errors.concat(e.errors)\n\t\t\t\t\t\tconsole.log(e.errors)\n\t\t\t\t\t\tsuccessfulEvents = eventsWithAlarmsOfOneList.filter(({ event }) => !e.failedInstances.includes(event))\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\tconst allAlarmNotificationsOfListId = flat(successfulEvents.map((event) => event.alarmNotifications))\n\t\t\tcollectedAlarmNotifications = collectedAlarmNotifications.concat(allAlarmNotificationsOfListId)\n\t\t\tcurrentProgress += Math.floor(56 / size)\n\t\t\tawait onProgress(currentProgress)\n\t\t}\n\n\t\tconst pushIdentifierList = await this.entityClient.loadAll(PushIdentifierTypeRef, neverNull(this.userFacade.getLoggedInUser().pushIdentifierList).list)\n\n\t\tif (collectedAlarmNotifications.length > 0 && pushIdentifierList.length > 0) {\n\t\t\tawait this.sendAlarmNotifications(collectedAlarmNotifications, pushIdentifierList)\n\t\t}\n\n\t\tawait onProgress(100)\n\n\t\tif (failed !== 0) {\n\t\t\tif (errors.some(isOfflineError)) {\n\t\t\t\t//In this case the user will not be informed about the number of failed events. We considered this is okay because it is not actionable anyways.\n\t\t\t\tthrow new ConnectionError(\"Connection lost while saving events\")\n\t\t\t} else {\n\t\t\t\tconsole.log(\"Could not save events. Number of failed imports: \", failed)\n\t\t\t\tthrow new ImportError(errors[0], \"Could not save events.\", failed)\n\t\t\t}\n\t\t}\n\t}\n\n\tasync saveCalendarEvent(event: CalendarEvent, alarmInfos: ReadonlyArray<AlarmInfo>, oldEvent: CalendarEvent | null): Promise<void> {\n\t\tif (event._id == null) throw new Error(\"No id set on the event\")\n\t\tif (event._ownerGroup == null) throw new Error(\"No _ownerGroup is set on the event\")\n\t\tif (event.uid == null) throw new Error(\"no uid set on the event\")\n\t\tevent.hashedUid = hashUid(event.uid)\n\n\t\tif (oldEvent) {\n\t\t\tawait this.entityClient.erase(oldEvent).catch(ofClass(NotFoundError, () => console.log(\"could not delete old event when saving new one\")))\n\t\t}\n\n\t\treturn await this.saveCalendarEvents(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tevent,\n\t\t\t\t\talarms: alarmInfos,\n\t\t\t\t},\n\t\t\t],\n\t\t\t() => Promise.resolve(),\n\t\t)\n\t}\n\n\tasync updateCalendarEvent(event: CalendarEvent, newAlarms: ReadonlyArray<AlarmInfo>, existingEvent: CalendarEvent): Promise<void> {\n\t\tevent._id = existingEvent._id\n\t\tevent._ownerEncSessionKey = existingEvent._ownerEncSessionKey\n\t\tevent._permissions = existingEvent._permissions\n\t\tif (existingEvent.uid == null) throw new Error(\"no uid set on the existing event\")\n\t\tevent.uid = existingEvent.uid\n\t\tevent.hashedUid = hashUid(existingEvent.uid)\n\n\t\tconst user = this.userFacade.getLoggedInUser()\n\n\t\tconst userAlarmIdsWithAlarmNotificationsPerEvent = await this.saveMultipleAlarms(user, [\n\t\t\t{\n\t\t\t\tevent,\n\t\t\t\talarms: newAlarms,\n\t\t\t},\n\t\t])\n\t\tconst { alarmInfoIds, alarmNotifications } = userAlarmIdsWithAlarmNotificationsPerEvent[0]\n\t\tconst userAlarmInfoListId = neverNull(user.alarmInfoList).alarms\n\t\t// Remove all alarms which belongs to the current user. We need to be careful about other users' alarms.\n\t\t// Server takes care of the removed alarms,\n\t\tevent.alarmInfos = existingEvent.alarmInfos.filter((a) => !isSameId(listIdPart(a), userAlarmInfoListId)).concat(alarmInfoIds)\n\t\tawait this.entityClient.update(event)\n\n\t\tif (alarmNotifications.length > 0) {\n\t\t\tconst pushIdentifierList = await this.entityClient.loadAll(\n\t\t\t\tPushIdentifierTypeRef,\n\t\t\t\tneverNull(this.userFacade.getLoggedInUser().pushIdentifierList).list,\n\t\t\t)\n\t\t\tawait this.sendAlarmNotifications(alarmNotifications, pushIdentifierList)\n\t\t}\n\t}\n\n\tasync addCalendar(name: string): Promise<{ user: User; group: Group }> {\n\t\tconst groupData = await this.groupManagementFacade.generateUserAreaGroupData(name)\n\t\tconst postData = createUserAreaGroupPostData({\n\t\t\tgroupData,\n\t\t})\n\t\tconst returnData = await this.serviceExecutor.post(CalendarService, postData)\n\t\tconst group = await this.entityClient.load(GroupTypeRef, returnData.group)\n\t\t// remove the user from the cache before loading it again to make sure we get the latest version.\n\t\t// otherwise we might not see the new calendar in case it is created at login and the websocket is not connected yet\n\t\tconst userId = this.userFacade.getLoggedInUser()._id\n\n\t\tawait this.entityRestCache.deleteFromCacheIfExists(UserTypeRef, null, userId)\n\n\t\tconst user = await this.entityClient.load(UserTypeRef, userId)\n\t\tthis.userFacade.updateUser(user)\n\t\treturn {\n\t\t\tuser,\n\t\t\tgroup,\n\t\t}\n\t}\n\n\tasync deleteCalendar(groupRootId: Id): Promise<void> {\n\t\tawait this.serviceExecutor.delete(CalendarService, createCalendarDeleteData({ groupRootId }))\n\t}\n\n\tasync scheduleAlarmsForNewDevice(pushIdentifier: PushIdentifier): Promise<void> {\n\t\tconst user = this.userFacade.getLoggedInUser()\n\n\t\tconst eventsWithAlarmInfos = await this.loadAlarmEvents()\n\t\tconst alarmNotifications = flatMap(eventsWithAlarmInfos, ({ event, userAlarmInfos }) =>\n\t\t\tuserAlarmInfos.map((userAlarmInfo) => createAlarmNotificationForEvent(event, userAlarmInfo.alarmInfo, user._id)),\n\t\t)\n\t\t// Theoretically we don't need to encrypt anything if we are sending things locally but we use already encrypted data on the client\n\t\t// to store alarms securely.\n\t\tconst notificationKey = aes128RandomKey()\n\t\tawait this.encryptNotificationKeyForDevices(notificationKey, alarmNotifications, [pushIdentifier])\n\t\tconst requestEntity = createAlarmServicePost({\n\t\t\talarmNotifications,\n\t\t})\n\t\tconst AlarmServicePostTypeModel = await resolveTypeReference(AlarmServicePostTypeRef)\n\t\tconst encEntity = await this.instanceMapper.encryptAndMapToLiteral(AlarmServicePostTypeModel, requestEntity, notificationKey)\n\t\tconst encryptedAlarms: EncryptedAlarmNotification[] = downcast(encEntity).alarmNotifications\n\t\tawait this.nativePushFacade.scheduleAlarms(encryptedAlarms)\n\t}\n\n\t/**\n\t * Load all events that have an alarm assigned.\n\t * @return: Map from concatenated ListId of an event to list of UserAlarmInfos for that event\n\t */\n\tasync loadAlarmEvents(): Promise<Array<EventWithAlarmInfos>> {\n\t\tconst alarmInfoList = this.userFacade.getLoggedInUser().alarmInfoList\n\n\t\tif (!alarmInfoList) {\n\t\t\tconsole.warn(\"No alarmInfo list on user\")\n\t\t\treturn []\n\t\t}\n\n\t\tconst userAlarmInfos = await this.entityClient.loadAll(UserAlarmInfoTypeRef, alarmInfoList.alarms)\n\t\t// Group referenced event ids by list id so we can load events of one list in one request.\n\t\tconst listIdToElementIds = groupByAndMapUniquely(\n\t\t\tuserAlarmInfos,\n\t\t\t(userAlarmInfo) => userAlarmInfo.alarmInfo.calendarRef.listId,\n\t\t\t(userAlarmInfo) => userAlarmInfo.alarmInfo.calendarRef.elementId,\n\t\t)\n\t\t// we group by the full concatenated list id\n\t\t// because there might be collisions between event element ids due to being custom ids\n\t\tconst eventIdToAlarmInfos = groupBy(userAlarmInfos, (userAlarmInfo) => getEventIdFromUserAlarmInfo(userAlarmInfo).join(\"\"))\n\t\tconst calendarEvents = await promiseMap(listIdToElementIds.entries(), ([listId, elementIds]) => {\n\t\t\treturn this.entityClient.loadMultiple(CalendarEventTypeRef, listId, Array.from(elementIds)).catch((error) => {\n\t\t\t\t// handle NotAuthorized here because user could have been removed from group.\n\t\t\t\tif (error instanceof NotAuthorizedError) {\n\t\t\t\t\tconsole.warn(\"NotAuthorized when downloading alarm events\", error)\n\t\t\t\t\treturn []\n\t\t\t\t}\n\n\t\t\t\tthrow error\n\t\t\t})\n\t\t})\n\t\treturn flat(calendarEvents).map((event) => {\n\t\t\treturn {\n\t\t\t\tevent,\n\t\t\t\tuserAlarmInfos: getFromMap(eventIdToAlarmInfos, getLetId(event).join(\"\"), () => []),\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Queries the event using the uid index. The index is stored per calendar so we have to go through all calendars to find matching event.\n\t * We currently only need this for calendar event updates and for that we don't want to look into shared calendars.\n\t */\n\tasync getEventByUid(uid: string): Promise<CalendarEvent | null> {\n\t\tconst calendarMemberships = this.userFacade.getLoggedInUser().memberships.filter((m) => m.groupType === GroupType.Calendar && m.capability == null)\n\t\tfor (const membership of calendarMemberships) {\n\t\t\tlet indexEntry\n\t\t\ttry {\n\t\t\t\tconst groupRoot = await this.entityClient.load(CalendarGroupRootTypeRef, membership.group)\n\t\t\t\tif (groupRoot.index == null) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tindexEntry = await this.entityClient.load<CalendarEventUidIndex>(CalendarEventUidIndexTypeRef, [\n\t\t\t\t\tgroupRoot.index.list,\n\t\t\t\t\tuint8arrayToCustomId(hashUid(uid)),\n\t\t\t\t])\n\t\t\t\treturn await this.entityClient.load<CalendarEvent>(CalendarEventTypeRef, indexEntry.calendarEvent)\n\t\t\t} catch (e) {\n\t\t\t\tif (e instanceof NotFoundError || e instanceof NotAuthorizedError) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n\n\tprivate async sendAlarmNotifications(alarmNotifications: Array<AlarmNotification>, pushIdentifierList: Array<PushIdentifier>): Promise<void> {\n\t\tconst notificationSessionKey = aes128RandomKey()\n\t\treturn this.encryptNotificationKeyForDevices(notificationSessionKey, alarmNotifications, pushIdentifierList).then(async () => {\n\t\t\tconst requestEntity = createAlarmServicePost({\n\t\t\t\talarmNotifications,\n\t\t\t})\n\t\t\ttry {\n\t\t\t\tawait this.serviceExecutor.post(AlarmService, requestEntity, { sessionKey: notificationSessionKey })\n\t\t\t} catch (e) {\n\t\t\t\tif (e instanceof PayloadTooLargeError) {\n\t\t\t\t\treturn this.infoMessageHandler.onInfoMessage({\n\t\t\t\t\t\ttranslationKey: \"calendarAlarmsTooBigError_msg\",\n\t\t\t\t\t\targs: {},\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tthrow e\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate async encryptNotificationKeyForDevices(\n\t\tnotificationSessionKey: Aes128Key,\n\t\talarmNotifications: Array<AlarmNotification>,\n\t\tpushIdentifierList: Array<PushIdentifier>,\n\t): Promise<void> {\n\t\t// PushID SK ->* Notification SK -> alarm fields\n\t\tconst maybeEncSessionKeys = await promiseMap(pushIdentifierList, async (identifier) => {\n\t\t\tconst pushIdentifierSk = await this.cryptoFacade.resolveSessionKeyForInstance(identifier)\n\t\t\tif (pushIdentifierSk) {\n\t\t\t\tconst pushIdentifierSessionEncSessionKey = encryptKey(pushIdentifierSk, notificationSessionKey)\n\t\t\t\treturn {\n\t\t\t\t\tidentifierId: identifier._id,\n\t\t\t\t\tpushIdentifierSessionEncSessionKey,\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn null\n\t\t\t}\n\t\t}) // rate limiting against blocking while resolving session keys (neccessary)\n\t\tconst encSessionKeys = maybeEncSessionKeys.filter(isNotNull)\n\n\t\tfor (let notification of alarmNotifications) {\n\t\t\tnotification.notificationSessionKeys = encSessionKeys.map((esk) => {\n\t\t\t\treturn createNotificationSessionKey({\n\t\t\t\t\tpushIdentifier: esk.identifierId,\n\t\t\t\t\tpushIdentifierSessionEncSessionKey: esk.pushIdentifierSessionEncSessionKey,\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate async saveMultipleAlarms(\n\t\tuser: User,\n\t\teventsWrapper: Array<{\n\t\t\tevent: CalendarEvent\n\t\t\talarms: ReadonlyArray<AlarmInfo>\n\t\t}>,\n\t): Promise<Array<AlarmNotificationsPerEvent>> {\n\t\tconst userAlarmInfosAndNotificationsPerEvent: Array<{\n\t\t\tevent: CalendarEvent\n\t\t\tuserAlarmInfoAndNotification: Array<{\n\t\t\t\talarm: UserAlarmInfo\n\t\t\t\talarmNotification: AlarmNotification\n\t\t\t}>\n\t\t}> = []\n\t\tconst userAlarmInfoListId = neverNull(user.alarmInfoList).alarms\n\t\tconst ownerGroup = user.userGroup.group\n\n\t\tfor (const { event, alarms } of eventsWrapper) {\n\t\t\tconst userAlarmInfoAndNotification: Array<{\n\t\t\t\talarm: UserAlarmInfo\n\t\t\t\talarmNotification: AlarmNotification\n\t\t\t}> = []\n\t\t\tconst calendarRef = createCalendarEventRef({\n\t\t\t\tlistId: listIdPart(event._id),\n\t\t\t\telementId: elementIdPart(event._id),\n\t\t\t})\n\n\t\t\tfor (const alarmInfo of alarms) {\n\t\t\t\tconst userAlarmInfo = createUserAlarmInfo()\n\t\t\t\tuserAlarmInfo._ownerGroup = ownerGroup\n\t\t\t\tuserAlarmInfo.alarmInfo = createAlarmInfo()\n\t\t\t\tuserAlarmInfo.alarmInfo.alarmIdentifier = alarmInfo.alarmIdentifier\n\t\t\t\tuserAlarmInfo.alarmInfo.trigger = alarmInfo.trigger\n\t\t\t\tuserAlarmInfo.alarmInfo.calendarRef = calendarRef\n\t\t\t\tconst alarmNotification = createAlarmNotificationForEvent(event, userAlarmInfo.alarmInfo, user._id)\n\t\t\t\tuserAlarmInfoAndNotification.push({\n\t\t\t\t\talarm: userAlarmInfo,\n\t\t\t\t\talarmNotification,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tuserAlarmInfosAndNotificationsPerEvent.push({\n\t\t\t\tevent,\n\t\t\t\tuserAlarmInfoAndNotification,\n\t\t\t})\n\t\t}\n\n\t\tconst allAlarms = flat(\n\t\t\tuserAlarmInfosAndNotificationsPerEvent.map(({ userAlarmInfoAndNotification }) => userAlarmInfoAndNotification.map(({ alarm }) => alarm)),\n\t\t)\n\n\t\tconst alarmIds: Array<Id> = await this.entityClient.setupMultipleEntities(userAlarmInfoListId, allAlarms)\n\t\tlet currentIndex = 0\n\t\treturn userAlarmInfosAndNotificationsPerEvent.map(({ event, userAlarmInfoAndNotification }) => {\n\t\t\treturn {\n\t\t\t\tevent,\n\t\t\t\talarmInfoIds: userAlarmInfoAndNotification.map(() => [userAlarmInfoListId, alarmIds[currentIndex++]]),\n\t\t\t\talarmNotifications: userAlarmInfoAndNotification.map(({ alarmNotification }) => alarmNotification),\n\t\t\t}\n\t\t})\n\t}\n}\n\nexport type EventWithAlarmInfos = {\n\tevent: CalendarEvent\n\tuserAlarmInfos: Array<UserAlarmInfo>\n}\n\nfunction createAlarmNotificationForEvent(event: CalendarEvent, alarmInfo: AlarmInfo, userId: Id): AlarmNotification {\n\treturn createAlarmNotification({\n\t\talarmInfo: createAlarmInfoForAlarmInfo(alarmInfo),\n\t\trepeatRule: event.repeatRule && createRepeatRuleForCalendarRepeatRule(event.repeatRule),\n\t\tnotificationSessionKeys: [],\n\t\toperation: OperationType.CREATE,\n\t\tsummary: event.summary,\n\t\teventStart: event.startTime,\n\t\teventEnd: event.endTime,\n\t\tuser: userId,\n\t})\n}\n\nfunction createAlarmInfoForAlarmInfo(alarmInfo: AlarmInfo): AlarmInfo {\n\tconst calendarRef = Object.assign(createCalendarEventRef(), {\n\t\telementId: alarmInfo.calendarRef.elementId,\n\t\tlistId: alarmInfo.calendarRef.listId,\n\t})\n\treturn Object.assign(createAlarmInfo(), {\n\t\talarmIdentifier: alarmInfo.alarmIdentifier,\n\t\ttrigger: alarmInfo.trigger,\n\t\tcalendarRef,\n\t})\n}\n\nfunction createRepeatRuleForCalendarRepeatRule(calendarRepeatRule: CalendarRepeatRule): RepeatRule {\n\treturn Object.assign(createRepeatRule(), {\n\t\tendType: calendarRepeatRule.endType,\n\t\tendValue: calendarRepeatRule.endValue,\n\t\tfrequency: calendarRepeatRule.frequency,\n\t\tinterval: calendarRepeatRule.interval,\n\t\ttimeZone: calendarRepeatRule.timeZone,\n\t\texcludedDates: calendarRepeatRule.excludedDates.map(({ date }) => createDateWrapper({ date })),\n\t})\n}\n\nfunction getEventIdFromUserAlarmInfo(userAlarmInfo: UserAlarmInfo): IdTuple {\n\treturn [userAlarmInfo.alarmInfo.calendarRef.listId, userAlarmInfo.alarmInfo.calendarRef.elementId]\n}\n\n/** to make lookup on the encrypted event uid possible, we hash it and use that value as a key. */\nfunction hashUid(uid: string): Uint8Array {\n\treturn sha256Hash(stringToUtf8Uint8Array(uid))\n}\n","import type { GroupInfo, MailAddressAliasServiceReturn, MailAddressAvailability } from \"../../../entities/sys/TypeRefs.js\"\nimport {\n\tcreateDomainMailAddressAvailabilityData,\n\tcreateMailAddressAliasGetIn,\n\tcreateMailAddressAliasServiceData,\n\tcreateMailAddressAliasServiceDataDelete,\n\tcreateMultipleMailAddressAvailabilityData,\n\tcreateStringWrapper,\n\tGroupInfoTypeRef,\n\tGroupTypeRef,\n\tUserTypeRef,\n} from \"../../../entities/sys/TypeRefs.js\"\nimport { DomainMailAddressAvailabilityService, MailAddressAliasService, MultipleMailAddressAvailabilityService } from \"../../../entities/sys/Services.js\"\nimport { assertWorkerOrNode } from \"../../../common/Env.js\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { UserFacade } from \"../UserFacade.js\"\nimport { EntityClient } from \"../../../common/EntityClient.js\"\nimport {\n\tcreateMailAddressProperties,\n\tcreateMailboxProperties,\n\tMailboxGroupRoot,\n\tMailboxGroupRootTypeRef,\n\tMailboxProperties,\n\tMailboxPropertiesTypeRef,\n} from \"../../../entities/tutanota/TypeRefs.js\"\nimport { assertNotNull, findAndRemove, getFirstOrThrow, ofClass } from \"@tutao/tutanota-utils\"\nimport { getEnabledMailAddressesForGroupInfo } from \"../../../common/utils/GroupUtils.js\"\nimport { PreconditionFailedError } from \"../../../common/error/RestError.js\"\nimport { ProgrammingError } from \"../../../common/error/ProgrammingError.js\"\nimport { GroupManagementFacade } from \"./GroupManagementFacade.js\"\n\nassertWorkerOrNode()\n\nexport class MailAddressFacade {\n\tconstructor(\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly groupManagement: GroupManagementFacade,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly nonCachingEntityClient: EntityClient,\n\t) {}\n\n\tgetAliasCounters(userGroupId: Id): Promise<MailAddressAliasServiceReturn> {\n\t\tconst data = createMailAddressAliasGetIn({ targetGroup: userGroupId })\n\t\treturn this.serviceExecutor.get(MailAddressAliasService, data)\n\t}\n\n\tisMailAddressAvailable(mailAddress: string): Promise<boolean> {\n\t\tif (this.userFacade.isFullyLoggedIn()) {\n\t\t\tconst data = createDomainMailAddressAvailabilityData({ mailAddress })\n\t\t\treturn this.serviceExecutor.get(DomainMailAddressAvailabilityService, data).then((result) => result.available)\n\t\t} else {\n\t\t\treturn this.areMailAddressesAvailable([mailAddress]).then((result) => getFirstOrThrow(result).available)\n\t\t}\n\t}\n\n\tasync areMailAddressesAvailable(mailAddresses: string[]): Promise<MailAddressAvailability[]> {\n\t\tconst data = createMultipleMailAddressAvailabilityData({\n\t\t\tmailAddresses: mailAddresses.map((mailAddress) => createStringWrapper({ value: mailAddress })),\n\t\t})\n\t\tconst result = await this.serviceExecutor.get(MultipleMailAddressAvailabilityService, data)\n\t\treturn result.availabilities\n\t}\n\n\t/**\n\t * Add an {@param alias} to {@param targetGroupId}.\n\t * {@param targetGroupId} is *not* a Mail group, it is currently only a user group.\n\t *\n\t * Can only be done by an admin.\n\t */\n\tasync addMailAlias(targetGroupId: Id, alias: string): Promise<void> {\n\t\tconst data = createMailAddressAliasServiceData({\n\t\t\tgroup: targetGroupId,\n\t\t\tmailAddress: alias,\n\t\t})\n\t\tawait this.serviceExecutor.post(MailAddressAliasService, data)\n\t}\n\n\t/**\n\t * Enable/disable an {@param alias} on {@param targetGroupId}.\n\t * {@param targetGroupId} is *not* a Mail group, it is currently only a user group.\n\t *\n\t * {@param restore} means whether the alias will be enabled or disabled.\n\t *\n\t * Can only be done by an admin.\n\t */\n\tasync setMailAliasStatus(targetGroupId: Id, alias: string, restore: boolean): Promise<void> {\n\t\tconst deleteData = createMailAddressAliasServiceDataDelete({\n\t\t\tmailAddress: alias,\n\t\t\trestore,\n\t\t\tgroup: targetGroupId,\n\t\t})\n\t\tawait this.serviceExecutor.delete(MailAddressAliasService, deleteData)\n\t}\n\n\t/**\n\t * Get mailAddress to senderName mappings for mail group that the specified user is a member of.\n\t * if no user is given, the operation is attempted as an admin of the given group.\n\t * */\n\tasync getSenderNames(mailGroupId: Id, viaUser?: Id): Promise<Map<string, string>> {\n\t\tconst mailboxProperties = await this.getOrCreateMailboxProperties(mailGroupId, viaUser)\n\t\treturn this.collectSenderNames(mailboxProperties)\n\t}\n\n\t/**\n\t * Set mailAddress to senderName mapping for mail group that the specified user is a member of.\n\t * if no user is specified, the operation will be attempted as an admin of the given group.\n\t * */\n\tasync setSenderName(mailGroupId: Id, mailAddress: string, senderName: string, viaUser?: Id): Promise<Map<string, string>> {\n\t\tconst mailboxProperties = await this.getOrCreateMailboxProperties(mailGroupId, viaUser)\n\t\tlet mailAddressProperty = mailboxProperties.mailAddressProperties.find((p) => p.mailAddress === mailAddress)\n\t\tif (mailAddressProperty == null) {\n\t\t\tmailAddressProperty = createMailAddressProperties({ mailAddress })\n\t\t\tmailboxProperties.mailAddressProperties.push(mailAddressProperty)\n\t\t}\n\t\tmailAddressProperty.senderName = senderName\n\t\tconst updatedProperties = await this.updateMailboxProperties(mailboxProperties, viaUser)\n\t\treturn this.collectSenderNames(updatedProperties)\n\t}\n\n\t/**\n\t * remove the sender name of the given mail address.\n\t * If no user is given, the operation will be attempted as an admin of the group.\n\t */\n\tasync removeSenderName(mailGroupId: Id, mailAddress: string, viaUser: Id): Promise<Map<string, string>> {\n\t\tconst mailboxProperties = await this.getOrCreateMailboxProperties(mailGroupId, viaUser)\n\t\tfindAndRemove(mailboxProperties.mailAddressProperties, (p) => p.mailAddress === mailAddress)\n\t\tconst updatedProperties = await this.updateMailboxProperties(mailboxProperties, viaUser)\n\t\treturn this.collectSenderNames(updatedProperties)\n\t}\n\n\tprivate async getOrCreateMailboxProperties(mailGroupId: Id, viaUser?: Id): Promise<MailboxProperties> {\n\t\tconst groupKey = viaUser\n\t\t\t? await this.groupManagement.getGroupKeyViaUser(mailGroupId, viaUser)\n\t\t\t: await this.groupManagement.getGroupKeyViaAdminEncGKey(mailGroupId)\n\t\t// Using non-caching entityClient because we are not a member of the user's mail group and we won't receive updates for it\n\t\tconst mailboxGroupRoot = await this.nonCachingEntityClient.load(MailboxGroupRootTypeRef, mailGroupId)\n\t\tif (mailboxGroupRoot.mailboxProperties == null) {\n\t\t\tmailboxGroupRoot.mailboxProperties = await this.createMailboxProperties(mailboxGroupRoot, groupKey)\n\t\t}\n\t\tconst mailboxProperties = await this.nonCachingEntityClient.load(\n\t\t\tMailboxPropertiesTypeRef,\n\t\t\tmailboxGroupRoot.mailboxProperties,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tgroupKey,\n\t\t)\n\n\t\treturn mailboxProperties.mailAddressProperties.length === 0 ? this.mailboxPropertiesWithLegacySenderName(mailboxProperties, viaUser) : mailboxProperties\n\t}\n\n\t/**\n\t * set the legacy sender name (groupInfo.name) of the group on all assigned mail addresses.\n\t * if no user is given, the operation will be attempted as an admin of the group of the given mailboxProperties.\n\t * */\n\tprivate async mailboxPropertiesWithLegacySenderName(mailboxProperties: MailboxProperties, viaUser?: Id): Promise<MailboxProperties> {\n\t\tconst groupInfo = viaUser ? await this.loadUserGroupInfo(viaUser) : await this.loadMailGroupInfo(mailboxProperties._ownerGroup!)\n\t\tconst legacySenderName = groupInfo.name\n\t\tconst mailAddresses = getEnabledMailAddressesForGroupInfo(groupInfo)\n\t\tfor (const mailAddress of mailAddresses) {\n\t\t\tmailboxProperties.mailAddressProperties.push(\n\t\t\t\tcreateMailAddressProperties({\n\t\t\t\t\tmailAddress,\n\t\t\t\t\tsenderName: legacySenderName,\n\t\t\t\t}),\n\t\t\t)\n\t\t}\n\t\treturn this.updateMailboxProperties(mailboxProperties, viaUser)\n\t}\n\n\tprivate async loadUserGroupInfo(userId: Id): Promise<GroupInfo> {\n\t\tconst user = await this.nonCachingEntityClient.load(UserTypeRef, userId)\n\t\treturn await this.nonCachingEntityClient.load(GroupInfoTypeRef, user.userGroup.groupInfo)\n\t}\n\n\tprivate async loadMailGroupInfo(groupId: Id): Promise<GroupInfo> {\n\t\tconst group = await this.nonCachingEntityClient.load(GroupTypeRef, groupId)\n\t\treturn await this.nonCachingEntityClient.load(GroupInfoTypeRef, group.groupInfo)\n\t}\n\n\tprivate async createMailboxProperties(mailboxGroupRoot: MailboxGroupRoot, groupKey: Aes128Key): Promise<Id> {\n\t\t// Using non-caching entityClient because we are not a member of the user's mail group and we won't receive updates for it\n\t\tconst mailboxProperties = createMailboxProperties({\n\t\t\t_ownerGroup: mailboxGroupRoot._ownerGroup,\n\t\t\treportMovedMails: \"\",\n\t\t\tmailAddressProperties: [],\n\t\t})\n\t\treturn this.nonCachingEntityClient.setup(null, mailboxProperties, undefined, { ownerKey: groupKey }).catch(\n\t\t\tofClass(PreconditionFailedError, (e) => {\n\t\t\t\t// in admin case it is much harder to run into it because we use non-caching entityClient but it is still possible\n\t\t\t\tif (e.data && e.data.startsWith(\"exists:\")) {\n\t\t\t\t\tconst existingId = e.data.substring(\"exists:\".length)\n\t\t\t\t\tconsole.log(\"mailboxProperties already exists\", existingId)\n\t\t\t\t\treturn existingId\n\t\t\t\t} else {\n\t\t\t\t\tthrow new ProgrammingError(`Could not create mailboxProperties, precondition: ${e.data}`)\n\t\t\t\t}\n\t\t\t}),\n\t\t)\n\t}\n\n\tprivate async updateMailboxProperties(mailboxProperties: MailboxProperties, viaUser?: Id): Promise<MailboxProperties> {\n\t\tconst groupKey = viaUser\n\t\t\t? await this.groupManagement.getGroupKeyViaUser(assertNotNull(mailboxProperties._ownerGroup), viaUser)\n\t\t\t: await this.groupManagement.getGroupKeyViaAdminEncGKey(assertNotNull(mailboxProperties._ownerGroup))\n\t\tawait this.nonCachingEntityClient.update(mailboxProperties, groupKey)\n\t\treturn await this.nonCachingEntityClient.load(MailboxPropertiesTypeRef, mailboxProperties._id, undefined, undefined, groupKey)\n\t}\n\n\tprivate async collectSenderNames(mailboxProperties: MailboxProperties): Promise<Map<string, string>> {\n\t\tconst result = new Map<string, string>()\n\t\tfor (const data of mailboxProperties.mailAddressProperties) {\n\t\t\tresult.set(data.mailAddress, data.senderName)\n\t\t}\n\t\treturn result\n\t}\n}\n","import type { CryptoFacade } from \"../../crypto/CryptoFacade.js\"\nimport { encryptBytes, encryptString } from \"../../crypto/CryptoFacade.js\"\nimport { GroupInfoTypeRef } from \"../../../entities/sys/TypeRefs.js\"\nimport type { GroupInfo, ReceivedGroupInvitation } from \"../../../entities/sys/TypeRefs.js\"\nimport type { ShareCapability } from \"../../../common/TutanotaConstants.js\"\nimport type { GroupInvitationPostReturn } from \"../../../entities/tutanota/TypeRefs.js\"\nimport {\n\tcreateGroupInvitationDeleteData,\n\tcreateGroupInvitationPostData,\n\tcreateGroupInvitationPutData,\n\tcreateSharedGroupData,\n} from \"../../../entities/tutanota/TypeRefs.js\"\nimport { neverNull } from \"@tutao/tutanota-utils\"\nimport { RecipientsNotFoundError } from \"../../../common/error/RecipientsNotFoundError.js\"\nimport { assertWorkerOrNode } from \"../../../common/Env.js\"\nimport { aes128RandomKey, bitArrayToUint8Array, encryptKey, uint8ArrayToBitArray } from \"@tutao/tutanota-crypto\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { GroupInvitationService } from \"../../../entities/tutanota/Services.js\"\nimport { UserFacade } from \"../UserFacade.js\"\nimport { EntityClient } from \"../../../common/EntityClient.js\"\n\nassertWorkerOrNode()\n\nexport class ShareFacade {\n\tconstructor(\n\t\tprivate readonly userFacade: UserFacade,\n\t\tprivate readonly cryptoFacade: CryptoFacade,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly entityClient: EntityClient,\n\t) {}\n\n\tasync sendGroupInvitation(\n\t\tsharedGroupInfo: GroupInfo,\n\t\tsharedGroupName: string,\n\t\trecipientMailAddresses: Array<string>,\n\t\tshareCapability: ShareCapability,\n\t): Promise<GroupInvitationPostReturn> {\n\t\tconst sharedGroupKey = this.userFacade.getGroupKey(sharedGroupInfo.group)\n\t\tconst userGroupInfo = await this.entityClient.load(GroupInfoTypeRef, this.userFacade.getLoggedInUser().userGroup.groupInfo)\n\n\t\tconst userGroupInfoSessionKey = await this.cryptoFacade.resolveSessionKeyForInstance(userGroupInfo)\n\t\tconst sharedGroupInfoSessionKey = await this.cryptoFacade.resolveSessionKeyForInstance(sharedGroupInfo)\n\t\tconst bucketKey = aes128RandomKey()\n\t\tconst invitationSessionKey = aes128RandomKey()\n\t\tconst sharedGroupData = createSharedGroupData({\n\t\t\tsessionEncInviterName: encryptString(invitationSessionKey, userGroupInfo.name),\n\t\t\tsessionEncSharedGroupKey: encryptBytes(invitationSessionKey, bitArrayToUint8Array(sharedGroupKey)),\n\t\t\tsessionEncSharedGroupName: encryptString(invitationSessionKey, sharedGroupName),\n\t\t\tbucketEncInvitationSessionKey: encryptKey(bucketKey, invitationSessionKey),\n\t\t\tsharedGroupEncInviterGroupInfoKey: encryptKey(sharedGroupKey, neverNull(userGroupInfoSessionKey)),\n\t\t\tsharedGroupEncSharedGroupInfoKey: encryptKey(sharedGroupKey, neverNull(sharedGroupInfoSessionKey)),\n\t\t\tcapability: shareCapability,\n\t\t\tsharedGroup: sharedGroupInfo.group,\n\t\t})\n\t\tconst invitationData = createGroupInvitationPostData({\n\t\t\tsharedGroupData,\n\t\t\tinternalKeyData: [],\n\t\t})\n\t\tconst notFoundRecipients: Array<string> = []\n\n\t\tfor (let mailAddress of recipientMailAddresses) {\n\t\t\tconst keyData = await this.cryptoFacade.encryptBucketKeyForInternalRecipient(bucketKey, mailAddress, notFoundRecipients)\n\n\t\t\tif (keyData) {\n\t\t\t\tinvitationData.internalKeyData.push(keyData)\n\t\t\t}\n\t\t}\n\n\t\tif (notFoundRecipients.length > 0) {\n\t\t\tthrow new RecipientsNotFoundError(notFoundRecipients.join(\"\\n\"))\n\t\t}\n\t\treturn this.serviceExecutor.post(GroupInvitationService, invitationData)\n\t}\n\n\tasync acceptGroupInvitation(invitation: ReceivedGroupInvitation): Promise<void> {\n\t\tconst userGroupInfo = await this.entityClient.load(GroupInfoTypeRef, this.userFacade.getLoggedInUser().userGroup.groupInfo)\n\t\tconst userGroupInfoSessionKey = await this.cryptoFacade.resolveSessionKeyForInstance(userGroupInfo)\n\t\tconst sharedGroupKey = uint8ArrayToBitArray(invitation.sharedGroupKey)\n\t\tconst serviceData = createGroupInvitationPutData({\n\t\t\treceivedInvitation: invitation._id,\n\t\t\tuserGroupEncGroupKey: encryptKey(this.userFacade.getUserGroupKey(), sharedGroupKey),\n\t\t\tsharedGroupEncInviteeGroupInfoKey: encryptKey(sharedGroupKey, neverNull(userGroupInfoSessionKey)),\n\t\t})\n\t\tawait this.serviceExecutor.put(GroupInvitationService, serviceData)\n\t}\n\n\tasync rejectGroupInvitation(receivedGroupInvitaitonId: IdTuple): Promise<void> {\n\t\tconst serviceData = createGroupInvitationDeleteData({\n\t\t\treceivedInvitation: receivedGroupInvitaitonId,\n\t\t})\n\t\tawait this.serviceExecutor.delete(GroupInvitationService, serviceData)\n\t}\n}\n","import { GroupType } from \"../../../common/TutanotaConstants.js\"\nimport {\n\tassertNotNull,\n\tBase64,\n\tbase64ExtToBase64,\n\tbase64ToBase64Ext,\n\tbase64ToBase64Url,\n\tbase64UrlToBase64,\n\tgetFirstOrThrow,\n\tuint8ArrayToBase64,\n} from \"@tutao/tutanota-utils\"\nimport type { GiftCardRedeemGetReturn } from \"../../../entities/sys/TypeRefs.js\"\nimport { createGiftCardCreateData, createGiftCardRedeemData, GiftCard } from \"../../../entities/sys/TypeRefs.js\"\nimport { aes128RandomKey, base64ToKey, bitArrayToUint8Array, encryptKey, sha256Hash } from \"@tutao/tutanota-crypto\"\nimport { IServiceExecutor } from \"../../../common/ServiceRequest.js\"\nimport { GiftCardRedeemService, GiftCardService } from \"../../../entities/sys/Services.js\"\nimport { elementIdPart, GENERATED_MAX_ID } from \"../../../common/utils/EntityUtils.js\"\nimport { CryptoFacade } from \"../../crypto/CryptoFacade.js\"\nimport { UserFacade } from \"../UserFacade.js\"\nimport { ProgrammingError } from \"../../../common/error/ProgrammingError.js\"\nimport { CustomerFacade } from \"./CustomerFacade.js\"\n\nconst ID_LENGTH = GENERATED_MAX_ID.length\nconst KEY_LENGTH_B64 = 24\n\nexport class GiftCardFacade {\n\tconstructor(\n\t\tprivate readonly user: UserFacade,\n\t\tprivate customer: CustomerFacade,\n\t\tprivate readonly serviceExecutor: IServiceExecutor,\n\t\tprivate readonly cryptoFacade: CryptoFacade,\n\t) {}\n\n\tasync generateGiftCard(message: string, value: NumberString): Promise<IdTuple> {\n\t\tconst adminGroupIds = this.user.getGroupIds(GroupType.Admin)\n\n\t\tif (adminGroupIds.length === 0) {\n\t\t\tthrow new Error(\"missing admin membership\")\n\t\t}\n\n\t\tconst ownerKey = this.user.getGroupKey(getFirstOrThrow(adminGroupIds)) // adminGroupKey\n\n\t\tconst sessionKey = aes128RandomKey()\n\t\tconst { giftCard } = await this.serviceExecutor.post(\n\t\t\tGiftCardService,\n\t\t\tcreateGiftCardCreateData({\n\t\t\t\tmessage: message,\n\t\t\t\tkeyHash: sha256Hash(bitArrayToUint8Array(sessionKey)),\n\t\t\t\tvalue,\n\t\t\t\townerEncSessionKey: encryptKey(ownerKey, sessionKey),\n\t\t\t}),\n\t\t\t{ sessionKey },\n\t\t)\n\n\t\treturn giftCard\n\t}\n\n\tgetGiftCardInfo(id: Id, key: string): Promise<GiftCardRedeemGetReturn> {\n\t\treturn this.serviceExecutor.get(\n\t\t\tGiftCardRedeemService,\n\t\t\tcreateGiftCardRedeemData({\n\t\t\t\tgiftCardInfo: id,\n\t\t\t\tkeyHash: sha256Hash(bitArrayToUint8Array(base64ToKey(key))),\n\t\t\t}),\n\t\t\t{\n\t\t\t\tsessionKey: base64ToKey(key),\n\t\t\t},\n\t\t)\n\t}\n\n\tasync redeemGiftCard(\n\t\tgiftCardInfoId: Id,\n\t\tkey: string,\n\t\t/** Country code to use if a free user is being upgraded to premium (required if accountType is free) */\n\t\tcountryCode: string,\n\t): Promise<void> {\n\t\tif ((await this.customer.loadAccountingInfo()).invoiceCountry == null && countryCode == null) {\n\t\t\tthrow new ProgrammingError(\"User must provide a country\")\n\t\t}\n\n\t\tawait this.serviceExecutor.post(\n\t\t\tGiftCardRedeemService,\n\t\t\tcreateGiftCardRedeemData({\n\t\t\t\tgiftCardInfo: giftCardInfoId,\n\t\t\t\tkeyHash: sha256Hash(bitArrayToUint8Array(base64ToKey(key))),\n\t\t\t\tcountryCode,\n\t\t\t}),\n\t\t)\n\t}\n\n\tasync encodeGiftCardToken(giftCard: GiftCard): Promise<string> {\n\t\tconst key = assertNotNull(await this.cryptoFacade.resolveSessionKeyForInstance(giftCard))\n\t\treturn this.encodeToken(elementIdPart(giftCard._id), bitArrayToUint8Array(key))\n\t}\n\n\tasync decodeGiftCardToken(token: string): Promise<{ id: Id; key: Base64 }> {\n\t\tconst id = base64ToBase64Ext(base64UrlToBase64(token.slice(0, ID_LENGTH)))\n\t\tconst key = base64UrlToBase64(token.slice(ID_LENGTH, token.length))\n\n\t\tif (id.length !== ID_LENGTH || key.length !== KEY_LENGTH_B64) {\n\t\t\tthrow new Error(\"invalid token\")\n\t\t}\n\n\t\treturn { id, key }\n\t}\n\n\tprivate encodeToken(id: Id, key: Uint8Array): Base64 {\n\t\tif (id.length !== ID_LENGTH) {\n\t\t\tthrow new Error(\"Invalid gift card params\")\n\t\t}\n\t\tconst keyBase64 = uint8ArrayToBase64(key)\n\t\tif (keyBase64.length != KEY_LENGTH_B64) {\n\t\t\tthrow new Error(\"Invalid gift card key\")\n\t\t}\n\n\t\tconst idPart = base64ToBase64Url(base64ExtToBase64(id))\n\t\tconst keyPart = base64ToBase64Url(keyBase64)\n\t\treturn idPart + keyPart\n\t}\n}\n","import { b64UserIdHash, DbFacade } from \"../../search/DbFacade.js\"\nimport { assertNotNull, LazyLoaded, stringToUtf8Uint8Array } from \"@tutao/tutanota-utils\"\nimport type { User } from \"../../../entities/sys/TypeRefs.js\"\nimport { ExternalImageRule } from \"../../../common/TutanotaConstants.js\"\nimport { aes256Decrypt, aes256Encrypt, aes256RandomKey, decrypt256Key, encrypt256Key, IV_BYTE_LENGTH, random } from \"@tutao/tutanota-crypto\"\nimport { UserFacade } from \"../UserFacade.js\"\nimport { Metadata, ObjectStoreName } from \"../../search/IndexTables.js\"\n\nconst VERSION: number = 1\nconst DB_KEY_PREFIX: string = \"ConfigStorage\"\nconst ExternalImageListOS: ObjectStoreName = \"ExternalAllowListOS\"\nconst MetaDataOS: ObjectStoreName = \"MetaDataOS\"\ntype EncryptionMetadata = {\n\treadonly key: Aes128Key\n\treadonly iv: Uint8Array\n}\ntype ConfigDb = {\n\treadonly db: DbFacade\n\treadonly metaData: EncryptionMetadata\n}\n\n/** @PublicForTesting */\nexport async function encryptItem(item: string, key: Aes128Key, iv: Uint8Array): Promise<Uint8Array> {\n\treturn aes256Encrypt(key, stringToUtf8Uint8Array(item), iv, true, false).slice(iv.length)\n}\n\n/**\n * A local configuration database that can be used as an alternative to DeviceConfig:\n * Ideal for cases where the configuration values should be stored encrypted,\n * Or when the configuration is a growing list or object, which would be unsuitable for localStorage\n * Or when the configuration is only required in the Worker\n */\nexport class ConfigurationDatabase {\n\treadonly db: LazyLoaded<ConfigDb>\n\n\tconstructor(userFacade: UserFacade, dbLoadFn: (arg0: User, arg1: Aes128Key) => Promise<ConfigDb> = loadConfigDb) {\n\t\tthis.db = new LazyLoaded(() => {\n\t\t\tconst user = assertNotNull(userFacade.getLoggedInUser())\n\t\t\tconst userGroupKey = userFacade.getUserGroupKey()\n\t\t\treturn dbLoadFn(user, userGroupKey)\n\t\t})\n\t}\n\n\tasync addExternalImageRule(address: string, rule: ExternalImageRule): Promise<void> {\n\t\tconst { db, metaData } = await this.db.getAsync()\n\t\tif (!db.indexingSupported) return\n\t\tconst encryptedAddress = await encryptItem(address, metaData.key, metaData.iv)\n\t\treturn this._addAddressToImageList(encryptedAddress, rule)\n\t}\n\n\tasync getExternalImageRule(address: string): Promise<ExternalImageRule> {\n\t\tconst { db, metaData } = await this.db.getAsync()\n\t\tif (!db.indexingSupported) return ExternalImageRule.None\n\t\tconst encryptedAddress = await encryptItem(address, metaData.key, metaData.iv)\n\t\tconst transaction = await db.createTransaction(true, [ExternalImageListOS])\n\t\tconst entry = await transaction.get(ExternalImageListOS, encryptedAddress)\n\t\tlet rule = ExternalImageRule.None\n\n\t\tif (entry != null) {\n\t\t\tif (entry.rule != null) {\n\t\t\t\trule = entry.rule\n\t\t\t} else {\n\t\t\t\t// No rule set from earlier version means Allow\n\t\t\t\tawait this._addAddressToImageList(encryptedAddress, ExternalImageRule.Allow)\n\t\t\t\trule = ExternalImageRule.Allow\n\t\t\t}\n\t\t}\n\n\t\treturn rule\n\t}\n\n\tasync _addAddressToImageList(encryptedAddress: Uint8Array, rule: ExternalImageRule): Promise<void> {\n\t\tconst { db } = await this.db.getAsync()\n\t\tconst transaction = await db.createTransaction(false, [ExternalImageListOS])\n\t\treturn transaction.put(ExternalImageListOS, null, {\n\t\t\taddress: encryptedAddress,\n\t\t\trule: rule,\n\t\t})\n\t}\n}\n\nasync function loadConfigDb(user: User, userGroupKey: Aes128Key): Promise<ConfigDb> {\n\tconst id = `${DB_KEY_PREFIX}_${b64UserIdHash(user)}`\n\tconst db = new DbFacade(VERSION, (event, db) => {\n\t\tdb.createObjectStore(MetaDataOS)\n\t\tdb.createObjectStore(ExternalImageListOS, {\n\t\t\tkeyPath: \"address\",\n\t\t})\n\t})\n\tconst metaData = (await loadEncryptionMetadata(db, id, userGroupKey)) || (await initializeDb(db, id, userGroupKey))\n\treturn {\n\t\tdb,\n\t\tmetaData,\n\t}\n}\n\n/**\n * Load the encryption key and iv from the db\n * @return { key, iv } or null if one or both don't exist\n */\nasync function loadEncryptionMetadata(db: DbFacade, id: string, userGroupKey: Aes128Key): Promise<EncryptionMetadata | null> {\n\tawait db.open(id)\n\tconst transaction = await db.createTransaction(true, [MetaDataOS])\n\tconst encDbKey = await transaction.get(MetaDataOS, Metadata.userEncDbKey)\n\tconst encDbIv = await transaction.get(MetaDataOS, Metadata.encDbIv)\n\n\tif (encDbKey == null || encDbIv == null) {\n\t\treturn null\n\t}\n\n\tconst key = decrypt256Key(userGroupKey, encDbKey)\n\tconst iv = aes256Decrypt(key, encDbIv, true, false)\n\treturn {\n\t\tkey,\n\t\tiv,\n\t}\n}\n\n/**\n * @caution This will clear any existing data in the database, because they key and IV will be regenerated\n * @return the newly generated key and iv for the database contents\n */\nasync function initializeDb(db: DbFacade, id: string, userGroupKey: Aes128Key): Promise<EncryptionMetadata> {\n\tawait db.deleteDatabase().then(() => db.open(id))\n\tconst key = aes256RandomKey()\n\tconst iv = random.generateRandomData(IV_BYTE_LENGTH)\n\tconst transaction = await db.createTransaction(false, [MetaDataOS, ExternalImageListOS])\n\tawait transaction.put(MetaDataOS, Metadata.userEncDbKey, encrypt256Key(userGroupKey, key))\n\tawait transaction.put(MetaDataOS, Metadata.encDbIv, aes256Encrypt(key, iv, random.generateRandomData(IV_BYTE_LENGTH), true, false))\n\treturn {\n\t\tkey,\n\t\tiv,\n\t}\n}\n","import { HttpMethod, MediaType, resolveTypeReference } from \"../../../common/EntityFunctions.js\"\nimport { typeRefToPath } from \"../../rest/EntityRestClient.js\"\nimport type { ContactForm } from \"../../../entities/tutanota/TypeRefs.js\"\nimport { ContactFormTypeRef } from \"../../../entities/tutanota/TypeRefs.js\"\nimport { RestClient } from \"../../rest/RestClient.js\"\nimport { assertWorkerOrNode } from \"../../../common/Env.js\"\nimport { InstanceMapper } from \"../../crypto/InstanceMapper.js\"\n\nassertWorkerOrNode()\n\nexport class ContactFormFacade {\n\tconstructor(private readonly restClient: RestClient, private readonly instanceMapper: InstanceMapper) {}\n\n\tasync loadContactForm(formId: string): Promise<ContactForm> {\n\t\tconst model = await resolveTypeReference(ContactFormTypeRef)\n\t\tconst path = typeRefToPath(ContactFormTypeRef)\n\t\tconst json = await this.restClient.request(path + \"/\" + formId, HttpMethod.GET, { headers: { v: model.version }, responseType: MediaType.Json })\n\t\tconst data = JSON.parse(json as string)\n\t\treturn this.instanceMapper.decryptAndMapToInstance(model, data, null)\n\t}\n}\n"],"names":["assertWorkerOrNode","constructor","serviceExecutor","this","getPrice","type","count","reactivate","priceRequestData","createPriceRequestData","featureType","String","paymentInterval","accountType","business","serviceData","createPriceServiceData","date","Const","CURRENT_DATE","priceRequest","get","PriceService","getCurrentPrice","getPriceItem","priceData","_a","neverNull","items","find","p","async","counterType","rowName","columnName","counterData","createReadCounterData","counterReturn","CounterService","Number","counterValues","value","customerId","user","counters","entityClient","rsa","group","readCounterValue","CounterType","UserStorageLegacy","customer","_id","name","mailAddress","adminGroupIds","getGroupIds","GroupType","Admin","length","LocalAdmin","adminGroupKey","getGroupKey","customerGroupKey","getGroupId","Customer","mailGroupKey","aes128RandomKey","mailGroupInfoSessionKey","mailboxSessionKey","keyPair","generateKey","mailGroupData","generateInternalGroupData","data","createCreateMailGroupData","encryptedName","encryptString","mailEncMailboxSessionKey","encryptKey","groupData","post","MailGroupService","generateUserAreaGroupData","load","GroupTypeRef","getUserGroupId","then","userGroup","adminGroupId","admin","getAllGroupIds","indexOf","userGroupKey","getUserGroupKey","groupRootSessionKey","groupInfoSessionKey","groupKey","createUserAreaGroupData","groupEncGroupRootSessionKey","customerEncGroupInfoSessionKey","userEncGroupKey","groupInfoEncName","adminEncGroupKey","adminGroup","createTemplateGroup","createUserAreaGroupPostData","TemplateGroupService","returnValue","ownerGroupKey","createInternalGroupData","publicKey","hexToUint8Array","publicKeyToHex","groupEncPrivateKey","encryptRsaKey","privateKey","ownerEncGroupInfoSessionKey","groupId","getGroupKeyViaAdminEncGKey","createMembershipAddData","symEncGKey","MembershipService","userId","createMembershipRemoveData","delete","restore","createDeleteGroupData","Mail","Error","LocalAdminGroupService","viaUser","UserTypeRef","ship","memberships","m","decryptKey","hasGroup","Promise","resolve","adminGroupEncGKey","ProgrammingError","assertNotNull","globalAdminGroupId","globalAdminGroupKey","localAdminGroup","userFacade","groupManagement","operationProgressTracker","newPassword","salt","generateRandomSalt","passwordKey","generateKeyFromPassphrase","KeyLength","b128","pwEncUserGroupKey","passwordVerifier","createAuthVerifier","createResetPasswordData","verifier","ResetPasswordService","addUserToGroup","AccountType","SYSTEM","keyData","_getAccountKeyData","addAccountGroup","removeUserFromGroup","keysReturn","SystemKeysService","getLoggedInUser","PREMIUM","premiumGroup","premiumGroupKey","STARTER","starterGroupKey","newAdminGroupId","newAdminGroup","oldAdminGroup","newAdminGroupEncGKey","localAdminGroupKey","createUpdateAdminshipData","UpdateAdminshipService","counterValue","createUserDataDelete","UserService","_getGroupId","groupType","User","membership","createUser","password","userIndex","overallNbrOfUsersToCreate","operationId","userGroupInfoSessionKey","userGroupData","onProgress","createUserAccountCreateData","userData","generateUserAccountData","generateRecoveryCode","UserAccountService","userName","recoverData","userPassphraseKey","contactGroupKey","fileGroupKey","clientKey","contactListSessionKey","fileSystemSessionKey","contactGroupInfoSessionKey","fileGroupInfoSessionKey","tutanotaPropertiesSessionKey","userEncEntropy","encryptBytes","random","generateRandomData","createUserAccountUserData","userEncClientKey","userEncCustomerGroupKey","userEncMailGroupKey","userEncContactGroupKey","userEncFileGroupKey","userEncTutanotaPropertiesSessionKey","mailEncMailBoxSessionKey","contactEncContactListSessionKey","fileEncFileSystemSessionKey","customerEncMailGroupInfoSessionKey","customerEncContactGroupInfoSessionKey","customerEncFileGroupInfoSessionKey","userEncRecoverCode","recoverCodeEncUserGroupKey","recoverCodeVerifier","recoveryCodeVerifier","generateContactFormUserAccountData","createContactFormUserData","ownerEncMailGroupInfoSessionKey","recoveryCode","aes256RandomKey","encrypt256Key","aes256EncryptKey","hexCode","uint8ArrayToHex","bitArrayToUint8Array","getRecoverCode","recoverCodeId","auth","recoverCode","reject","key","extraHeaders","authVerifier","createAuthVerifierAsBase64Url","RecoverCodeTypeRef","undefined","result","decrypt256Key","createRecoveryCode","getUser","recoverPasswordEntity","createRecoverCode","_ownerGroup","pwKey","setup","userManagement","bookingFacade","cryptoFacade","contactFormUserGroupData","domainName","getCustomerId","baseString","trim","toLowerCase","hash","sha256Hash","stringToUtf8Uint8Array","slice","addDomain","createCustomDomainData","domain","CustomDomainService","mailGroupId","catchAllMailGroup","put","CustomerTypeRef","customerInfo","CustomerInfoTypeRef","existingBrandingDomain","getWhitelabelDomain","systemAdminPubKey","hexToPublicKey","sessionKey","systemAdminPubEncAccountingInfoSessionKey","encrypt","createBrandingDomainData","systemAdminPubEncSessionKey","BrandingDomainService","createBrandingDomainDeleteData","readAllCustomerCounterValues","reduce","sum","readAvailableCustomerStorage","includedStorage","includedStorageCapacity","promotionStorage","promotionStorageCapacity","availableStorage","Math","max","bookedStorage","price","currentStorageItem","currentPriceNextPeriod","BookingItemFeatureType","Storage","MEMORY_GB_FACTOR","cspId","serverProperties","groupEncSessionKey","createCreateCustomerServerPropertiesData","adminGroupEncSessionKey","CreateCustomerServerProperties","id","CustomerServerPropertiesTypeRef","addSpamRule","field","loadCustomerServerProperties","props","newListEntry","createEmailSenderListElement","hashedValue","uint8ArrayToBase64","emailSenderList","push","update","catch","ofClass","LockedError","noOp","editSpamRule","spamRule","index","findIndex","item","JSON","stringify","key1","key2","key3","keyPairs","authToken","registrationCode","currentLanguage","adminGroupInfoSessionKey","customerGroupInfoSessionKey","accountingInfoSessionKey","customerServerPropertiesSessionKey","adminGroupData","customerGroupData","createCustomerAccountCreateData","lang","code","userEncAdminGroupKey","adminEncAccountingInfoSessionKey","adminEncCustomerServerPropertiesSessionKey","CustomerAccountService","createContactFormUserGroupData","downcast","contactFormId","getContactFormUserGroupData","createContactFormAccountData","contactForm","ContactFormAccountService","membershipAddData","uint8ArrayToBitArray","membershipRemoveData","freeGroup","e","message","console","log","freeGroupKey","invoiceData","paymentData","confirmedInvoiceCountry","accountingInfo","AccountingInfoTypeRef","resolveSessionKeyForInstance","service","createPaymentDataServicePutData","toString","invoiceName","invoiceAddress","invoiceCountry","country","a","invoiceVatIdNo","vatNumber","paymentMethod","paymentMethodInfo","paymentToken","creditCardData","creditCard","confirmedCountry","PaymentDataService","newPaymentInterval","getByAbbreviation","updatePaymentData","formatNameAndAddress","invoiceNumber","createPdfInvoiceServiceData","PdfInvoiceService","returnData","_type","mimeType","size","byteLength","BLOB_SERVICE_REST_PATH","BlobService","app","authDataProvider","restClient","suspensionHandler","fileApp","aesApp","instanceMapper","blobAccessTokenFacade","archiveDataType","blobData","ownerGroupId","chunks","splitUint8ArrayInChunks","MAX_BLOB_SIZE_BYTES","doBlobRequestWithRetry","blobServerAccessInfo","requestWriteToken","promiseMap","chunk","encryptAndUploadChunk","evictWriteToken","fileUri","isApp","isDesktop","chunkUris","splitFile","chunkUri","encryptAndUploadNativeChunk","referencingInstance","entity","requestReadTokenBlobs","blobs","blob","downloadAndDecryptChunk","evictReadBlobsToken","concat","fileName","decryptedChunkFileUris","clear","downloadAndDecryptChunkNative","decryptedChunkFileUri","deleteFile","decryptedFileUri","joinFiles","getSize","location","tmpBlobFile","encryptedData","blobHash","queryParams","createQueryParams","BlobGetInTypeRef","tryServers","servers","serverUrl","response","request","body","responseType","baseUrl","parseBlobPostOutResponse","encryptedChunkUri","aesEncryptFile","uri","hashFile","uploadNative","isSuspended","deferRequest","serviceUrl","URL","fullUrl","addParamsToUrl","suspensionTime","responseBody","statusCode","errorId","precondition","upload","uint8ArrayToString","isSuspensionResponse","activateSuspensionIfInactive","handleRestError","jsonData","responseTypeModel","resolveTypeReference","BlobPostOutTypeRef","instance","parse","blobReferenceToken","decryptAndMapToInstance","createBlobReferenceTokenWrapper","archiveId","blobId","getData","createBlobGetIn","BlobGetInTypeModel","literalGetData","encryptAndMapToLiteral","noCORS","aes128Decrypt","_body","blobFilename","downloadNative","additionalParams","url","encryptedFileUri","download","decryptedFileUrl","aesDecryptFile","REST_PATH","clearFileData","file","requestData","createFileDataDataGet","base64","entityToSend","FileDataDataGetTypeRef","headers","createAuthHeaders","modelInfo","version","convertToDataFile","assert","env","mode","Mode","App","Desktop","downloadFileContentNative","FileDataDataGetTypModel","getApiOrigin","warn","filterInt","phishingMarkerValue","murmurHash","replace","parseUrl","link","getUrlDomain","hostname","recipientToDraftRecipient","recipient","createDraftRecipient","address","recipientToEncryptedMailAddress","createEncryptedMailAddress","fileFacade","crypto","blobFacade","phishingMarkers","Set","deferredDraftId","deferredDraftUpdate","parent","sk","newFolder","createCreateMailFolderData","folderName","parentFolder","ownerEncSessionKey","ownerGroup","MailFolderService","folder","newName","newParent","isSameId","updateFolder","createUpdateMailFolderData","subject","bodyText","senderMailAddress","senderName","toRecipients","ccRecipients","bccRecipients","conversationType","previousMessageId","attachments","confidential","replyTos","method","UNCOMPRESSED_MAX_SIZE","MailBodyTooLargeError","senderMailGroupId","_getMailGroupIdForMailAddress","createDraftCreateData","symEncSessionKey","draftData","createDraftData","compressedBodyText","map","addedAttachments","_createAddedAttachments","createDraftReturn","DraftService","MailTypeRef","draft","currentAttachments","getAttachmentIds","getReplyTos","_ownerEncSessionKey","createDraftUpdateData","removedAttachments","_getRemovedAttachments","defer","deferredUpdatePromiseWrapper","promise","mails","targetFolder","MoveMailService","createMoveMailData","mail","reportType","mailSessionKey","postData","createReportMailPostData","mailId","ReportMailService","deleteMailData","createDeleteMailData","MailService","providedFiles","existingFileIds","removedAttachmentIds","forEach","fileId","attachment","getLetId","providedFile","isDataFile","fileSessionKey","referenceTokens","writeDataFile","encryptAndUploadNative","encryptAndUpload","createAndEncryptDraftAttachment","isFileReference","containsId","createDraftAttachment","existingFile","ownerEncFileSessionKey","filter","isNotNull","it","newAttachmentData","createNewDraftAttachment","encFileName","encMimeType","fileData","encCid","cid","newFile","recipients","language","sender","bucketKey","sendDraftData","createSendDraftData","FileTypeRef","createAttachmentKeyData","bucketEncFileSessionKey","keyToUint8Array","attachmentKeyData","all","loadRoot","TutanotaPropertiesTypeRef","tutanotaProperties","plaintext","sendPlaintextOnly","mailSessionkey","calendarMethod","bucketEncMailSessionKey","some","r","getContactPassword","contact","senderNameUnencrypted","_addRecipientKeyData","SendDraftService","isLegacyMail","MailDetailsDraftTypeRef","mailDetailsDraft","details","checkMailForPhishing","links","score","senderAddress","authStatus","MailAuthStatus","AUTHENTICATED","_checkFieldForPhishing","ReportedMailFieldType","FROM_ADDRESS","senderDomain","addressDomain","FROM_DOMAIN","FROM_ADDRESS_NON_AUTH","FROM_DOMAIN_NON_AUTH","SUBJECT","LINK","href","LINK_DOMAIN","innerHTML","textUrl","htmlToText","hrefUrl","deleteMailFolderData","createDeleteMailFolderData","folders","listId","unreadMails","createWriteCounterData","UnreadMails","row","column","has","notFoundRecipients","externalGroupKeys","_getExternalGroupKey","createSecureExternalRecipientKeyData","symEncBucketKey","ownerEncBucketKey","externalMailGroupKey","saltHash","pwEncCommunicationKey","externalUserGroupKey","secureExternalRecipientKeyData","encryptBucketKeyForInternalRecipient","internalRecipientKeyData","RecipientsNotFoundError","join","presharedPassword","autoTransmitPassword","_b","recipientMailAddress","externalUserPwKey","GroupRootTypeRef","groupRoot","cleanedMailAddress","toLocaleLowerCase","mailAddressId","stringToCustomId","ExternalUserReferenceTypeRef","externalUserReferences","externalUserReference","externalUser","externalMailGroup","externalUserGroup","NotFoundError","internalMailGroupKey","externalUserGroupInfoSessionKey","externalMailGroupInfoSessionKey","d","createExternalUserData","externalUserEncUserGroupInfoSessionKey","internalMailEncUserGroupInfoSessionKey","externalUserEncMailGroupKey","externalMailEncMailGroupInfoSessionKey","internalMailEncMailGroupInfoSessionKey","externalUserEncEntropy","externalUserEncTutanotaPropertiesSessionKey","externalMailEncMailBoxSessionKey","createCreateExternalUserGroupData","externalPwEncUserGroupKey","internalUserEncUserGroupKey","ExternalUserService","entityEventsReceived","operation","isSameTypeRefByAttr","application","instanceListId","instanceId","deferredPromiseWrapper","phishingMarkersUpdateReceived","markers","marker","status","add","getRecipientKeyData","PublicKeyService","createPublicKeyData","promiseFilter","getUserGroupMemberships","groupMembership","mailGroup","GroupInfoTypeRef","groupInfo","mailGroupInfo","contains","getEnabledMailAddressesForGroupInfo","userGroupInfo","filteredMemberships","folderId","createListUnsubscribeData","ListUnsubscribeService","createAlarmNotificationForEvent","event","alarmInfo","createAlarmNotification","createAlarmInfoForAlarmInfo","repeatRule","calendarRepeatRule","Object","assign","createRepeatRule","endType","endValue","frequency","interval","timeZone","excludedDates","createDateWrapper","notificationSessionKeys","summary","eventStart","startTime","eventEnd","endTime","calendarRef","createCalendarEventRef","elementId","createAlarmInfo","alarmIdentifier","trigger","hashUid","uid","groupManagementFacade","entityRestCache","nativePushFacade","infoMessageHandler","EntityClient","eventsWrapper","saveCalendarEvents","percent","currentProgress","hashedUid","numEvents","eventsWithAlarms","saveMultipleAlarms","SetupMultipleError","ImportError","errors","alarmInfoIds","alarmInfos","eventsWithAlarmsByEventListId","groupBy","eventWrapper","getListId","collectedAlarmNotifications","failed","eventsWithAlarmsOfOneList","successfulEvents","setupMultipleEntities","failedInstances","includes","allAlarmNotificationsOfListId","flat","alarmNotifications","floor","pushIdentifierList","loadAll","PushIdentifierTypeRef","list","sendAlarmNotifications","isOfflineError","ConnectionError","oldEvent","erase","alarms","newAlarms","existingEvent","_permissions","userAlarmIdsWithAlarmNotificationsPerEvent","userAlarmInfoListId","alarmInfoList","listIdPart","CalendarService","deleteFromCacheIfExists","updateUser","groupRootId","createCalendarDeleteData","pushIdentifier","eventsWithAlarmInfos","loadAlarmEvents","flatMap","userAlarmInfos","userAlarmInfo","notificationKey","encryptNotificationKeyForDevices","requestEntity","createAlarmServicePost","AlarmServicePostTypeModel","AlarmServicePostTypeRef","encEntity","encryptedAlarms","scheduleAlarms","UserAlarmInfoTypeRef","listIdToElementIds","groupByAndMapUniquely","eventIdToAlarmInfos","getEventIdFromUserAlarmInfo","calendarEvents","entries","elementIds","loadMultiple","CalendarEventTypeRef","Array","from","error","NotAuthorizedError","getFromMap","calendarMemberships","Calendar","capability","indexEntry","CalendarGroupRootTypeRef","CalendarEventUidIndexTypeRef","uint8arrayToCustomId","calendarEvent","notificationSessionKey","AlarmService","PayloadTooLargeError","onInfoMessage","translationKey","args","encSessionKeys","identifier","pushIdentifierSk","pushIdentifierSessionEncSessionKey","identifierId","notification","esk","createNotificationSessionKey","userAlarmInfosAndNotificationsPerEvent","userAlarmInfoAndNotification","elementIdPart","createUserAlarmInfo","alarmNotification","alarm","allAlarms","alarmIds","currentIndex","nonCachingEntityClient","getAliasCounters","userGroupId","createMailAddressAliasGetIn","targetGroup","MailAddressAliasService","isMailAddressAvailable","isFullyLoggedIn","createDomainMailAddressAvailabilityData","DomainMailAddressAvailabilityService","available","areMailAddressesAvailable","getFirstOrThrow","mailAddresses","createMultipleMailAddressAvailabilityData","createStringWrapper","MultipleMailAddressAvailabilityService","availabilities","targetGroupId","alias","createMailAddressAliasServiceData","deleteData","createMailAddressAliasServiceDataDelete","mailboxProperties","getOrCreateMailboxProperties","collectSenderNames","mailAddressProperty","mailAddressProperties","createMailAddressProperties","updatedProperties","updateMailboxProperties","findAndRemove","getGroupKeyViaUser","mailboxGroupRoot","MailboxGroupRootTypeRef","createMailboxProperties","MailboxPropertiesTypeRef","mailboxPropertiesWithLegacySenderName","loadUserGroupInfo","loadMailGroupInfo","legacySenderName","reportMovedMails","ownerKey","PreconditionFailedError","startsWith","existingId","substring","Map","set","sharedGroupInfo","sharedGroupName","recipientMailAddresses","shareCapability","sharedGroupKey","sharedGroupInfoSessionKey","invitationSessionKey","sharedGroupData","createSharedGroupData","sessionEncInviterName","sessionEncSharedGroupKey","sessionEncSharedGroupName","bucketEncInvitationSessionKey","sharedGroupEncInviterGroupInfoKey","sharedGroupEncSharedGroupInfoKey","sharedGroup","invitationData","createGroupInvitationPostData","internalKeyData","GroupInvitationService","invitation","createGroupInvitationPutData","receivedInvitation","userGroupEncGroupKey","sharedGroupEncInviteeGroupInfoKey","receivedGroupInvitaitonId","createGroupInvitationDeleteData","ID_LENGTH","GENERATED_MAX_ID","giftCard","GiftCardService","createGiftCardCreateData","keyHash","getGiftCardInfo","GiftCardRedeemService","createGiftCardRedeemData","giftCardInfo","base64ToKey","giftCardInfoId","countryCode","loadAccountingInfo","encodeToken","token","base64ToBase64Ext","base64UrlToBase64","keyBase64","base64ToBase64Url","base64ExtToBase64","ExternalImageListOS","MetaDataOS","encryptItem","iv","aes256Encrypt","loadConfigDb","b64UserIdHash","db","DbFacade","createObjectStore","keyPath","metaData","open","transaction","createTransaction","encDbKey","Metadata","userEncDbKey","encDbIv","aes256Decrypt","loadEncryptionMetadata","deleteDatabase","IV_BYTE_LENGTH","initializeDb","dbLoadFn","LazyLoaded","rule","getAsync","indexingSupported","encryptedAddress","_addAddressToImageList","entry","formId","model","ContactFormTypeRef","path","typeRefToPath","json","v"],"mappings":"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"}