Support legacy MAM versions

This commit is contained in:
Bohdan Horbeshko 2025-08-25 10:51:26 -04:00
parent 0f10dbff8c
commit c4341433d6
3 changed files with 305 additions and 40 deletions

View file

@ -376,6 +376,30 @@ type MAM2Query struct {
FlipPage *FlipPage `xml:"flip-page"`
}
// MAM1Query is from XEP-0313
type MAM1Query struct {
XMLName xml.Name `xml:"urn:xmpp:mam:1 query"`
Form *stanza.Form `xml:"jabber:x:data x"`
QueryId string `xml:"queryid,attr,omitempty"`
ResultSet *stanza.ResultSet `xml:"set,omitempty"`
}
// MAM0Query is from XEP-0313
type MAM0Query struct {
XMLName xml.Name `xml:"urn:xmpp:mam:0 query"`
Form *stanza.Form `xml:"jabber:x:data x"`
QueryId string `xml:"queryid,attr,omitempty"`
ResultSet *stanza.ResultSet `xml:"set,omitempty"`
}
type MAMQuery interface {
Namespace() string
GetForm() *stanza.Form
GetQueryId() string
GetSet() *stanza.ResultSet
GetFlipPage() *FlipPage
}
// FlipPage is an extended element from XEP-0313
type FlipPage struct {
XMLName xml.Name `xml:"flip-page"`
@ -396,6 +420,22 @@ type MAM2MessageResult struct {
Id string `xml:"id,attr,omitempty"`
}
// MAM1MessageResult is from XEP-0313
type MAM1MessageResult struct {
XMLName xml.Name `xml:"urn:xmpp:mam:1 result"`
Forwarded *ForwardedMessage `xml:"urn:xmpp:forward:0 forwarded,omitempty"`
QueryId string `xml:"queryid,attr,omitempty"`
Id string `xml:"id,attr,omitempty"`
}
// MAM0MessageResult is from XEP-0313
type MAM0MessageResult struct {
XMLName xml.Name `xml:"urn:xmpp:mam:0 result"`
Forwarded *ForwardedMessage `xml:"urn:xmpp:forward:0 forwarded,omitempty"`
QueryId string `xml:"queryid,attr,omitempty"`
Id string `xml:"id,attr,omitempty"`
}
// MAM2Fin is from XEP-0313
type MAM2Fin struct {
XMLName xml.Name `xml:"urn:xmpp:mam:2 fin"`
@ -404,6 +444,22 @@ type MAM2Fin struct {
Stable bool `xml:"stable,attr"`
}
// MAM1Fin is from XEP-0313
type MAM1Fin struct {
XMLName xml.Name `xml:"urn:xmpp:mam:1 fin"`
ResultSet *stanza.ResultSet `xml:"set,omitempty"`
Complete bool `xml:"complete,attr,omitempty"`
Stable bool `xml:"stable,attr"`
}
// MAM0Fin is from XEP-0313
type MAM0Fin struct {
XMLName xml.Name `xml:"urn:xmpp:mam:0 fin"`
ResultSet *stanza.ResultSet `xml:"set,omitempty"`
Complete bool `xml:"complete,attr,omitempty"`
Stable bool `xml:"stable,attr"`
}
// MAM2Metadata is from XEP-0313
type MAM2Metadata struct {
XMLName xml.Name `xml:"urn:xmpp:mam:2 metadata"`
@ -536,11 +592,76 @@ func (c MAM2Query) Namespace() string {
return c.XMLName.Space
}
// GetForm obtains the query form
func (c MAM2Query) GetForm() *stanza.Form {
return c.Form
}
// GetQueryId obtains the query id
func (c MAM2Query) GetQueryId() string {
return c.QueryId
}
// GetSet getsets!
func (c MAM2Query) GetSet() *stanza.ResultSet {
return c.ResultSet
}
// GetFlipPage obtains the flip-page element
func (c MAM2Query) GetFlipPage() *FlipPage {
return c.FlipPage
}
// Namespace is a namespace!
func (c MAM1Query) Namespace() string {
return c.XMLName.Space
}
// GetForm obtains the query form
func (c MAM1Query) GetForm() *stanza.Form {
return c.Form
}
// GetQueryId obtains the query id
func (c MAM1Query) GetQueryId() string {
return c.QueryId
}
// GetSet getsets!
func (c MAM1Query) GetSet() *stanza.ResultSet {
return c.ResultSet
}
// GetFlipPage is a stub as it's not supported in this MAM version
func (c MAM1Query) GetFlipPage() *FlipPage {
return nil
}
// Namespace is a namespace!
func (c MAM0Query) Namespace() string {
return c.XMLName.Space
}
// GetForm obtains the query form
func (c MAM0Query) GetForm() *stanza.Form {
return c.Form
}
// GetQueryId obtains the query id
func (c MAM0Query) GetQueryId() string {
return c.QueryId
}
// GetSet getsets!
func (c MAM0Query) GetSet() *stanza.ResultSet {
return c.ResultSet
}
// GetFlipPage is a stub as it's not supported in this MAM version
func (c MAM0Query) GetFlipPage() *FlipPage {
return nil
}
// Namespace is a namespace!
func (c MAM2Fin) Namespace() string {
return c.XMLName.Space
@ -551,6 +672,26 @@ func (c MAM2Fin) GetSet() *stanza.ResultSet {
return c.ResultSet
}
// Namespace is a namespace!
func (c MAM1Fin) Namespace() string {
return c.XMLName.Space
}
// GetSet getsets!
func (c MAM1Fin) GetSet() *stanza.ResultSet {
return c.ResultSet
}
// Namespace is a namespace!
func (c MAM0Fin) Namespace() string {
return c.XMLName.Space
}
// GetSet getsets!
func (c MAM0Fin) GetSet() *stanza.ResultSet {
return c.ResultSet
}
// Namespace is a namespace!
func (c MAM2Metadata) Namespace() string {
return c.XMLName.Space
@ -701,18 +842,54 @@ func init() {
"query",
}, MAM2Query{})
// MAM1 query
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
"urn:xmpp:mam:1",
"query",
}, MAM1Query{})
// MAM0 query
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
"urn:xmpp:mam:0",
"query",
}, MAM0Query{})
// MAM2 message result
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{
"urn:xmpp:mam:2",
"result",
}, MAM2MessageResult{})
// MAM1 message result
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{
"urn:xmpp:mam:1",
"result",
}, MAM1MessageResult{})
// MAM0 message result
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{
"urn:xmpp:mam:0",
"result",
}, MAM0MessageResult{})
// MAM2 fin
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
"urn:xmpp:mam:2",
"fin",
}, MAM2Fin{})
// MAM1 fin
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
"urn:xmpp:mam:1",
"fin",
}, MAM1Fin{})
// MAM0 fin
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
"urn:xmpp:mam:0",
"fin",
}, MAM0Fin{})
// MAM2 metadata
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
"urn:xmpp:mam:2",

View file

@ -50,6 +50,10 @@ const NodeAvatarMetadataNotify string = NodeAvatarMetadata + "+notify"
const NodeAvatarData string = "urn:xmpp:avatar:data"
const NSCommand string = "http://jabber.org/protocol/commands"
const NS_MAM2 = "urn:xmpp:mam:2"
const NS_MAM1 = "urn:xmpp:mam:1"
const NS_MAM0 = "urn:xmpp:mam:0"
// Queue stores presences to send later
var Queue = make(map[string]*stanza.Presence)
var QueueLock = sync.Mutex{}
@ -437,23 +441,46 @@ func sendMessageWrapper(to, from, body, subject, errorText, id string, component
}
sendMessage(&privilegeMessage, component)
} else if mamQueryId != "" {
mneVpadluProbrasyvatJoshParametryPoraRefaktorit := strings.Split(mamQueryId, " ")
ns := mneVpadluProbrasyvatJoshParametryPoraRefaktorit[0]
mamQueryId = mneVpadluProbrasyvatJoshParametryPoraRefaktorit[1]
delay := extensions.NewMessageDelay(timestamp, "")
forwarded := extensions.ForwardedMessage{
Delay: &delay,
Message: &message,
}
var ext stanza.MsgExtension
switch ns {
case NS_MAM2:
ext = extensions.MAM2MessageResult{
Id: stanzaId,
QueryId: mamQueryId,
Forwarded: &forwarded,
}
case NS_MAM1:
ext = extensions.MAM1MessageResult{
Id: stanzaId,
QueryId: mamQueryId,
Forwarded: &forwarded,
}
case NS_MAM0:
ext = extensions.MAM0MessageResult{
Id: stanzaId,
QueryId: mamQueryId,
Forwarded: &forwarded,
}
}
mamMessage := stanza.Message{
Attrs: stanza.Attrs{
From: mucJID,
To: to,
Type: messageType,
},
Extensions: []stanza.MsgExtension{
extensions.MAM2MessageResult{
Id: stanzaId,
QueryId: mamQueryId,
Forwarded: &extensions.ForwardedMessage{
Delay: &delay,
Message: &message,
},
},
},
Extensions: []stanza.MsgExtension{ext},
}
sendMessage(&mamMessage, component)
} else {

View file

@ -85,7 +85,17 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
}
queryMAM2, ok := iq.Payload.(*extensions.MAM2Query)
if ok {
go handleGetQueryMAM2(s, iq, queryMAM2)
go handleGetQueryMAM(s, iq, queryMAM2)
return
}
queryMAM1, ok := iq.Payload.(*extensions.MAM1Query)
if ok {
go handleGetQueryMAM(s, iq, queryMAM1)
return
}
queryMAM0, ok := iq.Payload.(*extensions.MAM0Query)
if ok {
go handleGetQueryMAM(s, iq, queryMAM0)
return
}
_, ok = iq.Payload.(*extensions.MAM2Metadata)
@ -116,7 +126,17 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
}
queryMAM2, ok := iq.Payload.(*extensions.MAM2Query)
if ok {
go handleSetQueryMAM2(s, iq, queryMAM2)
go handleSetQueryMAM(s, iq, queryMAM2)
return
}
queryMAM1, ok := iq.Payload.(*extensions.MAM1Query)
if ok {
go handleSetQueryMAM(s, iq, queryMAM1)
return
}
queryMAM0, ok := iq.Payload.(*extensions.MAM0Query)
if ok {
go handleSetQueryMAM(s, iq, queryMAM0)
return
}
} else if iq.Type == stanza.IQTypeResult {
@ -976,7 +996,9 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
"muc_unsecured",
"http://jabber.org/protocol/muc#stable_id",
"jabber:iq:register",
"urn:xmpp:mam:2",
gateway.NS_MAM0,
gateway.NS_MAM1,
gateway.NS_MAM2,
"urn:xmpp:mam:2#extended",
"urn:xmpp:sid:0",
"vcard-temp",
@ -1382,23 +1404,33 @@ func handleGetQueryMucOwner(s xmpp.Sender, iq *stanza.IQ) {
}
}
func handleGetQueryMAM2(s xmpp.Sender, iq *stanza.IQ, query *extensions.MAM2Query) {
func handleGetQueryMAM(s xmpp.Sender, iq *stanza.IQ, query extensions.MAMQuery) {
component, answer, ok := iqResultStub(s, iq)
if !ok {
return
}
defer gateway.ResumableSend(component, answer)
payload := &extensions.MAM2Query{}
answer.Payload = payload
payload2 := &extensions.MAM2Query{}
payload1 := &extensions.MAM1Query{}
payload0 := &extensions.MAM0Query{}
ns := query.Namespace()
switch ns {
case gateway.NS_MAM2:
answer.Payload = payload2
case gateway.NS_MAM1:
answer.Payload = payload1
case gateway.NS_MAM0:
answer.Payload = payload0
}
payload.Form = &stanza.Form{
form := stanza.Form{
Type: stanza.FormTypeForm,
Fields: []*stanza.Field{
&stanza.Field{
Var: "FORM_TYPE",
Type: stanza.FieldTypeHidden,
ValuesList: []string{"urn:xmpp:mam:2"},
ValuesList: []string{ns},
},
&stanza.Field{
Var: "with",
@ -1426,6 +1458,9 @@ func handleGetQueryMAM2(s xmpp.Sender, iq *stanza.IQ, query *extensions.MAM2Quer
},
},
}
payload2.Form = &form
payload1.Form = &form
payload0.Form = &form
log.Debugf("MAM info request: %#v", query)
}
@ -2154,7 +2189,7 @@ func handleSetQueryMucOwner(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
}
func handleSetQueryMAM2(s xmpp.Sender, iq *stanza.IQ, query *extensions.MAM2Query) {
func handleSetQueryMAM(s xmpp.Sender, iq *stanza.IQ, query extensions.MAMQuery) {
component, answer, ok := iqResultStub(s, iq)
if !ok {
return
@ -2194,9 +2229,13 @@ func handleSetQueryMAM2(s xmpp.Sender, iq *stanza.IQ, query *extensions.MAM2Quer
var rsmLimit int32
var justCount bool
log.Debugf("MAM query to %v: %#v %#v %#v", toID, query, query.Form, query.ResultSet)
if query.Form != nil && query.Form.Type == stanza.FormTypeSubmit {
for _, field := range query.Form.Fields {
form := query.GetForm()
queryRs := query.GetSet()
ns := query.Namespace()
log.Debugf("MAM query to %v: %#v %#v %#v", toID, query, form, queryRs)
if form != nil && form.Type == stanza.FormTypeSubmit {
for _, field := range form.Fields {
if len(field.ValuesList) < 1 {
iqAnswerSetError(answer, 400)
return
@ -2207,7 +2246,7 @@ func handleSetQueryMAM2(s xmpp.Sender, iq *stanza.IQ, query *extensions.MAM2Quer
switch field.Var {
case "FORM_TYPE":
if value != "urn:xmpp:mam:2" {
if value != ns {
iqAnswerSetError(answer, 400)
return
}
@ -2246,22 +2285,22 @@ func handleSetQueryMAM2(s xmpp.Sender, iq *stanza.IQ, query *extensions.MAM2Quer
}
}
if query.ResultSet != nil {
if query.ResultSet.After != nil {
rsmAfter, ok = parseMessageId(*query.ResultSet.After)
if queryRs != nil {
if queryRs.After != nil {
rsmAfter, ok = parseMessageId(*queryRs.After)
if !ok {
iqAnswerSetError(answer, 400)
return
}
log.Debugf("MAM RSM after: %v", rsmAfter)
}
if query.ResultSet.Before != nil {
before := *query.ResultSet.Before
if queryRs.Before != nil {
before := *queryRs.Before
if before == "" {
rsmLastPage = true
log.Debugf("MAM RSM last page")
} else {
rsmBefore, ok = parseMessageId(*query.ResultSet.Before)
rsmBefore, ok = parseMessageId(*queryRs.Before)
if !ok {
iqAnswerSetError(answer, 400)
return
@ -2269,23 +2308,23 @@ func handleSetQueryMAM2(s xmpp.Sender, iq *stanza.IQ, query *extensions.MAM2Quer
log.Debugf("MAM RSM before: %v", rsmBefore)
}
}
if query.ResultSet.Max != nil {
rsmLimit = int32(*query.ResultSet.Max)
if queryRs.Max != nil {
rsmLimit = int32(*queryRs.Max)
if rsmLimit == 0 {
justCount = true
}
log.Debugf("MAM RSM max: %v", rsmLimit)
}
if query.ResultSet.First != nil {
if queryRs.First != nil {
iqAnswerSetError(answer, 400)
return
}
if query.ResultSet.Index != nil {
if queryRs.Index != nil {
iqAnswerSetError(answer, 501)
return
}
if query.ResultSet.Last != nil {
if queryRs.Last != nil {
iqAnswerSetError(answer, 400)
return
}
@ -2317,7 +2356,7 @@ func handleSetQueryMAM2(s xmpp.Sender, iq *stanza.IQ, query *extensions.MAM2Quer
fromStart := true
var overallyFirstMessageId, overallyLastMessageId int64
reverse := query.FlipPage != nil
reverse := query.GetFlipPage() != nil
if ids != nil {
for _, sId := range ids {
@ -2532,16 +2571,38 @@ func handleSetQueryMAM2(s xmpp.Sender, iq *stanza.IQ, query *extensions.MAM2Quer
}
log.Debugf("obtained %v messages", len(messages))
// ty zhe lopnesh, detochka
queryId := ns + " " + query.GetQueryId()
for _, message := range messages {
session.SendDelayedMUCMessage(toID, message, iq.From, query.QueryId)
session.SendDelayedMUCMessage(toID, message, iq.From, queryId)
}
rs := stanza.ResultSet{}
switch ns {
case gateway.NS_MAM2:
answer.Payload = &extensions.MAM2Fin{
ResultSet: &rs,
Complete: complete,
Stable: false,
}
case gateway.NS_MAM1:
answer.Payload = &extensions.MAM1Fin{
ResultSet: &rs,
Complete: complete,
Stable: false,
}
case gateway.NS_MAM0:
answer.Payload = &extensions.MAM0Fin{
ResultSet: &rs,
Complete: complete,
Stable: false,
}
}
if answer.Payload == nil {
log.Error("Unknown MAM version")
return
}
if beyond {
count := 0