mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-23 18:36:46 +00:00
chore: merge release Quebec into Rialto
Fix: GODT-2614 Fix: GODT-2616
This commit is contained in:
@ -650,15 +650,28 @@ func (user *User) handleUpdateMessageEvent(ctx context.Context, message proton.M
|
||||
"subject": logging.Sensitive(message.Subject),
|
||||
}).Info("Handling message updated event")
|
||||
|
||||
flags := imap.NewFlagSet()
|
||||
|
||||
if message.Seen() {
|
||||
flags.AddToSelf(imap.FlagSeen)
|
||||
}
|
||||
|
||||
if message.Starred() {
|
||||
flags.AddToSelf(imap.FlagFlagged)
|
||||
}
|
||||
|
||||
if message.IsDraft() {
|
||||
flags.AddToSelf(imap.FlagDraft)
|
||||
}
|
||||
|
||||
if message.IsRepliedAll == true || message.IsReplied == true { //nolint: gosimple
|
||||
flags.AddToSelf(imap.FlagAnswered)
|
||||
}
|
||||
|
||||
update := imap.NewMessageMailboxesUpdated(
|
||||
imap.MessageID(message.ID),
|
||||
mapTo[string, imap.MailboxID](wantLabels(user.apiLabels, message.LabelIDs)),
|
||||
imap.MessageCustomFlags{
|
||||
Seen: message.Seen(),
|
||||
Flagged: message.Starred(),
|
||||
Draft: message.IsDraft(),
|
||||
Answered: message.IsRepliedAll == true || message.IsReplied == true, //nolint: gosimple
|
||||
},
|
||||
flags,
|
||||
)
|
||||
|
||||
user.updateCh[message.AddressID].Enqueue(update)
|
||||
|
||||
@ -20,6 +20,7 @@ package user
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"sync/atomic"
|
||||
@ -350,7 +351,13 @@ func (conn *imapConnector) CreateMessage(
|
||||
wantFlags = wantFlags.Add(proton.MessageFlagReplied)
|
||||
}
|
||||
|
||||
return conn.importMessage(ctx, literal, wantLabelIDs, wantFlags, unread)
|
||||
msg, literal, err := conn.importMessage(ctx, literal, wantLabelIDs, wantFlags, unread)
|
||||
if err != nil && errors.Is(err, proton.ErrImportSizeExceeded) {
|
||||
// Remap error so that Gluon does not put this message in the recovery mailbox.
|
||||
err = fmt.Errorf("%v: %w", err, connector.ErrMessageSizeExceedsLimits)
|
||||
}
|
||||
|
||||
return msg, literal, err
|
||||
}
|
||||
|
||||
func (conn *imapConnector) GetMessageLiteral(ctx context.Context, id imap.MessageID) ([]byte, error) {
|
||||
@ -626,7 +633,7 @@ func (conn *imapConnector) createDraft(ctx context.Context, literal []byte, addr
|
||||
return proton.Message{}, fmt.Errorf("failed to create parser: %w", err)
|
||||
}
|
||||
|
||||
message, err := message.ParseWithParser(parser)
|
||||
message, err := message.ParseWithParser(parser, true)
|
||||
if err != nil {
|
||||
return proton.Message{}, fmt.Errorf("failed to parse message: %w", err)
|
||||
}
|
||||
|
||||
@ -18,10 +18,7 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
@ -219,73 +216,7 @@ func (h *sendRecorder) getWaitCh(hash string) (<-chan struct{}, bool) {
|
||||
// - the Content-Disposition header of each (leaf) part,
|
||||
// - the (decoded) body of each part.
|
||||
func getMessageHash(b []byte) (string, error) {
|
||||
section := rfc822.Parse(b)
|
||||
|
||||
header, err := section.ParseHeader()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
|
||||
if _, err := h.Write([]byte(header.Get("Subject"))); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := h.Write([]byte(header.Get("From"))); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := h.Write([]byte(header.Get("To"))); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := h.Write([]byte(header.Get("Cc"))); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := h.Write([]byte(header.Get("Reply-To"))); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := h.Write([]byte(header.Get("In-Reply-To"))); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := section.Walk(func(section *rfc822.Section) error {
|
||||
children, err := section.Children()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(children) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
header, err := section.ParseHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := h.Write([]byte(header.Get("Content-Type"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := h.Write([]byte(header.Get("Content-Disposition"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body := section.Body()
|
||||
body = bytes.ReplaceAll(body, []byte{'\r'}, nil)
|
||||
body = bytes.TrimSpace(body)
|
||||
if _, err := h.Write(body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
|
||||
return rfc822.GetMessageHash(b)
|
||||
}
|
||||
|
||||
func matchToList(a, b []string) bool {
|
||||
|
||||
@ -140,7 +140,7 @@ func (user *User) sendMail(authID string, from string, to []string, r io.Reader)
|
||||
}
|
||||
|
||||
// Parse the message we want to send (after we have attached the public key).
|
||||
message, err := message.ParseWithParser(parser)
|
||||
message, err := message.ParseWithParser(parser, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse message: %w", err)
|
||||
}
|
||||
@ -300,7 +300,9 @@ func getParentID(
|
||||
}
|
||||
|
||||
// If no parent was found, try to find it in the last external reference.
|
||||
// There can be multiple messages with the same external ID; in this case, we don't pick any parent.
|
||||
// There can be multiple messages with the same external ID; in this case, we first look if
|
||||
// there is a single one sent by this account (with the `MessageFlagSent` flag set), if yes,
|
||||
// then pick that, otherwise don't pick any parent.
|
||||
if parentID == "" && len(external) > 0 {
|
||||
var addrID string
|
||||
|
||||
@ -316,8 +318,21 @@ func getParentID(
|
||||
return "", fmt.Errorf("failed to get message metadata: %w", err)
|
||||
}
|
||||
|
||||
if len(metadata) == 1 {
|
||||
switch len(metadata) {
|
||||
case 1:
|
||||
// found exactly one parent
|
||||
parentID = metadata[0].ID
|
||||
case 0:
|
||||
// found no parents
|
||||
default:
|
||||
// found multiple parents, search through metadata to try to find a singular parent that
|
||||
// was sent by this account.
|
||||
for _, metadata := range metadata {
|
||||
if metadata.Flags.Has(proton.MessageFlagSent) {
|
||||
parentID = metadata.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -262,7 +262,7 @@ func (user *User) syncMessages(
|
||||
syncStartTime := time.Now()
|
||||
defer func() { logrus.WithField("duration", time.Since(syncStartTime)).Info("Message sync completed") }()
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
user.log.WithFields(logrus.Fields{
|
||||
"messages": len(messageIDs),
|
||||
"numCPU": runtime.NumCPU(),
|
||||
}).Info("Starting message sync")
|
||||
|
||||
@ -119,6 +119,12 @@ func New(
|
||||
return nil, fmt.Errorf("failed to get labels: %w", err)
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"userID": apiUser.ID,
|
||||
"numAddr": len(apiAddrs),
|
||||
"numLabels": len(apiLabels),
|
||||
}).Info("Creating user object")
|
||||
|
||||
// Create the user object.
|
||||
user := &User{
|
||||
log: logrus.WithField("userID", apiUser.ID),
|
||||
@ -591,6 +597,36 @@ func (user *User) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
// IsTelemetryEnabled check if the telemetry is enabled or disabled for this user.
|
||||
func (user *User) IsTelemetryEnabled(ctx context.Context) bool {
|
||||
settings, err := user.client.GetUserSettings(ctx)
|
||||
if err != nil {
|
||||
user.log.WithError(err).Error("Failed to retrieve API user Settings")
|
||||
return false
|
||||
}
|
||||
return settings.Telemetry == proton.SettingEnabled
|
||||
}
|
||||
|
||||
// SendTelemetry send telemetry request.
|
||||
func (user *User) SendTelemetry(ctx context.Context, data []byte) error {
|
||||
var req proton.SendStatsReq
|
||||
if err := json.Unmarshal(data, &req); err != nil {
|
||||
user.log.WithError(err).Error("Failed to build telemetry request.")
|
||||
if err := user.reporter.ReportMessageWithContext("Failed to build telemetry request.", reporter.Context{
|
||||
"error": err,
|
||||
}); err != nil {
|
||||
logrus.WithError(err).Error("Failed to report telemetry request build error")
|
||||
}
|
||||
return err
|
||||
}
|
||||
err := user.client.SendDataEvent(ctx, req)
|
||||
if err != nil {
|
||||
user.log.WithError(err).Error("Failed to send telemetry.")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// initUpdateCh initializes the user's update channels in the given address mode.
|
||||
// It is assumed that user.apiAddrs and user.updateCh are already locked.
|
||||
func (user *User) initUpdateCh(mode vault.AddressMode) {
|
||||
|
||||
@ -80,6 +80,23 @@ func TestUser_AddressMode(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestUser_Telemetry(t *testing.T) {
|
||||
withAPI(t, context.Background(), func(ctx context.Context, s *server.Server, m *proton.Manager) {
|
||||
withAccount(t, s, "username", "password", []string{}, func(string, []string) {
|
||||
withUser(t, ctx, s, m, "username", "password", func(user *User) {
|
||||
// By default, user should have Telemetry enabled.
|
||||
telemetry := user.IsTelemetryEnabled(ctx)
|
||||
require.Equal(t, true, telemetry)
|
||||
|
||||
user.client.Close()
|
||||
// If telemetry cannot be retrieved it is disabled.
|
||||
telemetry = user.IsTelemetryEnabled(ctx)
|
||||
require.Equal(t, false, telemetry)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func withAPI(_ testing.TB, ctx context.Context, fn func(context.Context, *server.Server, *proton.Manager)) { //nolint:revive
|
||||
server := server.New()
|
||||
defer server.Close()
|
||||
|
||||
Reference in New Issue
Block a user