Skip to content

Commit

Permalink
Rework printing of sent stanzas when debug is enabled
Browse files Browse the repository at this point in the history
This got reworked to also work with multiple connections
as pointed out by @vcabbage in
xmppo#141 (comment)
  • Loading branch information
mdosch committed May 22, 2023
1 parent bef3e54 commit e02db80
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 46 deletions.
51 changes: 26 additions & 25 deletions xmpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ func getCookie() Cookie {

// Client holds XMPP connection options
type Client struct {
conn net.Conn // connection to server
jid string // Jabber ID for our connection
domain string
p *xml.Decoder
conn net.Conn // connection to server
jid string // Jabber ID for our connection
domain string
p *xml.Decoder
stanzaWriter io.Writer
}

func (c *Client) JID() string {
Expand Down Expand Up @@ -378,7 +379,7 @@ func (c *Client) init(o *Options) error {
foundAnonymous := false
for _, m := range f.Mechanisms.Mechanism {
if m == "ANONYMOUS" {
fmt.Fprintf(StanzaWriter, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL)
fmt.Fprintf(c.stanzaWriter, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL)
foundAnonymous = true
break
}
Expand Down Expand Up @@ -436,7 +437,7 @@ func (c *Client) init(o *Options) error {
}
clientNonce := cnonce()
clientFirstMessage := "n=" + user + ",r=" + clientNonce
fmt.Fprintf(StanzaWriter, "<auth xmlns='%s' mechanism='%s'>%s</auth>",
fmt.Fprintf(c.stanzaWriter, "<auth xmlns='%s' mechanism='%s'>%s</auth>",
nsSASL, mechanism, base64.StdEncoding.EncodeToString([]byte("n,,"+
clientFirstMessage)))
var sfm string
Expand Down Expand Up @@ -536,15 +537,15 @@ func (c *Client) init(o *Options) error {
}
clientFinalMessage := base64.StdEncoding.EncodeToString([]byte(clientFinalMessageBare +
",p=" + base64.StdEncoding.EncodeToString(clientProof)))
fmt.Fprintf(StanzaWriter, "<response xmlns='%s'>%s</response>", nsSASL,
fmt.Fprintf(c.stanzaWriter, "<response xmlns='%s'>%s</response>", nsSASL,
clientFinalMessage)
}
if mechanism == "X-OAUTH2" && o.OAuthToken != "" && o.OAuthScope != "" {
// Oauth authentication: send base64-encoded \x00 user \x00 token.
raw := "\x00" + user + "\x00" + o.OAuthToken
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
base64.StdEncoding.Encode(enc, []byte(raw))
fmt.Fprintf(StanzaWriter, "<auth xmlns='%s' mechanism='X-OAUTH2' auth:service='oauth2' "+
fmt.Fprintf(c.stanzaWriter, "<auth xmlns='%s' mechanism='X-OAUTH2' auth:service='oauth2' "+
"xmlns:auth='%s'>%s</auth>\n", nsSASL, o.OAuthXmlNs, enc)
}
if mechanism == "PLAIN" {
Expand All @@ -556,7 +557,7 @@ func (c *Client) init(o *Options) error {
}
if mechanism == "DIGEST-MD5" {
// Digest-MD5 authentication
fmt.Fprintf(StanzaWriter, "<auth xmlns='%s' mechanism='DIGEST-MD5'/>\n", nsSASL)
fmt.Fprintf(c.stanzaWriter, "<auth xmlns='%s' mechanism='DIGEST-MD5'/>\n", nsSASL)
var ch saslChallenge
if err = c.p.DecodeElement(&ch, nil); err != nil {
return errors.New("unmarshal <challenge>: " + err.Error())
Expand Down Expand Up @@ -586,7 +587,7 @@ func (c *Client) init(o *Options) error {
message := "username=\"" + user + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr +
"\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset

fmt.Fprintf(StanzaWriter, "<response xmlns='%s'>%s</response>\n", nsSASL, base64.StdEncoding.EncodeToString([]byte(message)))
fmt.Fprintf(c.stanzaWriter, "<response xmlns='%s'>%s</response>\n", nsSASL, base64.StdEncoding.EncodeToString([]byte(message)))

var rspauth saslRspAuth
if err = c.p.DecodeElement(&rspauth, nil); err != nil {
Expand All @@ -596,7 +597,7 @@ func (c *Client) init(o *Options) error {
if err != nil {
return err
}
fmt.Fprintf(StanzaWriter, "<response xmlns='%s'/>\n", nsSASL)
fmt.Fprintf(c.stanzaWriter, "<response xmlns='%s'/>\n", nsSASL)
}
}
if mechanism == "" {
Expand Down Expand Up @@ -649,9 +650,9 @@ func (c *Client) init(o *Options) error {

// Send IQ message asking to bind to the local user name.
if o.Resource == "" {
fmt.Fprintf(StanzaWriter, "<iq type='set' id='%x'><bind xmlns='%s'></bind></iq>\n", cookie, nsBind)
fmt.Fprintf(c.stanzaWriter, "<iq type='set' id='%x'><bind xmlns='%s'></bind></iq>\n", cookie, nsBind)
} else {
fmt.Fprintf(StanzaWriter, "<iq type='set' id='%x'><bind xmlns='%s'><resource>%s</resource></bind></iq>\n", cookie, nsBind, o.Resource)
fmt.Fprintf(c.stanzaWriter, "<iq type='set' id='%x'><bind xmlns='%s'><resource>%s</resource></bind></iq>\n", cookie, nsBind, o.Resource)
}
var iq clientIQ
if err = c.p.DecodeElement(&iq, nil); err != nil {
Expand All @@ -665,11 +666,11 @@ func (c *Client) init(o *Options) error {

if o.Session {
//if server support session, open it
fmt.Fprintf(StanzaWriter, "<iq to='%s' type='set' id='%x'><session xmlns='%s'/></iq>", xmlEscape(domain), cookie, nsSession)
fmt.Fprintf(c.stanzaWriter, "<iq to='%s' type='set' id='%x'><session xmlns='%s'/></iq>", xmlEscape(domain), cookie, nsSession)
}

// We're connected and can now receive and send messages.
fmt.Fprintf(StanzaWriter, "<presence xml:lang='en'><show>%s</show><status>%s</status></presence>", o.Status, o.StatusMessage)
fmt.Fprintf(c.stanzaWriter, "<presence xml:lang='en'><show>%s</show><status>%s</status></presence>", o.Status, o.StatusMessage)

return nil
}
Expand All @@ -691,7 +692,7 @@ func (c *Client) startTLSIfRequired(f *streamFeatures, o *Options, domain string
}
var err error

fmt.Fprintf(StanzaWriter, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n")
fmt.Fprintf(c.stanzaWriter, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n")
var k tlsProceed
if err = c.p.DecodeElement(&k, nil); err != nil {
return f, errors.New("unmarshal <proceed>: " + err.Error())
Expand Down Expand Up @@ -724,13 +725,13 @@ func (c *Client) startTLSIfRequired(f *streamFeatures, o *Options, domain string
func (c *Client) startStream(o *Options, domain string) (*streamFeatures, error) {
if o.Debug {
c.p = xml.NewDecoder(tee{c.conn, DebugWriter})
StanzaWriter = io.MultiWriter(c.conn, DebugWriter)
c.stanzaWriter = io.MultiWriter(c.conn, DebugWriter)
} else {
c.p = xml.NewDecoder(c.conn)
StanzaWriter = c.conn
c.stanzaWriter = c.conn
}

_, err := fmt.Fprintf(StanzaWriter, "<?xml version='1.0'?>"+
_, err := fmt.Fprintf(c.stanzaWriter, "<?xml version='1.0'?>"+
"<stream:stream to='%s' xmlns='%s'"+
" xmlns:stream='%s' version='1.0'>",
xmlEscape(domain), nsClient, nsStream)
Expand Down Expand Up @@ -1058,7 +1059,7 @@ func (c *Client) Send(chat Chat) (n int, err error) {

stanza := "<message to='%s' type='%s' id='%s' xml:lang='en'>" + subtext + "<body>%s</body>" + oobtext + thdtext + "</message>"

return fmt.Fprintf(StanzaWriter, stanza,
return fmt.Fprintf(c.stanzaWriter, stanza,
xmlEscape(chat.Remote), xmlEscape(chat.Type), cnonce(), xmlEscape(chat.Text))
}

Expand All @@ -1075,17 +1076,17 @@ func (c *Client) SendOOB(chat Chat) (n int, err error) {
}
oobtext += `</x>`
}
return fmt.Fprintf(StanzaWriter, "<message to='%s' type='%s' id='%s' xml:lang='en'>"+oobtext+thdtext+"</message>",
return fmt.Fprintf(c.stanzaWriter, "<message to='%s' type='%s' id='%s' xml:lang='en'>"+oobtext+thdtext+"</message>",
xmlEscape(chat.Remote), xmlEscape(chat.Type), cnonce())
}

// SendOrg sends the original text without being wrapped in an XMPP message stanza.
func (c *Client) SendOrg(org string) (n int, err error) {
return fmt.Fprint(StanzaWriter, org)
return fmt.Fprint(c.stanzaWriter, org)
}

func (c *Client) SendPresence(presence Presence) (n int, err error) {
return fmt.Fprintf(StanzaWriter, "<presence from='%s' to='%s'/>", xmlEscape(presence.From), xmlEscape(presence.To))
return fmt.Fprintf(c.stanzaWriter, "<presence from='%s' to='%s'/>", xmlEscape(presence.From), xmlEscape(presence.To))
}

// SendKeepAlive sends a "whitespace keepalive" as described in chapter 4.6.1 of RFC6120.
Expand All @@ -1095,15 +1096,15 @@ func (c *Client) SendKeepAlive() (n int, err error) {

// SendHtml sends the message as HTML as defined by XEP-0071
func (c *Client) SendHtml(chat Chat) (n int, err error) {
return fmt.Fprintf(StanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+
return fmt.Fprintf(c.stanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+
"<body>%s</body>"+
"<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html></message>",
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text), chat.Text)
}

// Roster asks for the chat roster.
func (c *Client) Roster() error {
fmt.Fprintf(StanzaWriter, "<iq from='%s' type='get' id='roster1'><query xmlns='jabber:iq:roster'/></iq>\n", xmlEscape(c.jid))
fmt.Fprintf(c.stanzaWriter, "<iq from='%s' type='get' id='roster1'><query xmlns='jabber:iq:roster'/></iq>\n", xmlEscape(c.jid))
return nil
}

Expand Down
4 changes: 2 additions & 2 deletions xmpp_information_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ func (c *Client) DiscoverEntityItems(jid string) (string, error) {
// RawInformationQuery sends an information query request to the server.
func (c *Client) RawInformationQuery(from, to, id, iqType, requestNamespace, body string) (string, error) {
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>"
_, err := fmt.Fprintf(StanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
return id, err
}

// rawInformation send a IQ request with the payload body to the server
func (c *Client) RawInformation(from, to, id, iqType, body string) (string, error) {
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'>%s</iq>"
_, err := fmt.Fprintf(StanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
return id, err
}
26 changes: 13 additions & 13 deletions xmpp_muc.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ const (

// Send sends room topic wrapped inside an XMPP message stanza body.
func (c *Client) SendTopic(chat Chat) (n int, err error) {
return fmt.Fprintf(StanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>",
return fmt.Fprintf(c.stanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>",
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
}

func (c *Client) JoinMUCNoHistory(jid, nick string) (n int, err error) {
if nick == "" {
nick = c.jid
}
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>"+
"<history maxchars='0'/></x>\n"+
"</presence>",
Expand All @@ -47,31 +47,31 @@ func (c *Client) JoinMUC(jid, nick string, history_type, history int, history_da
}
switch history_type {
case NoHistory:
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s' />\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC)
case CharHistory:
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<history maxchars='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case StanzaHistory:
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<history maxstanzas='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case SecondsHistory:
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<history seconds='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case SinceHistory:
if history_date != nil {
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<history since='%s'/></x>\n"+
"</presence>",
Expand All @@ -88,36 +88,36 @@ func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_typ
}
switch history_type {
case NoHistory:
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<password>%s</password>"+
"</x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
case CharHistory:
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<password>%s</password>\n"+
"<history maxchars='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case StanzaHistory:
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<password>%s</password>\n"+
"<history maxstanzas='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case SecondsHistory:
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<password>%s</password>\n"+
"<history seconds='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case SinceHistory:
if history_date != nil {
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<password>%s</password>\n"+
"<history since='%s'/></x>\n"+
Expand All @@ -130,6 +130,6 @@ func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_typ

// xep-0045 7.14
func (c *Client) LeaveMUC(jid string) (n int, err error) {
return fmt.Fprintf(StanzaWriter, "<presence from='%s' to='%s' type='unavailable' />",
return fmt.Fprintf(c.stanzaWriter, "<presence from='%s' to='%s' type='unavailable' />",
c.jid, xmlEscape(jid))
}
6 changes: 3 additions & 3 deletions xmpp_ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,23 @@ func (c *Client) PingC2S(jid, server string) error {
if server == "" {
server = c.domain
}
_, err := fmt.Fprintf(StanzaWriter, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+
"<ping xmlns='urn:xmpp:ping'/>\n"+
"</iq>",
xmlEscape(jid), xmlEscape(server))
return err
}

func (c *Client) PingS2S(fromServer, toServer string) error {
_, err := fmt.Fprintf(StanzaWriter, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+
"<ping xmlns='urn:xmpp:ping'/>\n"+
"</iq>",
xmlEscape(fromServer), xmlEscape(toServer))
return err
}

func (c *Client) SendResultPing(id, toServer string) error {
_, err := fmt.Fprintf(StanzaWriter, "<iq type='result' to='%s' id='%s'/>",
_, err := fmt.Fprintf(c.stanzaWriter, "<iq type='result' to='%s' id='%s'/>",
xmlEscape(toServer), xmlEscape(id))
return err
}
6 changes: 3 additions & 3 deletions xmpp_subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import (
)

func (c *Client) ApproveSubscription(jid string) {
fmt.Fprintf(StanzaWriter, "<presence to='%s' type='subscribed'/>",
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribed'/>",
xmlEscape(jid))
}

func (c *Client) RevokeSubscription(jid string) {
fmt.Fprintf(StanzaWriter, "<presence to='%s' type='unsubscribed'/>",
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='unsubscribed'/>",
xmlEscape(jid))
}

Expand All @@ -20,6 +20,6 @@ func (c *Client) RetrieveSubscription(jid string) {
}

func (c *Client) RequestSubscription(jid string) {
fmt.Fprintf(StanzaWriter, "<presence to='%s' type='subscribe'/>",
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribe'/>",
xmlEscape(jid))
}

0 comments on commit e02db80

Please sign in to comment.