~samwhited/xmpp

ref: c98da397314a57419fb1041f927b83246ed7366d xmpp/session_presence.go -rw-r--r-- 3.5 KiB
c98da397Sam Whited carbons: new package 4 months 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 isPresenceEmptySpace(name xml.Name) bool {
	return name.Local == "presence" && (name.Space == "" || name.Space == ns.Client || name.Space == ns.Server)
}

// SendPresence is like Send except that it returns an error if the first token
// read from the input is not a presence start token and blocks until an error
// response is received or the context times out.
// Presences are generally fire-and-forget meaning that the success behavior of
// SendPresence 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 presence being sent and need a mechanism to track
// general error responses without handling every single presence sent through
// the session to see if it has a matching ID.
//
// SendPresence is safe for concurrent use by multiple goroutines.
func (s *Session) SendPresence(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 !isPresenceEmptySpace(start.Name) {
		return nil, fmt.Errorf("expected start element to be a presence")
	}

	// 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 presence we don't ever expect a response, so just send
	// it normally.
	if typ == string(stanza.ErrorPresence) {
		return nil, s.SendElement(ctx, xmlstream.Inner(r), start)
	}

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

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

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

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