Step 3 of converting to interface Stanza and embedded struct Header.
// Copyright 2011 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.packagexmppimport("encoding/xml""fmt")// This file contains support for roster management, RFC 3921, Section 7.varrosterExtExtension=Extension{StanzaHandlers:map[string]func(*xml.Name)interface{}{NsRoster:newRosterQuery},Start:startRosterFilter}// 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}typerosterClientstruct{rosterChan<-chan[]RosterItemrosterUpdatechan<-RosterItem}var(rosterClients=make(map[string]rosterClient))// Implicitly becomes part of NewClient's extStanza arg.funcnewRosterQuery(name*xml.Name)interface{}{return&RosterQuery{}}// Synchronously fetch this entity's roster from the server and cache// that information. This is called once from a fairly deep call stack// as part of XMPP negotiation.funcfetchRoster(client*Client)error{rosterUpdate:=rosterClients[client.Uid].rosterUpdateiq:=&Iq{Header:Header{From:client.Jid.String(),Type:"get",Id:<-Id,Nested:[]interface{}{RosterQuery{}}}}ch:=make(chanerror)f:=func(vStanza)bool{deferclose(ch)iq,ok:=v.(*Iq)if!ok{ch<-fmt.Errorf("response to iq wasn't iq: %s",v)returnfalse}ifiq.Type=="error"{ch<-iq.Errorreturnfalse}varrq*RosterQueryfor_,ele:=rangeiq.Nested{ifq,ok:=ele.(*RosterQuery);ok{rq=qbreak}}ifrq==nil{ch<-fmt.Errorf("Roster query result not query: %v",v)returnfalse}for_,item:=rangerq.Item{rosterUpdate<-item}ch<-nilreturnfalse}client.HandleStanza(iq.Id,f)client.Out<-iq// Wait for f to complete.return<-ch}// The roster filter updates the Client's representation of the// roster, but it lets the relevant stanzas through. This also starts// the roster feeder, which is the goroutine that provides data on// client.Roster.funcstartRosterFilter(client*Client){out:=make(chanStanza)in:=client.AddFilter(out)gofunc(in<-chanStanza,outchan<-Stanza){deferclose(out)forst:=rangein{maybeUpdateRoster(client,st)out<-st}}(in,out)rosterCh:=make(chan[]RosterItem)rosterUpdate:=make(chanRosterItem)rosterClients[client.Uid]=rosterClient{rosterChan:rosterCh,rosterUpdate:rosterUpdate}gofeedRoster(rosterCh,rosterUpdate)}funcmaybeUpdateRoster(client*Client,stinterface{}){iq,ok:=st.(*Iq)if!ok{return}rosterUpdate:=rosterClients[client.Uid].rosterUpdatevarrq*RosterQueryfor_,ele:=rangeiq.Nested{ifq,ok:=ele.(*RosterQuery);ok{rq=qbreak}}ifiq.Type=="set"&&rq!=nil{for_,item:=rangerq.Item{rosterUpdate<-item}// Send a reply.reply:=&Iq{Header:Header{To:iq.From,Id:iq.Id,Type:"result"}}client.Out<-reply}}funcfeedRoster(rosterChchan<-[]RosterItem,rosterUpdate<-chanRosterItem){roster:=make(map[string]RosterItem)snapshot:=[]RosterItem{}for{select{casenewIt:=<-rosterUpdate:ifnewIt.Subscription=="remove"{delete(roster,newIt.Jid)}else{roster[newIt.Jid]=newIt}caserosterCh<-snapshot:}snapshot=make([]RosterItem,0,len(roster))for_,v:=rangeroster{snapshot=append(snapshot,v)}}}// Retrieve a snapshot of the roster for the given Client.funcRoster(client*Client)[]RosterItem{rosterChan:=rosterClients[client.Uid].rosterChanreturn<-rosterChan}