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 } |