packagexmpp// This file contains support for roster management, RFC 3921, Section 7.import("encoding/xml""reflect")// Roster query/resulttypeRosterQuerystruct{XMLNamexml.Name`xml:"jabber:iq:roster query"`Item[]RosterItem`xml:"item"`}// See RFC 3921, Section 7.1.typeRosterItemstruct{XMLNamexml.Name`xml:"jabber:iq:roster item"`Jidstring`xml:"jid,attr"`Subscriptionstring`xml:"subscription,attr"`Namestring`xml:"name,attr"`Group[]string}typerosterCbstruct{idstringcbfunc()}typeRosterstruct{Extensiongetchan[]RosterItemcallbackschanrosterCbtoServerchanStanza}typerosterClientstruct{rosterChan<-chan[]RosterItemrosterUpdatechan<-RosterItem}func(r*Roster)rosterMgr(upd<-chanStanza){roster:=make(map[string]RosterItem)waits:=make(map[string]func())varsnapshot[]RosterItemfor{select{casestan,ok:=<-upd:if!ok{return}hdr:=stan.GetHeader()iff:=waits[hdr.Id];f!=nil{delete(waits,hdr.Id)f()}iq,ok:=stan.(*Iq)if!ok{continue}ifiq.Type!="result"{continue}varrq*RosterQueryfor_,ele:=rangeiq.Nested{ifq,ok:=ele.(*RosterQuery);ok{rq=qbreak}}ifrq==nil{continue}for_,item:=rangerq.Item{roster[item.Jid]=item}snapshot=[]RosterItem{}for_,ri:=rangeroster{snapshot=append(snapshot,ri)}caser.get<-snapshot:casecb:=<-r.callbacks:waits[cb.id]=cb.cb}}}func(r*Roster)makeFilters()(Filter,Filter){rosterUpdate:=make(chanStanza)gor.rosterMgr(rosterUpdate)recv:=func(in<-chanStanza,outchan<-Stanza){deferclose(out)forstan:=rangein{rosterUpdate<-stanout<-stan}}send:=func(in<-chanStanza,outchan<-Stanza){deferclose(out)for{select{casestan,ok:=<-in:if!ok{return}out<-stancasestan:=<-r.toServer:out<-stan}}}returnrecv,send}funcnewRosterExt()*Roster{r:=Roster{}r.StanzaHandlers=make(map[xml.Name]reflect.Type)rName:=xml.Name{Space:NsRoster,Local:"query"}r.StanzaHandlers[rName]=reflect.TypeOf(RosterQuery{})r.RecvFilter,r.SendFilter=r.makeFilters()r.get=make(chan[]RosterItem)r.callbacks=make(chanrosterCb)r.toServer=make(chanStanza)return&r}// Return the most recent snapshot of the roster status. This is// updated automatically as roster updates are received from the// server, but especially in response to calls to Update().func(r*Roster)Get()[]RosterItem{return<-r.get}// Synchronously fetch this entity's roster from the server and cache// that information. The client can access the roster by watching for// RosterQuery objects or by calling Get().func(r*Roster)Update(){iq:=&Iq{Header:Header{Type:"get",Id:NextId(),Nested:[]interface{}{RosterQuery{}}}}waitchan:=make(chanint)done:=func(){close(waitchan)}r.waitFor(iq.Id,done)r.toServer<-iq<-waitchan}func(r*Roster)waitFor(idstring,cbfunc()){r.callbacks<-rosterCb{id:id,cb:cb}}