@rapidmx/web-client 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -14
- package/apps/admin/_layout.tsx +2 -2
- package/apps/admin/audit-log/index.tsx +4 -4
- package/apps/admin/branding/index.tsx +3 -3
- package/apps/admin/data-requests/index.tsx +8 -8
- package/apps/admin/distribution-lists/[uid].tsx +3 -3
- package/apps/admin/distribution-lists/index.tsx +4 -4
- package/apps/admin/distribution-lists/new/index.tsx +5 -5
- package/apps/admin/domains/[uid].tsx +6 -6
- package/apps/admin/domains/index.tsx +4 -4
- package/apps/admin/domains/new/index.tsx +5 -5
- package/apps/admin/encryption-policy/index.tsx +1 -1
- package/apps/admin/escrow-scopes/[uid].tsx +7 -7
- package/apps/admin/escrow-scopes/index.tsx +4 -4
- package/apps/admin/escrow-scopes/new/index.tsx +5 -5
- package/apps/admin/index.tsx +6 -6
- package/apps/admin/ingest-queue/index.tsx +4 -4
- package/apps/admin/mailbox-policy/index.tsx +1 -1
- package/apps/admin/mailboxes/[uid].tsx +5 -5
- package/apps/admin/quarantine/index.tsx +5 -5
- package/apps/admin/retention-policy/index.tsx +3 -3
- package/apps/admin/signing-certificates/index.tsx +5 -5
- package/apps/admin/transport-rules/[uid].tsx +5 -5
- package/apps/admin/transport-rules/_transportRuleConfig.tsx +3 -3
- package/apps/admin/transport-rules/index.tsx +4 -4
- package/apps/admin/transport-rules/new/index.tsx +5 -5
- package/apps/escrow/_layout.tsx +1 -1
- package/apps/escrow/audit-log/index.tsx +4 -4
- package/apps/escrow/index.tsx +4 -4
- package/apps/escrow/matters/[uid].tsx +10 -10
- package/apps/escrow/matters/new/index.tsx +6 -6
- package/apps/shared/appearance/AppearanceForm.tsx +3 -3
- package/apps/shared/appearance/AppearanceHead.tsx +1 -1
- package/apps/shared/appearance/AppearanceProvider.tsx +3 -3
- package/apps/shared/appearance/appearanceCache.ts +1 -1
- package/apps/shared/appearance/appearanceContext.ts +1 -1
- package/apps/shared/appearance/theme.ts +1 -1
- package/apps/shared/auth/adminAccess.ts +1 -1
- package/apps/shared/calendar/bookingPlugin.ts +1 -1
- package/apps/shared/calendar/calendarLiveUpdates.ts +1 -1
- package/apps/shared/calendar/calendarReminders.ts +1 -1
- package/apps/shared/calendar/useCalendarReminders.ts +1 -1
- package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/PackagesTable.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/VersionsPanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +1 -1
- package/apps/shared/components/admin/diagnostics/format.ts +1 -1
- package/apps/shared/components/admin/diagnostics/logClient.ts +1 -1
- package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
- package/apps/shared/components/admin/elevation.ts +1 -1
- package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +3 -3
- package/apps/shared/components/admin/layout/AdminShell.tsx +8 -8
- package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +6 -6
- package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +10 -8
- package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +4 -4
- package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +1 -1
- package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +19 -14
- package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +5 -5
- package/apps/shared/components/admin/settings/BrandingForm.tsx +5 -5
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +5 -5
- package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +4 -4
- package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +2 -2
- package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +32 -27
- package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +4 -4
- package/apps/shared/components/admin/settings/PluginsManager.tsx +5 -5
- package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +5 -5
- package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +7 -7
- package/apps/shared/components/admin/setup/SetupWizard.tsx +28 -24
- package/apps/shared/components/admin/signOut.ts +1 -1
- package/apps/shared/components/admin/usePagedList.tsx +2 -2
- package/apps/shared/components/calendar/AppointmentQuickForm.tsx +3 -3
- package/apps/shared/components/calendar/CalendarListSidebar.tsx +3 -3
- package/apps/shared/components/calendar/DescriptionEditor.tsx +1 -1
- package/apps/shared/components/calendar/EventDescriptionView.tsx +1 -1
- package/apps/shared/components/calendar/EventDetails.tsx +16 -14
- package/apps/shared/components/calendar/EventEditor.tsx +54 -39
- package/apps/shared/components/calendar/EventExpandedForm.tsx +3 -3
- package/apps/shared/components/calendar/EventFormParts.tsx +2 -2
- package/apps/shared/components/calendar/EventModal.tsx +5 -5
- package/apps/shared/components/calendar/EventQuickForm.tsx +3 -3
- package/apps/shared/components/calendar/EventShell.tsx +1 -1
- package/apps/shared/components/calendar/FindATime.tsx +6 -4
- package/apps/shared/components/calendar/GuestInput.tsx +2 -2
- package/apps/shared/components/calendar/MonthView.tsx +2 -2
- package/apps/shared/components/calendar/QuickCreateFaces.tsx +1 -1
- package/apps/shared/components/calendar/QuickFaceFrame.tsx +1 -1
- package/apps/shared/components/calendar/RecurrenceEditor.tsx +2 -2
- package/apps/shared/components/calendar/RequestChangeForm.tsx +8 -6
- package/apps/shared/components/calendar/ResourcePicker.tsx +7 -5
- package/apps/shared/components/calendar/SplitDayView.tsx +1 -1
- package/apps/shared/components/calendar/TaskQuickForm.tsx +23 -18
- package/apps/shared/components/calendar/TimeGridView.tsx +5 -3
- package/apps/shared/components/calendar/allDay.ts +2 -2
- package/apps/shared/components/calendar/eventDialogFields.ts +3 -3
- package/apps/shared/components/calendar/eventForm.ts +3 -3
- package/apps/shared/components/calendar/eventFormat.ts +3 -3
- package/apps/shared/components/calendar/findTimeGrid.ts +2 -2
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +11 -9
- package/apps/shared/components/contacts/ContactDetailPane.tsx +4 -4
- package/apps/shared/components/contacts/ContactForm.tsx +27 -17
- package/apps/shared/components/contacts/ContactsSidebar.tsx +7 -5
- package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -5
- package/apps/shared/components/contacts/contactKeys.ts +3 -3
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +9 -7
- package/apps/shared/components/escrow/layout/EscrowShell.tsx +6 -6
- package/apps/shared/components/forms/StringListField.tsx +1 -1
- package/apps/shared/components/layout/AppShell.tsx +12 -10
- package/apps/shared/components/layout/BrandingChrome.tsx +1 -1
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +9 -9
- package/apps/shared/components/layout/MailboxProvisioning.tsx +8 -6
- package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +9 -9
- package/apps/shared/components/layout/ThemeSwitch.tsx +1 -1
- package/apps/shared/components/layout/UnlockPromptProvider.tsx +6 -6
- package/apps/shared/components/layout/UserMenu.tsx +6 -4
- package/apps/shared/components/mail/ConversationList.tsx +25 -10
- package/apps/shared/components/mail/ConversationThreadPane.tsx +33 -17
- package/apps/shared/components/mail/InviteCard.tsx +11 -9
- package/apps/shared/components/mail/LazyReadingPane.tsx +2 -2
- package/apps/shared/components/mail/MailAddress.tsx +1 -1
- package/apps/shared/components/mail/MailListToolbar.tsx +2 -2
- package/apps/shared/components/mail/MailSelectionBar.tsx +2 -2
- package/apps/shared/components/mail/MenuButton.tsx +1 -1
- package/apps/shared/components/mail/MessageDetailPane.tsx +69 -48
- package/apps/shared/components/mail/MoveToFolderDialog.tsx +8 -6
- package/apps/shared/components/mail/OutboxRowStatus.tsx +1 -1
- package/apps/shared/components/mail/PermanentDeleteDialog.tsx +2 -2
- package/apps/shared/components/mail/compose/ComposeContext.tsx +14 -3
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +34 -30
- package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +4 -4
- package/apps/shared/components/mail/compose/EmojiPicker.tsx +2 -2
- package/apps/shared/components/mail/compose/GifPicker.tsx +3 -3
- package/apps/shared/components/mail/compose/RecipientInput.tsx +1 -1
- package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +1 -1
- package/apps/shared/components/mail/compose/encryptionRequirement.ts +2 -2
- package/apps/shared/components/mail/compose/quotedBody.ts +5 -5
- package/apps/shared/components/mail/compose/recipients.ts +1 -1
- package/apps/shared/components/mail/invite/InviteRowChip.tsx +10 -14
- package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +11 -9
- package/apps/shared/components/mail/invite/InviteTimeline.tsx +1 -1
- package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +2 -2
- package/apps/shared/components/mail/invite/inviteFormat.ts +2 -2
- package/apps/shared/components/mail/invite/inviteStore.ts +14 -6
- package/apps/shared/components/mail/labelMenu.tsx +9 -7
- package/apps/shared/components/mail/layout/MailShell.tsx +4 -4
- package/apps/shared/components/mail/listPreferences.ts +2 -2
- package/apps/shared/components/mail/pinnedSigners.ts +45 -19
- package/apps/shared/components/mail/reading/EncryptedBody.tsx +2 -2
- package/apps/shared/components/mail/reading/MessageBody.tsx +6 -6
- package/apps/shared/components/mail/reading/MessageCard.tsx +3 -3
- package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +3 -3
- package/apps/shared/components/mail/reading/PendingMessageCard.tsx +1 -1
- package/apps/shared/components/mail/reading/bodyContent.ts +3 -3
- package/apps/shared/components/mail/reading/bodyHtml.ts +1 -1
- package/apps/shared/components/mail/reading/conversationPrint.ts +76 -0
- package/apps/shared/components/mail/reading/messageExport.ts +1 -1
- package/apps/shared/components/mail/reading/printMessage.ts +46 -9
- package/apps/shared/components/mail/reading/useMessageActions.ts +28 -21
- package/apps/shared/components/mail/unreadStyle.tsx +1 -1
- package/apps/shared/components/mail/verificationSeals.ts +16 -12
- package/apps/shared/components/mail/writableMailboxes.ts +14 -7
- package/apps/shared/components/rules/RuleBuilder.tsx +1 -1
- package/apps/shared/components/settings/SigningCertificateCard.tsx +4 -4
- package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
- package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
- package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
- package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
- package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
- package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
- package/apps/shared/keyboard/ShortcutsDialog.tsx +1 -1
- package/apps/shared/keyboard/keymap.ts +1 -0
- package/apps/shared/mail/decryptedMessages.ts +116 -0
- package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
- package/apps/shared/mail/folderCounts.ts +8 -4
- package/apps/shared/mail/folderOfType.ts +5 -3
- package/apps/shared/mail/folderTree.ts +1 -1
- package/apps/shared/mail/listSnapshots.ts +2 -2
- package/apps/shared/mail/messageReadState.ts +6 -5
- package/apps/shared/mail/newMailNotifications.ts +2 -2
- package/apps/shared/mail/outbox/composeBridge.ts +2 -2
- package/apps/shared/mail/outbox/outboxState.ts +1 -1
- package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
- package/apps/shared/mail/outbox/sendDecision.ts +2 -2
- package/apps/shared/mail/outbox/sendJob.ts +40 -35
- package/apps/shared/mail/outbox/sendOutcomes.ts +20 -15
- package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
- package/apps/shared/mail/permanentDelete.ts +8 -7
- package/apps/shared/mail/primaryMailbox.ts +1 -1
- package/apps/shared/mail/reportNotices.ts +1 -1
- package/apps/shared/mail/senderBlocking.ts +36 -20
- package/apps/shared/mail/useMailConnection.ts +12 -10
- package/apps/shared/mail/useMailLiveUpdates.ts +3 -3
- package/apps/shared/mail/useMailboxUpdateAccess.ts +35 -11
- package/apps/shared/mail/useMarkMessageRead.ts +14 -7
- package/apps/shared/mail/useNewMailNotifications.ts +1 -1
- package/apps/shared/mail/usePermanentDelete.tsx +5 -3
- package/apps/shared/navigation/pageTitle.ts +1 -1
- package/apps/shared/notifications/NotificationCenter.tsx +1 -1
- package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
- package/apps/shared/notifications/apiErrors.ts +2 -2
- package/apps/shared/notifications/pushStatus.ts +1 -1
- package/apps/shared/notifications/systemErrors.ts +1 -1
- package/apps/shared/search/LocalIndexLifecycle.tsx +18 -12
- package/apps/shared/search/crossMailboxSearch.ts +3 -3
- package/apps/shared/search/localIndexBuilder.ts +24 -12
- package/apps/shared/search/localIndexKey.ts +1 -1
- package/apps/shared/search/localIndexRpcClient.ts +150 -30
- package/apps/shared/search/localIndexTransport.ts +84 -0
- package/apps/shared/search/localIndexWorker.ts +1 -1
- package/apps/shared/search/searchTier2.ts +3 -3
- package/apps/shared/signing/enrollmentTracker.ts +3 -3
- package/apps/shared/signing/enrollmentView.ts +1 -1
- package/apps/shared/signing/signingInfo.ts +1 -1
- package/apps/shared/signing/useSigningEnrollmentWatcher.ts +6 -4
- package/apps/shared/styles/app.css +14 -23
- package/apps/www/_layout.tsx +2 -2
- package/apps/www/calendar/index.tsx +20 -18
- package/apps/www/contacts/[uid].tsx +8 -6
- package/apps/www/contacts/index.tsx +19 -17
- package/apps/www/index.tsx +68 -48
- package/apps/www/messages/[uid].tsx +11 -9
- package/apps/www/settings/auto-reply/index.tsx +6 -4
- package/apps/www/settings/blocked-senders/index.tsx +9 -7
- package/apps/www/settings/encryption/index.tsx +38 -32
- package/apps/www/settings/filters/[uid].tsx +19 -14
- package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
- package/apps/www/settings/filters/index.tsx +4 -4
- package/apps/www/settings/filters/new/index.tsx +10 -8
- package/apps/www/settings/labels/index.tsx +12 -10
- package/apps/www/settings/privacy/index.tsx +18 -14
- package/apps/www/settings/profile/index.tsx +16 -11
- package/apps/www/settings/read-receipts/index.tsx +17 -12
- package/apps/www/settings/sharing/index.tsx +6 -6
- package/apps/www/settings/signatures/[uid].tsx +5 -5
- package/apps/www/settings/signatures/index.tsx +4 -4
- package/apps/www/settings/signatures/new/index.tsx +4 -4
- package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
- package/apps/www/tasks/index.tsx +28 -23
- package/dist/apps/admin/_layout.d.ts +1 -1
- package/dist/apps/admin/_layout.js +1 -1
- package/dist/apps/admin/audit-log/index.d.ts +1 -1
- package/dist/apps/admin/audit-log/index.js +4 -4
- package/dist/apps/admin/branding/index.js +3 -3
- package/dist/apps/admin/data-requests/index.js +8 -8
- package/dist/apps/admin/distribution-lists/[uid].js +3 -3
- package/dist/apps/admin/distribution-lists/index.js +4 -4
- package/dist/apps/admin/distribution-lists/new/index.js +5 -5
- package/dist/apps/admin/domains/[uid].js +6 -6
- package/dist/apps/admin/domains/index.js +4 -4
- package/dist/apps/admin/domains/new/index.js +5 -5
- package/dist/apps/admin/encryption-policy/index.js +1 -1
- package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
- package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
- package/dist/apps/admin/escrow-scopes/index.js +4 -4
- package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
- package/dist/apps/admin/index.js +6 -6
- package/dist/apps/admin/ingest-queue/index.js +4 -4
- package/dist/apps/admin/mailbox-policy/index.js +1 -1
- package/dist/apps/admin/mailboxes/[uid].js +5 -5
- package/dist/apps/admin/quarantine/index.js +5 -5
- package/dist/apps/admin/retention-policy/index.js +3 -3
- package/dist/apps/admin/signing-certificates/index.js +5 -5
- package/dist/apps/admin/transport-rules/[uid].js +5 -5
- package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
- package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
- package/dist/apps/admin/transport-rules/index.js +4 -4
- package/dist/apps/admin/transport-rules/new/index.js +5 -5
- package/dist/apps/escrow/_layout.d.ts +1 -1
- package/dist/apps/escrow/audit-log/index.d.ts +1 -1
- package/dist/apps/escrow/audit-log/index.js +4 -4
- package/dist/apps/escrow/index.js +4 -4
- package/dist/apps/escrow/matters/[uid].js +10 -10
- package/dist/apps/escrow/matters/new/index.js +6 -6
- package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
- package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
- package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
- package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
- package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
- package/dist/apps/shared/appearance/appearanceCache.js +1 -1
- package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
- package/dist/apps/shared/appearance/theme.d.ts +1 -1
- package/dist/apps/shared/auth/adminAccess.js +1 -1
- package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
- package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
- package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
- package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
- package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
- package/dist/apps/shared/components/admin/elevation.js +1 -1
- package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
- package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
- package/dist/apps/shared/components/admin/layout/AdminShell.js +7 -7
- package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
- package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
- package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
- package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +5 -5
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
- package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
- package/dist/apps/shared/components/admin/setup/SetupWizard.d.ts +4 -4
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +27 -23
- package/dist/apps/shared/components/admin/signOut.js +1 -1
- package/dist/apps/shared/components/admin/usePagedList.js +2 -2
- package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
- package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
- package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
- package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
- package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
- package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
- package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
- package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
- package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
- package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
- package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventModal.js +3 -3
- package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
- package/dist/apps/shared/components/calendar/EventShell.js +1 -1
- package/dist/apps/shared/components/calendar/FindATime.js +6 -4
- package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
- package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/MonthView.js +1 -1
- package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
- package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
- package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
- package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
- package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
- package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
- package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
- package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
- package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TimeGridView.js +2 -2
- package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
- package/dist/apps/shared/components/calendar/allDay.js +1 -1
- package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
- package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
- package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
- package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
- package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
- package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
- package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
- package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
- package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
- package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
- package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
- package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
- package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
- package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
- package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
- package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
- package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
- package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
- package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
- package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
- package/dist/apps/shared/components/escrow/layout/EscrowShell.js +6 -6
- package/dist/apps/shared/components/forms/StringListField.js +1 -1
- package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
- package/dist/apps/shared/components/layout/AppShell.js +11 -9
- package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
- package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
- package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
- package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
- package/dist/apps/shared/components/layout/UserMenu.js +6 -4
- package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
- package/dist/apps/shared/components/mail/ConversationList.js +23 -8
- package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +19 -13
- package/dist/apps/shared/components/mail/InviteCard.js +10 -8
- package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
- package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
- package/dist/apps/shared/components/mail/MailAddress.js +1 -1
- package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
- package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
- package/dist/apps/shared/components/mail/MenuButton.js +1 -1
- package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
- package/dist/apps/shared/components/mail/MessageDetailPane.js +61 -46
- package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
- package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
- package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
- package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
- package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +34 -30
- package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
- package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
- package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
- package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
- package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
- package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
- package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
- package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
- package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +3 -3
- package/dist/apps/shared/components/mail/invite/InviteRowChip.js +7 -9
- package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +2 -2
- package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +11 -9
- package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
- package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
- package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
- package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
- package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
- package/dist/apps/shared/components/mail/labelMenu.js +9 -7
- package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
- package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
- package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
- package/dist/apps/shared/components/mail/listPreferences.js +1 -1
- package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
- package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
- package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
- package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
- package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
- package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
- package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
- package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
- package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
- package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
- package/dist/apps/shared/components/mail/reading/conversationPrint.d.ts +15 -0
- package/dist/apps/shared/components/mail/reading/conversationPrint.js +69 -0
- package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
- package/dist/apps/shared/components/mail/reading/printMessage.d.ts +9 -3
- package/dist/apps/shared/components/mail/reading/printMessage.js +33 -6
- package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
- package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
- package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
- package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
- package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
- package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
- package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
- package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
- package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
- package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
- package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
- package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
- package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
- package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
- package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
- package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
- package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
- package/dist/apps/shared/keyboard/keymap.d.ts +6 -0
- package/dist/apps/shared/keyboard/keymap.js +1 -0
- package/dist/apps/shared/mail/decryptedMessages.d.ts +27 -0
- package/dist/apps/shared/mail/decryptedMessages.js +97 -0
- package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
- package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
- package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
- package/dist/apps/shared/mail/folderCounts.js +6 -4
- package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
- package/dist/apps/shared/mail/folderOfType.js +4 -4
- package/dist/apps/shared/mail/folderTree.d.ts +1 -1
- package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
- package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
- package/dist/apps/shared/mail/messageReadState.js +5 -5
- package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
- package/dist/apps/shared/mail/newMailNotifications.js +1 -1
- package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
- package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
- package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
- package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
- package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
- package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
- package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
- package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
- package/dist/apps/shared/mail/permanentDelete.js +8 -8
- package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
- package/dist/apps/shared/mail/primaryMailbox.js +1 -1
- package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
- package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
- package/dist/apps/shared/mail/senderBlocking.js +19 -16
- package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
- package/dist/apps/shared/mail/useMailConnection.js +12 -10
- package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
- package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
- package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
- package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
- package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
- package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
- package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
- package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
- package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
- package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
- package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
- package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
- package/dist/apps/shared/notifications/apiErrors.js +2 -2
- package/dist/apps/shared/notifications/pushStatus.js +1 -1
- package/dist/apps/shared/notifications/systemErrors.js +1 -1
- package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
- package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
- package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
- package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
- package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
- package/dist/apps/shared/search/localIndexBuilder.js +15 -12
- package/dist/apps/shared/search/localIndexKey.js +1 -1
- package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
- package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
- package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
- package/dist/apps/shared/search/localIndexTransport.js +1 -0
- package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
- package/dist/apps/shared/search/searchTier2.d.ts +3 -3
- package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
- package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
- package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
- package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
- package/dist/apps/shared/signing/signingInfo.js +1 -1
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
- package/dist/apps/shared/styles/app.css +14 -23
- package/dist/apps/www/_layout.d.ts +1 -1
- package/dist/apps/www/_layout.js +1 -1
- package/dist/apps/www/calendar/index.d.ts +1 -1
- package/dist/apps/www/calendar/index.js +20 -18
- package/dist/apps/www/contacts/[uid].d.ts +1 -1
- package/dist/apps/www/contacts/[uid].js +8 -6
- package/dist/apps/www/contacts/index.d.ts +1 -1
- package/dist/apps/www/contacts/index.js +19 -17
- package/dist/apps/www/index.d.ts +1 -1
- package/dist/apps/www/index.js +64 -50
- package/dist/apps/www/messages/[uid].d.ts +1 -1
- package/dist/apps/www/messages/[uid].js +11 -9
- package/dist/apps/www/settings/appearance/index.d.ts +1 -1
- package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
- package/dist/apps/www/settings/auto-reply/index.js +6 -4
- package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
- package/dist/apps/www/settings/blocked-senders/index.js +6 -4
- package/dist/apps/www/settings/encryption/index.d.ts +1 -1
- package/dist/apps/www/settings/encryption/index.js +31 -29
- package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
- package/dist/apps/www/settings/filters/[uid].js +11 -9
- package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
- package/dist/apps/www/settings/filters/index.d.ts +1 -1
- package/dist/apps/www/settings/filters/index.js +4 -4
- package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
- package/dist/apps/www/settings/filters/new/index.js +10 -8
- package/dist/apps/www/settings/labels/index.d.ts +1 -1
- package/dist/apps/www/settings/labels/index.js +12 -10
- package/dist/apps/www/settings/privacy/index.d.ts +1 -1
- package/dist/apps/www/settings/privacy/index.js +18 -14
- package/dist/apps/www/settings/profile/index.d.ts +1 -1
- package/dist/apps/www/settings/profile/index.js +7 -5
- package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
- package/dist/apps/www/settings/read-receipts/index.js +5 -3
- package/dist/apps/www/settings/sharing/index.d.ts +1 -1
- package/dist/apps/www/settings/sharing/index.js +6 -6
- package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
- package/dist/apps/www/settings/signatures/[uid].js +5 -5
- package/dist/apps/www/settings/signatures/index.d.ts +1 -1
- package/dist/apps/www/settings/signatures/index.js +4 -4
- package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
- package/dist/apps/www/settings/signatures/new/index.js +4 -4
- package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
- package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
- package/dist/apps/www/tasks/index.d.ts +1 -1
- package/dist/apps/www/tasks/index.js +17 -15
- package/dist/lib/admin/auditLogApi.d.ts +32 -0
- package/dist/lib/admin/auditLogApi.js +27 -0
- package/dist/lib/admin/distributionListsApi.d.ts +47 -0
- package/dist/lib/admin/distributionListsApi.js +34 -0
- package/dist/lib/admin/domainsApi.d.ts +75 -0
- package/dist/lib/admin/domainsApi.js +44 -0
- package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
- package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
- package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
- package/dist/lib/admin/escrowAuditLogApi.js +31 -0
- package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
- package/dist/lib/admin/escrowScopesApi.js +50 -0
- package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
- package/dist/lib/admin/leftoverMailboxApi.js +67 -0
- package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
- package/dist/lib/admin/mailboxPolicyApi.js +21 -0
- package/dist/lib/admin/matterExportApi.d.ts +40 -0
- package/dist/lib/admin/matterExportApi.js +39 -0
- package/dist/lib/admin/matterSearchApi.d.ts +20 -0
- package/dist/lib/admin/matterSearchApi.js +27 -0
- package/dist/lib/admin/mattersApi.d.ts +56 -0
- package/dist/lib/admin/mattersApi.js +40 -0
- package/dist/lib/admin/pluginsApi.d.ts +265 -0
- package/dist/lib/admin/pluginsApi.js +102 -0
- package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
- package/dist/lib/admin/retentionPolicyApi.js +29 -0
- package/dist/lib/admin/setupApi.d.ts +23 -0
- package/dist/lib/admin/setupApi.js +26 -0
- package/dist/lib/admin/transportRulesApi.d.ts +66 -0
- package/dist/lib/admin/transportRulesApi.js +43 -0
- package/dist/lib/appearance/preferencesApi.d.ts +128 -0
- package/dist/lib/appearance/preferencesApi.js +257 -0
- package/dist/lib/auth/profileApi.d.ts +33 -0
- package/dist/lib/auth/profileApi.js +52 -0
- package/dist/lib/auth/session.d.ts +70 -0
- package/dist/lib/auth/session.js +210 -0
- package/dist/lib/branding/brandingApi.d.ts +47 -0
- package/dist/lib/branding/brandingApi.js +61 -0
- package/dist/lib/branding/useBranding.d.ts +23 -0
- package/dist/lib/branding/useBranding.js +65 -0
- package/dist/lib/calendar/calendarApi.d.ts +230 -0
- package/dist/lib/calendar/calendarApi.js +106 -0
- package/dist/lib/calendar/calendarColors.d.ts +18 -0
- package/dist/lib/calendar/calendarColors.js +39 -0
- package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
- package/dist/lib/calendar/calendarDragIds.js +92 -0
- package/dist/lib/calendar/calendarMutations.d.ts +68 -0
- package/dist/lib/calendar/calendarMutations.js +190 -0
- package/dist/lib/calendar/eventDescription.d.ts +43 -0
- package/dist/lib/calendar/eventDescription.js +195 -0
- package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
- package/dist/lib/calendar/freeBusyApi.js +119 -0
- package/dist/lib/calendar/inviteApi.d.ts +121 -0
- package/dist/lib/calendar/inviteApi.js +54 -0
- package/dist/lib/calendar/recurrence.d.ts +60 -0
- package/dist/lib/calendar/recurrence.js +225 -0
- package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
- package/dist/lib/components/avatar/ContactAvatar.js +26 -0
- package/dist/lib/components/buttons/Button.d.ts +9 -0
- package/dist/lib/components/buttons/Button.js +14 -0
- package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
- package/dist/lib/components/buttons/CopyButton.js +14 -0
- package/dist/lib/components/feedback/Alert.d.ts +4 -0
- package/dist/lib/components/feedback/Alert.js +4 -0
- package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
- package/dist/lib/components/feedback/Skeleton.js +14 -0
- package/dist/lib/components/forms/FormField.d.ts +8 -0
- package/dist/lib/components/forms/FormField.js +5 -0
- package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
- package/dist/lib/components/navigation/BottomTabBar.js +32 -0
- package/dist/lib/components/overlays/Drawer.d.ts +20 -0
- package/dist/lib/components/overlays/Drawer.js +27 -0
- package/dist/lib/components/overlays/Modal.d.ts +17 -0
- package/dist/lib/components/overlays/Modal.js +25 -0
- package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
- package/dist/lib/components/overlays/PopoverPortal.js +53 -0
- package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
- package/dist/lib/components/overlays/overlayStack.js +143 -0
- package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
- package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
- package/dist/lib/contacts/contactsApi.d.ts +174 -0
- package/dist/lib/contacts/contactsApi.js +139 -0
- package/dist/lib/contacts/vcard.d.ts +23 -0
- package/dist/lib/contacts/vcard.js +179 -0
- package/dist/lib/crypto/composeSecurity.d.ts +67 -0
- package/dist/lib/crypto/composeSecurity.js +93 -0
- package/dist/lib/crypto/encoding.d.ts +9 -0
- package/dist/lib/crypto/encoding.js +32 -0
- package/dist/lib/crypto/escrowKeys.d.ts +25 -0
- package/dist/lib/crypto/escrowKeys.js +59 -0
- package/dist/lib/crypto/idleTimeout.d.ts +16 -0
- package/dist/lib/crypto/idleTimeout.js +47 -0
- package/dist/lib/crypto/keySession.d.ts +159 -0
- package/dist/lib/crypto/keySession.js +367 -0
- package/dist/lib/crypto/keys.d.ts +50 -0
- package/dist/lib/crypto/keys.js +89 -0
- package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
- package/dist/lib/crypto/keyvaultApi.js +350 -0
- package/dist/lib/crypto/masterKey.d.ts +59 -0
- package/dist/lib/crypto/masterKey.js +104 -0
- package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
- package/dist/lib/crypto/masterKeyWraps.js +228 -0
- package/dist/lib/crypto/messageSecurity.d.ts +248 -0
- package/dist/lib/crypto/messageSecurity.js +367 -0
- package/dist/lib/crypto/mime.d.ts +143 -0
- package/dist/lib/crypto/mime.js +504 -0
- package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
- package/dist/lib/crypto/passkeyUnlock.js +79 -0
- package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
- package/dist/lib/crypto/passwordUnlock.js +73 -0
- package/dist/lib/crypto/recoveryCode.d.ts +28 -0
- package/dist/lib/crypto/recoveryCode.js +86 -0
- package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
- package/dist/lib/crypto/signingProviderApi.js +60 -0
- package/dist/lib/crypto/smime.d.ts +128 -0
- package/dist/lib/crypto/smime.js +402 -0
- package/dist/lib/crypto/smimeMessage.d.ts +132 -0
- package/dist/lib/crypto/smimeMessage.js +341 -0
- package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
- package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
- package/dist/lib/crypto/verificationSeal.d.ts +56 -0
- package/dist/lib/crypto/verificationSeal.js +220 -0
- package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
- package/dist/lib/mail/compose/composeQuoting.js +204 -0
- package/dist/lib/mail/compose/emojiData.d.ts +9 -0
- package/dist/lib/mail/compose/emojiData.js +18 -0
- package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
- package/dist/lib/mail/compose/giphyApi.js +20 -0
- package/dist/lib/mail/conversationsApi.d.ts +80 -0
- package/dist/lib/mail/conversationsApi.js +44 -0
- package/dist/lib/mail/dataExportApi.d.ts +50 -0
- package/dist/lib/mail/dataExportApi.js +40 -0
- package/dist/lib/mail/directoryApi.d.ts +49 -0
- package/dist/lib/mail/directoryApi.js +101 -0
- package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
- package/dist/lib/mail/erasureRequestApi.js +43 -0
- package/dist/lib/mail/flaggedMessages.d.ts +22 -0
- package/dist/lib/mail/flaggedMessages.js +73 -0
- package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
- package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
- package/dist/lib/mail/labelsApi.d.ts +47 -0
- package/dist/lib/mail/labelsApi.js +38 -0
- package/dist/lib/mail/mailAddress.d.ts +27 -0
- package/dist/lib/mail/mailAddress.js +54 -0
- package/dist/lib/mail/mailApi.d.ts +850 -0
- package/dist/lib/mail/mailApi.js +602 -0
- package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
- package/dist/lib/mail/mailDetailHooks.js +53 -0
- package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
- package/dist/lib/mail/mailFilterRulesApi.js +45 -0
- package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
- package/dist/lib/mail/mailSignaturesApi.js +42 -0
- package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
- package/dist/lib/mail/mailboxAccessApi.js +56 -0
- package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
- package/dist/lib/mail/mailboxImportApi.js +51 -0
- package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
- package/dist/lib/mail/messageBodySanitizer.js +118 -0
- package/dist/lib/mail/pushClient.d.ts +106 -0
- package/dist/lib/mail/pushClient.js +317 -0
- package/dist/lib/mail/sendEvents.d.ts +33 -0
- package/dist/lib/mail/sendEvents.js +44 -0
- package/dist/lib/mail/sendFailure.d.ts +16 -0
- package/dist/lib/mail/sendFailure.js +86 -0
- package/dist/lib/mail/senderListsApi.d.ts +69 -0
- package/dist/lib/mail/senderListsApi.js +126 -0
- package/dist/lib/search/queryGrammar.d.ts +51 -0
- package/dist/lib/search/queryGrammar.js +111 -0
- package/dist/lib/search/searchApi.d.ts +100 -0
- package/dist/lib/search/searchApi.js +107 -0
- package/dist/lib/search/searchScoring.d.ts +34 -0
- package/dist/lib/search/searchScoring.js +44 -0
- package/dist/lib/search/searchTier3.d.ts +64 -0
- package/dist/lib/search/searchTier3.js +335 -0
- package/dist/lib/tasks/tasksApi.d.ts +85 -0
- package/dist/lib/tasks/tasksApi.js +55 -0
- package/dist/lib/util/api.d.ts +160 -0
- package/dist/lib/util/api.js +251 -0
- package/dist/lib/util/apiClientContext.d.ts +7 -0
- package/dist/lib/util/apiClientContext.js +32 -0
- package/dist/lib/util/apiQuery.d.ts +32 -0
- package/dist/lib/util/apiQuery.js +42 -0
- package/dist/lib/util/clipboard.d.ts +7 -0
- package/dist/lib/util/clipboard.js +56 -0
- package/dist/lib/util/dateInput.d.ts +6 -0
- package/dist/lib/util/dateInput.js +14 -0
- package/dist/lib/util/timeZone.d.ts +12 -0
- package/dist/lib/util/timeZone.js +32 -0
- package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
- package/dist/lib/util/useCopyToClipboard.js +34 -0
- package/dist/lib/util/useIsMobile.d.ts +10 -0
- package/dist/lib/util/useIsMobile.js +29 -0
- package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
- package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
- package/lib/admin/auditLogApi.ts +48 -0
- package/lib/admin/distributionListsApi.ts +77 -0
- package/lib/admin/domainsApi.ts +113 -0
- package/lib/admin/escrowAccessRequestsApi.ts +110 -0
- package/lib/admin/escrowAuditLogApi.ts +101 -0
- package/lib/admin/escrowScopesApi.ts +112 -0
- package/lib/admin/leftoverMailboxApi.ts +120 -0
- package/lib/admin/mailboxPolicyApi.ts +38 -0
- package/lib/admin/matterExportApi.ts +62 -0
- package/lib/admin/matterSearchApi.ts +35 -0
- package/lib/admin/mattersApi.ts +86 -0
- package/lib/admin/pluginsApi.ts +339 -0
- package/lib/admin/retentionPolicyApi.ts +46 -0
- package/lib/admin/setupApi.ts +40 -0
- package/lib/admin/transportRulesApi.ts +106 -0
- package/lib/appearance/preferencesApi.ts +335 -0
- package/lib/auth/profileApi.ts +77 -0
- package/lib/auth/session.ts +238 -0
- package/lib/branding/brandingApi.ts +97 -0
- package/lib/branding/useBranding.ts +81 -0
- package/lib/calendar/calendarApi.ts +309 -0
- package/lib/calendar/calendarColors.ts +45 -0
- package/lib/calendar/calendarDragIds.ts +112 -0
- package/lib/calendar/calendarMutations.ts +236 -0
- package/lib/calendar/eventDescription.ts +211 -0
- package/lib/calendar/freeBusyApi.ts +197 -0
- package/lib/calendar/inviteApi.ts +154 -0
- package/lib/calendar/recurrence.ts +285 -0
- package/lib/components/avatar/ContactAvatar.tsx +47 -0
- package/lib/components/buttons/Button.tsx +40 -0
- package/lib/components/buttons/CopyButton.tsx +45 -0
- package/lib/components/feedback/Alert.tsx +13 -0
- package/lib/components/feedback/Skeleton.tsx +38 -0
- package/lib/components/forms/FormField.tsx +23 -0
- package/lib/components/navigation/BottomTabBar.tsx +66 -0
- package/lib/components/overlays/Drawer.tsx +72 -0
- package/lib/components/overlays/Modal.tsx +64 -0
- package/lib/components/overlays/PopoverPortal.tsx +74 -0
- package/lib/components/overlays/overlayStack.ts +165 -0
- package/lib/components/pickers/MiniDatePicker.tsx +90 -0
- package/lib/contacts/contactsApi.ts +289 -0
- package/lib/contacts/vcard.ts +197 -0
- package/lib/crypto/composeSecurity.ts +140 -0
- package/lib/crypto/encoding.ts +36 -0
- package/lib/crypto/escrowKeys.ts +74 -0
- package/lib/crypto/idleTimeout.ts +49 -0
- package/lib/crypto/keySession.ts +483 -0
- package/lib/crypto/keys.ts +105 -0
- package/lib/crypto/keyvaultApi.ts +674 -0
- package/lib/crypto/masterKey.ts +134 -0
- package/lib/crypto/masterKeyWraps.ts +269 -0
- package/lib/crypto/messageSecurity.ts +600 -0
- package/lib/crypto/mime.ts +568 -0
- package/lib/crypto/passkeyUnlock.ts +103 -0
- package/lib/crypto/passwordUnlock.ts +98 -0
- package/lib/crypto/recoveryCode.ts +93 -0
- package/lib/crypto/signingProviderApi.ts +135 -0
- package/lib/crypto/smime.ts +459 -0
- package/lib/crypto/smimeMessage.ts +491 -0
- package/lib/crypto/useIdleKeyTimeout.ts +176 -0
- package/lib/crypto/verificationSeal.ts +288 -0
- package/lib/mail/compose/composeQuoting.ts +243 -0
- package/lib/mail/compose/emojiData.ts +34 -0
- package/lib/mail/compose/giphyApi.ts +31 -0
- package/lib/mail/conversationsApi.ts +123 -0
- package/lib/mail/dataExportApi.ts +73 -0
- package/lib/mail/directoryApi.ts +161 -0
- package/lib/mail/erasureRequestApi.ts +71 -0
- package/lib/mail/flaggedMessages.ts +80 -0
- package/lib/mail/focusedInboxOverridesApi.ts +64 -0
- package/lib/mail/labelsApi.ts +71 -0
- package/lib/mail/mailAddress.ts +76 -0
- package/lib/mail/mailApi.ts +1286 -0
- package/lib/mail/mailDetailHooks.ts +57 -0
- package/lib/mail/mailFilterRulesApi.ts +116 -0
- package/lib/mail/mailSignaturesApi.ts +84 -0
- package/lib/mail/mailboxAccessApi.ts +106 -0
- package/lib/mail/mailboxImportApi.ts +87 -0
- package/lib/mail/messageBodySanitizer.ts +129 -0
- package/lib/mail/pushClient.ts +375 -0
- package/lib/mail/sendEvents.ts +72 -0
- package/lib/mail/sendFailure.ts +98 -0
- package/lib/mail/senderListsApi.ts +161 -0
- package/lib/search/queryGrammar.ts +179 -0
- package/lib/search/searchApi.ts +199 -0
- package/lib/search/searchScoring.ts +46 -0
- package/lib/search/searchTier3.ts +406 -0
- package/lib/tasks/tasksApi.ts +128 -0
- package/lib/util/api.ts +318 -0
- package/lib/util/apiClientContext.ts +35 -0
- package/lib/util/apiQuery.ts +61 -0
- package/lib/util/clipboard.ts +55 -0
- package/lib/util/dateInput.ts +15 -0
- package/lib/util/timeZone.ts +33 -0
- package/lib/util/useCopyToClipboard.ts +48 -0
- package/lib/util/useIsMobile.ts +34 -0
- package/lib/videoconf/videoMeetingsApi.ts +144 -0
- package/package.json +10 -4
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* MIME assembly around `smime.ts`'s CMS primitives, implementing `specs/end-to-end_encryption.md`'s
|
|
7
|
+
* "Header Protection" requirement — RFC 9788, not the older, spec-forbidden RFC 8551 §3.1
|
|
8
|
+
* `message/rfc822`-wrapping approach.
|
|
9
|
+
*
|
|
10
|
+
* RFC 9788's actual mechanism (verified against the RFC text directly, not assumed from memory,
|
|
11
|
+
* given how easy it is to get a narrow/recent spec wrong from recollection alone): protected header
|
|
12
|
+
* fields are **not** wrapped in a `multipart/mixed` gutter part. They become literal header lines
|
|
13
|
+
* prepended directly to the MIME entity that is signed/encrypted (the "Cryptographic Payload"),
|
|
14
|
+
* whose own `Content-Type` gains an `hp="clear"` (signed-only) or `hp="cipher"` (encrypted) parameter.
|
|
15
|
+
* For encrypted messages, `HP-Outer: <Field>: <value>` lines carry a copy of the outer envelope's
|
|
16
|
+
* headers inside the protected payload, so a recipient can detect outer-header tampering. The outer,
|
|
17
|
+
* unprotected envelope keeps real `From`/`To`/`Cc`/`Date` values but MUST obscure `Subject` to the
|
|
18
|
+
* literal `"[...]"` under `hcp_baseline` — RFC 9788's own required minimum default policy (a
|
|
19
|
+
* conformant MUA "MUST have a default HCP that offers confidentiality for the Subject Header Field at
|
|
20
|
+
* least" — Section 3.3); this module implements exactly that baseline, not the more ambitious
|
|
21
|
+
* `hcp_shy` policy (which also strips display names and normalizes Date to UTC).
|
|
22
|
+
*
|
|
23
|
+
* Combined sign-then-encrypt uses **opaque** signing (`smime.ts`'s `signOpaque()`), embedding the
|
|
24
|
+
* `hp="cipher"`-tagged content inside an `application/pkcs7-mime; smime-type="signed-data"` entity,
|
|
25
|
+
* which is then itself the plaintext encrypted into the final `enveloped-data` — matching the nesting
|
|
26
|
+
* RFC 9788's own worked example uses (outer enveloped-data → decrypts to signed-data → unwraps to the
|
|
27
|
+
* real `hp="cipher"` content), not a `multipart/signed` structure encrypted as a whole.
|
|
28
|
+
*/
|
|
29
|
+
import { toBase64 } from "./encoding.js";
|
|
30
|
+
import {
|
|
31
|
+
DisplayBody,
|
|
32
|
+
binaryStringToBytes,
|
|
33
|
+
bytesToBinaryString,
|
|
34
|
+
decodeBase64Text,
|
|
35
|
+
decodeBodyText,
|
|
36
|
+
decodeHeaderText,
|
|
37
|
+
encodeAddressListHeaderValue,
|
|
38
|
+
encodeUnstructuredHeaderValue,
|
|
39
|
+
isBinaryString,
|
|
40
|
+
extractAttachments,
|
|
41
|
+
extractDisplayBody,
|
|
42
|
+
MimeAttachment,
|
|
43
|
+
MimeHeaderField,
|
|
44
|
+
parseMimeEntity,
|
|
45
|
+
parseParameterizedHeader,
|
|
46
|
+
splitMultipart,
|
|
47
|
+
} from "./mime.js";
|
|
48
|
+
import {
|
|
49
|
+
type DecryptionKey,
|
|
50
|
+
decryptEnvelopedDataWithKeys,
|
|
51
|
+
encryptForRecipients,
|
|
52
|
+
signDetached,
|
|
53
|
+
signOpaque,
|
|
54
|
+
UnsupportedContentEncryptionError,
|
|
55
|
+
verifyDetached,
|
|
56
|
+
verifyOpaque,
|
|
57
|
+
} from "./smime.js";
|
|
58
|
+
|
|
59
|
+
const CRLF = "\r\n";
|
|
60
|
+
/** RFC 2045's own line-length limit for base64-encoded body content. */
|
|
61
|
+
const BASE64_LINE_WIDTH = 76;
|
|
62
|
+
|
|
63
|
+
export interface ProtectedHeaders {
|
|
64
|
+
from: string;
|
|
65
|
+
to: string;
|
|
66
|
+
cc?: string;
|
|
67
|
+
/** An RFC 5322 date string (e.g. `new Date().toUTCString()`-shaped, `Date:`-header ready). */
|
|
68
|
+
date: string;
|
|
69
|
+
subject: string;
|
|
70
|
+
messageId: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** RFC 9788's `hcp_baseline` Header Confidentiality Policy — the RFC's own required minimum default
|
|
74
|
+
* (Section 3.3). Applied to produce the OUTER, unprotected envelope's headers from the real
|
|
75
|
+
* (protected) ones; `From`/`To`/`Cc`/`Date` pass through unchanged, `Subject` is obscured. */
|
|
76
|
+
export function applyBaselineOuterHeaders(headers: ProtectedHeaders): ProtectedHeaders {
|
|
77
|
+
return { ...headers, subject: "[...]" };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function base64Wrapped(bytes: Uint8Array): string {
|
|
81
|
+
const encoded = toBase64(bytes);
|
|
82
|
+
const lines: string[] = [];
|
|
83
|
+
for (let i = 0; i < encoded.length; i += BASE64_LINE_WIDTH) {
|
|
84
|
+
lines.push(encoded.slice(i, i + BASE64_LINE_WIDTH));
|
|
85
|
+
}
|
|
86
|
+
return lines.join(CRLF);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function generateBoundary(): string {
|
|
90
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
91
|
+
return `----=_RapidMX_${Array.from(bytes)
|
|
92
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
93
|
+
.join("")}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Strips CR/LF/NUL from a header value that has no further encoding (Date, Message-ID, Content-Type). */
|
|
97
|
+
function flattenHeaderValue(value: string): string {
|
|
98
|
+
return value.replace(/[\r\n\0]+/g, " ");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Serializes `headers` as RFC 5322 header lines, safely: CR/LF/NUL in any value become a space (so a
|
|
103
|
+
* caller-supplied Subject or display name can never inject an extra header or end the header block), and
|
|
104
|
+
* non-ASCII Subject text / display names are RFC 2047-encoded (`mime.ts`'s `encode*HeaderValue()`). The
|
|
105
|
+
* protected copy, the `HP-Outer` copy and the outer envelope all go through this same deterministic
|
|
106
|
+
* encoding, so the receive-side comparisons between them still compare like with like.
|
|
107
|
+
*/
|
|
108
|
+
function headerLines(headers: ProtectedHeaders, prefix = ""): string[] {
|
|
109
|
+
return [
|
|
110
|
+
`${prefix}From: ${encodeAddressListHeaderValue(headers.from)}`,
|
|
111
|
+
`${prefix}To: ${encodeAddressListHeaderValue(headers.to)}`,
|
|
112
|
+
...(headers.cc ? [`${prefix}Cc: ${encodeAddressListHeaderValue(headers.cc)}`] : []),
|
|
113
|
+
`${prefix}Date: ${flattenHeaderValue(headers.date)}`,
|
|
114
|
+
`${prefix}Subject: ${encodeUnstructuredHeaderValue(headers.subject)}`,
|
|
115
|
+
...(prefix ? [] : [`Message-ID: ${flattenHeaderValue(headers.messageId)}`]),
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function protectedHeaderLines(headers: ProtectedHeaders, hpOuter?: ProtectedHeaders): string {
|
|
120
|
+
const lines = headerLines(headers);
|
|
121
|
+
if (hpOuter) {
|
|
122
|
+
// RFC 9788 Section 2.2.1's `hp-outer` field: the literal string "HP-Outer:" followed by the
|
|
123
|
+
// original field name and its (outer, possibly-obscured) value. Written on every encrypted
|
|
124
|
+
// message per the spec, and compared against the actual outer envelope on receipt by
|
|
125
|
+
// parseEncryptedMessage() (see ParsedEncryptedMessage.headerTamperDetected).
|
|
126
|
+
lines.push(...headerLines(hpOuter, "HP-Outer: "));
|
|
127
|
+
}
|
|
128
|
+
return lines.join(CRLF);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** One MIME entity's worth of headers-beyond-Content-Type plus body, ready for a caller (the actual
|
|
132
|
+
* outbound-message compose step, built on top of this module) to combine with the outer envelope's
|
|
133
|
+
* own `From`/`To`/`Subject`/`Date`/`Message-ID`. */
|
|
134
|
+
export interface MimePart {
|
|
135
|
+
/** The full `Content-Type` header value, including any parameters. */
|
|
136
|
+
contentType: string;
|
|
137
|
+
/** Any other headers this entity needs (`Content-Transfer-Encoding`, `Content-Disposition`, ...). */
|
|
138
|
+
additionalHeaders?: Record<string, string>;
|
|
139
|
+
body: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Builds a detached `multipart/signed` message with RFC 9788 header protection (`hp="clear"`) — per
|
|
144
|
+
* the spec, the only form signature-only messages may use (opaque signing is reserved for the
|
|
145
|
+
* sign-then-encrypt case in `buildEncryptedMessage()`, where no legacy client is ever exposed to it).
|
|
146
|
+
*/
|
|
147
|
+
export async function buildSignedOnlyMessage(
|
|
148
|
+
bodyContentType: string,
|
|
149
|
+
bodyText: string,
|
|
150
|
+
protectedHeaders: ProtectedHeaders,
|
|
151
|
+
signingCertDer: Uint8Array,
|
|
152
|
+
signingPrivateKey: CryptoKey,
|
|
153
|
+
): Promise<MimePart> {
|
|
154
|
+
const boundary = generateBoundary();
|
|
155
|
+
// Base64 so the signed bytes survive transport unchanged: a raw UTF-8 body is usually one long line, which
|
|
156
|
+
// MTAs rewrap past 998 characters and so break the detached signature.
|
|
157
|
+
const innerEntity = [
|
|
158
|
+
protectedHeaderLines(protectedHeaders),
|
|
159
|
+
`Content-Type: ${flattenHeaderValue(bodyContentType)}; hp="clear"`,
|
|
160
|
+
`Content-Transfer-Encoding: base64`,
|
|
161
|
+
"",
|
|
162
|
+
base64Wrapped(new TextEncoder().encode(bodyText)),
|
|
163
|
+
].join(CRLF);
|
|
164
|
+
|
|
165
|
+
const signature = await signDetached(new TextEncoder().encode(innerEntity), signingCertDer, signingPrivateKey);
|
|
166
|
+
|
|
167
|
+
const body = [
|
|
168
|
+
`--${boundary}`,
|
|
169
|
+
innerEntity,
|
|
170
|
+
`--${boundary}`,
|
|
171
|
+
`Content-Type: application/pkcs7-signature; name="smime.p7s"`,
|
|
172
|
+
`Content-Transfer-Encoding: base64`,
|
|
173
|
+
`Content-Disposition: attachment; filename="smime.p7s"`,
|
|
174
|
+
"",
|
|
175
|
+
base64Wrapped(signature),
|
|
176
|
+
`--${boundary}--`,
|
|
177
|
+
].join(CRLF);
|
|
178
|
+
|
|
179
|
+
return { contentType: `multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-256; boundary="${boundary}"`, body };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface ParsedSignedOnlyMessage {
|
|
183
|
+
verified: boolean;
|
|
184
|
+
/** The certificate pkijs matched to the verified SignerInfo (not merely the first embedded one). */
|
|
185
|
+
signerCertificateDer?: Uint8Array;
|
|
186
|
+
protectedHeaders?: ProtectedHeaders;
|
|
187
|
+
/** Every header field of the signed entity, in order, repeats included - lets a caller reject a
|
|
188
|
+
* message carrying more than one protected `From`/`To`/`Cc`/`Sender` (see `messageSecurity.ts`). */
|
|
189
|
+
protectedHeaderFields?: MimeHeaderField[];
|
|
190
|
+
bodyContentType?: string;
|
|
191
|
+
/** The inner entity's body with its Content-Transfer-Encoding/charset decoded (for a multipart inner
|
|
192
|
+
* entity this is the raw multipart text - use `displayBody` for what to render). */
|
|
193
|
+
bodyText?: string;
|
|
194
|
+
/** The displayable body: `html` only for a real text/html part, `text` for text/plain. */
|
|
195
|
+
displayBody?: DisplayBody;
|
|
196
|
+
/** The attachments inside the *signed* entity - the only ones the signature covers. */
|
|
197
|
+
attachments?: MimeAttachment[];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const SIGNATURE_CONTENT_TYPES = new Set(["application/pkcs7-signature", "application/x-pkcs7-signature"]);
|
|
201
|
+
const PKCS7_MIME_CONTENT_TYPES = new Set(["application/pkcs7-mime", "application/x-pkcs7-mime"]);
|
|
202
|
+
|
|
203
|
+
/** Parses and verifies a `multipart/signed` message (RFC 1847/8551). Returns `{ verified: false }` (never
|
|
204
|
+
* throws) for anything malformed — same "degrade to a failed-signature state, don't crash" contract as
|
|
205
|
+
* `smime.ts`'s own verify functions. Accepts foreign-MUA framing: unquoted/case-varied parameters,
|
|
206
|
+
* preamble/epilogue, transport padding, and a signed part stored with bare-LF line endings (retried in
|
|
207
|
+
* canonical CRLF form, which is what RFC 8551 §3.1.1 says was actually signed). The body must hold exactly two
|
|
208
|
+
* parts (RFC 1847 §2.1): a third part isn't covered by the signature, so a message carrying one is rejected
|
|
209
|
+
* rather than shown - partly unsigned - under a verified badge. */
|
|
210
|
+
export async function parseSignedOnlyMessage(contentType: string, body: string): Promise<ParsedSignedOnlyMessage> {
|
|
211
|
+
const boundary = parseParameterizedHeader(contentType).params["boundary"];
|
|
212
|
+
if (!boundary) {
|
|
213
|
+
return { verified: false };
|
|
214
|
+
}
|
|
215
|
+
const parts = splitMultipart(body, boundary);
|
|
216
|
+
if (parts.length !== 2) {
|
|
217
|
+
return { verified: false };
|
|
218
|
+
}
|
|
219
|
+
const [innerText, signaturePartText] = parts;
|
|
220
|
+
const signaturePart = parseMimeEntity(signaturePartText);
|
|
221
|
+
if (!SIGNATURE_CONTENT_TYPES.has(parseParameterizedHeader(signaturePart.headers["content-type"]).value)) {
|
|
222
|
+
return { verified: false };
|
|
223
|
+
}
|
|
224
|
+
// The signature part is always base64 in practice (binary DER can't survive a text transport); a
|
|
225
|
+
// missing Content-Transfer-Encoding header is treated as base64 too, matching common senders.
|
|
226
|
+
const signatureDer = decodeBase64Text(signaturePart.body);
|
|
227
|
+
if (!signatureDer || signatureDer.length === 0) {
|
|
228
|
+
return { verified: false };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// The signed part's exact bytes: a binary string (see `mime.ts`) maps back to them one code unit per
|
|
232
|
+
// byte; a string that was already decoded as text is UTF-8 encoded instead. A binary string holding
|
|
233
|
+
// non-ASCII is also tried as UTF-8 text, for a caller that decoded an all-Latin-1 message itself. Each
|
|
234
|
+
// form is retried in canonical CRLF form. Trying several byte forms is safe: each must still verify.
|
|
235
|
+
const canonical = innerText.replace(/\r?\n/g, CRLF);
|
|
236
|
+
const candidates: Uint8Array[] = [];
|
|
237
|
+
for (const text of canonical === innerText ? [innerText] : [innerText, canonical]) {
|
|
238
|
+
candidates.push(binaryStringToBytes(text));
|
|
239
|
+
if (isBinaryString(text) && /[\x80-\xff]/.test(text)) {
|
|
240
|
+
candidates.push(new TextEncoder().encode(text));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
let result: Awaited<ReturnType<typeof verifyDetached>> = { valid: false };
|
|
244
|
+
for (const candidate of candidates) {
|
|
245
|
+
result = await verifyDetached(candidate, signatureDer);
|
|
246
|
+
if (result.valid) {
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (!result.valid) {
|
|
251
|
+
return { verified: false };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const inner = parseMimeEntity(innerText);
|
|
255
|
+
return {
|
|
256
|
+
verified: true,
|
|
257
|
+
signerCertificateDer: result.signerCertificateDer,
|
|
258
|
+
protectedHeaders: headersToProtectedHeaders(inner.headers),
|
|
259
|
+
protectedHeaderFields: inner.fields,
|
|
260
|
+
bodyContentType: inner.headers["content-type"],
|
|
261
|
+
bodyText: decodeBodyText(inner),
|
|
262
|
+
displayBody: extractDisplayBody(inner),
|
|
263
|
+
attachments: extractAttachments(inner),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Builds an `application/pkcs7-mime; smime-type="enveloped-data"` message with RFC 9788 header
|
|
269
|
+
* protection (`hp="cipher"`), optionally sign-then-encrypt when `signing` is supplied (opaque
|
|
270
|
+
* signing — see this module's own doc comment for why). Per the spec's "Encrypt to Self"
|
|
271
|
+
* requirement, `recipientCertDers` MUST include the sender's own encryption certificate.
|
|
272
|
+
*/
|
|
273
|
+
export async function buildEncryptedMessage(
|
|
274
|
+
bodyContentType: string,
|
|
275
|
+
bodyText: string,
|
|
276
|
+
protectedHeaders: ProtectedHeaders,
|
|
277
|
+
outerHeaders: ProtectedHeaders,
|
|
278
|
+
recipientCertDers: Uint8Array[],
|
|
279
|
+
signing?: { certDer: Uint8Array; privateKey: CryptoKey },
|
|
280
|
+
): Promise<MimePart> {
|
|
281
|
+
const plaintextEntity = [
|
|
282
|
+
protectedHeaderLines(protectedHeaders, outerHeaders),
|
|
283
|
+
`Content-Type: ${flattenHeaderValue(bodyContentType)}; hp="cipher"`,
|
|
284
|
+
"",
|
|
285
|
+
bodyText,
|
|
286
|
+
].join(CRLF);
|
|
287
|
+
|
|
288
|
+
let contentToEncrypt: Uint8Array;
|
|
289
|
+
if (signing) {
|
|
290
|
+
const signedDer = await signOpaque(new TextEncoder().encode(plaintextEntity), signing.certDer, signing.privateKey);
|
|
291
|
+
contentToEncrypt = new TextEncoder().encode(
|
|
292
|
+
[
|
|
293
|
+
`Content-Type: application/pkcs7-mime; smime-type="signed-data"; name="smime.p7m"`,
|
|
294
|
+
`Content-Transfer-Encoding: base64`,
|
|
295
|
+
"",
|
|
296
|
+
base64Wrapped(signedDer),
|
|
297
|
+
].join(CRLF),
|
|
298
|
+
);
|
|
299
|
+
} else {
|
|
300
|
+
contentToEncrypt = new TextEncoder().encode(plaintextEntity);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const envelopedDer = await encryptForRecipients(contentToEncrypt, recipientCertDers);
|
|
304
|
+
return {
|
|
305
|
+
contentType: `application/pkcs7-mime; smime-type="enveloped-data"; name="smime.p7m"`,
|
|
306
|
+
additionalHeaders: { "Content-Transfer-Encoding": "base64", "Content-Disposition": 'attachment; filename="smime.p7m"' },
|
|
307
|
+
body: base64Wrapped(envelopedDer),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Combines a `MimePart` (from `buildSignedOnlyMessage()`/`buildEncryptedMessage()`) with the outer
|
|
313
|
+
* envelope's own headers into one complete RFC 5322 message source, ready for `mailApi.ts`'s
|
|
314
|
+
* `assembleDraftRaw()`. `outerHeaders` MUST be the already-obscured headers for an encrypted message
|
|
315
|
+
* (`applyBaselineOuterHeaders()`'s output) - this function does no obscuring itself, it only serializes
|
|
316
|
+
* whatever headers it's given. Deliberately excludes `Bcc`: that recipient list is submission-only and
|
|
317
|
+
* must never appear as a message header (see `AssembleDraftRawInput`'s own doc comment - `bcc` is passed
|
|
318
|
+
* to that call separately, never baked into `rawMime`). Header values are sanitized and RFC 2047-encoded
|
|
319
|
+
* the same way as the protected headers (see `headerLines()`).
|
|
320
|
+
*/
|
|
321
|
+
export function assembleOutboundMime(outerHeaders: ProtectedHeaders, part: MimePart): string {
|
|
322
|
+
const lines = [
|
|
323
|
+
...headerLines(outerHeaders),
|
|
324
|
+
`MIME-Version: 1.0`,
|
|
325
|
+
`Content-Type: ${flattenHeaderValue(part.contentType)}`,
|
|
326
|
+
...Object.entries(part.additionalHeaders ?? {}).map(([key, value]) => `${flattenHeaderValue(key)}: ${flattenHeaderValue(value)}`),
|
|
327
|
+
"",
|
|
328
|
+
part.body,
|
|
329
|
+
];
|
|
330
|
+
return lines.join(CRLF);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export interface ParsedEncryptedMessage {
|
|
334
|
+
decrypted: boolean;
|
|
335
|
+
/** Whether an inner (opaque) signature was present and verified - `undefined` when the message
|
|
336
|
+
* was encrypted without also being signed. */
|
|
337
|
+
signatureVerified?: boolean;
|
|
338
|
+
signerCertificateDer?: Uint8Array;
|
|
339
|
+
protectedHeaders?: ProtectedHeaders;
|
|
340
|
+
/** Every header field of the decrypted (and unwrapped) protected entity, in order, repeats included. */
|
|
341
|
+
protectedHeaderFields?: MimeHeaderField[];
|
|
342
|
+
bodyContentType?: string;
|
|
343
|
+
bodyText?: string;
|
|
344
|
+
/** `true` when `actualOuterHeaders` was supplied and disagrees with the `HP-Outer:` field copies
|
|
345
|
+
* found in the decrypted protected content - RFC 9788's own requirement to "visually distinguish a
|
|
346
|
+
* message whose outer and protected headers disagree" (a MITM or malicious intermediary rewrote the
|
|
347
|
+
* unprotected envelope after signing/encryption). `false` when both were supplied and agree.
|
|
348
|
+
* `undefined` when there's nothing to compare - no `actualOuterHeaders` given, or the message
|
|
349
|
+
* carries no `HP-Outer:` lines at all (a foreign sender's S/MIME implementation that doesn't write
|
|
350
|
+
* them, or a message from before this field existed). */
|
|
351
|
+
headerTamperDetected?: boolean;
|
|
352
|
+
/** The displayable body: `html` only for a real text/html part, `text` for text/plain. */
|
|
353
|
+
displayBody?: DisplayBody;
|
|
354
|
+
/** `true` when decryption was refused because the content encryption isn't AEAD (see `smime.ts`'s
|
|
355
|
+
* `UnsupportedContentEncryptionError`) - lets the caller explain that instead of "wrong key". */
|
|
356
|
+
unsupportedContentEncryption?: boolean;
|
|
357
|
+
/** The attachments inside the decrypted (and, when signed, verified) entity. */
|
|
358
|
+
attachments?: MimeAttachment[];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** RFC 9788's `HP-Outer:` field-copy fields to actually compare - a subset of `ProtectedHeaders`
|
|
362
|
+
* (`messageId` has no `HP-Outer` counterpart; only the fields the outer envelope itself carries do).
|
|
363
|
+
* Exported so `messageSecurity.ts` can build one from a received message's real outer envelope. */
|
|
364
|
+
export type ComparableOuterHeaders = Partial<Pick<ProtectedHeaders, "from" | "to" | "cc" | "date" | "subject">>;
|
|
365
|
+
|
|
366
|
+
/** Extracts the outer-envelope header values RFC 9788's `HP-Outer: <Field>: <value>` mechanism embeds
|
|
367
|
+
* inside an encrypted message's protected content - one line per outer header, all sharing the literal
|
|
368
|
+
* field name `HP-Outer`, which `splitHeadersAndBody()`'s own flat header map can hold only the last of
|
|
369
|
+
* (a real bug if reused for this - JSON/`Record` keys aren't multi-valued). Scans the raw header block
|
|
370
|
+
* text directly instead. Returns `undefined` when no `HP-Outer:` lines are present at all, so a caller
|
|
371
|
+
* can distinguish "nothing to compare" from "compared and everything matched". */
|
|
372
|
+
function extractHpOuterHeaders(fields: MimeHeaderField[]): ComparableOuterHeaders | undefined {
|
|
373
|
+
const result: ComparableOuterHeaders = {};
|
|
374
|
+
let found = false;
|
|
375
|
+
for (const { name, value } of fields) {
|
|
376
|
+
const match = name.toLowerCase() === "hp-outer" ? /^(From|To|Cc|Date|Subject):\s*(.*)$/i.exec(value) : null;
|
|
377
|
+
if (!match) {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
found = true;
|
|
381
|
+
result[match[1].toLowerCase() as keyof ComparableOuterHeaders] = match[2].trim();
|
|
382
|
+
}
|
|
383
|
+
return found ? result : undefined;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/** `true` when every field `hpOuter` actually carries matches `actualOuterHeaders`' value for that same
|
|
387
|
+
* field - a field `hpOuter` doesn't carry (e.g. no `Cc` on this message) is not compared, since RFC
|
|
388
|
+
* 9788 never claims coverage for it either way. */
|
|
389
|
+
function outerHeadersMatch(hpOuter: ComparableOuterHeaders, actualOuterHeaders: ComparableOuterHeaders): boolean {
|
|
390
|
+
return (Object.keys(hpOuter) as (keyof ComparableOuterHeaders)[]).every((field) => hpOuter[field] === actualOuterHeaders[field]);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** Decrypts (and, if present, verifies the inner signature of) a message built by
|
|
394
|
+
* `buildEncryptedMessage()`. Returns `{ decrypted: false }` (never throws) for anything malformed or
|
|
395
|
+
* undecryptable with the given key — the caller's own concern is distinguishing "wrong key" from
|
|
396
|
+
* "corrupt message," neither of which this function treats as exceptional.
|
|
397
|
+
*
|
|
398
|
+
* `actualOuterHeaders` — the received message's real, unprotected outer envelope headers (as the
|
|
399
|
+
* caller itself read them, before ever calling this function) — enables the RFC 9788 `HP-Outer`
|
|
400
|
+
* comparison (see `ParsedEncryptedMessage.headerTamperDetected`); omit it to skip that check entirely.
|
|
401
|
+
*/
|
|
402
|
+
export async function parseEncryptedMessage(
|
|
403
|
+
base64Body: string,
|
|
404
|
+
recipientCertDer: Uint8Array,
|
|
405
|
+
recipientPrivateKey: CryptoKey,
|
|
406
|
+
actualOuterHeaders?: ComparableOuterHeaders,
|
|
407
|
+
): Promise<ParsedEncryptedMessage> {
|
|
408
|
+
return parseEncryptedMessageWithKeys(base64Body, [{ certDer: recipientCertDer, privateKey: recipientPrivateKey }], actualOuterHeaders);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/** `parseEncryptedMessage()` with several candidate keys - the active encryption key first, then retained older ones -
|
|
412
|
+
* decrypted by `smime.ts`'s `decryptEnvelopedDataWithKeys()` (recipient-identifier matching first, then a bounded
|
|
413
|
+
* trial). Same never-throws contract. */
|
|
414
|
+
export async function parseEncryptedMessageWithKeys(
|
|
415
|
+
base64Body: string,
|
|
416
|
+
keys: DecryptionKey[],
|
|
417
|
+
actualOuterHeaders?: ComparableOuterHeaders,
|
|
418
|
+
): Promise<ParsedEncryptedMessage> {
|
|
419
|
+
const envelopedDer = decodeBase64Text(base64Body);
|
|
420
|
+
if (!envelopedDer) {
|
|
421
|
+
return { decrypted: false };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
let decrypted: Uint8Array;
|
|
425
|
+
try {
|
|
426
|
+
decrypted = await decryptEnvelopedDataWithKeys(envelopedDer, keys);
|
|
427
|
+
} catch (err) {
|
|
428
|
+
return err instanceof UnsupportedContentEncryptionError ? { decrypted: false, unsupportedContentEncryption: true } : { decrypted: false };
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function compareTamper(fields: MimeHeaderField[]): boolean | undefined {
|
|
432
|
+
const hpOuter = extractHpOuterHeaders(fields);
|
|
433
|
+
if (!hpOuter || !actualOuterHeaders) {
|
|
434
|
+
return undefined;
|
|
435
|
+
}
|
|
436
|
+
return !outerHeadersMatch(hpOuter, actualOuterHeaders);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Binary strings (see `mime.ts`), so an 8bit body's charset is applied to its real bytes.
|
|
440
|
+
let entity = parseMimeEntity(bytesToBinaryString(decrypted));
|
|
441
|
+
const wrapper = parseParameterizedHeader(entity.headers["content-type"]);
|
|
442
|
+
let signature: Pick<ParsedEncryptedMessage, "signatureVerified" | "signerCertificateDer"> = {};
|
|
443
|
+
|
|
444
|
+
if (PKCS7_MIME_CONTENT_TYPES.has(wrapper.value) && wrapper.params["smime-type"]?.toLowerCase() === "signed-data") {
|
|
445
|
+
const signedDer = decodeBase64Text(entity.body);
|
|
446
|
+
if (!signedDer) {
|
|
447
|
+
return { decrypted: false };
|
|
448
|
+
}
|
|
449
|
+
const verifyResult = await verifyOpaque(signedDer);
|
|
450
|
+
if (!verifyResult.valid) {
|
|
451
|
+
return { decrypted: true, signatureVerified: false };
|
|
452
|
+
}
|
|
453
|
+
// verifyOpaque() always returns `content` alongside `valid: true`.
|
|
454
|
+
entity = parseMimeEntity(bytesToBinaryString(verifyResult.content as Uint8Array));
|
|
455
|
+
signature = { signatureVerified: true, signerCertificateDer: verifyResult.signerCertificateDer };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return {
|
|
459
|
+
decrypted: true,
|
|
460
|
+
...signature,
|
|
461
|
+
protectedHeaders: headersToProtectedHeaders(entity.headers),
|
|
462
|
+
protectedHeaderFields: entity.fields,
|
|
463
|
+
bodyContentType: entity.headers["content-type"],
|
|
464
|
+
bodyText: decodeBodyText(entity),
|
|
465
|
+
displayBody: extractDisplayBody(entity),
|
|
466
|
+
attachments: extractAttachments(entity),
|
|
467
|
+
headerTamperDetected: compareTamper(entity.fields),
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** Splits a MIME entity's raw text into its header lines (lowercased-key map, first occurrence of each
|
|
472
|
+
* name, unfolded) and raw body, plus the entity's own Content-Type value for convenience. Kept for
|
|
473
|
+
* backward compatibility - new code should use `mime.ts`'s `parseMimeEntity()`, which also exposes every
|
|
474
|
+
* repeated field in order. Tolerates bare-LF line endings. */
|
|
475
|
+
export function splitHeadersAndBody(entity: string): { headers: Record<string, string>; contentType?: string; body: string; rawHeaderBlock: string } {
|
|
476
|
+
const { headers, body, rawHeaderBlock } = parseMimeEntity(entity);
|
|
477
|
+
return { headers, contentType: headers["content-type"], body, rawHeaderBlock };
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function headersToProtectedHeaders(headers: Record<string, string>): ProtectedHeaders {
|
|
481
|
+
return {
|
|
482
|
+
from: headers["from"] ?? "",
|
|
483
|
+
to: headers["to"] ?? "",
|
|
484
|
+
cc: headers["cc"],
|
|
485
|
+
date: headers["date"] ?? "",
|
|
486
|
+
// Display text: RFC 2047 encoded-words / raw UTF-8 decoded. The address fields stay raw - they are
|
|
487
|
+
// only ever compared (via `extractAddresses()`), never shown.
|
|
488
|
+
subject: decodeHeaderText(headers["subject"] ?? ""),
|
|
489
|
+
messageId: headers["message-id"] ?? "",
|
|
490
|
+
};
|
|
491
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* Destroys every unlocked mailbox's in-memory keys after this device's configured idle period
|
|
7
|
+
* (`idleTimeout.ts`) with no user activity — `specs/end-to-end_encryption.md`'s "destroyed on ...
|
|
8
|
+
* a configurable idle period" trigger for `keySession.ts`'s `destroyUnlockedKeys()`.
|
|
9
|
+
*
|
|
10
|
+
* Listened at the `document` level (not scoped to any one app's own content area) specifically so
|
|
11
|
+
* activity in *any* app — Contacts, Calendar, Tasks, not just Mail/Settings, where the unlocked keys
|
|
12
|
+
* are actually read/used — resets the clock. A user reading a long document in Tasks for 40 minutes
|
|
13
|
+
* without touching Mail again is still an actively-attended device, not an idle one; scoping this to
|
|
14
|
+
* only the app(s) that touch key material would time it out from underneath a session that was never
|
|
15
|
+
* actually unattended.
|
|
16
|
+
*
|
|
17
|
+
* **Iframes.** Events inside an iframe never reach the parent `document`, so activity is also listened
|
|
18
|
+
* for inside every *same-origin* iframe's own document (attached as iframes appear or (re)load, found via
|
|
19
|
+
* a `MutationObserver` plus a capture-phase `load` listener). A sandboxed/cross-origin iframe's document
|
|
20
|
+
* is inaccessible by design — notably `MessageDetailPane`'s `sandbox=""` message body — so for those the
|
|
21
|
+
* one observable signal is used instead: the parent window blurring *because focus moved into an iframe*
|
|
22
|
+
* (a click inside it) counts as activity. Scrolling such an iframe without clicking is not observable.
|
|
23
|
+
*
|
|
24
|
+
* **Sleep/suspend.** Timers don't run while a laptop sleeps, so a `setTimeout` alone could fire long after
|
|
25
|
+
* the idle deadline has passed by wall clock. Whenever the page becomes visible or the window regains
|
|
26
|
+
* focus, elapsed wall-clock time since the last activity is checked and keys are destroyed immediately if
|
|
27
|
+
* the deadline already passed (otherwise the timer is re-armed for just the remaining time).
|
|
28
|
+
*/
|
|
29
|
+
import { useEffect } from "react";
|
|
30
|
+
import { destroyUnlockedKeys } from "./keySession.js";
|
|
31
|
+
import { getIdleTimeoutMinutes } from "./idleTimeout.js";
|
|
32
|
+
|
|
33
|
+
const ACTIVITY_EVENTS = ["mousedown", "keydown", "scroll", "touchstart"] as const;
|
|
34
|
+
|
|
35
|
+
/** Reads an iframe's document if (and only if) this origin may access it — `null` for a cross-origin or
|
|
36
|
+
* `sandbox`ed (opaque-origin) frame, whichever way the browser reports that (a `null` or a throw). */
|
|
37
|
+
function accessibleFrameDocument(frame: HTMLIFrameElement): Document | null {
|
|
38
|
+
try {
|
|
39
|
+
return frame.contentDocument;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Safe to mount unconditionally, even before any mailbox has been unlocked this session -
|
|
47
|
+
* `destroyUnlockedKeys()` is a no-op against an empty session store, so an idle period elapsing with
|
|
48
|
+
* nothing unlocked yet has no observable effect. Reads the configured duration once, at mount - the
|
|
49
|
+
* host component (`AppShell.tsx`) remounts on every navigation in this framework's own no-client-
|
|
50
|
+
* router design (see that component's own doc comment), so a preference change made on the Settings
|
|
51
|
+
* page takes effect the next time the user goes anywhere, without needing this hook to poll for
|
|
52
|
+
* changes itself.
|
|
53
|
+
*/
|
|
54
|
+
export function useIdleKeyTimeout(): void {
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const minutes = getIdleTimeoutMinutes();
|
|
57
|
+
if (!minutes) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const timeoutMs = minutes * 60_000;
|
|
61
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
62
|
+
let blurCheck: ReturnType<typeof setTimeout> | undefined;
|
|
63
|
+
let lastActivity = Date.now();
|
|
64
|
+
|
|
65
|
+
function arm(delayMs: number) {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
timer = setTimeout(() => destroyUnlockedKeys(), delayMs);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function reset() {
|
|
71
|
+
lastActivity = Date.now();
|
|
72
|
+
arm(timeoutMs);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Wall-clock check on resume - see this module's doc comment. */
|
|
76
|
+
function checkElapsed() {
|
|
77
|
+
if (document.visibilityState === "hidden") {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const remaining = timeoutMs - (Date.now() - lastActivity);
|
|
81
|
+
if (remaining <= 0) {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
destroyUnlockedKeys();
|
|
84
|
+
} else {
|
|
85
|
+
arm(remaining);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function handleWindowBlur() {
|
|
90
|
+
// `document.activeElement` isn't reliably updated to the iframe until after the blur event
|
|
91
|
+
// itself has finished dispatching, so check on the next tick.
|
|
92
|
+
clearTimeout(blurCheck);
|
|
93
|
+
blurCheck = setTimeout(() => {
|
|
94
|
+
if (document.activeElement instanceof HTMLIFrameElement) {
|
|
95
|
+
reset();
|
|
96
|
+
}
|
|
97
|
+
}, 0);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const frameDocs = new Map<HTMLIFrameElement, Document>();
|
|
101
|
+
|
|
102
|
+
function attachFrame(frame: HTMLIFrameElement) {
|
|
103
|
+
const doc = accessibleFrameDocument(frame);
|
|
104
|
+
const previous = frameDocs.get(frame);
|
|
105
|
+
if (previous === doc) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (previous) {
|
|
109
|
+
removeActivityListeners(previous);
|
|
110
|
+
frameDocs.delete(frame);
|
|
111
|
+
}
|
|
112
|
+
if (doc) {
|
|
113
|
+
addActivityListeners(doc);
|
|
114
|
+
frameDocs.set(frame, doc);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function scanFrames() {
|
|
119
|
+
const current = new Set(document.querySelectorAll("iframe"));
|
|
120
|
+
for (const [frame, doc] of frameDocs) {
|
|
121
|
+
if (!current.has(frame)) {
|
|
122
|
+
removeActivityListeners(doc);
|
|
123
|
+
frameDocs.delete(frame);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const frame of current) {
|
|
127
|
+
attachFrame(frame);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function handleLoad(e: Event) {
|
|
132
|
+
if (e.target instanceof HTMLIFrameElement) {
|
|
133
|
+
attachFrame(e.target);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function addActivityListeners(target: Document) {
|
|
138
|
+
for (const event of ACTIVITY_EVENTS) {
|
|
139
|
+
target.addEventListener(event, reset, { passive: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function removeActivityListeners(target: Document) {
|
|
144
|
+
for (const event of ACTIVITY_EVENTS) {
|
|
145
|
+
target.removeEventListener(event, reset);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
reset();
|
|
150
|
+
addActivityListeners(document);
|
|
151
|
+
document.addEventListener("visibilitychange", checkElapsed);
|
|
152
|
+
document.addEventListener("load", handleLoad, true);
|
|
153
|
+
window.addEventListener("focus", checkElapsed);
|
|
154
|
+
window.addEventListener("pageshow", checkElapsed);
|
|
155
|
+
window.addEventListener("blur", handleWindowBlur);
|
|
156
|
+
const observer = new MutationObserver(scanFrames);
|
|
157
|
+
observer.observe(document.documentElement, { childList: true, subtree: true });
|
|
158
|
+
scanFrames();
|
|
159
|
+
|
|
160
|
+
return () => {
|
|
161
|
+
clearTimeout(timer);
|
|
162
|
+
clearTimeout(blurCheck);
|
|
163
|
+
observer.disconnect();
|
|
164
|
+
removeActivityListeners(document);
|
|
165
|
+
document.removeEventListener("visibilitychange", checkElapsed);
|
|
166
|
+
document.removeEventListener("load", handleLoad, true);
|
|
167
|
+
window.removeEventListener("focus", checkElapsed);
|
|
168
|
+
window.removeEventListener("pageshow", checkElapsed);
|
|
169
|
+
window.removeEventListener("blur", handleWindowBlur);
|
|
170
|
+
for (const doc of frameDocs.values()) {
|
|
171
|
+
removeActivityListeners(doc);
|
|
172
|
+
}
|
|
173
|
+
frameDocs.clear();
|
|
174
|
+
};
|
|
175
|
+
}, []);
|
|
176
|
+
}
|