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