libpurple/protocols/jabber/disco.c

branch
cpw.khc.msnp14
changeset 20478
46933dc62880
parent 20472
6a6d2ef151e6
parent 15884
4de1981757fc
child 20481
65485e2ed8a3
equal deleted inserted replaced
20476:198222e01a7d 20478:46933dc62880
1 /*
2 * purple - Jabber Protocol Plugin
3 *
4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22 #include "internal.h"
23 #include "prefs.h"
24 #include "debug.h"
25
26 #include "buddy.h"
27 #include "google.h"
28 #include "iq.h"
29 #include "disco.h"
30 #include "jabber.h"
31 #include "presence.h"
32 #include "roster.h"
33
34 struct _jabber_disco_info_cb_data {
35 gpointer data;
36 JabberDiscoInfoCallback *callback;
37 };
38
39 #define SUPPORT_FEATURE(x) \
40 feature = xmlnode_new_child(query, "feature"); \
41 xmlnode_set_attrib(feature, "var", x);
42
43
44 void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) {
45 const char *from = xmlnode_get_attrib(packet, "from");
46 const char *type = xmlnode_get_attrib(packet, "type");
47
48 if(!from || !type)
49 return;
50
51 if(!strcmp(type, "get")) {
52 xmlnode *query, *identity, *feature;
53 JabberIq *iq;
54
55 xmlnode *in_query;
56 const char *node = NULL;
57
58 if((in_query = xmlnode_get_child(packet, "query"))) {
59 node = xmlnode_get_attrib(in_query, "node");
60 }
61
62
63 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT,
64 "http://jabber.org/protocol/disco#info");
65
66 jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id"));
67
68 xmlnode_set_attrib(iq->node, "to", from);
69 query = xmlnode_get_child(iq->node, "query");
70
71 if(node)
72 xmlnode_set_attrib(query, "node", node);
73
74 if(!node || !strcmp(node, CAPS0115_NODE "#" VERSION)) {
75
76 identity = xmlnode_new_child(query, "identity");
77 xmlnode_set_attrib(identity, "category", "client");
78 xmlnode_set_attrib(identity, "type", "pc"); /* XXX: bot, console,
79 * handheld, pc, phone,
80 * web */
81 xmlnode_set_attrib(identity, "name", PACKAGE);
82
83 SUPPORT_FEATURE("jabber:iq:last")
84 SUPPORT_FEATURE("jabber:iq:oob")
85 SUPPORT_FEATURE("jabber:iq:time")
86 SUPPORT_FEATURE("jabber:iq:version")
87 SUPPORT_FEATURE("jabber:x:conference")
88 SUPPORT_FEATURE("http://jabber.org/protocol/bytestreams")
89 SUPPORT_FEATURE("http://jabber.org/protocol/disco#info")
90 SUPPORT_FEATURE("http://jabber.org/protocol/disco#items")
91 #if 0
92 SUPPORT_FEATURE("http://jabber.org/protocol/ibb")
93 #endif
94 SUPPORT_FEATURE("http://jabber.org/protocol/muc")
95 SUPPORT_FEATURE("http://jabber.org/protocol/muc#user")
96 SUPPORT_FEATURE("http://jabber.org/protocol/si")
97 SUPPORT_FEATURE("http://jabber.org/protocol/si/profile/file-transfer")
98 SUPPORT_FEATURE("http://jabber.org/protocol/xhtml-im")
99 } else {
100 xmlnode *error, *inf;
101
102 /* XXX: gross hack, implement jabber_iq_set_type or something */
103 xmlnode_set_attrib(iq->node, "type", "error");
104 iq->type = JABBER_IQ_ERROR;
105
106 error = xmlnode_new_child(query, "error");
107 xmlnode_set_attrib(error, "code", "404");
108 xmlnode_set_attrib(error, "type", "cancel");
109 inf = xmlnode_new_child(error, "item-not-found");
110 xmlnode_set_namespace(inf, "urn:ietf:params:xml:ns:xmpp-stanzas");
111 }
112
113 jabber_iq_send(iq);
114 } else if(!strcmp(type, "result")) {
115 xmlnode *query = xmlnode_get_child(packet, "query");
116 xmlnode *child;
117 JabberID *jid;
118 JabberBuddy *jb;
119 JabberBuddyResource *jbr = NULL;
120 JabberCapabilities capabilities = JABBER_CAP_NONE;
121 struct _jabber_disco_info_cb_data *jdicd;
122
123 if((jid = jabber_id_new(from))) {
124 if(jid->resource && (jb = jabber_buddy_find(js, from, TRUE)))
125 jbr = jabber_buddy_find_resource(jb, jid->resource);
126 jabber_id_free(jid);
127 }
128
129 if(jbr)
130 capabilities = jbr->capabilities;
131
132 for(child = query->child; child; child = child->next) {
133 if(child->type != XMLNODE_TYPE_TAG)
134 continue;
135
136 if(!strcmp(child->name, "identity")) {
137 const char *category = xmlnode_get_attrib(child, "category");
138 const char *type = xmlnode_get_attrib(child, "type");
139 if(!category || !type)
140 continue;
141
142 if(!strcmp(category, "conference") && !strcmp(type, "text")) {
143 /* we found a groupchat or MUC server, add it to the list */
144 /* XXX: actually check for protocol/muc or gc-1.0 support */
145 js->chat_servers = g_list_append(js->chat_servers, g_strdup(from));
146 } else if(!strcmp(category, "directory") && !strcmp(type, "user")) {
147 /* we found a JUD */
148 js->user_directories = g_list_append(js->user_directories, g_strdup(from));
149 }
150
151 } else if(!strcmp(child->name, "feature")) {
152 const char *var = xmlnode_get_attrib(child, "var");
153 if(!var)
154 continue;
155
156 if(!strcmp(var, "http://jabber.org/protocol/si"))
157 capabilities |= JABBER_CAP_SI;
158 else if(!strcmp(var, "http://jabber.org/protocol/si/profile/file-transfer"))
159 capabilities |= JABBER_CAP_SI_FILE_XFER;
160 else if(!strcmp(var, "http://jabber.org/protocol/bytestreams"))
161 capabilities |= JABBER_CAP_BYTESTREAMS;
162 else if(!strcmp(var, "jabber:iq:search"))
163 capabilities |= JABBER_CAP_IQ_SEARCH;
164 else if(!strcmp(var, "jabber:iq:register"))
165 capabilities |= JABBER_CAP_IQ_REGISTER;
166 }
167 }
168
169 capabilities |= JABBER_CAP_RETRIEVED;
170
171 if(jbr)
172 jbr->capabilities = capabilities;
173
174 if((jdicd = g_hash_table_lookup(js->disco_callbacks, from))) {
175 jdicd->callback(js, from, capabilities, jdicd->data);
176 g_hash_table_remove(js->disco_callbacks, from);
177 }
178 } else if(!strcmp(type, "error")) {
179 JabberID *jid;
180 JabberBuddy *jb;
181 JabberBuddyResource *jbr = NULL;
182 JabberCapabilities capabilities = JABBER_CAP_NONE;
183 struct _jabber_disco_info_cb_data *jdicd;
184
185 if(!(jdicd = g_hash_table_lookup(js->disco_callbacks, from)))
186 return;
187
188 if((jid = jabber_id_new(from))) {
189 if(jid->resource && (jb = jabber_buddy_find(js, from, TRUE)))
190 jbr = jabber_buddy_find_resource(jb, jid->resource);
191 jabber_id_free(jid);
192 }
193
194 if(jbr)
195 capabilities = jbr->capabilities;
196
197 jdicd->callback(js, from, capabilities, jdicd->data);
198 g_hash_table_remove(js->disco_callbacks, from);
199 }
200 }
201
202 void jabber_disco_items_parse(JabberStream *js, xmlnode *packet) {
203 const char *from = xmlnode_get_attrib(packet, "from");
204 const char *type = xmlnode_get_attrib(packet, "type");
205
206 if(type && !strcmp(type, "get")) {
207 JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_RESULT,
208 "http://jabber.org/protocol/disco#items");
209
210 jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id"));
211
212 xmlnode_set_attrib(iq->node, "to", from);
213 jabber_iq_send(iq);
214 }
215 }
216
217 static void
218 jabber_disco_finish_server_info_result_cb(JabberStream *js)
219 {
220 PurplePresence *gpresence;
221 PurpleStatus *status;
222
223 if (!(js->server_caps & JABBER_CAP_GOOGLE_ROSTER)) {
224 /* If the server supports JABBER_CAP_GOOGLE_ROSTER; we will have already requested it */
225 jabber_roster_request(js);
226 }
227
228 /* Send initial presence; this will trigger receipt of presence for contacts on the roster */
229 gpresence = purple_account_get_presence(js->gc->account);
230 status = purple_presence_get_active_status(gpresence);
231 jabber_presence_send(js->gc->account, status);
232 }
233
234 static void
235 jabber_disco_server_info_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
236 {
237 xmlnode *query, *child;
238 const char *from = xmlnode_get_attrib(packet, "from");
239 const char *type = xmlnode_get_attrib(packet, "type");
240
241 if((!from || !type) ||
242 (strcmp(from, js->user->domain))) {
243 jabber_disco_finish_server_info_result_cb(js);
244 return;
245 }
246
247 if(strcmp(type, "result")) {
248 /* A common way to get here is for the server not to support xmlns http://jabber.org/protocol/disco#info */
249 jabber_disco_finish_server_info_result_cb(js);
250 return;
251 }
252
253 query = xmlnode_get_child(packet, "query");
254
255 if (!query) {
256 jabber_disco_finish_server_info_result_cb(js);
257 return;
258 }
259
260 for (child = xmlnode_get_child(query, "identity"); child;
261 child = xmlnode_get_next_twin(child)) {
262 const char *category, *type, *name;
263 category = xmlnode_get_attrib(child, "category");
264 if (!category || strcmp(category, "server"))
265 continue;
266 type = xmlnode_get_attrib(child, "type");
267 if (!type || strcmp(type, "im"))
268 continue;
269
270 name = xmlnode_get_attrib(child, "name");
271 if (!name)
272 continue;
273
274 g_free(js->server_name);
275 js->server_name = g_strdup(name);
276 if (!strcmp(name, "Google Talk")) {
277 purple_debug_info("jabber", "Google Talk!");
278 js->googletalk = TRUE;
279 }
280 }
281
282 for (child = xmlnode_get_child(query, "feature"); child;
283 child = xmlnode_get_next_twin(child)) {
284 const char *var;
285 var = xmlnode_get_attrib(child, "var");
286 if (!var)
287 continue;
288
289 if (!strcmp("google:mail:notify", var)) {
290 js->server_caps |= JABBER_CAP_GMAIL_NOTIFY;
291 jabber_gmail_init(js);
292 } else if (!strcmp("google:roster", var)) {
293 js->server_caps |= JABBER_CAP_GOOGLE_ROSTER;
294 jabber_google_roster_init(js);
295 }
296 }
297
298 jabber_disco_finish_server_info_result_cb(js);
299 }
300
301 static void
302 jabber_disco_server_items_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
303 {
304 xmlnode *query, *child;
305 const char *from = xmlnode_get_attrib(packet, "from");
306 const char *type = xmlnode_get_attrib(packet, "type");
307
308 if(!from || !type)
309 return;
310
311 if(strcmp(from, js->user->domain))
312 return;
313
314 if(strcmp(type, "result"))
315 return;
316
317 while(js->chat_servers) {
318 g_free(js->chat_servers->data);
319 js->chat_servers = g_list_delete_link(js->chat_servers, js->chat_servers);
320 }
321
322 query = xmlnode_get_child(packet, "query");
323
324 for(child = xmlnode_get_child(query, "item"); child;
325 child = xmlnode_get_next_twin(child)) {
326 JabberIq *iq;
327 const char *jid;
328
329 if(!(jid = xmlnode_get_attrib(child, "jid")))
330 continue;
331
332 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
333 xmlnode_set_attrib(iq->node, "to", jid);
334 jabber_iq_send(iq);
335 }
336 }
337
338 void jabber_disco_items_server(JabberStream *js)
339 {
340 JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET,
341 "http://jabber.org/protocol/disco#items");
342
343 xmlnode_set_attrib(iq->node, "to", js->user->domain);
344
345 jabber_iq_set_callback(iq, jabber_disco_server_items_result_cb, NULL);
346 jabber_iq_send(iq);
347
348 iq = jabber_iq_new_query(js, JABBER_IQ_GET,
349 "http://jabber.org/protocol/disco#info");
350 xmlnode_set_attrib(iq->node, "to", js->user->domain);
351 jabber_iq_set_callback(iq, jabber_disco_server_info_result_cb, NULL);
352 jabber_iq_send(iq);
353 }
354
355 void jabber_disco_info_do(JabberStream *js, const char *who, JabberDiscoInfoCallback *callback, gpointer data)
356 {
357 JabberID *jid;
358 JabberBuddy *jb;
359 JabberBuddyResource *jbr = NULL;
360 struct _jabber_disco_info_cb_data *jdicd;
361 JabberIq *iq;
362
363 if((jid = jabber_id_new(who))) {
364 if(jid->resource && (jb = jabber_buddy_find(js, who, TRUE)))
365 jbr = jabber_buddy_find_resource(jb, jid->resource);
366 jabber_id_free(jid);
367 }
368
369 if(jbr && jbr->capabilities & JABBER_CAP_RETRIEVED) {
370 callback(js, who, jbr->capabilities, data);
371 return;
372 }
373
374 jdicd = g_new0(struct _jabber_disco_info_cb_data, 1);
375 jdicd->data = data;
376 jdicd->callback = callback;
377
378 g_hash_table_insert(js->disco_callbacks, g_strdup(who), jdicd);
379
380 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
381 xmlnode_set_attrib(iq->node, "to", who);
382
383 jabber_iq_send(iq);
384 }
385
386

mercurial