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