Mercurial > pidgin.yaz
annotate libgaim/protocols/jabber/buddy.c @ 15204:f814b2df9cce
[gaim-migrate @ 17993]
Blocking on Google Talk. Our Privacy API sucks so bad that even with no prior support for blocking in Jabber, this has no interface changes. If someone wanted to implement the deprecated Jabber privacy lists API, though, that would be ok by me.
committer: Tailor Script <tailor@pidgin.im>
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Thu, 14 Dec 2006 04:56:54 +0000 |
parents | b81e4e44b509 |
children | 49afc9ce69d2 |
rev | line source |
---|---|
14192 | 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 #include "internal.h" | |
22 #include "cipher.h" | |
23 #include "debug.h" | |
24 #include "imgstore.h" | |
25 #include "prpl.h" | |
26 #include "notify.h" | |
27 #include "request.h" | |
28 #include "util.h" | |
29 #include "xmlnode.h" | |
30 | |
31 #include "buddy.h" | |
32 #include "chat.h" | |
33 #include "jabber.h" | |
34 #include "iq.h" | |
35 #include "presence.h" | |
36 #include "xdata.h" | |
37 | |
38 typedef struct { | |
39 long idle_seconds; | |
40 } JabberBuddyInfoResource; | |
41 | |
42 typedef struct { | |
43 JabberStream *js; | |
44 JabberBuddy *jb; | |
45 char *jid; | |
46 GSList *ids; | |
47 GHashTable *resources; | |
48 int timeout_handle; | |
49 char *vcard_text; | |
50 GSList *vcard_imgids; | |
51 } JabberBuddyInfo; | |
52 | |
53 void jabber_buddy_free(JabberBuddy *jb) | |
54 { | |
55 g_return_if_fail(jb != NULL); | |
56 | |
57 if(jb->error_msg) | |
58 g_free(jb->error_msg); | |
59 while(jb->resources) | |
60 jabber_buddy_resource_free(jb->resources->data); | |
61 | |
62 g_free(jb); | |
63 } | |
64 | |
65 JabberBuddy *jabber_buddy_find(JabberStream *js, const char *name, | |
66 gboolean create) | |
67 { | |
68 JabberBuddy *jb; | |
69 const char *realname; | |
70 | |
15082 | 71 if (js->buddies == NULL) |
72 return NULL; | |
73 | |
14192 | 74 if(!(realname = jabber_normalize(js->gc->account, name))) |
75 return NULL; | |
76 | |
77 jb = g_hash_table_lookup(js->buddies, realname); | |
78 | |
79 if(!jb && create) { | |
80 jb = g_new0(JabberBuddy, 1); | |
81 g_hash_table_insert(js->buddies, g_strdup(realname), jb); | |
82 } | |
83 | |
84 return jb; | |
85 } | |
86 | |
87 | |
88 JabberBuddyResource *jabber_buddy_find_resource(JabberBuddy *jb, | |
89 const char *resource) | |
90 { | |
91 JabberBuddyResource *jbr = NULL; | |
92 GList *l; | |
93 | |
94 if(!jb) | |
95 return NULL; | |
96 | |
97 for(l = jb->resources; l; l = l->next) | |
98 { | |
99 if(!jbr && !resource) { | |
100 jbr = l->data; | |
101 } else if(!resource) { | |
102 if(((JabberBuddyResource *)l->data)->priority >= jbr->priority) | |
103 jbr = l->data; | |
104 } else if(((JabberBuddyResource *)l->data)->name) { | |
105 if(!strcmp(((JabberBuddyResource *)l->data)->name, resource)) { | |
106 jbr = l->data; | |
107 break; | |
108 } | |
109 } | |
110 } | |
111 | |
112 return jbr; | |
113 } | |
114 | |
115 JabberBuddyResource *jabber_buddy_track_resource(JabberBuddy *jb, const char *resource, | |
116 int priority, JabberBuddyState state, const char *status) | |
117 { | |
118 JabberBuddyResource *jbr = jabber_buddy_find_resource(jb, resource); | |
119 | |
120 if(!jbr) { | |
121 jbr = g_new0(JabberBuddyResource, 1); | |
122 jbr->jb = jb; | |
123 jbr->name = g_strdup(resource); | |
124 jbr->capabilities = JABBER_CAP_XHTML; | |
125 jb->resources = g_list_append(jb->resources, jbr); | |
126 } | |
127 jbr->priority = priority; | |
128 jbr->state = state; | |
129 if(jbr->status) | |
130 g_free(jbr->status); | |
131 if (status) | |
132 jbr->status = g_markup_escape_text(status, -1); | |
133 else | |
134 jbr->status = NULL; | |
135 | |
136 return jbr; | |
137 } | |
138 | |
139 void jabber_buddy_resource_free(JabberBuddyResource *jbr) | |
140 { | |
141 g_return_if_fail(jbr != NULL); | |
142 | |
143 jbr->jb->resources = g_list_remove(jbr->jb->resources, jbr); | |
144 | |
145 g_free(jbr->name); | |
146 g_free(jbr->status); | |
147 g_free(jbr->thread_id); | |
148 g_free(jbr->client.name); | |
149 g_free(jbr->client.version); | |
150 g_free(jbr->client.os); | |
151 g_free(jbr); | |
152 } | |
153 | |
154 void jabber_buddy_remove_resource(JabberBuddy *jb, const char *resource) | |
155 { | |
156 JabberBuddyResource *jbr = jabber_buddy_find_resource(jb, resource); | |
157 | |
158 if(!jbr) | |
159 return; | |
160 | |
161 jabber_buddy_resource_free(jbr); | |
162 } | |
163 | |
164 const char *jabber_buddy_get_status_msg(JabberBuddy *jb) | |
165 { | |
166 JabberBuddyResource *jbr; | |
167 | |
168 if(!jb) | |
169 return NULL; | |
170 | |
171 jbr = jabber_buddy_find_resource(jb, NULL); | |
172 | |
173 if(!jbr) | |
174 return NULL; | |
175 | |
176 return jbr->status; | |
177 } | |
178 | |
179 /******* | |
180 * This is the old vCard stuff taken from the old prpl. vCards, by definition | |
181 * are a temporary thing until jabber can get its act together and come up | |
182 * with a format for user information, hence the namespace of 'vcard-temp' | |
183 * | |
184 * Since I don't feel like putting that much work into something that's | |
185 * _supposed_ to go away, i'm going to just copy the kludgy old code here, | |
186 * and make it purdy when jabber comes up with a standards-track JEP to | |
187 * replace vcard-temp | |
188 * --Nathan | |
189 *******/ | |
190 | |
191 /*---------------------------------------*/ | |
192 /* Jabber "set info" (vCard) support */ | |
193 /*---------------------------------------*/ | |
194 | |
195 /* | |
196 * V-Card format: | |
197 * | |
198 * <vCard prodid='' version='' xmlns=''> | |
199 * <FN></FN> | |
200 * <N> | |
201 * <FAMILY/> | |
202 * <GIVEN/> | |
203 * </N> | |
204 * <NICKNAME/> | |
205 * <URL/> | |
206 * <ADR> | |
207 * <STREET/> | |
208 * <EXTADD/> | |
209 * <LOCALITY/> | |
210 * <REGION/> | |
211 * <PCODE/> | |
212 * <COUNTRY/> | |
213 * </ADR> | |
214 * <TEL/> | |
215 * <EMAIL/> | |
216 * <ORG> | |
217 * <ORGNAME/> | |
218 * <ORGUNIT/> | |
219 * </ORG> | |
220 * <TITLE/> | |
221 * <ROLE/> | |
222 * <DESC/> | |
223 * <BDAY/> | |
224 * </vCard> | |
225 * | |
226 * See also: | |
227 * | |
228 * http://docs.jabber.org/proto/html/vcard-temp.html | |
229 * http://www.vcard-xml.org/dtd/vCard-XML-v2-20010520.dtd | |
230 */ | |
231 | |
232 /* | |
233 * Cross-reference user-friendly V-Card entry labels to vCard XML tags | |
234 * and attributes. | |
235 * | |
236 * Order is (or should be) unimportant. For example: we have no way of | |
237 * knowing in what order real data will arrive. | |
238 * | |
239 * Format: Label, Pre-set text, "visible" flag, "editable" flag, XML tag | |
240 * name, XML tag's parent tag "path" (relative to vCard node). | |
241 * | |
242 * List is terminated by a NULL label pointer. | |
243 * | |
244 * Entries with no label text, but with XML tag and parent tag | |
245 * entries, are used by V-Card XML construction routines to | |
246 * "automagically" construct the appropriate XML node tree. | |
247 * | |
248 * Thoughts on future direction/expansion | |
249 * | |
250 * This is a "simple" vCard. | |
251 * | |
252 * It is possible for nodes other than the "vCard" node to have | |
253 * attributes. Should that prove necessary/desirable, add an | |
254 * "attributes" pointer to the vcard_template struct, create the | |
255 * necessary tag_attr structs, and add 'em to the vcard_dflt_data | |
256 * array. | |
257 * | |
258 * The above changes will (obviously) require changes to the vCard | |
259 * construction routines. | |
260 */ | |
261 | |
262 struct vcard_template { | |
263 char *label; /* label text pointer */ | |
264 char *text; /* entry text pointer */ | |
265 int visible; /* should entry field be "visible?" */ | |
266 int editable; /* should entry field be editable? */ | |
267 char *tag; /* tag text */ | |
268 char *ptag; /* parent tag "path" text */ | |
269 char *url; /* vCard display format if URL */ | |
270 } vcard_template_data[] = { | |
271 {N_("Full Name"), NULL, TRUE, TRUE, "FN", NULL, NULL}, | |
272 {N_("Family Name"), NULL, TRUE, TRUE, "FAMILY", "N", NULL}, | |
273 {N_("Given Name"), NULL, TRUE, TRUE, "GIVEN", "N", NULL}, | |
274 {N_("Nickname"), NULL, TRUE, TRUE, "NICKNAME", NULL, NULL}, | |
275 {N_("URL"), NULL, TRUE, TRUE, "URL", NULL, "<A HREF=\"%s\">%s</A>"}, | |
276 {N_("Street Address"), NULL, TRUE, TRUE, "STREET", "ADR", NULL}, | |
277 {N_("Extended Address"), NULL, TRUE, TRUE, "EXTADD", "ADR", NULL}, | |
278 {N_("Locality"), NULL, TRUE, TRUE, "LOCALITY", "ADR", NULL}, | |
279 {N_("Region"), NULL, TRUE, TRUE, "REGION", "ADR", NULL}, | |
280 {N_("Postal Code"), NULL, TRUE, TRUE, "PCODE", "ADR", NULL}, | |
281 {N_("Country"), NULL, TRUE, TRUE, "CTRY", "ADR", NULL}, | |
282 {N_("Telephone"), NULL, TRUE, TRUE, "NUMBER", "TEL", NULL}, | |
283 {N_("E-Mail"), NULL, TRUE, TRUE, "USERID", "EMAIL", "<A HREF=\"mailto:%s\">%s</A>"}, | |
284 {N_("Organization Name"), NULL, TRUE, TRUE, "ORGNAME", "ORG", NULL}, | |
285 {N_("Organization Unit"), NULL, TRUE, TRUE, "ORGUNIT", "ORG", NULL}, | |
286 {N_("Title"), NULL, TRUE, TRUE, "TITLE", NULL, NULL}, | |
287 {N_("Role"), NULL, TRUE, TRUE, "ROLE", NULL, NULL}, | |
288 {N_("Birthday"), NULL, TRUE, TRUE, "BDAY", NULL, NULL}, | |
289 {N_("Description"), NULL, TRUE, TRUE, "DESC", NULL, NULL}, | |
290 {"", NULL, TRUE, TRUE, "N", NULL, NULL}, | |
291 {"", NULL, TRUE, TRUE, "ADR", NULL, NULL}, | |
292 {"", NULL, TRUE, TRUE, "ORG", NULL, NULL}, | |
293 {NULL, NULL, 0, 0, NULL, NULL, NULL} | |
294 }; | |
295 | |
296 /* | |
297 * The "vCard" tag's attribute list... | |
298 */ | |
299 struct tag_attr { | |
300 char *attr; | |
301 char *value; | |
302 } vcard_tag_attr_list[] = { | |
303 {"prodid", "-//HandGen//NONSGML vGen v1.0//EN"}, | |
304 {"version", "2.0", }, | |
305 {"xmlns", "vcard-temp", }, | |
306 {NULL, NULL}, | |
307 }; | |
308 | |
309 | |
310 /* | |
311 * Insert a tag node into an xmlnode tree, recursively inserting parent tag | |
312 * nodes as necessary | |
313 * | |
314 * Returns pointer to inserted node | |
315 * | |
316 * Note to hackers: this code is designed to be re-entrant (it's recursive--it | |
317 * calls itself), so don't put any "static"s in here! | |
318 */ | |
319 static xmlnode *insert_tag_to_parent_tag(xmlnode *start, const char *parent_tag, const char *new_tag) | |
320 { | |
321 xmlnode *x = NULL; | |
322 | |
323 /* | |
324 * If the parent tag wasn't specified, see if we can get it | |
325 * from the vCard template struct. | |
326 */ | |
327 if(parent_tag == NULL) { | |
328 struct vcard_template *vc_tp = vcard_template_data; | |
329 | |
330 while(vc_tp->label != NULL) { | |
331 if(strcmp(vc_tp->tag, new_tag) == 0) { | |
332 parent_tag = vc_tp->ptag; | |
333 break; | |
334 } | |
335 ++vc_tp; | |
336 } | |
337 } | |
338 | |
339 /* | |
340 * If we have a parent tag... | |
341 */ | |
342 if(parent_tag != NULL ) { | |
343 /* | |
344 * Try to get the parent node for a tag | |
345 */ | |
346 if((x = xmlnode_get_child(start, parent_tag)) == NULL) { | |
347 /* | |
348 * Descend? | |
349 */ | |
350 char *grand_parent = g_strdup(parent_tag); | |
351 char *parent; | |
352 | |
353 if((parent = strrchr(grand_parent, '/')) != NULL) { | |
354 *(parent++) = '\0'; | |
355 x = insert_tag_to_parent_tag(start, grand_parent, parent); | |
356 } else { | |
357 x = xmlnode_new_child(start, grand_parent); | |
358 } | |
359 g_free(grand_parent); | |
360 } else { | |
361 /* | |
362 * We found *something* to be the parent node. | |
363 * Note: may be the "root" node! | |
364 */ | |
365 xmlnode *y; | |
366 if((y = xmlnode_get_child(x, new_tag)) != NULL) { | |
367 return(y); | |
368 } | |
369 } | |
370 } | |
371 | |
372 /* | |
373 * insert the new tag into its parent node | |
374 */ | |
375 return(xmlnode_new_child((x == NULL? start : x), new_tag)); | |
376 } | |
377 | |
378 /* | |
379 * Send vCard info to Jabber server | |
380 */ | |
381 void jabber_set_info(GaimConnection *gc, const char *info) | |
382 { | |
383 JabberIq *iq; | |
384 JabberStream *js = gc->proto_data; | |
385 xmlnode *vc_node; | |
386 char *avatar_file = NULL; | |
387 struct tag_attr *tag_attr; | |
388 | |
389 if(js->avatar_hash) | |
390 g_free(js->avatar_hash); | |
391 js->avatar_hash = NULL; | |
392 | |
393 /* | |
394 * Send only if there's actually any *information* to send | |
395 */ | |
396 vc_node = info ? xmlnode_from_str(info, -1) : NULL; | |
397 avatar_file = gaim_buddy_icons_get_full_path(gaim_account_get_buddy_icon(gc->account)); | |
398 | |
399 if(!vc_node && avatar_file) { | |
400 vc_node = xmlnode_new("vCard"); | |
401 for(tag_attr = vcard_tag_attr_list; tag_attr->attr != NULL; ++tag_attr) | |
402 xmlnode_set_attrib(vc_node, tag_attr->attr, tag_attr->value); | |
403 } | |
404 | |
405 if(vc_node) { | |
406 if (vc_node->name && | |
407 !g_ascii_strncasecmp(vc_node->name, "vCard", 5)) { | |
408 GError *error = NULL; | |
409 gchar *avatar_data_tmp; | |
410 guchar *avatar_data; | |
411 gsize avatar_len; | |
412 | |
413 if(avatar_file && g_file_get_contents(avatar_file, &avatar_data_tmp, &avatar_len, &error)) { | |
414 xmlnode *photo, *binval; | |
415 gchar *enc; | |
416 int i; | |
417 unsigned char hashval[20]; | |
418 char *p, hash[41]; | |
419 | |
420 avatar_data = (guchar *) avatar_data_tmp; | |
421 photo = xmlnode_new_child(vc_node, "PHOTO"); | |
422 binval = xmlnode_new_child(photo, "BINVAL"); | |
423 enc = gaim_base64_encode(avatar_data, avatar_len); | |
424 | |
425 gaim_cipher_digest_region("sha1", (guchar *)avatar_data, | |
426 avatar_len, sizeof(hashval), | |
427 hashval, NULL); | |
428 | |
429 p = hash; | |
430 for(i=0; i<20; i++, p+=2) | |
431 snprintf(p, 3, "%02x", hashval[i]); | |
432 js->avatar_hash = g_strdup(hash); | |
433 | |
434 xmlnode_insert_data(binval, enc, -1); | |
435 g_free(enc); | |
436 g_free(avatar_data); | |
437 } else if (error != NULL) { | |
438 g_error_free(error); | |
439 } | |
440 g_free(avatar_file); | |
441 | |
442 iq = jabber_iq_new(js, JABBER_IQ_SET); | |
443 xmlnode_insert_child(iq->node, vc_node); | |
444 jabber_iq_send(iq); | |
445 } else { | |
446 xmlnode_free(vc_node); | |
447 } | |
448 } | |
449 } | |
450 | |
451 void jabber_set_buddy_icon(GaimConnection *gc, const char *iconfile) | |
452 { | |
453 GaimPresence *gpresence; | |
454 GaimStatus *status; | |
455 | |
456 jabber_set_info(gc, gaim_account_get_user_info(gc->account)); | |
457 | |
458 gpresence = gaim_account_get_presence(gc->account); | |
459 status = gaim_presence_get_active_status(gpresence); | |
460 jabber_presence_send(gc->account, status); | |
461 } | |
462 | |
463 /* | |
464 * This is the callback from the "ok clicked" for "set vCard" | |
465 * | |
466 * Formats GSList data into XML-encoded string and returns a pointer | |
467 * to said string. | |
468 * | |
469 * g_free()'ing the returned string space is the responsibility of | |
470 * the caller. | |
471 */ | |
472 static void | |
473 jabber_format_info(GaimConnection *gc, GaimRequestFields *fields) | |
474 { | |
475 xmlnode *vc_node; | |
476 GaimRequestField *field; | |
477 const char *text; | |
478 char *p; | |
479 const struct vcard_template *vc_tp; | |
480 struct tag_attr *tag_attr; | |
481 | |
482 vc_node = xmlnode_new("vCard"); | |
483 | |
484 for(tag_attr = vcard_tag_attr_list; tag_attr->attr != NULL; ++tag_attr) | |
485 xmlnode_set_attrib(vc_node, tag_attr->attr, tag_attr->value); | |
486 | |
487 for (vc_tp = vcard_template_data; vc_tp->label != NULL; vc_tp++) { | |
488 if (*vc_tp->label == '\0') | |
489 continue; | |
490 | |
491 field = gaim_request_fields_get_field(fields, vc_tp->tag); | |
492 text = gaim_request_field_string_get_value(field); | |
493 | |
494 | |
495 if (text != NULL && *text != '\0') { | |
496 xmlnode *xp; | |
497 | |
498 gaim_debug(GAIM_DEBUG_INFO, "jabber", | |
499 "Setting %s to '%s'\n", vc_tp->tag, text); | |
500 | |
501 if ((xp = insert_tag_to_parent_tag(vc_node, | |
502 NULL, vc_tp->tag)) != NULL) { | |
503 | |
504 xmlnode_insert_data(xp, text, -1); | |
505 } | |
506 } | |
507 } | |
508 | |
509 p = xmlnode_to_str(vc_node, NULL); | |
510 xmlnode_free(vc_node); | |
511 | |
14607 | 512 gaim_account_set_user_info(gaim_connection_get_account(gc), p); |
513 serv_set_info(gc, p); | |
14192 | 514 |
515 g_free(p); | |
516 } | |
517 | |
518 /* | |
519 * This gets executed by the proto action | |
520 * | |
521 * Creates a new GaimRequestFields struct, gets the XML-formatted user_info | |
522 * string (if any) into GSLists for the (multi-entry) edit dialog and | |
523 * calls the set_vcard dialog. | |
524 */ | |
525 void jabber_setup_set_info(GaimPluginAction *action) | |
526 { | |
527 GaimConnection *gc = (GaimConnection *) action->context; | |
528 GaimRequestFields *fields; | |
529 GaimRequestFieldGroup *group; | |
530 GaimRequestField *field; | |
531 const struct vcard_template *vc_tp; | |
532 const char *user_info; | |
533 char *cdata = NULL; | |
534 xmlnode *x_vc_data = NULL; | |
535 | |
536 fields = gaim_request_fields_new(); | |
537 group = gaim_request_field_group_new(NULL); | |
538 gaim_request_fields_add_group(fields, group); | |
539 | |
540 /* | |
541 * Get existing, XML-formatted, user info | |
542 */ | |
543 if((user_info = gaim_account_get_user_info(gc->account)) != NULL) | |
544 x_vc_data = xmlnode_from_str(user_info, -1); | |
545 | |
546 /* | |
547 * Set up GSLists for edit with labels from "template," data from user info | |
548 */ | |
549 for(vc_tp = vcard_template_data; vc_tp->label != NULL; ++vc_tp) { | |
550 xmlnode *data_node; | |
551 if((vc_tp->label)[0] == '\0') | |
552 continue; | |
553 | |
554 if (x_vc_data != NULL) { | |
555 if(vc_tp->ptag == NULL) { | |
556 data_node = xmlnode_get_child(x_vc_data, vc_tp->tag); | |
557 } else { | |
558 gchar *tag = g_strdup_printf("%s/%s", vc_tp->ptag, vc_tp->tag); | |
559 data_node = xmlnode_get_child(x_vc_data, tag); | |
560 g_free(tag); | |
561 } | |
562 if(data_node) | |
563 cdata = xmlnode_get_data(data_node); | |
564 } | |
565 | |
566 if(strcmp(vc_tp->tag, "DESC") == 0) { | |
567 field = gaim_request_field_string_new(vc_tp->tag, | |
568 _(vc_tp->label), cdata, | |
569 TRUE); | |
570 } else { | |
571 field = gaim_request_field_string_new(vc_tp->tag, | |
572 _(vc_tp->label), cdata, | |
573 FALSE); | |
574 } | |
575 | |
576 g_free(cdata); | |
577 cdata = NULL; | |
578 | |
579 gaim_request_field_group_add_field(group, field); | |
580 } | |
581 | |
582 if(x_vc_data != NULL) | |
583 xmlnode_free(x_vc_data); | |
584 | |
585 gaim_request_fields(gc, _("Edit Jabber vCard"), | |
586 _("Edit Jabber vCard"), | |
587 _("All items below are optional. Enter only the " | |
588 "information with which you feel comfortable."), | |
589 fields, | |
590 _("Save"), G_CALLBACK(jabber_format_info), | |
591 _("Cancel"), NULL, | |
592 gc); | |
593 } | |
594 | |
595 /*---------------------------------------*/ | |
596 /* End Jabber "set info" (vCard) support */ | |
597 /*---------------------------------------*/ | |
598 | |
599 /****** | |
600 * end of that ancient crap that needs to die | |
601 ******/ | |
602 | |
603 static void jabber_buddy_info_show_if_ready(JabberBuddyInfo *jbi) | |
604 { | |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
605 char *resource_name, *tmp; |
14192 | 606 JabberBuddyResource *jbr; |
15031 | 607 JabberBuddyInfoResource *jbir = NULL; |
14192 | 608 GList *resources; |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
609 GaimNotifyUserInfo *user_info; |
14192 | 610 |
611 /* not yet */ | |
612 if(jbi->ids) | |
613 return; | |
614 | |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
615 user_info = gaim_notify_user_info_new(); |
14192 | 616 resource_name = jabber_get_resource(jbi->jid); |
617 | |
618 if(resource_name) { | |
619 jbr = jabber_buddy_find_resource(jbi->jb, resource_name); | |
620 jbir = g_hash_table_lookup(jbi->resources, resource_name); | |
621 if(jbr) { | |
622 char *purdy = NULL; | |
623 if(jbr->status) | |
624 purdy = gaim_strdup_withhtml(jbr->status); | |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
625 tmp = g_strdup_printf("%s%s%s", jabber_buddy_state_get_name(jbr->state), |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
626 (purdy ? ": " : ""), |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
627 (purdy ? purdy : "")); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
628 gaim_notify_user_info_add_pair(user_info, _("Status"), tmp); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
629 g_free(tmp); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
630 g_free(purdy); |
14192 | 631 } else { |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
632 gaim_notify_user_info_add_pair(user_info, _("Status"), _("Unknown")); |
14192 | 633 } |
634 if(jbir) { | |
635 if(jbir->idle_seconds > 0) { | |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
636 gaim_notify_user_info_add_pair(user_info, _("Idle"), gaim_str_seconds_to_string(jbir->idle_seconds)); |
14192 | 637 } |
638 } | |
639 if(jbr && jbr->client.name) { | |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
640 tmp = g_strdup_printf("%s%s%s", jbr->client.name, |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
641 (jbr->client.version ? " " : ""), |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
642 (jbr->client.version ? jbr->client.version : "")); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
643 gaim_notify_user_info_add_pair(user_info, _("Client"), tmp); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
644 g_free(tmp); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
645 |
14192 | 646 if(jbr->client.os) { |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
647 gaim_notify_user_info_add_pair(user_info, _("Operating System"), jbr->client.os); |
14192 | 648 } |
649 } | |
650 } else { | |
651 for(resources = jbi->jb->resources; resources; resources = resources->next) { | |
652 char *purdy = NULL; | |
653 jbr = resources->data; | |
654 if(jbr->status) | |
655 purdy = gaim_strdup_withhtml(jbr->status); | |
15031 | 656 if(jbr->name) |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
657 gaim_notify_user_info_add_pair(user_info, _("Resource"), jbr->name); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
658 tmp = g_strdup_printf("%d", jbr->priority); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
659 gaim_notify_user_info_add_pair(user_info, _("Priority"), tmp); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
660 g_free(tmp); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
661 |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
662 tmp = g_strdup_printf("%s%s%s", jabber_buddy_state_get_name(jbr->state), |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
663 (purdy ? ": " : ""), |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
664 (purdy ? purdy : "")); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
665 gaim_notify_user_info_add_pair(user_info, _("Status"), tmp); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
666 g_free(tmp); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
667 g_free(purdy); |
14192 | 668 |
15031 | 669 if(jbr->name) |
670 jbir = g_hash_table_lookup(jbi->resources, jbr->name); | |
671 | |
14192 | 672 if(jbir) { |
673 if(jbir->idle_seconds > 0) { | |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
674 gaim_notify_user_info_add_pair(user_info, _("Idle"), gaim_str_seconds_to_string(jbir->idle_seconds)); |
14192 | 675 } |
676 } | |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
677 if(jbr && jbr->client.name) { |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
678 tmp = g_strdup_printf("%s%s%s", jbr->client.name, |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
679 (jbr->client.version ? " " : ""), |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
680 (jbr->client.version ? jbr->client.version : "")); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
681 gaim_notify_user_info_add_pair(user_info, |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
682 _("Client"), tmp); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
683 g_free(tmp); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
684 |
14192 | 685 if(jbr->client.os) { |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
686 gaim_notify_user_info_add_pair(user_info, _("Operating System"), jbr->client.os); |
14192 | 687 } |
688 } | |
689 } | |
690 } | |
691 | |
692 g_free(resource_name); | |
693 | |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
694 if (jbi->vcard_text != NULL) { |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
695 gaim_notify_user_info_add_section_break(user_info); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
696 /* Should this have some sort of label? */ |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
697 gaim_notify_user_info_add_pair(user_info, NULL, jbi->vcard_text); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
698 } |
14192 | 699 |
15144
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
700 gaim_notify_userinfo(jbi->js->gc, jbi->jid, user_info, NULL, NULL); |
b81e4e44b509
[gaim-migrate @ 17929]
Evan Schoenberg <evan.s@dreskin.net>
parents:
15082
diff
changeset
|
701 gaim_notify_user_info_destroy(user_info); |
14192 | 702 |
703 while(jbi->vcard_imgids) { | |
704 gaim_imgstore_unref(GPOINTER_TO_INT(jbi->vcard_imgids->data)); | |
705 jbi->vcard_imgids = g_slist_delete_link(jbi->vcard_imgids, jbi->vcard_imgids); | |
706 } | |
707 | |
708 if (jbi->timeout_handle > 0) | |
709 gaim_timeout_remove(jbi->timeout_handle); | |
710 | |
711 g_free(jbi->jid); | |
712 g_hash_table_destroy(jbi->resources); | |
713 g_free(jbi->vcard_text); | |
714 g_free(jbi); | |
715 } | |
716 | |
717 static void jabber_buddy_info_remove_id(JabberBuddyInfo *jbi, const char *id) | |
718 { | |
719 GSList *l = jbi->ids; | |
720 | |
721 if(!id) | |
722 return; | |
723 | |
724 while(l) { | |
725 if(!strcmp(id, l->data)) { | |
726 jbi->ids = g_slist_remove(jbi->ids, l->data); | |
727 return; | |
728 } | |
729 l = l->next; | |
730 } | |
731 } | |
732 | |
733 static void jabber_vcard_parse(JabberStream *js, xmlnode *packet, gpointer data) | |
734 { | |
735 const char *id, *from; | |
736 GString *info_text; | |
737 char *bare_jid; | |
738 char *text; | |
739 xmlnode *vcard; | |
740 GaimBuddy *b; | |
741 JabberBuddyInfo *jbi = data; | |
742 | |
743 from = xmlnode_get_attrib(packet, "from"); | |
744 id = xmlnode_get_attrib(packet, "id"); | |
745 | |
746 if(!jbi) | |
747 return; | |
748 | |
749 jabber_buddy_info_remove_id(jbi, id); | |
750 | |
751 if(!from) | |
752 return; | |
753 | |
754 if(!jabber_buddy_find(js, from, FALSE)) | |
755 return; | |
756 | |
757 /* XXX: handle the error case */ | |
758 | |
759 bare_jid = jabber_get_bare_jid(from); | |
760 | |
761 b = gaim_find_buddy(js->gc->account, bare_jid); | |
762 | |
763 info_text = g_string_new(""); | |
764 | |
765 if((vcard = xmlnode_get_child(packet, "vCard")) || | |
766 (vcard = xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) { | |
767 xmlnode *child; | |
768 for(child = vcard->child; child; child = child->next) | |
769 { | |
770 xmlnode *child2; | |
771 | |
772 if(child->type != XMLNODE_TYPE_TAG) | |
773 continue; | |
774 | |
775 text = xmlnode_get_data(child); | |
776 if(text && !strcmp(child->name, "FN")) { | |
777 g_string_append_printf(info_text, "<b>%s:</b> %s<br/>", | |
778 _("Full Name"), text); | |
779 } else if(!strcmp(child->name, "N")) { | |
780 for(child2 = child->child; child2; child2 = child2->next) | |
781 { | |
782 char *text2; | |
783 | |
784 if(child2->type != XMLNODE_TYPE_TAG) | |
785 continue; | |
786 | |
787 text2 = xmlnode_get_data(child2); | |
788 if(text2 && !strcmp(child2->name, "FAMILY")) { | |
789 g_string_append_printf(info_text, | |
790 "<b>%s:</b> %s<br/>", | |
791 _("Family Name"), text2); | |
792 } else if(text2 && !strcmp(child2->name, "GIVEN")) { | |
793 g_string_append_printf(info_text, | |
794 "<b>%s:</b> %s<br/>", | |
795 _("Given Name"), text2); | |
796 } else if(text2 && !strcmp(child2->name, "MIDDLE")) { | |
797 g_string_append_printf(info_text, | |
798 "<b>%s:</b> %s<br/>", | |
799 _("Middle Name"), text2); | |
800 } | |
801 g_free(text2); | |
802 } | |
803 } else if(text && !strcmp(child->name, "NICKNAME")) { | |
804 serv_got_alias(js->gc, from, text); | |
805 if(b) { | |
806 gaim_blist_node_set_string((GaimBlistNode*)b, "servernick", text); | |
807 } | |
808 g_string_append_printf(info_text, "<b>%s:</b> %s<br/>", | |
809 _("Nickname"), text); | |
810 } else if(text && !strcmp(child->name, "BDAY")) { | |
811 g_string_append_printf(info_text, "<b>%s:</b> %s<br/>", | |
812 _("Birthday"), text); | |
813 } else if(!strcmp(child->name, "ADR")) { | |
814 gboolean address_line_added = FALSE; | |
815 | |
816 for(child2 = child->child; child2; child2 = child2->next) | |
817 { | |
818 char *text2; | |
819 | |
820 if(child2->type != XMLNODE_TYPE_TAG) | |
821 continue; | |
822 | |
823 text2 = xmlnode_get_data(child2); | |
824 if (text2 == NULL) | |
825 continue; | |
826 | |
827 /* We do this here so that it's not added if all the child | |
828 * elements are empty. */ | |
829 if (!address_line_added) | |
830 { | |
831 g_string_append_printf(info_text, "<b>%s:</b><br/>", | |
832 _("Address")); | |
833 address_line_added = TRUE; | |
834 } | |
835 | |
836 if(!strcmp(child2->name, "POBOX")) { | |
837 g_string_append_printf(info_text, | |
838 " <b>%s:</b> %s<br/>", | |
839 _("P.O. Box"), text2); | |
840 } else if(!strcmp(child2->name, "EXTADR")) { | |
841 g_string_append_printf(info_text, | |
842 " <b>%s:</b> %s<br/>", | |
843 _("Extended Address"), text2); | |
844 } else if(!strcmp(child2->name, "STREET")) { | |
845 g_string_append_printf(info_text, | |
846 " <b>%s:</b> %s<br/>", | |
847 _("Street Address"), text2); | |
848 } else if(!strcmp(child2->name, "LOCALITY")) { | |
849 g_string_append_printf(info_text, | |
850 " <b>%s:</b> %s<br/>", | |
851 _("Locality"), text2); | |
852 } else if(!strcmp(child2->name, "REGION")) { | |
853 g_string_append_printf(info_text, | |
854 " <b>%s:</b> %s<br/>", | |
855 _("Region"), text2); | |
856 } else if(!strcmp(child2->name, "PCODE")) { | |
857 g_string_append_printf(info_text, | |
858 " <b>%s:</b> %s<br/>", | |
859 _("Postal Code"), text2); | |
860 } else if(!strcmp(child2->name, "CTRY") | |
861 || !strcmp(child2->name, "COUNTRY")) { | |
862 g_string_append_printf(info_text, | |
863 " <b>%s:</b> %s<br/>", | |
864 _("Country"), text2); | |
865 } | |
866 g_free(text2); | |
867 } | |
868 } else if(!strcmp(child->name, "TEL")) { | |
869 char *number; | |
870 if((child2 = xmlnode_get_child(child, "NUMBER"))) { | |
871 /* show what kind of number it is */ | |
872 number = xmlnode_get_data(child2); | |
873 if(number) { | |
874 g_string_append_printf(info_text, | |
875 "<b>%s:</b> %s<br/>", _("Telephone"), number); | |
876 g_free(number); | |
877 } | |
878 } else if((number = xmlnode_get_data(child))) { | |
879 /* lots of clients (including gaim) do this, but it's | |
880 * out of spec */ | |
881 g_string_append_printf(info_text, | |
882 "<b>%s:</b> %s<br/>", _("Telephone"), number); | |
883 g_free(number); | |
884 } | |
885 } else if(!strcmp(child->name, "EMAIL")) { | |
886 char *userid; | |
887 if((child2 = xmlnode_get_child(child, "USERID"))) { | |
888 /* show what kind of email it is */ | |
889 userid = xmlnode_get_data(child2); | |
890 if(userid) { | |
891 g_string_append_printf(info_text, | |
892 "<b>%s:</b> <a href='mailto:%s'>%s</a><br/>", | |
893 _("E-Mail"), userid, userid); | |
894 g_free(userid); | |
895 } | |
896 } else if((userid = xmlnode_get_data(child))) { | |
897 /* lots of clients (including gaim) do this, but it's | |
898 * out of spec */ | |
899 g_string_append_printf(info_text, | |
900 "<b>%s:</b> <a href='mailto:%s'>%s</a><br/>", | |
901 _("E-Mail"), userid, userid); | |
902 g_free(userid); | |
903 } | |
904 } else if(!strcmp(child->name, "ORG")) { | |
905 for(child2 = child->child; child2; child2 = child2->next) | |
906 { | |
907 char *text2; | |
908 | |
909 if(child2->type != XMLNODE_TYPE_TAG) | |
910 continue; | |
911 | |
912 text2 = xmlnode_get_data(child2); | |
913 if(text2 && !strcmp(child2->name, "ORGNAME")) { | |
914 g_string_append_printf(info_text, | |
915 "<b>%s:</b> %s<br/>", | |
916 _("Organization Name"), text2); | |
917 } else if(text2 && !strcmp(child2->name, "ORGUNIT")) { | |
918 g_string_append_printf(info_text, | |
919 "<b>%s:</b> %s<br/>", | |
920 _("Organization Unit"), text2); | |
921 } | |
922 g_free(text2); | |
923 } | |
924 } else if(text && !strcmp(child->name, "TITLE")) { | |
925 g_string_append_printf(info_text, "<b>%s:</b> %s<br/>", | |
926 _("Title"), text); | |
927 } else if(text && !strcmp(child->name, "ROLE")) { | |
928 g_string_append_printf(info_text, "<b>%s:</b> %s<br/>", | |
929 _("Role"), text); | |
930 } else if(text && !strcmp(child->name, "DESC")) { | |
931 g_string_append_printf(info_text, "<b>%s:</b> %s<br/>", | |
932 _("Description"), text); | |
933 } else if(!strcmp(child->name, "PHOTO") || | |
934 !strcmp(child->name, "LOGO")) { | |
935 char *bintext = NULL; | |
936 xmlnode *binval; | |
937 | |
938 if( ((binval = xmlnode_get_child(child, "BINVAL")) && | |
939 (bintext = xmlnode_get_data(binval))) || | |
940 (bintext = xmlnode_get_data(child))) { | |
941 gsize size; | |
942 guchar *data; | |
943 int i; | |
944 unsigned char hashval[20]; | |
945 char *p, hash[41]; | |
946 gboolean photo = (strcmp(child->name, "PHOTO") == 0); | |
947 | |
948 data = gaim_base64_decode(bintext, &size); | |
949 | |
950 jbi->vcard_imgids = g_slist_prepend(jbi->vcard_imgids, GINT_TO_POINTER(gaim_imgstore_add(data, size, "logo.png"))); | |
951 g_string_append_printf(info_text, | |
952 "<b>%s:</b> <img id='%d'><br/>", | |
953 photo ? _("Photo") : _("Logo"), | |
954 GPOINTER_TO_INT(jbi->vcard_imgids->data)); | |
955 | |
956 gaim_buddy_icons_set_for_user(js->gc->account, bare_jid, | |
957 data, size); | |
958 | |
959 gaim_cipher_digest_region("sha1", (guchar *)data, size, | |
960 sizeof(hashval), hashval, NULL); | |
961 p = hash; | |
962 for(i=0; i<20; i++, p+=2) | |
963 snprintf(p, 3, "%02x", hashval[i]); | |
964 gaim_blist_node_set_string((GaimBlistNode*)b, "avatar_hash", hash); | |
965 | |
966 g_free(data); | |
967 g_free(bintext); | |
968 } | |
969 } | |
970 g_free(text); | |
971 } | |
972 } | |
973 | |
974 jbi->vcard_text = gaim_strdup_withhtml(info_text->str); | |
975 g_string_free(info_text, TRUE); | |
976 g_free(bare_jid); | |
977 | |
978 jabber_buddy_info_show_if_ready(jbi); | |
979 } | |
980 | |
981 | |
982 static void jabber_buddy_info_resource_free(gpointer data) | |
983 { | |
984 JabberBuddyInfoResource *jbri = data; | |
985 g_free(jbri); | |
986 } | |
987 | |
988 static void jabber_version_parse(JabberStream *js, xmlnode *packet, gpointer data) | |
989 { | |
990 JabberBuddyInfo *jbi = data; | |
991 const char *type, *id, *from; | |
992 xmlnode *query; | |
993 char *resource_name; | |
994 | |
995 g_return_if_fail(jbi != NULL); | |
996 | |
997 type = xmlnode_get_attrib(packet, "type"); | |
998 id = xmlnode_get_attrib(packet, "id"); | |
999 from = xmlnode_get_attrib(packet, "from"); | |
1000 | |
1001 jabber_buddy_info_remove_id(jbi, id); | |
1002 | |
1003 if(!from) | |
1004 return; | |
1005 | |
1006 resource_name = jabber_get_resource(from); | |
1007 | |
1008 if(resource_name) { | |
1009 if(type && !strcmp(type, "result")) { | |
1010 if((query = xmlnode_get_child(packet, "query"))) { | |
1011 JabberBuddyResource *jbr = jabber_buddy_find_resource(jbi->jb, resource_name); | |
1012 if(jbr) { | |
1013 xmlnode *node; | |
1014 if((node = xmlnode_get_child(query, "name"))) { | |
1015 jbr->client.name = xmlnode_get_data(node); | |
1016 } | |
1017 if((node = xmlnode_get_child(query, "version"))) { | |
1018 jbr->client.version = xmlnode_get_data(node); | |
1019 } | |
1020 if((node = xmlnode_get_child(query, "os"))) { | |
1021 jbr->client.os = xmlnode_get_data(node); | |
1022 } | |
1023 } | |
1024 } | |
1025 } | |
1026 g_free(resource_name); | |
1027 } | |
1028 | |
1029 jabber_buddy_info_show_if_ready(jbi); | |
1030 } | |
1031 | |
1032 static void jabber_last_parse(JabberStream *js, xmlnode *packet, gpointer data) | |
1033 { | |
1034 JabberBuddyInfo *jbi = data; | |
1035 xmlnode *query; | |
1036 char *resource_name; | |
1037 const char *type, *id, *from, *seconds; | |
1038 | |
1039 g_return_if_fail(jbi != NULL); | |
1040 | |
1041 type = xmlnode_get_attrib(packet, "type"); | |
1042 id = xmlnode_get_attrib(packet, "id"); | |
1043 from = xmlnode_get_attrib(packet, "from"); | |
1044 | |
1045 jabber_buddy_info_remove_id(jbi, id); | |
1046 | |
1047 if(!from) | |
1048 return; | |
1049 | |
1050 resource_name = jabber_get_resource(from); | |
1051 | |
1052 if(resource_name) { | |
1053 if(type && !strcmp(type, "result")) { | |
1054 if((query = xmlnode_get_child(packet, "query"))) { | |
1055 seconds = xmlnode_get_attrib(query, "seconds"); | |
1056 if(seconds) { | |
1057 char *end = NULL; | |
1058 long sec = strtol(seconds, &end, 10); | |
1059 if(end != seconds) { | |
1060 JabberBuddyInfoResource *jbir = g_hash_table_lookup(jbi->resources, resource_name); | |
1061 if(jbir) { | |
1062 jbir->idle_seconds = sec; | |
1063 } | |
1064 } | |
1065 } | |
1066 } | |
1067 } | |
1068 g_free(resource_name); | |
1069 } | |
1070 | |
1071 jabber_buddy_info_show_if_ready(jbi); | |
1072 } | |
1073 | |
1074 static gboolean jabber_buddy_get_info_timeout(gpointer data) | |
1075 { | |
1076 JabberBuddyInfo *jbi = data; | |
1077 | |
1078 /* remove the pending callbacks */ | |
1079 while(jbi->ids) { | |
1080 char *id = jbi->ids->data; | |
1081 jabber_iq_remove_callback_by_id(jbi->js, id); | |
1082 g_free(id); | |
1083 jbi->ids = g_slist_remove(jbi->ids, id); | |
1084 } | |
1085 | |
1086 jbi->timeout_handle = 0; | |
1087 | |
1088 jabber_buddy_info_show_if_ready(jbi); | |
1089 | |
1090 return FALSE; | |
1091 } | |
1092 | |
1093 static void jabber_buddy_get_info_for_jid(JabberStream *js, const char *jid) | |
1094 { | |
1095 JabberIq *iq; | |
1096 xmlnode *vcard; | |
1097 GList *resources; | |
1098 JabberBuddy *jb; | |
1099 JabberBuddyInfo *jbi; | |
1100 | |
1101 jb = jabber_buddy_find(js, jid, TRUE); | |
1102 | |
1103 /* invalid JID */ | |
1104 if(!jb) | |
1105 return; | |
1106 | |
1107 jbi = g_new0(JabberBuddyInfo, 1); | |
1108 jbi->jid = g_strdup(jid); | |
1109 jbi->js = js; | |
1110 jbi->jb = jb; | |
1111 jbi->resources = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, jabber_buddy_info_resource_free); | |
1112 | |
1113 iq = jabber_iq_new(js, JABBER_IQ_GET); | |
1114 | |
1115 xmlnode_set_attrib(iq->node, "to", jid); | |
1116 vcard = xmlnode_new_child(iq->node, "vCard"); | |
1117 xmlnode_set_namespace(vcard, "vcard-temp"); | |
1118 | |
1119 jabber_iq_set_callback(iq, jabber_vcard_parse, jbi); | |
1120 jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id)); | |
1121 | |
1122 jabber_iq_send(iq); | |
1123 | |
1124 for(resources = jb->resources; resources; resources = resources->next) | |
1125 { | |
1126 JabberBuddyResource *jbr = resources->data; | |
1127 JabberBuddyInfoResource *jbir; | |
1128 char *full_jid; | |
1129 | |
1130 if ((strchr(jid, '/') == NULL) && (jbr->name != NULL)) { | |
1131 full_jid = g_strdup_printf("%s/%s", jid, jbr->name); | |
1132 } else { | |
1133 full_jid = g_strdup(jid); | |
1134 } | |
1135 | |
1136 if (jbr->name != NULL) | |
1137 { | |
1138 jbir = g_new0(JabberBuddyInfoResource, 1); | |
1139 g_hash_table_insert(jbi->resources, g_strdup(jbr->name), jbir); | |
1140 } | |
1141 | |
1142 if(!jbr->client.name) { | |
1143 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:version"); | |
1144 xmlnode_set_attrib(iq->node, "to", full_jid); | |
1145 jabber_iq_set_callback(iq, jabber_version_parse, jbi); | |
1146 jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id)); | |
1147 jabber_iq_send(iq); | |
1148 } | |
1149 | |
1150 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:last"); | |
1151 xmlnode_set_attrib(iq->node, "to", full_jid); | |
1152 jabber_iq_set_callback(iq, jabber_last_parse, jbi); | |
1153 jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id)); | |
1154 jabber_iq_send(iq); | |
1155 | |
1156 g_free(full_jid); | |
1157 } | |
1158 | |
1159 jbi->timeout_handle = gaim_timeout_add(30000, jabber_buddy_get_info_timeout, jbi); | |
1160 } | |
1161 | |
1162 void jabber_buddy_get_info(GaimConnection *gc, const char *who) | |
1163 { | |
1164 JabberStream *js = gc->proto_data; | |
1165 char *bare_jid = jabber_get_bare_jid(who); | |
1166 | |
1167 if(bare_jid) { | |
1168 jabber_buddy_get_info_for_jid(js, bare_jid); | |
1169 g_free(bare_jid); | |
1170 } | |
1171 } | |
1172 | |
1173 void jabber_buddy_get_info_chat(GaimConnection *gc, int id, | |
1174 const char *resource) | |
1175 { | |
1176 JabberStream *js = gc->proto_data; | |
1177 JabberChat *chat = jabber_chat_find_by_id(js, id); | |
1178 char *full_jid; | |
1179 | |
1180 if(!chat) | |
1181 return; | |
1182 | |
1183 full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server, resource); | |
1184 jabber_buddy_get_info_for_jid(js, full_jid); | |
1185 g_free(full_jid); | |
1186 } | |
1187 | |
1188 | |
1189 static void jabber_buddy_set_invisibility(JabberStream *js, const char *who, | |
1190 gboolean invisible) | |
1191 { | |
1192 GaimPresence *gpresence; | |
1193 GaimAccount *account; | |
1194 GaimStatus *status; | |
1195 JabberBuddy *jb = jabber_buddy_find(js, who, TRUE); | |
1196 xmlnode *presence; | |
1197 JabberBuddyState state; | |
14463 | 1198 char *msg; |
14192 | 1199 int priority; |
1200 | |
1201 account = gaim_connection_get_account(js->gc); | |
1202 gpresence = gaim_account_get_presence(account); | |
1203 status = gaim_presence_get_active_status(gpresence); | |
1204 | |
1205 gaim_status_to_jabber(status, &state, &msg, &priority); | |
1206 presence = jabber_presence_create(state, msg, priority); | |
1207 | |
14463 | 1208 g_free(msg); |
1209 | |
14192 | 1210 xmlnode_set_attrib(presence, "to", who); |
1211 if(invisible) { | |
1212 xmlnode_set_attrib(presence, "type", "invisible"); | |
1213 jb->invisible |= JABBER_INVIS_BUDDY; | |
1214 } else { | |
1215 jb->invisible &= ~JABBER_INVIS_BUDDY; | |
1216 } | |
1217 | |
1218 jabber_send(js, presence); | |
1219 xmlnode_free(presence); | |
1220 } | |
1221 | |
1222 static void jabber_buddy_make_invisible(GaimBlistNode *node, gpointer data) | |
1223 { | |
1224 GaimBuddy *buddy; | |
1225 GaimConnection *gc; | |
1226 JabberStream *js; | |
1227 | |
1228 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
1229 | |
1230 buddy = (GaimBuddy *) node; | |
1231 gc = gaim_account_get_connection(buddy->account); | |
1232 js = gc->proto_data; | |
1233 | |
1234 jabber_buddy_set_invisibility(js, buddy->name, TRUE); | |
1235 } | |
1236 | |
1237 static void jabber_buddy_make_visible(GaimBlistNode *node, gpointer data) | |
1238 { | |
1239 GaimBuddy *buddy; | |
1240 GaimConnection *gc; | |
1241 JabberStream *js; | |
1242 | |
1243 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
1244 | |
1245 buddy = (GaimBuddy *) node; | |
1246 gc = gaim_account_get_connection(buddy->account); | |
1247 js = gc->proto_data; | |
1248 | |
1249 jabber_buddy_set_invisibility(js, buddy->name, FALSE); | |
1250 } | |
1251 | |
1252 static void jabber_buddy_cancel_presence_notification(GaimBlistNode *node, | |
1253 gpointer data) | |
1254 { | |
1255 GaimBuddy *buddy; | |
1256 GaimConnection *gc; | |
1257 JabberStream *js; | |
1258 | |
1259 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
1260 | |
1261 buddy = (GaimBuddy *) node; | |
1262 gc = gaim_account_get_connection(buddy->account); | |
1263 js = gc->proto_data; | |
1264 | |
1265 /* I wonder if we should prompt the user before doing this */ | |
1266 jabber_presence_subscription_set(js, buddy->name, "unsubscribed"); | |
1267 } | |
1268 | |
1269 static void jabber_buddy_rerequest_auth(GaimBlistNode *node, gpointer data) | |
1270 { | |
1271 GaimBuddy *buddy; | |
1272 GaimConnection *gc; | |
1273 JabberStream *js; | |
1274 | |
1275 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
1276 | |
1277 buddy = (GaimBuddy *) node; | |
1278 gc = gaim_account_get_connection(buddy->account); | |
1279 js = gc->proto_data; | |
1280 | |
1281 jabber_presence_subscription_set(js, buddy->name, "subscribe"); | |
1282 } | |
1283 | |
1284 | |
1285 static void jabber_buddy_unsubscribe(GaimBlistNode *node, gpointer data) | |
1286 { | |
1287 GaimBuddy *buddy; | |
1288 GaimConnection *gc; | |
1289 JabberStream *js; | |
1290 | |
1291 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
1292 | |
1293 buddy = (GaimBuddy *) node; | |
1294 gc = gaim_account_get_connection(buddy->account); | |
1295 js = gc->proto_data; | |
1296 | |
1297 jabber_presence_subscription_set(js, buddy->name, "unsubscribe"); | |
1298 } | |
1299 | |
1300 | |
1301 static GList *jabber_buddy_menu(GaimBuddy *buddy) | |
1302 { | |
1303 GaimConnection *gc = gaim_account_get_connection(buddy->account); | |
1304 JabberStream *js = gc->proto_data; | |
1305 JabberBuddy *jb = jabber_buddy_find(js, buddy->name, TRUE); | |
1306 | |
1307 GList *m = NULL; | |
1308 GaimMenuAction *act; | |
1309 | |
1310 if(!jb) | |
1311 return m; | |
1312 | |
1313 /* XXX: fix the NOT ME below */ | |
1314 | |
1315 if(js->protocol_version == JABBER_PROTO_0_9 /* && NOT ME */) { | |
1316 if(jb->invisible & JABBER_INVIS_BUDDY) { | |
1317 act = gaim_menu_action_new(_("Un-hide From"), | |
1318 GAIM_CALLBACK(jabber_buddy_make_visible), | |
1319 NULL, NULL); | |
1320 } else { | |
1321 act = gaim_menu_action_new(_("Temporarily Hide From"), | |
1322 GAIM_CALLBACK(jabber_buddy_make_invisible), | |
1323 NULL, NULL); | |
1324 } | |
1325 m = g_list_append(m, act); | |
1326 } | |
1327 | |
1328 if(jb->subscription & JABBER_SUB_FROM /* && NOT ME */) { | |
1329 act = gaim_menu_action_new(_("Cancel Presence Notification"), | |
1330 GAIM_CALLBACK(jabber_buddy_cancel_presence_notification), | |
1331 NULL, NULL); | |
1332 m = g_list_append(m, act); | |
1333 } | |
1334 | |
1335 if(!(jb->subscription & JABBER_SUB_TO)) { | |
1336 act = gaim_menu_action_new(_("(Re-)Request authorization"), | |
1337 GAIM_CALLBACK(jabber_buddy_rerequest_auth), | |
1338 NULL, NULL); | |
1339 m = g_list_append(m, act); | |
1340 | |
1341 } else /* if(NOT ME) */{ | |
1342 | |
1343 /* shouldn't this just happen automatically when the buddy is | |
1344 removed? */ | |
1345 act = gaim_menu_action_new(_("Unsubscribe"), | |
1346 GAIM_CALLBACK(jabber_buddy_unsubscribe), | |
1347 NULL, NULL); | |
1348 m = g_list_append(m, act); | |
1349 } | |
1350 | |
1351 return m; | |
1352 } | |
1353 | |
1354 GList * | |
1355 jabber_blist_node_menu(GaimBlistNode *node) | |
1356 { | |
1357 if(GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1358 return jabber_buddy_menu((GaimBuddy *) node); | |
1359 } else { | |
1360 return NULL; | |
1361 } | |
1362 } | |
1363 | |
1364 | |
1365 const char * | |
1366 jabber_buddy_state_get_name(JabberBuddyState state) | |
1367 { | |
1368 switch(state) { | |
1369 case JABBER_BUDDY_STATE_UNKNOWN: | |
1370 return _("Unknown"); | |
1371 case JABBER_BUDDY_STATE_ERROR: | |
1372 return _("Error"); | |
1373 case JABBER_BUDDY_STATE_UNAVAILABLE: | |
1374 return _("Offline"); | |
1375 case JABBER_BUDDY_STATE_ONLINE: | |
1376 return _("Available"); | |
1377 case JABBER_BUDDY_STATE_CHAT: | |
1378 return _("Chatty"); | |
1379 case JABBER_BUDDY_STATE_AWAY: | |
1380 return _("Away"); | |
1381 case JABBER_BUDDY_STATE_XA: | |
1382 return _("Extended Away"); | |
1383 case JABBER_BUDDY_STATE_DND: | |
1384 return _("Do Not Disturb"); | |
1385 } | |
1386 | |
1387 return _("Unknown"); | |
1388 } | |
1389 | |
1390 JabberBuddyState jabber_buddy_status_id_get_state(const char *id) { | |
1391 if(!id) | |
1392 return JABBER_BUDDY_STATE_UNKNOWN; | |
1393 if(!strcmp(id, "available")) | |
1394 return JABBER_BUDDY_STATE_ONLINE; | |
1395 if(!strcmp(id, "freeforchat")) | |
1396 return JABBER_BUDDY_STATE_CHAT; | |
1397 if(!strcmp(id, "away")) | |
1398 return JABBER_BUDDY_STATE_AWAY; | |
1399 if(!strcmp(id, "extended_away")) | |
1400 return JABBER_BUDDY_STATE_XA; | |
1401 if(!strcmp(id, "dnd")) | |
1402 return JABBER_BUDDY_STATE_DND; | |
1403 if(!strcmp(id, "offline")) | |
1404 return JABBER_BUDDY_STATE_UNAVAILABLE; | |
1405 if(!strcmp(id, "error")) | |
1406 return JABBER_BUDDY_STATE_ERROR; | |
1407 | |
1408 return JABBER_BUDDY_STATE_UNKNOWN; | |
1409 } | |
1410 | |
1411 JabberBuddyState jabber_buddy_show_get_state(const char *id) { | |
1412 if(!id) | |
1413 return JABBER_BUDDY_STATE_UNKNOWN; | |
1414 if(!strcmp(id, "available")) | |
1415 return JABBER_BUDDY_STATE_ONLINE; | |
1416 if(!strcmp(id, "chat")) | |
1417 return JABBER_BUDDY_STATE_CHAT; | |
1418 if(!strcmp(id, "away")) | |
1419 return JABBER_BUDDY_STATE_AWAY; | |
1420 if(!strcmp(id, "xa")) | |
1421 return JABBER_BUDDY_STATE_XA; | |
1422 if(!strcmp(id, "dnd")) | |
1423 return JABBER_BUDDY_STATE_DND; | |
1424 if(!strcmp(id, "offline")) | |
1425 return JABBER_BUDDY_STATE_UNAVAILABLE; | |
1426 if(!strcmp(id, "error")) | |
1427 return JABBER_BUDDY_STATE_ERROR; | |
1428 | |
1429 return JABBER_BUDDY_STATE_UNKNOWN; | |
1430 } | |
1431 | |
1432 const char *jabber_buddy_state_get_show(JabberBuddyState state) { | |
1433 switch(state) { | |
1434 case JABBER_BUDDY_STATE_CHAT: | |
1435 return "chat"; | |
1436 case JABBER_BUDDY_STATE_AWAY: | |
1437 return "away"; | |
1438 case JABBER_BUDDY_STATE_XA: | |
1439 return "xa"; | |
1440 case JABBER_BUDDY_STATE_DND: | |
1441 return "dnd"; | |
1442 case JABBER_BUDDY_STATE_ONLINE: | |
1443 return "available"; | |
1444 case JABBER_BUDDY_STATE_UNKNOWN: | |
1445 case JABBER_BUDDY_STATE_ERROR: | |
1446 return NULL; | |
1447 case JABBER_BUDDY_STATE_UNAVAILABLE: | |
1448 return "offline"; | |
1449 } | |
1450 return NULL; | |
1451 } | |
1452 | |
1453 const char *jabber_buddy_state_get_status_id(JabberBuddyState state) { | |
1454 switch(state) { | |
1455 case JABBER_BUDDY_STATE_CHAT: | |
1456 return "freeforchat"; | |
1457 case JABBER_BUDDY_STATE_AWAY: | |
1458 return "away"; | |
1459 case JABBER_BUDDY_STATE_XA: | |
1460 return "extended_away"; | |
1461 case JABBER_BUDDY_STATE_DND: | |
1462 return "dnd"; | |
1463 case JABBER_BUDDY_STATE_ONLINE: | |
1464 return "available"; | |
1465 case JABBER_BUDDY_STATE_UNKNOWN: | |
1466 return "available"; | |
1467 case JABBER_BUDDY_STATE_ERROR: | |
1468 return "error"; | |
1469 case JABBER_BUDDY_STATE_UNAVAILABLE: | |
1470 return "offline"; | |
1471 } | |
1472 return NULL; | |
1473 } | |
1474 | |
1475 static void user_search_result_add_buddy_cb(GaimConnection *gc, GList *row, void *user_data) | |
1476 { | |
1477 /* XXX find out the jid */ | |
1478 gaim_blist_request_add_buddy(gaim_connection_get_account(gc), | |
1479 g_list_nth_data(row, 0), NULL, NULL); | |
1480 } | |
1481 | |
1482 static void user_search_result_cb(JabberStream *js, xmlnode *packet, gpointer data) | |
1483 { | |
1484 GaimNotifySearchResults *results; | |
1485 GaimNotifySearchColumn *column; | |
1486 xmlnode *x, *query, *item, *field; | |
1487 | |
1488 /* XXX error checking? */ | |
1489 if(!(query = xmlnode_get_child(packet, "query"))) | |
1490 return; | |
1491 | |
1492 results = gaim_notify_searchresults_new(); | |
1493 if((x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) { | |
1494 xmlnode *reported; | |
1495 gaim_debug_info("jabber", "new-skool\n"); | |
1496 if((reported = xmlnode_get_child(x, "reported"))) { | |
1497 xmlnode *field = xmlnode_get_child(reported, "field"); | |
1498 while(field) { | |
1499 /* XXX keep track of this order, use it below */ | |
1500 const char *var = xmlnode_get_attrib(field, "var"); | |
1501 const char *label = xmlnode_get_attrib(field, "label"); | |
1502 if(var) { | |
1503 column = gaim_notify_searchresults_column_new(label ? label : var); | |
1504 gaim_notify_searchresults_column_add(results, column); | |
1505 } | |
1506 field = xmlnode_get_next_twin(field); | |
1507 } | |
1508 } | |
1509 item = xmlnode_get_child(x, "item"); | |
1510 while(item) { | |
1511 GList *row = NULL; | |
1512 field = xmlnode_get_child(item, "field"); | |
1513 while(field) { | |
1514 xmlnode *valuenode = xmlnode_get_child(field, "value"); | |
1515 if(valuenode) { | |
1516 char *value = xmlnode_get_data(valuenode); | |
1517 row = g_list_append(row, value); | |
1518 } | |
1519 field = xmlnode_get_next_twin(field); | |
1520 } | |
1521 gaim_notify_searchresults_row_add(results, row); | |
1522 | |
1523 item = xmlnode_get_next_twin(item); | |
1524 } | |
1525 } else { | |
1526 /* old skool */ | |
1527 gaim_debug_info("jabber", "old-skool\n"); | |
1528 | |
1529 column = gaim_notify_searchresults_column_new(_("JID")); | |
1530 gaim_notify_searchresults_column_add(results, column); | |
1531 column = gaim_notify_searchresults_column_new(_("First Name")); | |
1532 gaim_notify_searchresults_column_add(results, column); | |
1533 column = gaim_notify_searchresults_column_new(_("Last Name")); | |
1534 gaim_notify_searchresults_column_add(results, column); | |
1535 column = gaim_notify_searchresults_column_new(_("Nickname")); | |
1536 gaim_notify_searchresults_column_add(results, column); | |
1537 column = gaim_notify_searchresults_column_new(_("E-Mail")); | |
1538 gaim_notify_searchresults_column_add(results, column); | |
1539 | |
1540 for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item)) { | |
1541 const char *jid; | |
1542 xmlnode *node; | |
1543 GList *row = NULL; | |
1544 | |
1545 if(!(jid = xmlnode_get_attrib(item, "jid"))) | |
1546 continue; | |
1547 | |
1548 row = g_list_append(row, g_strdup(jid)); | |
1549 node = xmlnode_get_child(item, "first"); | |
1550 row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); | |
1551 node = xmlnode_get_child(item, "last"); | |
1552 row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); | |
1553 node = xmlnode_get_child(item, "nick"); | |
1554 row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); | |
1555 node = xmlnode_get_child(item, "email"); | |
1556 row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); | |
1557 gaim_debug_info("jabber", "row=%d\n", row); | |
1558 gaim_notify_searchresults_row_add(results, row); | |
1559 } | |
1560 } | |
1561 | |
1562 gaim_notify_searchresults_button_add(results, GAIM_NOTIFY_BUTTON_ADD, | |
1563 user_search_result_add_buddy_cb); | |
1564 | |
1565 gaim_notify_searchresults(js->gc, NULL, NULL, _("The following are the results of your search"), results, NULL, NULL); | |
1566 } | |
1567 | |
1568 static void user_search_x_data_cb(JabberStream *js, xmlnode *result, gpointer data) | |
1569 { | |
1570 xmlnode *query; | |
1571 JabberIq *iq; | |
1572 char *dir_server = data; | |
1573 | |
1574 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:search"); | |
1575 query = xmlnode_get_child(iq->node, "query"); | |
1576 | |
1577 xmlnode_insert_child(query, result); | |
1578 | |
1579 jabber_iq_set_callback(iq, user_search_result_cb, NULL); | |
1580 xmlnode_set_attrib(iq->node, "to", dir_server); | |
1581 jabber_iq_send(iq); | |
1582 g_free(dir_server); | |
1583 } | |
1584 | |
1585 struct user_search_info { | |
1586 JabberStream *js; | |
1587 char *directory_server; | |
1588 }; | |
1589 | |
1590 static void user_search_cancel_cb(struct user_search_info *usi, GaimRequestFields *fields) | |
1591 { | |
1592 g_free(usi->directory_server); | |
1593 g_free(usi); | |
1594 } | |
1595 | |
1596 static void user_search_cb(struct user_search_info *usi, GaimRequestFields *fields) | |
1597 { | |
1598 JabberStream *js = usi->js; | |
1599 JabberIq *iq; | |
1600 xmlnode *query; | |
1601 GList *groups, *flds; | |
1602 | |
1603 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:search"); | |
1604 query = xmlnode_get_child(iq->node, "query"); | |
1605 | |
1606 for(groups = gaim_request_fields_get_groups(fields); groups; groups = groups->next) { | |
1607 for(flds = gaim_request_field_group_get_fields(groups->data); | |
1608 flds; flds = flds->next) { | |
1609 GaimRequestField *field = flds->data; | |
1610 const char *id = gaim_request_field_get_id(field); | |
1611 const char *value = gaim_request_field_string_get_value(field); | |
1612 | |
1613 if(value && (!strcmp(id, "first") || !strcmp(id, "last") || !strcmp(id, "nick") || !strcmp(id, "email"))) { | |
1614 xmlnode *y = xmlnode_new_child(query, id); | |
1615 xmlnode_insert_data(y, value, -1); | |
1616 } | |
1617 } | |
1618 } | |
1619 | |
1620 jabber_iq_set_callback(iq, user_search_result_cb, NULL); | |
1621 xmlnode_set_attrib(iq->node, "to", usi->directory_server); | |
1622 jabber_iq_send(iq); | |
1623 | |
1624 g_free(usi->directory_server); | |
1625 g_free(usi); | |
1626 } | |
1627 | |
1628 #if 0 | |
1629 /* This is for gettext only -- it will see this even though there's an #if 0. */ | |
1630 | |
1631 /* | |
1632 * An incomplete list of server generated original language search | |
1633 * comments for Jabber User Directories | |
1634 * | |
1635 * See discussion thread "Search comment for Jabber is not translatable" | |
1636 * in gaim-i18n@lists.sourceforge.net (March 2006) | |
1637 */ | |
1638 static const char * jabber_user_dir_comments [] = { | |
1639 /* current comment from Jabber User Directory users.jabber.org */ | |
1640 N_("Find a contact by entering the search criteria in the given fields. " | |
1641 "Note: Each field supports wild card searches (%)"), | |
1642 NULL | |
1643 }; | |
1644 #endif | |
1645 | |
1646 static void user_search_fields_result_cb(JabberStream *js, xmlnode *packet, gpointer data) | |
1647 { | |
1648 xmlnode *query, *x; | |
1649 const char *from, *type; | |
1650 | |
1651 if(!(from = xmlnode_get_attrib(packet, "from"))) | |
1652 return; | |
1653 | |
1654 if(!(type = xmlnode_get_attrib(packet, "type")) || !strcmp(type, "error")) { | |
1655 char *msg = jabber_parse_error(js, packet); | |
1656 | |
1657 if(!msg) | |
1658 msg = g_strdup(_("Unknown error")); | |
1659 | |
1660 gaim_notify_error(js->gc, _("Directory Query Failed"), | |
1661 _("Could not query the directory server."), msg); | |
1662 g_free(msg); | |
1663 | |
1664 return; | |
1665 } | |
1666 | |
1667 | |
1668 if(!(query = xmlnode_get_child(packet, "query"))) | |
1669 return; | |
1670 | |
1671 if((x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) { | |
1672 jabber_x_data_request(js, x, user_search_x_data_cb, g_strdup(from)); | |
1673 return; | |
1674 } else { | |
1675 struct user_search_info *usi; | |
1676 xmlnode *instnode; | |
1677 char *instructions = NULL; | |
1678 GaimRequestFields *fields; | |
1679 GaimRequestFieldGroup *group; | |
1680 GaimRequestField *field; | |
1681 | |
1682 /* old skool */ | |
1683 fields = gaim_request_fields_new(); | |
1684 group = gaim_request_field_group_new(NULL); | |
1685 gaim_request_fields_add_group(fields, group); | |
1686 | |
1687 if((instnode = xmlnode_get_child(query, "instructions"))) | |
1688 { | |
1689 char *tmp = xmlnode_get_data(instnode); | |
1690 | |
1691 if(tmp) | |
1692 { | |
1693 /* Try to translate the message (see static message | |
1694 list in jabber_user_dir_comments[]) */ | |
1695 instructions = g_strdup_printf(_("Server Instructions: %s"), _(tmp)); | |
1696 g_free(tmp); | |
1697 } | |
1698 } | |
1699 | |
1700 if(!instructions) | |
1701 { | |
1702 instructions = g_strdup(_("Fill in one or more fields to search " | |
1703 "for any matching Jabber users.")); | |
1704 } | |
1705 | |
1706 if(xmlnode_get_child(query, "first")) { | |
1707 field = gaim_request_field_string_new("first", _("First Name"), | |
1708 NULL, FALSE); | |
1709 gaim_request_field_group_add_field(group, field); | |
1710 } | |
1711 if(xmlnode_get_child(query, "last")) { | |
1712 field = gaim_request_field_string_new("last", _("Last Name"), | |
1713 NULL, FALSE); | |
1714 gaim_request_field_group_add_field(group, field); | |
1715 } | |
1716 if(xmlnode_get_child(query, "nick")) { | |
1717 field = gaim_request_field_string_new("nick", _("Nickname"), | |
1718 NULL, FALSE); | |
1719 gaim_request_field_group_add_field(group, field); | |
1720 } | |
1721 if(xmlnode_get_child(query, "email")) { | |
1722 field = gaim_request_field_string_new("email", _("E-Mail Address"), | |
1723 NULL, FALSE); | |
1724 gaim_request_field_group_add_field(group, field); | |
1725 } | |
1726 | |
1727 usi = g_new0(struct user_search_info, 1); | |
1728 usi->js = js; | |
1729 usi->directory_server = g_strdup(from); | |
1730 | |
1731 gaim_request_fields(js->gc, _("Search for Jabber users"), | |
1732 _("Search for Jabber users"), instructions, fields, | |
1733 _("Search"), G_CALLBACK(user_search_cb), | |
1734 _("Cancel"), G_CALLBACK(user_search_cancel_cb), usi); | |
1735 | |
1736 g_free(instructions); | |
1737 } | |
1738 } | |
1739 | |
1740 static void jabber_user_search_ok(JabberStream *js, const char *directory) | |
1741 { | |
1742 JabberIq *iq; | |
1743 | |
1744 /* XXX: should probably better validate the directory we're given */ | |
1745 if(!directory || !*directory) { | |
1746 gaim_notify_error(js->gc, _("Invalid Directory"), _("Invalid Directory"), NULL); | |
1747 return; | |
1748 } | |
1749 | |
1750 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:search"); | |
1751 xmlnode_set_attrib(iq->node, "to", directory); | |
1752 | |
1753 jabber_iq_set_callback(iq, user_search_fields_result_cb, NULL); | |
1754 | |
1755 jabber_iq_send(iq); | |
1756 } | |
1757 | |
1758 void jabber_user_search_begin(GaimPluginAction *action) | |
1759 { | |
1760 GaimConnection *gc = (GaimConnection *) action->context; | |
1761 JabberStream *js = gc->proto_data; | |
1762 | |
1763 gaim_request_input(gc, _("Enter a User Directory"), _("Enter a User Directory"), | |
1764 _("Select a user directory to search"), | |
1765 js->user_directories ? js->user_directories->data : "users.jabber.org", | |
1766 FALSE, FALSE, NULL, | |
1767 _("Search Directory"), GAIM_CALLBACK(jabber_user_search_ok), | |
1768 _("Cancel"), NULL, js); | |
1769 } | |
1770 | |
1771 | |
1772 |