Mercurial > pidgin
annotate src/protocols/jabber/buddy.c @ 12645:fc28451f5d96
[gaim-migrate @ 14983]
SF Patch #1314512 from Sadrul (who has a patch for everything)
"This patch introduces a flag for protocol plugins that
support offline messages (like Y!M and ICQ). This was
encouraged by the following conversation:
<sadrul> should offline buddies be listed/enabled in
the send-to menu?
<rekkanoryo> i would think only for protocols that
support offline messaging, if it's indicated that the
buddy is offline
-- <snip> --
<Bleeter> sadrul: personally, I'd like to see a
'supports offline' flag of some description
<Bleeter> one could then redirect (via plugins) through
email or alternative methods
<Bleeter> just a thought
<Paco-Paco> yeah, that sounds like a reasonble thing to have
This patch uses this flag to disable the buddies in the
send-to menu who are offline and the protocol doesn't
support offline messages."
I made this make the label insensitive instead of the whole menuitem. This
should address SimGuy's concerns about inconsistency (i.e. you could create a
conversation with someone via the buddy list that you couldn't create via the
Send To menu). I also hacked up some voodoo to show the label as sensitive when
moused-over, as that looks better (given the label-insensitive thing is itself a
hack). I think this works quite well.
BUG NOTE:
This makes more obvious an existing bug. The Send To menu isn't updated when
buddies sign on or off or change status (at least under some circumstances).
We need to fix that anyway, so I'm not going to let it hold up this commit.
Switching tabs will clear it up. I'm thinking we just might want to build the
contents of that menu when it is selected. That would save us a mess of
inefficient signal callbacks that update the Send To menus in open windows all
the time.
AIM NOTE:
This assumes that AIM can't offline message. That's not strictly true. You can
message invisible users on AIM. However, by design, we can't tell when a user
is invisible without resorting to dirty hackery. In practice, this isn't a
problem, as you can still select the AIM user from the menu. And really, how
often will you be choosing the Invisible contact, rather than the user going
Invisible in the middle of a conversation or IMing you while they're Invisible?
JABBER NOTE:
This assumes that Jabber can always offline message. This isn't strictly true.
Sadrul said:
I have updated Jabber according to this link which seems to
talk about how to determine the existence offline-message
support in a server:
http://www.jabber.org/jeps/jep-0013.html#discover
However, jabber.org doesn't seem to send the required
info. So I am not sure about it.
He later said:
I talked to Nathan and he said offline message support is
mostly assumed for most jabber servers. GTalk doesn't yet
support it, but they are working on it. So I have made
jabber to always return TRUE.
If there is truly no way to detect offline messaging capability, then this is
an acceptable solution. We could special case Google Talk because of its
popularity, and remove that later. It's probably not worth it though.
MSN NOTE:
This assumes that MSN can never offline message. That's effectively true, but
to be technically correct, MSN can offline message if there's already a
switchboard conversation open with a user. We could write an offline_message
function in the MSN prpl to detect that, but it'd be of limited usefulness,
especially given that under most circumstances (where this might matter), the
switchboard connection will be closed almost immediately.
CVS NOTE:
I'm writing to share a tragic little story.
I have a PC that I use for Gaim development. One day, I was writing a commit
message on it, when all of a suddent it went berserk. The screen started
flashing, and the whole commit message just disappeared. All of it. And it was
a good commit message! I had to cram and rewrite it really quickly. Needless to
say, my rushed commit message wasn't nearly as good, and I blame the PC for that.
Seriously, though, what kind of version control system loses your commit
message on a broken connection to the server? Stupid!
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Fri, 23 Dec 2005 19:26:04 +0000 |
parents | b4d2bba86f65 |
children | 41cf9be29754 |
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}, | |
259 {N_("Country"), NULL, TRUE, TRUE, "COUNTRY", "ADR", NULL}, | |
11361 | 260 {N_("Telephone"), NULL, TRUE, TRUE, "NUMBER", "TEL", NULL}, |
261 {N_("Email"), 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")) { | |
700 /* show which address it is */ | |
701 if(child->child) | |
8213 | 702 g_string_append_printf(info_text, "<b>%s:</b><br/>", |
7014 | 703 _("Address")); |
704 for(child2 = child->child; child2; child2 = child2->next) | |
705 { | |
706 char *text2; | |
707 | |
8135 | 708 if(child2->type != XMLNODE_TYPE_TAG) |
7014 | 709 continue; |
710 | |
711 text2 = xmlnode_get_data(child2); | |
712 if(text2 && !strcmp(child2->name, "POBOX")) { | |
713 g_string_append_printf(info_text, | |
8213 | 714 " <b>%s:</b> %s<br/>", |
7014 | 715 _("P.O. Box"), text2); |
716 } else if(text2 && !strcmp(child2->name, "EXTADR")) { | |
717 g_string_append_printf(info_text, | |
8213 | 718 " <b>%s:</b> %s<br/>", |
7014 | 719 _("Extended Address"), text2); |
720 } else if(text2 && !strcmp(child2->name, "STREET")) { | |
721 g_string_append_printf(info_text, | |
8213 | 722 " <b>%s:</b> %s<br/>", |
7014 | 723 _("Street Address"), text2); |
724 } else if(text2 && !strcmp(child2->name, "LOCALITY")) { | |
725 g_string_append_printf(info_text, | |
8213 | 726 " <b>%s:</b> %s<br/>", |
7014 | 727 _("Locality"), text2); |
728 } else if(text2 && !strcmp(child2->name, "REGION")) { | |
729 g_string_append_printf(info_text, | |
8213 | 730 " <b>%s:</b> %s<br/>", |
7014 | 731 _("Region"), text2); |
732 } else if(text2 && !strcmp(child2->name, "PCODE")) { | |
733 g_string_append_printf(info_text, | |
8213 | 734 " <b>%s:</b> %s<br/>", |
7014 | 735 _("Postal Code"), text2); |
736 } else if(text2 && (!strcmp(child2->name, "CTRY") | |
737 || !strcmp(child2->name, "COUNTRY"))) { | |
738 g_string_append_printf(info_text, | |
8213 | 739 " <b>%s:</b> %s<br/>", |
7014 | 740 _("Country"), text2); |
741 } | |
742 g_free(text2); | |
743 } | |
744 } else if(!strcmp(child->name, "TEL")) { | |
745 char *number; | |
746 if((child2 = xmlnode_get_child(child, "NUMBER"))) { | |
747 /* show what kind of number it is */ | |
748 number = xmlnode_get_data(child2); | |
749 if(number) { | |
750 g_string_append_printf(info_text, | |
8213 | 751 "<b>%s:</b> %s<br/>", _("Telephone"), number); |
7014 | 752 g_free(number); |
753 } | |
754 } else if((number = xmlnode_get_data(child))) { | |
755 /* lots of clients (including gaim) do this, but it's | |
756 * out of spec */ | |
757 g_string_append_printf(info_text, | |
8213 | 758 "<b>%s:</b> %s<br/>", _("Telephone"), number); |
7014 | 759 g_free(number); |
760 } | |
761 } else if(!strcmp(child->name, "EMAIL")) { | |
762 char *userid; | |
763 if((child2 = xmlnode_get_child(child, "USERID"))) { | |
764 /* show what kind of email it is */ | |
765 userid = xmlnode_get_data(child2); | |
766 if(userid) { | |
767 g_string_append_printf(info_text, | |
8213 | 768 "<b>%s:</b> <a href='mailto:%s'>%s</a><br/>", |
7014 | 769 _("Email"), userid, userid); |
770 g_free(userid); | |
771 } | |
772 } else if((userid = xmlnode_get_data(child))) { | |
773 /* lots of clients (including gaim) do this, but it's | |
774 * out of spec */ | |
775 g_string_append_printf(info_text, | |
8213 | 776 "<b>%s:</b> <a href='mailto:%s'>%s</a><br/>", |
7014 | 777 _("Email"), userid, userid); |
778 g_free(userid); | |
779 } | |
780 } else if(!strcmp(child->name, "ORG")) { | |
781 for(child2 = child->child; child2; child2 = child2->next) | |
782 { | |
783 char *text2; | |
784 | |
8135 | 785 if(child2->type != XMLNODE_TYPE_TAG) |
7014 | 786 continue; |
787 | |
788 text2 = xmlnode_get_data(child2); | |
789 if(text2 && !strcmp(child2->name, "ORGNAME")) { | |
790 g_string_append_printf(info_text, | |
8213 | 791 "<b>%s:</b> %s<br/>", |
7014 | 792 _("Organization Name"), text2); |
793 } else if(text2 && !strcmp(child2->name, "ORGUNIT")) { | |
794 g_string_append_printf(info_text, | |
8213 | 795 "<b>%s:</b> %s<br/>", |
7014 | 796 _("Organization Unit"), text2); |
797 } | |
798 g_free(text2); | |
799 } | |
800 } else if(text && !strcmp(child->name, "TITLE")) { | |
8213 | 801 g_string_append_printf(info_text, "<b>%s:</b> %s<br/>", |
7014 | 802 _("Title"), text); |
803 } else if(text && !strcmp(child->name, "ROLE")) { | |
8213 | 804 g_string_append_printf(info_text, "<b>%s:</b> %s<br/>", |
7014 | 805 _("Role"), text); |
806 } else if(text && !strcmp(child->name, "DESC")) { | |
8213 | 807 g_string_append_printf(info_text, "<b>%s:</b> %s<br/>", |
808 _("Description"), text); | |
7076 | 809 } else if(!strcmp(child->name, "PHOTO") || |
810 !strcmp(child->name, "LOGO")) { | |
10941 | 811 char *bintext = NULL; |
812 xmlnode *binval; | |
11361 | 813 |
814 if( ((binval = xmlnode_get_child(child, "BINVAL")) && | |
815 (bintext = xmlnode_get_data(binval))) || | |
816 (bintext = xmlnode_get_data(child))) { | |
11127 | 817 gsize size; |
11137 | 818 guchar *data; |
11127 | 819 int i; |
10941 | 820 unsigned char hashval[20]; |
11127 | 821 char *p, hash[41]; |
10941 | 822 gboolean photo = (strcmp(child->name, "PHOTO") == 0); |
10189 | 823 |
11361 | 824 data = gaim_base64_decode(bintext, &size); |
7076 | 825 |
10941 | 826 imgids = g_slist_prepend(imgids, GINT_TO_POINTER(gaim_imgstore_add(data, size, "logo.png"))); |
827 g_string_append_printf(info_text, | |
828 "<b>%s:</b> <img id='%d'><br/>", | |
829 photo ? _("Photo") : _("Logo"), | |
830 GPOINTER_TO_INT(imgids->data)); | |
7076 | 831 |
10941 | 832 gaim_buddy_icons_set_for_user(js->gc->account, bare_jid, |
833 data, size); | |
10189 | 834 |
11183 | 835 gaim_cipher_digest_region("sha1", (guchar *)data, size, |
10941 | 836 sizeof(hashval), hashval, NULL); |
837 p = hash; | |
838 for(i=0; i<20; i++, p+=2) | |
839 snprintf(p, 3, "%02x", hashval[i]); | |
840 gaim_blist_node_set_string((GaimBlistNode*)b, "avatar_hash", hash); | |
10189 | 841 |
10941 | 842 g_free(data); |
843 g_free(bintext); | |
844 } | |
7014 | 845 } |
846 g_free(text); | |
847 } | |
848 } | |
849 | |
8213 | 850 text = gaim_strdup_withhtml(info_text->str); |
851 | |
11533
c9b815aeddc1
[gaim-migrate @ 13782]
Richard Laager <rlaager@wiktel.com>
parents:
11532
diff
changeset
|
852 gaim_notify_userinfo(js->gc, from, text, NULL, NULL); |
7014 | 853 |
10189 | 854 while(imgids) { |
855 gaim_imgstore_unref(GPOINTER_TO_INT(imgids->data)); | |
856 imgids = g_slist_delete_link(imgids, imgids); | |
857 } | |
7014 | 858 g_string_free(info_text, TRUE); |
8213 | 859 g_free(text); |
10189 | 860 g_free(bare_jid); |
7014 | 861 } |
862 | |
10189 | 863 static void jabber_buddy_get_info_for_jid(JabberStream *js, const char *full_jid) |
7014 | 864 { |
865 JabberIq *iq; | |
866 xmlnode *vcard; | |
867 | |
868 iq = jabber_iq_new(js, JABBER_IQ_GET); | |
869 | |
10189 | 870 xmlnode_set_attrib(iq->node, "to", full_jid); |
7014 | 871 vcard = xmlnode_new_child(iq->node, "vCard"); |
872 xmlnode_set_attrib(vcard, "xmlns", "vcard-temp"); | |
873 | |
7395 | 874 jabber_iq_set_callback(iq, jabber_vcard_parse, NULL); |
7014 | 875 |
876 jabber_iq_send(iq); | |
877 } | |
878 | |
10189 | 879 void jabber_buddy_get_info(GaimConnection *gc, const char *who) |
880 { | |
881 JabberStream *js = gc->proto_data; | |
882 char *bare_jid = jabber_get_bare_jid(who); | |
883 | |
884 if(bare_jid) { | |
885 jabber_buddy_get_info_for_jid(js, bare_jid); | |
886 g_free(bare_jid); | |
887 } | |
888 } | |
889 | |
7014 | 890 void jabber_buddy_get_info_chat(GaimConnection *gc, int id, |
891 const char *resource) | |
892 { | |
893 JabberStream *js = gc->proto_data; | |
894 JabberChat *chat = jabber_chat_find_by_id(js, id); | |
895 char *full_jid; | |
896 | |
897 if(!chat) | |
898 return; | |
899 | |
900 full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server, resource); | |
10189 | 901 jabber_buddy_get_info_for_jid(js, full_jid); |
7014 | 902 g_free(full_jid); |
903 } | |
904 | |
7395 | 905 |
7014 | 906 static void jabber_buddy_set_invisibility(JabberStream *js, const char *who, |
907 gboolean invisible) | |
908 { | |
9944 | 909 GaimPresence *gpresence; |
910 GaimAccount *account; | |
911 GaimStatus *status; | |
7014 | 912 JabberBuddy *jb = jabber_buddy_find(js, who, TRUE); |
913 xmlnode *presence; | |
9954 | 914 JabberBuddyState state; |
915 const char *msg; | |
916 int priority; | |
7014 | 917 |
9944 | 918 account = gaim_connection_get_account(js->gc); |
919 gpresence = gaim_account_get_presence(account); | |
920 status = gaim_presence_get_active_status(gpresence); | |
921 | |
9954 | 922 gaim_status_to_jabber(status, &state, &msg, &priority); |
923 presence = jabber_presence_create(state, msg, priority); | |
924 | |
7014 | 925 xmlnode_set_attrib(presence, "to", who); |
926 if(invisible) { | |
927 xmlnode_set_attrib(presence, "type", "invisible"); | |
928 jb->invisible |= JABBER_INVIS_BUDDY; | |
929 } else { | |
930 jb->invisible &= ~JABBER_INVIS_BUDDY; | |
931 } | |
932 | |
933 jabber_send(js, presence); | |
934 xmlnode_free(presence); | |
935 } | |
936 | |
9030 | 937 static void jabber_buddy_make_invisible(GaimBlistNode *node, gpointer data) |
7014 | 938 { |
9030 | 939 GaimBuddy *buddy; |
940 GaimConnection *gc; | |
941 JabberStream *js; | |
942 | |
943 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
944 | |
945 buddy = (GaimBuddy *) node; | |
946 gc = gaim_account_get_connection(buddy->account); | |
947 js = gc->proto_data; | |
948 | |
949 jabber_buddy_set_invisibility(js, buddy->name, TRUE); | |
7014 | 950 } |
951 | |
9030 | 952 static void jabber_buddy_make_visible(GaimBlistNode *node, gpointer data) |
7014 | 953 { |
9030 | 954 GaimBuddy *buddy; |
955 GaimConnection *gc; | |
956 JabberStream *js; | |
7014 | 957 |
9030 | 958 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); |
7014 | 959 |
9030 | 960 buddy = (GaimBuddy *) node; |
961 gc = gaim_account_get_connection(buddy->account); | |
962 js = gc->proto_data; | |
963 | |
964 jabber_buddy_set_invisibility(js, buddy->name, FALSE); | |
7014 | 965 } |
966 | |
9030 | 967 static void jabber_buddy_cancel_presence_notification(GaimBlistNode *node, |
968 gpointer data) | |
7014 | 969 { |
9030 | 970 GaimBuddy *buddy; |
971 GaimConnection *gc; | |
972 JabberStream *js; | |
973 | |
974 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
7014 | 975 |
9030 | 976 buddy = (GaimBuddy *) node; |
977 gc = gaim_account_get_connection(buddy->account); | |
978 js = gc->proto_data; | |
979 | |
980 /* I wonder if we should prompt the user before doing this */ | |
981 jabber_presence_subscription_set(js, buddy->name, "unsubscribed"); | |
7014 | 982 } |
983 | |
9030 | 984 static void jabber_buddy_rerequest_auth(GaimBlistNode *node, gpointer data) |
7250 | 985 { |
9030 | 986 GaimBuddy *buddy; |
987 GaimConnection *gc; | |
988 JabberStream *js; | |
989 | |
990 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
7250 | 991 |
9030 | 992 buddy = (GaimBuddy *) node; |
993 gc = gaim_account_get_connection(buddy->account); | |
994 js = gc->proto_data; | |
995 | |
996 jabber_presence_subscription_set(js, buddy->name, "subscribe"); | |
7250 | 997 } |
998 | |
9030 | 999 |
1000 static void jabber_buddy_unsubscribe(GaimBlistNode *node, gpointer data) | |
7014 | 1001 { |
9030 | 1002 GaimBuddy *buddy; |
1003 GaimConnection *gc; | |
1004 JabberStream *js; | |
1005 | |
1006 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
1007 | |
1008 buddy = (GaimBuddy *) node; | |
1009 gc = gaim_account_get_connection(buddy->account); | |
1010 js = gc->proto_data; | |
1011 | |
1012 jabber_presence_subscription_set(js, buddy->name, "unsubscribe"); | |
1013 } | |
1014 | |
1015 | |
12323
fc464a0abccc
[gaim-migrate @ 14627]
Richard Laager <rlaager@wiktel.com>
parents:
12284
diff
changeset
|
1016 static GList *jabber_buddy_menu(GaimBuddy *buddy) |
9030 | 1017 { |
1018 GaimConnection *gc = gaim_account_get_connection(buddy->account); | |
1019 JabberStream *js = gc->proto_data; | |
1020 JabberBuddy *jb = jabber_buddy_find(js, buddy->name, TRUE); | |
1021 | |
7014 | 1022 GList *m = NULL; |
9030 | 1023 GaimBlistNodeAction *act; |
7395 | 1024 |
1025 if(!jb) | |
1026 return m; | |
1027 | |
8185 | 1028 /* XXX: fix the NOT ME below */ |
1029 | |
1030 if(js->protocol_version == JABBER_PROTO_0_9 /* && NOT ME */) { | |
8166 | 1031 if(jb->invisible & JABBER_INVIS_BUDDY) { |
9030 | 1032 act = gaim_blist_node_action_new(_("Un-hide From"), |
10662
54ac161a876e
[gaim-migrate @ 12199]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10504
diff
changeset
|
1033 jabber_buddy_make_visible, NULL, NULL); |
8166 | 1034 } else { |
9030 | 1035 act = gaim_blist_node_action_new(_("Temporarily Hide From"), |
10662
54ac161a876e
[gaim-migrate @ 12199]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10504
diff
changeset
|
1036 jabber_buddy_make_invisible, NULL, NULL); |
8166 | 1037 } |
9030 | 1038 m = g_list_append(m, act); |
7014 | 1039 } |
1040 | |
8185 | 1041 if(jb->subscription & JABBER_SUB_FROM /* && NOT ME */) { |
9030 | 1042 act = gaim_blist_node_action_new(_("Cancel Presence Notification"), |
10662
54ac161a876e
[gaim-migrate @ 12199]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10504
diff
changeset
|
1043 jabber_buddy_cancel_presence_notification, NULL, NULL); |
9030 | 1044 m = g_list_append(m, act); |
7014 | 1045 } |
1046 | |
1047 if(!(jb->subscription & JABBER_SUB_TO)) { | |
9030 | 1048 act = gaim_blist_node_action_new(_("(Re-)Request authorization"), |
10662
54ac161a876e
[gaim-migrate @ 12199]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10504
diff
changeset
|
1049 jabber_buddy_rerequest_auth, NULL, NULL); |
9030 | 1050 m = g_list_append(m, act); |
1051 | |
8185 | 1052 } else /* if(NOT ME) */{ |
9030 | 1053 |
1054 /* shouldn't this just happen automatically when the buddy is | |
1055 removed? */ | |
1056 act = gaim_blist_node_action_new(_("Unsubscribe"), | |
10662
54ac161a876e
[gaim-migrate @ 12199]
Etan Reisner <pidgin@unreliablesource.net>
parents:
10504
diff
changeset
|
1057 jabber_buddy_unsubscribe, NULL, NULL); |
9030 | 1058 m = g_list_append(m, act); |
7014 | 1059 } |
1060 | |
1061 return m; | |
1062 } | |
9030 | 1063 |
1064 GList * | |
1065 jabber_blist_node_menu(GaimBlistNode *node) | |
1066 { | |
1067 if(GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1068 return jabber_buddy_menu((GaimBuddy *) node); | |
1069 } else { | |
1070 return NULL; | |
1071 } | |
1072 } | |
1073 | |
1074 | |
9954 | 1075 const char * |
1076 jabber_buddy_state_get_name(JabberBuddyState state) | |
1077 { | |
1078 switch(state) { | |
1079 case JABBER_BUDDY_STATE_UNKNOWN: | |
1080 return _("Unknown"); | |
1081 case JABBER_BUDDY_STATE_ERROR: | |
1082 return _("Error"); | |
1083 case JABBER_BUDDY_STATE_UNAVAILABLE: | |
1084 return _("Offline"); | |
1085 case JABBER_BUDDY_STATE_ONLINE: | |
12467
1b57012eec7b
[gaim-migrate @ 14777]
Richard Laager <rlaager@wiktel.com>
parents:
12323
diff
changeset
|
1086 return _("Available"); |
9954 | 1087 case JABBER_BUDDY_STATE_CHAT: |
1088 return _("Chatty"); | |
1089 case JABBER_BUDDY_STATE_AWAY: | |
1090 return _("Away"); | |
1091 case JABBER_BUDDY_STATE_XA: | |
1092 return _("Extended Away"); | |
1093 case JABBER_BUDDY_STATE_DND: | |
1094 return _("Do Not Disturb"); | |
1095 } | |
1096 | |
1097 return _("Unknown"); | |
1098 } | |
1099 | |
1100 JabberBuddyState jabber_buddy_status_id_get_state(const char *id) { | |
1101 if(!id) | |
1102 return JABBER_BUDDY_STATE_UNKNOWN; | |
11540 | 1103 if(!strcmp(id, "available")) |
9954 | 1104 return JABBER_BUDDY_STATE_ONLINE; |
1105 if(!strcmp(id, "chat")) | |
1106 return JABBER_BUDDY_STATE_CHAT; | |
1107 if(!strcmp(id, "away")) | |
1108 return JABBER_BUDDY_STATE_AWAY; | |
1109 if(!strcmp(id, "xa")) | |
1110 return JABBER_BUDDY_STATE_XA; | |
1111 if(!strcmp(id, "dnd")) | |
1112 return JABBER_BUDDY_STATE_DND; | |
1113 if(!strcmp(id, "offline")) | |
1114 return JABBER_BUDDY_STATE_UNAVAILABLE; | |
1115 if(!strcmp(id, "error")) | |
1116 return JABBER_BUDDY_STATE_ERROR; | |
1117 | |
1118 return JABBER_BUDDY_STATE_UNKNOWN; | |
1119 } | |
1120 | |
1121 const char *jabber_buddy_state_get_status_id(JabberBuddyState state) { | |
1122 switch(state) { | |
1123 case JABBER_BUDDY_STATE_CHAT: | |
1124 return "chat"; | |
1125 case JABBER_BUDDY_STATE_AWAY: | |
1126 return "away"; | |
1127 case JABBER_BUDDY_STATE_XA: | |
1128 return "xa"; | |
1129 case JABBER_BUDDY_STATE_DND: | |
1130 return "dnd"; | |
1131 case JABBER_BUDDY_STATE_ONLINE: | |
11540 | 1132 return "available"; |
9954 | 1133 case JABBER_BUDDY_STATE_UNKNOWN: |
1134 case JABBER_BUDDY_STATE_ERROR: | |
1135 return NULL; | |
1136 case JABBER_BUDDY_STATE_UNAVAILABLE: | |
1137 return "offline"; | |
1138 } | |
1139 return NULL; | |
1140 } | |
11675 | 1141 |
1142 static void user_search_result_add_buddy_cb(GaimConnection *gc, GList *row) | |
1143 { | |
1144 /* XXX find out the jid */ | |
1145 gaim_blist_request_add_buddy(gaim_connection_get_account(gc), | |
1146 g_list_nth_data(row, 0), NULL, NULL); | |
1147 } | |
1148 | |
1149 static void user_search_result_cb(JabberStream *js, xmlnode *packet, gpointer data) | |
1150 { | |
1151 GaimNotifySearchResults *results; | |
1152 GaimNotifySearchColumn *column; | |
1153 xmlnode *x, *query, *item, *field; | |
1154 | |
1155 /* XXX error checking? */ | |
1156 if(!(query = xmlnode_get_child(packet, "query"))) | |
1157 return; | |
1158 | |
1159 results = gaim_notify_searchresults_new(); | |
1160 if((x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) { | |
1161 xmlnode *reported; | |
1162 gaim_debug_info("jabber", "new-skool\n"); | |
1163 if((reported = xmlnode_get_child(x, "reported"))) { | |
1164 xmlnode *field = xmlnode_get_child(reported, "field"); | |
1165 while(field) { | |
1166 /* XXX keep track of this order, use it below */ | |
1167 const char *var = xmlnode_get_attrib(field, "var"); | |
1168 const char *label = xmlnode_get_attrib(field, "label"); | |
1169 if(var) { | |
1170 column = gaim_notify_searchresults_column_new(label ? label : var); | |
1171 gaim_notify_searchresults_column_add(results, column); | |
1172 } | |
1173 field = xmlnode_get_next_twin(field); | |
1174 } | |
1175 } | |
1176 item = xmlnode_get_child(x, "item"); | |
1177 while(item) { | |
1178 GList *row = NULL; | |
1179 field = xmlnode_get_child(item, "field"); | |
1180 while(field) { | |
1181 xmlnode *valuenode = xmlnode_get_child(item, "value"); | |
1182 if(valuenode) { | |
1183 char *value = xmlnode_get_data(valuenode); | |
1184 row = g_list_append(row, value); | |
1185 } | |
1186 field = xmlnode_get_next_twin(field); | |
1187 } | |
1188 gaim_notify_searchresults_row_add(results, row); | |
1189 | |
1190 item = xmlnode_get_next_twin(item); | |
1191 } | |
1192 } else { | |
1193 /* old skool */ | |
1194 gaim_debug_info("jabber", "old-skool\n"); | |
1195 | |
1196 column = gaim_notify_searchresults_column_new("JID"); | |
1197 gaim_notify_searchresults_column_add(results, column); | |
1198 column = gaim_notify_searchresults_column_new("First"); | |
1199 gaim_notify_searchresults_column_add(results, column); | |
1200 column = gaim_notify_searchresults_column_new("Last"); | |
1201 gaim_notify_searchresults_column_add(results, column); | |
1202 column = gaim_notify_searchresults_column_new("Nickname"); | |
1203 gaim_notify_searchresults_column_add(results, column); | |
1204 column = gaim_notify_searchresults_column_new("E-Mail"); | |
1205 gaim_notify_searchresults_column_add(results, column); | |
1206 | |
1207 for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item)) { | |
1208 const char *jid; | |
1209 xmlnode *node; | |
1210 GList *row = NULL; | |
1211 | |
1212 if(!(jid = xmlnode_get_attrib(item, "jid"))) | |
1213 continue; | |
1214 | |
1215 row = g_list_append(row, g_strdup(jid)); | |
1216 node = xmlnode_get_child(item, "first"); | |
1217 row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); | |
1218 node = xmlnode_get_child(item, "last"); | |
1219 row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); | |
1220 node = xmlnode_get_child(item, "nick"); | |
1221 row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); | |
1222 node = xmlnode_get_child(item, "email"); | |
1223 row = g_list_append(row, node ? xmlnode_get_data(node) : NULL); | |
1224 gaim_debug_info("jabber", "row=%d\n", row); | |
1225 gaim_notify_searchresults_row_add(results, row); | |
1226 } | |
1227 } | |
1228 | |
12624
851b0bd7eb52
[gaim-migrate @ 14960]
Christopher O'Brien <siege@pidgin.im>
parents:
12467
diff
changeset
|
1229 gaim_notify_searchresults_button_add(results, GAIM_NOTIFY_BUTTON_ADD, |
11675 | 1230 user_search_result_add_buddy_cb); |
1231 | |
1232 gaim_notify_searchresults(js->gc, NULL, NULL, _("The following are the results of your search"), results, NULL, NULL); | |
1233 } | |
1234 | |
1235 static void user_search_x_data_cb(JabberStream *js, xmlnode *result, gpointer data) | |
1236 { | |
1237 xmlnode *query; | |
1238 JabberIq *iq; | |
1239 | |
1240 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:search"); | |
1241 query = xmlnode_get_child(iq->node, "query"); | |
1242 | |
1243 xmlnode_insert_child(query, result); | |
1244 | |
1245 jabber_iq_set_callback(iq, user_search_result_cb, NULL); | |
1246 jabber_iq_send(iq); | |
1247 } | |
1248 | |
1249 struct user_search_info { | |
1250 JabberStream *js; | |
1251 char *directory_server; | |
1252 }; | |
1253 | |
1254 static void user_search_cancel_cb(struct user_search_info *usi, GaimRequestFields *fields) | |
1255 { | |
1256 g_free(usi->directory_server); | |
1257 g_free(usi); | |
1258 } | |
1259 | |
1260 static void user_search_cb(struct user_search_info *usi, GaimRequestFields *fields) | |
1261 { | |
1262 JabberStream *js = usi->js; | |
1263 JabberIq *iq; | |
1264 xmlnode *query; | |
1265 GList *groups, *flds; | |
1266 | |
1267 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:search"); | |
1268 query = xmlnode_get_child(iq->node, "query"); | |
1269 | |
1270 for(groups = gaim_request_fields_get_groups(fields); groups; groups = groups->next) { | |
1271 for(flds = gaim_request_field_group_get_fields(groups->data); | |
1272 flds; flds = flds->next) { | |
1273 GaimRequestField *field = flds->data; | |
1274 const char *id = gaim_request_field_get_id(field); | |
1275 const char *value = gaim_request_field_string_get_value(field); | |
1276 | |
1277 if(value && (!strcmp(id, "first") || !strcmp(id, "last") || !strcmp(id, "nick") || !strcmp(id, "email"))) { | |
1278 xmlnode *y = xmlnode_new_child(query, id); | |
1279 xmlnode_insert_data(y, value, -1); | |
1280 } | |
1281 } | |
1282 } | |
1283 | |
1284 jabber_iq_set_callback(iq, user_search_result_cb, NULL); | |
1285 xmlnode_set_attrib(iq->node, "to", usi->directory_server); | |
1286 jabber_iq_send(iq); | |
1287 | |
1288 g_free(usi->directory_server); | |
1289 g_free(usi); | |
1290 } | |
1291 | |
1292 static void user_search_fields_result_cb(JabberStream *js, xmlnode *packet, gpointer data) | |
1293 { | |
1294 xmlnode *query, *x; | |
1295 const char *from; | |
1296 | |
1297 /* i forget, do i have to check for error? XXX */ | |
12284
ecd471d1eeec
[gaim-migrate @ 14588]
Etan Reisner <pidgin@unreliablesource.net>
parents:
11675
diff
changeset
|
1298 if(!(from = xmlnode_get_attrib(packet, "from"))) |
11675 | 1299 return; |
1300 | |
1301 | |
1302 if(!(query = xmlnode_get_child(packet, "query"))) | |
1303 return; | |
1304 | |
1305 if((x = xmlnode_get_child_with_namespace(packet, "x", "jabber:x:data"))) { | |
1306 jabber_x_data_request(js, x, user_search_x_data_cb, NULL); | |
1307 return; | |
1308 } else { | |
1309 struct user_search_info *usi; | |
1310 xmlnode *instnode; | |
1311 char *instructions; | |
1312 GaimRequestFields *fields; | |
1313 GaimRequestFieldGroup *group; | |
1314 GaimRequestField *field; | |
1315 | |
1316 /* old skool */ | |
1317 fields = gaim_request_fields_new(); | |
1318 group = gaim_request_field_group_new(NULL); | |
1319 gaim_request_fields_add_group(fields, group); | |
1320 | |
1321 if((instnode = xmlnode_get_child(query, "instructions"))) | |
1322 instructions = xmlnode_get_data(instnode); | |
1323 else | |
1324 instructions = g_strdup(_("Fill in one or more fields to search " | |
1325 "for any matching Jabber users.")); | |
1326 | |
1327 if(xmlnode_get_child(query, "first")) { | |
1328 field = gaim_request_field_string_new("first", _("First Name"), | |
1329 NULL, FALSE); | |
1330 gaim_request_field_group_add_field(group, field); | |
1331 } | |
1332 if(xmlnode_get_child(query, "last")) { | |
1333 field = gaim_request_field_string_new("last", _("Last Name"), | |
1334 NULL, FALSE); | |
1335 gaim_request_field_group_add_field(group, field); | |
1336 } | |
1337 if(xmlnode_get_child(query, "nick")) { | |
1338 field = gaim_request_field_string_new("nick", _("Nickname"), | |
1339 NULL, FALSE); | |
1340 gaim_request_field_group_add_field(group, field); | |
1341 } | |
1342 if(xmlnode_get_child(query, "email")) { | |
1343 field = gaim_request_field_string_new("email", _("E-Mail Address"), | |
1344 NULL, FALSE); | |
1345 gaim_request_field_group_add_field(group, field); | |
1346 } | |
1347 | |
1348 usi = g_new0(struct user_search_info, 1); | |
1349 usi->js = js; | |
1350 usi->directory_server = g_strdup(from); | |
1351 | |
1352 gaim_request_fields(js->gc, _("Search for Jabber users"), | |
1353 _("Search for Jabber users"), instructions, fields, | |
1354 _("Search"), G_CALLBACK(user_search_cb), | |
1355 _("Cancel"), G_CALLBACK(user_search_cancel_cb), usi); | |
1356 | |
1357 g_free(instructions); | |
1358 } | |
1359 } | |
1360 | |
1361 static void jabber_user_search_ok(JabberStream *js, const char *directory) | |
1362 { | |
1363 JabberIq *iq; | |
1364 | |
1365 /* XXX: should probably better validate the directory we're given */ | |
1366 if(!directory || !*directory) { | |
1367 gaim_notify_error(js->gc, _("Invalid Directory"), _("Invalid Directory"), NULL); | |
1368 return; | |
1369 } | |
1370 | |
1371 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:search"); | |
1372 xmlnode_set_attrib(iq->node, "to", directory); | |
1373 | |
1374 jabber_iq_set_callback(iq, user_search_fields_result_cb, NULL); | |
1375 | |
1376 jabber_iq_send(iq); | |
1377 } | |
1378 | |
1379 void jabber_user_search_begin(GaimPluginAction *action) | |
1380 { | |
1381 GaimConnection *gc = (GaimConnection *) action->context; | |
1382 JabberStream *js = gc->proto_data; | |
1383 | |
1384 gaim_request_input(gc, _("Enter a User Directory"), _("Enter a User Directory"), | |
1385 _("Select a user directory to search"), | |
1386 js->user_directories ? js->user_directories->data : "users.jabber.org", | |
1387 FALSE, FALSE, NULL, | |
1388 _("Search Directory"), GAIM_CALLBACK(jabber_user_search_ok), | |
1389 _("Cancel"), NULL, js); | |
1390 } |