1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package xmpp implements the XMPP IM protocol, as specified in RFC 6120 and
6// 6121.
7package xmpp
8
9import (
10	"bytes"
11	"encoding/xml"
12	"errors"
13	"fmt"
14	"log"
15	"reflect"
16	"strings"
17
18	"github.com/coyim/coyim/xmpp/data"
19	"github.com/coyim/coyim/xmpp/interfaces"
20)
21
22var xmlSpecial = map[byte]string{
23	'<':  "&lt;",
24	'>':  "&gt;",
25	'"':  "&quot;",
26	'\'': "&apos;",
27	'&':  "&amp;",
28}
29
30func xmlEscape(s string) string {
31	//TODO: Why not using xml.EscapeText(), from stdlib?
32	var b bytes.Buffer
33	for i := 0; i < len(s); i++ {
34		c := s[i]
35		if s, ok := xmlSpecial[c]; ok {
36			b.WriteString(s)
37		} else {
38			b.WriteByte(c)
39		}
40	}
41	return b.String()
42}
43
44// Scan XML token stream to finc next Element (start or end)
45func nextElement(p *xml.Decoder) (xml.Token, error) {
46	for {
47		t, err := p.Token()
48		if err != nil {
49			return xml.StartElement{}, err
50		}
51
52		switch elem := t.(type) {
53		case xml.StartElement, xml.EndElement:
54			return t, nil
55		case xml.CharData:
56			// https://xmpp.org/rfcs/rfc6120.html#xml-whitespace
57			// rfc6120, section 1.4: "whitespace" is used to refer to any character
58			// or characters matching [...] SP, HTAB, CR, or LF.
59			switch string(elem) {
60			case " ", "\t", "\r", "\n": //TODO: consider more than one whitespace
61				log.Println("xmpp: received whitespace ping")
62			}
63		case xml.ProcInst:
64			if !(elem.Target == "xml" && strings.HasPrefix(string(elem.Inst), "version=")) {
65				log.Printf("xmpp: received unhandled ProcInst element: target=%s inst=%s\n", elem.Target, string(elem.Inst))
66			}
67		default:
68			log.Printf("xmpp: received unhandled element: %#v\n", elem)
69		}
70	}
71}
72
73// Scan XML token stream to find next StartElement.
74func nextStart(p *xml.Decoder) (xml.StartElement, error) {
75	for {
76		t, err := nextElement(p)
77		if err != nil {
78			return xml.StartElement{}, err
79		}
80
81		if start, ok := t.(xml.StartElement); ok {
82			return start, nil
83		}
84	}
85}
86
87// Scan XML token stream for next element and save into val.
88// If val == nil, allocate new element based on proto map.
89// Either way, return val.
90func next(c interfaces.Conn) (xml.Name, interface{}, error) {
91	elem, err := nextElement(c.In())
92	if err != nil {
93		return xml.Name{}, nil, err
94	}
95
96	//FIXME: Why? It does not seem to do anything on a critical section
97	c.Lock().Lock()
98	defer c.Lock().Unlock()
99
100	switch el := elem.(type) {
101	case xml.StartElement:
102		return decodeStartElement(c, el)
103	case xml.EndElement:
104		return decodeEndElement(el)
105	}
106
107	return xml.Name{}, nil, fmt.Errorf("unexpected element %s", elem)
108}
109
110func decodeStartElement(c interfaces.Conn, se xml.StartElement) (xml.Name, interface{}, error) {
111
112	// Put it in an interface and allocate one.
113	var nv interface{}
114	if t, e := c.CustomStorage()[se.Name]; e {
115		nv = reflect.New(t).Interface()
116	} else if t, e := defaultStorage[se.Name]; e {
117		nv = reflect.New(t).Interface()
118	} else {
119		return xml.Name{}, nil, errors.New("unexpected XMPP message " +
120			se.Name.Space + " <" + se.Name.Local + "/>")
121	}
122
123	// Unmarshal into that storage.
124	if err := c.In().DecodeElement(nv, &se); err != nil {
125		return xml.Name{}, nil, err
126	}
127
128	return se.Name, nv, nil
129}
130
131func decodeEndElement(ce xml.EndElement) (xml.Name, interface{}, error) {
132	switch ce.Name {
133	case xml.Name{Space: NsStream, Local: "stream"}:
134		return ce.Name, &data.StreamClose{}, nil
135	}
136
137	return ce.Name, nil, nil
138}
139
140var defaultStorage = map[xml.Name]reflect.Type{
141	xml.Name{Space: NsStream, Local: "features"}: reflect.TypeOf(data.StreamFeatures{}),
142	xml.Name{Space: NsStream, Local: "error"}:    reflect.TypeOf(data.StreamError{}),
143	xml.Name{Space: NsTLS, Local: "starttls"}:    reflect.TypeOf(data.StartTLS{}),
144	xml.Name{Space: NsTLS, Local: "proceed"}:     reflect.TypeOf(data.ProceedTLS{}),
145	xml.Name{Space: NsTLS, Local: "failure"}:     reflect.TypeOf(data.FailureTLS{}),
146	xml.Name{Space: NsSASL, Local: "mechanisms"}: reflect.TypeOf(data.SaslMechanisms{}),
147	xml.Name{Space: NsSASL, Local: "challenge"}:  reflect.TypeOf(""),
148	xml.Name{Space: NsSASL, Local: "response"}:   reflect.TypeOf(""),
149	xml.Name{Space: NsSASL, Local: "abort"}:      reflect.TypeOf(data.SaslAbort{}),
150	xml.Name{Space: NsSASL, Local: "success"}:    reflect.TypeOf(data.SaslSuccess{}),
151	xml.Name{Space: NsSASL, Local: "failure"}:    reflect.TypeOf(data.SaslFailure{}),
152	xml.Name{Space: NsBind, Local: "bind"}:       reflect.TypeOf(data.BindBind{}),
153	xml.Name{Space: NsClient, Local: "message"}:  reflect.TypeOf(data.ClientMessage{}),
154	xml.Name{Space: NsClient, Local: "presence"}: reflect.TypeOf(data.ClientPresence{}),
155	xml.Name{Space: NsClient, Local: "iq"}:       reflect.TypeOf(data.ClientIQ{}),
156	xml.Name{Space: NsClient, Local: "error"}:    reflect.TypeOf(data.StanzaError{}),
157}
158