roster.go
changeset 72 53f15893a1a7
parent 61 16513974d273
child 74 e619e18dcec3
child 98 c9cc4eda6dce
equal deleted inserted replaced
71:578c2a83dc18 72:53f15893a1a7
    10 	"xml"
    10 	"xml"
    11 )
    11 )
    12 
    12 
    13 // This file contains support for roster management, RFC 3921, Section 7.
    13 // This file contains support for roster management, RFC 3921, Section 7.
    14 
    14 
    15 var rosterExt Extension = Extension{StanzaHandlers:
    15 var rosterExt Extension = Extension{StanzaHandlers: map[string]func(*xml.Name) interface{}{NsRoster: newRosterQuery}, Start: startRosterFilter}
    16 	map[string] func(*xml.Name) interface{}{NsRoster:
       
    17 		newRosterQuery}, Start: startRosterFilter}
       
    18 
    16 
    19 // Roster query/result
    17 // Roster query/result
    20 type RosterQuery struct {
    18 type RosterQuery struct {
    21 	XMLName xml.Name `xml:"jabber:iq:roster query"`
    19 	XMLName xml.Name `xml:"jabber:iq:roster query"`
    22 	Item []RosterItem
    20 	Item    []RosterItem
    23 }
    21 }
    24 
    22 
    25 // See RFC 3921, Section 7.1.
    23 // See RFC 3921, Section 7.1.
    26 type RosterItem struct {
    24 type RosterItem struct {
    27 	XMLName xml.Name `xml:"item"`
    25 	XMLName      xml.Name `xml:"item"`
    28 	Jid string `xml:"attr"`
    26 	Jid          string   `xml:"attr"`
    29 	Subscription string `xml:"attr"`
    27 	Subscription string   `xml:"attr"`
    30 	Name string `xml:"attr"`
    28 	Name         string   `xml:"attr"`
    31 	Group []string
    29 	Group        []string
    32 }
    30 }
    33 
    31 
    34 type rosterClient struct {
    32 type rosterClient struct {
    35 	rosterChan <-chan []RosterItem
    33 	rosterChan   <-chan []RosterItem
    36 	rosterUpdate chan<- RosterItem
    34 	rosterUpdate chan<- RosterItem
    37 }
    35 }
    38 
    36 
    39 var (
    37 var (
    40 	rosterClients = make(map[string] rosterClient)
    38 	rosterClients = make(map[string]rosterClient)
    41 )
    39 )
    42 
    40 
    43 // Implicitly becomes part of NewClient's extStanza arg.
    41 // Implicitly becomes part of NewClient's extStanza arg.
    44 func newRosterQuery(name *xml.Name) interface{} {
    42 func newRosterQuery(name *xml.Name) interface{} {
    45 	return &RosterQuery{}
    43 	return &RosterQuery{}
    49 // that information. This is called once from a fairly deep call stack
    47 // that information. This is called once from a fairly deep call stack
    50 // as part of XMPP negotiation.
    48 // as part of XMPP negotiation.
    51 func fetchRoster(client *Client) os.Error {
    49 func fetchRoster(client *Client) os.Error {
    52 	rosterUpdate := rosterClients[client.Uid].rosterUpdate
    50 	rosterUpdate := rosterClients[client.Uid].rosterUpdate
    53 
    51 
    54 	iq := &Iq{From: client.Jid.String(), Id: <- Id, Type: "get",
    52 	iq := &Iq{From: client.Jid.String(), Id: <-Id, Type: "get",
    55 		Nested: []interface{}{RosterQuery{}}}
    53 		Nested: []interface{}{RosterQuery{}}}
    56 	ch := make(chan os.Error)
    54 	ch := make(chan os.Error)
    57 	f := func(st Stanza) bool {
    55 	f := func(st Stanza) bool {
    58 		defer close(ch)
    56 		defer close(ch)
    59 		if iq.Type == "error" {
    57 		if iq.Type == "error" {
    60 			ch <- iq.Error
    58 			ch <- iq.Error
    61 			return false
    59 			return false
    62 		}
    60 		}
    63 		var rq *RosterQuery
    61 		var rq *RosterQuery
    64 		for _, ele := range(st.GetNested()) {
    62 		for _, ele := range st.GetNested() {
    65 			if q, ok := ele.(*RosterQuery) ; ok {
    63 			if q, ok := ele.(*RosterQuery); ok {
    66 				rq = q
    64 				rq = q
    67 				break
    65 				break
    68 			}
    66 			}
    69 		}
    67 		}
    70 		if rq == nil {
    68 		if rq == nil {
    71 			ch <- os.NewError(fmt.Sprintf(
    69 			ch <- os.NewError(fmt.Sprintf(
    72 				"Roster query result not query: %v", st))
    70 				"Roster query result not query: %v", st))
    73 			return false
    71 			return false
    74 		}
    72 		}
    75 		for _, item := range(rq.Item) {
    73 		for _, item := range rq.Item {
    76 			rosterUpdate <- item
    74 			rosterUpdate <- item
    77 		}
    75 		}
    78 		ch <- nil
    76 		ch <- nil
    79 		return false
    77 		return false
    80 	}
    78 	}
    81 	client.HandleStanza(iq.Id, f)
    79 	client.HandleStanza(iq.Id, f)
    82 	client.Out <- iq
    80 	client.Out <- iq
    83 	// Wait for f to complete.
    81 	// Wait for f to complete.
    84 	return <- ch
    82 	return <-ch
    85 }
    83 }
    86 
    84 
    87 // The roster filter updates the Client's representation of the
    85 // The roster filter updates the Client's representation of the
    88 // roster, but it lets the relevant stanzas through. This also starts
    86 // roster, but it lets the relevant stanzas through. This also starts
    89 // the roster feeder, which is the goroutine that provides data on
    87 // the roster feeder, which is the goroutine that provides data on
    91 func startRosterFilter(client *Client) {
    89 func startRosterFilter(client *Client) {
    92 	out := make(chan Stanza)
    90 	out := make(chan Stanza)
    93 	in := client.AddFilter(out)
    91 	in := client.AddFilter(out)
    94 	go func(in <-chan Stanza, out chan<- Stanza) {
    92 	go func(in <-chan Stanza, out chan<- Stanza) {
    95 		defer close(out)
    93 		defer close(out)
    96 		for st := range(in) {
    94 		for st := range in {
    97 			maybeUpdateRoster(client, st)
    95 			maybeUpdateRoster(client, st)
    98 			out <- st
    96 			out <- st
    99 		}
    97 		}
   100 	}(in, out)
    98 	}(in, out)
   101 
    99 
   108 
   106 
   109 func maybeUpdateRoster(client *Client, st Stanza) {
   107 func maybeUpdateRoster(client *Client, st Stanza) {
   110 	rosterUpdate := rosterClients[client.Uid].rosterUpdate
   108 	rosterUpdate := rosterClients[client.Uid].rosterUpdate
   111 
   109 
   112 	var rq *RosterQuery
   110 	var rq *RosterQuery
   113 	for _, ele := range(st.GetNested()) {
   111 	for _, ele := range st.GetNested() {
   114 		if q, ok := ele.(*RosterQuery) ; ok {
   112 		if q, ok := ele.(*RosterQuery); ok {
   115 			rq = q
   113 			rq = q
   116 			break
   114 			break
   117 		}
   115 		}
   118 	}
   116 	}
   119 	if st.GetName() == "iq" && st.GetType() == "set" && rq != nil {
   117 	if st.GetName() == "iq" && st.GetType() == "set" && rq != nil {
   120 		for _, item := range(rq.Item) {
   118 		for _, item := range rq.Item {
   121 			rosterUpdate <- item
   119 			rosterUpdate <- item
   122 		}
   120 		}
   123 		// Send a reply.
   121 		// Send a reply.
   124 		iq := &Iq{To: st.GetFrom(), Id: st.GetId(), Type:
   122 		iq := &Iq{To: st.GetFrom(), Id: st.GetId(), Type: "result"}
   125 			"result"}
       
   126 		client.Out <- iq
   123 		client.Out <- iq
   127 	}
   124 	}
   128 }
   125 }
   129 
   126 
   130 func feedRoster(rosterCh chan<- []RosterItem, rosterUpdate <-chan RosterItem) {
   127 func feedRoster(rosterCh chan<- []RosterItem, rosterUpdate <-chan RosterItem) {
   131 	roster := make(map[string] RosterItem)
   128 	roster := make(map[string]RosterItem)
   132 	snapshot := []RosterItem{}
   129 	snapshot := []RosterItem{}
   133 	for {
   130 	for {
   134 		select {
   131 		select {
   135 		case newIt := <-rosterUpdate:
   132 		case newIt := <-rosterUpdate:
   136 			if newIt.Subscription == "remove" {
   133 			if newIt.Subscription == "remove" {
   139 				roster[newIt.Jid] = newIt
   136 				roster[newIt.Jid] = newIt
   140 			}
   137 			}
   141 		case rosterCh <- snapshot:
   138 		case rosterCh <- snapshot:
   142 		}
   139 		}
   143 		snapshot = make([]RosterItem, 0, len(roster))
   140 		snapshot = make([]RosterItem, 0, len(roster))
   144 		for _, v := range(roster) {
   141 		for _, v := range roster {
   145 			snapshot = append(snapshot, v)
   142 			snapshot = append(snapshot, v)
   146 		}
   143 		}
   147 	}
   144 	}
   148 }
   145 }
   149 
   146 
   150 // Retrieve a snapshot of the roster for the given Client.
   147 // Retrieve a snapshot of the roster for the given Client.
   151 func Roster(client *Client) []RosterItem {
   148 func Roster(client *Client) []RosterItem {
   152 	rosterChan := rosterClients[client.Uid].rosterChan
   149 	rosterChan := rosterClients[client.Uid].rosterChan
   153 	return <- rosterChan
   150 	return <-rosterChan
   154 }
   151 }