Truncate message times to the second when using the FS message store

The FS message store truncates message times to the second.

This means that a message sent out as 2020-01-01T00:00:00.123Z could be
sent later as part of a CHATHISTORY batch as 2020-01-01T00:00:00.000Z,
which could cause issues in clients.

One such issue is a client sending a MARKREAD for
2020-01-01T00:00:00.000Z, with another client considering the
2020-01-01T00:00:00.123Z message it has as unread.

This fixes the issue by truncating all message times to the second when
using the FS message store.
This commit is contained in:
delthas 2022-10-18 14:57:33 +02:00 committed by Simon Ser
parent 87b2d32682
commit 897c21dbb4
4 changed files with 25 additions and 5 deletions

View File

@ -2281,7 +2281,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
dc.logger.Printf("broadcasting bouncer-wide %v: %v", msg.Command, text) dc.logger.Printf("broadcasting bouncer-wide %v: %v", msg.Command, text)
broadcastTags := tags.Copy() broadcastTags := tags.Copy()
broadcastTags["time"] = xirc.FormatServerTime(time.Now()) broadcastTags["time"] = dc.user.FormatServerTime(time.Now())
broadcastMsg := &irc.Message{ broadcastMsg := &irc.Message{
Tags: broadcastTags, Tags: broadcastTags,
Prefix: servicePrefix, Prefix: servicePrefix,
@ -2307,7 +2307,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
if casemapASCII(name) == serviceNickCM { if casemapASCII(name) == serviceNickCM {
if dc.caps.IsEnabled("echo-message") { if dc.caps.IsEnabled("echo-message") {
echoTags := tags.Copy() echoTags := tags.Copy()
echoTags["time"] = xirc.FormatServerTime(time.Now()) echoTags["time"] = dc.user.FormatServerTime(time.Now())
dc.SendMessage(&irc.Message{ dc.SendMessage(&irc.Message{
Tags: echoTags, Tags: echoTags,
Prefix: dc.prefix(), Prefix: dc.prefix(),
@ -2351,7 +2351,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
} }
echoTags := tags.Copy() echoTags := tags.Copy()
echoTags["time"] = xirc.FormatServerTime(time.Now()) echoTags["time"] = dc.user.FormatServerTime(time.Now())
if uc.account != "" { if uc.account != "" {
echoTags["account"] = uc.account echoTags["account"] = uc.account
} }

View File

@ -96,6 +96,11 @@ var (
_ RenameNetworkStore = (*fsMessageStore)(nil) _ RenameNetworkStore = (*fsMessageStore)(nil)
) )
func IsFSStore(store Store) bool {
_, ok := store.(*fsMessageStore)
return ok
}
func NewFSStore(root string, user *database.User) *fsMessageStore { func NewFSStore(root string, user *database.User) *fsMessageStore {
return &fsMessageStore{ return &fsMessageStore{
root: filepath.Join(root, escapeFilename(user.Username)), root: filepath.Join(root, escapeFilename(user.Username)),

View File

@ -486,8 +486,12 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
msg.Prefix = uc.serverPrefix msg.Prefix = uc.serverPrefix
} }
if _, ok := msg.Tags["time"]; !ok && !isNumeric(msg.Command) { if !isNumeric(msg.Command) {
msg.Tags["time"] = xirc.FormatServerTime(time.Now()) t, err := time.Parse(xirc.ServerTimeLayout, string(msg.Tags["time"]))
if err != nil {
t = time.Now()
}
msg.Tags["time"] = uc.user.FormatServerTime(t)
} }
switch msg.Command { switch msg.Command {

11
user.go
View File

@ -13,6 +13,8 @@ import (
"strings" "strings"
"time" "time"
"git.sr.ht/~emersion/soju/xirc"
"github.com/SherClockHolmes/webpush-go" "github.com/SherClockHolmes/webpush-go"
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
@ -1114,6 +1116,15 @@ func (u *user) hasPersistentMsgStore() bool {
return !msgstore.IsMemoryStore(u.msgStore) return !msgstore.IsMemoryStore(u.msgStore)
} }
func (u *user) FormatServerTime(t time.Time) string {
if u.msgStore != nil && msgstore.IsFSStore(u.msgStore) {
// The FS message store truncates message timestamps to the second,
// so truncate them here to get consistent timestamps.
t = t.Truncate(time.Second)
}
return xirc.FormatServerTime(t)
}
// localAddrForHost returns the local address to use when connecting to host. // localAddrForHost returns the local address to use when connecting to host.
// A nil address is returned when the OS should automatically pick one. // A nil address is returned when the OS should automatically pick one.
func (u *user) localTCPAddrForHost(ctx context.Context, host string) (*net.TCPAddr, error) { func (u *user) localTCPAddrForHost(ctx context.Context, host string) (*net.TCPAddr, error) {