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 '<': "<", 24 '>': ">", 25 '"': """, 26 '\'': "'", 27 '&': "&", 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