~samwhited/xmpp

xmpp/session_message.go -rw-r--r-- 3.5 KiB
dcc6772fSam Whited all: add strict namespace checking to mux 22 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Copyright 2021 The Mellium Contributors.
// Use of this source code is governed by the BSD 2-clause
// license that can be found in the LICENSE file.

package xmpp

import (
	"context"
	"encoding/xml"
	"fmt"

	"mellium.im/xmlstream"
	"mellium.im/xmpp/internal/attr"
	"mellium.im/xmpp/internal/marshal"
	"mellium.im/xmpp/internal/ns"
	"mellium.im/xmpp/stanza"
)

func isMessageEmptySpace(name xml.Name) bool {
	return name.Local == "message" && (name.Space == "" || name.Space == ns.Client || name.Space == ns.Server)
}

// SendMessage is like Send except that it returns an error if the first token
// read from the input is not a message start token and blocks until an error
// response is received or the context times out.
// Messages are generally fire-and-forget meaning that the success behavior of
// SendMessage is to time out and that methods such as Send should normally be
// used instead.
// It is thus mainly for use by extensions that define an extension-namespaced
// success response to a message being sent and need a mechanism to track
// general error responses without handling every single message sent through
// the session to see if it has a matching ID.
//
// SendMessage is safe for concurrent use by multiple goroutines.
func (s *Session) SendMessage(ctx context.Context, r xml.TokenReader) (xmlstream.TokenReadCloser, error) {
	tok, err := r.Token()
	if err != nil {
		return nil, err
	}
	start, ok := tok.(xml.StartElement)
	if !ok {
		return nil, fmt.Errorf("expected IQ start element, got %T", tok)
	}
	if !isMessageEmptySpace(start.Name) {
		return nil, fmt.Errorf("expected start element to be a message")
	}

	// If there's no ID, add one.
	idx, _, id, typ := getIDTyp(start.Attr)
	if idx == -1 {
		idx = len(start.Attr)
		start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "id"}, Value: ""})
	}
	if id == "" {
		id = attr.RandomID()
		start.Attr[idx].Value = id
	}

	// If this is an error message we don't ever expect a response, so just send
	// it normally.
	if typ == string(stanza.ErrorMessage) {
		return nil, s.SendElement(ctx, xmlstream.Inner(r), start)
	}

	return s.sendResp(ctx, id, xmlstream.Inner(r), start)
}

// SendMessageElement is like SendMessage except that it wraps the payload in a
// Message element.
// For more information see SendMessage.
//
// SendMessageElement is safe for concurrent use by multiple goroutines.
func (s *Session) SendMessageElement(ctx context.Context, payload xml.TokenReader, msg stanza.Message) (xmlstream.TokenReadCloser, error) {
	return s.SendMessage(ctx, msg.Wrap(payload))
}

// EncodeMessage is like Encode except that it returns an error if v does not
// marshal to an Message stanza and like SendMessage it blocks until an error
// response is received or the context times out.
// For more information see SendMessage.
//
// EncodeMessage is safe for concurrent use by multiple goroutines.
func (s *Session) EncodeMessage(ctx context.Context, v interface{}) (xmlstream.TokenReadCloser, error) {
	r, err := marshal.TokenReader(v)
	if err != nil {
		return nil, err
	}
	return s.SendMessage(ctx, r)
}

// EncodeMessageElement is like EncodeMessage except that it wraps the payload
// in a Message element.
// For more information see SendMessage.
//
// EncodeMessageElement is safe for concurrent use by multiple goroutines.
func (s *Session) EncodeMessageElement(ctx context.Context, payload interface{}, msg stanza.Message) (xmlstream.TokenReadCloser, error) {
	r, err := marshal.TokenReader(payload)
	if err != nil {
		return nil, err
	}
	return s.SendMessageElement(ctx, r, msg)
}