├── .gitattributes ├── .gitignore ├── .gitlab-ci.yml ├── .gitlab └── CODEOWNERS ├── .gitmodules ├── .golangci.yml ├── .grype.yaml ├── BUILDS.md ├── CONTRIBUTING.md ├── COPYING_NOTES.md ├── Changelog.md ├── LICENSE ├── Makefile ├── README.md ├── TODO.md ├── ci ├── build.yml ├── env.yml ├── report.yml ├── rules.yml ├── setup.yml └── test.yml ├── cmd ├── Desktop-Bridge │ ├── main.go │ └── main_test.go └── launcher │ ├── main.go │ └── main_test.go ├── dist ├── Bridge.icns ├── bridge.ico ├── bridge.svg ├── bridgeMacOS.svg ├── info.rc ├── proton-bridge.desktop └── raw │ ├── mac_icon_128x128.png │ ├── mac_icon_128x128@2x.png │ ├── mac_icon_16x16.png │ ├── mac_icon_16x16@2x.png │ ├── mac_icon_256x256.png │ ├── mac_icon_256x256@2x.png │ ├── mac_icon_32x32.png │ ├── mac_icon_32x32@2x.png │ ├── mac_icon_512x512.png │ ├── mac_icon_512x512@2x.png │ ├── win+lin_icon_16x16.svg │ ├── win+lin_icon_24x24.svg │ ├── win+lin_icon_256x256.png │ ├── win+lin_icon_256x256.svg │ ├── win+lin_icon_32x32.svg │ └── win+lin_icon_48x48.svg ├── go.mod ├── go.sum ├── internal ├── app │ ├── app.go │ ├── bridge.go │ ├── frontend.go │ ├── migration.go │ ├── migration_test.go │ ├── singleinstance.go │ ├── testdata │ │ ├── with_keys │ │ │ └── protonmail │ │ │ │ └── bridge │ │ │ │ ├── cert.pem │ │ │ │ ├── key.pem │ │ │ │ └── prefs.json │ │ └── without_keys │ │ │ └── protonmail │ │ │ └── bridge │ │ │ └── prefs.json │ └── vault.go ├── bridge │ ├── api.go │ ├── api_default.go │ ├── api_qa.go │ ├── bridge.go │ ├── bridge_test.go │ ├── bug_report.go │ ├── configure.go │ ├── debug.go │ ├── draft_test.go │ ├── errors.go │ ├── events.go │ ├── heartbeat.go │ ├── identifier.go │ ├── imap.go │ ├── imapsmtp_telemetry.go │ ├── keychain.go │ ├── locations.go │ ├── main_test.go │ ├── mocks.go │ ├── mocks │ │ ├── async_mocks.go │ │ ├── gluon_mocks.go │ │ ├── matcher.go │ │ ├── mocks.go │ │ ├── observability_mocks.go │ │ └── telemetry_mocks.go │ ├── observability_test.go │ ├── refresh_test.go │ ├── send_test.go │ ├── sentry_test.go │ ├── server_manager_test.go │ ├── settings.go │ ├── settings_test.go │ ├── smtp.go │ ├── sync_test.go │ ├── sync_unix_test.go │ ├── testdata │ │ ├── invite.eml │ │ └── text-plain.eml │ ├── tls.go │ ├── types.go │ ├── unleash_test.go │ ├── updates.go │ ├── updates_test.go │ ├── user.go │ ├── user_event_test.go │ ├── user_events.go │ └── user_test.go ├── certs │ ├── cert_store_darwin.go │ ├── cert_store_darwin_test.go │ ├── cert_store_linux.go │ ├── cert_store_windows.go │ ├── installer.go │ ├── tls.go │ └── tls_test.go ├── clientconfig │ ├── applemail.go │ └── applemail_test.go ├── constants │ ├── constants.go │ ├── host_default.go │ ├── host_qa.go │ ├── update_default.go │ ├── update_qa.go │ ├── version_default.go │ ├── version_qa.go │ └── version_test.go ├── cookies │ ├── jar.go │ └── jar_test.go ├── crash │ ├── actions.go │ ├── handler.go │ └── handler_test.go ├── dialer │ ├── dialer_basic.go │ ├── dialer_pinning.go │ ├── dialer_pinning_checker.go │ ├── dialer_pinning_checker_default.go │ ├── dialer_pinning_checker_qa.go │ ├── dialer_pinning_report.go │ ├── dialer_pinning_reporter.go │ ├── dialer_pinning_reporter_test.go │ ├── dialer_pinning_test.go │ ├── dialer_proxy.go │ ├── dialer_proxy_provider.go │ ├── dialer_proxy_provider_test.go │ ├── dialer_proxy_test.go │ └── dialer_test.go ├── errors.go ├── events │ ├── address.go │ ├── connection.go │ ├── error.go │ ├── events.go │ ├── label.go │ ├── mocks │ │ └── mocks.go │ ├── raise.go │ ├── serve.go │ ├── sync.go │ ├── update.go │ └── user.go ├── files │ ├── files.go │ └── files_test.go ├── focus │ ├── client.go │ ├── focus_test.go │ ├── proto │ │ ├── focus.go │ │ ├── focus.pb.go │ │ ├── focus.proto │ │ └── focus_grpc.pb.go │ └── service.go ├── frontend │ ├── .gitignore │ ├── Makefile.local │ ├── bridge-gui │ │ ├── BridgeSetup.cmake │ │ ├── CMakeLists.txt │ │ ├── FindQt.cmake │ │ ├── README.md │ │ ├── bridge-gui-tester │ │ │ ├── .lldbinit │ │ │ ├── AppController.cpp │ │ │ ├── AppController.h │ │ │ ├── CMakeLists.txt │ │ │ ├── Cert.cpp │ │ │ ├── Cert.h │ │ │ ├── Doxyfile │ │ │ ├── GRPCMetaDataProcessor.cpp │ │ │ ├── GRPCMetaDataProcessor.h │ │ │ ├── GRPCQtProxy.cpp │ │ │ ├── GRPCQtProxy.h │ │ │ ├── GRPCServerWorker.cpp │ │ │ ├── GRPCServerWorker.h │ │ │ ├── GRPCService.cpp │ │ │ ├── GRPCService.h │ │ │ ├── MainWindow.cpp │ │ │ ├── MainWindow.h │ │ │ ├── MainWindow.ui │ │ │ ├── Pch.h │ │ │ ├── Tabs │ │ │ │ ├── EventsTab.cpp │ │ │ │ ├── EventsTab.h │ │ │ │ ├── EventsTab.ui │ │ │ │ ├── KnowledgeBaseTab.cpp │ │ │ │ ├── KnowledgeBaseTab.h │ │ │ │ ├── KnowledgeBaseTab.ui │ │ │ │ ├── SettingsTab.cpp │ │ │ │ ├── SettingsTab.h │ │ │ │ ├── SettingsTab.ui │ │ │ │ ├── UsersTab.cpp │ │ │ │ ├── UsersTab.h │ │ │ │ └── UsersTab.ui │ │ │ ├── UserDialog.cpp │ │ │ ├── UserDialog.h │ │ │ ├── UserDialog.ui │ │ │ ├── UserTable.cpp │ │ │ ├── UserTable.h │ │ │ └── main.cpp │ │ ├── bridge-gui │ │ │ ├── .lldbinit │ │ │ ├── AppController.cpp │ │ │ ├── AppController.h │ │ │ ├── BridgeApp.cpp │ │ │ ├── BridgeApp.h │ │ │ ├── BuildConfig.h.in │ │ │ ├── CMakeLists.txt │ │ │ ├── ClipboardProxy.cpp │ │ │ ├── ClipboardProxy.h │ │ │ ├── CommandLine.cpp │ │ │ ├── CommandLine.h │ │ │ ├── DeployDarwin.cmake │ │ │ ├── DeployLinux.cmake │ │ │ ├── DeployWindows.cmake │ │ │ ├── Doxyfile │ │ │ ├── EventStreamWorker.cpp │ │ │ ├── EventStreamWorker.h │ │ │ ├── LogUtils.cpp │ │ │ ├── LogUtils.h │ │ │ ├── MacOS │ │ │ │ ├── DockIcon.cpp │ │ │ │ ├── DockIcon.h │ │ │ │ ├── DockIcon.mm │ │ │ │ ├── SecondInstance.h │ │ │ │ └── SecondInstance.mm │ │ │ ├── Pch.h │ │ │ ├── QMLBackend.cpp │ │ │ ├── QMLBackend.h │ │ │ ├── README.md │ │ │ ├── Resources.qrc │ │ │ ├── Resources.rc.in │ │ │ ├── SentryUtils.cpp │ │ │ ├── SentryUtils.h │ │ │ ├── Settings.cpp │ │ │ ├── Settings.h │ │ │ ├── TrayIcon.cpp │ │ │ ├── TrayIcon.h │ │ │ ├── UserList.cpp │ │ │ ├── UserList.h │ │ │ ├── build.ps1 │ │ │ ├── build.sh │ │ │ ├── main.cpp │ │ │ ├── qml │ │ │ │ ├── AccountDelegate.qml │ │ │ │ ├── AccountView.qml │ │ │ │ ├── Banner.qml │ │ │ │ ├── Bridge.qml │ │ │ │ ├── BugReport │ │ │ │ │ ├── BugCategoryView.qml │ │ │ │ │ ├── BugQuestionView.qml │ │ │ │ │ ├── BugReportFlow.qml │ │ │ │ │ ├── BugReportView.qml │ │ │ │ │ ├── CategoryItem.qml │ │ │ │ │ └── QuestionItem.qml │ │ │ │ ├── Configuration.qml │ │ │ │ ├── ConfigurationItem.qml │ │ │ │ ├── ConnectionModeSettings.qml │ │ │ │ ├── ContentWrapper.qml │ │ │ │ ├── DebugWrapper.qml │ │ │ │ ├── GeneralSettings.qml │ │ │ │ ├── HelpView.qml │ │ │ │ ├── KeychainSettings.qml │ │ │ │ ├── LocalCacheSettings.qml │ │ │ │ ├── MainWindow.qml │ │ │ │ ├── NoAccountView.qml │ │ │ │ ├── NotificationDialog.qml │ │ │ │ ├── NotificationPopups.qml │ │ │ │ ├── Notifications │ │ │ │ │ ├── Notification.qml │ │ │ │ │ ├── NotificationFilter.qml │ │ │ │ │ ├── Notifications.qml │ │ │ │ │ └── qmldir │ │ │ │ ├── PortSettings.qml │ │ │ │ ├── Proton │ │ │ │ │ ├── Action.qml │ │ │ │ │ ├── ApplicationWindow.qml │ │ │ │ │ ├── Button.qml │ │ │ │ │ ├── CheckBox.qml │ │ │ │ │ ├── ColorScheme.qml │ │ │ │ │ ├── ComboBox.qml │ │ │ │ │ ├── ContextMenu.qml │ │ │ │ │ ├── Dialog.qml │ │ │ │ │ ├── InfoTooltip.qml │ │ │ │ │ ├── Label.qml │ │ │ │ │ ├── LinkLabel.qml │ │ │ │ │ ├── Menu.qml │ │ │ │ │ ├── MenuItem.qml │ │ │ │ │ ├── Popup.qml │ │ │ │ │ ├── RadioButton.qml │ │ │ │ │ ├── Style.qml │ │ │ │ │ ├── Switch.qml │ │ │ │ │ ├── TextArea.qml │ │ │ │ │ ├── TextField.qml │ │ │ │ │ ├── Toggle.qml │ │ │ │ │ └── qmldir │ │ │ │ ├── Resources │ │ │ │ │ ├── Help │ │ │ │ │ │ ├── Template.html │ │ │ │ │ │ ├── WhyBridge.html │ │ │ │ │ │ ├── WhyCertificate.html │ │ │ │ │ │ └── WhyProfileWarning.html │ │ │ │ │ └── bug_report_flow.json │ │ │ │ ├── SettingsItem.qml │ │ │ │ ├── SettingsView.qml │ │ │ │ ├── SetupWizard │ │ │ │ │ ├── ClientConfigAppleMail.qml │ │ │ │ │ ├── ClientConfigCertInstall.qml │ │ │ │ │ ├── ClientConfigEnd.qml │ │ │ │ │ ├── ClientConfigParameters.qml │ │ │ │ │ ├── ClientConfigSelector.qml │ │ │ │ │ ├── ClientListItem.qml │ │ │ │ │ ├── HelpButton.qml │ │ │ │ │ ├── LeftPane.qml │ │ │ │ │ ├── Login.qml │ │ │ │ │ ├── Onboarding.qml │ │ │ │ │ ├── SetupWizard.qml │ │ │ │ │ └── StepDescriptionBox.qml │ │ │ │ ├── SplashScreen.qml │ │ │ │ ├── Status.qml │ │ │ │ ├── UserNotificationDialog.qml │ │ │ │ └── icons │ │ │ │ │ ├── Loader_16.svg │ │ │ │ │ ├── Loader_48.svg │ │ │ │ │ ├── ic-alert.svg │ │ │ │ │ ├── ic-apple-mail.svg │ │ │ │ │ ├── ic-arrow-left.svg │ │ │ │ │ ├── ic-bridge.svg │ │ │ │ │ ├── ic-card-identity.svg │ │ │ │ │ ├── ic-check.svg │ │ │ │ │ ├── ic-chevron-down.svg │ │ │ │ │ ├── ic-chevron-left.svg │ │ │ │ │ ├── ic-chevron-right.svg │ │ │ │ │ ├── ic-chevron-up.svg │ │ │ │ │ ├── ic-cog-wheel.svg │ │ │ │ │ ├── ic-connected.svg │ │ │ │ │ ├── ic-copy.svg │ │ │ │ │ ├── ic-cross-close.svg │ │ │ │ │ ├── ic-dot.svg │ │ │ │ │ ├── ic-drive.svg │ │ │ │ │ ├── ic-exclamation-circle-filled.svg │ │ │ │ │ ├── ic-external-link.svg │ │ │ │ │ ├── ic-eye-slash.svg │ │ │ │ │ ├── ic-eye.svg │ │ │ │ │ ├── ic-illustrative-view-html-code.svg │ │ │ │ │ ├── ic-info-circle-filled.svg │ │ │ │ │ ├── ic-info-circle.svg │ │ │ │ │ ├── ic-info.svg │ │ │ │ │ ├── ic-microsoft-outlook.svg │ │ │ │ │ ├── ic-mozilla-thunderbird.svg │ │ │ │ │ ├── ic-no-connection.svg │ │ │ │ │ ├── ic-notification-bell.svg │ │ │ │ │ ├── ic-other-mail-clients.svg │ │ │ │ │ ├── ic-plus.svg │ │ │ │ │ ├── ic-question-circle.svg │ │ │ │ │ ├── ic-splash-check.svg │ │ │ │ │ ├── ic-success.svg │ │ │ │ │ ├── ic-three-dots-vertical.svg │ │ │ │ │ ├── ic-trash.svg │ │ │ │ │ ├── ic-warning-orange.svg │ │ │ │ │ ├── img-client-config-selector.svg │ │ │ │ │ ├── img-client-config-success.svg │ │ │ │ │ ├── img-macos-cert-screenshot.png │ │ │ │ │ ├── img-macos-profile-screenshot.png │ │ │ │ │ ├── img-mail-clients.svg │ │ │ │ │ ├── img-mail-logo-wordmark-dark.svg │ │ │ │ │ ├── img-mail-logo-wordmark.svg │ │ │ │ │ ├── img-proton-logos.png │ │ │ │ │ ├── img-proton-logos.svg │ │ │ │ │ ├── img-splash.png │ │ │ │ │ ├── img-splash.svg │ │ │ │ │ ├── img-welcome.svg │ │ │ │ │ ├── product_logos.svg │ │ │ │ │ ├── product_logos_dark.svg │ │ │ │ │ ├── systray-color-error.png │ │ │ │ │ ├── systray-color-norm.png │ │ │ │ │ ├── systray-color-update.png │ │ │ │ │ ├── systray-color-warn.png │ │ │ │ │ ├── systray-mono-error.png │ │ │ │ │ ├── systray-mono-norm.png │ │ │ │ │ ├── systray-mono-update.png │ │ │ │ │ ├── systray-mono-warn.png │ │ │ │ │ └── systray.svg │ │ │ └── vcpkg │ │ │ │ └── triplets │ │ │ │ ├── arm64-osx-min-11-0.cmake │ │ │ │ └── x64-osx-min-10-15.cmake │ │ └── bridgepp │ │ │ ├── CMakeLists.txt │ │ │ ├── Doxyfile │ │ │ ├── Pch.h │ │ │ ├── Test │ │ │ ├── TestBridgeUtils.cpp │ │ │ ├── TestBugReportFlow.cpp │ │ │ ├── TestBugReportFlow.h │ │ │ ├── TestCLI.cpp │ │ │ ├── TestException.cpp │ │ │ ├── TestSessionID.cpp │ │ │ ├── TestWorker.cpp │ │ │ └── TestWorker.h │ │ │ └── bridgepp │ │ │ ├── BridgeUtils.cpp │ │ │ ├── BridgeUtils.h │ │ │ ├── BugReportFlow │ │ │ ├── BugReportFlow.cpp │ │ │ └── BugReportFlow.h │ │ │ ├── CLI │ │ │ ├── CLIUtils.cpp │ │ │ └── CLIUtils.h │ │ │ ├── Exception │ │ │ ├── Exception.cpp │ │ │ └── Exception.h │ │ │ ├── FocusGRPC │ │ │ ├── FocusGRPCClient.cpp │ │ │ └── FocusGRPCClient.h │ │ │ ├── GRPC │ │ │ ├── EventFactory.cpp │ │ │ ├── EventFactory.h │ │ │ ├── GRPCClient.cpp │ │ │ ├── GRPCClient.h │ │ │ ├── GRPCConfig.cpp │ │ │ ├── GRPCConfig.h │ │ │ ├── GRPCErrors.cpp │ │ │ ├── GRPCErrors.h │ │ │ ├── GRPCUtils.cpp │ │ │ └── GRPCUtils.h │ │ │ ├── Log │ │ │ ├── Log.cpp │ │ │ ├── Log.h │ │ │ ├── LogUtils.cpp │ │ │ └── LogUtils.h │ │ │ ├── ProcessMonitor.cpp │ │ │ ├── ProcessMonitor.h │ │ │ ├── SessionID │ │ │ ├── SessionID.cpp │ │ │ └── SessionID.h │ │ │ ├── User │ │ │ ├── User.cpp │ │ │ └── User.h │ │ │ └── Worker │ │ │ ├── Overseer.cpp │ │ │ ├── Overseer.h │ │ │ └── Worker.h │ ├── cli │ │ ├── account_utils.go │ │ ├── accounts.go │ │ ├── debug.go │ │ ├── frontend.go │ │ ├── system.go │ │ ├── updates.go │ │ └── utils.go │ ├── grpc │ │ ├── bridge.pb.go │ │ ├── bridge.proto │ │ ├── bridge_grpc.pb.go │ │ ├── event_factory.go │ │ ├── event_utils.go │ │ ├── service.go │ │ ├── service_cert.go │ │ ├── service_methods.go │ │ ├── service_stream.go │ │ ├── service_user.go │ │ ├── types.go │ │ ├── utils.go │ │ └── utils_test.go │ └── theme │ │ ├── detect_darwin.go │ │ ├── detect_default.go │ │ ├── detect_windows.go │ │ ├── theme.go │ │ └── theme_test.go ├── hv │ ├── hv.go │ └── hv_test.go ├── identifier │ └── identifier.go ├── kb │ ├── kbArticleList.json │ ├── suggester.go │ └── suggester_test.go ├── legacy │ └── credentials │ │ ├── credentials.go │ │ ├── credentials_test.go │ │ ├── store.go │ │ └── store_test.go ├── locations │ ├── locations.go │ ├── locations_test.go │ └── provider.go ├── logging │ ├── compression.go │ ├── compression_test.go │ ├── crash.go │ ├── crash_test.go │ ├── imap_logger.go │ ├── logging.go │ ├── logging_test.go │ ├── pruning.go │ ├── pruning_test.go │ ├── rotator.go │ ├── rotator_test.go │ ├── sensitive_default.go │ ├── sensitive_sensitive.go │ ├── session_id.go │ ├── session_id_test.go │ └── smtp_logger.go ├── network │ └── proton.go ├── plan │ └── plan.go ├── safe │ └── mutex.go ├── sentry │ ├── hostarch_darwin.go │ ├── hostarch_default.go │ ├── lang_default.go │ ├── reporter.go │ └── reporter_test.go ├── service │ ├── config.go │ ├── config_test.go │ └── types.go ├── services │ ├── imapservice │ │ ├── api_client.go │ │ ├── conflicts.go │ │ ├── conflicts_test.go │ │ ├── connector.go │ │ ├── connector_test.go │ │ ├── helpers.go │ │ ├── imap_updates.go │ │ ├── mocks │ │ │ └── mocks.go │ │ ├── observabilitymetrics │ │ │ ├── evtloopmsgevents │ │ │ │ └── metrics.go │ │ │ └── syncmsgevents │ │ │ │ └── metrics.go │ │ ├── server_manager.go │ │ ├── service.go │ │ ├── service_address_events.go │ │ ├── service_label_events.go │ │ ├── service_message_events.go │ │ ├── service_sync_events.go │ │ ├── shared_cache.go │ │ ├── shared_identity.go │ │ ├── shared_labels.go │ │ ├── sync_build.go │ │ ├── sync_build_test.go │ │ ├── sync_message_builder.go │ │ ├── sync_reporter.go │ │ ├── sync_state_provider.go │ │ ├── sync_state_provider_test.go │ │ ├── sync_update_applier.go │ │ ├── utils.go │ │ └── utils_test.go │ ├── imapsmtpserver │ │ ├── imap.go │ │ ├── listener.go │ │ ├── service.go │ │ ├── smtp.go │ │ └── telemetry.go │ ├── notifications │ │ ├── metrics.go │ │ ├── notification_test.go │ │ ├── service.go │ │ └── store.go │ ├── observability │ │ ├── adapter.go │ │ ├── adapter_test.go │ │ ├── distinction_error_types.go │ │ ├── distinction_utility.go │ │ ├── heartbeat.go │ │ ├── plan_utils.go │ │ ├── service.go │ │ ├── test_utils.go │ │ ├── utils.go │ │ └── utils_test.go │ ├── orderedtasks │ │ └── ordered_cancel.go │ ├── sendrecorder │ │ ├── recorder.go │ │ └── recorder_test.go │ ├── smtp │ │ ├── accounts.go │ │ ├── accounts_test.go │ │ ├── errors.go │ │ ├── observabilitymetrics │ │ │ └── metrics.go │ │ ├── server_manager.go │ │ ├── service.go │ │ ├── smtp.go │ │ ├── smtp_backend.go │ │ ├── smtp_debug.go │ │ ├── smtp_default.go │ │ ├── smtp_packages.go │ │ ├── smtp_packages_test.go │ │ ├── smtp_prefs.go │ │ └── smtp_prefs_test.go │ ├── syncservice │ │ ├── api_client.go │ │ ├── download_cache.go │ │ ├── handler.go │ │ ├── handler_test.go │ │ ├── interfaces.go │ │ ├── job.go │ │ ├── job_test.go │ │ ├── limits.go │ │ ├── mocks_test.go │ │ ├── observabilitymetrics │ │ │ └── metrics.go │ │ ├── service.go │ │ ├── stage_apply.go │ │ ├── stage_apply_test.go │ │ ├── stage_build.go │ │ ├── stage_build_test.go │ │ ├── stage_download.go │ │ ├── stage_download_test.go │ │ ├── stage_metadata.go │ │ ├── stage_metadata_test.go │ │ ├── stage_output.go │ │ └── utils.go │ ├── telemetry │ │ ├── service.go │ │ └── service_test.go │ ├── userevents │ │ ├── event_controller.go │ │ ├── event_poll_waiter.go │ │ ├── event_source.go │ │ ├── eventid_store.go │ │ ├── mocks │ │ │ └── mocks.go │ │ ├── mocks_test.go │ │ ├── service.go │ │ ├── service_handle_event_error_test.go │ │ ├── service_handle_event_test.go │ │ ├── service_test.go │ │ ├── subscribable.go │ │ ├── subscriber.go │ │ ├── subscriber_test.go │ │ └── subscription.go │ └── useridentity │ │ ├── auth.go │ │ ├── mocks │ │ └── mocks.go │ │ ├── service.go │ │ ├── service_test.go │ │ └── state.go ├── telemetry │ ├── heartbeat.go │ ├── heartbeat_test.go │ ├── mocks │ │ └── mocks.go │ ├── repair.go │ └── types_heartbeat.go ├── try │ ├── try.go │ └── try_test.go ├── unleash │ └── service.go ├── updater │ ├── channel.go │ ├── host_default.go │ ├── host_qa.go │ ├── install_darwin.go │ ├── install_default.go │ ├── key_default.go │ ├── keyring.go │ ├── mocks │ │ └── mocks.go │ ├── sync.go │ ├── sync_test.go │ ├── types_test.go │ ├── types_version.go │ ├── updater.go │ ├── version.go │ ├── version_test.go │ └── versioncompare │ │ ├── compare_darwin.go │ │ ├── compare_darwin_test.go │ │ ├── compare_linux.go │ │ ├── compare_windows.go │ │ └── types.go ├── user │ ├── debug.go │ ├── errors.go │ ├── keys_test.go │ ├── migration.go │ ├── repair_telemetry.go │ ├── user.go │ ├── user_check.go │ ├── user_check_test.go │ └── user_test.go ├── useragent │ ├── platform.go │ ├── platform_darwin.go │ ├── platform_default.go │ ├── platform_test.go │ ├── useragent.go │ └── useragent_test.go ├── usertypes │ ├── addressmode.go │ ├── keys.go │ ├── types.go │ └── types_test.go ├── vault │ ├── certs.go │ ├── certs_test.go │ ├── cookies.go │ ├── cookies_test.go │ ├── helper.go │ ├── helper_test.go │ ├── keychain_settings.go │ ├── keychain_settings_test.go │ ├── migrate.go │ ├── migrate_test.go │ ├── migrate_v2.3.x.go │ ├── migrate_v2.4.x.go │ ├── password_archive.go │ ├── settings.go │ ├── settings_test.go │ ├── token.go │ ├── types_certs.go │ ├── types_data.go │ ├── types_file.go │ ├── types_password_archive.go │ ├── types_settings.go │ ├── types_user.go │ ├── user.go │ ├── user_test.go │ ├── vault.go │ ├── vault_bench_test.go │ ├── vault_debug.go │ └── vault_test.go └── versioner │ ├── install.go │ ├── name_default.go │ ├── name_windows.go │ ├── remove.go │ ├── remove_darwin.go │ ├── remove_linux.go │ ├── remove_windows.go │ ├── util.go │ ├── version.go │ ├── version_test.go │ ├── versioner.go │ ├── versioner_remove_test.go │ └── versioner_test.go ├── pkg ├── algo │ ├── algo.go │ ├── encode.go │ ├── hash.go │ ├── sets.go │ └── sets_test.go ├── cpc │ ├── cpc.go │ └── cpc_test.go ├── dialer │ └── dial_client.go ├── files │ ├── removal.go │ └── removal_test.go ├── keychain │ ├── helper_darwin.go │ ├── helper_dbus_linux.go │ ├── helper_linux.go │ ├── helper_windows.go │ ├── keychain.go │ ├── keychain_darwin.go │ ├── keychain_default.go │ ├── keychain_missing.go │ ├── keychain_test.go │ └── test_helper.go ├── message │ ├── build.go │ ├── build_custom.go │ ├── build_framework_test.go │ ├── build_test.go │ ├── decrypt.go │ ├── decrypt_and_build.go │ ├── header.go │ ├── header_test.go │ ├── message.go │ ├── options.go │ ├── parser.go │ ├── parser │ │ ├── handler.go │ │ ├── parser.go │ │ ├── parser_test.go │ │ ├── part.go │ │ ├── part_test.go │ │ ├── testdata │ │ │ ├── complex_structure.eml │ │ │ ├── forwarding_html_attachment.eml │ │ │ ├── multipart_alternative.eml │ │ │ ├── text_html.eml │ │ │ └── text_html_octet_attachment.eml │ │ ├── trimmer.go │ │ ├── trimmer_test.go │ │ ├── visitor.go │ │ ├── walker.go │ │ ├── walker_test.go │ │ ├── writer.go │ │ └── writer_test.go │ ├── parser_test.go │ └── testdata │ │ ├── enc-body-structure.eml │ │ ├── ics_attachment.eml │ │ ├── incorrect_boundary_w_invalid_character_tuta.eml │ │ ├── long_header_line.eml │ │ ├── long_header_line_multiline.eml │ │ ├── multipart_alternative.eml │ │ ├── multipart_alternative_latin1.eml │ │ ├── multipart_alternative_nested.eml │ │ ├── multipart_alternative_related_inline_image.eml │ │ ├── multipart_attachment_encoded_no_quote.eml │ │ ├── multiple_text_parts.eml │ │ ├── non-encoded-content-transfer-encoding.eml │ │ ├── pgp-mime-body-html.eml │ │ ├── pgp-mime-body-plaintext-latin2.eml │ │ ├── pgp-mime-body-plaintext.eml │ │ ├── pgp-mime-body-signed-embedded-message-rfc822-with-pubkey.eml │ │ ├── pgp-mime-body-signed-html-with-pubkey.eml │ │ ├── pgp-mime-body-signed-html.eml │ │ ├── pgp-mime-body-signed-multipart-alternative-with-pubkey.eml │ │ ├── pgp-mime-body-signed-plaintext-with-pubkey.eml │ │ ├── pgp-mime-body-signed-plaintext.eml │ │ ├── plaintext-invalid-header.eml │ │ ├── plaintext-missing-header.eml │ │ ├── references-comma.eml │ │ ├── references.eml │ │ ├── reply-to_no_references.eml │ │ ├── rfc2047-content-transfer-encoding-bad.eml │ │ ├── rfc2047-content-transfer-encoding.eml │ │ ├── text_html.eml │ │ ├── text_html_7bit.eml │ │ ├── text_html_embedded_foreign_encoding.eml │ │ ├── text_html_image_inline.eml │ │ ├── text_html_image_inline_no_disposition.eml │ │ ├── text_html_octet_attachment.eml │ │ ├── text_html_plain_attachment.eml │ │ ├── text_html_trailing_end_of_mail.eml │ │ ├── text_plain.eml │ │ ├── text_plain_7bit.eml │ │ ├── text_plain_bad_sender.eml │ │ ├── text_plain_bad_subject.eml │ │ ├── text_plain_base64.eml │ │ ├── text_plain_docx_attachment_cyrillic.eml │ │ ├── text_plain_duplicate_charset.eml │ │ ├── text_plain_empty_addresses.eml │ │ ├── text_plain_image_inline.eml │ │ ├── text_plain_image_inline2.eml │ │ ├── text_plain_image_inline_attachment_first.eml │ │ ├── text_plain_image_inline_between_attachment.eml │ │ ├── text_plain_latin1.eml │ │ ├── text_plain_latin2_subject.eml │ │ ├── text_plain_octet_attachment.eml │ │ ├── text_plain_octet_attachment_bad_2231_filename.eml │ │ ├── text_plain_octet_attachment_good_2231_filename.eml │ │ ├── text_plain_octet_attachment_name_conflict.eml │ │ ├── text_plain_octet_attachment_name_in_contenttype.eml │ │ ├── text_plain_pdf_attachment_cyrillic.eml │ │ ├── text_plain_plain_attachment.eml │ │ ├── text_plain_plain_attachment_latin1.eml │ │ ├── text_plain_plain_attachment_latin2.eml │ │ ├── text_plain_rfc822_attachment.eml │ │ ├── text_plain_unknown_latin1.eml │ │ ├── text_plain_unknown_latin2.eml │ │ ├── text_plain_utf8.eml │ │ ├── text_plain_utf8_reply_to_and_x_forward.eml │ │ ├── text_plain_utf8_subject.eml │ │ ├── text_plain_xml_attachment_cp1250.eml │ │ └── wrong_base64.eml ├── mime │ ├── Changelog.md │ ├── encoding.go │ ├── encoding_test.go │ ├── mediaType.go │ └── utf7Decoder.go ├── mobileconfig │ ├── config.go │ └── template.go ├── ports │ ├── ports.go │ └── ports_test.go ├── restarter │ ├── restarter.go │ ├── restarter_test.go │ ├── start_default.go │ └── start_windows.go ├── sum │ ├── sum.go │ └── sum_test.go └── tar │ └── tar.go ├── release-notes ├── bridge_early.md └── bridge_stable.md ├── tests ├── README.md ├── _features │ ├── start.feature │ └── users │ │ └── delete.feature ├── api_test.go ├── bdd_test.go ├── bridge_test.go ├── collector_test.go ├── contact_test.go ├── ctx_bridge_test.go ├── ctx_heartbeat_test.go ├── ctx_helper_test.go ├── ctx_imap_test.go ├── ctx_reporter_test.go ├── ctx_smtp_test.go ├── ctx_test.go ├── diff.go ├── diff_test.go ├── e2e │ └── ui_tests │ │ └── windows_os │ │ ├── ProtonMailBridge.UI.Tests.csproj │ │ ├── Results │ │ ├── HelpMenuResults.cs │ │ ├── HomeResult.cs │ │ └── SettingsMenuResults.cs │ │ ├── TestSession.cs │ │ ├── Tests │ │ ├── HelpMenuTests.cs │ │ ├── LoginLogoutTests.cs │ │ ├── SettingsMenuTests.cs │ │ └── ZeroPercentUpdateTest.cs │ │ ├── TestsHelper │ │ ├── TestData.cs │ │ └── TestUserData.cs │ │ ├── UIActions.cs │ │ ├── Windows │ │ ├── HelpMenuWindow.cs │ │ ├── HomeWindow.cs │ │ ├── LoginWindow.cs │ │ ├── SettingsMenuWindow.cs │ │ └── ZeroPercentUpdateWindow.cs │ │ ├── app.config │ │ └── ui_tests.sln ├── environment_test.go ├── external_test.go ├── fast.go ├── features │ ├── bridge │ │ ├── default_ports.feature │ │ ├── heartbeat.feature │ │ └── updates_legacy.feature │ ├── external │ │ ├── html_external_to_proton.feature │ │ ├── html_proton_to_external.feature │ │ ├── plain_external_to_proton.feature │ │ └── plain_proton_to_external.feature │ ├── frontend │ │ └── frontend.feature │ ├── imap │ │ ├── addressmode.feature │ │ ├── auth.feature │ │ ├── id.feature │ │ ├── mailbox │ │ │ ├── create.feature │ │ │ ├── delete.feature │ │ │ ├── hide_all_mail.feature │ │ │ ├── info.feature │ │ │ ├── list.feature │ │ │ ├── rename.feature │ │ │ ├── rename_hiearchy.feature │ │ │ └── select.feature │ │ ├── message │ │ │ ├── copy.feature │ │ │ ├── create.feature │ │ │ ├── delete.feature │ │ │ ├── delete_from_trash.feature │ │ │ ├── drafts.feature │ │ │ ├── fetch.feature │ │ │ ├── import.feature │ │ │ ├── import_key.feature │ │ │ ├── move.feature │ │ │ ├── move_without_support.feature │ │ │ ├── scheduled.feature │ │ │ ├── state.feature │ │ │ └── store.feature │ │ ├── migration.feature │ │ └── ports.feature │ ├── observability │ │ ├── all_metrics.feature │ │ ├── gluon_metrics.feature │ │ └── remote_notification.feature │ ├── smtp │ │ ├── addressmode.feature │ │ ├── auth.feature │ │ ├── init.feature │ │ ├── ports.feature │ │ └── send │ │ │ ├── attachment.feature │ │ │ ├── bcc.feature │ │ │ ├── embedded_message.feature │ │ │ ├── failures.feature │ │ │ ├── failures_disabled.feature │ │ │ ├── html.feature │ │ │ ├── html_att.feature │ │ │ ├── html_to_internal.feature │ │ │ ├── inline.feature │ │ │ ├── mixed_case.feature │ │ │ ├── one_account_to_another.feature │ │ │ ├── plain.feature │ │ │ ├── plain_att.feature │ │ │ ├── plain_to_internal.feature │ │ │ ├── same_message.feature │ │ │ ├── send_append.feature │ │ │ ├── send_reply.feature │ │ │ ├── sender_key.feature │ │ │ └── two_messages.feature │ └── user │ │ ├── account.feature │ │ ├── addressmode.feature │ │ ├── contact.feature │ │ ├── delete.feature │ │ ├── delete_imap.feature │ │ ├── kb_article_suggestions.feature │ │ ├── login.feature │ │ ├── relogin.feature │ │ ├── report_problem.feature │ │ ├── revoke.feature │ │ ├── sync.feature │ │ ├── sync_high_number.feature │ │ └── telemetry.feature ├── frontend_test.go ├── heartbeat_test.go ├── imap_test.go ├── main_test.go ├── observability_test.go ├── smtp_test.go ├── steps_test.go ├── testdata │ ├── html │ │ └── foreign_ascii_subject_body.template.eml │ ├── keys │ │ └── pubkey.asc │ ├── multipart │ │ ├── mixed_with_attachment_encoded.eml │ │ ├── mixed_with_attachment_encoded_no_quote.eml │ │ └── mixed_with_attachment_no_quote.eml │ └── plain │ │ ├── text_plain_latin1.eml │ │ ├── text_plain_multiple_attachments.template.eml │ │ ├── text_plain_unknown_latin1.eml │ │ └── text_plain_wrong_latin1.eml ├── types_test.go ├── user_test.go └── utils │ └── gmail │ ├── service.go │ └── tokenservice │ └── tokenservice.go └── utils ├── QTBUG-88600 ├── README.txt ├── cocoa.patch └── libqcocoa.dylib ├── bridge-rollout ├── README.md └── bridge-rollout.go ├── bridge_app_version.ps1 ├── bridge_app_version.sh ├── changelog_linter.sh ├── coverage.sh ├── credits.sh ├── debug └── debug_assemble.go ├── dependency_license.sh ├── dxtn ├── dxtn.cbp ├── dxtn.layout ├── main.cpp └── main.h ├── export_icons.sh ├── get_revision.sh ├── githooks └── pre-push ├── govulncheck.sh ├── hasher └── main.go ├── kb-suggester └── kb-suggester.go ├── keyring.go ├── license_header.txt ├── missing_license.sh ├── port-blocker └── port-blocker.go ├── rebranding.sh ├── release_notes.css ├── release_notes.sh ├── remove-bridge ├── README.md ├── Remove-Bridge.ps1 └── remove_bridge ├── remove_non_relative_links_darwin.sh ├── smtp-send └── main.go ├── sync_bench.py ├── update_test_keys.sh ├── validate_bug_report_file.py ├── vault-editor ├── README.md └── main.go └── versioner └── main.go /.gitattributes: -------------------------------------------------------------------------------- 1 | unreleased.md merge=union 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # System files 2 | *.app 3 | *.DS_Store 4 | 5 | # Editor files 6 | .*.sw? 7 | *~ 8 | .idea 9 | .vscode 10 | .vs 11 | 12 | # Test files 13 | godog.test 14 | debug.test 15 | coverage.html 16 | gobinsec-cache*.yml 17 | 18 | # Run files 19 | mem.pprof 20 | 21 | # Auto generated 22 | internal/**/credits.go 23 | vendor 24 | vendor-cache 25 | /main.go 26 | 27 | 28 | # Build files 29 | /launcher-* 30 | /bridge_*_*.tgz 31 | /ie_*_*.tgz 32 | /versioner 33 | /hasher 34 | cmd/Desktop-Bridge/deploy 35 | cmd/Import-Export/deploy 36 | proton-bridge 37 | cmd/Desktop-Bridge/*.exe 38 | cmd/launcher/*.exe 39 | bin/ 40 | obj/ 41 | 42 | # Jetbrains (CLion, Golang) cmake build dirs 43 | cmake-build-*/ 44 | 45 | # Doxygen doc files 46 | _doc/ 47 | 48 | # gRPC auto-generated C++ source files 49 | *.pb.cc 50 | *.pb.h 51 | -------------------------------------------------------------------------------- /.gitlab/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * inbox-desktop-approvers -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/vcpkg"] 2 | path = extern/vcpkg 3 | url = https://github.com/Microsoft/vcpkg.git 4 | -------------------------------------------------------------------------------- /.grype.yaml: -------------------------------------------------------------------------------- 1 | # Check out for configuration details: https://github.com/anchore/grype?tab=readme-ov-file#configuration 2 | fail-on-severity: "medium" -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - when cache is full, we need to stop the watcher? don't want to keep downloading messages and throwing them away when we try to cache them. 2 | -------------------------------------------------------------------------------- /ci/report.yml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | 4 | include: 5 | - project: 'tpe/testmo-reporter' 6 | ref: master 7 | file: '/scenarios/testmo-script.yml' 8 | 9 | testmo-upload: 10 | stage: report 11 | extends: 12 | - .testmo-upload 13 | - .rules-branch-manual-scheduled-and-test-branch-always 14 | needs: 15 | - test-integration-nightly 16 | before_script: [] 17 | variables: 18 | TESTMO_TOKEN: "$TESTMO_TOKEN" 19 | TESTMO_URL: "https://proton.testmo.net" 20 | PROJECT_ID: "9" 21 | NAME: "Nightly integration tests" 22 | MILESTONE: "Nightly integration tests" 23 | SOURCE: "test-integration-nightly" 24 | TAGS: "$CI_COMMIT_REF_SLUG" 25 | RESULT_FOLDER: "tests/result/*.xml" 26 | -------------------------------------------------------------------------------- /ci/setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | include: 4 | - project: 'go/bridge-internal' 5 | ref: 'master' 6 | file: 'ci/runners-setup.yml' 7 | 8 | -------------------------------------------------------------------------------- /dist/Bridge.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/Bridge.icns -------------------------------------------------------------------------------- /dist/bridge.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/bridge.ico -------------------------------------------------------------------------------- /dist/info.rc: -------------------------------------------------------------------------------- 1 | #define STRINGIZE_(x) #x 2 | #define STRINGIZE(x) STRINGIZE_(x) 3 | 4 | IDI_ICON1 ICON DISCARDABLE STRINGIZE(ICO_FILE) 5 | 6 | #define FILE_COMMENTS "Proton Mail Bridge is a desktop application that runs in the background, encrypting and decrypting messages as they enter and leave your computer." 7 | #define FILE_DESCRIPTION "Proton Mail Bridge" 8 | #define INTERNAL_NAME STRINGIZE(EXE_NAME) 9 | #define PRODUCT_NAME "Proton Mail Bridge for Windows" 10 | 11 | #define LEGAL_COPYRIGHT "(C) " STRINGIZE(YEAR) " Proton AG" 12 | 13 | 1 VERSIONINFO 14 | FILEVERSION FILE_VERSION_COMMA,0 15 | PRODUCTVERSION FILE_VERSION_COMMA,0 16 | BEGIN 17 | BLOCK "StringFileInfo" 18 | BEGIN 19 | BLOCK "040904b0" 20 | BEGIN 21 | VALUE "Comments", FILE_COMMENTS 22 | VALUE "CompanyName", "Proton AG" 23 | VALUE "FileDescription", FILE_DESCRIPTION 24 | VALUE "FileVersion", STRINGIZE(FILE_VERSION) 25 | VALUE "InternalName", INTERNAL_NAME 26 | VALUE "LegalCopyright", LEGAL_COPYRIGHT 27 | VALUE "OriginalFilename", STRINGIZE(ORIGINAL_FILE_NAME) 28 | VALUE "ProductName", PRODUCT_NAME 29 | VALUE "ProductVersion", STRINGIZE(PRODUCT_VERSION) 30 | END 31 | END 32 | BLOCK "VarFileInfo" 33 | BEGIN 34 | VALUE "Translation", 0x0409, 0x04B0 35 | END 36 | END 37 | -------------------------------------------------------------------------------- /dist/proton-bridge.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Version=1.1 4 | Name=Proton Mail Bridge 5 | GenericName=Proton Mail Bridge for Linux 6 | Comment=Proton Mail Bridge is a desktop application that runs in the background, encrypting and decrypting messages as they enter and leave your computer. 7 | Icon=protonmail-bridge 8 | Exec=protonmail-bridge 9 | Terminal=false 10 | Categories=Office;Email;Network 11 | StartupWMClass=Proton Mail Bridge 12 | -------------------------------------------------------------------------------- /dist/raw/mac_icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/mac_icon_128x128.png -------------------------------------------------------------------------------- /dist/raw/mac_icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/mac_icon_128x128@2x.png -------------------------------------------------------------------------------- /dist/raw/mac_icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/mac_icon_16x16.png -------------------------------------------------------------------------------- /dist/raw/mac_icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/mac_icon_16x16@2x.png -------------------------------------------------------------------------------- /dist/raw/mac_icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/mac_icon_256x256.png -------------------------------------------------------------------------------- /dist/raw/mac_icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/mac_icon_256x256@2x.png -------------------------------------------------------------------------------- /dist/raw/mac_icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/mac_icon_32x32.png -------------------------------------------------------------------------------- /dist/raw/mac_icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/mac_icon_32x32@2x.png -------------------------------------------------------------------------------- /dist/raw/mac_icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/mac_icon_512x512.png -------------------------------------------------------------------------------- /dist/raw/mac_icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/mac_icon_512x512@2x.png -------------------------------------------------------------------------------- /dist/raw/win+lin_icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/dist/raw/win+lin_icon_256x256.png -------------------------------------------------------------------------------- /internal/app/testdata/with_keys/protonmail/bridge/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- -------------------------------------------------------------------------------- /internal/app/testdata/with_keys/protonmail/bridge/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /internal/bridge/api_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !build_qa && !test_integration 19 | 20 | package bridge 21 | 22 | import ( 23 | "net/http" 24 | 25 | "github.com/Masterminds/semver/v3" 26 | "github.com/ProtonMail/gluon/async" 27 | "github.com/ProtonMail/go-proton-api" 28 | ) 29 | 30 | // newAPIOptions returns a set of API options for the given parameters. 31 | func newAPIOptions( 32 | apiURL string, 33 | version *semver.Version, 34 | cookieJar http.CookieJar, 35 | transport http.RoundTripper, 36 | panicHandler async.PanicHandler, 37 | ) []proton.Option { 38 | return defaultAPIOptions(apiURL, version, cookieJar, transport, panicHandler) 39 | } 40 | -------------------------------------------------------------------------------- /internal/bridge/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package bridge 19 | 20 | import "errors" 21 | 22 | var ( 23 | ErrVaultInsecure = errors.New("the vault is insecure") 24 | ErrVaultCorrupt = errors.New("the vault is corrupt") 25 | ErrWatchUpdates = errors.New("failed to watch for updates") 26 | 27 | ErrNoSuchUser = errors.New("no such user") 28 | ErrUserAlreadyExists = errors.New("user already exists") 29 | ErrUserAlreadyLoggedIn = errors.New("the user is already logged in") 30 | ErrNotImplemented = errors.New("not implemented") 31 | 32 | ErrSizeTooLarge = errors.New("file is too big") 33 | ) 34 | -------------------------------------------------------------------------------- /internal/bridge/imapsmtp_telemetry.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package bridge 19 | 20 | type bridgeIMAPSMTPTelemetry struct { 21 | b *Bridge 22 | } 23 | 24 | func (b bridgeIMAPSMTPTelemetry) SetCacheLocation(s string) { 25 | b.b.heartbeat.SetCacheLocation(s) 26 | } 27 | -------------------------------------------------------------------------------- /internal/bridge/keychain.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package bridge 19 | 20 | import "golang.org/x/exp/maps" 21 | 22 | func (bridge *Bridge) GetHelpersNames() []string { 23 | return maps.Keys(bridge.keychains.GetHelpers()) 24 | } 25 | -------------------------------------------------------------------------------- /internal/bridge/locations.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package bridge 19 | 20 | func (bridge *Bridge) GetLogsPath() (string, error) { 21 | return bridge.locator.ProvideLogsPath() 22 | } 23 | 24 | func (bridge *Bridge) GetLicenseFilePath() string { 25 | return bridge.locator.GetLicenseFilePath() 26 | } 27 | 28 | func (bridge *Bridge) GetDependencyLicensesLink() string { 29 | return bridge.locator.GetDependencyLicensesLink() 30 | } 31 | -------------------------------------------------------------------------------- /internal/bridge/main_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package bridge_test 19 | 20 | import ( 21 | "os" 22 | "testing" 23 | 24 | "github.com/sirupsen/logrus" 25 | "go.uber.org/goleak" 26 | ) 27 | 28 | func TestMain(m *testing.M) { 29 | if level := os.Getenv("BRIDGE_LOG_LEVEL"); level != "" { 30 | if parsed, err := logrus.ParseLevel(level); err == nil { 31 | logrus.SetLevel(parsed) 32 | } 33 | } 34 | 35 | goleak.VerifyTestMain(m, goleak.IgnoreCurrent()) 36 | } 37 | -------------------------------------------------------------------------------- /internal/bridge/testdata/text-plain.eml: -------------------------------------------------------------------------------- 1 | To: recipient@pm.me 2 | From: sender@pm.me 3 | Subject: Test 4 | Content-Type: text/plain; charset=utf-8 5 | 6 | Test -------------------------------------------------------------------------------- /internal/bridge/tls.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package bridge 19 | 20 | func (bridge *Bridge) GetBridgeTLSCert() ([]byte, []byte) { 21 | return bridge.vault.GetBridgeTLSCert() 22 | } 23 | 24 | func (bridge *Bridge) SetBridgeTLSCertPath(certPath, keyPath string) error { 25 | return bridge.vault.SetBridgeTLSCertPath(certPath, keyPath) 26 | } 27 | -------------------------------------------------------------------------------- /internal/certs/cert_store_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package certs 19 | 20 | func osSupportCertInstall() bool { 21 | return false 22 | } 23 | 24 | func installCert([]byte) error { 25 | return nil // Linux doesn't have a root cert store. 26 | } 27 | 28 | func uninstallCert([]byte) error { 29 | return nil // Linux doesn't have a root cert store. 30 | } 31 | 32 | func isCertInstalled([]byte) bool { 33 | return false 34 | } 35 | -------------------------------------------------------------------------------- /internal/certs/cert_store_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package certs 19 | 20 | func osSupportCertInstall() bool { 21 | return false 22 | } 23 | 24 | func installCert([]byte) error { 25 | return nil // NOTE(GODT-986): Install certs to root cert store? 26 | } 27 | 28 | func uninstallCert([]byte) error { 29 | return nil // NOTE(GODT-986): Uninstall certs from root cert store? 30 | } 31 | 32 | func isCertInstalled([]byte) bool { 33 | return false 34 | } 35 | -------------------------------------------------------------------------------- /internal/constants/host_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !build_qa 19 | 20 | package constants 21 | 22 | // APIHost is our API address. 23 | const APIHost = "https://mail-api.proton.me" 24 | -------------------------------------------------------------------------------- /internal/constants/host_qa.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build build_qa 19 | 20 | package constants 21 | 22 | import "os" 23 | 24 | // APIHost is our API address. 25 | var APIHost = "https://mail-api.proton.me" 26 | 27 | func init() { 28 | if apiHost := os.Getenv("BRIDGE_HOST_URL"); apiHost != "" { 29 | APIHost = apiHost 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /internal/constants/update_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !build_qa 19 | // +build !build_qa 20 | 21 | package constants 22 | 23 | import "time" 24 | 25 | // UpdateCheckInterval defines how often we check for new version. 26 | const UpdateCheckInterval = time.Hour 27 | -------------------------------------------------------------------------------- /internal/constants/update_qa.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build build_qa 19 | // +build build_qa 20 | 21 | package constants 22 | 23 | import "time" 24 | 25 | // UpdateCheckInterval defines how often we check for new version 26 | const UpdateCheckInterval = time.Duration(5 * time.Minute) 27 | -------------------------------------------------------------------------------- /internal/constants/version_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge.Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !build_qa 19 | // +build !build_qa 20 | 21 | package constants 22 | 23 | import "fmt" 24 | 25 | // AppVersion returns the full rendered version of the app (to be used in request headers). 26 | func AppVersion(version string) string { 27 | return fmt.Sprintf("%v-%v@%v", getAPIOS(), AppName, version) 28 | } 29 | -------------------------------------------------------------------------------- /internal/constants/version_qa.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build build_qa 19 | // +build build_qa 20 | 21 | package constants 22 | 23 | import ( 24 | "fmt" 25 | 26 | "github.com/Masterminds/semver/v3" 27 | ) 28 | 29 | // AppVersion returns the full rendered version of the app (to be used in request headers). 30 | func AppVersion(version string) string { 31 | ver, _ := semver.MustParse(version).SetPrerelease("dev") 32 | 33 | return fmt.Sprintf("%v-%v@%v", getAPIOS(), AppName, ver.String()) 34 | } 35 | -------------------------------------------------------------------------------- /internal/constants/version_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge.Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package constants 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/Masterminds/semver/v3" 24 | "github.com/stretchr/testify/require" 25 | ) 26 | 27 | func TestPrereleaseSemver(t *testing.T) { 28 | ver, err := semver.MustParse("2.3.0+qa").SetPrerelease("dev") 29 | require.NoError(t, err) 30 | 31 | require.Equal(t, "2.3.0-dev+qa", ver.String()) 32 | 33 | ver, err = semver.MustParse("2.3.0-dev+qa").SetPrerelease("dev") 34 | require.NoError(t, err) 35 | 36 | require.Equal(t, "2.3.0-dev+qa", ver.String()) 37 | } 38 | -------------------------------------------------------------------------------- /internal/dialer/dialer_pinning_checker_qa.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge.Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build build_qa 19 | 20 | package dialer 21 | 22 | import "net" 23 | 24 | // CheckCertificate returns whether the connection presents a known TLS certificate. 25 | // The QA implementation always returns nil. 26 | func (p *TLSPinChecker) CheckCertificate(conn net.Conn) error { 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /internal/dialer/dialer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package dialer 19 | 20 | import ( 21 | "testing" 22 | 23 | "golang.org/x/net/http/httpproxy" 24 | ) 25 | 26 | // skipIfProxyIsSet skips the tests if HTTPS proxy is set. 27 | // Should be used for tests depending on proper certificate checks which 28 | // is not possible under our CI setup. 29 | func skipIfProxyIsSet(t *testing.T) { 30 | if httpproxy.FromEnvironment().HTTPSProxy != "" { 31 | t.SkipNow() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /internal/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package internal 19 | 20 | import ( 21 | "errors" 22 | "fmt" 23 | ) 24 | 25 | // ErrCause returns the cause of the error, the inner-most error in the wrapped chain. 26 | func ErrCause(err error) error { 27 | cause := err 28 | 29 | for errors.Unwrap(cause) != nil { 30 | cause = errors.Unwrap(cause) 31 | } 32 | 33 | return cause 34 | } 35 | 36 | func ErrCauseType(err error) string { 37 | return fmt.Sprintf("%T", ErrCause(err)) 38 | } 39 | -------------------------------------------------------------------------------- /internal/events/connection.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package events 19 | 20 | type TLSIssue struct { 21 | eventBase 22 | } 23 | 24 | func (event TLSIssue) String() string { 25 | return "TLSIssue" 26 | } 27 | 28 | type ConnStatusUp struct { 29 | eventBase 30 | } 31 | 32 | func (event ConnStatusUp) String() string { 33 | return "ConnStatusUp" 34 | } 35 | 36 | type ConnStatusDown struct { 37 | eventBase 38 | } 39 | 40 | func (event ConnStatusDown) String() string { 41 | return "ConnStatusDown" 42 | } 43 | -------------------------------------------------------------------------------- /internal/events/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package events 19 | 20 | import "fmt" 21 | 22 | type Error struct { 23 | eventBase 24 | 25 | Error error 26 | } 27 | 28 | func (event Error) String() string { 29 | return fmt.Sprintf("Error: %s", event.Error) 30 | } 31 | -------------------------------------------------------------------------------- /internal/events/raise.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package events 19 | 20 | type Raise struct { 21 | eventBase 22 | } 23 | 24 | func (event Raise) String() string { 25 | return "Raise" 26 | } 27 | -------------------------------------------------------------------------------- /internal/focus/proto/focus.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | // Package proto provides the gRPC definition of the focus service. 19 | package proto 20 | 21 | //go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative focus.proto 22 | -------------------------------------------------------------------------------- /internal/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Auto generated 2 | moc.cpp 3 | moc.go 4 | moc.h 5 | moc_cgo_*.go 6 | moc_moc.h 7 | rcc.cpp 8 | rcc.qrc 9 | rcc_cgo_*.go 10 | *.qmlc 11 | 12 | # Generated file 13 | bridge-gui/bridge-gui/BuildConfig.h 14 | bridge-gui/bridge-gui/Resources.rc 15 | -------------------------------------------------------------------------------- /internal/frontend/Makefile.local: -------------------------------------------------------------------------------- 1 | FILES=$(shell find . -iname 'rcc.qrc') 2 | FILES+=$(shell find . -iname 'rcc.cpp') 3 | FILES+=$(shell find . -iname 'rcc_cgo*.go') 4 | 5 | FILES+=$(shell find . -iname 'moc.go') 6 | FILES+=$(shell find . -iname 'moc.cpp') 7 | FILES+=$(shell find . -iname 'moc.h') 8 | FILES+=$(shell find . -iname 'moc_cgo*.go') 9 | 10 | FILES+=$(shell find ./qml -iname '*.qmlc') 11 | 12 | clean: 13 | rm -f ${FILES} 14 | 15 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022 Proton AG 2 | # 3 | # This file is part of Proton Mail Bridge. 4 | # 5 | # Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Proton Mail Bridge is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | cmake_minimum_required(VERSION 3.22) 20 | 21 | 22 | #***************************************************************************************************************************************************** 23 | # Project 24 | #***************************************************************************************************************************************************** 25 | set(BRIDGE_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../..") 26 | include("BridgeSetup.cmake") 27 | 28 | project(frontend) 29 | 30 | add_subdirectory(bridgepp) 31 | add_subdirectory(bridge-gui) 32 | add_subdirectory(bridge-gui-tester) 33 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/FindQt.cmake: -------------------------------------------------------------------------------- 1 | find_program(QMAKE_EXE NAMES "qmake" "qmake6") 2 | if (NOT QMAKE_EXE) 3 | message(FATAL_ERROR "Could not locate qmake executable, make sure you have Qt 6 installed in that qmake is in your PATH environment variable.") 4 | endif() 5 | message(STATUS "Found qmake at ${QMAKE_EXE}") 6 | execute_process(COMMAND "${QMAKE_EXE}" -query QT_INSTALL_PREFIX OUTPUT_VARIABLE QT_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) 7 | 8 | set(CMAKE_PREFIX_PATH ${QT_DIR} ${CMAKE_PREFIX_PATH}) 9 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui-tester/.lldbinit: -------------------------------------------------------------------------------- 1 | # The following fix an issue happening using LLDB with OpenSSL 3.1 on ARM64 architecture. (GODT-2680) 2 | # WARNING: this file is ignored if you do not enable reading lldb config from cwd in ~/.lldbinit (`settings set target.load-cwd-lldbinit true`) 3 | settings set platform.plugin.darwin.ignored-exceptions EXC_BAD_INSTRUCTION 4 | process handle SIGILL -n false -p true -s false 5 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui-tester/Cert.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | #ifndef BRIDGE_GUI_TESTER_CERT_H 20 | #define BRIDGE_GUI_TESTER_CERT_H 21 | 22 | 23 | extern QString const testTLSCert; ///< The test TLS Cert used by the app, in PEM format. 24 | extern QString const testTLSKey; ///< The test TLS Key used by the app, in PEM format. 25 | 26 | 27 | #endif //BRIDGE_GUI_TESTER_CERT_H 28 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui-tester/Pch.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | #ifndef BRIDGE_GUI_PCH_H 20 | #define BRIDGE_GUI_PCH_H 21 | 22 | 23 | #include 24 | #include 25 | #include 26 | #include "AppController.h" 27 | 28 | 29 | #endif // BRIDGE_GUI_PCH_H 30 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/.lldbinit: -------------------------------------------------------------------------------- 1 | # The following fix an issue happening using LLDB with OpenSSL 3.1 on ARM64 architecture. (GODT-2680) 2 | # WARNING: this file is ignored if you do not enable reading lldb config from cwd in ~/.lldbinit (`settings set target.load-cwd-lldbinit true`) 3 | settings set platform.plugin.darwin.ignored-exceptions EXC_BAD_INSTRUCTION 4 | process handle SIGILL -n false -p true -s false 5 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/ClipboardProxy.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // This file is part of Proton Mail Bridge. 3 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // Proton Mail Bridge is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU General Public License for more details. 11 | // You should have received a copy of the GNU General Public License 12 | // along with Proton Mail Bridge. If not, see . 13 | #include "ClipboardProxy.h" 14 | 15 | // The following definitions were taken and adapted from: 16 | // https://stackoverflow.com/questions/40092352/passing-qclipboard-to-qml 17 | // Author: krzaq 18 | 19 | ClipboardProxy::ClipboardProxy(QClipboard* c) : clipboard(c) { 20 | connect(clipboard, &QClipboard::dataChanged, this, &ClipboardProxy::textChanged); 21 | } 22 | 23 | QString ClipboardProxy::text() const { 24 | return clipboard->text(); 25 | } -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/ClipboardProxy.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // This file is part of Proton Mail Bridge. 3 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // Proton Mail Bridge is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU General Public License for more details. 11 | // You should have received a copy of the GNU General Public License 12 | // along with Proton Mail Bridge. If not, see . 13 | #ifndef BRIDGE_GUI_CLIPBOARDPROXY_H 14 | #define BRIDGE_GUI_CLIPBOARDPROXY_H 15 | 16 | // The following class declarations were taken and adapted from: 17 | // https://stackoverflow.com/questions/40092352/passing-qclipboard-to-qml 18 | // Author: krzaq 19 | 20 | 21 | class ClipboardProxy : public QObject 22 | { 23 | Q_OBJECT 24 | Q_PROPERTY(QString text READ text NOTIFY textChanged) 25 | public: 26 | explicit ClipboardProxy(QClipboard*); 27 | 28 | QString text() const; 29 | 30 | signals: 31 | void textChanged(); 32 | 33 | private: 34 | QClipboard* clipboard; 35 | }; 36 | 37 | 38 | #endif //BRIDGE_GUI_CLIPBOARDPROXY_H 39 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/LogUtils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | #ifndef BRIDGE_GUI_LOG_UTILS_H 20 | #define BRIDGE_GUI_LOG_UTILS_H 21 | 22 | 23 | #include 24 | 25 | 26 | bridgepp::Log &initLog(); ///< Initialize the application log. 27 | 28 | 29 | #endif //BRIDGE_GUI_LOG_UTILS_H 30 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/MacOS/DockIcon.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | 20 | 21 | #ifndef Q_OS_MACOS 22 | 23 | 24 | void setDockIconVisibleState(bool visible) { Q_UNUSED(visible) } 25 | 26 | 27 | bool getDockIconVisibleState() { return true; } 28 | 29 | 30 | #endif -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/MacOS/DockIcon.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | #ifndef BRIDGE_GUI_DOCK_ICON_H 20 | #define BRIDGE_GUI_DOCK_ICON_H 21 | 22 | 23 | void setDockIconVisibleState(bool visible); ///< Set the DOCK icon visibility state 24 | bool getDockIconVisibleState(); ///< Get the Dock icon visibility state 25 | 26 | 27 | #endif // #ifndef BRIDGE_GUI_DOCK_ICON_H 28 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/MacOS/SecondInstance.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | #ifndef BRIDGE_GUI_APP_DELEGATE_H 20 | #define BRIDGE_GUI_APP_DELEGATE_H 21 | #ifdef Q_OS_MACOS 22 | 23 | 24 | void registerSecondInstanceHandler(); 25 | 26 | 27 | #endif // Q_OS_MACOS 28 | #endif //BRIDGE_GUI_APP_DELEGATE_H 29 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/Pch.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | #ifndef BRIDGE_GUI_PCH_H 20 | #define BRIDGE_GUI_PCH_H 21 | 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | 33 | #endif // BRIDGE_GUI_PCH_H 34 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/Resources.rc.in: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "${BRIDGE_REPO_ROOT}/dist/bridge.ico" 2 | 3 | 1 VERSIONINFO 4 | FILEVERSION ${BRIDGE_APP_VERSION_COMMA} 5 | PRODUCTVERSION ${BRIDGE_APP_VERSION_COMMA} 6 | BEGIN 7 | BLOCK "StringFileInfo" 8 | BEGIN 9 | BLOCK "040904b0" 10 | BEGIN 11 | VALUE "Comments", "Proton Mail Bridge is a desktop application that runs in the background, encrypting and decrypting messages as they enter and leave your computer." 12 | VALUE "CompanyName", "${BRIDGE_VENDOR}" 13 | VALUE "FileDescription", "${BRIDGE_APP_FULL_NAME}" 14 | VALUE "FileVersion", "${BRIDGE_APP_VERSION_COMMA}" 15 | VALUE "InternalName", "${PROJECT_NAME}.exe" 16 | VALUE "LegalCopyright", "(C) ${BRIDGE_BUILD_YEAR} ${BRIDGE_VENDOR}" 17 | VALUE "OriginalFilename", "${PROJECT_NAME}.exe" 18 | VALUE "ProductName", "${BRIDGE_APP_FULL_NAME} for Windows" 19 | VALUE "ProductVersion", "${BRIDGE_APP_VERSION}" 20 | END 21 | END 22 | BLOCK "VarFileInfo" 23 | BEGIN 24 | VALUE "Translation", 0x0409, 0x04B0 25 | END 26 | END 27 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/SentryUtils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | #ifndef BRIDGE_GUI_SENTRYUTILS_H 19 | #define BRIDGE_GUI_SENTRYUTILS_H 20 | 21 | 22 | #include 23 | 24 | void initSentry(); 25 | QByteArray getProtectedHostname(); 26 | void setSentryReportScope(); 27 | sentry_options_t* newSentryOptions(const char * sentryDNS, const char * cacheDir); 28 | sentry_uuid_t reportSentryEvent(sentry_level_t level, const char *message); 29 | sentry_uuid_t reportSentryException(QString const& message, bridgepp::Exception const exception); 30 | 31 | 32 | #endif //BRIDGE_GUI_SENTRYUTILS_H 33 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/Notifications/qmldir: -------------------------------------------------------------------------------- 1 | module Notifications 2 | depends QtQml 2.12 3 | 4 | Notifications 1.0 Notifications.qml 5 | NotificationFilter 1.0 NotificationFilter.qml 6 | Notification 1.0 Notification.qml 7 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/Proton/Action.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // This file is part of Proton Mail Bridge. 3 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // Proton Mail Bridge is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU General Public License for more details. 11 | // You should have received a copy of the GNU General Public License 12 | // along with Proton Mail Bridge. If not, see . 13 | import QtQuick 14 | import QtQuick.Templates as T 15 | 16 | T.Action { 17 | property bool loading 18 | } 19 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/Template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | %1 15 | 16 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyBridge.html: -------------------------------------------------------------------------------- 1 |

Why do I need bridge?

2 |

3 | Proton does not have access to the content of your messages, so it cannot share your unencrypted messages with your email client from the 4 | Proton servers. 5 |

6 |

7 | Email clients such as Microsoft Outlook, Mozilla Thunderbird and Apple Mail use standard protocols named IMAP and SMTP to receive and send emails. 8 |

9 |

10 | Even though the IMAP and SMTP protocols can use secure channels (using SSL/TLS), they do not offer support for encrypted messages. 11 | Because Proton does not have access to the content of your messages, it is not possible to configure your email client to connect directly to 12 | Proton servers. 13 |

14 |

15 | The key to solving this problem is Bridge. Once installed on your computer and connected to your Proton account, Bridge can access your 16 | encrypted messages stored on the Proton servers. Bridge integrates an IMAP and a SMTP server that run on your computer and are accessible only 17 | to applications executing on your machine. Your email client connects to these local servers and Bridge is responsible for seamlessly encrypting 18 | and decrypting the messages that you send and receive. 19 |

20 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyCertificate.html: -------------------------------------------------------------------------------- 1 |

Why do I need to install a certificate when configuring Apple Mail with Bridge?

2 |

3 | Apple Mail requires a secure channel for communications with email servers, and the server needs to be acknowledged as trusted. 4 |

5 |

6 | In order to communicate with Bridge, Apple Mail requires secure connections using SSL/TLS. This cryptographic protocol includes an identity 7 | verification system using certificates. For publicly available servers, certificates are normally issued and digitally signed by a certificate 8 | authority, such as Let's Encrypt. This is not possible for Bridge, as the IMAP and SMTP servers are running on your own computer, and are not 9 | accessible from any network (local or internet). 10 |

11 |

12 | The solution is to use a self-signed certificate. When setting up an email account where the server provides a self-signed certificate, most 13 | email clients will issue a warning asking you whether you trust the server or not, because the certificate was not issued by a certificate 14 | authority. 15 |

16 |

17 | Apple Mail requires an extra step. It will simply refuse to connect if the certificate is not set as trusted. Bridge solves this by storing this 18 | certificate in the macOS keychain. This operation requires that you provide your macOS account password. 19 |

20 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/Loader_16.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/Loader_48.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-alert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-chevron-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-chevron-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-chevron-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-cross-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-drive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-exclamation-circle-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-external-link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-illustrative-view-html-code.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-info-circle-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-info-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-question-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-splash-check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-success.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-three-dots-vertical.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-trash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/ic-warning-orange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/img-macos-cert-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/img-macos-cert-screenshot.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/img-macos-profile-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/img-macos-profile-screenshot.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/img-proton-logos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/img-proton-logos.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/img-splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/img-splash.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-color-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-color-error.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-color-norm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-color-norm.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-color-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-color-update.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-color-warn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-color-warn.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-mono-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-mono-error.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-mono-norm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-mono-norm.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-mono-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-mono-update.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-mono-warn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/internal/frontend/bridge-gui/bridge-gui/qml/icons/systray-mono-warn.png -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/vcpkg/triplets/arm64-osx-min-11-0.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE arm64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE static) 4 | 5 | set(VCPKG_CMAKE_SYSTEM_NAME Darwin) 6 | set(VCPKG_OSX_ARCHITECTURES arm64) 7 | set(VCPKG_OSX_DEPLOYMENT_TARGET "11.0") 8 | 9 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridge-gui/vcpkg/triplets/x64-osx-min-10-15.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE static) 4 | 5 | set(VCPKG_CMAKE_SYSTEM_NAME Darwin) 6 | set(VCPKG_OSX_ARCHITECTURES x86_64) 7 | set(VCPKG_OSX_DEPLOYMENT_TARGET "10.15") 8 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridgepp/Pch.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | #ifndef BRIDGE_PP_PCH_H 20 | #define BRIDGE_PP_PCH_H 21 | 22 | 23 | #include 24 | 25 | 26 | #endif // BRIDGE_PP_PCH_H -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridgepp/Test/TestSessionID.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | #include "QtCore/qdatetime.h" 20 | #include 21 | #include 22 | 23 | 24 | using namespace bridgepp; 25 | 26 | 27 | TEST(SessionID, SessionID) { 28 | QString const sessionID = newSessionID(); 29 | EXPECT_TRUE(sessionID.size() > 0); 30 | 31 | EXPECT_FALSE(sessionIDToDateTime("invalidSessionID").isValid()); 32 | 33 | QDateTime const dateTime = sessionIDToDateTime(sessionID); 34 | EXPECT_TRUE(dateTime.isValid()); 35 | EXPECT_TRUE(qAbs(dateTime.secsTo(QDateTime::currentDateTime())) < 5); 36 | } 37 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridgepp/bridgepp/Log/LogUtils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | #ifndef BRIDGE_PP_LOG_UTILS_H 20 | #define BRIDGE_PP_LOG_UTILS_H 21 | 22 | 23 | namespace bridgepp { 24 | 25 | 26 | QString userLogsDir(); ///< Return the path of the user logs dir. 27 | QByteArray tailOfLatestBridgeLog(QString const &sessionID); ///< Return the last bytes of the last bridge log. 28 | 29 | 30 | } // namespace bridgepp 31 | 32 | 33 | #endif //BRIDGE_PP_LOG_UTILS_H 34 | -------------------------------------------------------------------------------- /internal/frontend/bridge-gui/bridgepp/bridgepp/SessionID/SessionID.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | 19 | #ifndef BRIDGE_PP_SESSION_ID_H 20 | #define BRIDGE_PP_SESSION_ID_H 21 | 22 | 23 | namespace bridgepp { 24 | 25 | 26 | extern QString const sessionIDFlag; ///< The sessionID command-line flag (without hyphens) 27 | extern QString const hyphenatedSessionIDFlag; ///< The sessionID command-line flag (with two hyphens) 28 | 29 | 30 | QString newSessionID(); ///< Create a new sessions 31 | QDateTime sessionIDToDateTime(QString const &sessionID); ///< Parse the date/time from a sessionID. 32 | 33 | 34 | } // namespace 35 | 36 | #endif //BRIDGE_PP_SESSION_ID_H 37 | -------------------------------------------------------------------------------- /internal/frontend/grpc/event_utils.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package grpc 19 | 20 | import "github.com/bradenaw/juniper/xslices" 21 | 22 | // isInternetStatus returns true iff the event is InternetStatus. 23 | func (x *StreamEvent) isInternetStatus() bool { 24 | appEvent := x.GetApp() 25 | 26 | return (appEvent != nil) && (appEvent.GetInternetStatus() != nil) 27 | } 28 | 29 | // filterOutInternetStatusEvents return a copy of the events list where all internet connection events have been removed. 30 | func filterOutInternetStatusEvents(events []*StreamEvent) []*StreamEvent { 31 | return xslices.Filter(events, func(event *StreamEvent) bool { return !event.isInternetStatus() }) 32 | } 33 | -------------------------------------------------------------------------------- /internal/frontend/grpc/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge.Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package grpc 19 | 20 | type Restarter interface { 21 | Set(restart, crash bool) 22 | AddFlags(flags ...string) 23 | Override(exe string) 24 | } 25 | -------------------------------------------------------------------------------- /internal/frontend/theme/detect_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !windows && !darwin 19 | // +build !windows,!darwin 20 | 21 | package theme 22 | 23 | func detectSystemTheme() Theme { 24 | return Light 25 | } 26 | -------------------------------------------------------------------------------- /internal/frontend/theme/theme.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package theme 19 | 20 | import ( 21 | "runtime" 22 | ) 23 | 24 | type Theme string 25 | 26 | const ( 27 | Light = Theme("light") 28 | Dark = Theme("dark") 29 | ) 30 | 31 | func IsAvailable(have Theme) bool { 32 | return have == Light || have == Dark 33 | } 34 | 35 | func DefaultTheme() Theme { 36 | switch runtime.GOOS { 37 | case "darwin", "windows": 38 | return detectSystemTheme() 39 | default: 40 | return Light 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /internal/identifier/identifier.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package identifier 19 | 20 | type Identifier interface { 21 | GetUserAgent() string 22 | HasClient() bool 23 | SetClient(name, version string) 24 | SetPlatform(platform string) 25 | SetClientString(client string) 26 | GetClientString() string 27 | } 28 | 29 | type UserAgentUpdater interface { 30 | Identifier 31 | SetUserAgent(name, version string) 32 | } 33 | -------------------------------------------------------------------------------- /internal/logging/crash_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package logging 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/ProtonMail/proton-bridge/v3/internal/constants" 24 | "github.com/stretchr/testify/require" 25 | ) 26 | 27 | func TestLogging_MatchStackTraceName(t *testing.T) { 28 | filename := getStackTraceName(NewSessionID(), constants.AppName, constants.Version, constants.Tag) 29 | require.True(t, len(filename) > 0) 30 | require.True(t, MatchStackTraceName(filename)) 31 | require.False(t, MatchStackTraceName("Invalid.log")) 32 | } 33 | -------------------------------------------------------------------------------- /internal/logging/imap_logger.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package logging 19 | 20 | import "github.com/sirupsen/logrus" 21 | 22 | // IMAPLogger implements the writer interface for Gluon IMAP logs. 23 | type IMAPLogger struct{} 24 | 25 | func NewIMAPLogger() *IMAPLogger { 26 | return &IMAPLogger{} 27 | } 28 | 29 | func (l *IMAPLogger) Write(p []byte) (n int, err error) { 30 | return logrus.WithField("pkg", "log/IMAP").WriterLevel(logrus.TraceLevel).Write(p) 31 | } 32 | -------------------------------------------------------------------------------- /internal/logging/sensitive_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !sensitive 19 | 20 | package logging 21 | 22 | import ( 23 | "crypto/sha256" 24 | "encoding/hex" 25 | "fmt" 26 | ) 27 | 28 | func Sensitive(s string) string { 29 | return fmt.Sprintf("******** (%s)", hash(s)) 30 | } 31 | 32 | func hash(s string) string { 33 | h := sha256.New() 34 | 35 | if _, err := h.Write([]byte(s)); err != nil { 36 | panic(err) 37 | } 38 | 39 | return hex.EncodeToString(h.Sum(nil))[0:8] 40 | } 41 | -------------------------------------------------------------------------------- /internal/logging/sensitive_sensitive.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build sensitive 19 | 20 | package logging 21 | 22 | func Sensitive(s string) string { 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /internal/logging/session_id_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | 17 | package logging 18 | 19 | import ( 20 | "testing" 21 | "time" 22 | 23 | "github.com/stretchr/testify/require" 24 | ) 25 | 26 | func TestLogging_SessionID(t *testing.T) { 27 | now := time.Now() 28 | sessionID := NewSessionID() 29 | sessionTime := sessionID.toTime() 30 | require.False(t, sessionTime.IsZero()) 31 | require.WithinRange(t, sessionTime, now.Add(-1*time.Millisecond), now.Add(1*time.Millisecond)) 32 | 33 | fromString := NewSessionIDFromString("") 34 | require.True(t, len(fromString) > 0) 35 | fromString = NewSessionIDFromString(string(sessionID)) 36 | require.True(t, len(fromString) > 0) 37 | require.Equal(t, sessionID, fromString) 38 | } 39 | -------------------------------------------------------------------------------- /internal/sentry/hostarch_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !darwin 19 | // +build !darwin 20 | 21 | package sentry 22 | 23 | import ( 24 | "github.com/elastic/go-sysinfo/types" 25 | ) 26 | 27 | func getHostArch(host types.Host) string { 28 | return host.Info().Architecture 29 | } 30 | -------------------------------------------------------------------------------- /internal/sentry/lang_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package sentry 19 | 20 | import ( 21 | "github.com/jeandeaual/go-locale" 22 | "github.com/sirupsen/logrus" 23 | ) 24 | 25 | func GetSystemLang() string { 26 | lang, err := locale.GetLanguage() 27 | if err != nil { 28 | logrus.WithError(err).Error("Failed to get system language") 29 | lang = "Unknown" 30 | } 31 | return lang 32 | } 33 | -------------------------------------------------------------------------------- /internal/service/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge.Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package service 19 | 20 | type Locator interface { 21 | ProvideSettingsPath() (string, error) 22 | ProvideUnleashCachePath() (string, error) 23 | } 24 | -------------------------------------------------------------------------------- /internal/services/imapsmtpserver/telemetry.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package imapsmtpserver 19 | 20 | type Telemetry interface { 21 | SetCacheLocation(string) 22 | } 23 | -------------------------------------------------------------------------------- /internal/services/smtp/smtp_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !build_qa 19 | 20 | package smtp 21 | 22 | func debugDumpToDisk(_ []byte) error { 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /internal/services/syncservice/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package syncservice 19 | 20 | import ( 21 | "context" 22 | "time" 23 | ) 24 | 25 | // sleepCtx sleeps for the given duration, or until the context is canceled. 26 | func sleepCtx(ctx context.Context, d time.Duration) { 27 | select { 28 | case <-ctx.Done(): 29 | case <-time.After(d): 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /internal/services/userevents/event_controller.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package userevents 19 | 20 | type EventController interface { 21 | Pause() 22 | Resume() 23 | } 24 | -------------------------------------------------------------------------------- /internal/services/userevents/subscribable.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package userevents 19 | 20 | // Subscribable represents a type that allows the registration of event subscribers. 21 | type Subscribable interface { 22 | Subscribe(subscription EventSubscriber) 23 | Unsubscribe(subscription EventSubscriber) 24 | } 25 | 26 | type NoOpSubscribable struct{} 27 | 28 | func (n NoOpSubscribable) Subscribe(_ EventSubscriber) { 29 | // Does nothing 30 | } 31 | 32 | func (n NoOpSubscribable) Unsubscribe(_ EventSubscriber) { 33 | // Does nothing 34 | } 35 | -------------------------------------------------------------------------------- /internal/services/useridentity/auth.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package useridentity 19 | 20 | type KeyPassProvider interface { 21 | KeyPass() []byte 22 | } 23 | 24 | type BridgePassProvider interface { 25 | BridgePass() []byte 26 | } 27 | 28 | type FixedBridgePassProvider struct { 29 | pass []byte 30 | } 31 | 32 | func (f FixedBridgePassProvider) BridgePass() []byte { 33 | return f.pass 34 | } 35 | 36 | func NewFixedBridgePassProvider(pass []byte) *FixedBridgePassProvider { 37 | return &FixedBridgePassProvider{pass: pass} 38 | } 39 | -------------------------------------------------------------------------------- /internal/updater/channel.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package updater 19 | 20 | // Channel represents an update channel users can be subscribed to. 21 | type Channel string 22 | 23 | const ( 24 | // StableChannel is the channel all users are subscribed to by default. 25 | StableChannel Channel = "stable" 26 | 27 | // EarlyChannel is the channel users subscribe to when they enable "Early Access". 28 | EarlyChannel Channel = "early" 29 | ) 30 | 31 | // DefaultUpdateChannel is the default update channel to subscribe to. 32 | // It is set to the stable channel by default, unless overridden at build time. 33 | var DefaultUpdateChannel = StableChannel //nolint:gochecknoglobals 34 | -------------------------------------------------------------------------------- /internal/updater/host_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !build_qa 19 | // +build !build_qa 20 | 21 | package updater 22 | 23 | const Host = "https://proton.me/download" 24 | -------------------------------------------------------------------------------- /internal/updater/host_qa.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build build_qa 19 | // +build build_qa 20 | 21 | package updater 22 | 23 | const Host = "https://bridgeteam.protontech.ch/bridgeteam/autoupdates/download" 24 | -------------------------------------------------------------------------------- /internal/updater/keyring.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package updater 19 | 20 | import ( 21 | "github.com/ProtonMail/gopenpgp/v2/crypto" 22 | "github.com/sirupsen/logrus" 23 | ) 24 | 25 | func GetDefaultKeyring() (*crypto.KeyRing, error) { 26 | l := logrus.WithField("pkg", "updater") 27 | 28 | key, err := crypto.NewKeyFromArmored(DefaultPublicKey) 29 | if err != nil { 30 | l.WithError(err).Error("Failed to create new verification key") 31 | return nil, err 32 | } 33 | 34 | kr, err := crypto.NewKeyRing(key) 35 | if err != nil { 36 | l.WithError(err).Fatal("Failed to create new verification keyring") 37 | } 38 | 39 | return kr, nil 40 | } 41 | -------------------------------------------------------------------------------- /internal/updater/versioncompare/compare_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build linux 19 | 20 | package versioncompare 21 | 22 | import ( 23 | "github.com/elastic/go-sysinfo/types" 24 | "github.com/sirupsen/logrus" 25 | ) 26 | 27 | // IsHostVersionEligible - Checks whether host OS version is eligible for update. Defaults to true on Linux. 28 | func (sysVer SystemVersion) IsHostVersionEligible(log *logrus.Entry, _ types.Host, _ func(host types.Host) string) (bool, error) { 29 | log.Info("Checking host OS version on Linux. Defaulting to true.") 30 | return true, nil 31 | } 32 | -------------------------------------------------------------------------------- /internal/updater/versioncompare/compare_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build windows 19 | 20 | package versioncompare 21 | 22 | import ( 23 | "github.com/elastic/go-sysinfo/types" 24 | "github.com/sirupsen/logrus" 25 | ) 26 | 27 | // IsHostVersionEligible - Checks whether host OS version is eligible for update. Defaults to true on Linux. 28 | func (sysVer SystemVersion) IsHostVersionEligible(log *logrus.Entry, _ types.Host, _ func(host types.Host) string) (bool, error) { 29 | log.Info("Checking host OS version on Windows. Defaulting to true.") 30 | return true, nil 31 | } 32 | -------------------------------------------------------------------------------- /internal/updater/versioncompare/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package versioncompare 19 | 20 | import "fmt" 21 | 22 | type SystemVersion struct { 23 | Minimum string `json:"Minimum,omitempty"` 24 | Maximum string `json:"Maximum,omitempty"` 25 | } 26 | 27 | func (sysVer SystemVersion) String() string { 28 | return fmt.Sprintf("SystemVersion: Maximum %s, Minimum %s", sysVer.Maximum, sysVer.Minimum) 29 | } 30 | -------------------------------------------------------------------------------- /internal/user/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package user 19 | 20 | import "errors" 21 | 22 | var ( 23 | ErrNoSuchAddress = errors.New("no such address") 24 | ErrMissingAddrKey = errors.New("missing address key") 25 | ) 26 | -------------------------------------------------------------------------------- /internal/useragent/platform_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build darwin 19 | // +build darwin 20 | 21 | package useragent 22 | 23 | import ( 24 | "syscall" 25 | ) 26 | 27 | func getDarwinVersion() (string, error) { 28 | return syscall.Sysctl("kern.osrelease") 29 | } 30 | -------------------------------------------------------------------------------- /internal/useragent/platform_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !darwin 19 | // +build !darwin 20 | 21 | package useragent 22 | 23 | import "errors" 24 | 25 | func getDarwinVersion() (string, error) { 26 | return "", errors.New("implemented only for darwin") 27 | } 28 | -------------------------------------------------------------------------------- /internal/vault/certs_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package vault_test 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/stretchr/testify/require" 24 | ) 25 | 26 | func TestVault_TLSCerts(t *testing.T) { 27 | // create a new test vault. 28 | s := newVault(t) 29 | 30 | // Check the default bridge TLS certs. 31 | cert, key := s.GetBridgeTLSCert() 32 | require.NotEmpty(t, cert) 33 | require.NotEmpty(t, key) 34 | } 35 | -------------------------------------------------------------------------------- /internal/vault/cookies.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package vault 19 | 20 | func (vault *Vault) GetCookies() ([]byte, error) { 21 | return vault.getSafe().Cookies, nil 22 | } 23 | 24 | func (vault *Vault) SetCookies(cookies []byte) error { 25 | return vault.modSafe(func(data *Data) { 26 | data.Cookies = cookies 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /internal/vault/migrate.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package vault 19 | 20 | import "fmt" 21 | 22 | type Version int 23 | 24 | const ( 25 | v2_3_x Version = iota 26 | v2_4_x 27 | v2_5_x 28 | 29 | Current = v2_5_x 30 | ) 31 | 32 | // upgrade migrates the vault from the given version to the next version. 33 | func upgrade(v Version, b []byte) ([]byte, error) { 34 | switch v { 35 | case v2_3_x: 36 | return upgrade_2_3_x(b) 37 | 38 | case v2_4_x: 39 | return upgrade_2_4_x(b) 40 | 41 | case Current: 42 | return nil, fmt.Errorf("already at current version %d", Current) 43 | 44 | default: 45 | return nil, fmt.Errorf("unknown version %d", v) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /internal/vault/token.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package vault 19 | 20 | import ( 21 | "github.com/ProtonMail/gopenpgp/v2/crypto" 22 | ) 23 | 24 | // RandomToken is a function that returns a random token. 25 | // By default, we use crypto.RandomToken to generate tokens. 26 | var RandomToken = crypto.RandomToken // nolint:gochecknoglobals 27 | 28 | func newRandomToken(size int) []byte { 29 | token, err := RandomToken(size) 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | return token 35 | } 36 | -------------------------------------------------------------------------------- /internal/vault/types_data.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package vault 19 | 20 | type Data struct { 21 | Settings Settings 22 | Users []UserData 23 | Cookies []byte 24 | Certs Certs 25 | Migrated bool 26 | } 27 | 28 | func newDefaultData(gluonDir string) Data { 29 | return Data{ 30 | Settings: newDefaultSettings(gluonDir), 31 | Certs: newDefaultCerts(), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /internal/vault/types_password_archive.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package vault 19 | 20 | // PasswordArchive maps a list email address hashes to passwords. 21 | // The type is not defined as a map alias to prevent having to handle nil default values when vault was created by an older version of the application. 22 | type PasswordArchive struct { 23 | // we store the SHA-256 sum as string for readability and JSON marshalling of map[[32]byte][]byte will not be allowed, thus breaking vault-editor. 24 | Archive map[string][]byte 25 | } 26 | -------------------------------------------------------------------------------- /internal/vault/vault_debug.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build debug 19 | 20 | package vault 21 | 22 | import ( 23 | "encoding/json" 24 | ) 25 | 26 | func (vault *Vault) ImportJSON(dec []byte) { 27 | vault.modSafe(func(data *Data) { 28 | if err := json.Unmarshal(dec, data); err != nil { 29 | panic(err) 30 | } 31 | }) 32 | } 33 | 34 | func (vault *Vault) ExportJSON() []byte { 35 | enc, err := json.MarshalIndent(vault.getSafe(), "", " ") 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | return enc 41 | } 42 | -------------------------------------------------------------------------------- /internal/versioner/install.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package versioner 19 | 20 | import ( 21 | "compress/gzip" 22 | "io" 23 | "path/filepath" 24 | 25 | "github.com/Masterminds/semver/v3" 26 | "github.com/ProtonMail/proton-bridge/v3/pkg/tar" 27 | ) 28 | 29 | // InstallNewVersion installs a tgz update package of the given version. 30 | func (v *Versioner) InstallNewVersion(version *semver.Version, r io.Reader) error { 31 | gr, err := gzip.NewReader(r) 32 | if err != nil { 33 | return err 34 | } 35 | defer func() { _ = gr.Close() }() 36 | 37 | return tar.UntarToDir(gr, filepath.Join(v.root, version.Original())) 38 | } 39 | -------------------------------------------------------------------------------- /internal/versioner/name_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !windows 19 | // +build !windows 20 | 21 | package versioner 22 | 23 | func getExeName(name string) string { 24 | return name 25 | } 26 | -------------------------------------------------------------------------------- /internal/versioner/name_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package versioner 19 | 20 | import "strings" 21 | 22 | func getExeName(name string) string { 23 | if strings.HasSuffix(name, ".exe") { 24 | return name 25 | } 26 | 27 | return name + ".exe" 28 | } 29 | -------------------------------------------------------------------------------- /internal/versioner/util.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package versioner 19 | 20 | import ( 21 | "os" 22 | "runtime" 23 | ) 24 | 25 | // fileExists returns whether the given file exists. 26 | func fileExists(path string) bool { 27 | _, err := os.Stat(path) 28 | return err == nil 29 | } 30 | 31 | // fileIsExecutable returns the given filepath and true if it exists. 32 | func fileIsExecutable(path string) bool { 33 | if runtime.GOOS == "windows" { 34 | return true 35 | } 36 | 37 | info, err := os.Stat(path) 38 | if err != nil { 39 | return false 40 | } 41 | 42 | return info.Mode()&0o111 != 0 43 | } 44 | -------------------------------------------------------------------------------- /pkg/algo/algo.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | // Package algo provides some algorithm utils. 19 | package algo 20 | -------------------------------------------------------------------------------- /pkg/algo/hash.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package algo 19 | 20 | import ( 21 | "crypto/sha256" 22 | "encoding/base64" 23 | "encoding/hex" 24 | ) 25 | 26 | func Hash256(b []byte) []byte { 27 | h := sha256.Sum256(b) 28 | return h[:] 29 | } 30 | 31 | func HashBase64SHA256(s string) string { 32 | return base64.StdEncoding.EncodeToString( 33 | Hash256([]byte(s)), 34 | ) 35 | } 36 | 37 | func HashHexSHA256(s string) string { 38 | return hex.EncodeToString( 39 | Hash256([]byte(s)), 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /pkg/keychain/keychain_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !darwin 19 | // +build !darwin 20 | 21 | package keychain 22 | 23 | import "fmt" 24 | 25 | // hostURL uniquely identifies the app's keychain items within the system keychain. 26 | func hostURL(keychainName string) string { 27 | return fmt.Sprintf("protonmail/%v/users", keychainName) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/message/message.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | // Package message contains set of tools to convert message between Proton API 19 | // and IMAP format. 20 | package message 21 | 22 | import ( 23 | "github.com/sirupsen/logrus" 24 | ) 25 | 26 | var log = logrus.WithField("pkg", "pkg/message") //nolint:gochecknoglobals 27 | -------------------------------------------------------------------------------- /pkg/message/parser/testdata/multipart_alternative.eml: -------------------------------------------------------------------------------- 1 | To: pmbridgeietest@outlook.com 2 | From: schizofrenic 3 | Subject: aoeuaoeu 4 | Message-ID: <7dc32b61-b9cf-f2d3-8ec5-10e5b4a33ec1@pm.me> 5 | Date: Thu, 30 Jul 2020 13:35:24 +0200 6 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:68.0) 7 | Gecko/20100101 Thunderbird/68.11.0 8 | MIME-Version: 1.0 9 | Content-Type: multipart/alternative; 10 | boundary="------------22BC647264E52252E386881A" 11 | Content-Language: en-US 12 | 13 | This is a multi-part message in MIME format. 14 | --------------22BC647264E52252E386881A 15 | Content-Type: text/plain; charset=utf-8; format=flowed 16 | Content-Transfer-Encoding: 7bit 17 | 18 | *aoeuaoeu* 19 | 20 | 21 | --------------22BC647264E52252E386881A 22 | Content-Type: text/html; charset=utf-8 23 | Content-Transfer-Encoding: 7bit 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |

aoeuaoeu
32 |

33 | 34 | 35 | 36 | --------------22BC647264E52252E386881A-- 37 | -------------------------------------------------------------------------------- /pkg/message/parser/testdata/text_html.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | Content-Type: text/html 7 | 8 | This is body of HTML mail with attachment 9 | --longrandomstring-- 10 | -------------------------------------------------------------------------------- /pkg/message/parser/testdata/text_html_octet_attachment.eml: -------------------------------------------------------------------------------- 1 | Mime-Version: 1.0 2 | From: Sender 3 | To: Receiver 4 | Content-Type: multipart/mixed; boundary=longrandomstring 5 | 6 | --longrandomstring 7 | Content-Type: text/html 8 | 9 | This is body of HTML mail with attachment 10 | --longrandomstring 11 | Content-Type: application/octet-stream 12 | Content-Transfer-Encoding: base64 13 | Content-Disposition: attachment 14 | 15 | aWYgeW91IGFyZSByZWFkaW5nIHRoaXMsIGhpIQ== 16 | --longrandomstring-- 17 | -------------------------------------------------------------------------------- /pkg/message/parser/writer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package parser 19 | 20 | import ( 21 | "bytes" 22 | "strings" 23 | "testing" 24 | 25 | "github.com/stretchr/testify/assert" 26 | ) 27 | 28 | func TestParserWrite(t *testing.T) { 29 | p := newTestParser(t, "text_html_octet_attachment.eml") 30 | 31 | w := p.NewWriter() 32 | 33 | buf := new(bytes.Buffer) 34 | 35 | assert.NoError(t, w.Write(buf)) 36 | assert.Equal(t, getFileAsString("text_html_octet_attachment.eml"), crlf(buf.String())) 37 | } 38 | 39 | func crlf(s string) string { 40 | return strings.ReplaceAll(s, "\r\n", "\n") 41 | } 42 | -------------------------------------------------------------------------------- /pkg/message/testdata/ics_attachment.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | 7 | body 8 | --longrandomstring 9 | Content-Type: text/calendar; charset=utf-8; method=REQUEST; name=invite.ics' 10 | Content-Transfer-Encoding: Base64 11 | Content-Disposition: attachment; filename=invite.ics 12 | 13 | VGhpcyBpcyBhbiBpY3MgY2FsZW5kYXIgaW52aXRl 14 | --longrandomstring-- 15 | -------------------------------------------------------------------------------- /pkg/message/testdata/incorrect_boundary_w_invalid_character_tuta.eml: -------------------------------------------------------------------------------- 1 | Date: Mon, 01 Jan 2000 00:00:00 +0000 (UTC) 2 | From: Daniel at Test 3 | Mime-Version: 1.0 4 | Subject: Test incorrect original boundary w. invalid character 5 | To: david@test.com 6 | Content-Type: multipart/related; boundary="------------1234567890@tutanota" 7 | 8 | --------------1234567890@tutanota 9 | Content-Type: text/html; charset=UTF-8 10 | Content-transfer-encoding: base64 11 | 12 | PGh0bWw+PGgxPkhlbGxvIFdvcmxkITwvaDE+PC9odG1sPg== 13 | --------------1234567890@tutanota-- 14 | -------------------------------------------------------------------------------- /pkg/message/testdata/multipart_alternative.eml: -------------------------------------------------------------------------------- 1 | To: pmbridgeietest@outlook.com 2 | From: schizofrenic 3 | Subject: aoeuaoeu 4 | Date: Thu, 30 Jul 2020 13:35:24 +0200 5 | MIME-Version: 1.0 6 | Content-Type: multipart/alternative; boundary="------------22BC647264E52252E386881A" 7 | Content-Language: en-US 8 | 9 | This is a multi-part message in MIME format. 10 | --------------22BC647264E52252E386881A 11 | Content-Type: text/plain; charset=utf-8; format=flowed 12 | Content-Transfer-Encoding: 7bit 13 | 14 | *aoeuaoeu* 15 | 16 | 17 | --------------22BC647264E52252E386881A 18 | Content-Type: text/html; charset=utf-8 19 | Content-Transfer-Encoding: 7bit 20 | 21 | 22 | 23 | 24 | 25 | 26 | aoeuaoeu 27 | 28 | 29 | 30 | --------------22BC647264E52252E386881A-- -------------------------------------------------------------------------------- /pkg/message/testdata/multipart_alternative_latin1.eml: -------------------------------------------------------------------------------- 1 | To: pmbridgeietest@outlook.com 2 | From: schizofrenic 3 | Subject: aoeuaoeu 4 | Date: Thu, 30 Jul 2020 13:35:24 +0200 5 | MIME-Version: 1.0 6 | Content-Type: multipart/alternative; boundary="------------22BC647264E52252E386881A"; charset="iso-8859-1" 7 | Content-Language: en-US 8 | 9 | This is a multi-part message in MIME format. 10 | --------------22BC647264E52252E386881A 11 | Content-Type: text/plain 12 | Content-Transfer-Encoding: 7bit 13 | 14 | *aoeuaoeu* 15 | 16 | 17 | --------------22BC647264E52252E386881A 18 | Content-Type: text/html 19 | Content-Transfer-Encoding: 7bit 20 | 21 | 22 | 23 | 24 | 25 | 26 | aoeuaoeu 27 | 28 | 29 | 30 | --------------22BC647264E52252E386881A-- -------------------------------------------------------------------------------- /pkg/message/testdata/multipart_attachment_encoded_no_quote.eml: -------------------------------------------------------------------------------- 1 | From: Bridge Test 2 | Date: 01 Jan 1980 00:00:00 +0000 3 | To: Internal Bridge 4 | Subject: Message with attachment name 5 | Content-type: multipart/mixed; boundary="boundary" 6 | Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000 7 | 8 | This is a multi-part message in MIME format. 9 | 10 | --boundary 11 | Content-Type: text/plain 12 | 13 | Hello 14 | 15 | --boundary 16 | Content-Type: text/html; charset=utf-8 17 | Content-Transfer-Encoding: 7bit 18 | 19 |

HELLO

20 | 21 | --boundary 22 | Content-Type: application/pdf; name==?US-ASCII?Q?filename?= 23 | Content-Disposition: attachment; filename==?US-ASCII?Q?filename?= 24 | 25 | somebytes 26 | 27 | --boundary-- 28 | -------------------------------------------------------------------------------- /pkg/message/testdata/multiple_text_parts.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | this part of the text should be ignored 6 | 7 | --longrandomstring 8 | 9 | body 10 | 11 | --longrandomstring 12 | 13 | some other part of the message 14 | --longrandomstring-- -------------------------------------------------------------------------------- /pkg/message/testdata/non-encoded-content-transfer-encoding.eml: -------------------------------------------------------------------------------- 1 | To: user@somewhere.org 2 | Subject: =?utf-8?Q?aoeuaoeuaoeu?= 3 | Date: Sat, 16 Jun 2020 17:36:02 +0200 4 | MIME-Version: 1.0 5 | Content-Type: text/plain; 6 | charset="utf-8" 7 | Content-Transfer-Encoding: 8bit 8 | From: =?utf-8?Q?Sender?= 9 | 10 | bodybodybody 11 | -------------------------------------------------------------------------------- /pkg/message/testdata/pgp-mime-body-html.eml: -------------------------------------------------------------------------------- 1 | Content-Type: multipart/mixed; boundary="u5NoTcx3NkhqapFjjYFKJZdxCaEWvrsGw"; 2 | protected-headers="v1" 3 | Subject: html no pubkey no sign 4 | From: "pm.bridge.qa" 5 | To: schizofrenic@pm.me 6 | Message-ID: 7 | 8 | --u5NoTcx3NkhqapFjjYFKJZdxCaEWvrsGw 9 | Content-Type: text/html; charset=utf-8 10 | Content-Language: en-US 11 | Content-Transfer-Encoding: quoted-printable 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 |
    21 |
  • What do you call a poor Santa Claus? St. 22 | Nickel-less.
  • 23 |
  • Where do boats go when they're sick? To the boat 24 | doc.
    25 |
  • 26 |
27 |


28 |

29 | 30 | 31 | 32 | 33 | --u5NoTcx3NkhqapFjjYFKJZdxCaEWvrsGw-- 34 | -------------------------------------------------------------------------------- /pkg/message/testdata/pgp-mime-body-plaintext-latin2.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/pkg/message/testdata/pgp-mime-body-plaintext-latin2.eml -------------------------------------------------------------------------------- /pkg/message/testdata/pgp-mime-body-plaintext.eml: -------------------------------------------------------------------------------- 1 | Content-Type: multipart/mixed; boundary="unlHEst6hn6dMAzATXJvy5dCLgUfF9Vvs"; 2 | protected-headers="v1" 3 | Subject: plain no pubkey no sign 4 | From: "pm.bridge.qa" 5 | To: schizofrenic@pm.me 6 | Message-ID: <564b9c7c-91eb-6508-107a-35108f383a44@gmail.com> 7 | 8 | --unlHEst6hn6dMAzATXJvy5dCLgUfF9Vvs 9 | Content-Type: text/plain; charset=utf-8; format=flowed 10 | Content-Transfer-Encoding: quoted-printable 11 | Content-Language: en-US 12 | 13 | Where do fruits go on vacation? Pear-is! 14 | 15 | 16 | 17 | --unlHEst6hn6dMAzATXJvy5dCLgUfF9Vvs-- 18 | -------------------------------------------------------------------------------- /pkg/message/testdata/plaintext-invalid-header.eml: -------------------------------------------------------------------------------- 1 | MalformedKey Value 2 | 3 | How do we know that the ocean is friendly? It waves! 4 | -------------------------------------------------------------------------------- /pkg/message/testdata/plaintext-missing-header.eml: -------------------------------------------------------------------------------- 1 | How do we know that the ocean is friendly? It waves! 2 | -------------------------------------------------------------------------------- /pkg/message/testdata/references-comma.eml: -------------------------------------------------------------------------------- 1 | Content-Type: multipart/mixed; 2 | boundary=987c7102dcaf02d01860ce777b465f86d39ec16a3b4e12605eb6b0eb200a 3 | X-Original-To: someone@protonmail.com 4 | Delivered-To: someone@protonmail.com 5 | Date: Sun, 04 Aug 2019 13:03:26 +0000 6 | From: ProtonVPN 7 | Reply-To: ProtonVPN 8 | To: someone 9 | Message-Id: 10 | In-Reply-To: 11 | References: , 12 | Subject: Some test subject 13 | Mime-Version: 1.0 14 | 15 | --987c7102dcaf02d01860ce777b465f86d39ec16a3b4e12605eb6b0eb200a 16 | Content-Transfer-Encoding: quoted-printable 17 | Content-Type: text/html; charset=utf-8 18 | 19 | 21 | 22 | 23 | test test test 24 | 25 | 26 | 27 | --987c7102dcaf02d01860ce777b465f86d39ec16a3b4e12605eb6b0eb200a-- 28 | -------------------------------------------------------------------------------- /pkg/message/testdata/references.eml: -------------------------------------------------------------------------------- 1 | Content-Type: multipart/mixed; 2 | boundary=987c7102dcaf02d01860ce777b465f86d39ec16a3b4e12605eb6b0eb200a 3 | X-Original-To: someone@protonmail.com 4 | Delivered-To: someone@protonmail.com 5 | Date: Sun, 04 Aug 2019 13:03:26 +0000 6 | From: ProtonVPN 7 | Reply-To: ProtonVPN 8 | To: someone 9 | Message-Id: 10 | In-Reply-To: 11 | References: 12 | Subject: Some test subject 13 | Mime-Version: 1.0 14 | 15 | --987c7102dcaf02d01860ce777b465f86d39ec16a3b4e12605eb6b0eb200a 16 | Content-Transfer-Encoding: quoted-printable 17 | Content-Type: text/html; charset=utf-8 18 | 19 | 21 | 22 | 23 | test test test 24 | 25 | 26 | 27 | --987c7102dcaf02d01860ce777b465f86d39ec16a3b4e12605eb6b0eb200a-- 28 | -------------------------------------------------------------------------------- /pkg/message/testdata/reply-to_no_references.eml: -------------------------------------------------------------------------------- 1 | Content-Type: multipart/mixed; 2 | boundary=987c7102dcaf02d01860ce777b465f86d39ec16a3b4e12605eb6b0eb200a 3 | X-Original-To: someone@protonmail.com 4 | Delivered-To: someone@protonmail.com 5 | Date: Sun, 04 Aug 2019 13:03:26 +0000 6 | From: ProtonVPN 7 | Reply-To: ProtonVPN 8 | To: someone 9 | Message-Id: 10 | In-Reply-To: 11 | Subject: Some test subject 12 | Mime-Version: 1.0 13 | 14 | --987c7102dcaf02d01860ce777b465f86d39ec16a3b4e12605eb6b0eb200a 15 | Content-Transfer-Encoding: quoted-printable 16 | Content-Type: text/html; charset=utf-8 17 | 18 | 20 | 21 | 22 | test test test 23 | 24 | 25 | 26 | --987c7102dcaf02d01860ce777b465f86d39ec16a3b4e12605eb6b0eb200a-- 27 | -------------------------------------------------------------------------------- /pkg/message/testdata/rfc2047-content-transfer-encoding-bad.eml: -------------------------------------------------------------------------------- 1 | To: user@somewhere.org 2 | Subject: =?utf-8?Q?aoeuaoeuaoeu?= 3 | Date: Sat, 16 Jun 2020 17:36:02 +0200 4 | MIME-Version: 1.0 5 | Content-Type: text/plain; 6 | charset="utf-8" 7 | Content-Transfer-Encoding: =?utf-8?Q?8bit 8 | From: =?utf-8?Q?Sender?= 9 | 10 | bodybodybody 11 | -------------------------------------------------------------------------------- /pkg/message/testdata/rfc2047-content-transfer-encoding.eml: -------------------------------------------------------------------------------- 1 | To: user@somewhere.org 2 | Subject: =?utf-8?Q?aoeuaoeuaoeu?= 3 | Date: Sat, 16 Jun 2020 17:36:02 +0200 4 | MIME-Version: 1.0 5 | Content-Type: text/plain; 6 | charset="utf-8" 7 | Content-Transfer-Encoding: =?utf-8?Q?8bit?= 8 | From: =?utf-8?Q?Sender?= 9 | 10 | bodybodybody 11 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_html.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: text/html 4 | 5 | This is body of HTML mail without attachment -------------------------------------------------------------------------------- /pkg/message/testdata/text_html_7bit.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: text/html 4 | Content-Transfer-Encoding: 7bit 5 | 6 | This is body of HTML mail without attachment -------------------------------------------------------------------------------- /pkg/message/testdata/text_html_embedded_foreign_encoding.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/pkg/message/testdata/text_html_embedded_foreign_encoding.eml -------------------------------------------------------------------------------- /pkg/message/testdata/text_html_octet_attachment.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | Content-Type: text/html 7 | 8 | This is body of HTML mail with attachment 9 | --longrandomstring 10 | Content-Type: application/octet-stream 11 | Content-Transfer-Encoding: base64 12 | 13 | aWYgeW91IGFyZSByZWFkaW5nIHRoaXMsIGhpIQ== 14 | --longrandomstring-- 15 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_html_plain_attachment.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | Content-Type: text/html 7 | 8 | This is body of HTML mail with attachment 9 | --longrandomstring 10 | Content-Disposition: attachment 11 | 12 | attachment 13 | --longrandomstring-- 14 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_html_trailing_end_of_mail.eml: -------------------------------------------------------------------------------- 1 | From: "Sender" 2 | To: "Receiver" 3 | Content-Type: text/html; charset="utf-8" 4 | Content-Transfer-Encoding: base64 5 | MIME-Version: 1.0 6 | 7 | PCFET0NUWVBFIEhUTUw+CjxodG1sPjxib2R5PmJvbyE8L2JvZHk+PC9odG1sPg== 8 | . 9 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | 4 | body -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_7bit.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Transfer-Encoding: 7bit 4 | 5 | body -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_bad_sender.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/pkg/message/testdata/text_plain_bad_sender.eml -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_bad_subject.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/pkg/message/testdata/text_plain_bad_subject.eml -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_base64.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Transfer-Encoding: base64 4 | 5 | Ym9keQ== 6 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_docx_attachment_cyrillic.eml: -------------------------------------------------------------------------------- 1 | Content-Type: multipart/mixed; boundary="------------nq8WTMHkJcymWO6pWfby0uY3" 2 | To: "Receiver" 3 | From: "Sender" 4 | Subject: Test with cyrillic attachment 5 | 6 | --------------nq8WTMHkJcymWO6pWfby0uY3 7 | Content-Type: text/plain; charset=UTF-8; format=flowed 8 | Content-Transfer-Encoding: 7bit 9 | 10 | Shake that body 11 | --------------nq8WTMHkJcymWO6pWfby0uY3 12 | Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document; 13 | name="=?UTF-8?B?0JDQkdCS0JPQlNCD0JXQltCX0IXQmNCI0JrQm9CJ0JzQndCK0J7Qn9Cg?= 14 | =?UTF-8?B?0KHQotCM0KPQpNCl0KfQj9CX0KguZG9jeA==?=" 15 | Content-Disposition: attachment; 16 | filename*0*=UTF-8''%D0%90%D0%91%D0%92%D0%93%D0%94%D0%83%D0%95%D0%96%D0%97; 17 | filename*1*=%D0%85%D0%98%D0%88%D0%9A%D0%9B%D0%89%D0%9C%D0%9D%D0%8A%D0%9E; 18 | filename*2*=%D0%9F%D0%A0%D0%A1%D0%A2%D0%8C%D0%A3%D0%A4%D0%A5%D0%A7%D0%8F; 19 | filename*3*=%D0%97%D0%A8%2E%64%6F%63%78 20 | Content-Transfer-Encoding: base64 21 | 22 | 0JDQkdCS0JPQlNCD0JXQltCX0IXQmNCI0JrQm9CJ0JzQndCK0J7Qn9Cg0KHQotCM0KPQpNCl0KfQj9CX0Kg= 23 | 24 | --------------nq8WTMHkJcymWO6pWfby0uY3-- 25 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_duplicate_charset.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: text/plain; charset=utf-8; charset=UTF-8 4 | 5 | body -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_empty_addresses.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | CC: 4 | Reply-To: <> 5 | 6 | body -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_latin1.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/pkg/message/testdata/text_plain_latin1.eml -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_latin2_subject.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?= 4 | =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?= 5 | 6 | body -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_octet_attachment.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | 7 | body 8 | --longrandomstring 9 | Content-Type: application/octet-stream 10 | Content-Transfer-Encoding: base64 11 | 12 | aWYgeW91IGFyZSByZWFkaW5nIHRoaXMsIGhpIQ== 13 | --longrandomstring-- -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_octet_attachment_bad_2231_filename.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | 7 | body 8 | --longrandomstring 9 | Content-Type: application/octet-stream 10 | Content-Transfer-Encoding: base64 11 | Content-Disposition: attachment; filename*=utf-8'%F0%9F%98%81%F0%9F%98%82.txt 12 | 13 | aWYgeW91IGFyZSByZWFkaW5nIHRoaXMsIGhpIQ== 14 | --longrandomstring-- 15 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_octet_attachment_good_2231_filename.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | 7 | body 8 | --longrandomstring 9 | Content-Type: application/octet-stream 10 | Content-Transfer-Encoding: base64 11 | Content-Disposition: attachment; filename*=utf-8''%F0%9F%98%81%F0%9F%98%82.txt 12 | 13 | aWYgeW91IGFyZSByZWFkaW5nIHRoaXMsIGhpIQ== 14 | --longrandomstring-- 15 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_octet_attachment_name_conflict.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | 7 | body 8 | --longrandomstring 9 | Content-Type: application/octet-stream; name="attachment-contenttype.txt" 10 | Content-Transfer-Encoding: base64 11 | Content-Disposition: attachment; filename="attachment-disposition.txt" 12 | 13 | aWYgeW91IGFyZSByZWFkaW5nIHRoaXMsIGhpIQ== 14 | --longrandomstring-- 15 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_octet_attachment_name_in_contenttype.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | 7 | body 8 | --longrandomstring 9 | Content-Type: application/octet-stream; name="attachment-contenttype.txt" 10 | Content-Transfer-Encoding: base64 11 | Content-Disposition: attachment 12 | 13 | aWYgeW91IGFyZSByZWFkaW5nIHRoaXMsIGhpIQ== 14 | --longrandomstring-- 15 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_pdf_attachment_cyrillic.eml: -------------------------------------------------------------------------------- 1 | Content-Type: multipart/mixed; boundary="------------bYzsV6z0EdKTbltmCDZgIM15" 2 | To: "Receiver" 3 | From: "Sender" 4 | Subject: Test with cyrillic attachment 5 | 6 | --------------bYzsV6z0EdKTbltmCDZgIM15 7 | Content-Transfer-Encoding: quoted-printable 8 | Content-Type: text/plain; charset=utf-8 9 | 10 | Shake that body 11 | --------------bYzsV6z0EdKTbltmCDZgIM15 12 | Content-Type: application/pdf; 13 | name="=?UTF-8?B?0JDQkdCS0JPQlNCD0JXQltCX0IXQmNCI0JrQm9CJ0JzQndCK0J7Qn9Cg?= 14 | =?UTF-8?B?0KHQotCM0KPQpNCl0KfQj9CX0KgucGRm?=" 15 | Content-Disposition: attachment; 16 | filename*0*=UTF-8''%D0%90%D0%91%D0%92%D0%93%D0%94%D0%83%D0%95%D0%96%D0%97; 17 | filename*1*=%D0%85%D0%98%D0%88%D0%9A%D0%9B%D0%89%D0%9C%D0%9D%D0%8A%D0%9E; 18 | filename*2*=%D0%9F%D0%A0%D0%A1%D0%A2%D0%8C%D0%A3%D0%A4%D0%A5%D0%A7%D0%8F; 19 | filename*3*=%D0%97%D0%A8%2E%70%64%66 20 | Content-Transfer-Encoding: base64 21 | 22 | 0JDQkdCS0JPQlNCD0JXQltCX0IXQmNCI0JrQm9CJ0JzQndCK0J7Qn9Cg0KHQotCM0KPQpNCl0KfQj9CX0Kg= 23 | 24 | 25 | --------------bYzsV6z0EdKTbltmCDZgIM15-- 26 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_plain_attachment.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | 7 | body 8 | --longrandomstring 9 | Content-Disposition: attachment 10 | 11 | attachment 12 | --longrandomstring-- -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_plain_attachment_latin1.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/pkg/message/testdata/text_plain_plain_attachment_latin1.eml -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_plain_attachment_latin2.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/pkg/message/testdata/text_plain_plain_attachment_latin2.eml -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_rfc822_attachment.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | 7 | body 8 | --longrandomstring 9 | Content-Type: message/rfc822 10 | Content-Disposition: attachment 11 | 12 | From: Sender 13 | To: Receiver 14 | 15 | inner body 16 | --longrandomstring-- 17 | -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_unknown_latin1.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/pkg/message/testdata/text_plain_unknown_latin1.eml -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_unknown_latin2.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/pkg/message/testdata/text_plain_unknown_latin2.eml -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_utf8.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: text/plain; charset=utf-8 4 | 5 | body -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_utf8_reply_to_and_x_forward.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: text/plain; charset=utf-8 4 | X-Forwarded-Message-Id: <00000@protonmail.com> 5 | In-Reply-To: <00000@protonmail.com> 6 | 7 | body -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_utf8_subject.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Subject: =?UTF-8?B?5rGJ5a2X5rGJ5a2X5rGJ?= 4 | 5 | body -------------------------------------------------------------------------------- /pkg/message/testdata/text_plain_xml_attachment_cp1250.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/pkg/message/testdata/text_plain_xml_attachment_cp1250.eml -------------------------------------------------------------------------------- /pkg/message/testdata/wrong_base64.eml: -------------------------------------------------------------------------------- 1 | From: Sender 2 | To: Receiver 3 | Content-Type: multipart/mixed; boundary=longrandomstring 4 | 5 | --longrandomstring 6 | Content-Type: text/html 7 | 8 | This is body of HTML mail with attachment 9 | --longrandomstring 10 | Content-Type: application/octet-stream 11 | Content-Transfer-Encoding: base64 12 | 13 | b'aWYgeW91IGFyZSByZWFkaW5nIHRoaXMsIGhpIQ==' 14 | --longrandomstring-- 15 | -------------------------------------------------------------------------------- /pkg/mime/Changelog.md: -------------------------------------------------------------------------------- 1 | # Do not modify this file! 2 | It is here for historical reasons only. All changes should be documented in the 3 | Changelog at the root of this repository. 4 | 5 | 6 | # Changelog 7 | 8 | ## [2019-12-10] v1.0.2 9 | 10 | ### Added 11 | * support for shift_JIS (cp932) encoding 12 | 13 | ## [2019-09-30] v1.0.1 14 | 15 | ### Changed 16 | * fix divide by zero 17 | 18 | ## [2019-09-26] v1.0.0 19 | 20 | ### Changed 21 | * Import-Export#192: filter header parameters 22 | * ignore twice the same parameter (take the latest) 23 | * convert non utf8 RFC2231 parameters to a single line utf8 RFC2231 24 | 25 | -------------------------------------------------------------------------------- /pkg/restarter/start_default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build !windows 19 | // +build !windows 20 | 21 | package restarter 22 | 23 | import ( 24 | "os/exec" 25 | "syscall" 26 | 27 | "github.com/sirupsen/logrus" 28 | ) 29 | 30 | func run(cmd *exec.Cmd) error { 31 | // Provide a new Group ID to the new process to ensure the child is detached even if parent crash before ending. 32 | cmd.SysProcAttr = &syscall.SysProcAttr{ 33 | Setpgid: true, 34 | Pgid: 0, 35 | } 36 | 37 | logrus.WithField("cmd", cmd).Info("Starting new process") 38 | 39 | return cmd.Start() 40 | } 41 | -------------------------------------------------------------------------------- /pkg/restarter/start_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | //go:build windows 19 | // +build windows 20 | 21 | package restarter 22 | 23 | import ( 24 | "os/exec" 25 | "syscall" 26 | 27 | "github.com/sirupsen/logrus" 28 | ) 29 | 30 | func run(cmd *exec.Cmd) error { 31 | // Provide a new Group ID to the new process to ensure the child is detached even if parent crash before ending. 32 | cmd.SysProcAttr = &syscall.SysProcAttr{ 33 | CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, 34 | } 35 | 36 | logrus.WithField("cmd", cmd).Info("Starting new process") 37 | 38 | return cmd.Start() 39 | } 40 | -------------------------------------------------------------------------------- /tests/e2e/ui_tests/windows_os/TestsHelper/TestData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.IO; 4 | 5 | namespace ProtonMailBridge.UI.Tests.TestsHelper 6 | { 7 | public static class TestData 8 | { 9 | public static TimeSpan FiveSecondsTimeout => TimeSpan.FromSeconds(5); 10 | public static TimeSpan TenSecondsTimeout => TimeSpan.FromSeconds(10); 11 | public static TimeSpan ThirtySecondsTimeout => TimeSpan.FromSeconds(30); 12 | public static TimeSpan OneMinuteTimeout => TimeSpan.FromSeconds(60); 13 | public static TimeSpan RetryInterval => TimeSpan.FromMilliseconds(1000); 14 | public static string AppExecutable => "C:\\Program Files\\Proton AG\\Proton Mail Bridge\\proton-bridge.exe"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/e2e/ui_tests/windows_os/UIActions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FlaUI.Core.AutomationElements; 3 | using FlaUI.Core.Definitions; 4 | using FlaUI.Core.Input; 5 | using FlaUI.Core.Tools; 6 | using NUnit.Framework; 7 | 8 | namespace ProtonMailBridge.UI.Tests 9 | { 10 | public class UIActions : TestSession 11 | { 12 | public AutomationElement AccountView => Window.FindFirstDescendant(cf => cf.ByControlType(ControlType.Pane)); 13 | } 14 | } -------------------------------------------------------------------------------- /tests/fast.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package tests 19 | 20 | import ( 21 | "crypto/x509" 22 | 23 | "github.com/ProtonMail/proton-bridge/v3/internal/certs" 24 | ) 25 | 26 | var ( 27 | preCompCertPEM []byte 28 | preCompKeyPEM []byte 29 | ) 30 | 31 | func FastGenerateCert(_ *x509.Certificate) ([]byte, []byte, error) { 32 | return preCompCertPEM, preCompKeyPEM, nil 33 | } 34 | 35 | func init() { 36 | template, err := certs.NewTLSTemplate() 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | certPEM, keyPEM, err := certs.GenerateCert(template) 42 | if err != nil { 43 | panic(err) 44 | } 45 | 46 | preCompCertPEM = certPEM 47 | preCompKeyPEM = keyPEM 48 | } 49 | -------------------------------------------------------------------------------- /tests/features/bridge/default_ports.feature: -------------------------------------------------------------------------------- 1 | Feature: Bridge picks default ports wisely 2 | 3 | Scenario: bridge picks ports for IMAP and SMTP using default values. 4 | When bridge starts 5 | Then bridge IMAP port is 1143 6 | Then bridge SMTP port is 1025 7 | 8 | Scenario: bridge picks ports for IMAP wisely when default port is busy. 9 | When the network port 1143 is busy 10 | And bridge starts 11 | Then bridge IMAP port is 1144 12 | Then bridge SMTP port is 1025 13 | 14 | Scenario: bridge picks ports for SMTP wisely when default port is busy. 15 | When the network port range 1025-1030 is busy 16 | And bridge starts 17 | Then bridge IMAP port is 1143 18 | Then bridge SMTP port is 1031 19 | 20 | Scenario: bridge picks ports for IMAP SMTP wisely when default ports are busy. 21 | When the network port range 1025-1200 is busy 22 | And bridge starts 23 | Then bridge IMAP port is 1201 24 | Then bridge SMTP port is 1202 25 | -------------------------------------------------------------------------------- /tests/features/frontend/frontend.feature: -------------------------------------------------------------------------------- 1 | Feature: Frontend events 2 | Scenario: Frontend starts and stops 3 | Given bridge is version "2.3.0" and the latest available version is "2.3.0" reachable from "2.3.0" 4 | When bridge starts 5 | Then frontend sees that bridge is version "2.3.0" 6 | -------------------------------------------------------------------------------- /tests/features/imap/mailbox/delete.feature: -------------------------------------------------------------------------------- 1 | Feature: IMAP delete mailbox 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | And the account "[user:user]" has the following custom mailboxes: 5 | | name | type | 6 | | one | folder | 7 | | two | folder | 8 | | three | label | 9 | Then it succeeds 10 | When bridge starts 11 | And the user logs in with username "[user:user]" and password "password" 12 | And user "[user:user]" finishes syncing 13 | And user "[user:user]" connects and authenticates IMAP client "1" 14 | Then it succeeds 15 | 16 | Scenario: Delete folder 17 | When IMAP client "1" deletes "Folders/one" 18 | Then IMAP client "1" does not see "Folders/one" 19 | But IMAP client "1" sees "Folders/two" 20 | But IMAP client "1" sees "Labels/three" 21 | 22 | Scenario: Delete label 23 | When IMAP client "1" deletes "Labels/three" 24 | Then IMAP client "1" does not see "Labels/three" 25 | But IMAP client "1" sees "Folders/one" 26 | But IMAP client "1" sees "Folders/two" 27 | 28 | Scenario: Deleting system mailbox is not possible 29 | When IMAP client "1" deletes "INBOX" 30 | Then it fails 31 | And IMAP client "1" sees "INBOX" 32 | -------------------------------------------------------------------------------- /tests/features/imap/mailbox/info.feature: -------------------------------------------------------------------------------- 1 | Feature: IMAP get mailbox info 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | And the account "[user:user]" has the following custom mailboxes: 5 | | name | type | 6 | | one | folder | 7 | And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Folders/one": 8 | | from | to | subject | unread | 9 | | a@example.com | a@example.com | one | true | 10 | | b@example.com | b@example.com | two | false | 11 | Then it succeeds 12 | When bridge starts 13 | And the user logs in with username "[user:user]" and password "password" 14 | And user "[user:user]" finishes syncing 15 | Then it succeeds 16 | 17 | Scenario: Mailbox status reports correct name, total and unread 18 | When user "[user:user]" connects and authenticates IMAP client "1" 19 | Then IMAP client "1" sees the following mailbox info for "Folders/one": 20 | | name | total | unread | 21 | | Folders/one | 2 | 1 | -------------------------------------------------------------------------------- /tests/features/imap/mailbox/rename.feature: -------------------------------------------------------------------------------- 1 | Feature: IMAP get mailbox info 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | And the account "[user:user]" has the following custom mailboxes: 5 | | name | type | 6 | | f1 | folder | 7 | | l1 | label | 8 | Then it succeeds 9 | When bridge starts 10 | And the user logs in with username "[user:user]" and password "password" 11 | And user "[user:user]" finishes syncing 12 | And user "[user:user]" connects and authenticates IMAP client "1" 13 | Then it succeeds 14 | 15 | Scenario: Rename folder 16 | When IMAP client "1" renames "Folders/f1" to "Folders/f2" 17 | Then IMAP client "1" sees "Folders/f2" 18 | And IMAP client "1" does not see "Folders/f1" 19 | 20 | Scenario: Rename label 21 | When IMAP client "1" renames "Labels/l1" to "Labels/l2" 22 | Then IMAP client "1" sees "Labels/l2" 23 | And IMAP client "1" does not see "Labels/l1" 24 | 25 | Scenario: Renaming folder to label is not possible 26 | When IMAP client "1" renames "Folders/f1" to "Labels/f2" 27 | Then it fails 28 | 29 | Scenario: Renaming system folder is not possible 30 | When IMAP client "1" renames "Labels/l1" to "Folders/l2" 31 | Then it fails -------------------------------------------------------------------------------- /tests/features/imap/mailbox/rename_hiearchy.feature: -------------------------------------------------------------------------------- 1 | Feature: IMAP get mailbox info 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | And the account "[user:user]" has the following custom mailboxes: 5 | | name | type | 6 | | f1 | folder | 7 | | f1/f2| folder | 8 | Then it succeeds 9 | When bridge starts 10 | And the user logs in with username "[user:user]" and password "password" 11 | And user "[user:user]" finishes syncing 12 | And user "[user:user]" connects and authenticates IMAP client "1" 13 | Then it succeeds 14 | 15 | # with black subfolder is not renamed (maybe missing event?) 16 | @skip-black 17 | Scenario: Rename folder with subfolders 18 | When IMAP client "1" renames "Folders/f1" to "Folders/f3" 19 | And it succeeds 20 | Then IMAP client "1" sees "Folders/f3" 21 | Then IMAP client "1" sees "Folders/f3/f2" 22 | And IMAP client "1" does not see "Folders/f1" 23 | And IMAP client "1" does not see "Folders/f1/f2" 24 | -------------------------------------------------------------------------------- /tests/features/imap/mailbox/select.feature: -------------------------------------------------------------------------------- 1 | Feature: IMAP select mailbox 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | And the account "[user:user]" has the following custom mailboxes: 5 | | name | type | 6 | | mbox | folder | 7 | | label | label | 8 | Then it succeeds 9 | When bridge starts 10 | And the user logs in with username "[user:user]" and password "password" 11 | And user "[user:user]" finishes syncing 12 | And user "[user:user]" connects and authenticates IMAP client "1" 13 | Then it succeeds 14 | 15 | Scenario: Select inbox 16 | When IMAP client "1" selects "INBOX" 17 | Then it succeeds 18 | 19 | Scenario: Select custom mailbox 20 | When IMAP client "1" selects "Folders/mbox" 21 | Then it succeeds 22 | 23 | Scenario: Select custom label 24 | When IMAP client "1" selects "Labels/label" 25 | Then it succeeds 26 | 27 | Scenario: Select non-existing mailbox 28 | When IMAP client "1" selects "qwerty" 29 | Then it fails 30 | -------------------------------------------------------------------------------- /tests/features/imap/ports.feature: -------------------------------------------------------------------------------- 1 | Feature: A user can connect an IMAP client to custom ports 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | Then it succeeds 5 | When bridge starts 6 | And the user logs in with username "[user:user]" and password "password" 7 | And the user changes the IMAP port to 1144 8 | Then it succeeds 9 | 10 | Scenario: Authenticates successfully on custom port 11 | When user "[user:user]" connects IMAP client "1" on port 1144 12 | Then IMAP client "1" can authenticate -------------------------------------------------------------------------------- /tests/features/observability/gluon_metrics.feature: -------------------------------------------------------------------------------- 1 | Feature: Bridge send remote notification observability metrics 2 | Background: 3 | Given there exists an account with username "[user:user1]" and password "password" 4 | Then it succeeds 5 | When bridge starts 6 | Then it succeeds 7 | 8 | Scenario: Test all possible gluon error observability metrics 9 | When the user logs in with username "[user:user1]" and password "password" 10 | And the user with username "[user:user1]" sends all possible gluon error observability metrics 11 | Then it succeeds 12 | -------------------------------------------------------------------------------- /tests/features/observability/remote_notification.feature: -------------------------------------------------------------------------------- 1 | Feature: Bridge send remote notification observability metrics 2 | Background: 3 | Given there exists an account with username "[user:user1]" and password "password" 4 | And there exists an account with username "[user:user2]" and password "password" 5 | Then it succeeds 6 | When bridge starts 7 | Then it succeeds 8 | 9 | 10 | Scenario: Send notification 'received' and 'processed' observability metric 11 | When the user logs in with username "[user:user1]" and password "password" 12 | And the user with username "[user:user1]" sends the following remote notification observability metric "received" 13 | Then it succeeds 14 | And the user with username "[user:user1]" sends the following remote notification observability metric "processed" 15 | Then it succeeds 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/features/smtp/ports.feature: -------------------------------------------------------------------------------- 1 | Feature: A user can connect an SMTP client to custom ports 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | Then it succeeds 5 | When bridge starts 6 | And the user logs in with username "[user:user]" and password "password" 7 | Then it succeeds 8 | 9 | Scenario: Authenticates successfully on custom port 10 | When the user changes the SMTP port to 1144 11 | When user "[user:user]" connects SMTP client "1" on port 1144 12 | Then SMTP client "1" can authenticate -------------------------------------------------------------------------------- /tests/features/smtp/send/failures_disabled.feature: -------------------------------------------------------------------------------- 1 | Feature: SMTP wrong messages 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | And the account "[user:user]" has additional disabled address "[user:disabled]@[domain]" 5 | And there exists an account with username "[user:to]" and password "password" 6 | Then it succeeds 7 | When bridge starts 8 | And the user logs in with username "[user:user]" and password "password" 9 | And user "[user:user]" connects and authenticates SMTP client "1" 10 | Then it succeeds 11 | 12 | # Need to find way to setup disabled address on black 13 | @skip-black 14 | Scenario: Send from a valid address that cannot send 15 | Given the account "[user:user]" has additional disabled address "[user:disabled]@[domain]" 16 | When SMTP client "1" sends the following message from "[user:disabled]@[domain]" to "[user:to]@[domain]": 17 | """ 18 | From: Bridge Test Disabled <[user:disabled]@[domain]> 19 | To: Internal Bridge <[user:to]@[domain]> 20 | 21 | Hello 22 | """ 23 | And it fails with error "Error: cannot send from address: [user:disabled]@[domain]" 24 | 25 | -------------------------------------------------------------------------------- /tests/features/user/account.feature: -------------------------------------------------------------------------------- 1 | Feature: Account settings 2 | 3 | Background: 4 | Given there exists an account with username "[user:user]" and password "password" 5 | Then it succeeds 6 | When bridge starts 7 | 8 | Scenario: Check account default settings 9 | Then the account "[user:user]" matches the following settings: 10 | | DraftMIMEType | AttachPublicKey | Sign | PGPScheme | 11 | | text/html | false | 0 | 0 | 12 | When the account "[user:user]" has public key attachment "enabled" 13 | And the account "[user:user]" has sign external messages "enabled" 14 | And the account "[user:user]" has default draft format "plain" 15 | And the account "[user:user]" has default PGP schema "inline" 16 | Then the account "[user:user]" matches the following settings: 17 | | DraftMIMEType | AttachPublicKey | Sign | PGPScheme | 18 | | text/plain | true | 1 | 8 | 19 | 20 | -------------------------------------------------------------------------------- /tests/features/user/delete.feature: -------------------------------------------------------------------------------- 1 | Feature: A user can be deleted 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | Then it succeeds 5 | When bridge starts 6 | And the user logs in with username "[user:user]" and password "password" 7 | Then it succeeds 8 | 9 | Scenario: Delete a connected user 10 | When user "[user:user]" is deleted 11 | Then user "[user:user]" is not listed 12 | 13 | Scenario: Delete a disconnected user 14 | Given user "[user:user]" logs out 15 | When user "[user:user]" is deleted 16 | Then user "[user:user]" is not listed -------------------------------------------------------------------------------- /tests/features/user/delete_imap.feature: -------------------------------------------------------------------------------- 1 | Feature: User deletion with IMAP data removal 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | And the account "[user:user]" has the following custom mailboxes: 5 | | name | type | 6 | | one | folder | 7 | And the address "[user:user]@[domain]" of account "[user:user]" has the following messages in "Folders/one": 8 | | from | to | subject | unread | 9 | | a@example.com | a@example.com | one | true | 10 | | b@example.com | b@example.com | two | false | 11 | | c@example.com | c@example.com | three | true | 12 | | c@example.com | c@example.com | four | false | 13 | Then it succeeds 14 | When bridge starts 15 | And the user logs in with username "[user:user]" and password "password" 16 | And user "[user:user]" finishes syncing 17 | Then it succeeds 18 | 19 | Scenario: User is deleted from Bridge and IMAP data is removed 20 | When user "[user:user]" connects and authenticates IMAP client "1" 21 | Then IMAP client "1" sees the following mailbox info for "Folders/one": 22 | | name | total | unread | 23 | | Folders/one | 4 | 2 | 24 | And user "[user:user]" is deleted alongside IMAP data for client "1" 25 | Then it succeeds 26 | -------------------------------------------------------------------------------- /tests/features/user/relogin.feature: -------------------------------------------------------------------------------- 1 | Feature: A logged out user can login again 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | Then it succeeds 5 | When bridge starts 6 | And the user logs in with username "[user:user]" and password "password" 7 | Then it succeeds 8 | 9 | Scenario: Login to disconnected account 10 | When user "[user:user]" logs out 11 | And bridge restarts 12 | And the user logs in with username "[user:user]" and password "password" 13 | Then user "[user:user]" is eventually listed and connected 14 | 15 | Scenario: Cannot login to removed account 16 | When user "[user:user]" is deleted 17 | Then user "[user:user]" is not listed 18 | 19 | Scenario: Bridge password persists after logout/login 20 | Given there exists an account with username "[user:test]" and password "password" 21 | And the user logs in with username "[user:test]" and password "password" 22 | And the bridge password of user "[user:test]" is changed to "YnJpZGdlcGFzc3dvcmQK" 23 | And user "[user:test]" is deleted 24 | And the user logs in with username "[user:test]" and password "password" 25 | Then user "[user:test]" is eventually listed and connected 26 | And the bridge password of user "[user:test]" is equal to "YnJpZGdlcGFzc3dvcmQK" 27 | -------------------------------------------------------------------------------- /tests/features/user/revoke.feature: -------------------------------------------------------------------------------- 1 | Feature: A logged in user is logged out when its auth is revoked. 2 | Background: 3 | Given there exists an account with username "[user:user]" and password "password" 4 | Then it succeeds 5 | When bridge starts 6 | And the user logs in with username "[user:user]" and password "password" 7 | Then it succeeds 8 | 9 | Scenario: The auth is revoked while bridge is running 10 | When the auth of user "[user:user]" is revoked 11 | Then bridge sends a deauth event for user "[user:user]" 12 | And user "[user:user]" is listed but not connected 13 | 14 | Scenario: The auth is revoked while bridge is not running 15 | Given bridge stops 16 | And the auth of user "[user:user]" is revoked 17 | When bridge starts 18 | Then user "[user:user]" is listed but not connected -------------------------------------------------------------------------------- /tests/features/user/telemetry.feature: -------------------------------------------------------------------------------- 1 | Feature: Bridge send usage metrics 2 | Background: 3 | Given there exists an account with username "[user:user1]" and password "password" 4 | And there exists an account with username "[user:user2]" and password "password" 5 | Then it succeeds 6 | When bridge starts 7 | Then it succeeds 8 | 9 | 10 | Scenario: Telemetry availability - No user 11 | Then bridge telemetry feature is enabled 12 | When the user disables telemetry in bridge settings 13 | Then bridge telemetry feature is disabled 14 | When the user enables telemetry in bridge settings 15 | Then bridge telemetry feature is enabled 16 | 17 | 18 | Scenario: Telemetry availability - Multi user 19 | When the user logs in with username "[user:user1]" and password "password" 20 | And user "[user:user1]" finishes syncing 21 | Then bridge telemetry feature is enabled 22 | When the user logs in with username "[user:user2]" and password "password" 23 | And user "[user:user2]" finishes syncing 24 | When user "[user:user2]" has telemetry set to 0 25 | Then bridge telemetry feature is disabled 26 | When user "[user:user2]" has telemetry set to 1 27 | Then bridge telemetry feature is enabled 28 | When the user disables telemetry in bridge settings 29 | Then bridge telemetry feature is disabled 30 | When the user enables telemetry in bridge settings 31 | Then bridge telemetry feature is enabled -------------------------------------------------------------------------------- /tests/frontend_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package tests 19 | 20 | import ( 21 | "context" 22 | "fmt" 23 | 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | ) 26 | 27 | func (s *scenario) frontendSeesThatBridgeIsVersion(version string) error { 28 | res, err := s.t.client.Version(context.Background(), &emptypb.Empty{}) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | if version != res.GetValue() { 34 | return fmt.Errorf("expected version %s, got %s", version, res.GetValue()) 35 | } 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /tests/testdata/html/foreign_ascii_subject_body.template.eml: -------------------------------------------------------------------------------- 1 | From: Bridge Test <[user:user]@[domain]> 2 | To: External Bridge 3 | Subject: =?UTF-8?B?U3Vias61zq3Pgs+EIMK2IMOEIMOI?= 4 | Content-Type: text/html; charset=UTF-8 5 | Content-Transfer-Encoding: 8bit 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Subjεέςτ ¶ Ä È 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/testdata/multipart/mixed_with_attachment_encoded.eml: -------------------------------------------------------------------------------- 1 | From: Bridge Test 2 | Date: 01 Jan 1980 00:00:00 +0000 3 | To: Internal Bridge 4 | Subject: Message with attachment name 5 | Content-type: multipart/mixed; boundary="boundary" 6 | Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000 7 | 8 | This is a multi-part message in MIME format. 9 | 10 | --boundary 11 | Content-Type: text/plain 12 | 13 | Hello 14 | 15 | --boundary 16 | Content-Type: text/html; charset=utf-8 17 | Content-Transfer-Encoding: 7bit 18 | 19 |

HELLO

20 | 21 | --boundary 22 | Content-Type: application/pdf; name="=?US-ASCII?Q?filename?=" 23 | Content-Disposition: attachment; filename="=?US-ASCII?Q?filename?=" 24 | 25 | somebytes 26 | 27 | --boundary-- 28 | -------------------------------------------------------------------------------- /tests/testdata/multipart/mixed_with_attachment_encoded_no_quote.eml: -------------------------------------------------------------------------------- 1 | From: Bridge Test 2 | Date: 01 Jan 1980 00:00:00 +0000 3 | To: Internal Bridge 4 | Subject: Message with attachment name 5 | Content-type: multipart/mixed; boundary="boundary" 6 | Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000 7 | 8 | This is a multi-part message in MIME format. 9 | 10 | --boundary 11 | Content-Type: text/plain 12 | 13 | Hello 14 | 15 | --boundary 16 | Content-Type: text/html; charset=utf-8 17 | Content-Transfer-Encoding: 7bit 18 | 19 |

HELLO

20 | 21 | --boundary 22 | Content-Type: application/pdf; name==?US-ASCII?Q?filename?= 23 | Content-Disposition: attachment; filename==?US-ASCII?Q?filename?= 24 | 25 | somebytes 26 | 27 | --boundary-- 28 | -------------------------------------------------------------------------------- /tests/testdata/multipart/mixed_with_attachment_no_quote.eml: -------------------------------------------------------------------------------- 1 | From: Bridge Test 2 | Date: 01 Jan 1980 00:00:00 +0000 3 | To: Internal Bridge 4 | Subject: Message with attachment name 5 | Content-type: multipart/mixed; boundary="boundary" 6 | Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000 7 | 8 | This is a multi-part message in MIME format. 9 | 10 | --boundary 11 | Content-Type: text/plain 12 | 13 | Hello 14 | 15 | --boundary 16 | Content-Type: text/html; charset=utf-8 17 | Content-Transfer-Encoding: 7bit 18 | 19 |

HELLO

20 | 21 | --boundary 22 | Content-Type: application/pdf; name=filename 23 | Content-Disposition: attachment; filename=filename 24 | 25 | somebytes 26 | 27 | --boundary-- 28 | -------------------------------------------------------------------------------- /tests/testdata/plain/text_plain_latin1.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/tests/testdata/plain/text_plain_latin1.eml -------------------------------------------------------------------------------- /tests/testdata/plain/text_plain_unknown_latin1.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/tests/testdata/plain/text_plain_unknown_latin1.eml -------------------------------------------------------------------------------- /tests/testdata/plain/text_plain_wrong_latin1.eml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/tests/testdata/plain/text_plain_wrong_latin1.eml -------------------------------------------------------------------------------- /utils/QTBUG-88600/README.txt: -------------------------------------------------------------------------------- 1 | This is version of libqcocoa obtained from original Qt 5.13.0 source available at https://github.com/qt/qtbase with patch from cocoa.patch applied. 2 | 3 | Please refer to https://bugreports.qt.io/browse/QTBUG-88600 for patch origin and https://doc.qt.io/qt-5/macos-building.html for instructions how to build Qt from source. 4 | -------------------------------------------------------------------------------- /utils/QTBUG-88600/cocoa.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm 2 | index 4982f5ee05..3ae1e4fca3 100644 3 | --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm 4 | +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm 5 | @@ -224,6 +224,7 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) 6 | r.moveCenter(fullHeightPixmap.rect().center()); 7 | p.drawPixmap(r, pixmap); 8 | } 9 | + fullHeightPixmap.setDevicePixelRatio(devicePixelRatio); 10 | 11 | NSImage *nsimage = static_cast(qt_mac_create_nsimage(fullHeightPixmap)); 12 | [nsimage setTemplate:icon.isMask()]; 13 | -------------------------------------------------------------------------------- /utils/QTBUG-88600/libqcocoa.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProtonMail/proton-bridge/94125056abbcbbb91df6b0f95fb6fe605d583754/utils/QTBUG-88600/libqcocoa.dylib -------------------------------------------------------------------------------- /utils/bridge-rollout/README.md: -------------------------------------------------------------------------------- 1 | @@ -0,0 +1,10 @@ 2 | # Vault Editor 3 | 4 | Bridge uses an encrypted vault to store persistent data. One of the parameters stored in this vault is the roll factor (between 0.0 and 1.0) 5 | 6 | It can be built with `make vault-editor` in the bridge source code root directory. 7 | 8 | Example usage: 9 | 10 | Setting the rollout value: 11 | ```bash 12 | $ ./bridge-rollout set -v=0.81 13 | 0.81 14 | ``` 15 | Note that the provided value will be clamped between 0 and 1. 16 | 17 | ```bash 18 | $ ./bridge-rollout get 19 | 0.81 20 | ``` 21 | -------------------------------------------------------------------------------- /utils/bridge_app_version.ps1: -------------------------------------------------------------------------------- 1 | Select-String -Path (Join-Path $PSScriptRoot "../Makefile") -Pattern "^BRIDGE_APP_VERSION\?=(\S*)" | 2 | ForEach-Object {$_.Matches} | ForEach-Object { $_.Groups[1].Value } -------------------------------------------------------------------------------- /utils/bridge_app_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (c) 2025 Proton AG 4 | # 5 | # This file is part of Proton Mail Bridge. 6 | # 7 | # Proton Mail Bridge is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Proton Mail Bridge is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with Proton Mail Bridge. If not, see . 19 | 20 | sed -n "s/BRIDGE_APP_VERSION?=\(\S*\)/\1/p" "$(dirname $0)/../Makefile" 21 | -------------------------------------------------------------------------------- /utils/debug/debug_assemble.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge.Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package main 19 | 20 | import ( 21 | "fmt" 22 | "os" 23 | 24 | "github.com/ProtonMail/proton-bridge/v3/internal/user" 25 | ) 26 | 27 | func main() { 28 | if len(os.Args) < 2 { 29 | fmt.Printf("Usage: %v \n", os.Args[0]) 30 | return 31 | } 32 | 33 | if err := user.TryBuildDebugMessage(os.Args[1]); err != nil { 34 | fmt.Printf("%v\n", err.Error()) 35 | return 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /utils/dxtn/dxtn.layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /utils/dxtn/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge.Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | #include "main.h" 19 | 20 | extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 21 | { 22 | return FALSE; 23 | } 24 | -------------------------------------------------------------------------------- /utils/dxtn/main.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge.Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | #ifndef __MAIN_H__ 19 | #define __MAIN_H__ 20 | 21 | #include 22 | 23 | #ifdef BUILD_DLL 24 | #define DLL_EXPORT __declspec(dllexport) 25 | #else 26 | #define DLL_EXPORT __declspec(dllimport) 27 | #endif 28 | 29 | #endif // __MAIN_H__ 30 | -------------------------------------------------------------------------------- /utils/get_revision.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright (c) 2025 Proton AG 4 | # 5 | # This file is part of Proton Mail Bridge. 6 | # 7 | # Proton Mail Bridge is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # Proton Mail Bridge is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with Proton Mail Bridge. If not, see . 19 | 20 | 21 | if ! which git &> /dev/null; then 22 | echo "NOGIT" 23 | exit 24 | fi 25 | 26 | if ! git status &> /dev/null; then 27 | echo "NOREPO" 28 | exit 29 | fi 30 | 31 | if [ "$1" == "tag" ]; then 32 | if ! git describe --tags; then 33 | echo "NOTAG" 34 | fi 35 | exit 36 | fi 37 | 38 | if ! git rev-parse --short=10 HEAD; then 39 | echo "NOREV" 40 | exit 41 | fi 42 | 43 | -------------------------------------------------------------------------------- /utils/keyring.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | package utils 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/ProtonMail/gopenpgp/v2/crypto" 24 | "github.com/stretchr/testify/require" 25 | ) 26 | 27 | func MakeKeyRing(t *testing.T) *crypto.KeyRing { 28 | key, err := crypto.GenerateKey("name", "email", "rsa", 2048) 29 | require.NoError(t, err) 30 | 31 | kr, err := crypto.NewKeyRing(key) 32 | require.NoError(t, err) 33 | 34 | return kr 35 | } 36 | -------------------------------------------------------------------------------- /utils/license_header.txt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Proton AG 2 | // 3 | // This file is part of Proton Mail Bridge. 4 | // 5 | // Proton Mail Bridge is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Proton Mail Bridge is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Proton Mail Bridge. If not, see . 17 | 18 | --------------------------------------------------------------------------------