// This layer of the XMPP protocol translates between bytes and XMLish// structures.packagexmppimport("encoding/xml""fmt""io""reflect""strings")// Read bytes from a reader, unmarshal them as XML into structures of// the appropriate type, and send those structures on a channel.funcrecvXml(rio.Reader,chchan<-interface{},extStanzamap[xml.Name]reflect.Type){if_,ok:=Debug.(*noLog);!ok{pr,pw:=io.Pipe()gotee(r,pw,"S: ")r=pr}deferclose(ch)// This trick loads our namespaces into the parser.nsstr:=fmt.Sprintf(`<a xmlns="%s" xmlns:stream="%s">`,NsClient,NsStream)nsrdr:=strings.NewReader(nsstr)p:=xml.NewDecoder(io.MultiReader(nsrdr,r))p.Token()Loop:for{// Sniff the next token on the stream.t,err:=p.Token()ift==nil{iferr!=io.EOF{Warn.Logf("read: %s",err)}break}varsexml.StartElementvarokboolifse,ok=t.(xml.StartElement);!ok{continue}// Allocate the appropriate structure for this token.varobjinterface{}switchse.Name.Space+" "+se.Name.Local{caseNsStream+" stream":st,err:=parseStream(se)iferr!=nil{Warn.Logf("unmarshal stream: %s",err)breakLoop}ch<-stcontinuecase"stream error",NsStream+" error":obj=&streamError{}caseNsStream+" features":obj=&Features{}caseNsTLS+" proceed",NsTLS+" failure":obj=&starttls{}caseNsSASL+" challenge",NsSASL+" failure",NsSASL+" success":obj=&auth{}caseNsClient+" iq":obj=&Iq{}caseNsClient+" message":obj=&Message{}caseNsClient+" presence":obj=&Presence{}default:obj=&Generic{}Info.Logf("Ignoring unrecognized: %s %s",se.Name.Space,se.Name.Local)}// Read the complete XML stanza.err=p.DecodeElement(obj,&se)iferr!=nil{Warn.Logf("unmarshal: %s",err)breakLoop}// If it's a Stanza, we try to unmarshal its innerxml// into objects of the appropriate respective// types. This is specified by our extensions.ifst,ok:=obj.(Stanza);ok{err=parseExtended(st.GetHeader(),extStanza)iferr!=nil{Warn.Logf("ext unmarshal: %s",err)breakLoop}}// Put it on the channel.ch<-obj}}funcparseExtended(st*Header,extStanzamap[xml.Name]reflect.Type)error{// Now parse the stanza's innerxml to find the string that we// can unmarshal this nested element from.reader:=strings.NewReader(st.Innerxml)p:=xml.NewDecoder(reader)for{t,err:=p.Token()iferr==io.EOF{break}iferr!=nil{returnerr}ifse,ok:=t.(xml.StartElement);ok{iftyp,ok:=extStanza[se.Name];ok{nested:=reflect.New(typ).Interface()// Unmarshal the nested element and// stuff it back into the stanza.err:=p.DecodeElement(nested,&se)iferr!=nil{returnerr}st.Nested=append(st.Nested,nested)}}}returnnil}// Receive structures on a channel, marshal them to XML, and send the// bytes on a writer.funcsendXml(wio.Writer,ch<-chaninterface{}){if_,ok:=Debug.(*noLog);!ok{pr,pw:=io.Pipe()gotee(pr,w,"C: ")w=pw}deferfunc(wio.Writer){ifc,ok:=w.(io.Closer);ok{c.Close()}}(w)enc:=xml.NewEncoder(w)forobj:=rangech{ifst,ok:=obj.(*stream);ok{_,err:=w.Write([]byte(st.String()))iferr!=nil{Warn.Logf("write: %s",err)}}else{err:=enc.Encode(obj)iferr!=nil{Warn.Logf("marshal: %s",err)break}}}}