summaryrefslogtreecommitdiff
path: root/xmpp/streams.go
blob: 87df86aecf1a62cf2fa5cddc8fe1a80e55ed2df9 (plain)
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
131
package xmpp

import (
	"encoding/xml"
	"log"
)

func runStreamPair(s *session) {
	end := openStream(s)
	defer closeStream(s, end)

	buf := newElementBuffer()

	for {
		select {
		case data := <-s.in:
			switch data.(type) {
			case SessionShouldDisconnect:
				return
			default:
				log.Printf("Unknown data '%d'!\n", data)
			}
		case t := <-s.rx:
			err := buf.add(t)
			if err != nil {
				log.Printf("Could not add XML token to buffer: %v\n", err)
				return
			}
			if buf.isComplete() {
				element := buf.reset()
				route(s, element, getRoutingTable())
			}
		}
	}
}

func openStream(s *session) xml.EndElement {
	start := xml.StartElement{
		xml.Name{"jabber:client", "stream:stream"},
		[]xml.Attr{
			xml.Attr{xml.Name{"", "from"}, s.jid},
			xml.Attr{xml.Name{"", "to"}, domainpart(s.jid)},
			xml.Attr{xml.Name{"", "version"}, "1.0"},
			xml.Attr{xml.Name{"", "xml:lang"}, "en"},
			xml.Attr{xml.Name{"", "xmlns:stream"}, "http://etherx.jabber.org/streams"},
		},
	}
	end := start.End()

	err := s.ed.encodeToken(start)
	if err != nil {
		log.Println("Could not encode stream start!")
	}

	syncStreams(s)

	return end
}

// syncStreams drops XML tokens from the receiving stream until an
// xml.StartElement with the local name `stream` is received. If this function
// is called after opening a new stream in the sending direction it is ensured
// that both streams directions work on the same stream level and are in sync.
// Tokens received which are not a stream StartElement are not handled but
// logged since this should not happen.
func syncStreams(s *session) {
	for {
		select {
		case data := <-s.in:
			switch data.(type) {
			case SessionShouldDisconnect:
				return
			default:
				log.Printf("Unhandled data '%d' during stream sync!\n", data)
			}
		case t := <-s.rx:
			switch token := t.(type) {
			case xml.StartElement:
				if token.Name.Local == "stream" {
					return
				}
			}
			log.Printf("Unhandled XML token '%v' during stream sync!\n", t)
		}
	}
}

func closeStream(s *session, end xml.EndElement) {
	err := s.ed.encodeToken(end)
	if err != nil {
		log.Println("Could not encode stream end!")
	}
}

func streamFeaturesHandler(s *session, e []xml.Token) {
	if hasSaslPlain(e) {
		s.sasl()
		return
	}

	if hasBind(e) {
		s.sendBind()
		return
	}

	log.Println("Stream has no implemented features!")
}

func iqHandler(s *session, e []xml.Token) {
	isResult := false
	idMatches := false

	result := xml.Attr{xml.Name{"", "type"}, "result"}
	id := xml.Attr{xml.Name{"", "id"}, s.resourceReq}

	switch start := e[0].(type) {
	case xml.StartElement:
		for _, v := range start.Attr {
			if v == result {
				isResult = true
			}
			if v == id {
				idMatches = true
			}
		}

		if isResult && idMatches {
			s.sendPresence()
		}
	}
}