chore: (BRIDGE-253) removing unused telemetry (activation and troubleshooting)

This commit is contained in:
Atanas Janeshliev
2024-10-30 11:45:50 +01:00
parent 531368da86
commit 6647231278
44 changed files with 107 additions and 2194 deletions

View File

@ -25,7 +25,6 @@ import (
"github.com/ProtonMail/go-proton-api" "github.com/ProtonMail/go-proton-api"
"github.com/ProtonMail/proton-bridge/v3/internal/constants" "github.com/ProtonMail/proton-bridge/v3/internal/constants"
"github.com/ProtonMail/proton-bridge/v3/internal/logging" "github.com/ProtonMail/proton-bridge/v3/internal/logging"
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
"github.com/ProtonMail/proton-bridge/v3/internal/vault" "github.com/ProtonMail/proton-bridge/v3/internal/vault"
) )
@ -80,12 +79,6 @@ func (bridge *Bridge) ReportBug(ctx context.Context, report *ReportBugReq) error
return err return err
} }
safe.RLock(func() {
for _, user := range bridge.users {
user.ReportBugSent()
}
}, bridge.usersLock)
// if we have a token we can append more attachment to the bugReport // if we have a token we can append more attachment to the bugReport
for i, att := range attachments { for i, att := range attachments {
if i == 0 && report.IncludeLogs { if i == 0 && report.IncludeLogs {

View File

@ -1,46 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package bridge
import (
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
)
func (bridge *Bridge) ReportBugClicked() {
safe.RLock(func() {
for _, user := range bridge.users {
user.ReportBugClicked()
}
}, bridge.usersLock)
}
func (bridge *Bridge) AutoconfigUsed(client string) {
safe.RLock(func() {
for _, user := range bridge.users {
user.AutoconfigUsed(client)
}
}, bridge.usersLock)
}
func (bridge *Bridge) ExternalLinkClicked(article string) {
safe.RLock(func() {
for _, user := range bridge.users {
user.ExternalLinkClicked(article)
}
}, bridge.usersLock)
}

View File

@ -318,11 +318,10 @@ func (bridge *Bridge) GetKnowledgeBaseSuggestions(userInput string) (kb.ArticleL
// Note: it does not clear the keychain. The only entry in the keychain is the vault password, // Note: it does not clear the keychain. The only entry in the keychain is the vault password,
// which we need at next startup to decrypt the vault. // which we need at next startup to decrypt the vault.
func (bridge *Bridge) FactoryReset(ctx context.Context) { func (bridge *Bridge) FactoryReset(ctx context.Context) {
useTelemetry := !bridge.GetTelemetryDisabled()
// Delete all the users. // Delete all the users.
safe.Lock(func() { safe.Lock(func() {
for _, user := range bridge.users { for _, user := range bridge.users {
bridge.logoutUser(ctx, user, true, true, useTelemetry) bridge.logoutUser(ctx, user, true, true)
} }
}, bridge.usersLock) }, bridge.usersLock)

View File

@ -28,7 +28,6 @@ type Locator interface {
ProvideLogsPath() (string, error) ProvideLogsPath() (string, error)
ProvideGluonCachePath() (string, error) ProvideGluonCachePath() (string, error)
ProvideGluonDataPath() (string, error) ProvideGluonDataPath() (string, error)
ProvideStatsPath() (string, error)
GetLicenseFilePath() string GetLicenseFilePath() string
GetDependencyLicensesLink() string GetDependencyLicensesLink() string
Clear(...string) error Clear(...string) error

View File

@ -255,7 +255,7 @@ func (bridge *Bridge) LogoutUser(ctx context.Context, userID string) error {
return ErrNoSuchUser return ErrNoSuchUser
} }
bridge.logoutUser(ctx, user, true, false, false) bridge.logoutUser(ctx, user, true, false)
bridge.publish(events.UserLoggedOut{ bridge.publish(events.UserLoggedOut{
UserID: userID, UserID: userID,
@ -280,7 +280,7 @@ func (bridge *Bridge) DeleteUser(ctx context.Context, userID string) error {
} }
if user, ok := bridge.users[userID]; ok { if user, ok := bridge.users[userID]; ok {
bridge.logoutUser(ctx, user, true, true, !bridge.GetTelemetryDisabled()) bridge.logoutUser(ctx, user, true, true)
} }
if err := imapservice.DeleteSyncState(syncConfigDir, userID); err != nil { if err := imapservice.DeleteSyncState(syncConfigDir, userID); err != nil {
@ -358,7 +358,7 @@ func (bridge *Bridge) SendBadEventUserFeedback(_ context.Context, userID string,
return user.BadEventFeedbackResync(ctx) return user.BadEventFeedbackResync(ctx)
} }
bridge.logoutUser(ctx, user, true, false, false) bridge.logoutUser(ctx, user, true, false)
bridge.publish(events.UserLoggedOut{ bridge.publish(events.UserLoggedOut{
UserID: userID, UserID: userID,
@ -527,11 +527,6 @@ func (bridge *Bridge) addUserWithVault(
vault *vault.User, vault *vault.User,
isNew bool, isNew bool,
) error { ) error {
statsPath, err := bridge.locator.ProvideStatsPath()
if err != nil {
return fmt.Errorf("failed to get Statistics directory: %w", err)
}
syncSettingsPath, err := bridge.locator.ProvideIMAPSyncConfigPath() syncSettingsPath, err := bridge.locator.ProvideIMAPSyncConfigPath()
if err != nil { if err != nil {
return fmt.Errorf("failed to get IMAP sync config path: %w", err) return fmt.Errorf("failed to get IMAP sync config path: %w", err)
@ -546,7 +541,6 @@ func (bridge *Bridge) addUserWithVault(
bridge.panicHandler, bridge.panicHandler,
bridge.vault.GetShowAllMail(), bridge.vault.GetShowAllMail(),
bridge.vault.GetMaxSyncMemory(), bridge.vault.GetMaxSyncMemory(),
statsPath,
bridge, bridge,
bridge.serverManager, bridge.serverManager,
bridge.serverManager, bridge.serverManager,
@ -611,14 +605,9 @@ func (bridge *Bridge) newVaultUser(
} }
// logout logs out the given user, optionally logging them out from the API too. // logout logs out the given user, optionally logging them out from the API too.
func (bridge *Bridge) logoutUser(ctx context.Context, user *user.User, withAPI, withData, withTelemetry bool) { func (bridge *Bridge) logoutUser(ctx context.Context, user *user.User, withAPI, withData bool) {
defer delete(bridge.users, user.ID()) defer delete(bridge.users, user.ID())
// if this is actually a remove account
if withData && withAPI {
user.SendConfigStatusAbort(ctx, withTelemetry)
}
logUser.WithFields(logrus.Fields{ logUser.WithFields(logrus.Fields{
"userID": user.ID(), "userID": user.ID(),
"withAPI": withAPI, "withAPI": withAPI,

View File

@ -43,8 +43,7 @@ func (bridge *Bridge) handleUserEvent(ctx context.Context, user *user.User, even
func (bridge *Bridge) handleUserDeauth(ctx context.Context, user *user.User) { func (bridge *Bridge) handleUserDeauth(ctx context.Context, user *user.User) {
safe.Lock(func() { safe.Lock(func() {
bridge.logoutUser(ctx, user, false, false, false) bridge.logoutUser(ctx, user, false, false)
user.ReportConfigStatusFailure("User deauth.")
}, bridge.usersLock) }, bridge.usersLock)
} }

View File

@ -1,228 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus
import (
"encoding/json"
"fmt"
"os"
"strconv"
"time"
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
"github.com/sirupsen/logrus"
)
const version = "1.0.0"
func LoadConfigurationStatus(filepath string) (*ConfigurationStatus, error) {
status := ConfigurationStatus{
FilePath: filepath,
DataLock: safe.NewRWMutex(),
Data: &ConfigurationStatusData{},
}
if _, err := os.Stat(filepath); err == nil {
if err := status.Load(); err == nil {
return &status, nil
}
logrus.WithError(err).Warn("Cannot load configuration status file. Reset it.")
}
status.Data.init()
if err := status.Save(); err != nil {
return &status, err
}
return &status, nil
}
func (status *ConfigurationStatus) Load() error {
bytes, err := os.ReadFile(status.FilePath)
if err != nil {
return err
}
var metadata MetadataOnly
if err := json.Unmarshal(bytes, &metadata); err != nil {
return err
}
if metadata.Metadata.Version != version {
return fmt.Errorf("unsupported configstatus file version %s", metadata.Metadata.Version)
}
return json.Unmarshal(bytes, status.Data)
}
func (status *ConfigurationStatus) Save() error {
temp := status.FilePath + "_temp"
f, err := os.Create(temp) //nolint:gosec
if err != nil {
return err
}
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
err = enc.Encode(status.Data)
if err := f.Close(); err != nil {
logrus.WithError(err).Error("Error while closing configstatus file.")
}
if err != nil {
return err
}
return os.Rename(temp, status.FilePath)
}
func (status *ConfigurationStatus) IsPending() bool {
status.DataLock.RLock()
defer status.DataLock.RUnlock()
return !status.Data.DataV1.PendingSince.IsZero()
}
func (status *ConfigurationStatus) isPendingSinceMin() int {
if min := int(time.Since(status.Data.DataV1.PendingSince).Minutes()); min > 0 { //nolint:predeclared
return min
}
return 0
}
func (status *ConfigurationStatus) IsFromFailure() bool {
status.DataLock.RLock()
defer status.DataLock.RUnlock()
return status.Data.DataV1.FailureDetails != ""
}
func (status *ConfigurationStatus) ApplySuccess() error {
status.DataLock.Lock()
defer status.DataLock.Unlock()
status.Data.init()
status.Data.DataV1.PendingSince = time.Time{}
return status.Save()
}
func (status *ConfigurationStatus) ApplyFailure(err string) error {
status.DataLock.Lock()
defer status.DataLock.Unlock()
status.Data.init()
status.Data.DataV1.FailureDetails = err
return status.Save()
}
func (status *ConfigurationStatus) ApplyProgress() error {
status.DataLock.Lock()
defer status.DataLock.Unlock()
status.Data.DataV1.LastProgress = time.Now()
return status.Save()
}
func (status *ConfigurationStatus) RecordLinkClicked(link uint64) error {
status.DataLock.Lock()
defer status.DataLock.Unlock()
if !status.Data.hasLinkClicked(link) {
status.Data.setClickedLink(link)
return status.Save()
}
return nil
}
func (status *ConfigurationStatus) ReportClicked() error {
status.DataLock.Lock()
defer status.DataLock.Unlock()
if !status.Data.DataV1.ReportClick {
status.Data.DataV1.ReportClick = true
return status.Save()
}
return nil
}
func (status *ConfigurationStatus) ReportSent() error {
status.DataLock.Lock()
defer status.DataLock.Unlock()
if !status.Data.DataV1.ReportSent {
status.Data.DataV1.ReportSent = true
return status.Save()
}
return nil
}
func (status *ConfigurationStatus) AutoconfigUsed(client string) error {
status.DataLock.Lock()
defer status.DataLock.Unlock()
if client != status.Data.DataV1.Autoconf {
status.Data.DataV1.Autoconf = client
return status.Save()
}
return nil
}
func (status *ConfigurationStatus) Remove() error {
status.DataLock.Lock()
defer status.DataLock.Unlock()
return os.Remove(status.FilePath)
}
func (data *ConfigurationStatusData) init() {
data.Metadata = Metadata{
Version: version,
}
data.DataV1.PendingSince = time.Now()
data.DataV1.LastProgress = time.Time{}
data.DataV1.Autoconf = ""
data.DataV1.ClickedLink = 0
data.DataV1.ReportSent = false
data.DataV1.ReportClick = false
data.DataV1.FailureDetails = ""
}
func (data *ConfigurationStatusData) setClickedLink(pos uint64) {
data.DataV1.ClickedLink |= 1 << pos
}
func (data *ConfigurationStatusData) hasLinkClicked(pos uint64) bool {
val := data.DataV1.ClickedLink & (1 << pos)
return val > 0
}
func (data *ConfigurationStatusData) clickedLinkToString() string {
var str = ""
var first = true
for i := 0; i < 64; i++ {
if data.hasLinkClicked(uint64(i)) { //nolint:gosec // disable G115
if !first {
str += ","
} else {
first = false
str += "["
}
str += strconv.Itoa(i)
}
}
if str != "" {
str += "]"
}
return str
}

View File

@ -1,252 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus_test
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
"github.com/ProtonMail/proton-bridge/v3/internal/configstatus"
"github.com/stretchr/testify/require"
)
func TestConfigStatus_init_virgin(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, "1.0.0", config.Data.Metadata.Version)
require.Equal(t, false, config.Data.DataV1.PendingSince.IsZero())
require.Equal(t, true, config.Data.DataV1.LastProgress.IsZero())
require.Equal(t, "", config.Data.DataV1.Autoconf)
require.Equal(t, uint64(0), config.Data.DataV1.ClickedLink)
require.Equal(t, false, config.Data.DataV1.ReportSent)
require.Equal(t, false, config.Data.DataV1.ReportClick)
require.Equal(t, "", config.Data.DataV1.FailureDetails)
}
func TestConfigStatus_init_existing(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
var data = configstatus.ConfigurationStatusData{
Metadata: configstatus.Metadata{Version: "1.0.0"},
DataV1: configstatus.DataV1{Autoconf: "Mr TBird"},
}
require.NoError(t, dumpConfigStatusInFile(&data, file))
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, "1.0.0", config.Data.Metadata.Version)
require.Equal(t, "Mr TBird", config.Data.DataV1.Autoconf)
}
func TestConfigStatus_init_bad_version(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
var data = configstatus.ConfigurationStatusData{
Metadata: configstatus.Metadata{Version: "2.0.0"},
DataV1: configstatus.DataV1{Autoconf: "Mr TBird"},
}
require.NoError(t, dumpConfigStatusInFile(&data, file))
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, "1.0.0", config.Data.Metadata.Version)
require.Equal(t, "", config.Data.DataV1.Autoconf)
}
func TestConfigStatus_IsPending(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, true, config.IsPending())
config.Data.DataV1.PendingSince = time.Time{}
require.Equal(t, false, config.IsPending())
}
func TestConfigStatus_IsFromFailure(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, false, config.IsFromFailure())
config.Data.DataV1.FailureDetails = "test"
require.Equal(t, true, config.IsFromFailure())
}
func TestConfigStatus_ApplySuccess(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, true, config.IsPending())
require.NoError(t, config.ApplySuccess())
require.Equal(t, false, config.IsPending())
config2, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, "1.0.0", config2.Data.Metadata.Version)
require.Equal(t, true, config2.Data.DataV1.PendingSince.IsZero())
require.Equal(t, true, config2.Data.DataV1.LastProgress.IsZero())
require.Equal(t, "", config2.Data.DataV1.Autoconf)
require.Equal(t, uint64(0), config2.Data.DataV1.ClickedLink)
require.Equal(t, false, config2.Data.DataV1.ReportSent)
require.Equal(t, false, config2.Data.DataV1.ReportClick)
require.Equal(t, "", config2.Data.DataV1.FailureDetails)
}
func TestConfigStatus_ApplyFailure(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.NoError(t, config.ApplySuccess())
require.NoError(t, config.ApplyFailure("Big Failure"))
require.Equal(t, true, config.IsFromFailure())
require.Equal(t, true, config.IsPending())
config2, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, "1.0.0", config2.Data.Metadata.Version)
require.Equal(t, false, config2.Data.DataV1.PendingSince.IsZero())
require.Equal(t, true, config2.Data.DataV1.LastProgress.IsZero())
require.Equal(t, "", config2.Data.DataV1.Autoconf)
require.Equal(t, uint64(0), config2.Data.DataV1.ClickedLink)
require.Equal(t, false, config2.Data.DataV1.ReportSent)
require.Equal(t, false, config2.Data.DataV1.ReportClick)
require.Equal(t, "Big Failure", config2.Data.DataV1.FailureDetails)
}
func TestConfigStatus_ApplyProgress(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, true, config.IsPending())
require.Equal(t, true, config.Data.DataV1.LastProgress.IsZero())
require.NoError(t, config.ApplyProgress())
config2, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, "1.0.0", config2.Data.Metadata.Version)
require.Equal(t, false, config2.Data.DataV1.PendingSince.IsZero())
require.Equal(t, false, config2.Data.DataV1.LastProgress.IsZero())
require.Equal(t, "", config2.Data.DataV1.Autoconf)
require.Equal(t, uint64(0), config2.Data.DataV1.ClickedLink)
require.Equal(t, false, config2.Data.DataV1.ReportSent)
require.Equal(t, false, config2.Data.DataV1.ReportClick)
require.Equal(t, "", config2.Data.DataV1.FailureDetails)
}
func TestConfigStatus_RecordLinkClicked(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, uint64(0), config.Data.DataV1.ClickedLink)
require.NoError(t, config.RecordLinkClicked(0))
require.Equal(t, uint64(1), config.Data.DataV1.ClickedLink)
require.NoError(t, config.RecordLinkClicked(1))
require.Equal(t, uint64(3), config.Data.DataV1.ClickedLink)
config2, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, "1.0.0", config2.Data.Metadata.Version)
require.Equal(t, false, config2.Data.DataV1.PendingSince.IsZero())
require.Equal(t, true, config2.Data.DataV1.LastProgress.IsZero())
require.Equal(t, "", config2.Data.DataV1.Autoconf)
require.Equal(t, uint64(3), config2.Data.DataV1.ClickedLink)
require.Equal(t, false, config2.Data.DataV1.ReportSent)
require.Equal(t, false, config2.Data.DataV1.ReportClick)
require.Equal(t, "", config2.Data.DataV1.FailureDetails)
}
func TestConfigStatus_ReportClicked(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, false, config.Data.DataV1.ReportClick)
require.NoError(t, config.ReportClicked())
require.Equal(t, true, config.Data.DataV1.ReportClick)
config2, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, "1.0.0", config2.Data.Metadata.Version)
require.Equal(t, false, config2.Data.DataV1.PendingSince.IsZero())
require.Equal(t, true, config2.Data.DataV1.LastProgress.IsZero())
require.Equal(t, "", config2.Data.DataV1.Autoconf)
require.Equal(t, uint64(0), config2.Data.DataV1.ClickedLink)
require.Equal(t, false, config2.Data.DataV1.ReportSent)
require.Equal(t, true, config2.Data.DataV1.ReportClick)
require.Equal(t, "", config2.Data.DataV1.FailureDetails)
}
func TestConfigStatus_ReportSent(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, false, config.Data.DataV1.ReportSent)
require.NoError(t, config.ReportSent())
require.Equal(t, true, config.Data.DataV1.ReportSent)
config2, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
require.Equal(t, "1.0.0", config2.Data.Metadata.Version)
require.Equal(t, false, config2.Data.DataV1.PendingSince.IsZero())
require.Equal(t, true, config2.Data.DataV1.LastProgress.IsZero())
require.Equal(t, "", config2.Data.DataV1.Autoconf)
require.Equal(t, uint64(0), config2.Data.DataV1.ClickedLink)
require.Equal(t, true, config2.Data.DataV1.ReportSent)
require.Equal(t, false, config2.Data.DataV1.ReportClick)
require.Equal(t, "", config2.Data.DataV1.FailureDetails)
}
func dumpConfigStatusInFile(data *configstatus.ConfigurationStatusData, file string) error {
f, err := os.Create(file)
if err != nil {
return err
}
defer func() { _ = f.Close() }()
return json.NewEncoder(f).Encode(data)
}

View File

@ -1,59 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus
import (
"strconv"
)
type ConfigAbortValues struct {
Duration int `json:"duration"`
}
type ConfigAbortDimensions struct {
ReportClick string `json:"report_click"`
ReportSent string `json:"report_sent"`
ClickedLink string `json:"clicked_link"`
}
type ConfigAbortData struct {
MeasurementGroup string
Event string
Values ConfigSuccessValues
Dimensions ConfigSuccessDimensions
}
type ConfigAbortBuilder struct{}
func (*ConfigAbortBuilder) New(config *ConfigurationStatus) ConfigAbortData {
config.DataLock.RLock()
defer config.DataLock.RUnlock()
return ConfigAbortData{
MeasurementGroup: "bridge.any.configuration",
Event: "bridge_config_abort",
Values: ConfigSuccessValues{
Duration: config.isPendingSinceMin(),
},
Dimensions: ConfigSuccessDimensions{
ReportClick: strconv.FormatBool(config.Data.DataV1.ReportClick),
ReportSent: strconv.FormatBool(config.Data.DataV1.ReportSent),
ClickedLink: config.Data.clickedLinkToString(),
},
}
}

View File

@ -1,75 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus_test
import (
"path/filepath"
"testing"
"time"
"github.com/ProtonMail/proton-bridge/v3/internal/configstatus"
"github.com/stretchr/testify/require"
)
func TestConfigurationAbort_default(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
var builder = configstatus.ConfigAbortBuilder{}
req := builder.New(config)
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
require.Equal(t, "bridge_config_abort", req.Event)
require.Equal(t, 0, req.Values.Duration)
require.Equal(t, "false", req.Dimensions.ReportClick)
require.Equal(t, "false", req.Dimensions.ReportSent)
require.Equal(t, "", req.Dimensions.ClickedLink)
}
func TestConfigurationAbort_fed(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
var data = configstatus.ConfigurationStatusData{
Metadata: configstatus.Metadata{Version: "1.0.0"},
DataV1: configstatus.DataV1{
PendingSince: time.Now().Add(-10 * time.Minute),
LastProgress: time.Time{},
Autoconf: "Mr TBird",
ClickedLink: 42,
ReportSent: false,
ReportClick: true,
FailureDetails: "Not an error",
},
}
require.NoError(t, dumpConfigStatusInFile(&data, file))
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
var builder = configstatus.ConfigAbortBuilder{}
req := builder.New(config)
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
require.Equal(t, "bridge_config_abort", req.Event)
require.Equal(t, 10, req.Values.Duration)
require.Equal(t, "true", req.Dimensions.ReportClick)
require.Equal(t, "false", req.Dimensions.ReportSent)
require.Equal(t, "[1,3,5]", req.Dimensions.ClickedLink)
}

View File

@ -1,60 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus
import "time"
type ConfigProgressValues struct {
NbDay int `json:"nb_day"`
NbDaySinceLast int `json:"nb_day_since_last"`
}
type ConfigProgressData struct {
MeasurementGroup string
Event string
Values ConfigProgressValues
Dimensions struct{}
}
type ConfigProgressBuilder struct{}
func (*ConfigProgressBuilder) New(config *ConfigurationStatus) ConfigProgressData {
config.DataLock.RLock()
defer config.DataLock.RUnlock()
return ConfigProgressData{
MeasurementGroup: "bridge.any.configuration",
Event: "bridge_config_progress",
Values: ConfigProgressValues{
NbDay: numberOfDay(time.Now(), config.Data.DataV1.PendingSince),
NbDaySinceLast: numberOfDay(time.Now(), config.Data.DataV1.LastProgress),
},
}
}
func numberOfDay(now, prev time.Time) int {
if now.IsZero() || prev.IsZero() {
return 1
}
if now.Year() > prev.Year() {
return (365 * (now.Year() - prev.Year())) + now.YearDay() - prev.YearDay()
} else if now.YearDay() > prev.YearDay() {
return now.YearDay() - prev.YearDay()
}
return 0
}

View File

@ -1,100 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus_test
import (
"path/filepath"
"testing"
"time"
"github.com/ProtonMail/proton-bridge/v3/internal/configstatus"
"github.com/stretchr/testify/require"
)
func TestConfigurationProgress_default(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
var builder = configstatus.ConfigProgressBuilder{}
req := builder.New(config)
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
require.Equal(t, "bridge_config_progress", req.Event)
require.Equal(t, 0, req.Values.NbDay)
require.Equal(t, 1, req.Values.NbDaySinceLast)
}
func TestConfigurationProgress_fed(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
var data = configstatus.ConfigurationStatusData{
Metadata: configstatus.Metadata{Version: "1.0.0"},
DataV1: configstatus.DataV1{
PendingSince: time.Now().AddDate(0, 0, -5),
LastProgress: time.Now().AddDate(0, 0, -2),
Autoconf: "Mr TBird",
ClickedLink: 42,
ReportSent: false,
ReportClick: true,
FailureDetails: "Not an error",
},
}
require.NoError(t, dumpConfigStatusInFile(&data, file))
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
var builder = configstatus.ConfigProgressBuilder{}
req := builder.New(config)
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
require.Equal(t, "bridge_config_progress", req.Event)
require.Equal(t, 5, req.Values.NbDay)
require.Equal(t, 2, req.Values.NbDaySinceLast)
}
func TestConfigurationProgress_fed_year_change(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
var data = configstatus.ConfigurationStatusData{
Metadata: configstatus.Metadata{Version: "1.0.0"},
DataV1: configstatus.DataV1{
PendingSince: time.Now().AddDate(-1, 0, -5),
LastProgress: time.Now().AddDate(0, 0, -2),
Autoconf: "Mr TBird",
ClickedLink: 42,
ReportSent: false,
ReportClick: true,
FailureDetails: "Not an error",
},
}
require.NoError(t, dumpConfigStatusInFile(&data, file))
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
var builder = configstatus.ConfigProgressBuilder{}
req := builder.New(config)
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
require.Equal(t, "bridge_config_progress", req.Event)
require.True(t, (req.Values.NbDay == 370) || (req.Values.NbDay == 371)) // leap year is accounted for in the simplest manner.
require.Equal(t, 2, req.Values.NbDaySinceLast)
}

View File

@ -1,63 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus
import (
"strconv"
)
type ConfigRecoveryValues struct {
Duration int `json:"duration"`
}
type ConfigRecoveryDimensions struct {
Autoconf string `json:"autoconf"`
ReportClick string `json:"report_click"`
ReportSent string `json:"report_sent"`
ClickedLink string `json:"clicked_link"`
FailureDetails string `json:"failure_details"`
}
type ConfigRecoveryData struct {
MeasurementGroup string
Event string
Values ConfigRecoveryValues
Dimensions ConfigRecoveryDimensions
}
type ConfigRecoveryBuilder struct{}
func (*ConfigRecoveryBuilder) New(config *ConfigurationStatus) ConfigRecoveryData {
config.DataLock.RLock()
defer config.DataLock.RUnlock()
return ConfigRecoveryData{
MeasurementGroup: "bridge.any.configuration",
Event: "bridge_config_recovery",
Values: ConfigRecoveryValues{
Duration: config.isPendingSinceMin(),
},
Dimensions: ConfigRecoveryDimensions{
Autoconf: config.Data.DataV1.Autoconf,
ReportClick: strconv.FormatBool(config.Data.DataV1.ReportClick),
ReportSent: strconv.FormatBool(config.Data.DataV1.ReportSent),
ClickedLink: config.Data.clickedLinkToString(),
FailureDetails: config.Data.DataV1.FailureDetails,
},
}
}

View File

@ -1,79 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus_test
import (
"path/filepath"
"testing"
"time"
"github.com/ProtonMail/proton-bridge/v3/internal/configstatus"
"github.com/stretchr/testify/require"
)
func TestConfigurationRecovery_default(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
var builder = configstatus.ConfigRecoveryBuilder{}
req := builder.New(config)
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
require.Equal(t, "bridge_config_recovery", req.Event)
require.Equal(t, 0, req.Values.Duration)
require.Equal(t, "", req.Dimensions.Autoconf)
require.Equal(t, "false", req.Dimensions.ReportClick)
require.Equal(t, "false", req.Dimensions.ReportSent)
require.Equal(t, "", req.Dimensions.ClickedLink)
require.Equal(t, "", req.Dimensions.FailureDetails)
}
func TestConfigurationRecovery_fed(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
var data = configstatus.ConfigurationStatusData{
Metadata: configstatus.Metadata{Version: "1.0.0"},
DataV1: configstatus.DataV1{
PendingSince: time.Now().Add(-10 * time.Minute),
LastProgress: time.Time{},
Autoconf: "Mr TBird",
ClickedLink: 42,
ReportSent: false,
ReportClick: true,
FailureDetails: "Not an error",
},
}
require.NoError(t, dumpConfigStatusInFile(&data, file))
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
var builder = configstatus.ConfigRecoveryBuilder{}
req := builder.New(config)
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
require.Equal(t, "bridge_config_recovery", req.Event)
require.Equal(t, 10, req.Values.Duration)
require.Equal(t, "Mr TBird", req.Dimensions.Autoconf)
require.Equal(t, "true", req.Dimensions.ReportClick)
require.Equal(t, "false", req.Dimensions.ReportSent)
require.Equal(t, "[1,3,5]", req.Dimensions.ClickedLink)
require.Equal(t, "Not an error", req.Dimensions.FailureDetails)
}

View File

@ -1,61 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus
import (
"strconv"
)
type ConfigSuccessValues struct {
Duration int `json:"duration"`
}
type ConfigSuccessDimensions struct {
Autoconf string `json:"autoconf"`
ReportClick string `json:"report_click"`
ReportSent string `json:"report_sent"`
ClickedLink string `json:"clicked_link"`
}
type ConfigSuccessData struct {
MeasurementGroup string
Event string
Values ConfigSuccessValues
Dimensions ConfigSuccessDimensions
}
type ConfigSuccessBuilder struct{}
func (*ConfigSuccessBuilder) New(config *ConfigurationStatus) ConfigSuccessData {
config.DataLock.RLock()
defer config.DataLock.RUnlock()
return ConfigSuccessData{
MeasurementGroup: "bridge.any.configuration",
Event: "bridge_config_success",
Values: ConfigSuccessValues{
Duration: config.isPendingSinceMin(),
},
Dimensions: ConfigSuccessDimensions{
Autoconf: config.Data.DataV1.Autoconf,
ReportClick: strconv.FormatBool(config.Data.DataV1.ReportClick),
ReportSent: strconv.FormatBool(config.Data.DataV1.ReportSent),
ClickedLink: config.Data.clickedLinkToString(),
},
}
}

View File

@ -1,77 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus_test
import (
"path/filepath"
"testing"
"time"
"github.com/ProtonMail/proton-bridge/v3/internal/configstatus"
"github.com/stretchr/testify/require"
)
func TestConfigurationSuccess_default(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
var builder = configstatus.ConfigSuccessBuilder{}
req := builder.New(config)
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
require.Equal(t, "bridge_config_success", req.Event)
require.Equal(t, 0, req.Values.Duration)
require.Equal(t, "", req.Dimensions.Autoconf)
require.Equal(t, "false", req.Dimensions.ReportClick)
require.Equal(t, "false", req.Dimensions.ReportSent)
require.Equal(t, "", req.Dimensions.ClickedLink)
}
func TestConfigurationSuccess_fed(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "dummy.json")
var data = configstatus.ConfigurationStatusData{
Metadata: configstatus.Metadata{Version: "1.0.0"},
DataV1: configstatus.DataV1{
PendingSince: time.Now().Add(-10 * time.Minute),
LastProgress: time.Time{},
Autoconf: "Mr TBird",
ClickedLink: 42,
ReportSent: false,
ReportClick: true,
FailureDetails: "Not an error",
},
}
require.NoError(t, dumpConfigStatusInFile(&data, file))
config, err := configstatus.LoadConfigurationStatus(file)
require.NoError(t, err)
var builder = configstatus.ConfigSuccessBuilder{}
req := builder.New(config)
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
require.Equal(t, "bridge_config_success", req.Event)
require.Equal(t, 10, req.Values.Duration)
require.Equal(t, "Mr TBird", req.Dimensions.Autoconf)
require.Equal(t, "true", req.Dimensions.ReportClick)
require.Equal(t, "false", req.Dimensions.ReportSent)
require.Equal(t, "[1,3,5]", req.Dimensions.ClickedLink)
}

View File

@ -1,56 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package configstatus
import (
"time"
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
)
const ProgressCheckInterval = time.Hour
type Metadata struct {
Version string `json:"version"`
}
type MetadataOnly struct {
Metadata Metadata `json:"metadata"`
}
type DataV1 struct {
PendingSince time.Time `json:"pending_since"`
LastProgress time.Time `json:"last_progress"`
Autoconf string `json:"auto_conf"`
ClickedLink uint64 `json:"clicked_link"`
ReportSent bool `json:"report_sent"`
ReportClick bool `json:"report_click"`
FailureDetails string `json:"failure_details"`
}
type ConfigurationStatusData struct {
Metadata Metadata `json:"metadata"`
DataV1 DataV1 `json:"dataV1"`
}
type ConfigurationStatus struct {
FilePath string
DataLock safe.RWMutex
Data *ConfigurationStatusData
}

View File

@ -846,32 +846,6 @@ Status GRPCService::InstallTLSCertificate(ServerContext *, Empty const *, Empty
} }
//****************************************************************************************************************************************************
/// \param[in] request The request.
//****************************************************************************************************************************************************
Status GRPCService::ExternalLinkClicked(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) {
app().log().debug(QString("%1 - URL = %2").arg(__FUNCTION__, QString::fromStdString(request->value())));
return Status::OK;
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
Status GRPCService::ReportBugClicked(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::Empty *) {
app().log().debug(__FUNCTION__);
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
//****************************************************************************************************************************************************
Status GRPCService::AutoconfigClicked(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *response) {
app().log().debug(QString("%1 - Client = %2").arg(__FUNCTION__, QString::fromStdString(request->value())));
return Status::OK;
}
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \param[in] request The request /// \param[in] request The request
/// \param[in] writer The writer /// \param[in] writer The writer

View File

@ -97,9 +97,6 @@ public: // member functions.
grpc::Status IsTLSCertificateInstalled(::grpc::ServerContext *, ::google::protobuf::Empty const*, ::google::protobuf::BoolValue *response) override; grpc::Status IsTLSCertificateInstalled(::grpc::ServerContext *, ::google::protobuf::Empty const*, ::google::protobuf::BoolValue *response) override;
grpc::Status InstallTLSCertificate(::grpc::ServerContext *, ::google::protobuf::Empty const*, ::google::protobuf::Empty *) override; grpc::Status InstallTLSCertificate(::grpc::ServerContext *, ::google::protobuf::Empty const*, ::google::protobuf::Empty *) override;
grpc::Status ExportTLSCertificates(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override; grpc::Status ExportTLSCertificates(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
grpc::Status ReportBugClicked(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Empty *) override;
grpc::Status AutoconfigClicked(::grpc::ServerContext *context, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
grpc::Status ExternalLinkClicked(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
grpc::Status RunEventStream(::grpc::ServerContext *ctx, ::grpc::EventStreamRequest const *request, ::grpc::ServerWriter<::grpc::StreamEvent> *writer) override; grpc::Status RunEventStream(::grpc::ServerContext *ctx, ::grpc::EventStreamRequest const *request, ::grpc::ServerWriter<::grpc::StreamEvent> *writer) override;
grpc::Status StopEventStream(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::Empty *) override; grpc::Status StopEventStream(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::Empty *) override;
bool sendEvent(bridgepp::SPStreamEvent const &event); ///< Queue an event for sending through the event stream. bool sendEvent(bridgepp::SPStreamEvent const &event); ///< Queue an event for sending through the event stream.

View File

@ -303,7 +303,6 @@ void QMLBackend::openExternalLink(QString const &url) {
HANDLE_EXCEPTION( HANDLE_EXCEPTION(
QString const u = url.isEmpty() ? bridgeKBUrl : url; QString const u = url.isEmpty() ? bridgeKBUrl : url;
QDesktopServices::openUrl(u); QDesktopServices::openUrl(u);
emit notifyExternalLinkClicked(u);
) )
} }
@ -1095,33 +1094,6 @@ void QMLBackend::sendBadEventUserFeedback(QString const &userID, bool doResync)
) )
} }
//****************************************************************************************************************************************************
///
//****************************************************************************************************************************************************
void QMLBackend::notifyReportBugClicked() const {
HANDLE_EXCEPTION(
app().grpc().reportBugClicked();
)
}
//****************************************************************************************************************************************************
/// \param[in] client The selected Mail client for autoconfig.
//****************************************************************************************************************************************************
void QMLBackend::notifyAutoconfigClicked(QString const &client) const {
HANDLE_EXCEPTION(
app().grpc().autoconfigClicked(client);
)
}
//****************************************************************************************************************************************************
/// \param[in] article The url of the KB article.
//****************************************************************************************************************************************************
void QMLBackend::notifyExternalLinkClicked(QString const &article) const {
HANDLE_EXCEPTION(
app().grpc().externalLinkClicked(article);
)
}
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
// //
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************

View File

@ -213,9 +213,6 @@ public slots: // slot for signals received from QML -> To be forwarded to Bridge
void onVersionChanged(); ///< Slot for the version change signal. void onVersionChanged(); ///< Slot for the version change signal.
void setMailServerSettings(int imapPort, int smtpPort, bool useSSLForIMAP, bool useSSLForSMTP) const; ///< Forwards a connection mode change request from QML to gRPC void setMailServerSettings(int imapPort, int smtpPort, bool useSSLForIMAP, bool useSSLForSMTP) const; ///< Forwards a connection mode change request from QML to gRPC
void sendBadEventUserFeedback(QString const &userID, bool doResync); ///< Slot the providing user feedback for a bad event. void sendBadEventUserFeedback(QString const &userID, bool doResync); ///< Slot the providing user feedback for a bad event.
void notifyReportBugClicked() const; ///< Slot for the ReportBugClicked gRPC event.
void notifyAutoconfigClicked(QString const &client) const; ///< Slot for gAutoconfigClicked gRPC event.
void notifyExternalLinkClicked(QString const &article) const; ///< Slot for KBArticleClicked gRPC event.
void triggerRepair() const; ///< Slot for the triggering of the bridge repair function i.e. 'resync'. void triggerRepair() const; ///< Slot for the triggering of the bridge repair function i.e. 'resync'.
void userNotificationDismissed(); ///< Slot to pop the notification from the stack and display the rest. void userNotificationDismissed(); ///< Slot to pop the notification from the stack and display the rest.

View File

@ -83,7 +83,6 @@ SettingsView {
onClicked: { onClicked: {
Backend.updateCurrentMailClient(); Backend.updateCurrentMailClient();
Backend.notifyReportBugClicked();
root.parent.showBugReport(); root.parent.showBugReport();
} }
} }

View File

@ -1572,32 +1572,6 @@ UPClientContext GRPCClient::clientContext() const {
return ctx; return ctx;
} }
//****************************************************************************************************************************************************
/// \return the status for the gRPC call.
//****************************************************************************************************************************************************
grpc::Status GRPCClient::reportBugClicked() {
return this->logGRPCCallStatus(stub_->ReportBugClicked(this->clientContext().get(), empty, &empty), __FUNCTION__);
}
//****************************************************************************************************************************************************
/// \param[in] client The client string.
/// \return the status for the gRPC call.
//****************************************************************************************************************************************************
grpc::Status GRPCClient::autoconfigClicked(QString const &client) {
StringValue s;
s.set_value(client.toStdString());
return this->logGRPCCallStatus(stub_->AutoconfigClicked(this->clientContext().get(), s, &empty), __FUNCTION__);
}
//****************************************************************************************************************************************************
/// \param[in] link The clicked link.
/// \return the status for the gRPC call.
//****************************************************************************************************************************************************
grpc::Status GRPCClient::externalLinkClicked(QString const &link) {
StringValue s;
s.set_value(link.toStdString());
return this->logGRPCCallStatus(stub_->ExternalLinkClicked(this->clientContext().get(), s, &empty), __FUNCTION__);
}
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
// //

View File

@ -232,11 +232,6 @@ signals:
void syncFinished(QString const &userID); void syncFinished(QString const &userID);
void syncProgress(QString const &userID, double progress, qint64 elapsedMs, qint64 remainingMs); void syncProgress(QString const &userID, double progress, qint64 elapsedMs, qint64 remainingMs);
public: // telemetry related calls
grpc::Status reportBugClicked(); ///< Performs the 'reportBugClicked' call.
grpc::Status autoconfigClicked(QString const &userID); ///< Performs the 'AutoconfigClicked' call.
grpc::Status externalLinkClicked(QString const &userID); ///< Performs the 'KBArticleClicked' call.
public: // keychain related calls public: // keychain related calls
grpc::Status availableKeychains(QStringList &outKeychains); grpc::Status availableKeychains(QStringList &outKeychains);
grpc::Status currentKeychain(QString &outKeychain); grpc::Status currentKeychain(QString &outKeychain);

View File

@ -5425,7 +5425,7 @@ var file_bridge_proto_rawDesc = []byte{
0x4c, 0x53, 0x5f, 0x43, 0x45, 0x52, 0x54, 0x5f, 0x45, 0x58, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x45, 0x4c, 0x53, 0x5f, 0x43, 0x45, 0x52, 0x54, 0x5f, 0x45, 0x58, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x45,
0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x4c, 0x53, 0x5f, 0x4b, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x4c, 0x53, 0x5f, 0x4b, 0x45,
0x59, 0x5f, 0x45, 0x58, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x59, 0x5f, 0x45, 0x58, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02,
0x32, 0x99, 0x23, 0x0a, 0x06, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x12, 0x49, 0x0a, 0x0b, 0x43, 0x32, 0xbd, 0x21, 0x0a, 0x06, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x12, 0x49, 0x0a, 0x0b, 0x43,
0x68, 0x65, 0x63, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
@ -5666,51 +5666,37 @@ var file_bridge_proto_rawDesc = []byte{
0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x4d, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x4d,
0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
0x74, 0x79, 0x12, 0x42, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x75, 0x67, 0x43, 0x74, 0x79, 0x12, 0x4f, 0x0a, 0x19, 0x49, 0x73, 0x54, 0x4c, 0x53, 0x43, 0x65, 0x72, 0x74, 0x69,
0x6c, 0x69, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x12,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, 0x11, 0x41, 0x75, 0x74, 0x6f, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x43, 0x6c, 0x69, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x1c, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
0x79, 0x12, 0x4b, 0x0a, 0x13, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4c, 0x69, 0x6e,
0x6b, 0x43, 0x6c, 0x69, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e,
0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4f,
0x0a, 0x19, 0x49, 0x73, 0x54, 0x4c, 0x53, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
0x74, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
0x47, 0x0a, 0x15, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x54, 0x4c, 0x53, 0x43, 0x65, 0x72,
0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4d, 0x0a, 0x15, 0x45, 0x78, 0x70, 0x6f,
0x72, 0x74, 0x54, 0x4c, 0x53, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
0x73, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a,
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x45, 0x76, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61,
0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x6c, 0x75, 0x65, 0x12, 0x47, 0x0a, 0x15, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x54, 0x4c,
0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x53, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x67,
0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x70,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x16, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0d, 0x54,
0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, 0x70, 0x61, 0x69, 0x72, 0x12, 0x16, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x36, 0x5a, 0x34, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4d, 0x0a, 0x15,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x4c, 0x53, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
0x6e, 0x4d, 0x61, 0x69, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2d, 0x62, 0x72, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x64, 0x67, 0x65, 0x2f, 0x76, 0x33, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61,
0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0e, 0x52,
0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, 0x2e,
0x67, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53,
0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0f,
0x53, 0x74, 0x6f, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12,
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12,
0x3f, 0x0a, 0x0d, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, 0x70, 0x61, 0x69, 0x72,
0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50,
0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x4d, 0x61, 0x69, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e,
0x2d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2f, 0x76, 0x33, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -5938,81 +5924,75 @@ var file_bridge_proto_depIdxs = []int32{
80, // 121: grpc.Bridge.LogoutUser:input_type -> google.protobuf.StringValue 80, // 121: grpc.Bridge.LogoutUser:input_type -> google.protobuf.StringValue
80, // 122: grpc.Bridge.RemoveUser:input_type -> google.protobuf.StringValue 80, // 122: grpc.Bridge.RemoveUser:input_type -> google.protobuf.StringValue
18, // 123: grpc.Bridge.ConfigureUserAppleMail:input_type -> grpc.ConfigureAppleMailRequest 18, // 123: grpc.Bridge.ConfigureUserAppleMail:input_type -> grpc.ConfigureAppleMailRequest
81, // 124: grpc.Bridge.ReportBugClicked:input_type -> google.protobuf.Empty 81, // 124: grpc.Bridge.IsTLSCertificateInstalled:input_type -> google.protobuf.Empty
80, // 125: grpc.Bridge.AutoconfigClicked:input_type -> google.protobuf.StringValue 81, // 125: grpc.Bridge.InstallTLSCertificate:input_type -> google.protobuf.Empty
80, // 126: grpc.Bridge.ExternalLinkClicked:input_type -> google.protobuf.StringValue 80, // 126: grpc.Bridge.ExportTLSCertificates:input_type -> google.protobuf.StringValue
81, // 127: grpc.Bridge.IsTLSCertificateInstalled:input_type -> google.protobuf.Empty 19, // 127: grpc.Bridge.RunEventStream:input_type -> grpc.EventStreamRequest
81, // 128: grpc.Bridge.InstallTLSCertificate:input_type -> google.protobuf.Empty 81, // 128: grpc.Bridge.StopEventStream:input_type -> google.protobuf.Empty
80, // 129: grpc.Bridge.ExportTLSCertificates:input_type -> google.protobuf.StringValue 81, // 129: grpc.Bridge.TriggerRepair:input_type -> google.protobuf.Empty
19, // 130: grpc.Bridge.RunEventStream:input_type -> grpc.EventStreamRequest 80, // 130: grpc.Bridge.CheckTokens:output_type -> google.protobuf.StringValue
81, // 131: grpc.Bridge.StopEventStream:input_type -> google.protobuf.Empty 81, // 131: grpc.Bridge.AddLogEntry:output_type -> google.protobuf.Empty
81, // 132: grpc.Bridge.TriggerRepair:input_type -> google.protobuf.Empty 8, // 132: grpc.Bridge.GuiReady:output_type -> grpc.GuiReadyResponse
80, // 133: grpc.Bridge.CheckTokens:output_type -> google.protobuf.StringValue 81, // 133: grpc.Bridge.Quit:output_type -> google.protobuf.Empty
81, // 134: grpc.Bridge.AddLogEntry:output_type -> google.protobuf.Empty 81, // 134: grpc.Bridge.Restart:output_type -> google.protobuf.Empty
8, // 135: grpc.Bridge.GuiReady:output_type -> grpc.GuiReadyResponse 82, // 135: grpc.Bridge.ShowOnStartup:output_type -> google.protobuf.BoolValue
81, // 136: grpc.Bridge.Quit:output_type -> google.protobuf.Empty 81, // 136: grpc.Bridge.SetIsAutostartOn:output_type -> google.protobuf.Empty
81, // 137: grpc.Bridge.Restart:output_type -> google.protobuf.Empty 82, // 137: grpc.Bridge.IsAutostartOn:output_type -> google.protobuf.BoolValue
82, // 138: grpc.Bridge.ShowOnStartup:output_type -> google.protobuf.BoolValue 81, // 138: grpc.Bridge.SetIsBetaEnabled:output_type -> google.protobuf.Empty
81, // 139: grpc.Bridge.SetIsAutostartOn:output_type -> google.protobuf.Empty 82, // 139: grpc.Bridge.IsBetaEnabled:output_type -> google.protobuf.BoolValue
82, // 140: grpc.Bridge.IsAutostartOn:output_type -> google.protobuf.BoolValue 81, // 140: grpc.Bridge.SetIsAllMailVisible:output_type -> google.protobuf.Empty
81, // 141: grpc.Bridge.SetIsBetaEnabled:output_type -> google.protobuf.Empty 82, // 141: grpc.Bridge.IsAllMailVisible:output_type -> google.protobuf.BoolValue
82, // 142: grpc.Bridge.IsBetaEnabled:output_type -> google.protobuf.BoolValue 81, // 142: grpc.Bridge.SetIsTelemetryDisabled:output_type -> google.protobuf.Empty
81, // 143: grpc.Bridge.SetIsAllMailVisible:output_type -> google.protobuf.Empty 82, // 143: grpc.Bridge.IsTelemetryDisabled:output_type -> google.protobuf.BoolValue
82, // 144: grpc.Bridge.IsAllMailVisible:output_type -> google.protobuf.BoolValue 80, // 144: grpc.Bridge.GoOs:output_type -> google.protobuf.StringValue
81, // 145: grpc.Bridge.SetIsTelemetryDisabled:output_type -> google.protobuf.Empty 81, // 145: grpc.Bridge.TriggerReset:output_type -> google.protobuf.Empty
82, // 146: grpc.Bridge.IsTelemetryDisabled:output_type -> google.protobuf.BoolValue 80, // 146: grpc.Bridge.Version:output_type -> google.protobuf.StringValue
80, // 147: grpc.Bridge.GoOs:output_type -> google.protobuf.StringValue 80, // 147: grpc.Bridge.LogsPath:output_type -> google.protobuf.StringValue
81, // 148: grpc.Bridge.TriggerReset:output_type -> google.protobuf.Empty 80, // 148: grpc.Bridge.LicensePath:output_type -> google.protobuf.StringValue
80, // 149: grpc.Bridge.Version:output_type -> google.protobuf.StringValue 80, // 149: grpc.Bridge.ReleaseNotesPageLink:output_type -> google.protobuf.StringValue
80, // 150: grpc.Bridge.LogsPath:output_type -> google.protobuf.StringValue 80, // 150: grpc.Bridge.DependencyLicensesLink:output_type -> google.protobuf.StringValue
80, // 151: grpc.Bridge.LicensePath:output_type -> google.protobuf.StringValue 80, // 151: grpc.Bridge.LandingPageLink:output_type -> google.protobuf.StringValue
80, // 152: grpc.Bridge.ReleaseNotesPageLink:output_type -> google.protobuf.StringValue 81, // 152: grpc.Bridge.SetColorSchemeName:output_type -> google.protobuf.Empty
80, // 153: grpc.Bridge.DependencyLicensesLink:output_type -> google.protobuf.StringValue 80, // 153: grpc.Bridge.ColorSchemeName:output_type -> google.protobuf.StringValue
80, // 154: grpc.Bridge.LandingPageLink:output_type -> google.protobuf.StringValue 80, // 154: grpc.Bridge.CurrentEmailClient:output_type -> google.protobuf.StringValue
81, // 155: grpc.Bridge.SetColorSchemeName:output_type -> google.protobuf.Empty 81, // 155: grpc.Bridge.ReportBug:output_type -> google.protobuf.Empty
80, // 156: grpc.Bridge.ColorSchemeName:output_type -> google.protobuf.StringValue 81, // 156: grpc.Bridge.ForceLauncher:output_type -> google.protobuf.Empty
80, // 157: grpc.Bridge.CurrentEmailClient:output_type -> google.protobuf.StringValue 81, // 157: grpc.Bridge.SetMainExecutable:output_type -> google.protobuf.Empty
81, // 158: grpc.Bridge.ReportBug:output_type -> google.protobuf.Empty 81, // 158: grpc.Bridge.RequestKnowledgeBaseSuggestions:output_type -> google.protobuf.Empty
81, // 159: grpc.Bridge.ForceLauncher:output_type -> google.protobuf.Empty 81, // 159: grpc.Bridge.Login:output_type -> google.protobuf.Empty
81, // 160: grpc.Bridge.SetMainExecutable:output_type -> google.protobuf.Empty 81, // 160: grpc.Bridge.Login2FA:output_type -> google.protobuf.Empty
81, // 161: grpc.Bridge.RequestKnowledgeBaseSuggestions:output_type -> google.protobuf.Empty 81, // 161: grpc.Bridge.Login2Passwords:output_type -> google.protobuf.Empty
81, // 162: grpc.Bridge.Login:output_type -> google.protobuf.Empty 81, // 162: grpc.Bridge.LoginAbort:output_type -> google.protobuf.Empty
81, // 163: grpc.Bridge.Login2FA:output_type -> google.protobuf.Empty 81, // 163: grpc.Bridge.CheckUpdate:output_type -> google.protobuf.Empty
81, // 164: grpc.Bridge.Login2Passwords:output_type -> google.protobuf.Empty 81, // 164: grpc.Bridge.InstallUpdate:output_type -> google.protobuf.Empty
81, // 165: grpc.Bridge.LoginAbort:output_type -> google.protobuf.Empty 81, // 165: grpc.Bridge.SetIsAutomaticUpdateOn:output_type -> google.protobuf.Empty
81, // 166: grpc.Bridge.CheckUpdate:output_type -> google.protobuf.Empty 82, // 166: grpc.Bridge.IsAutomaticUpdateOn:output_type -> google.protobuf.BoolValue
81, // 167: grpc.Bridge.InstallUpdate:output_type -> google.protobuf.Empty 80, // 167: grpc.Bridge.DiskCachePath:output_type -> google.protobuf.StringValue
81, // 168: grpc.Bridge.SetIsAutomaticUpdateOn:output_type -> google.protobuf.Empty 81, // 168: grpc.Bridge.SetDiskCachePath:output_type -> google.protobuf.Empty
82, // 169: grpc.Bridge.IsAutomaticUpdateOn:output_type -> google.protobuf.BoolValue 81, // 169: grpc.Bridge.SetIsDoHEnabled:output_type -> google.protobuf.Empty
80, // 170: grpc.Bridge.DiskCachePath:output_type -> google.protobuf.StringValue 82, // 170: grpc.Bridge.IsDoHEnabled:output_type -> google.protobuf.BoolValue
81, // 171: grpc.Bridge.SetDiskCachePath:output_type -> google.protobuf.Empty 12, // 171: grpc.Bridge.MailServerSettings:output_type -> grpc.ImapSmtpSettings
81, // 172: grpc.Bridge.SetIsDoHEnabled:output_type -> google.protobuf.Empty 81, // 172: grpc.Bridge.SetMailServerSettings:output_type -> google.protobuf.Empty
82, // 173: grpc.Bridge.IsDoHEnabled:output_type -> google.protobuf.BoolValue 80, // 173: grpc.Bridge.Hostname:output_type -> google.protobuf.StringValue
12, // 174: grpc.Bridge.MailServerSettings:output_type -> grpc.ImapSmtpSettings 82, // 174: grpc.Bridge.IsPortFree:output_type -> google.protobuf.BoolValue
81, // 175: grpc.Bridge.SetMailServerSettings:output_type -> google.protobuf.Empty 13, // 175: grpc.Bridge.AvailableKeychains:output_type -> grpc.AvailableKeychainsResponse
80, // 176: grpc.Bridge.Hostname:output_type -> google.protobuf.StringValue 81, // 176: grpc.Bridge.SetCurrentKeychain:output_type -> google.protobuf.Empty
82, // 177: grpc.Bridge.IsPortFree:output_type -> google.protobuf.BoolValue 80, // 177: grpc.Bridge.CurrentKeychain:output_type -> google.protobuf.StringValue
13, // 178: grpc.Bridge.AvailableKeychains:output_type -> grpc.AvailableKeychainsResponse 17, // 178: grpc.Bridge.GetUserList:output_type -> grpc.UserListResponse
81, // 179: grpc.Bridge.SetCurrentKeychain:output_type -> google.protobuf.Empty 14, // 179: grpc.Bridge.GetUser:output_type -> grpc.User
80, // 180: grpc.Bridge.CurrentKeychain:output_type -> google.protobuf.StringValue 81, // 180: grpc.Bridge.SetUserSplitMode:output_type -> google.protobuf.Empty
17, // 181: grpc.Bridge.GetUserList:output_type -> grpc.UserListResponse 81, // 181: grpc.Bridge.SendBadEventUserFeedback:output_type -> google.protobuf.Empty
14, // 182: grpc.Bridge.GetUser:output_type -> grpc.User 81, // 182: grpc.Bridge.LogoutUser:output_type -> google.protobuf.Empty
81, // 183: grpc.Bridge.SetUserSplitMode:output_type -> google.protobuf.Empty 81, // 183: grpc.Bridge.RemoveUser:output_type -> google.protobuf.Empty
81, // 184: grpc.Bridge.SendBadEventUserFeedback:output_type -> google.protobuf.Empty 81, // 184: grpc.Bridge.ConfigureUserAppleMail:output_type -> google.protobuf.Empty
81, // 185: grpc.Bridge.LogoutUser:output_type -> google.protobuf.Empty 82, // 185: grpc.Bridge.IsTLSCertificateInstalled:output_type -> google.protobuf.BoolValue
81, // 186: grpc.Bridge.RemoveUser:output_type -> google.protobuf.Empty 81, // 186: grpc.Bridge.InstallTLSCertificate:output_type -> google.protobuf.Empty
81, // 187: grpc.Bridge.ConfigureUserAppleMail:output_type -> google.protobuf.Empty 81, // 187: grpc.Bridge.ExportTLSCertificates:output_type -> google.protobuf.Empty
81, // 188: grpc.Bridge.ReportBugClicked:output_type -> google.protobuf.Empty 20, // 188: grpc.Bridge.RunEventStream:output_type -> grpc.StreamEvent
81, // 189: grpc.Bridge.AutoconfigClicked:output_type -> google.protobuf.Empty 81, // 189: grpc.Bridge.StopEventStream:output_type -> google.protobuf.Empty
81, // 190: grpc.Bridge.ExternalLinkClicked:output_type -> google.protobuf.Empty 81, // 190: grpc.Bridge.TriggerRepair:output_type -> google.protobuf.Empty
82, // 191: grpc.Bridge.IsTLSCertificateInstalled:output_type -> google.protobuf.BoolValue 130, // [130:191] is the sub-list for method output_type
81, // 192: grpc.Bridge.InstallTLSCertificate:output_type -> google.protobuf.Empty 69, // [69:130] is the sub-list for method input_type
81, // 193: grpc.Bridge.ExportTLSCertificates:output_type -> google.protobuf.Empty
20, // 194: grpc.Bridge.RunEventStream:output_type -> grpc.StreamEvent
81, // 195: grpc.Bridge.StopEventStream:output_type -> google.protobuf.Empty
81, // 196: grpc.Bridge.TriggerRepair:output_type -> google.protobuf.Empty
133, // [133:197] is the sub-list for method output_type
69, // [69:133] is the sub-list for method input_type
69, // [69:69] is the sub-list for extension type_name 69, // [69:69] is the sub-list for extension type_name
69, // [69:69] is the sub-list for extension extendee 69, // [69:69] is the sub-list for extension extendee
0, // [0:69] is the sub-list for field type_name 0, // [0:69] is the sub-list for field type_name

View File

@ -98,11 +98,6 @@ service Bridge {
rpc RemoveUser(google.protobuf.StringValue) returns (google.protobuf.Empty); rpc RemoveUser(google.protobuf.StringValue) returns (google.protobuf.Empty);
rpc ConfigureUserAppleMail(ConfigureAppleMailRequest) returns (google.protobuf.Empty); rpc ConfigureUserAppleMail(ConfigureAppleMailRequest) returns (google.protobuf.Empty);
// Telemetry
rpc ReportBugClicked(google.protobuf.Empty) returns (google.protobuf.Empty);
rpc AutoconfigClicked(google.protobuf.StringValue) returns (google.protobuf.Empty);
rpc ExternalLinkClicked(google.protobuf.StringValue) returns (google.protobuf.Empty);
// TLS certificate related calls // TLS certificate related calls
rpc IsTLSCertificateInstalled(google.protobuf.Empty) returns (google.protobuf.BoolValue); rpc IsTLSCertificateInstalled(google.protobuf.Empty) returns (google.protobuf.BoolValue);
rpc InstallTLSCertificate(google.protobuf.Empty) returns (google.protobuf.Empty); rpc InstallTLSCertificate(google.protobuf.Empty) returns (google.protobuf.Empty);

View File

@ -93,9 +93,6 @@ const (
Bridge_LogoutUser_FullMethodName = "/grpc.Bridge/LogoutUser" Bridge_LogoutUser_FullMethodName = "/grpc.Bridge/LogoutUser"
Bridge_RemoveUser_FullMethodName = "/grpc.Bridge/RemoveUser" Bridge_RemoveUser_FullMethodName = "/grpc.Bridge/RemoveUser"
Bridge_ConfigureUserAppleMail_FullMethodName = "/grpc.Bridge/ConfigureUserAppleMail" Bridge_ConfigureUserAppleMail_FullMethodName = "/grpc.Bridge/ConfigureUserAppleMail"
Bridge_ReportBugClicked_FullMethodName = "/grpc.Bridge/ReportBugClicked"
Bridge_AutoconfigClicked_FullMethodName = "/grpc.Bridge/AutoconfigClicked"
Bridge_ExternalLinkClicked_FullMethodName = "/grpc.Bridge/ExternalLinkClicked"
Bridge_IsTLSCertificateInstalled_FullMethodName = "/grpc.Bridge/IsTLSCertificateInstalled" Bridge_IsTLSCertificateInstalled_FullMethodName = "/grpc.Bridge/IsTLSCertificateInstalled"
Bridge_InstallTLSCertificate_FullMethodName = "/grpc.Bridge/InstallTLSCertificate" Bridge_InstallTLSCertificate_FullMethodName = "/grpc.Bridge/InstallTLSCertificate"
Bridge_ExportTLSCertificates_FullMethodName = "/grpc.Bridge/ExportTLSCertificates" Bridge_ExportTLSCertificates_FullMethodName = "/grpc.Bridge/ExportTLSCertificates"
@ -170,10 +167,6 @@ type BridgeClient interface {
LogoutUser(ctx context.Context, in *wrapperspb.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error) LogoutUser(ctx context.Context, in *wrapperspb.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error)
RemoveUser(ctx context.Context, in *wrapperspb.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error) RemoveUser(ctx context.Context, in *wrapperspb.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error)
ConfigureUserAppleMail(ctx context.Context, in *ConfigureAppleMailRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) ConfigureUserAppleMail(ctx context.Context, in *ConfigureAppleMailRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
// Telemetry
ReportBugClicked(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
AutoconfigClicked(ctx context.Context, in *wrapperspb.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error)
ExternalLinkClicked(ctx context.Context, in *wrapperspb.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error)
// TLS certificate related calls // TLS certificate related calls
IsTLSCertificateInstalled(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*wrapperspb.BoolValue, error) IsTLSCertificateInstalled(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*wrapperspb.BoolValue, error)
InstallTLSCertificate(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) InstallTLSCertificate(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
@ -688,33 +681,6 @@ func (c *bridgeClient) ConfigureUserAppleMail(ctx context.Context, in *Configure
return out, nil return out, nil
} }
func (c *bridgeClient) ReportBugClicked(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, Bridge_ReportBugClicked_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *bridgeClient) AutoconfigClicked(ctx context.Context, in *wrapperspb.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, Bridge_AutoconfigClicked_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *bridgeClient) ExternalLinkClicked(ctx context.Context, in *wrapperspb.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, Bridge_ExternalLinkClicked_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *bridgeClient) IsTLSCertificateInstalled(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*wrapperspb.BoolValue, error) { func (c *bridgeClient) IsTLSCertificateInstalled(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*wrapperspb.BoolValue, error) {
out := new(wrapperspb.BoolValue) out := new(wrapperspb.BoolValue)
err := c.cc.Invoke(ctx, Bridge_IsTLSCertificateInstalled_FullMethodName, in, out, opts...) err := c.cc.Invoke(ctx, Bridge_IsTLSCertificateInstalled_FullMethodName, in, out, opts...)
@ -858,10 +824,6 @@ type BridgeServer interface {
LogoutUser(context.Context, *wrapperspb.StringValue) (*emptypb.Empty, error) LogoutUser(context.Context, *wrapperspb.StringValue) (*emptypb.Empty, error)
RemoveUser(context.Context, *wrapperspb.StringValue) (*emptypb.Empty, error) RemoveUser(context.Context, *wrapperspb.StringValue) (*emptypb.Empty, error)
ConfigureUserAppleMail(context.Context, *ConfigureAppleMailRequest) (*emptypb.Empty, error) ConfigureUserAppleMail(context.Context, *ConfigureAppleMailRequest) (*emptypb.Empty, error)
// Telemetry
ReportBugClicked(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
AutoconfigClicked(context.Context, *wrapperspb.StringValue) (*emptypb.Empty, error)
ExternalLinkClicked(context.Context, *wrapperspb.StringValue) (*emptypb.Empty, error)
// TLS certificate related calls // TLS certificate related calls
IsTLSCertificateInstalled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) IsTLSCertificateInstalled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error)
InstallTLSCertificate(context.Context, *emptypb.Empty) (*emptypb.Empty, error) InstallTLSCertificate(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
@ -1043,15 +1005,6 @@ func (UnimplementedBridgeServer) RemoveUser(context.Context, *wrapperspb.StringV
func (UnimplementedBridgeServer) ConfigureUserAppleMail(context.Context, *ConfigureAppleMailRequest) (*emptypb.Empty, error) { func (UnimplementedBridgeServer) ConfigureUserAppleMail(context.Context, *ConfigureAppleMailRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConfigureUserAppleMail not implemented") return nil, status.Errorf(codes.Unimplemented, "method ConfigureUserAppleMail not implemented")
} }
func (UnimplementedBridgeServer) ReportBugClicked(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method ReportBugClicked not implemented")
}
func (UnimplementedBridgeServer) AutoconfigClicked(context.Context, *wrapperspb.StringValue) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method AutoconfigClicked not implemented")
}
func (UnimplementedBridgeServer) ExternalLinkClicked(context.Context, *wrapperspb.StringValue) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method ExternalLinkClicked not implemented")
}
func (UnimplementedBridgeServer) IsTLSCertificateInstalled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) { func (UnimplementedBridgeServer) IsTLSCertificateInstalled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
return nil, status.Errorf(codes.Unimplemented, "method IsTLSCertificateInstalled not implemented") return nil, status.Errorf(codes.Unimplemented, "method IsTLSCertificateInstalled not implemented")
} }
@ -2073,60 +2026,6 @@ func _Bridge_ConfigureUserAppleMail_Handler(srv interface{}, ctx context.Context
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Bridge_ReportBugClicked_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(emptypb.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BridgeServer).ReportBugClicked(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Bridge_ReportBugClicked_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BridgeServer).ReportBugClicked(ctx, req.(*emptypb.Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Bridge_AutoconfigClicked_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(wrapperspb.StringValue)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BridgeServer).AutoconfigClicked(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Bridge_AutoconfigClicked_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BridgeServer).AutoconfigClicked(ctx, req.(*wrapperspb.StringValue))
}
return interceptor(ctx, in, info, handler)
}
func _Bridge_ExternalLinkClicked_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(wrapperspb.StringValue)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BridgeServer).ExternalLinkClicked(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Bridge_ExternalLinkClicked_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BridgeServer).ExternalLinkClicked(ctx, req.(*wrapperspb.StringValue))
}
return interceptor(ctx, in, info, handler)
}
func _Bridge_IsTLSCertificateInstalled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _Bridge_IsTLSCertificateInstalled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(emptypb.Empty) in := new(emptypb.Empty)
if err := dec(in); err != nil { if err := dec(in); err != nil {
@ -2465,18 +2364,6 @@ var Bridge_ServiceDesc = grpc.ServiceDesc{
MethodName: "ConfigureUserAppleMail", MethodName: "ConfigureUserAppleMail",
Handler: _Bridge_ConfigureUserAppleMail_Handler, Handler: _Bridge_ConfigureUserAppleMail_Handler,
}, },
{
MethodName: "ReportBugClicked",
Handler: _Bridge_ReportBugClicked_Handler,
},
{
MethodName: "AutoconfigClicked",
Handler: _Bridge_AutoconfigClicked_Handler,
},
{
MethodName: "ExternalLinkClicked",
Handler: _Bridge_ExternalLinkClicked_Handler,
},
{ {
MethodName: "IsTLSCertificateInstalled", MethodName: "IsTLSCertificateInstalled",
Handler: _Bridge_IsTLSCertificateInstalled_Handler, Handler: _Bridge_IsTLSCertificateInstalled_Handler,

View File

@ -1,44 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package grpc
import (
"context"
"github.com/ProtonMail/gluon/async"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
func (s *Service) ReportBugClicked(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.bridge.ReportBugClicked()
return &emptypb.Empty{}, nil
}
func (s *Service) AutoconfigClicked(_ context.Context, client *wrapperspb.StringValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.bridge.AutoconfigUsed(client.Value)
return &emptypb.Empty{}, nil
}
func (s *Service) ExternalLinkClicked(_ context.Context, article *wrapperspb.StringValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.bridge.ExternalLinkClicked(article.Value)
return &emptypb.Empty{}, nil
}

View File

@ -188,16 +188,6 @@ func (l *Locations) ProvideUpdatesPath() (string, error) {
return l.getUpdatesPath(), nil return l.getUpdatesPath(), nil
} }
// ProvideStatsPath returns a location for statistics files (e.g. ~/.local/share/<company>/<app>/stats).
// It creates it if it doesn't already exist.
func (l *Locations) ProvideStatsPath() (string, error) {
if err := os.MkdirAll(l.getStatsPath(), 0o700); err != nil {
return "", err
}
return l.getStatsPath(), nil
}
func (l *Locations) ProvideIMAPSyncConfigPath() (string, error) { func (l *Locations) ProvideIMAPSyncConfigPath() (string, error) {
if err := os.MkdirAll(l.getIMAPSyncConfigPath(), 0o700); err != nil { if err := os.MkdirAll(l.getIMAPSyncConfigPath(), 0o700); err != nil {
return "", err return "", err
@ -252,10 +242,6 @@ func (l *Locations) getNotificationsCachePath() string {
return filepath.Join(l.userCache, "notifications") return filepath.Join(l.userCache, "notifications")
} }
func (l *Locations) getStatsPath() string {
return filepath.Join(l.userData, "stats")
}
func (l *Locations) getUnleashCachePath() string { return filepath.Join(l.userCache, "unleash_cache") } func (l *Locations) getUnleashCachePath() string { return filepath.Join(l.userCache, "unleash_cache") }
// Clear removes everything except the lock and update files. // Clear removes everything except the lock and update files.

View File

@ -56,7 +56,6 @@ type Connector struct {
identityState sharedIdentity identityState sharedIdentity
client APIClient client APIClient
telemetry Telemetry
reporter reporter.Reporter reporter reporter.Reporter
panicHandler async.PanicHandler panicHandler async.PanicHandler
sendRecorder *sendrecorder.SendRecorder sendRecorder *sendrecorder.SendRecorder
@ -80,7 +79,6 @@ func NewConnector(
addressMode usertypes.AddressMode, addressMode usertypes.AddressMode,
sendRecorder *sendrecorder.SendRecorder, sendRecorder *sendrecorder.SendRecorder,
panicHandler async.PanicHandler, panicHandler async.PanicHandler,
telemetry Telemetry,
reporter reporter.Reporter, reporter reporter.Reporter,
showAllMail bool, showAllMail bool,
syncState *SyncState, syncState *SyncState,
@ -96,7 +94,6 @@ func NewConnector(
attrs: defaultMailboxAttributes(), attrs: defaultMailboxAttributes(),
client: apiClient, client: apiClient,
telemetry: telemetry,
reporter: reporter, reporter: reporter,
panicHandler: panicHandler, panicHandler: panicHandler,
sendRecorder: sendRecorder, sendRecorder: sendRecorder,
@ -169,10 +166,9 @@ func (s *Connector) Init(ctx context.Context, cache connector.IMAPState) error {
}) })
} }
func (s *Connector) Authorize(ctx context.Context, username string, password []byte) bool { func (s *Connector) Authorize(_ context.Context, username string, password []byte) bool {
addrID, err := s.identityState.CheckAuth(username, password) addrID, err := s.identityState.CheckAuth(username, password)
if err != nil { if err != nil {
s.telemetry.ReportConfigStatusFailure("IMAP " + err.Error())
return false return false
} }
@ -180,8 +176,6 @@ func (s *Connector) Authorize(ctx context.Context, username string, password []b
return false return false
} }
s.telemetry.SendConfigStatusSuccess(ctx)
return true return true
} }

View File

@ -47,12 +47,6 @@ type EventProvider interface {
RewindEventID(ctx context.Context, eventID string) error RewindEventID(ctx context.Context, eventID string) error
} }
type Telemetry interface {
useridentity.Telemetry
SendConfigStatusSuccess(ctx context.Context)
ReportConfigStatusFailure(errDetails string)
}
type GluonIDProvider interface { type GluonIDProvider interface {
GetGluonID(addrID string) (string, bool) GetGluonID(addrID string) (string, bool)
GetGluonIDs() map[string]string GetGluonIDs() map[string]string
@ -77,7 +71,6 @@ type Service struct {
serverManager IMAPServerManager serverManager IMAPServerManager
eventPublisher events.EventPublisher eventPublisher events.EventPublisher
telemetry Telemetry
panicHandler async.PanicHandler panicHandler async.PanicHandler
sendRecorder *sendrecorder.SendRecorder sendRecorder *sendrecorder.SendRecorder
reporter reporter.Reporter reporter reporter.Reporter
@ -112,7 +105,6 @@ func NewService(
keyPassProvider useridentity.KeyPassProvider, keyPassProvider useridentity.KeyPassProvider,
panicHandler async.PanicHandler, panicHandler async.PanicHandler,
sendRecorder *sendrecorder.SendRecorder, sendRecorder *sendrecorder.SendRecorder,
telemetry Telemetry,
reporter reporter.Reporter, reporter reporter.Reporter,
addressMode usertypes.AddressMode, addressMode usertypes.AddressMode,
subscription events.Subscription, subscription events.Subscription,
@ -150,7 +142,6 @@ func NewService(
panicHandler: panicHandler, panicHandler: panicHandler,
sendRecorder: sendRecorder, sendRecorder: sendRecorder,
telemetry: telemetry,
reporter: reporter, reporter: reporter,
connectors: make(map[string]*Connector), connectors: make(map[string]*Connector),
@ -513,7 +504,6 @@ func (s *Service) buildConnectors() (map[string]*Connector, error) {
s.addressMode, s.addressMode,
s.sendRecorder, s.sendRecorder,
s.panicHandler, s.panicHandler,
s.telemetry,
s.reporter, s.reporter,
s.showAllMail, s.showAllMail,
s.syncStateProvider, s.syncStateProvider,
@ -531,7 +521,6 @@ func (s *Service) buildConnectors() (map[string]*Connector, error) {
s.addressMode, s.addressMode,
s.sendRecorder, s.sendRecorder,
s.panicHandler, s.panicHandler,
s.telemetry,
s.reporter, s.reporter,
s.showAllMail, s.showAllMail,
s.syncStateProvider, s.syncStateProvider,

View File

@ -154,7 +154,6 @@ func addNewAddressSplitMode(ctx context.Context, s *Service, addrID string) erro
s.addressMode, s.addressMode,
s.sendRecorder, s.sendRecorder,
s.panicHandler, s.panicHandler,
s.telemetry,
s.reporter, s.reporter,
s.showAllMail, s.showAllMail,
s.syncStateProvider, s.syncStateProvider,

View File

@ -66,14 +66,9 @@ func (s *Accounts) CheckAuth(user string, password []byte) (string, string, erro
continue continue
} }
account.service.telemetry.ReportSMTPAuthSuccess(context.Background())
return id, addrID, nil return id, addrID, nil
} }
for _, service := range s.accounts {
service.service.telemetry.ReportSMTPAuthFailed(user)
}
return "", "", ErrNoSuchUser return "", "", ErrNoSuchUser
} }

View File

@ -39,12 +39,6 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type Telemetry interface {
useridentity.Telemetry
ReportSMTPAuthSuccess(context.Context)
ReportSMTPAuthFailed(username string)
}
type Service struct { type Service struct {
userID string userID string
panicHandler async.PanicHandler panicHandler async.PanicHandler
@ -57,7 +51,6 @@ type Service struct {
bridgePassProvider useridentity.BridgePassProvider bridgePassProvider useridentity.BridgePassProvider
keyPassProvider useridentity.KeyPassProvider keyPassProvider useridentity.KeyPassProvider
identityState *useridentity.State identityState *useridentity.State
telemetry Telemetry
eventService userevents.Subscribable eventService userevents.Subscribable
subscription *userevents.EventChanneledSubscriber subscription *userevents.EventChanneledSubscriber
@ -76,7 +69,6 @@ func NewService(
reporter reporter.Reporter, reporter reporter.Reporter,
bridgePassProvider useridentity.BridgePassProvider, bridgePassProvider useridentity.BridgePassProvider,
keyPassProvider useridentity.KeyPassProvider, keyPassProvider useridentity.KeyPassProvider,
telemetry Telemetry,
eventService userevents.Subscribable, eventService userevents.Subscribable,
mode usertypes.AddressMode, mode usertypes.AddressMode,
identityState *useridentity.State, identityState *useridentity.State,
@ -99,7 +91,6 @@ func NewService(
bridgePassProvider: bridgePassProvider, bridgePassProvider: bridgePassProvider,
keyPassProvider: keyPassProvider, keyPassProvider: keyPassProvider,
telemetry: telemetry,
identityState: identityState, identityState: identityState,
eventService: eventService, eventService: eventService,

View File

@ -64,38 +64,3 @@ func (mr *MockIdentityProviderMockRecorder) GetUser(arg0 interface{}) *gomock.Ca
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockIdentityProvider)(nil).GetUser), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockIdentityProvider)(nil).GetUser), arg0)
} }
// MockTelemetry is a mock of Telemetry interface.
type MockTelemetry struct {
ctrl *gomock.Controller
recorder *MockTelemetryMockRecorder
}
// MockTelemetryMockRecorder is the mock recorder for MockTelemetry.
type MockTelemetryMockRecorder struct {
mock *MockTelemetry
}
// NewMockTelemetry creates a new mock instance.
func NewMockTelemetry(ctrl *gomock.Controller) *MockTelemetry {
mock := &MockTelemetry{ctrl: ctrl}
mock.recorder = &MockTelemetryMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockTelemetry) EXPECT() *MockTelemetryMockRecorder {
return m.recorder
}
// ReportConfigStatusFailure mocks base method.
func (m *MockTelemetry) ReportConfigStatusFailure(arg0 string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "ReportConfigStatusFailure", arg0)
}
// ReportConfigStatusFailure indicates an expected call of ReportConfigStatusFailure.
func (mr *MockTelemetryMockRecorder) ReportConfigStatusFailure(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportConfigStatusFailure", reflect.TypeOf((*MockTelemetry)(nil).ReportConfigStatusFailure), arg0)
}

View File

@ -50,7 +50,6 @@ type Service struct {
subscription *userevents.EventChanneledSubscriber subscription *userevents.EventChanneledSubscriber
bridgePassProvider BridgePassProvider bridgePassProvider BridgePassProvider
telemetry Telemetry
} }
func NewService( func NewService(
@ -58,7 +57,6 @@ func NewService(
eventPublisher events.EventPublisher, eventPublisher events.EventPublisher,
state *State, state *State,
bridgePassProvider BridgePassProvider, bridgePassProvider BridgePassProvider,
telemetry Telemetry,
) *Service { ) *Service {
subscriberName := fmt.Sprintf("identity-%v", state.User.ID) subscriberName := fmt.Sprintf("identity-%v", state.User.ID)
@ -73,7 +71,6 @@ func NewService(
}), }),
subscription: userevents.NewEventSubscriber(subscriberName), subscription: userevents.NewEventSubscriber(subscriberName),
bridgePassProvider: bridgePassProvider, bridgePassProvider: bridgePassProvider,
telemetry: telemetry,
} }
} }

View File

@ -361,10 +361,9 @@ func newTestService(_ *testing.T, mockCtrl *gomock.Controller) (*Service, *mocks
eventPublisher := mocks2.NewMockEventPublisher(mockCtrl) eventPublisher := mocks2.NewMockEventPublisher(mockCtrl)
provider := mocks.NewMockIdentityProvider(mockCtrl) provider := mocks.NewMockIdentityProvider(mockCtrl)
user := newTestUser() user := newTestUser()
telemetry := mocks.NewMockTelemetry(mockCtrl)
bridgePassProvider := NewFixedBridgePassProvider([]byte("hello")) bridgePassProvider := NewFixedBridgePassProvider([]byte("hello"))
service := NewService(subscribable, eventPublisher, NewState(*user, newTestAddresses(), provider), bridgePassProvider, telemetry) service := NewService(subscribable, eventPublisher, NewState(*user, newTestAddresses(), provider), bridgePassProvider)
return service, eventPublisher, provider return service, eventPublisher, provider
} }

View File

@ -1,22 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package useridentity
type Telemetry interface {
ReportConfigStatusFailure(errDetails string)
}

View File

@ -1,219 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package user
import (
"context"
"encoding/json"
"errors"
"github.com/ProtonMail/gluon/reporter"
"github.com/ProtonMail/proton-bridge/v3/internal/configstatus"
"github.com/ProtonMail/proton-bridge/v3/internal/kb"
)
func (user *User) SendConfigStatusSuccess(ctx context.Context) {
if user.configStatus.IsFromFailure() {
user.SendConfigStatusRecovery(ctx)
return
}
if !user.IsTelemetryEnabled(ctx) {
return
}
if !user.configStatus.IsPending() {
return
}
var builder configstatus.ConfigSuccessBuilder
success := builder.New(user.configStatus)
data, err := json.Marshal(success)
if err != nil {
if err := user.reporter.ReportMessageWithContext("Cannot parse config_success data.", reporter.Context{
"error": err,
}); err != nil {
user.log.WithError(err).Error("Failed to report config_success data parsing error.")
}
return
}
if err := user.SendTelemetry(ctx, data); err == nil {
user.log.Info("Configuration Status Success event sent.")
if err := user.configStatus.ApplySuccess(); err != nil {
user.log.WithError(err).Error("Failed to ApplySuccess on config_status.")
}
}
}
func (user *User) SendConfigStatusAbort(ctx context.Context, withTelemetry bool) {
if err := user.configStatus.Remove(); err != nil {
user.log.WithError(err).Error("Failed to remove config_status file.")
}
if !user.configStatus.IsPending() {
return
}
if !withTelemetry || !user.IsTelemetryEnabled(ctx) {
return
}
var builder configstatus.ConfigAbortBuilder
abort := builder.New(user.configStatus)
data, err := json.Marshal(abort)
if err != nil {
if err := user.reporter.ReportMessageWithContext("Cannot parse config_abort data.", reporter.Context{
"error": err,
}); err != nil {
user.log.WithError(err).Error("Failed to report config_abort data parsing error.")
}
return
}
if err := user.SendTelemetry(ctx, data); err == nil {
user.log.Info("Configuration Status Abort event sent.")
}
}
func (user *User) SendConfigStatusRecovery(ctx context.Context) {
if !user.configStatus.IsFromFailure() {
user.SendConfigStatusSuccess(ctx)
return
}
if !user.IsTelemetryEnabled(ctx) {
return
}
if !user.configStatus.IsPending() {
return
}
var builder configstatus.ConfigRecoveryBuilder
success := builder.New(user.configStatus)
data, err := json.Marshal(success)
if err != nil {
if err := user.reporter.ReportMessageWithContext("Cannot parse config_recovery data.", reporter.Context{
"error": err,
}); err != nil {
user.log.WithError(err).Error("Failed to report config_recovery data parsing error.")
}
return
}
if err := user.SendTelemetry(ctx, data); err == nil {
user.log.Info("Configuration Status Recovery event sent.")
if err := user.configStatus.ApplySuccess(); err != nil {
user.log.WithError(err).Error("Failed to ApplySuccess on config_status.")
}
}
}
func (user *User) SendConfigStatusProgress(ctx context.Context) {
if !user.IsTelemetryEnabled(ctx) {
return
}
if !user.configStatus.IsPending() {
return
}
var builder configstatus.ConfigProgressBuilder
progress := builder.New(user.configStatus)
if progress.Values.NbDay == 0 {
return
}
if progress.Values.NbDaySinceLast == 0 {
return
}
data, err := json.Marshal(progress)
if err != nil {
if err := user.reporter.ReportMessageWithContext("Cannot parse config_progress data.", reporter.Context{
"error": err,
}); err != nil {
user.log.WithError(err).Error("Failed to report config_progress data parsing error.")
}
return
}
if err := user.SendTelemetry(ctx, data); err == nil {
user.log.Info("Configuration Status Progress event sent.")
if err := user.configStatus.ApplyProgress(); err != nil {
user.log.WithError(err).Error("Failed to ApplyProgress on config_status.")
}
}
}
func (user *User) ReportConfigStatusFailure(errDetails string) {
if user.configStatus.IsPending() {
return
}
if err := user.configStatus.ApplyFailure(errDetails); err != nil {
user.log.WithError(err).Error("Failed to ApplyFailure on config_status.")
} else {
user.log.Info("Configuration Status is back to Pending due to Failure.")
}
}
func (user *User) ReportBugClicked() {
if !user.configStatus.IsPending() {
return
}
if err := user.configStatus.ReportClicked(); err != nil {
user.log.WithError(err).Error("Failed to log ReportClicked in config_status.")
}
}
func (user *User) ReportBugSent() {
if !user.configStatus.IsPending() {
return
}
if err := user.configStatus.ReportSent(); err != nil {
user.log.WithError(err).Error("Failed to log ReportSent in config_status.")
}
}
func (user *User) AutoconfigUsed(client string) {
if !user.configStatus.IsPending() {
return
}
if err := user.configStatus.AutoconfigUsed(client); err != nil {
user.log.WithError(err).Error("Failed to log Autoconf in config_status.")
}
}
func (user *User) ExternalLinkClicked(url string) {
if !user.configStatus.IsPending() {
return
}
const externalLinkWasClicked = "External link was clicked."
index, err := kb.GetArticleIndex(url)
if err != nil {
if errors.Is(err, kb.ErrArticleNotFound) {
user.log.WithField("report", false).WithField("url", url).Debug(externalLinkWasClicked)
} else {
user.log.WithError(err).Error("Failed to retrieve list of KB articles.")
}
return
}
if err := user.configStatus.RecordLinkClicked(index); err != nil {
user.log.WithError(err).Error("Failed to log LinkClicked in config_status.")
} else {
user.log.WithField("report", true).WithField("url", url).Debug(externalLinkWasClicked)
}
}

View File

@ -21,13 +21,11 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"path/filepath"
"time" "time"
"github.com/ProtonMail/gluon/async" "github.com/ProtonMail/gluon/async"
"github.com/ProtonMail/gluon/reporter" "github.com/ProtonMail/gluon/reporter"
"github.com/ProtonMail/go-proton-api" "github.com/ProtonMail/go-proton-api"
"github.com/ProtonMail/proton-bridge/v3/internal/configstatus"
"github.com/ProtonMail/proton-bridge/v3/internal/events" "github.com/ProtonMail/proton-bridge/v3/internal/events"
"github.com/ProtonMail/proton-bridge/v3/internal/safe" "github.com/ProtonMail/proton-bridge/v3/internal/safe"
"github.com/ProtonMail/proton-bridge/v3/internal/services/imapservice" "github.com/ProtonMail/proton-bridge/v3/internal/services/imapservice"
@ -78,10 +76,7 @@ type User struct {
maxSyncMemory uint64 maxSyncMemory uint64
panicHandler async.PanicHandler panicHandler async.PanicHandler
configStatus *configstatus.ConfigurationStatus
telemetryManager telemetry.Availability telemetryManager telemetry.Availability
// goStatusProgress triggers a check/sending if progress is needed.
goStatusProgress func()
eventService *userevents.Service eventService *userevents.Service
identityService *useridentity.Service identityService *useridentity.Service
@ -104,7 +99,6 @@ func New(
crashHandler async.PanicHandler, crashHandler async.PanicHandler,
showAllMail bool, showAllMail bool,
maxSyncMemory uint64, maxSyncMemory uint64,
statsDir string,
telemetryManager telemetry.Availability, telemetryManager telemetry.Availability,
imapServerManager imapservice.IMAPServerManager, imapServerManager imapservice.IMAPServerManager,
smtpServerManager smtp.ServerManager, smtpServerManager smtp.ServerManager,
@ -125,7 +119,6 @@ func New(
crashHandler, crashHandler,
showAllMail, showAllMail,
maxSyncMemory, maxSyncMemory,
statsDir,
telemetryManager, telemetryManager,
imapServerManager, imapServerManager,
smtpServerManager, smtpServerManager,
@ -159,7 +152,6 @@ func newImpl(
crashHandler async.PanicHandler, crashHandler async.PanicHandler,
showAllMail bool, showAllMail bool,
maxSyncMemory uint64, maxSyncMemory uint64,
statsDir string,
telemetryManager telemetry.Availability, telemetryManager telemetry.Availability,
imapServerManager imapservice.IMAPServerManager, imapServerManager imapservice.IMAPServerManager,
smtpServerManager smtp.ServerManager, smtpServerManager smtp.ServerManager,
@ -198,12 +190,6 @@ func newImpl(
"numLabels": len(apiLabels), "numLabels": len(apiLabels),
}).Info("Creating user object") }).Info("Creating user object")
configStatusFile := filepath.Join(statsDir, apiUser.ID+".json")
configStatus, err := configstatus.LoadConfigurationStatus(configStatusFile)
if err != nil {
return nil, fmt.Errorf("failed to init configuration status file: %w", err)
}
sendRecorder := sendrecorder.NewSendRecorder(sendrecorder.SendEntryExpiry) sendRecorder := sendrecorder.NewSendRecorder(sendrecorder.SendEntryExpiry)
// Create the user object. // Create the user object.
@ -225,7 +211,6 @@ func newImpl(
panicHandler: crashHandler, panicHandler: crashHandler,
configStatus: configStatus,
telemetryManager: telemetryManager, telemetryManager: telemetryManager,
serviceGroup: orderedtasks.NewOrderedCancelGroup(crashHandler), serviceGroup: orderedtasks.NewOrderedCancelGroup(crashHandler),
@ -248,7 +233,7 @@ func newImpl(
addressMode := usertypes.VaultToAddressMode(encVault.AddressMode()) addressMode := usertypes.VaultToAddressMode(encVault.AddressMode())
user.identityService = useridentity.NewService(user.eventService, user, identityState, encVault, user) user.identityService = useridentity.NewService(user.eventService, user, identityState, encVault)
user.telemetryService = telemetryservice.NewService(apiUser.ID, client, user.eventService) user.telemetryService = telemetryservice.NewService(apiUser.ID, client, user.eventService)
@ -260,7 +245,6 @@ func newImpl(
reporter, reporter,
encVault, encVault,
encVault, encVault,
user,
user.eventService, user.eventService,
addressMode, addressMode,
identityState.Clone(), identityState.Clone(),
@ -279,7 +263,6 @@ func newImpl(
encVault, encVault,
crashHandler, crashHandler,
sendRecorder, sendRecorder,
user,
reporter, reporter,
addressMode, addressMode,
eventSubscription, eventSubscription,
@ -291,12 +274,6 @@ func newImpl(
user.notificationService = notifications.NewService(user.id, user.eventService, user, notificationStore, getFlagValueFn, observabilityService) user.notificationService = notifications.NewService(user.id, user.eventService, user, notificationStore, getFlagValueFn, observabilityService)
// Check for status_progress when triggered.
user.goStatusProgress = user.tasks.PeriodicOrTrigger(configstatus.ProgressCheckInterval, 0, func(ctx context.Context) {
user.SendConfigStatusProgress(ctx)
})
defer user.goStatusProgress()
// When we receive an auth object, we update it in the vault. // When we receive an auth object, we update it in the vault.
// This will be used to authorize the user on the next run. // This will be used to authorize the user on the next run.
user.client.AddAuthHandler(func(auth proton.Auth) { user.client.AddAuthHandler(func(auth proton.Auth) {
@ -698,19 +675,6 @@ func (user *User) SendTelemetry(ctx context.Context, data []byte) error {
return nil return nil
} }
func (user *User) ReportSMTPAuthFailed(username string) {
emails := user.Emails()
for _, mail := range emails {
if mail == username {
user.ReportConfigStatusFailure("SMTP invalid username or password")
}
}
}
func (user *User) ReportSMTPAuthSuccess(ctx context.Context) {
user.SendConfigStatusSuccess(ctx)
}
func (user *User) GetSMTPService() *smtp.Service { func (user *User) GetSMTPService() *smtp.Service {
return user.smtpService return user.smtpService
} }

View File

@ -160,7 +160,6 @@ func withUser(tb testing.TB, ctx context.Context, _ *server.Server, m *proton.Ma
nil, nil,
true, true,
vault.DefaultMaxSyncMemory, vault.DefaultMaxSyncMemory,
tb.TempDir(),
manager, manager,
nullIMAPServerManager, nullIMAPServerManager,
nullSMTPServerManager, nullSMTPServerManager,

View File

@ -1,187 +0,0 @@
// Copyright (c) 2024 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 <https://www.gnu.org/licenses/>.
package tests
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
"github.com/ProtonMail/go-proton-api"
"github.com/ProtonMail/go-proton-api/server"
"github.com/ProtonMail/proton-bridge/v3/internal/configstatus"
"github.com/sirupsen/logrus"
)
func (s *scenario) configStatusFileExistForUser(username string) error {
configStatusFile, err := getConfigStatusFile(s.t, username)
if err != nil {
return err
}
if _, err := os.Stat(configStatusFile); err != nil {
return err
}
return nil
}
func (s *scenario) configStatusIsPendingForUser(username string) error {
data, err := loadConfigStatusFile(s.t, username)
if err != nil {
return err
}
if data.DataV1.PendingSince.IsZero() {
return fmt.Errorf("expected ConfigStatus pending but got success instead")
}
return nil
}
func (s *scenario) configStatusIsPendingWithFailureForUser(username string) error {
data, err := loadConfigStatusFile(s.t, username)
if err != nil {
return err
}
if data.DataV1.PendingSince.IsZero() {
return fmt.Errorf("expected ConfigStatus pending but got success instead")
}
if data.DataV1.FailureDetails == "" {
return fmt.Errorf("expected ConfigStatus pending with failure but got no failure instead")
}
return nil
}
func (s *scenario) configStatusSucceedForUser(username string) error {
data, err := loadConfigStatusFile(s.t, username)
if err != nil {
return err
}
if !data.DataV1.PendingSince.IsZero() {
return fmt.Errorf("expected ConfigStatus success but got pending since %s", data.DataV1.PendingSince)
}
return nil
}
func (s *scenario) configStatusEventIsEventuallySendXTime(event string, number int) error {
return eventually(func() error {
err := s.checkEventSentForUser(event, number)
logrus.WithError(err).Trace("Matching eventually")
return err
})
}
func (s *scenario) configStatusEventIsNotSendMoreThanXTime(event string, number int) error {
if err := eventually(func() error {
err := s.checkEventSentForUser(event, number+1)
logrus.WithError(err).Trace("Matching eventually")
return err
}); err == nil {
return fmt.Errorf("expected %s to be sent %d but catch %d", event, number, number+1)
}
return nil
}
func (s *scenario) forceConfigStatusProgressToBeSentForUser(username string) error {
configStatusFile, err := getConfigStatusFile(s.t, username)
if err != nil {
return err
}
data, err := loadConfigStatusFile(s.t, username)
if err != nil {
return err
}
data.DataV1.PendingSince = time.Now().AddDate(0, 0, -2)
data.DataV1.LastProgress = time.Now().AddDate(0, 0, -1)
f, err := os.Create(configStatusFile)
if err != nil {
return err
}
defer func() { _ = f.Close() }()
return json.NewEncoder(f).Encode(data)
}
func (s *scenario) checkEventSentForUser(event string, number int) error {
calls, err := getLastTelemetryEventSent(s.t, event)
if err != nil {
return err
}
if len(calls) != number {
return fmt.Errorf("expected %s to be sent %d but catch %d", event, number, len(calls))
}
return nil
}
func getConfigStatusFile(t *testCtx, username string) (string, error) {
userID := t.getUserByName(username).getUserID()
statsDir, err := t.locator.ProvideStatsPath()
if err != nil {
return "", fmt.Errorf("failed to get Statistics directory: %w", err)
}
return filepath.Join(statsDir, userID+".json"), nil
}
func loadConfigStatusFile(t *testCtx, username string) (configstatus.ConfigurationStatusData, error) {
data := configstatus.ConfigurationStatusData{}
configStatusFile, err := getConfigStatusFile(t, username)
if err != nil {
return data, err
}
if _, err := os.Stat(configStatusFile); err != nil {
return data, err
}
f, err := os.Open(configStatusFile)
if err != nil {
return data, err
}
defer func() { _ = f.Close() }()
err = json.NewDecoder(f).Decode(&data)
return data, err
}
func getLastTelemetryEventSent(t *testCtx, event string) ([]server.Call, error) {
var matches []server.Call
calls, err := t.getAllCalls("POST", "/data/v1/stats")
if err != nil {
return matches, err
}
for _, call := range calls {
var req proton.SendStatsReq
if err := json.Unmarshal(call.RequestBody, &req); err != nil {
continue
}
if req.Event == event {
matches = append(matches, call)
}
}
return matches, err
}

View File

@ -1,79 +0,0 @@
Feature: Configuration Status Telemetry
Background:
Given there exists an account with username "[user:user]" and password "password"
Then it succeeds
When bridge starts
Then it succeeds
Scenario: Init config status on user addition
Then bridge telemetry feature is enabled
When the user logs in with username "[user:user]" and password "password"
Then config status file exist for user "[user:user]"
And config status is pending for user "[user:user]"
Scenario: Config Status Success on IMAP
Then bridge telemetry feature is enabled
When the user logs in with username "[user:user]" and password "password"
Then config status file exist for user "[user:user]"
And config status is pending for user "[user:user]"
When user "[user:user]" connects and authenticates IMAP client "1"
Then config status succeed for user "[user:user]"
And config status event "bridge_config_success" is eventually send 1 time
Scenario: Config Status Success on SMTP
Then bridge telemetry feature is enabled
When the user logs in with username "[user:user]" and password "password"
Then config status file exist for user "[user:user]"
And config status is pending for user "[user:user]"
When user "[user:user]" connects and authenticates SMTP client "1"
Then config status succeed for user "[user:user]"
And config status event "bridge_config_success" is eventually send 1 time
Scenario: Config Status Success send only once
Then bridge telemetry feature is enabled
When the user logs in with username "[user:user]" and password "password"
Then config status file exist for user "[user:user]"
And config status is pending for user "[user:user]"
When user "[user:user]" connects and authenticates IMAP client "1"
Then config status succeed for user "[user:user]"
And config status event "bridge_config_success" is eventually send 1 time
When user "[user:user]" connects and authenticates IMAP client "2"
Then config status event "bridge_config_success" is not send more than 1 time
Scenario: Config Status Abort
Then bridge telemetry feature is enabled
When the user logs in with username "[user:user]" and password "password"
And user "[user:user]" finishes syncing
Then config status file exist for user "[user:user]"
And config status is pending for user "[user:user]"
When user "[user:user]" is deleted
Then config status event "bridge_config_abort" is eventually send 1 time
Scenario: Config Status Recovery from deauth
Then bridge telemetry feature is enabled
When the user logs in with username "[user:user]" and password "password"
And user "[user:user]" connects and authenticates IMAP client "1"
Then config status succeed for user "[user:user]"
When the auth of user "[user:user]" is revoked
Then bridge sends a deauth event for user "[user:user]"
Then config status is pending with failure for user "[user:user]"
When the user logs in with username "[user:user]" and password "password"
And user "[user:user]" connects and authenticates IMAP client "1"
Then config status succeed for user "[user:user]"
And config status event "bridge_config_recovery" is eventually send 1 time
Scenario: Config Status Progress
Then bridge telemetry feature is enabled
When the user logs in with username "[user:user]" and password "password"
And config status is pending for user "[user:user]"
And bridge stops
And force config status progress to be sent for user"[user:user]"
And bridge starts
Then config status event "bridge_config_progress" is eventually send 1 time

View File

@ -203,13 +203,6 @@ func (s *scenario) steps(ctx *godog.ScenarioContext) {
ctx.Step(`^bridge needs to send heartbeat`, s.bridgeNeedsToSendHeartbeat) ctx.Step(`^bridge needs to send heartbeat`, s.bridgeNeedsToSendHeartbeat)
ctx.Step(`^bridge do not need to send heartbeat`, s.bridgeDoNotNeedToSendHeartbeat) ctx.Step(`^bridge do not need to send heartbeat`, s.bridgeDoNotNeedToSendHeartbeat)
ctx.Step(`^heartbeat is not whitelisted`, s.heartbeatIsNotwhitelisted) ctx.Step(`^heartbeat is not whitelisted`, s.heartbeatIsNotwhitelisted)
ctx.Step(`^config status file exist for user "([^"]*)"$`, s.configStatusFileExistForUser)
ctx.Step(`^config status is pending for user "([^"]*)"$`, s.configStatusIsPendingForUser)
ctx.Step(`^config status is pending with failure for user "([^"]*)"$`, s.configStatusIsPendingWithFailureForUser)
ctx.Step(`^config status succeed for user "([^"]*)"$`, s.configStatusSucceedForUser)
ctx.Step(`^config status event "([^"]*)" is eventually send (\d+) time`, s.configStatusEventIsEventuallySendXTime)
ctx.Step(`^config status event "([^"]*)" is not send more than (\d+) time`, s.configStatusEventIsNotSendMoreThanXTime)
ctx.Step(`^force config status progress to be sent for user"([^"]*)"$`, s.forceConfigStatusProgressToBeSentForUser)
// ==== CONTACT ==== // ==== CONTACT ====
ctx.Step(`^user "([^"]*)" has contact "([^"]*)" with name "([^"]*)"$`, s.userHasContactWithName) ctx.Step(`^user "([^"]*)" has contact "([^"]*)" with name "([^"]*)"$`, s.userHasContactWithName)