chore: merge release Quebec into Rialto

Fix: GODT-2614
Fix: GODT-2616
This commit is contained in:
Jakub
2023-05-10 08:50:20 +02:00
115 changed files with 5827 additions and 4030 deletions

View File

@ -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)

View File

@ -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)
}

View File

@ -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 {

View File

@ -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
}
}
}
}

View File

@ -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")

View File

@ -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) {

View File

@ -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()