From 26348c9353b008ceee74f0359a2063978423511f Mon Sep 17 00:00:00 2001 From: Mark Mennell Date: Sat, 16 May 2026 16:16:06 +0800 Subject: [PATCH] ensure sha256 populated if sending --- cmd/fmsgd/sender.go | 20 ++++++++++++++------ cmd/fmsgd/store.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/cmd/fmsgd/sender.go b/cmd/fmsgd/sender.go index a33a1e8..fb30d3a 100644 --- a/cmd/fmsgd/sender.go +++ b/cmd/fmsgd/sender.go @@ -366,12 +366,20 @@ func deliverMessage(target pendingTarget) { } } - // Ensure sha256 is populated for outgoing messages so future pid lookups - // (e.g. add-to notifications referencing this message) can find it. - msgHash, err := h.GetMessageHash() - if err != nil { - log.Printf("ERROR: sender: computing message hash for msg %d: %s", target.MsgID, err) - return + // Ensure sha256 is populated for this message so future pid lookups + // (e.g. add-to notifications or replies referencing it) can find it. + // For an add-to message the row being delivered IS the shared message, + // whose original-form hash loadMsg has already placed in h.Pid; calling + // h.GetMessageHash() here would instead hash the add-to variant. + var msgHash []byte + if h.Flags&FlagHasAddTo != 0 { + msgHash = h.Pid + } else { + msgHash, err = h.GetMessageHash() + if err != nil { + log.Printf("ERROR: sender: computing message hash for msg %d: %s", target.MsgID, err) + return + } } if _, err := tx.Exec(`UPDATE msg SET sha256 = $1 WHERE id = $2 AND sha256 IS NULL`, msgHash, target.MsgID); err != nil { diff --git a/cmd/fmsgd/store.go b/cmd/fmsgd/store.go index b2689f2..825f7f1 100644 --- a/cmd/fmsgd/store.go +++ b/cmd/fmsgd/store.go @@ -541,6 +541,43 @@ func loadMsg(tx *sql.Tx, msgID int64) (*FMsgHeader, error) { // When add-to recipients exist, the wire pid references the message being // shared, not that message's parent. This keeps add-to on replies pointing // at the reply payload rather than the root message. + // + // If that shared message has no persisted sha256 (e.g. it was delivered + // locally only and so never sent over the wire), compute its original-form + // message hash now. Without it the add-to wire header would omit the pid + // field entirely and be rejected as invalid (SPEC §10.3 step 7, §12). + if len(allAddTo) > 0 && len(msgHash) == 0 { + origFlags := uint8(0) + if noReply { + origFlags |= FlagNoReply + } + if isImportant { + origFlags |= FlagImportant + } + if isDeflate { + origFlags |= FlagDeflate + } + if len(pid) > 0 { + origFlags |= FlagHasPid + } + orig := &FMsgHeader{ + Version: uint8(version), + Flags: origFlags, + Pid: pid, + From: *from, + To: allTo, + Timestamp: timeSent, + Topic: topic, + Type: typ, + Size: uint32(size), + Attachments: attachments, + Filepath: filepath, + } + msgHash, err = orig.GetMessageHash() + if err != nil { + return nil, fmt.Errorf("compute original message hash for msg %d: %w", msgID, err) + } + } pid = wirePidForLoadedMessage(pid, msgHash, len(allAddTo) > 0) var addToFrom *FMsgAddress