roster.go
changeset 98 c9cc4eda6dce
parent 72 53f15893a1a7
child 110 7696e6a01709
equal deleted inserted replaced
88:d2ec96c80efe 98:c9cc4eda6dce
     3 // license that can be found in the LICENSE file.
     3 // license that can be found in the LICENSE file.
     4 
     4 
     5 package xmpp
     5 package xmpp
     6 
     6 
     7 import (
     7 import (
       
     8 	"encoding/xml"
     8 	"fmt"
     9 	"fmt"
     9 	"os"
       
    10 	"xml"
       
    11 )
    10 )
    12 
    11 
    13 // This file contains support for roster management, RFC 3921, Section 7.
    12 // This file contains support for roster management, RFC 3921, Section 7.
    14 
    13 
    15 var rosterExt Extension = Extension{StanzaHandlers: map[string]func(*xml.Name) interface{}{NsRoster: newRosterQuery}, Start: startRosterFilter}
    14 var rosterExt Extension = Extension{StanzaHandlers: map[string]func(*xml.Name) interface{}{NsRoster: newRosterQuery}, Start: startRosterFilter}
    16 
    15 
    17 // Roster query/result
    16 // Roster query/result
    18 type RosterQuery struct {
    17 type RosterQuery struct {
    19 	XMLName xml.Name `xml:"jabber:iq:roster query"`
    18 	XMLName xml.Name `xml:"jabber:iq:roster query"`
    20 	Item    []RosterItem
    19 	Item    []RosterItem `xml:"item"`
    21 }
    20 }
    22 
    21 
    23 // See RFC 3921, Section 7.1.
    22 // See RFC 3921, Section 7.1.
    24 type RosterItem struct {
    23 type RosterItem struct {
    25 	XMLName      xml.Name `xml:"item"`
    24 	XMLName      xml.Name `xml:"jabber:iq:roster item"`
    26 	Jid          string   `xml:"attr"`
    25 	Jid          string   `xml:"jid,attr"`
    27 	Subscription string   `xml:"attr"`
    26 	Subscription string   `xml:"subscription,attr"`
    28 	Name         string   `xml:"attr"`
    27 	Name         string   `xml:"name,attr"`
    29 	Group        []string
    28 	Group        []string
    30 }
    29 }
    31 
    30 
    32 type rosterClient struct {
    31 type rosterClient struct {
    33 	rosterChan   <-chan []RosterItem
    32 	rosterChan   <-chan []RosterItem
    44 }
    43 }
    45 
    44 
    46 // Synchronously fetch this entity's roster from the server and cache
    45 // Synchronously fetch this entity's roster from the server and cache
    47 // that information. This is called once from a fairly deep call stack
    46 // that information. This is called once from a fairly deep call stack
    48 // as part of XMPP negotiation.
    47 // as part of XMPP negotiation.
    49 func fetchRoster(client *Client) os.Error {
    48 func fetchRoster(client *Client) error {
    50 	rosterUpdate := rosterClients[client.Uid].rosterUpdate
    49 	rosterUpdate := rosterClients[client.Uid].rosterUpdate
    51 
    50 
    52 	iq := &Iq{From: client.Jid.String(), Id: <-Id, Type: "get",
    51 	iq := &Iq{From: client.Jid.String(), Id: <-Id, Type: "get",
    53 		Nested: []interface{}{RosterQuery{}}}
    52 		Nested: []interface{}{RosterQuery{}}}
    54 	ch := make(chan os.Error)
    53 	ch := make(chan error)
    55 	f := func(st Stanza) bool {
    54 	f := func(st Stanza) bool {
    56 		defer close(ch)
    55 		defer close(ch)
       
    56 		iq, ok := st.(*Iq)
       
    57 		if !ok {
       
    58 			ch <- fmt.Errorf("response to iq wasn't iq: %s", st)
       
    59 			return false
       
    60 		}
    57 		if iq.Type == "error" {
    61 		if iq.Type == "error" {
    58 			ch <- iq.Error
    62 			ch <- iq.Error
    59 			return false
    63 			return false
    60 		}
    64 		}
    61 		var rq *RosterQuery
    65 		var rq *RosterQuery
    62 		for _, ele := range st.GetNested() {
    66 		for _, ele := range iq.Nested {
    63 			if q, ok := ele.(*RosterQuery); ok {
    67 			if q, ok := ele.(*RosterQuery); ok {
    64 				rq = q
    68 				rq = q
    65 				break
    69 				break
    66 			}
    70 			}
    67 		}
    71 		}
    68 		if rq == nil {
    72 		if rq == nil {
    69 			ch <- os.NewError(fmt.Sprintf(
    73 			ch <- fmt.Errorf(
    70 				"Roster query result not query: %v", st))
    74 				"Roster query result not query: %v", st)
    71 			return false
    75 			return false
    72 		}
    76 		}
    73 		for _, item := range rq.Item {
    77 		for _, item := range rq.Item {
    74 			rosterUpdate <- item
    78 			rosterUpdate <- item
    75 		}
    79 		}
   103 		rosterUpdate: rosterUpdate}
   107 		rosterUpdate: rosterUpdate}
   104 	go feedRoster(rosterCh, rosterUpdate)
   108 	go feedRoster(rosterCh, rosterUpdate)
   105 }
   109 }
   106 
   110 
   107 func maybeUpdateRoster(client *Client, st Stanza) {
   111 func maybeUpdateRoster(client *Client, st Stanza) {
       
   112 	iq, ok := st.(*Iq)
       
   113 	if !ok {
       
   114 		return
       
   115 	}
       
   116 
   108 	rosterUpdate := rosterClients[client.Uid].rosterUpdate
   117 	rosterUpdate := rosterClients[client.Uid].rosterUpdate
   109 
   118 
   110 	var rq *RosterQuery
   119 	var rq *RosterQuery
   111 	for _, ele := range st.GetNested() {
   120 	for _, ele := range iq.Nested {
   112 		if q, ok := ele.(*RosterQuery); ok {
   121 		if q, ok := ele.(*RosterQuery); ok {
   113 			rq = q
   122 			rq = q
   114 			break
   123 			break
   115 		}
   124 		}
   116 	}
   125 	}
   117 	if st.GetName() == "iq" && st.GetType() == "set" && rq != nil {
   126 	if iq.Type == "set" && rq != nil {
   118 		for _, item := range rq.Item {
   127 		for _, item := range rq.Item {
   119 			rosterUpdate <- item
   128 			rosterUpdate <- item
   120 		}
   129 		}
   121 		// Send a reply.
   130 		// Send a reply.
   122 		iq := &Iq{To: st.GetFrom(), Id: st.GetId(), Type: "result"}
   131 		reply := &Iq{To: iq.From, Id: iq.Id, Type: "result"}
   123 		client.Out <- iq
   132 		client.Out <- reply
   124 	}
   133 	}
   125 }
   134 }
   126 
   135 
   127 func feedRoster(rosterCh chan<- []RosterItem, rosterUpdate <-chan RosterItem) {
   136 func feedRoster(rosterCh chan<- []RosterItem, rosterUpdate <-chan RosterItem) {
   128 	roster := make(map[string]RosterItem)
   137 	roster := make(map[string]RosterItem)
   129 	snapshot := []RosterItem{}
   138 	snapshot := []RosterItem{}
   130 	for {
   139 	for {
   131 		select {
   140 		select {
   132 		case newIt := <-rosterUpdate:
   141 		case newIt := <-rosterUpdate:
   133 			if newIt.Subscription == "remove" {
   142 			if newIt.Subscription == "remove" {
   134 				roster[newIt.Jid] = RosterItem{}, false
   143 				delete(roster, newIt.Jid)
   135 			} else {
   144 			} else {
   136 				roster[newIt.Jid] = newIt
   145 				roster[newIt.Jid] = newIt
   137 			}
   146 			}
   138 		case rosterCh <- snapshot:
   147 		case rosterCh <- snapshot:
   139 		}
   148 		}