~samwhited/xmpp

ref: 075b015c37056b42c97f8c4e221dd39f477e642c xmpp/stream/stream.go -rw-r--r-- 3.1 KiB
075b015cSam Whited Make stream a "dumb" XML element. 5 years 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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright 2015 Sam Whited.
// Use of this source code is governed by the BSD 2-clause license that can be
// found in the LICENSE file.

package stream

import (
	"encoding/xml"
	"errors"

	"bitbucket.org/mellium/xmpp/internal"
	"bitbucket.org/mellium/xmpp/jid"
	"golang.org/x/text/language"
)

// A Stream is a container for the exchange of XML elements between two
// endpoints. It maintains state about stream-level features, and handles
// decoding and routing incoming XMPP stanza's and other elements, as well as
// encoding outgoing XMPP elements. Each XMPP connection has two streams, one
// for input, and one for output.
type Stream struct {
	options
	to      *jid.JID
	from    *jid.JID
	id      string
	version internal.Version
}

// New creates a stream that will be used to initiate a new XMPP connection.
// This should always be used by clients to create a new stream, and by the
// initiating server in server-to-server connections.
func New(to, from *jid.JID, opts ...Option) Stream {
	return Stream{
		to:      to,
		from:    from,
		version: internal.DefaultVersion,
		options: getOpts(opts...),
	}
}

// FromStartElement constructs a new Stream from the given XML StartElement.
func FromStartElement(start xml.StartElement) (Stream, error) {

	stream := Stream{}
	if start.Name.Local != "stream" || start.Name.Space != "stream" {
		return stream, errors.New("Incorrect XML name on stream start element.")
	}

	for _, attr := range start.Attr {
		switch attr.Name {
		case xml.Name{"", "from"}:
			j, err := jid.ParseString(attr.Value)
			if err != nil {
				return stream, err
			}
			stream.from = j
		case xml.Name{"", "to"}:
			j, err := jid.ParseString(attr.Value)
			if err != nil {
				return stream, err
			}
			stream.to = j
		case xml.Name{"", "xmlns"}:
			switch attr.Value {
			case "jabber:server":
				stream.options.s2sStream = true
			case "jabber:client":
				stream.options.s2sStream = false
			default:
				return stream, errors.New("Stream has invalid xmlns.")
			}
		case xml.Name{"xml", "lang"}:
			var err error
			stream.lang, err = language.Parse(attr.Value)
			if err != nil {
				return stream, err
			}
		case xml.Name{"", "id"}:
			stream.id = attr.Value
		case xml.Name{"", "version"}:
			v, err := internal.ParseVersion(attr.Value)
			if err != nil {
				return stream, err
			}
			stream.version = v
		}
	}

	return stream, nil
}

// StartElement creates an XML start element from the given stream which is
// suitable for encoding and transmitting over the wire.
func (s Stream) StartElement() xml.StartElement {
	var xmlns string
	if s.options.s2sStream {
		xmlns = "jabber:server"
	} else {
		xmlns = "jabber:client"
	}
	return xml.StartElement{
		Name: xml.Name{"stream", "stream"},
		Attr: []xml.Attr{
			xml.Attr{
				xml.Name{"", "to"},
				s.to.String(),
			},
			xml.Attr{
				xml.Name{"", "from"},
				s.from.String(),
			},
			xml.Attr{
				xml.Name{"", "version"},
				s.version.String(),
			},
			xml.Attr{
				xml.Name{"xml", "lang"},
				s.options.lang.String(),
			},
			xml.Attr{
				xml.Name{"", "id"},
				s.id,
			},
			xml.Attr{
				xml.Name{"", "xmlns"},
				xmlns,
			},
		},
	}
}