// 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("fmt""os""xml")// This file contains support for roster management, RFC 3921, Section 7.// Roster query/resulttypeRosterQuerystruct{XMLNamexml.Name`xml:"jabber:iq:roster query"`Item[]RosterItem}// See RFC 3921, Section 7.1.typeRosterItemstruct{XMLNamexml.Name`xml:"item"`Jidstring`xml:"attr"`Subscriptionstring`xml:"attr"`Namestring`xml:"attr"`Group[]string}// 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.func(cl*Client)fetchRoster()os.Error{iq:=&Iq{From:cl.Jid.String(),Id:<-cl.Id,Type:"get",Nested:RosterQuery{}}ch:=make(chanos.Error)f:=func(stStanza)bool{ifiq.Type=="error"{ch<-iq.Errorreturnfalse}rq,ok:=st.GetNested().(*RosterQuery)if!ok{ch<-os.NewError(fmt.Sprintf("Roster query result not query: %v",st))returnfalse}cl.roster=make(map[string]*RosterItem,len(rq.Item))for_,item:=range(rq.Item){cl.roster[item.Jid]=&item}ch<-nilreturnfalse}cl.HandleStanza(iq.Id,f)cl.Out<-iq// Wait for f to complete.return<-ch}// Returns the current roster of other entities which this one has a// relationship with. Changes to the roster will be signaled by an// appropriate Iq appearing on Client.In. See RFC 3921, Section 7.4.func(cl*Client)Roster()map[string]*RosterItem{r:=make(map[string]*RosterItem)forkey,val:=range(cl.roster){r[key]=val}returnr}// The roster filter updates the Client's representation of the// roster, but it lets the relevant stanzas through.func(cl*Client)startRosterFilter(){out:=make(chanStanza)in:=cl.AddFilter(out)gofunc(in<-chanStanza,outchan<-Stanza){deferclose(out)forst:=range(in){cl.maybeUpdateRoster(st)out<-st}}(in,out)}// BUG(cjyar) This isn't actually thread safe, though it's unlikely it// will fail in practice. Either the roster should be protected with a// mutex, or we should make the roster available on a channel instead// of via a method call.func(cl*Client)maybeUpdateRoster(stStanza){rq,ok:=st.GetNested().(*RosterQuery)ifst.GetName()=="iq"&&st.GetType()=="set"&&ok{for_,item:=range(rq.Item){ifitem.Subscription=="remove"{cl.roster[item.Jid]=nil}else{cl.roster[item.Jid]=&item}}}}