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