From 4566297817b3fc2b9d088d37b037b975a74926f3 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Fri, 21 Aug 2026 21:05:51 -0400 Subject: [PATCH] Ping (XEP-0199/XEP-0410) --- xmpp/extensions/extensions.go | 21 ++++++++++++++++ xmpp/gateway/gateway.go | 1 + xmpp/handlers.go | 47 +++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/xmpp/extensions/extensions.go b/xmpp/extensions/extensions.go index 4d777f9..c17ccd5 100644 --- a/xmpp/extensions/extensions.go +++ b/xmpp/extensions/extensions.go @@ -493,6 +493,21 @@ type EntityTime struct { ResultSet *stanza.ResultSet `xml:"set,omitempty"` } +// Ping is from XEP-0199, also used for XEP-0410 MUC self-ping +type Ping struct { + XMLName xml.Name `xml:"urn:xmpp:ping ping"` +} + +// Namespace is a namespace! +func (c Ping) Namespace() string { + return c.XMLName.Space +} + +// GetSet getsets! +func (c Ping) GetSet() *stanza.ResultSet { + return nil +} + // AttachTo is from XEP-0367 type AttachTo struct { XMLName xml.Name `xml:"urn:xmpp:message-attaching:1 attach-to"` @@ -952,6 +967,12 @@ func init() { "time", }, EntityTime{}) + // ping + stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{ + "urn:xmpp:ping", + "ping", + }, Ping{}) + // attach-to stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{ "urn:xmpp:message-attaching:1", diff --git a/xmpp/gateway/gateway.go b/xmpp/gateway/gateway.go index 0c4cc15..b192555 100644 --- a/xmpp/gateway/gateway.go +++ b/xmpp/gateway/gateway.go @@ -1172,6 +1172,7 @@ func AddCommonDiscoFeatures(disco *stanza.DiscoInfo) { NSCommand, "jabber:iq:version", "urn:xmpp:time", + "urn:xmpp:ping", stanza.NSDiscoInfo, NSCaps, ) diff --git a/xmpp/handlers.go b/xmpp/handlers.go index b078573..511f853 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -149,6 +149,11 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) { go handleGetEntityTime(s, iq) return } + _, ok = iq.Payload.(*extensions.Ping) + if ok { + go handleGetPing(s, iq) + return + } } else if iq.Type == stanza.IQTypeSet { queryRegister, ok := iq.Payload.(*extensions.QueryRegister) if ok { @@ -1459,6 +1464,48 @@ func handleGetEntityTime(s xmpp.Sender, iq *stanza.IQ) { log.Debugf("%#v", entityTime) } +// handleGetPing answers XEP-0199 ping. When addressed to a MUC occupant JID +// (room@service/nick), it implements the XEP-0410 self-ping optimization: +// answer directly from in-memory occupant state instead of routing the ping +// to a (nonexistent) full JID for that occupant. +func handleGetPing(s xmpp.Sender, iq *stanza.IQ) { + component, answer, ok := iqResultStub(s, iq) + if !ok { + return + } + defer gateway.ResumableSend(component, answer) + + toJid, err := stanza.NewJid(iq.To) + if err != nil || toJid.Resource == "" { + // plain service/entity ping, not addressed to a MUC occupant + return + } + + toID, toOk, toIsGroup := toToID(iq.To) + if !toOk || !toIsGroup { + // not a MUC room JID either, treat as a plain entity ping + return + } + + bare, resource, fromOk := gateway.SplitJID(iq.From) + if !fromOk { + iqAnswerSetError(answer, 400) + return + } + + session, sessionOk := sessions[bare] + if !sessionOk || !session.Session.MUC { + iqAnswerSetError(answer, 403) + return + } + + nick, hasNick := session.GetMyMUCNickname(toID) + if !hasNick || nick != toJid.Resource || !session.MUCHasResource(toID, resource) { + iqAnswerSetError(answer, 404) + return + } +} + func handleGetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryMucAdmin) { component, answer, ok := iqResultStub(s, iq) if !ok {