packagexmpp// This file contains data structures.import("bytes""encoding/xml""flag""fmt"// BUG(cjyar): Doesn't use stringprep. Could try the implementation at// "code.google.com/p/go-idn/src/stringprep""reflect""regexp""strings")// JID represents an entity that can communicate with other// entities. It looks like node@domain/resource. Node and resource are// sometimes optional.typeJIDstruct{NodestringDomainstringResourcestring}var_fmt.Stringer=&JID{}var_flag.Value=&JID{}// XMPP's <stream:stream> XML elementtypestreamstruct{XMLNamexml.Name`xml:"stream=http://etherx.jabber.org/streams stream"`Tostring`xml:"to,attr"`Fromstring`xml:"from,attr"`Idstring`xml:"id,attr"`Langstring`xml:"http://www.w3.org/XML/1998/namespace lang,attr"`Versionstring`xml:"version,attr"`}var_fmt.Stringer=&stream{}// <stream:error>typestreamErrorstruct{XMLNamexml.Name`xml:"http://etherx.jabber.org/streams error"`AnyGeneric`xml:",any"`Text*errText}typeerrTextstruct{XMLNamexml.Name`xml:"urn:ietf:params:xml:ns:xmpp-streams text"`Langstring`xml:"http://www.w3.org/XML/1998/namespace lang,attr"`Textstring`xml:",chardata"`}typeFeaturesstruct{Starttls*starttls`xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`Mechanismsmechs`xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`Bind*bindIqSession*GenericAny*Generic}typestarttlsstruct{XMLNamexml.NameRequired*string}typemechsstruct{Mechanism[]string`xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanism"`}typeauthstruct{XMLNamexml.NameChardatastring`xml:",chardata"`Mechanismstring`xml:"mechanism,attr,omitempty"`Any*Generic}typeStanzainterface{GetHeader()*Header}// One of the three core XMPP stanza types: iq, message, presence. See// RFC3920, section 9.typeHeaderstruct{Tostring`xml:"to,attr,omitempty"`Fromstring`xml:"from,attr,omitempty"`Idstring`xml:"id,attr,omitempty"`Typestring`xml:"type,attr,omitempty"`Langstring`xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`Innerxmlstring`xml:",innerxml"`Error*ErrorNested[]interface{}}// message stanzatypeMessagestruct{XMLNamexml.Name`xml:"jabber:client message"`HeaderSubject*Generic`xml:"jabber:client subject"`Body*Generic`xml:"jabber:client body"`Thread*Generic`xml:"jabber:client thread"`}var_Stanza=&Message{}// presence stanzatypePresencestruct{XMLNamexml.Name`xml:"presence"`HeaderShow*Generic`xml:"jabber:client show"`Status*Generic`xml:"jabber:client status"`Priority*Generic`xml:"jabber:client priority"`}var_Stanza=&Presence{}// iq stanzatypeIqstruct{XMLNamexml.Name`xml:"iq"`Header}var_Stanza=&Iq{}// Describes an XMPP stanza error. See RFC 3920, Section 9.3.typeErrorstruct{XMLNamexml.Name`xml:"error"`// The error type attribute.Typestring`xml:"type,attr"`// Any nested element, if present.Any*Generic}var_error=&Error{}// Used for resource binding as a nested element inside <iq/>.typebindIqstruct{XMLNamexml.Name`xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`Resource*string`xml:"resource"`Jid*string`xml:"jid"`}// Holds an XML element not described by the more specific types.typeGenericstruct{XMLNamexml.NameAny*Generic`xml:",any"`Chardatastring`xml:",chardata"`}var_fmt.Stringer=&Generic{}func(jid*JID)String()string{result:=jid.Domainifjid.Node!=""{result=jid.Node+"@"+result}ifjid.Resource!=""{result=result+"/"+jid.Resource}returnresult}// Set implements flag.Value. It returns true if it successfully// parses the string.func(jid*JID)Set(valstring)error{r:=regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$")parts:=r.FindStringSubmatch(val)ifparts==nil{returnfmt.Errorf("%s doesn't match user@domain/resource",val)}// jid.Node = stringprep.Nodeprep(parts[2])// jid.Domain = stringprep.Nodeprep(parts[3])// jid.Resource = stringprep.Resourceprep(parts[5])jid.Node=parts[2]jid.Domain=parts[3]jid.Resource=parts[5]returnnil}func(s*stream)String()string{varbufbytes.Bufferbuf.WriteString(`<stream:stream xmlns="`)buf.WriteString(NsClient)buf.WriteString(`" xmlns:stream="`)buf.WriteString(NsStream)buf.WriteString(`"`)ifs.To!=""{buf.WriteString(` to="`)xml.Escape(&buf,[]byte(s.To))buf.WriteString(`"`)}ifs.From!=""{buf.WriteString(` from="`)xml.Escape(&buf,[]byte(s.From))buf.WriteString(`"`)}ifs.Id!=""{buf.WriteString(` id="`)xml.Escape(&buf,[]byte(s.Id))buf.WriteString(`"`)}ifs.Lang!=""{buf.WriteString(` xml:lang="`)xml.Escape(&buf,[]byte(s.Lang))buf.WriteString(`"`)}ifs.Version!=""{buf.WriteString(` version="`)xml.Escape(&buf,[]byte(s.Version))buf.WriteString(`"`)}buf.WriteString(">")returnbuf.String()}funcparseStream(sexml.StartElement)(*stream,error){s:=&stream{}for_,attr:=rangese.Attr{switchstrings.ToLower(attr.Name.Local){case"to":s.To=attr.Valuecase"from":s.From=attr.Valuecase"id":s.Id=attr.Valuecase"lang":s.Lang=attr.Valuecase"version":s.Version=attr.Value}}returns,nil}func(iq*Iq)GetHeader()*Header{return&iq.Header}func(m*Message)GetHeader()*Header{return&m.Header}func(p*Presence)GetHeader()*Header{return&p.Header}func(u*Generic)String()string{ifu==nil{return"nil"}varsubstringifu.Any!=nil{sub=u.Any.String()}returnfmt.Sprintf("<%s %s>%s%s</%s %s>",u.XMLName.Space,u.XMLName.Local,sub,u.Chardata,u.XMLName.Space,u.XMLName.Local)}func(er*Error)Error()string{buf,err:=xml.Marshal(er)iferr!=nil{Warn.Log("double bad error: couldn't marshal error")return"unreadable error"}returnstring(buf)}varbindExtExtension=Extension{}funcinit(){bindExt.StanzaHandlers=make(map[xml.Name]reflect.Type)bName:=xml.Name{Space:NsBind,Local:"bind"}bindExt.StanzaHandlers[bName]=reflect.TypeOf(bindIq{})}