comparison libpurple/protocols/jabber/disco.c @ 15373:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children ccbdf500f13a
comparison
equal deleted inserted replaced
15372:f79e0f4df793 15373:5fe8042783c1
1 /*
2 * gaim - 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 GaimPresence *gpresence;
221 GaimStatus *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 = gaim_account_get_presence(js->gc->account);
230 status = gaim_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, "category"); 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 js->googletalk = TRUE;
278 }
279
280 for (child = xmlnode_get_child(query, "feature"); child;
281 child = xmlnode_get_next_twin(child)) {
282 const char *var;
283 var = xmlnode_get_attrib(child, "var");
284 if (!var)
285 continue;
286
287 if (!strcmp("google:mail:notify", var)) {
288 js->server_caps |= JABBER_CAP_GMAIL_NOTIFY;
289 jabber_gmail_init(js);
290 } else if (!strcmp("google:roster", var)) {
291 js->server_caps |= JABBER_CAP_GOOGLE_ROSTER;
292 jabber_google_roster_init(js);
293 }
294 }
295
296 jabber_disco_finish_server_info_result_cb(js);
297 }
298
299 static void
300 jabber_disco_server_items_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
301 {
302 xmlnode *query, *child;
303 const char *from = xmlnode_get_attrib(packet, "from");
304 const char *type = xmlnode_get_attrib(packet, "type");
305
306 if(!from || !type)
307 return;
308
309 if(strcmp(from, js->user->domain))
310 return;
311
312 if(strcmp(type, "result"))
313 return;
314
315 while(js->chat_servers) {
316 g_free(js->chat_servers->data);
317 js->chat_servers = g_list_delete_link(js->chat_servers, js->chat_servers);
318 }
319
320 query = xmlnode_get_child(packet, "query");
321
322 for(child = xmlnode_get_child(query, "item"); child;
323 child = xmlnode_get_next_twin(child)) {
324 JabberIq *iq;
325 const char *jid;
326
327 if(!(jid = xmlnode_get_attrib(child, "jid")))
328 continue;
329
330 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
331 xmlnode_set_attrib(iq->node, "to", jid);
332 jabber_iq_send(iq);
333 }
334 }
335
336 void jabber_disco_items_server(JabberStream *js)
337 {
338 JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET,
339 "http://jabber.org/protocol/disco#items");
340
341 xmlnode_set_attrib(iq->node, "to", js->user->domain);
342
343 jabber_iq_set_callback(iq, jabber_disco_server_items_result_cb, NULL);
344 jabber_iq_send(iq);
345
346 iq = jabber_iq_new_query(js, JABBER_IQ_GET,
347 "http://jabber.org/protocol/disco#info");
348 xmlnode_set_attrib(iq->node, "to", js->user->domain);
349 jabber_iq_set_callback(iq, jabber_disco_server_info_result_cb, NULL);
350 jabber_iq_send(iq);
351 }
352
353 void jabber_disco_info_do(JabberStream *js, const char *who, JabberDiscoInfoCallback *callback, gpointer data)
354 {
355 JabberID *jid;
356 JabberBuddy *jb;
357 JabberBuddyResource *jbr = NULL;
358 struct _jabber_disco_info_cb_data *jdicd;
359 JabberIq *iq;
360
361 if((jid = jabber_id_new(who))) {
362 if(jid->resource && (jb = jabber_buddy_find(js, who, TRUE)))
363 jbr = jabber_buddy_find_resource(jb, jid->resource);
364 jabber_id_free(jid);
365 }
366
367 if(jbr && jbr->capabilities & JABBER_CAP_RETRIEVED) {
368 callback(js, who, jbr->capabilities, data);
369 return;
370 }
371
372 jdicd = g_new0(struct _jabber_disco_info_cb_data, 1);
373 jdicd->data = data;
374 jdicd->callback = callback;
375
376 g_hash_table_insert(js->disco_callbacks, g_strdup(who), jdicd);
377
378 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
379 xmlnode_set_attrib(iq->node, "to", who);
380
381 jabber_iq_send(iq);
382 }
383
384