diff --git a/internal/app/base/base.go b/internal/app/base/base.go
index ac4adc93b..6f90834c9 100644
--- a/internal/app/base/base.go
+++ b/internal/app/base/base.go
@@ -159,12 +159,8 @@ func New( //nolint:funlen
return nil, api.CheckOtherInstanceAndFocus(settingsObj.GetInt(settings.APIPortKey))
}
- if err := migrateMacKeychainBefore220(settingsObj, keychainName); err != nil {
- logrus.WithError(err).Warn("Keychain migration failed")
- }
-
- if err := migrateStartup220(settingsObj); err != nil {
- logrus.WithError(err).Warn("Failed to remove old startup paths")
+ if err := migrateRebranding(settingsObj, keychainName); err != nil {
+ logrus.WithError(err).Warn("Rebranding migration failed")
}
cachePath, err := locations.ProvideCachePath()
@@ -244,7 +240,7 @@ func New( //nolint:funlen
}
autostart := &autostart.App{
- Name: appName,
+ Name: startupNameForRebranding(appName),
DisplayName: appName,
Exec: []string{exe, "--" + FlagNoWindow},
}
diff --git a/internal/app/base/migration.go b/internal/app/base/migration.go
index 8ca413ae3..603a34be9 100644
--- a/internal/app/base/migration.go
+++ b/internal/app/base/migration.go
@@ -18,16 +18,11 @@
package base
import (
- "errors"
"os"
"path/filepath"
- "runtime"
- "github.com/Masterminds/semver/v3"
- "github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/constants"
"github.com/ProtonMail/proton-bridge/internal/locations"
- "github.com/ProtonMail/proton-bridge/pkg/keychain"
"github.com/sirupsen/logrus"
)
@@ -112,122 +107,6 @@ func migrateUpdatesFrom16x(configName string, locations *locations.Locations) er
return moveIfExists(oldUpdatesPath, newUpdatesPath)
}
-// migrateMacKeychainBefore220 deals with write access restriction to mac
-// keychain passwords which are caused by application renaming. The old
-// passwords are copied under new name in order to have write access afer
-// renaming.
-func migrateMacKeychainBefore220(settingsObj *settings.Settings, keychainName string) error {
- l := logrus.WithField("pkg", "app/base/migration")
- if runtime.GOOS != "darwin" {
- return nil
- }
-
- if shouldContinue, err := isBefore220(settingsObj); !shouldContinue || err != nil {
- return err
- }
-
- l.Warn("Migrating mac keychain")
-
- helperConstructor, ok := keychain.Helpers["macos-keychain"]
- if !ok {
- return errors.New("cannot find macos-keychain helper")
- }
-
- oldKC, err := helperConstructor("ProtonMailBridgeService")
- if err != nil {
- l.WithError(err).Error("Keychain constructor failed")
- return err
- }
-
- idByURL, err := oldKC.List()
- if err != nil {
- l.WithError(err).Error("List old keychain failed")
- return err
- }
-
- newKC, err := keychain.NewKeychain(settingsObj, keychainName)
- if err != nil {
- return err
- }
-
- for url, id := range idByURL {
- li := l.WithField("id", id).WithField("url", url)
- userID, secret, err := oldKC.Get(url)
- if err != nil {
- li.WithField("userID", userID).
- WithField("err", err).
- Error("Faild to get old item")
- continue
- }
-
- if _, _, err := newKC.Get(userID); err == nil {
- li.Warn("Skipping migration, item already exists.")
- continue
- }
-
- if err := newKC.Put(userID, secret); err != nil {
- li.WithError(err).Error("Failed to migrate user")
- }
-
- li.Info("Item migrated")
- }
-
- return nil
-}
-
-// migrateStartup220 removes old startup links. The creation of new links is
-// handled by bridge initialisation.
-func migrateStartup220(settingsObj *settings.Settings) error {
- if shouldContinue, err := isBefore220(settingsObj); !shouldContinue || err != nil {
- return err
- }
-
- logrus.WithField("pkg", "app/base/migration").Warn("Migrating autostartup links")
-
- path, err := os.UserHomeDir()
- if err != nil {
- return err
- }
-
- switch runtime.GOOS {
- case "windows":
- path = filepath.Join(path, `AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\ProtonMail Bridge.lnk`)
- case "linux":
- path = filepath.Join(path, `.config/autostart/ProtonMail Bridge.desktop`)
- case "darwin":
- path = filepath.Join(path, `Library/LaunchAgents/ProtonMail Bridge.plist`)
- default:
- return errors.New("unknown GOOS")
- }
-
- return os.Remove(path)
-}
-
-// isBefore220 decide if last used version was older than 2.2.0. If cannot decide it returns false with error.
-func isBefore220(settingsObj *settings.Settings) (bool, error) {
- lastUsedVersion := settingsObj.Get(settings.LastVersionKey)
-
- // Skipping migration: it is first bridge start or cache was cleared.
- if lastUsedVersion == "" {
- return false, nil
- }
-
- v220 := semver.MustParse("2.2.0")
- lastVer, err := semver.NewVersion(lastUsedVersion)
-
- // Skipping migration: Should not happen but cannot decide what to do.
- if err != nil {
- return false, err
- }
-
- // Skipping migration: 2.2.0>= was already used hence old stuff was already migrated.
- if !lastVer.LessThan(v220) {
- return false, nil
- }
-
- return true, nil
-}
-
func moveIfExists(source, destination string) error {
l := logrus.WithField("source", source).WithField("destination", destination)
diff --git a/internal/app/base/migration_rebranding.go b/internal/app/base/migration_rebranding.go
new file mode 100644
index 000000000..8bc13ff89
--- /dev/null
+++ b/internal/app/base/migration_rebranding.go
@@ -0,0 +1,197 @@
+// Copyright (c) 2022 Proton AG
+//
+// This file is part of Proton Mail Bridge.
+//
+// Proton Mail Bridge is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Proton Mail Bridge is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Proton Mail Bridge. If not, see .
+
+package base
+
+import (
+ "errors"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+
+ "github.com/ProtonMail/proton-bridge/internal/config/settings"
+ "github.com/ProtonMail/proton-bridge/pkg/keychain"
+ "github.com/hashicorp/go-multierror"
+ "github.com/sirupsen/logrus"
+)
+
+const darwin = "darwin"
+
+func migrateRebranding(settingsObj *settings.Settings, keychainName string) (result error) {
+ if err := migrateStartupBeforeRebranding(); err != nil {
+ result = multierror.Append(result, err)
+ }
+
+ lastUsedVersion := settingsObj.Get(settings.LastVersionKey)
+
+ // Skipping migration: it is first bridge start or cache was cleared.
+ if lastUsedVersion == "" {
+ settingsObj.SetBool(settings.RebrandingMigrationKey, true)
+ return
+ }
+
+ // Skipping rest of migration: already done
+ if settingsObj.GetBool(settings.RebrandingMigrationKey) {
+ return
+ }
+
+ switch runtime.GOOS {
+ case "windows", "linux":
+ // GODT-1260 we would need admin rights to changes desktop files
+ // and start menu items.
+ settingsObj.SetBool(settings.RebrandingMigrationKey, true)
+ case darwin:
+ if shouldContinue, err := isMacBeforeRebranding(); !shouldContinue || err != nil {
+ if err != nil {
+ result = multierror.Append(result, err)
+ }
+ break
+ }
+
+ if err := migrateMacKeychainBeforeRebranding(settingsObj, keychainName); err != nil {
+ result = multierror.Append(result, err)
+ }
+
+ settingsObj.SetBool(settings.RebrandingMigrationKey, true)
+ }
+
+ return result
+}
+
+// migrateMacKeychainBeforeRebranding deals with write access restriction to
+// mac keychain passwords which are caused by application renaming. The old
+// passwords are copied under new name in order to have write access afer
+// renaming.
+func migrateMacKeychainBeforeRebranding(settingsObj *settings.Settings, keychainName string) error {
+ l := logrus.WithField("pkg", "app/base/migration")
+ l.Warn("Migrating mac keychain")
+
+ helperConstructor, ok := keychain.Helpers["macos-keychain"]
+ if !ok {
+ return errors.New("cannot find macos-keychain helper")
+ }
+
+ oldKC, err := helperConstructor("ProtonMailBridgeService")
+ if err != nil {
+ l.WithError(err).Error("Keychain constructor failed")
+ return err
+ }
+
+ idByURL, err := oldKC.List()
+ if err != nil {
+ l.WithError(err).Error("List old keychain failed")
+ return err
+ }
+
+ newKC, err := keychain.NewKeychain(settingsObj, keychainName)
+ if err != nil {
+ return err
+ }
+
+ for url, id := range idByURL {
+ li := l.WithField("id", id).WithField("url", url)
+ userID, secret, err := oldKC.Get(url)
+ if err != nil {
+ li.WithField("userID", userID).
+ WithField("err", err).
+ Error("Faild to get old item")
+ continue
+ }
+
+ if _, _, err := newKC.Get(userID); err == nil {
+ li.Warn("Skipping migration, item already exists.")
+ continue
+ }
+
+ if err := newKC.Put(userID, secret); err != nil {
+ li.WithError(err).Error("Failed to migrate user")
+ }
+
+ li.Info("Item migrated")
+ }
+
+ return nil
+}
+
+// migrateStartupBeforeRebranding removes old startup links. The creation of new links is
+// handled by bridge initialisation.
+func migrateStartupBeforeRebranding() error {
+ path, err := os.UserHomeDir()
+ if err != nil {
+ return err
+ }
+
+ switch runtime.GOOS {
+ case "windows":
+ path = filepath.Join(path, `AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\ProtonMail Bridge.lnk`)
+ case "linux":
+ path = filepath.Join(path, `.config/autostart/ProtonMail Bridge.desktop`)
+ case darwin:
+ path = filepath.Join(path, `Library/LaunchAgents/ProtonMail Bridge.plist`)
+ default:
+ return errors.New("unknown GOOS")
+ }
+
+ if _, err := os.Stat(path); os.IsNotExist(err) {
+ return nil
+ }
+
+ logrus.WithField("pkg", "app/base/migration").Warn("Migrating autostartup links")
+ return os.Remove(path)
+}
+
+// startupNameForRebranding returns the name for autostart launcher based on
+// type of rebranded instance i.e. update or manual.
+//
+// This only affects darwin when udpate re-writes the old startup and then
+// manual installed it would not run proper exe. Therefore we return "old" name
+// for updates and "new" name for manual which would be properly migrated.
+//
+// For orther (linux and windows) the link is always pointing to launcher which
+// path didn't changed.
+func startupNameForRebranding(origin string) string {
+ if runtime.GOOS == darwin {
+ if path, err := os.Executable(); err == nil && strings.Contains(path, "ProtonMail Bridge") {
+ return "ProtonMail Bridge"
+ }
+ }
+
+ // No need to solve for other OS. See comment above.
+ return origin
+}
+
+// isBeforeRebranding decide if last used version was older than 2.2.0. If
+// cannot decide it returns false with error.
+func isMacBeforeRebranding() (bool, error) {
+ // previous version | update | do mac migration |
+ // | first | false |
+ // cleared-cache | manual | false |
+ // cleared-cache | in-app | false |
+ // old | in-app | false |
+ // old in-app | in-app | false |
+ // old | manual | true |
+ // old in-app | manual | true |
+ // manual | in-app | false |
+
+ // Skip if it was in-app update and not manual
+ if path, err := os.Executable(); err != nil || strings.Contains(path, "ProtonMail Bridge") {
+ return false, err
+ }
+
+ return true, nil
+}
diff --git a/internal/bridge/autostart.go b/internal/bridge/autostart.go
index f8112b94a..78224223c 100644
--- a/internal/bridge/autostart.go
+++ b/internal/bridge/autostart.go
@@ -18,14 +18,21 @@
// Package bridge provides core functionality of Bridge app.
package bridge
+import "github.com/ProtonMail/proton-bridge/internal/config/settings"
+
+// IsAutostartEnabled checks if link file exits.
func (b *Bridge) IsAutostartEnabled() bool {
return b.autostart.IsEnabled()
}
+// EnableAutostart creates link and sets the preferences.
func (b *Bridge) EnableAutostart() error {
+ b.settings.SetBool(settings.AutostartKey, true)
return b.autostart.Enable()
}
+// DisableAutostart removes link and sets the preferences.
func (b *Bridge) DisableAutostart() error {
+ b.settings.SetBool(settings.AutostartKey, false)
return b.autostart.Disable()
}
diff --git a/internal/config/settings/settings.go b/internal/config/settings/settings.go
index 9133424d5..00e1aabb4 100644
--- a/internal/config/settings/settings.go
+++ b/internal/config/settings/settings.go
@@ -54,6 +54,7 @@ const (
FetchWorkers = "fetch_workers"
AttachmentWorkers = "attachment_workers"
ColorScheme = "color_scheme"
+ RebrandingMigrationKey = "rebranding_migrated"
)
type Settings struct {
diff --git a/internal/frontend/qml/MainWindow.qml b/internal/frontend/qml/MainWindow.qml
index 8dfa488f1..761a42f56 100644
--- a/internal/frontend/qml/MainWindow.qml
+++ b/internal/frontend/qml/MainWindow.qml
@@ -97,10 +97,6 @@ ApplicationWindow {
property bool _showSetup: false
currentIndex: {
- if (backend.showSplashScreen) {
- return 3
- }
-
// show welcome when there are no users or only one non-logged-in user is present
if (backend.users.count === 0) {
return 1
@@ -167,14 +163,6 @@ ApplicationWindow {
}
}
- SplashScreen { // 3
- id: splashScreen
- colorScheme: root.colorScheme
- backend: root.backend
-
- Layout.fillHeight: true
- Layout.fillWidth: true
- }
}
NotificationPopups {
@@ -184,6 +172,12 @@ ApplicationWindow {
backend: root.backend
}
+ SplashScreen {
+ id: splashScreen
+ colorScheme: root.colorScheme
+ backend: root.backend
+ }
+
function showLocalCacheSettings() { contentWrapper.showLocalCacheSettings() }
function showSettings() { contentWrapper.showSettings() }
function showHelp() { contentWrapper.showHelp() }
diff --git a/internal/frontend/qml/SplashScreen.qml b/internal/frontend/qml/SplashScreen.qml
index 234885885..22f938226 100644
--- a/internal/frontend/qml/SplashScreen.qml
+++ b/internal/frontend/qml/SplashScreen.qml
@@ -23,126 +23,86 @@ import QtQuick.Controls.impl 2.12
import Proton 4.0
-Rectangle {
+Dialog {
id: root
- property ColorScheme colorScheme
property var backend
- color: root.colorScheme.background_norm
+ shouldShow: root.backend.showSplashScreen
+ modal: true
+
+ topPadding : 0
+ leftPadding : 0
+ rightPadding : 0
ColumnLayout {
- anchors.centerIn: root
+ spacing: 20
- //width: 320
+ Image {
+ Layout.alignment: Qt.AlignHCenter
- spacing: 20
+ sourceSize.width: 400
+ sourceSize.height: 225
- Label {
- Layout.bottomMargin: 12;
- Layout.alignment: Qt.AlignHCenter;
+ Layout.preferredWidth: 400
+ Layout.preferredHeight: 225
- colorScheme: root.colorScheme;
- text: "What's new in Bridge"
- type: Label.Heading
- horizontalAlignment: Text.AlignCenter
+ source: "./icons/img-splash.png"
}
- Repeater {
- model: ListModel {
- ListElement { icon: "ic-illustrative-view-html-code" ; title: qsTr("New interface") ; description: qsTr("Entirely redesigned GUI with more intuitive setup.")}
- ListElement { icon: "ic-card-identity" ; title: qsTr("Status view") ; description: qsTr("Important notifications and available storage at a glance.")}
- ListElement { icon: "ic-drive" ; title: qsTr("Local cache") ; description: qsTr("New and improved cache for major stability and performance enhancements.")}
- }
-
- Item {
- implicitWidth: children[0].implicitWidth
- implicitHeight: children[0].implicitHeight
-
- RowLayout {
- id: row
- spacing: 25
-
- Item {
- Layout.topMargin: itemTitle.height/2
- Layout.alignment: Qt.AlignTop
- Layout.preferredWidth: 24
- Layout.preferredHeight: 24
-
- ColorImage {
- anchors.top: parent.top
- anchors.left: parent.left
-
- color: root.colorScheme.interaction_norm
- source: "./icons/"+model.icon+".svg"
- width: parent.width
- sourceSize.width: parent.width
- }
- }
-
- ColumnLayout {
- spacing: 0
-
- Label {
- id: itemTitle
- colorScheme: root.colorScheme
- text: model.title
- type: Label.Body_bold
- }
-
- Label {
- Layout.preferredWidth: 320
- colorScheme: root.colorScheme
- text: model.description
- wrapMode: Text.WordWrap
- }
- }
- }
- }
+ Label {
+ colorScheme: root.colorScheme;
+
+ Layout.alignment: Qt.AlignHCenter;
+ Layout.leftMargin: 24
+ Layout.rightMargin: 24
+ Layout.preferredWidth: 336
+ type: Label.Title
+ horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Updated Proton, unified protection")
}
- Item {
+ Label {
+ colorScheme: root.colorScheme
+
+ Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter;
- implicitWidth: children[0].width
- implicitHeight: children[0].height
-
- RowLayout {
- spacing: 10
-
- Label {
- colorScheme: root.colorScheme;
- text: qsTr("Full release notes")
- Layout.alignment: Qt.AlignHCenter
- type: Label.LabelType.Body
- onLinkActivated: Qt.openUrlExternally(link)
- color: root.colorScheme.interaction_norm
- }
-
- ColorImage {
- color: root.colorScheme.interaction_norm
- source: "./icons/ic-external-link.svg"
- width: 16
- sourceSize.width: 16
- }
-
- }
-
- MouseArea {
- anchors.fill: parent
- cursorShape: Qt.PointingHandCursor
- onClicked: Qt.openUrlExternally(root.backend.releaseNotesLink)
- }
+ Layout.preferredWidth: 336
+ Layout.leftMargin: 24
+ Layout.rightMargin: 24
+ wrapMode: Text.WordWrap
+
+ type: Label.Body
+ horizontalAlignment: Text.AlignHCenter
+ textFormat: Text.StyledText
+ text: qsTr("Introducing Proton’s refreshed look.
") +
+ qsTr("Many services, one mission. Welcome to an Internet where privacy is the default. ") +
+ link("https://proton.me/news/updated-proton",qsTr("Learn More"))
+
+ onLinkActivated: Qt.openUrlExternally(link)
}
-
Button {
- Layout.topMargin: 12
Layout.fillWidth: true
+ Layout.leftMargin: 24
+ Layout.rightMargin: 24
colorScheme: root.colorScheme
- text: "Start using Bridge"
+ text: "Got it"
onClicked: root.backend.showSplashScreen = false
}
+
+ Image {
+ Layout.alignment: Qt.AlignHCenter
+
+ sourceSize.width: 164
+ sourceSize.height: 32
+
+ Layout.preferredWidth: 164
+ Layout.preferredHeight: 32
+
+ source: "./icons/img-proton-logos.svg"
+ }
}
}
diff --git a/internal/frontend/qml/icons/img-proton-logos.png b/internal/frontend/qml/icons/img-proton-logos.png
new file mode 100644
index 000000000..cf54402e1
Binary files /dev/null and b/internal/frontend/qml/icons/img-proton-logos.png differ
diff --git a/internal/frontend/qml/icons/img-proton-logos.svg b/internal/frontend/qml/icons/img-proton-logos.svg
new file mode 100644
index 000000000..3ef4d8b27
--- /dev/null
+++ b/internal/frontend/qml/icons/img-proton-logos.svg
@@ -0,0 +1,63 @@
+
diff --git a/internal/frontend/qml/icons/img-splash.png b/internal/frontend/qml/icons/img-splash.png
new file mode 100644
index 000000000..9bc375ada
Binary files /dev/null and b/internal/frontend/qml/icons/img-splash.png differ
diff --git a/internal/frontend/qml/icons/img-splash.svg b/internal/frontend/qml/icons/img-splash.svg
new file mode 100644
index 000000000..238beccc3
--- /dev/null
+++ b/internal/frontend/qml/icons/img-splash.svg
@@ -0,0 +1,2884 @@
+
+
+
+
diff --git a/internal/frontend/qt/frontend_init.go b/internal/frontend/qt/frontend_init.go
index 78bcbfa73..49901c2e6 100644
--- a/internal/frontend/qt/frontend_init.go
+++ b/internal/frontend/qt/frontend_init.go
@@ -112,10 +112,9 @@ func (f *FrontendQt) setShowSplashScreen() {
return
}
- // Current splash screen contains update on "What's new" in facelift.
- // Therefore, it should be shown only if the last used version was less
- // than 1.9.0.
- if ver.LessThan(semver.MustParse("1.9.0")) {
+ // Current splash screen contains update on rebranding. Therefore, it
+ // should be shown only if the last used version was less than 2.2.0.
+ if ver.LessThan(semver.MustParse("2.2.0")) {
f.qml.SetShowSplashScreen(true)
}
}
diff --git a/internal/frontend/qt/frontend_settings.go b/internal/frontend/qt/frontend_settings.go
index 38933416b..0669ee8fe 100644
--- a/internal/frontend/qt/frontend_settings.go
+++ b/internal/frontend/qt/frontend_settings.go
@@ -77,13 +77,15 @@ func (f *FrontendQt) changeLocalCache(enableDiskCache bool, diskCachePath *core.
func (f *FrontendQt) setIsAutostartOn() {
// GODT-1507 Windows: autostart needs to be created after Qt is initialized.
+ // GODT-1206: if preferences file says it should be on enable it here.
f.firstTimeAutostart.Do(func() {
- if !f.bridge.IsFirstStart() {
+ shouldAutostartBeOn := f.settings.GetBool(settings.AutostartKey)
+ if f.bridge.IsFirstStart() || shouldAutostartBeOn {
+ if err := f.bridge.EnableAutostart(); err != nil {
+ f.log.WithField("prefs", shouldAutostartBeOn).WithError(err).Error("Failed to enable first autostart")
+ }
return
}
- if err := f.bridge.EnableAutostart(); err != nil {
- f.log.WithError(err).Error("Failed to enable autostart")
- }
})
f.qml.SetIsAutostartOn(f.bridge.IsAutostartEnabled())
}
diff --git a/pkg/keychain/keychain_darwin.go b/pkg/keychain/keychain_darwin.go
index 2a37872d3..b9a49328b 100644
--- a/pkg/keychain/keychain_darwin.go
+++ b/pkg/keychain/keychain_darwin.go
@@ -15,16 +15,22 @@
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
+//go:build darwin
// +build darwin
package keychain
import (
"fmt"
+ "os"
"strings"
)
// hostURL uniquely identifies the app's keychain items within the system keychain.
func hostURL(keychainName string) string {
+ // Skip when it was in-app update and not manual
+ if path, err := os.Executable(); err == nil && strings.Contains(path, "ProtonMail Bridge") {
+ return fmt.Sprintf("ProtonMail%vService", strings.Title(keychainName))
+ }
return fmt.Sprintf("Proton Mail %v", strings.Title(keychainName))
}
diff --git a/utils/dependency_license.sh b/utils/dependency_license.sh
index 1f2180352..9cc3b26fd 100755
--- a/utils/dependency_license.sh
+++ b/utils/dependency_license.sh
@@ -1,21 +1,21 @@
#!/bin/bash
-# Copyright (c) 2022 Proton Technologies AG
+# Copyright (c) 2022 Proton AG
#
-# This file is part of ProtonMail Bridge.
+# This file is part of Proton Mail Bridge.
#
-# ProtonMail Bridge is free software: you can redistribute it and/or modify
+# Proton Mail Bridge is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
-# ProtonMail Bridge is distributed in the hope that it will be useful,
+# Proton Mail Bridge is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with ProtonMail Bridge. If not, see .
+# along with Proton Mail Bridge. If not, see .
set -eo pipefail