Mercurial > pidgin.yaz
annotate src/protocols/jabber/jabber.c @ 3572:bdd0bebd2d04
[gaim-migrate @ 3670]
Phase II. No longer do you have to worry about protocol plugins. When
Gaim probes plugins on load, it will detect protocol plugins and add them
to the list of available protocols. When you try to log an account on with
one of them, Gaim will automatically load the plugin--when no more accounts
need the protocol--Gaim will automatically unload it. Protocol plugins are
no longer available in the plugins ui, and no protocols are compiled statically
by default.
committer: Tailor Script <tailor@pidgin.im>
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Mon, 30 Sep 2002 01:05:18 +0000 |
parents | 8e688863a9f1 |
children | a662c133d1fd |
rev | line source |
---|---|
2086 | 1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ |
2 /* | |
3 * gaim | |
4 * | |
5 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> | |
6 * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx> | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 * | |
22 */ | |
23 | |
24 #ifdef HAVE_CONFIG_H | |
25 #include "config.h" | |
26 #endif | |
27 | |
28 | |
29 #include <netdb.h> | |
30 #include <unistd.h> | |
31 #include <errno.h> | |
32 #include <netinet/in.h> | |
33 #include <arpa/inet.h> | |
34 #include <string.h> | |
35 #include <stdlib.h> | |
36 #include <stdio.h> | |
37 #include <time.h> | |
38 #include <sys/socket.h> | |
39 #include <sys/utsname.h> | |
40 #include <sys/stat.h> | |
41 #include "multi.h" | |
42 #include "prpl.h" | |
43 #include "gaim.h" | |
2232
14e8978f86bb
[gaim-migrate @ 2242]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2231
diff
changeset
|
44 #ifdef MAX |
14e8978f86bb
[gaim-migrate @ 2242]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2231
diff
changeset
|
45 #undef MAX |
14e8978f86bb
[gaim-migrate @ 2242]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2231
diff
changeset
|
46 #endif |
14e8978f86bb
[gaim-migrate @ 2242]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2231
diff
changeset
|
47 #ifdef MIN |
14e8978f86bb
[gaim-migrate @ 2242]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2231
diff
changeset
|
48 #undef MIN |
14e8978f86bb
[gaim-migrate @ 2242]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2231
diff
changeset
|
49 #endif |
2086 | 50 #include "jabber.h" |
51 #include "proxy.h" | |
52 | |
53 #include "pixmaps/available.xpm" | |
54 #include "pixmaps/available-away.xpm" | |
55 #include "pixmaps/available-chat.xpm" | |
56 #include "pixmaps/available-xa.xpm" | |
57 #include "pixmaps/available-dnd.xpm" | |
3259 | 58 #include "pixmaps/available-error.xpm" |
2086 | 59 |
60 /* The priv member of gjconn's is a gaim_connection for now. */ | |
61 #define GJ_GC(x) ((struct gaim_connection *)(x)->priv) | |
62 | |
63 #define IQID_AUTH "__AUTH__" | |
64 | |
65 #define IQ_NONE -1 | |
66 #define IQ_AUTH 0 | |
67 #define IQ_ROSTER 1 | |
68 | |
3259 | 69 #define UC_AWAY (0x02 | UC_UNAVAILABLE) |
70 #define UC_CHAT 0x04 | |
71 #define UC_XA (0x08 | UC_UNAVAILABLE) | |
72 #define UC_DND (0x10 | UC_UNAVAILABLE) | |
73 #define UC_ERROR (0x20 | UC_UNAVAILABLE) | |
2086 | 74 |
75 #define DEFAULT_SERVER "jabber.org" | |
76 #define DEFAULT_GROUPCHAT "conference.jabber.org" | |
77 #define DEFAULT_PORT 5222 | |
78 | |
79 #define USEROPT_PORT 0 | |
80 | |
3311 | 81 #define JABBER_TYPING_NOTIFY_INT 15 /* Delay (in seconds) between sending typing notifications */ |
82 | |
3074 | 83 /* |
84 * Note: "was_connected" may seem redundant, but it was needed and I | |
85 * didn't want to touch the Jabber state stuff not specific to Gaim. | |
86 */ | |
2086 | 87 typedef struct gjconn_struct { |
88 /* Core structure */ | |
89 pool p; /* Memory allocation pool */ | |
90 int state; /* Connection state flag */ | |
3074 | 91 int was_connected; /* We were once connected */ |
2086 | 92 int fd; /* Connection file descriptor */ |
93 jid user; /* User info */ | |
94 char *pass; /* User passwd */ | |
95 | |
96 /* Stream stuff */ | |
97 int id; /* id counter for jab_getid() function */ | |
98 char idbuf[9]; /* temporary storage for jab_getid() */ | |
99 char *sid; /* stream id from server, for digest auth */ | |
100 XML_Parser parser; /* Parser instance */ | |
101 xmlnode current; /* Current node in parsing instance.. */ | |
102 | |
103 /* Event callback ptrs */ | |
2956 | 104 void (*on_state)(struct gjconn_struct *gjc, int state); |
105 void (*on_packet)(struct gjconn_struct *gjc, jpacket p); | |
106 | |
107 GHashTable *queries; /* query tracker */ | |
2086 | 108 |
109 void *priv; | |
110 | |
111 } *gjconn, gjconn_struct; | |
112 | |
2956 | 113 typedef void (*gjconn_state_h)(gjconn gjc, int state); |
114 typedef void (*gjconn_packet_h)(gjconn gjc, jpacket p); | |
2086 | 115 |
116 static gjconn gjab_new(char *user, char *pass, void *priv); | |
2956 | 117 static void gjab_delete(gjconn gjc); |
118 static void gjab_state_handler(gjconn gjc, gjconn_state_h h); | |
119 static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h); | |
120 static void gjab_start(gjconn gjc); | |
121 static void gjab_stop(gjconn gjc); | |
2086 | 122 /* |
2956 | 123 static int gjab_getfd(gjconn gjc); |
124 static jid gjab_getjid(gjconn gjc); | |
125 static char *gjab_getsid(gjconn gjc); | |
2086 | 126 */ |
2956 | 127 static char *gjab_getid(gjconn gjc); |
128 static void gjab_send(gjconn gjc, xmlnode x); | |
129 static void gjab_send_raw(gjconn gjc, const char *str); | |
130 static void gjab_recv(gjconn gjc); | |
131 static void gjab_auth(gjconn gjc); | |
132 | |
133 /* | |
134 * It is *this* to which we point the gaim_connection proto_data | |
135 */ | |
2086 | 136 struct jabber_data { |
2956 | 137 gjconn gjc; |
2086 | 138 gboolean did_import; |
2956 | 139 GSList *chats; |
2086 | 140 time_t idle; |
2800
0ad63a625eec
[gaim-migrate @ 2813]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2791
diff
changeset
|
141 gboolean die; |
3311 | 142 GHashTable *buddies; |
2086 | 143 }; |
144 | |
2956 | 145 /* |
3340 | 146 * Used in jabber_buddy_data.invisible, below |
147 */ | |
148 #define JABBER_NOT_INVIS 0x00 | |
149 #define JABBER_SERV_INVIS 0x01 /* Invisible set on server */ | |
150 #define JABBER_BUD_INVIS 0x02 /* Invisible set on buddy */ | |
151 | |
152 /* | |
3311 | 153 * It is *this* to which we point the buddy proto_data |
154 */ | |
155 struct jabber_buddy_data { | |
156 GSList *resources; | |
157 char *error_msg; | |
3340 | 158 unsigned invisible; /* We've set presence type invisible for this buddy */ |
3311 | 159 }; |
160 | |
161 /* | |
162 * per-resource info | |
163 */ | |
164 typedef struct jabber_resource_info { | |
165 char *name; | |
166 int priority; | |
167 int state; | |
168 char *away_msg; | |
169 char *thread_id; | |
170 gboolean has_composing; | |
171 } *jab_res_info; | |
172 | |
173 /* | |
174 * For our own jid handling | |
175 * | |
176 * We do our own so we can cleanly parse buddy names | |
177 * (user@server/resource) and rid ourselves of the | |
178 * struct when we're done with it. The Jabber lib | |
179 * structs last the life of the pool--we frequently | |
180 * don't want that. | |
181 * | |
182 * We use the real jid structs so we can make use of | |
183 * jid_safe(), jid_cmp() and some others. | |
184 * | |
185 * BE CAREFUL using the Jabber lib routines. | |
186 * Many of them assume pool use and are not | |
187 * amenable to use with our own! | |
188 * | |
189 * We give them special names so we know, throughout | |
190 * the code, that they're not alloc'd out of pool | |
191 * memory and we can, and must, dispose of them when | |
192 * we're done with 'em. | |
193 */ | |
194 #define gaim_jid_struct jid_struct | |
195 typedef struct gaim_jid_struct *gaim_jid; | |
196 | |
197 /* | |
2956 | 198 * Jabber "chat group" info. Pointers to these go in jabber_data |
199 * pending and existing chats lists. | |
200 */ | |
2086 | 201 struct jabber_chat { |
3311 | 202 gaim_jid gjid; |
2086 | 203 struct gaim_connection *gc; |
204 struct conversation *b; | |
205 int id; | |
2956 | 206 int state; |
2086 | 207 }; |
208 | |
2956 | 209 /* |
210 * Jabber chat states... | |
211 * | |
212 * Note: due to a bug in one version of the Jabber server, subscriptions | |
213 * to chat groups aren't (always?) properly removed at the server. The | |
214 * result is clients receive Jabber "presence" notifications for JIDs | |
215 * they no longer care about. The problem with such vestigial notifies is | |
216 * that we really have no way of telling if it's vestigial or if it's a | |
217 * valid "buddy" presence notification. So we keep jabber_chat structs | |
218 * around after leaving a chat group and simply mark them "closed." That | |
219 * way we can test for such errant presence notifications. I.e.: if we | |
220 * get a presence notfication from a JID that matches a chat group JID, | |
221 * we disregard it. | |
222 */ | |
223 #define JCS_PENDING 1 /* pending */ | |
224 #define JCS_ACTIVE 2 /* active */ | |
225 #define JCS_CLOSED 3 /* closed */ | |
226 | |
227 | |
228 #define STATE_EVT(arg) if(gjc->on_state) { (gjc->on_state)(gjc, (arg) ); } | |
229 | |
230 static void jabber_handlevcard(gjconn, xmlnode, char *); | |
2086 | 231 |
232 static char *create_valid_jid(const char *given, char *server, char *resource) | |
233 { | |
234 char *valid; | |
235 | |
236 if (!strchr(given, '@')) | |
237 valid = g_strdup_printf("%s@%s/%s", given, server, resource); | |
238 else if (!strchr(strchr(given, '@'), '/')) | |
239 valid = g_strdup_printf("%s/%s", given, resource); | |
240 else | |
241 valid = g_strdup(given); | |
242 | |
243 return valid; | |
244 } | |
245 | |
3311 | 246 /* |
247 * Dispose of a gaim_jid_struct | |
248 */ | |
249 static void gaim_jid_free(gaim_jid gjid) | |
250 { | |
251 if(gjid) { | |
252 if(gjid->resource) | |
253 free(gjid->resource); | |
254 if(gjid->user) | |
255 free(gjid->user); | |
256 if(gjid->server) | |
257 free(gjid->server); | |
258 if(gjid->full) | |
259 free(gjid->full); | |
260 free(gjid); | |
261 } | |
262 } | |
263 | |
264 /* | |
265 * Create a new gjid struct | |
266 * | |
267 * Unlike jid_new(), also creates "full." | |
268 * | |
269 * Shamelessly copied, in part, from jid.c: jid_new() | |
270 * | |
271 * Caller is responsible for freeing the space allocated by this via | |
272 * gaim_jid_free(). | |
273 * | |
274 * JFIXME: Has a local declaration for jid.c:jid_safe(). I've put in a | |
275 * request to have that added to libjabber's lib.h file. (JSeymour) | |
276 */ | |
277 static gaim_jid gaim_jid_new(char *name) | |
278 { | |
279 extern jid jid_safe(jid); /* *retch* */ | |
280 | |
281 gaim_jid gjid = NULL; | |
282 | |
283 if(name && strlen(name)) { | |
284 char *server, *resource, *type, *str; | |
285 int full_len = 0; | |
286 | |
287 /* user@server/resource */ | |
288 | |
289 str = strdup(name); /* we mangle a copy */ | |
290 | |
291 gjid = calloc(1, sizeof(struct gaim_jid_struct)); | |
292 | |
293 if((resource = strstr(str, "/")) != NULL) { | |
294 *resource = '\0'; | |
295 ++resource; | |
296 if((full_len = strlen(resource)) > 0) { | |
297 gjid->resource = strdup(resource); | |
298 ++full_len; /* for later "/" addition */ | |
299 } | |
300 } else { | |
301 resource = str + strlen(str); /* point to end */ | |
302 } | |
303 | |
304 type = strstr(str, ":"); | |
305 if(type != NULL && type < resource) { | |
306 *type = '\0'; | |
307 ++type; | |
308 str = type; /* ignore the type: prefix */ | |
309 } | |
310 | |
311 server = strstr(str, "@"); | |
312 | |
313 /* | |
314 * if there's no @, it's just the server address | |
315 */ | |
316 if(server == NULL || server > resource) { | |
317 gjid->server = strdup(str); | |
318 full_len += strlen(str); | |
319 } else { | |
320 *server = '\0'; | |
321 ++server; | |
322 gjid->server = strdup(server); | |
323 full_len += strlen(server) + 1; /* account for later "@" */ | |
324 if(strlen(str) > 0) { | |
325 gjid->user = strdup(str); | |
326 full_len += strlen(str); | |
327 } | |
328 } | |
329 | |
330 free(str); | |
331 | |
332 if(!jid_safe(gjid)) { | |
333 gaim_jid_free(gjid); | |
334 gjid = NULL; | |
335 } else { | |
336 if(full_len) { | |
337 char *s = gjid->full = malloc(++full_len); | |
338 | |
339 if(gjid->user) { | |
340 strcpy(s, gjid->user); | |
341 s += strlen(gjid->user); | |
342 } | |
343 if(gjid->server) { | |
344 if(s > gjid->full) | |
345 *(s++) = '@'; | |
346 strcpy(s, gjid->server); | |
347 s += strlen(gjid->server); | |
348 } | |
349 if(gjid->resource) { | |
350 *(s++) = '/'; | |
351 strcpy(s, gjid->resource); | |
352 } | |
353 } | |
354 } | |
355 } | |
356 | |
357 return gjid; | |
358 } | |
359 | |
360 /* | |
361 * Get a "username@server" from unadorned "username" | |
362 * | |
363 * If there's no "@server" part and "who" doesn't match the | |
364 * gjconn server (which would indicate that "who" *is* the | |
365 * server in case of server messages), the gjconn server is | |
366 * appended. | |
367 * | |
368 * If incl_resource is TRUE (non-0), the returned string | |
369 * includes the "/resource" part (if it exists), otherwise not. | |
370 * | |
371 * Allocates space for returned string. Caller is | |
372 * responsible for freeing it with g_free(). | |
373 * | |
374 * If "gjid" is non-null, sets that as well. Caller is | |
375 * reponsible for freeing that via gaim_jid_free() when done | |
376 * with it. | |
377 */ | |
3466 | 378 static gchar *get_realwho(gjconn gjc, const char *who, int incl_resource, gaim_jid *gjid) |
3311 | 379 { |
380 gaim_jid my_gjid; | |
381 gchar *my_who; | |
382 gchar *realwho = NULL; | |
383 | |
384 if(!(who && who[0])) { | |
385 return NULL; | |
386 } | |
387 | |
388 /* | |
389 * Bare username and "username" not the server itself? | |
390 */ | |
391 if(!strchr(who, '@') && strcasecmp(who, gjc->user->server)) { | |
392 my_who = g_strdup_printf("%s@%s", who, gjc->user->server); | |
393 } else { | |
394 my_who = g_strdup(who); | |
395 } | |
396 | |
397 if((my_gjid = gaim_jid_new(my_who)) != NULL) { | |
398 /* | |
399 * If there's no "user" part, "who" was just the server or perhaps a transport (?) | |
400 */ | |
401 if(my_gjid->user) { | |
402 /* | |
403 * Include "/resource" bit? | |
404 */ | |
405 if(incl_resource) { | |
406 realwho = g_strdup(my_gjid->full); | |
407 } else { | |
408 realwho = g_strdup_printf("%s@%s", my_gjid->user, my_gjid->server); | |
409 } | |
410 } else { | |
411 realwho = g_strdup(my_gjid->server); | |
412 } | |
413 } | |
414 | |
415 g_free(my_who); | |
416 | |
417 if(gjid) { | |
418 *gjid = my_gjid; | |
419 } else { | |
420 gaim_jid_free(my_gjid); | |
421 } | |
422 | |
423 return realwho; | |
424 } | |
425 | |
2086 | 426 static gjconn gjab_new(char *user, char *pass, void *priv) |
427 { | |
428 pool p; | |
2956 | 429 gjconn gjc; |
2086 | 430 |
431 if (!user) | |
432 return (NULL); | |
433 | |
434 p = pool_new(); | |
435 if (!p) | |
436 return (NULL); | |
2956 | 437 gjc = pmalloc_x(p, sizeof(gjconn_struct), 0); |
438 if (!gjc) { | |
439 pool_free(p); /* no need for this anymore! */ | |
2086 | 440 return (NULL); |
2956 | 441 } |
442 gjc->p = p; | |
443 | |
3236 | 444 if((gjc->user = jid_new(p, user)) == NULL) { |
445 pool_free(p); /* no need for this anymore! */ | |
446 return (NULL); | |
447 } | |
3257 | 448 |
449 gjc->pass = strdup(pass); | |
2956 | 450 |
451 gjc->state = JCONN_STATE_OFF; | |
3074 | 452 gjc->was_connected = 0; |
2956 | 453 gjc->id = 1; |
454 gjc->fd = -1; | |
455 | |
456 gjc->priv = priv; | |
457 | |
458 return gjc; | |
2086 | 459 } |
460 | |
2956 | 461 static void gjab_delete(gjconn gjc) |
2086 | 462 { |
2956 | 463 if (!gjc) |
2086 | 464 return; |
465 | |
2956 | 466 gjab_stop(gjc); |
3257 | 467 free(gjc->pass); |
2956 | 468 pool_free(gjc->p); |
2086 | 469 } |
470 | |
2956 | 471 static void gjab_state_handler(gjconn gjc, gjconn_state_h h) |
2086 | 472 { |
2956 | 473 if (!gjc) |
2086 | 474 return; |
475 | |
2956 | 476 gjc->on_state = h; |
2086 | 477 } |
478 | |
2956 | 479 static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h) |
2086 | 480 { |
2956 | 481 if (!gjc) |
2086 | 482 return; |
483 | |
2956 | 484 gjc->on_packet = h; |
2086 | 485 } |
486 | |
2956 | 487 static void gjab_stop(gjconn gjc) |
2086 | 488 { |
2956 | 489 if (!gjc || gjc->state == JCONN_STATE_OFF) |
2086 | 490 return; |
491 | |
2956 | 492 gjab_send_raw(gjc, "</stream:stream>"); |
493 gjc->state = JCONN_STATE_OFF; | |
3074 | 494 gjc->was_connected = 0; |
2956 | 495 close(gjc->fd); |
496 gjc->fd = -1; | |
497 XML_ParserFree(gjc->parser); | |
498 gjc->parser = NULL; | |
2086 | 499 } |
500 | |
501 /* | |
2956 | 502 static int gjab_getfd(gjconn gjc) |
2086 | 503 { |
2956 | 504 if (gjc) |
505 return gjc->fd; | |
2086 | 506 else |
507 return -1; | |
508 } | |
509 | |
2956 | 510 static jid gjab_getjid(gjconn gjc) |
2086 | 511 { |
2956 | 512 if (gjc) |
513 return (gjc->user); | |
2086 | 514 else |
515 return NULL; | |
516 } | |
517 | |
2956 | 518 static char *gjab_getsid(gjconn gjc) |
2086 | 519 { |
2956 | 520 if (gjc) |
521 return (gjc->sid); | |
2086 | 522 else |
523 return NULL; | |
524 } | |
525 */ | |
526 | |
2956 | 527 static char *gjab_getid(gjconn gjc) |
2086 | 528 { |
2956 | 529 snprintf(gjc->idbuf, 8, "%d", gjc->id++); |
530 return &gjc->idbuf[0]; | |
2086 | 531 } |
532 | |
2956 | 533 static void gjab_send(gjconn gjc, xmlnode x) |
2086 | 534 { |
2956 | 535 if (gjc && gjc->state != JCONN_STATE_OFF) { |
2086 | 536 char *buf = xmlnode2str(x); |
537 if (buf) | |
2956 | 538 write(gjc->fd, buf, strlen(buf)); |
2086 | 539 debug_printf("gjab_send: %s\n", buf); |
540 } | |
541 } | |
542 | |
2956 | 543 static void gjab_send_raw(gjconn gjc, const char *str) |
2086 | 544 { |
2956 | 545 if (gjc && gjc->state != JCONN_STATE_OFF) { |
546 /* | |
547 * JFIXME: No error detection?!?! | |
548 */ | |
549 if(write(gjc->fd, str, strlen(str)) < 0) { | |
550 fprintf(stderr, "DBG: Problem sending. Error: %d\n", errno); | |
551 fflush(stderr); | |
552 } | |
2086 | 553 debug_printf("gjab_send_raw: %s\n", str); |
554 } | |
555 } | |
556 | |
2956 | 557 static void gjab_reqroster(gjconn gjc) |
2086 | 558 { |
559 xmlnode x; | |
560 | |
561 x = jutil_iqnew(JPACKET__GET, NS_ROSTER); | |
2956 | 562 xmlnode_put_attrib(x, "id", gjab_getid(gjc)); |
563 | |
564 gjab_send(gjc, x); | |
2086 | 565 xmlnode_free(x); |
566 } | |
567 | |
2956 | 568 static void gjab_reqauth(gjconn gjc) |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
569 { |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
570 xmlnode x, y, z; |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
571 char *user; |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
572 |
2956 | 573 if (!gjc) |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
574 return; |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
575 |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
576 x = jutil_iqnew(JPACKET__GET, NS_AUTH); |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
577 xmlnode_put_attrib(x, "id", IQID_AUTH); |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
578 y = xmlnode_get_tag(x, "query"); |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
579 |
2956 | 580 user = gjc->user->user; |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
581 |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
582 if (user) { |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
583 z = xmlnode_insert_tag(y, "username"); |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
584 xmlnode_insert_cdata(z, user, -1); |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
585 } |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
586 |
2956 | 587 gjab_send(gjc, x); |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
588 xmlnode_free(x); |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
589 } |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
590 |
2956 | 591 static void gjab_auth(gjconn gjc) |
2086 | 592 { |
593 xmlnode x, y, z; | |
594 char *hash, *user; | |
595 | |
2956 | 596 if (!gjc) |
2086 | 597 return; |
598 | |
599 x = jutil_iqnew(JPACKET__SET, NS_AUTH); | |
600 xmlnode_put_attrib(x, "id", IQID_AUTH); | |
601 y = xmlnode_get_tag(x, "query"); | |
602 | |
2956 | 603 user = gjc->user->user; |
2086 | 604 |
605 if (user) { | |
606 z = xmlnode_insert_tag(y, "username"); | |
607 xmlnode_insert_cdata(z, user, -1); | |
608 } | |
609 | |
610 z = xmlnode_insert_tag(y, "resource"); | |
2956 | 611 xmlnode_insert_cdata(z, gjc->user->resource, -1); |
612 | |
613 if (gjc->sid) { | |
614 debug_printf("digest authentication (sid %s)\n", gjc->sid); | |
2086 | 615 z = xmlnode_insert_tag(y, "digest"); |
2956 | 616 hash = pmalloc(x->p, strlen(gjc->sid) + strlen(gjc->pass) + 1); |
617 strcpy(hash, gjc->sid); | |
618 strcat(hash, gjc->pass); | |
2086 | 619 hash = shahash(hash); |
620 xmlnode_insert_cdata(z, hash, 40); | |
621 } else { | |
622 z = xmlnode_insert_tag(y, "password"); | |
2956 | 623 xmlnode_insert_cdata(z, gjc->pass, -1); |
2086 | 624 } |
625 | |
2956 | 626 gjab_send(gjc, x); |
2086 | 627 xmlnode_free(x); |
628 | |
629 return; | |
630 } | |
631 | |
2956 | 632 static void gjab_recv(gjconn gjc) |
2086 | 633 { |
634 static char buf[4096]; | |
635 int len; | |
636 | |
2956 | 637 if (!gjc || gjc->state == JCONN_STATE_OFF) |
2086 | 638 return; |
639 | |
3234 | 640 if ((len = read(gjc->fd, buf, sizeof(buf) - 1)) > 0) { |
2956 | 641 struct jabber_data *jd = GJ_GC(gjc)->proto_data; |
2086 | 642 buf[len] = '\0'; |
643 debug_printf("input (len %d): %s\n", len, buf); | |
2956 | 644 XML_Parse(gjc->parser, buf, len, 0); |
2800
0ad63a625eec
[gaim-migrate @ 2813]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2791
diff
changeset
|
645 if (jd->die) |
2956 | 646 signoff(GJ_GC(gjc)); |
3105 | 647 } else if (len < 0 || errno != EAGAIN) { |
2086 | 648 STATE_EVT(JCONN_STATE_OFF) |
649 } | |
650 } | |
651 | |
652 static void startElement(void *userdata, const char *name, const char **attribs) | |
653 { | |
654 xmlnode x; | |
2956 | 655 gjconn gjc = (gjconn) userdata; |
656 | |
657 if (gjc->current) { | |
2086 | 658 /* Append the node to the current one */ |
2956 | 659 x = xmlnode_insert_tag(gjc->current, name); |
2086 | 660 xmlnode_put_expat_attribs(x, attribs); |
661 | |
2956 | 662 gjc->current = x; |
2086 | 663 } else { |
664 x = xmlnode_new_tag(name); | |
665 xmlnode_put_expat_attribs(x, attribs); | |
666 if (strcmp(name, "stream:stream") == 0) { | |
667 /* special case: name == stream:stream */ | |
668 /* id attrib of stream is stored for digest auth */ | |
2956 | 669 gjc->sid = g_strdup(xmlnode_get_attrib(x, "id")); |
2086 | 670 /* STATE_EVT(JCONN_STATE_AUTH) */ |
2635
8c75e59e4bdf
[gaim-migrate @ 2648]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2607
diff
changeset
|
671 xmlnode_free(x); |
2086 | 672 } else { |
2956 | 673 gjc->current = x; |
2086 | 674 } |
675 } | |
676 } | |
677 | |
678 static void endElement(void *userdata, const char *name) | |
679 { | |
2956 | 680 gjconn gjc = (gjconn) userdata; |
2086 | 681 xmlnode x; |
682 jpacket p; | |
683 | |
2956 | 684 if (gjc->current == NULL) { |
2086 | 685 /* we got </stream:stream> */ |
686 STATE_EVT(JCONN_STATE_OFF) | |
687 return; | |
688 } | |
689 | |
2956 | 690 x = xmlnode_get_parent(gjc->current); |
2086 | 691 |
692 if (!x) { | |
693 /* it is time to fire the event */ | |
2956 | 694 p = jpacket_new(gjc->current); |
695 | |
696 if (gjc->on_packet) | |
697 (gjc->on_packet) (gjc, p); | |
2086 | 698 else |
2956 | 699 xmlnode_free(gjc->current); |
2086 | 700 } |
701 | |
2956 | 702 gjc->current = x; |
2086 | 703 } |
704 | |
2090
b66aca8e8dce
[gaim-migrate @ 2100]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2086
diff
changeset
|
705 static void jabber_callback(gpointer data, gint source, GaimInputCondition condition) |
2086 | 706 { |
707 struct gaim_connection *gc = (struct gaim_connection *)data; | |
708 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; | |
709 | |
2956 | 710 gjab_recv(jd->gjc); |
2086 | 711 } |
712 | |
713 static void charData(void *userdata, const char *s, int slen) | |
714 { | |
2956 | 715 gjconn gjc = (gjconn) userdata; |
716 | |
717 if (gjc->current) | |
718 xmlnode_insert_cdata(gjc->current, s, slen); | |
2086 | 719 } |
720 | |
2090
b66aca8e8dce
[gaim-migrate @ 2100]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2086
diff
changeset
|
721 static void gjab_connected(gpointer data, gint source, GaimInputCondition cond) |
2086 | 722 { |
723 xmlnode x; | |
724 char *t, *t2; | |
725 struct gaim_connection *gc = data; | |
726 struct jabber_data *jd; | |
2956 | 727 gjconn gjc; |
2086 | 728 |
729 if (!g_slist_find(connections, gc)) { | |
730 close(source); | |
731 return; | |
732 } | |
733 | |
734 jd = gc->proto_data; | |
2956 | 735 gjc = jd->gjc; |
736 | |
737 if (gjc->fd != source) | |
738 gjc->fd = source; | |
2300
d2686f757d6e
[gaim-migrate @ 2310]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2289
diff
changeset
|
739 |
2086 | 740 if (source == -1) { |
741 STATE_EVT(JCONN_STATE_OFF) | |
742 return; | |
743 } | |
744 | |
2956 | 745 gjc->state = JCONN_STATE_CONNECTED; |
2086 | 746 STATE_EVT(JCONN_STATE_CONNECTED) |
747 | |
748 /* start stream */ | |
2956 | 749 x = jutil_header(NS_CLIENT, gjc->user->server); |
2086 | 750 t = xmlnode2str(x); |
751 /* this is ugly, we can create the string here instead of jutil_header */ | |
752 /* what do you think about it? -madcat */ | |
753 t2 = strstr(t, "/>"); | |
754 *t2++ = '>'; | |
755 *t2 = '\0'; | |
2956 | 756 gjab_send_raw(gjc, "<?xml version='1.0'?>"); |
757 gjab_send_raw(gjc, t); | |
2086 | 758 xmlnode_free(x); |
759 | |
2956 | 760 gjc->state = JCONN_STATE_ON; |
2086 | 761 STATE_EVT(JCONN_STATE_ON); |
762 | |
2956 | 763 gc = GJ_GC(gjc); |
764 gc->inpa = gaim_input_add(gjc->fd, GAIM_INPUT_READ, jabber_callback, gc); | |
2086 | 765 } |
766 | |
2956 | 767 static void gjab_start(gjconn gjc) |
2086 | 768 { |
769 struct aim_user *user; | |
770 int port; | |
771 | |
2956 | 772 if (!gjc || gjc->state != JCONN_STATE_OFF) |
2086 | 773 return; |
774 | |
2956 | 775 user = GJ_GC(gjc)->user; |
2086 | 776 port = user->proto_opt[USEROPT_PORT][0] ? atoi(user->proto_opt[USEROPT_PORT]) : DEFAULT_PORT; |
777 | |
2956 | 778 gjc->parser = XML_ParserCreate(NULL); |
779 XML_SetUserData(gjc->parser, (void *)gjc); | |
780 XML_SetElementHandler(gjc->parser, startElement, endElement); | |
781 XML_SetCharacterDataHandler(gjc->parser, charData); | |
782 | |
783 gjc->fd = proxy_connect(gjc->user->server, port, gjab_connected, GJ_GC(gjc)); | |
784 if (!user->gc || (gjc->fd < 0)) { | |
2086 | 785 STATE_EVT(JCONN_STATE_OFF) |
786 return; | |
787 } | |
788 } | |
789 | |
2956 | 790 /* |
791 * Find chat by chat group name | |
792 */ | |
2086 | 793 static struct conversation *find_chat(struct gaim_connection *gc, char *name) |
794 { | |
795 GSList *bcs = gc->buddy_chats; | |
796 struct conversation *b = NULL; | |
797 char *chat = g_strdup(normalize(name)); | |
798 | |
799 while (bcs) { | |
800 b = bcs->data; | |
801 if (!strcasecmp(normalize(b->name), chat)) | |
802 break; | |
803 b = NULL; | |
804 bcs = bcs->next; | |
805 } | |
806 | |
807 g_free(chat); | |
808 return b; | |
809 } | |
810 | |
2956 | 811 /* |
812 * Find chat by "chat id" | |
813 * | |
814 * Returns: 0 on success and jabber_chat pointer set | |
815 * or -EINVAL on error and jabber_chat pointer is | |
816 * undefined. | |
817 * | |
818 * TBD: Slogging through the buddy_chats list seems | |
819 * redundant since the chat i.d. is mirrored in the | |
820 * jabber_chat struct list. But that's the way it | |
821 * was, so that's the way I'm leaving it--for now. | |
822 */ | |
823 static int jabber_find_chat_by_convo_id(struct gaim_connection *gc, int id, struct jabber_chat **jc) | |
2086 | 824 { |
2956 | 825 GSList *bcs = gc->buddy_chats; |
826 struct conversation *b = NULL; | |
827 struct jabber_data *jd = gc->proto_data; | |
828 | |
829 *jc = NULL; | |
830 | |
831 while(bcs != NULL) { | |
832 b = bcs->data; | |
833 if (id == b->id) | |
834 break; | |
835 bcs = bcs->next; | |
836 } | |
837 | |
838 if (bcs != NULL) { | |
839 bcs = jd->chats; | |
840 while (bcs != NULL) { | |
841 *jc = bcs->data; | |
842 if ((*jc)->state == JCS_ACTIVE && (*jc)->b == b) | |
843 break; | |
844 bcs = bcs->next; | |
845 } | |
846 } | |
847 | |
848 return(bcs == NULL? -EINVAL : 0); | |
849 } | |
850 | |
851 /* | |
852 * Find any chat | |
853 */ | |
854 static struct jabber_chat *find_any_chat(struct gaim_connection *gc, jid chat) | |
855 { | |
856 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; | |
2086 | 857 struct jabber_chat *jc = NULL; |
858 | |
2956 | 859 while (jcs) { |
860 jc = jcs->data; | |
3311 | 861 if (!jid_cmpx(chat, jc->gjid, JID_USER | JID_SERVER)) |
2086 | 862 break; |
863 jc = NULL; | |
2956 | 864 jcs = jcs->next; |
2086 | 865 } |
866 | |
867 return jc; | |
868 } | |
869 | |
2956 | 870 |
871 /* | |
872 * Find existing/active Jabber chat | |
873 */ | |
874 static struct jabber_chat *find_existing_chat(struct gaim_connection *gc, jid chat) | |
875 { | |
876 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; | |
877 struct jabber_chat *jc = NULL; | |
878 | |
879 while (jcs) { | |
880 jc = jcs->data; | |
3311 | 881 if (jc->state == JCS_ACTIVE && !jid_cmpx(chat, jc->gjid, JID_USER | JID_SERVER)) |
2956 | 882 break; |
883 jc = NULL; | |
884 jcs = jcs->next; | |
885 } | |
886 | |
887 return jc; | |
888 } | |
889 | |
890 /* | |
891 * Find pending chat | |
892 */ | |
2086 | 893 static struct jabber_chat *find_pending_chat(struct gaim_connection *gc, jid chat) |
894 { | |
2956 | 895 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; |
2086 | 896 struct jabber_chat *jc = NULL; |
897 | |
2956 | 898 while (jcs) { |
899 jc = jcs->data; | |
3311 | 900 if (jc->state == JCS_PENDING && !jid_cmpx(chat, jc->gjid, JID_USER | JID_SERVER)) |
2086 | 901 break; |
902 jc = NULL; | |
2956 | 903 jcs = jcs->next; |
2086 | 904 } |
905 | |
906 return jc; | |
907 } | |
908 | |
909 static gboolean find_chat_buddy(struct conversation *b, char *name) | |
910 { | |
911 GList *m = b->in_room; | |
912 | |
913 while (m) { | |
914 if (!strcmp(m->data, name)) | |
915 return TRUE; | |
916 m = m->next; | |
917 } | |
918 | |
919 return FALSE; | |
920 } | |
921 | |
2956 | 922 /* |
3236 | 923 * Remove a buddy from the (gaim) buddylist (if he's on it) |
924 */ | |
3466 | 925 static void jabber_remove_gaim_buddy(struct gaim_connection *gc, const char *buddyname) |
3236 | 926 { |
927 struct buddy *b; | |
928 | |
929 if ((b = find_buddy(gc, buddyname)) != NULL) { | |
930 struct group *group; | |
931 | |
932 group = find_group_by_buddy(gc, buddyname); | |
933 debug_printf("removing buddy [1]: %s, from group: %s\n", buddyname, group->name); | |
934 remove_buddy(gc, group, b); | |
935 do_export(gc); | |
936 } | |
937 } | |
938 | |
3466 | 939 static void jabber_change_passwd(struct gaim_connection *gc, const char *old, const char *new) |
3257 | 940 { |
941 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; | |
942 | |
943 if(strcmp(old, gjc->pass)) | |
944 { | |
3427 | 945 do_error_dialog(_("Unable to change password."), |
946 _("The current password you entered is incorrect. Your password has " | |
947 "not been changed."), GAIM_ERROR); | |
3257 | 948 } |
949 else if(!strcmp(old, new)) | |
950 { | |
3427 | 951 do_error_dialog(_("Unable to change password"), |
952 _("The new password you entered is the same as your current passord. " | |
953 "Your password remains the same."), GAIM_ERROR); | |
3257 | 954 } |
955 else | |
956 { | |
957 xmlnode x, y, z; | |
958 char *id; | |
959 | |
960 x = jutil_iqnew(JPACKET__SET, NS_REGISTER); | |
961 xmlnode_put_attrib(x, "to", gjc->user->server); | |
962 y = xmlnode_get_tag(x, "query"); | |
963 z = xmlnode_insert_tag(y, "username"); | |
964 xmlnode_insert_cdata(z, gjc->user->user, -1); | |
965 z = xmlnode_insert_tag(y, "password"); | |
966 xmlnode_insert_cdata(z, new, -1); | |
967 | |
968 id = gjab_getid(gjc); | |
969 xmlnode_put_attrib(x, "id", id); | |
970 | |
971 free(gjc->pass); | |
972 gjc->pass = strdup(new); | |
973 | |
974 g_hash_table_insert(gjc->queries, g_strdup(id), g_strdup("change_password")); | |
975 | |
976 gjab_send(gjc, x); | |
977 xmlnode_free(x); | |
978 } | |
979 } | |
3311 | 980 |
3340 | 981 /* |
982 * Return pointer to jabber_buddy_data if buddy found. Create if necessary. | |
983 */ | |
3311 | 984 static struct jabber_buddy_data* jabber_find_buddy(struct gaim_connection *gc, char *buddy) |
985 { | |
986 struct jabber_data *jd = gc->proto_data; | |
987 gpointer val; | |
988 char *realwho; | |
989 | |
990 if((realwho = get_realwho(jd->gjc, buddy, FALSE, NULL)) == NULL) | |
991 return NULL; | |
992 | |
993 val = g_hash_table_lookup(jd->buddies, realwho); | |
994 if(val) { | |
995 g_free(realwho); | |
996 return (struct jabber_buddy_data *)val; | |
997 | |
998 } else { | |
999 struct jabber_buddy_data *jbd = g_new0(struct jabber_buddy_data, 1); | |
1000 jbd->error_msg = NULL; | |
1001 jbd->resources = NULL; | |
3340 | 1002 jbd->invisible = JABBER_NOT_INVIS; |
3311 | 1003 g_hash_table_insert(jd->buddies, g_strdup(realwho), jbd); |
1004 g_free(realwho); | |
1005 return jbd; | |
1006 } | |
1007 } | |
3257 | 1008 |
3236 | 1009 /* |
3311 | 1010 * find a resource by name, or if no name given, return the "default" resource |
1011 * default being the highest priority one. | |
1012 */ | |
1013 | |
1014 static jab_res_info jabber_find_resource(struct gaim_connection *gc, char *who) | |
1015 { | |
1016 GSList *resources; | |
1017 struct jabber_buddy_data *jbd = jabber_find_buddy(gc, who); | |
1018 jab_res_info jri = NULL; | |
1019 char *res = strstr(who, "/"); | |
1020 | |
1021 if(res) | |
1022 res++; | |
1023 | |
1024 if(jbd) | |
1025 { | |
1026 resources = jbd->resources; | |
1027 while(resources) | |
1028 { | |
1029 if(!jri && !res) { | |
1030 jri = (jab_res_info) resources->data; | |
1031 } else if(!res) { /* we're looking for the default priority, so... */ | |
1032 if(((jab_res_info) resources->data)->priority >= jri->priority) | |
1033 jri = (jab_res_info) resources->data; | |
3337 | 1034 } else if(((jab_res_info)resources->data)->name) { |
3311 | 1035 if(!strcasecmp(((jab_res_info) resources->data)->name, res)) { |
1036 jri = (jab_res_info) resources->data; | |
1037 break; | |
1038 } | |
1039 } | |
1040 resources = resources->next; | |
1041 } | |
1042 } | |
1043 | |
1044 return jri; | |
1045 } | |
1046 | |
1047 /* | |
1048 * if the resource doesn't exist, create it. otherwise, just update the priority | |
2956 | 1049 */ |
3311 | 1050 static void jabber_track_resource(struct gaim_connection *gc, |
1051 char *buddy, | |
1052 char *res, | |
1053 int priority, | |
1054 int state) | |
1055 { | |
1056 struct jabber_buddy_data *jbd = jabber_find_buddy(gc, buddy); | |
1057 | |
3337 | 1058 if(jbd) { |
1059 char *who; | |
1060 jab_res_info jri; | |
1061 if(res) | |
1062 who = g_strdup_printf("%s/%s", buddy, res); | |
1063 else | |
1064 who = g_strdup(buddy); | |
1065 jri = jabber_find_resource(gc, who); | |
3311 | 1066 g_free(who); |
1067 if(!jri) { | |
1068 jri = g_new0(struct jabber_resource_info, 1); | |
1069 jri->name = g_strdup(res); | |
1070 jri->away_msg = NULL; | |
1071 jbd->resources = g_slist_append(jbd->resources, jri); | |
1072 } | |
1073 jri->priority = priority; | |
1074 jri->state = state; | |
1075 } | |
1076 } | |
1077 | |
1078 /* | |
1079 * remove the resource, if it exists | |
1080 */ | |
1081 static void jabber_remove_resource(struct gaim_connection *gc, char *buddy, char *res) | |
2956 | 1082 { |
3311 | 1083 struct jabber_buddy_data *jbd = jabber_find_buddy(gc, buddy); |
3337 | 1084 if(jbd) { |
1085 char *who; | |
1086 jab_res_info jri; | |
1087 if(res) | |
1088 who = g_strdup_printf("%s/%s", buddy, res); | |
1089 else | |
1090 who = g_strdup(buddy); | |
1091 jri = jabber_find_resource(gc, who); | |
3311 | 1092 g_free(who); |
1093 if(jri) { | |
3337 | 1094 if(jri->name) |
1095 g_free(jri->name); | |
3311 | 1096 if(jri->away_msg) |
1097 g_free(jri->away_msg); | |
1098 jbd->resources = g_slist_remove(jbd->resources, jri); | |
1099 g_free(jri); | |
1100 } | |
1101 } | |
1102 } | |
1103 | |
1104 /* | |
1105 * grab the away message for the default resource | |
1106 */ | |
1107 static char *jabber_lookup_away(gjconn gjc, char *name) | |
1108 { | |
1109 jab_res_info jri = jabber_find_resource(GJ_GC(gjc), name); | |
1110 | |
1111 if(!jri || !jri->away_msg) | |
1112 return _("Unknown"); | |
1113 | |
1114 return jri->away_msg; | |
1115 } | |
1116 | |
1117 static void jabber_track_away(gjconn gjc, jpacket p, char *type) | |
1118 { | |
2956 | 1119 char *show; |
1120 char *vshow = NULL; | |
1121 char *status = NULL; | |
1122 char *msg = NULL; | |
3311 | 1123 jab_res_info jri = NULL; |
1124 | |
3337 | 1125 if(!p || !p->from || !p->from->user) |
3311 | 1126 return; |
1127 | |
1128 jri = jabber_find_resource(GJ_GC(gjc), jid_full(p->from)); | |
1129 | |
1130 if(!jri) | |
1131 return; | |
1132 | |
3105 | 1133 if (type && (strcasecmp(type, "unavailable") == 0)) { |
1134 vshow = _("Unavailable"); | |
1135 } else { | |
1136 if((show = xmlnode_get_tag_data(p->x, "show")) != NULL) { | |
1137 if (!strcasecmp(show, "away")) { | |
1138 vshow = _("Away"); | |
1139 } else if (!strcasecmp(show, "chat")) { | |
1140 vshow = _("Online"); | |
1141 } else if (!strcasecmp(show, "xa")) { | |
1142 vshow = _("Extended Away"); | |
1143 } else if (!strcasecmp(show, "dnd")) { | |
1144 vshow = _("Do Not Disturb"); | |
1145 } | |
2956 | 1146 } |
1147 } | |
1148 | |
3311 | 1149 status = g_strdup(xmlnode_get_tag_data(p->x, "status")); |
2956 | 1150 |
1151 if(vshow != NULL || status != NULL ) { | |
1152 /* kinda hokey, but it works :-) */ | |
1153 msg = g_strdup_printf("%s%s%s", | |
1154 (vshow == NULL? "" : vshow), | |
1155 (vshow == NULL || status == NULL? "" : ": "), | |
1156 (status == NULL? "" : status)); | |
3105 | 1157 } else { |
1158 msg = g_strdup(_("Online")); | |
2956 | 1159 } |
1160 | |
3259 | 1161 g_free(status); |
1162 | |
3311 | 1163 if(jri->away_msg) |
1164 g_free(jri->away_msg); | |
1165 | |
1166 jri->away_msg = msg; | |
1167 } | |
1168 | |
1169 static void jabber_convo_closed(struct gaim_connection *gc, char *name) | |
1170 { | |
1171 jab_res_info jri = jabber_find_resource(gc, name); | |
1172 | |
1173 if(jri) { | |
1174 if(jri->thread_id) | |
1175 g_free(jri->thread_id); | |
1176 | |
1177 jri->thread_id = NULL; | |
2956 | 1178 } |
1179 } | |
1180 | |
3311 | 1181 static void jabber_track_convo_thread(gjconn gjc, char *name, char *thread_id) |
1182 { | |
1183 jab_res_info jri = jabber_find_resource(GJ_GC(gjc), name); | |
1184 | |
1185 if(jri) { | |
1186 if(jri->thread_id) | |
1187 g_free(jri->thread_id); | |
1188 | |
1189 jri->thread_id = g_strdup(thread_id); | |
1190 } | |
1191 } | |
1192 | |
1193 static char *jabber_get_convo_thread(gjconn gjc, char *name) | |
1194 { | |
1195 char *ct = NULL; | |
1196 jab_res_info jri = jabber_find_resource(GJ_GC(gjc), name); | |
1197 | |
1198 if(jri) { | |
1199 if(jri->thread_id) | |
1200 ct = g_strdup(jri->thread_id); | |
1201 } | |
1202 | |
1203 return ct; | |
1204 } | |
1205 | |
1206 | |
3159 | 1207 static time_t iso8601_to_time(char *timestamp) |
1208 { | |
3229 | 1209 struct tm t; |
1210 time_t retval = 0; | |
1211 | |
3311 | 1212 if(sscanf(timestamp, "%04d%02d%02dT%02d:%02d:%02d", |
3229 | 1213 &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec)) |
1214 { | |
1215 t.tm_year -= 1900; | |
1216 t.tm_mon -= 1; | |
1217 t.tm_isdst = 0; | |
1218 retval = mktime(&t); | |
1219 # ifdef HAVE_TM_GMTOFF | |
1220 retval += t.tm_gmtoff; | |
1221 # else | |
1222 # ifdef HAVE_TIMEZONE | |
1223 tzset(); /* making sure */ | |
1224 retval -= timezone; | |
1225 # endif | |
1226 # endif | |
1227 } | |
1228 | |
1229 return retval; | |
3159 | 1230 } |
1231 | |
2956 | 1232 static void jabber_handlemessage(gjconn gjc, jpacket p) |
2086 | 1233 { |
3311 | 1234 xmlnode y, subj; |
3159 | 1235 time_t time_sent = time(NULL); |
3311 | 1236 gboolean typing = FALSE; |
2086 | 1237 |
1238 char *from = NULL, *msg = NULL, *type = NULL, *topic = NULL; | |
3311 | 1239 char *thread_id = NULL; |
1240 char *conference_room = NULL; | |
2086 | 1241 char m[BUF_LONG * 2]; |
1242 | |
1243 type = xmlnode_get_attrib(p->x, "type"); | |
3311 | 1244 |
1245 if ((y = xmlnode_get_tag(p->x, "thread"))) | |
1246 thread_id = xmlnode_get_data(y); | |
1247 | |
1248 y = xmlnode_get_firstchild(p->x); | |
1249 | |
1250 while(y) { | |
1251 if(NSCHECK(y, NS_DELAY)) { | |
1252 char *timestamp = xmlnode_get_attrib(y, "stamp"); | |
1253 time_sent = iso8601_to_time(timestamp); | |
1254 } else if(NSCHECK(y, "jabber:x:event")) { | |
1255 if(xmlnode_get_tag(y, "composing")) | |
1256 typing = TRUE; | |
1257 } else if(NSCHECK(y, "jabber:x:conference")) { | |
1258 conference_room = xmlnode_get_attrib(y, "jid"); | |
1259 } | |
1260 y = xmlnode_get_nextsibling(y); | |
3159 | 1261 } |
1262 | |
2086 | 1263 if (!type || !strcasecmp(type, "normal") || !strcasecmp(type, "chat")) { |
1264 | |
1265 from = jid_full(p->from); | |
1266 /* | |
1267 if ((y = xmlnode_get_tag(p->x, "html"))) { | |
1268 msg = xmlnode_get_data(y); | |
1269 } else | |
1270 */ | |
1271 if ((y = xmlnode_get_tag(p->x, "body"))) { | |
1272 msg = xmlnode_get_data(y); | |
1273 } | |
1274 | |
1275 msg = utf8_to_str(msg); | |
1276 | |
1277 if (!from) | |
1278 return; | |
1279 | |
3311 | 1280 if (conference_room) { |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
1281 GList *m = NULL; |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
1282 char **data; |
2086 | 1283 |
3311 | 1284 data = g_strsplit(conference_room, "@", 2); |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
1285 m = g_list_append(m, g_strdup(data[0])); |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
1286 m = g_list_append(m, g_strdup(data[1])); |
2956 | 1287 m = g_list_append(m, g_strdup(gjc->user->user)); |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
1288 g_strfreev(data); |
2086 | 1289 |
3311 | 1290 serv_got_chat_invite(GJ_GC(gjc), conference_room, from, msg, m); |
2086 | 1291 } else if (msg) { /* whisper */ |
1292 struct jabber_chat *jc; | |
1293 g_snprintf(m, sizeof(m), "%s", msg); | |
2956 | 1294 if (((jc = find_existing_chat(GJ_GC(gjc), p->from)) != NULL) && jc->b) |
3311 | 1295 serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 1, m, |
1296 time_sent); | |
2086 | 1297 else { |
2278
00a8b7bcef6c
[gaim-migrate @ 2288]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2232
diff
changeset
|
1298 int flags = 0; |
3311 | 1299 jab_res_info jri = jabber_find_resource(GJ_GC(gjc), jid_full(p->from)); |
1300 if(jri && typing) | |
1301 jri->has_composing = TRUE; | |
2278
00a8b7bcef6c
[gaim-migrate @ 2288]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2232
diff
changeset
|
1302 if (xmlnode_get_tag(p->x, "gaim")) |
00a8b7bcef6c
[gaim-migrate @ 2288]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2232
diff
changeset
|
1303 flags = IM_FLAG_GAIMUSER; |
3311 | 1304 if ((y = xmlnode_get_tag(p->x, "thread"))) |
1305 jabber_track_convo_thread(gjc, jid_full(p->from), | |
1306 xmlnode_get_data(y)); | |
2086 | 1307 if (find_conversation(jid_full(p->from))) |
3311 | 1308 serv_got_im(GJ_GC(gjc), jid_full(p->from), m, flags, |
1309 time_sent, -1); | |
2086 | 1310 else { |
2956 | 1311 if(p->from->user) { |
3311 | 1312 from = g_strdup_printf("%s@%s", p->from->user, |
1313 p->from->server); | |
2956 | 1314 } else { |
3311 | 1315 /* server message? */ |
1316 from = g_strdup(p->from->server); | |
2956 | 1317 } |
3159 | 1318 serv_got_im(GJ_GC(gjc), from, m, flags, time_sent, -1); |
2086 | 1319 g_free(from); |
1320 } | |
1321 } | |
3311 | 1322 } else { |
1323 /* a non-message message! */ | |
1324 from = g_strdup_printf("%s@%s", p->from->user, p->from->server); | |
1325 if(typing) | |
1326 serv_got_typing(GJ_GC(gjc), from, 0); | |
1327 else | |
1328 serv_got_typing_stopped(GJ_GC(gjc), from); | |
1329 g_free(from); | |
2086 | 1330 } |
1331 | |
1332 if (msg) | |
1333 g_free(msg); | |
1334 | |
1335 } else if (!strcasecmp(type, "error")) { | |
1336 if ((y = xmlnode_get_tag(p->x, "error"))) { | |
1337 type = xmlnode_get_attrib(y, "code"); | |
1338 msg = xmlnode_get_data(y); | |
1339 } | |
1340 | |
1341 if (msg) { | |
3427 | 1342 from = g_strdup_printf(_("Jabber Error %s"), type ? type : ""); |
1343 do_error_dialog(from, msg, GAIM_ERROR); | |
2086 | 1344 g_free(from); |
1345 } | |
1346 } else if (!strcasecmp(type, "groupchat")) { | |
1347 struct jabber_chat *jc; | |
1348 static int i = 0; | |
1349 | |
1350 /* | |
1351 if ((y = xmlnode_get_tag(p->x, "html"))) { | |
1352 msg = xmlnode_get_data(y); | |
1353 } else | |
1354 */ | |
1355 if ((y = xmlnode_get_tag(p->x, "body"))) { | |
1356 msg = xmlnode_get_data(y); | |
1357 } | |
1358 | |
1359 msg = utf8_to_str(msg); | |
1360 | |
1361 if ((subj = xmlnode_get_tag(p->x, "subject"))) { | |
1362 topic = xmlnode_get_data(subj); | |
1363 } | |
1364 topic = utf8_to_str(topic); | |
1365 | |
2956 | 1366 jc = find_existing_chat(GJ_GC(gjc), p->from); |
2086 | 1367 if (!jc) { |
1368 /* we're not in this chat. are we supposed to be? */ | |
2956 | 1369 if ((jc = find_pending_chat(GJ_GC(gjc), p->from)) != NULL) { |
2086 | 1370 /* yes, we're supposed to be. so now we are. */ |
2956 | 1371 jc->b = serv_got_joined_chat(GJ_GC(gjc), i++, p->from->user); |
2086 | 1372 jc->id = jc->b->id; |
2956 | 1373 jc->state = JCS_ACTIVE; |
2086 | 1374 } else { |
1375 /* no, we're not supposed to be. */ | |
1376 g_free(msg); | |
1377 return; | |
1378 } | |
1379 } | |
1380 if (p->from->resource) { | |
1381 if (!y) { | |
2956 | 1382 if (!find_chat_buddy(jc->b, p->from->resource)) { |
2086 | 1383 add_chat_buddy(jc->b, p->from->resource); |
2956 | 1384 } else if ((y = xmlnode_get_tag(p->x, "status"))) { |
3311 | 1385 jabber_track_away(gjc, p, NULL); |
2086 | 1386 } |
1387 } else if (jc->b && msg) { | |
1388 char buf[8192]; | |
1389 | |
1390 if (topic) { | |
1391 char tbuf[8192]; | |
1392 g_snprintf(tbuf, sizeof(tbuf), "%s", topic); | |
1393 chat_set_topic(jc->b, p->from->resource, tbuf); | |
1394 } | |
1395 | |
1396 g_snprintf(buf, sizeof(buf), "%s", msg); | |
3311 | 1397 serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 0, buf, |
1398 time_sent); | |
2086 | 1399 } |
1400 } else { /* message from the server */ | |
1401 if(jc->b && topic) { | |
1402 char tbuf[8192]; | |
1403 g_snprintf(tbuf, sizeof(tbuf), "%s", topic); | |
1404 chat_set_topic(jc->b, "", tbuf); | |
1405 } | |
1406 } | |
1407 | |
1408 g_free(msg); | |
1409 g_free(topic); | |
1410 | |
1411 } else { | |
1412 debug_printf("unhandled message %s\n", type); | |
1413 } | |
1414 } | |
3159 | 1415 |
2956 | 1416 static void jabber_handlepresence(gjconn gjc, jpacket p) |
2086 | 1417 { |
1418 char *to, *from, *type; | |
1419 struct buddy *b = NULL; | |
3311 | 1420 gaim_jid gjid; |
2086 | 1421 char *buddy; |
3194 | 1422 xmlnode y; |
2086 | 1423 char *show; |
2501
227cc42ffa6e
[gaim-migrate @ 2514]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2382
diff
changeset
|
1424 int state = 0; |
2086 | 1425 struct conversation *cnv = NULL; |
1426 struct jabber_chat *jc = NULL; | |
3311 | 1427 int priority = 0; |
1428 struct jabber_buddy_data *jbd; | |
1429 | |
2086 | 1430 to = xmlnode_get_attrib(p->x, "to"); |
1431 from = xmlnode_get_attrib(p->x, "from"); | |
1432 type = xmlnode_get_attrib(p->x, "type"); | |
3150 | 1433 |
3311 | 1434 if((buddy = get_realwho(gjc, from, FALSE, &gjid)) == NULL) |
1435 return; | |
1436 | |
1437 if (gjid->user == NULL) { | |
1438 /* FIXME: transport */ | |
1439 g_free(buddy); | |
1440 gaim_jid_free(gjid); | |
1441 return; | |
1442 } | |
1443 | |
1444 jbd = jabber_find_buddy(GJ_GC(gjc), buddy); | |
1445 | |
1446 if(jbd->error_msg) { | |
1447 g_free(jbd->error_msg); | |
1448 jbd->error_msg = NULL; | |
1449 } | |
1450 | |
3259 | 1451 if(type && !strcasecmp(type, "error")) { |
1452 state = UC_ERROR; | |
3311 | 1453 if((y = xmlnode_get_tag(p->x, "error")) != NULL) { |
1454 jbd->error_msg = g_strdup_printf(_("Error %s: %s"), | |
1455 xmlnode_get_attrib(y, "code"), xmlnode_get_data(y)); | |
1456 } else { | |
1457 jbd->error_msg = g_strdup(_("Unknown Error in presence")); | |
1458 } | |
3259 | 1459 } else { |
1460 if ((y = xmlnode_get_tag(p->x, "show"))) { | |
1461 show = xmlnode_get_data(y); | |
1462 if (!show) { | |
1463 state = 0; | |
1464 } else if (!strcasecmp(show, "away")) { | |
1465 state = UC_AWAY; | |
1466 } else if (!strcasecmp(show, "chat")) { | |
1467 state = UC_CHAT; | |
1468 } else if (!strcasecmp(show, "xa")) { | |
1469 state = UC_XA; | |
1470 } else if (!strcasecmp(show, "dnd")) { | |
1471 state = UC_DND; | |
1472 } | |
1473 } else { | |
2501
227cc42ffa6e
[gaim-migrate @ 2514]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2382
diff
changeset
|
1474 state = 0; |
2086 | 1475 } |
1476 } | |
1477 | |
3311 | 1478 if ((y = xmlnode_get_tag(p->x, "priority"))) |
1479 priority = atoi(xmlnode_get_data(y)); | |
2086 | 1480 |
1481 /* um. we're going to check if it's a chat. if it isn't, and there are pending | |
2956 | 1482 * chats, create the chat. if there aren't pending chats and we don't have the |
1483 * buddy on our list, simply bail out. */ | |
3311 | 1484 if ((cnv = find_chat(GJ_GC(gjc), gjid->user)) == NULL) { |
2086 | 1485 static int i = 0x70; |
3311 | 1486 if ((jc = find_pending_chat(GJ_GC(gjc), gjid)) != NULL) { |
1487 jc->b = cnv = serv_got_joined_chat(GJ_GC(gjc), i++, gjid->user); | |
2086 | 1488 jc->id = jc->b->id; |
2956 | 1489 jc->state = JCS_ACTIVE; |
1490 } else if ((b = find_buddy(GJ_GC(gjc), buddy)) == NULL) { | |
1491 g_free(buddy); | |
3311 | 1492 gaim_jid_free(gjid); |
2956 | 1493 return; |
2086 | 1494 } |
1495 } | |
1496 | |
3311 | 1497 if (type && (strcasecmp(type, "unavailable") == 0)) |
1498 jabber_remove_resource(GJ_GC(gjc), buddy, gjid->resource); | |
1499 else { | |
1500 jabber_track_resource(GJ_GC(gjc), buddy, gjid->resource, priority, state); | |
1501 | |
1502 /* keep track of away msg somewhat the same as the yahoo plugin */ | |
1503 jabber_track_away(gjc, p, type); | |
1504 } | |
1505 | |
1506 | |
2086 | 1507 if (!cnv) { |
3311 | 1508 /* this is where we handle presence information for "regular" buddies */ |
1509 jab_res_info jri = jabber_find_resource(GJ_GC(gjc), buddy); | |
1510 if(jri) { | |
1511 serv_got_update(GJ_GC(gjc), buddy, 1, 0, b->signon, b->idle, jri->state, 0); | |
1512 } else | |
1513 serv_got_update(GJ_GC(gjc), buddy, 0, 0, 0, 0, 0, 0); | |
1514 | |
2086 | 1515 } else { |
3311 | 1516 if (gjid->resource) { |
3259 | 1517 if (type && (!strcasecmp(type, "unavailable"))) { |
2086 | 1518 struct jabber_data *jd; |
3311 | 1519 if (!jc && !(jc = find_existing_chat(GJ_GC(gjc), gjid))) { |
2086 | 1520 g_free(buddy); |
3311 | 1521 gaim_jid_free(gjid); |
2086 | 1522 return; |
1523 } | |
1524 jd = jc->gc->proto_data; | |
2956 | 1525 /* if it's not ourselves...*/ |
3311 | 1526 if (strcmp(gjid->resource, jc->gjid->resource) && jc->b) { |
1527 remove_chat_buddy(jc->b, gjid->resource, NULL); | |
2086 | 1528 g_free(buddy); |
3311 | 1529 gaim_jid_free(gjid); |
2086 | 1530 return; |
1531 } | |
2956 | 1532 |
1533 jc->state = JCS_CLOSED; | |
1534 serv_got_chat_left(GJ_GC(gjc), jc->id); | |
1535 /* | |
1536 * TBD: put back some day? | |
1537 jd->chats = g_slist_remove(jd->chats, jc); | |
1538 g_free(jc); | |
1539 */ | |
1540 } else { | |
3311 | 1541 if ((!jc && !(jc = find_existing_chat(GJ_GC(gjc), gjid))) || !jc->b) { |
2956 | 1542 g_free(buddy); |
3311 | 1543 gaim_jid_free(gjid); |
2956 | 1544 return; |
1545 } | |
3311 | 1546 if (!find_chat_buddy(jc->b, gjid->resource)) { |
1547 add_chat_buddy(jc->b, gjid->resource); | |
2086 | 1548 } |
1549 } | |
1550 } | |
1551 } | |
1552 | |
1553 g_free(buddy); | |
3311 | 1554 gaim_jid_free(gjid); |
2086 | 1555 |
1556 return; | |
1557 } | |
1558 | |
3229 | 1559 /* |
1560 * Used only by Jabber accept/deny add stuff just below | |
1561 */ | |
1562 struct jabber_add_permit { | |
1563 gjconn gjc; | |
1564 gchar *user; | |
1565 }; | |
1566 | |
1567 /* | |
1568 * Common part for Jabber accept/deny adds | |
1569 * | |
1570 * "type" says whether we'll permit/deny the subscribe request | |
1571 */ | |
1572 static void jabber_accept_deny_add(struct jabber_add_permit *jap, const char *type) | |
1573 { | |
1574 xmlnode g = xmlnode_new_tag("presence"); | |
1575 | |
1576 xmlnode_put_attrib(g, "to", jap->user); | |
1577 xmlnode_put_attrib(g, "type", type); | |
1578 gjab_send(jap->gjc, g); | |
1579 | |
1580 xmlnode_free(g); | |
1581 } | |
1582 | |
1583 /* | |
1584 * Callback from "accept" in do_ask_dialog() invoked by jabber_handles10n() | |
1585 */ | |
1586 static void jabber_accept_add(gpointer w, struct jabber_add_permit *jap) | |
1587 { | |
1588 jabber_accept_deny_add(jap, "subscribed"); | |
1589 /* | |
1590 * If we don't already have the buddy on *our* buddylist, | |
1591 * ask if we want him or her added. | |
1592 */ | |
1593 if(find_buddy(GJ_GC(jap->gjc), jap->user) == NULL) { | |
1594 show_got_added(GJ_GC(jap->gjc), NULL, jap->user, NULL, NULL); | |
1595 } | |
1596 g_free(jap->user); | |
1597 g_free(jap); | |
1598 } | |
1599 | |
1600 /* | |
1601 * Callback from "deny/cancel" in do_ask_dialog() invoked by jabber_handles10n() | |
1602 */ | |
1603 static void jabber_deny_add(gpointer w, struct jabber_add_permit *jap) | |
1604 { | |
1605 jabber_accept_deny_add(jap, "unsubscribed"); | |
1606 g_free(jap->user); | |
1607 g_free(jap); | |
1608 } | |
1609 | |
1610 /* | |
1611 * Handle subscription requests | |
1612 */ | |
2956 | 1613 static void jabber_handles10n(gjconn gjc, jpacket p) |
2086 | 1614 { |
1615 xmlnode g; | |
1616 char *Jid = xmlnode_get_attrib(p->x, "from"); | |
3136 | 1617 char *type = xmlnode_get_attrib(p->x, "type"); |
2086 | 1618 |
1619 g = xmlnode_new_tag("presence"); | |
1620 xmlnode_put_attrib(g, "to", Jid); | |
3229 | 1621 |
1622 if (!strcmp(type, "subscribe")) { | |
1623 /* | |
1624 * A "subscribe to us" request was received - put up the approval dialog | |
1625 */ | |
1626 struct jabber_add_permit *jap = g_new0(struct jabber_add_permit, 1); | |
1627 gchar *msg = g_strdup_printf(_("The user %s wants to add you to their buddy list."), | |
1628 Jid); | |
1629 | |
1630 jap->gjc = gjc; | |
1631 jap->user = g_strdup(Jid); | |
1632 do_ask_dialog(msg, jap, jabber_accept_add, jabber_deny_add); | |
1633 | |
1634 g_free(msg); | |
1635 xmlnode_free(g); /* Never needed it here anyway */ | |
1636 return; | |
1637 | |
1638 } else if (!strcmp(type, "unsubscribe")) { | |
1639 /* | |
1640 * An "unsubscribe to us" was received - simply "approve" it | |
1641 */ | |
2086 | 1642 xmlnode_put_attrib(g, "type", "unsubscribed"); |
3229 | 1643 } else { |
1644 /* | |
1645 * Did we attempt to subscribe to somebody and they do not exist? | |
1646 */ | |
3136 | 1647 if (!strcmp(type, "unsubscribed")) { |
1648 xmlnode y; | |
1649 char *status; | |
1650 if((y = xmlnode_get_tag(p->x, "status")) && (status = xmlnode_get_data(y)) && | |
1651 !strcmp(status, "Not Found")) { | |
3427 | 1652 char *msg = g_strdup_printf(_("The Jabber user %s does not exist and was therefore " |
1653 "not added to your roster."), | |
1654 xmlnode_get_attrib(p->x, "from")); | |
1655 do_error_dialog(_("No such user."), msg, GAIM_ERROR ); | |
3136 | 1656 g_free(msg); |
1657 } | |
1658 } | |
1659 | |
2956 | 1660 xmlnode_free(g); |
2086 | 1661 return; |
2956 | 1662 } |
1663 | |
1664 gjab_send(gjc, g); | |
1665 xmlnode_free(g); | |
2086 | 1666 } |
1667 | |
2956 | 1668 /* |
1669 * Pending subscription to a buddy? | |
1670 */ | |
1671 #define BUD_SUB_TO_PEND(sub, ask) ((!strcasecmp((sub), "none") || !strcasecmp((sub), "from")) && \ | |
1672 (ask) != NULL && !strcasecmp((ask), "subscribe")) | |
1673 | |
1674 /* | |
1675 * Subscribed to a buddy? | |
1676 */ | |
1677 #define BUD_SUBD_TO(sub, ask) ((!strcasecmp((sub), "to") || !strcasecmp((sub), "both")) && \ | |
1678 ((ask) == NULL || !strcasecmp((ask), "subscribe"))) | |
1679 | |
1680 /* | |
1681 * Pending unsubscription to a buddy? | |
1682 */ | |
1683 #define BUD_USUB_TO_PEND(sub, ask) ((!strcasecmp((sub), "to") || !strcasecmp((sub), "both")) && \ | |
1684 (ask) != NULL && !strcasecmp((ask), "unsubscribe")) | |
1685 | |
1686 /* | |
1687 * Unsubscribed to a buddy? | |
1688 */ | |
1689 #define BUD_USUBD_TO(sub, ask) ((!strcasecmp((sub), "none") || !strcasecmp((sub), "from")) && \ | |
1690 ((ask) == NULL || !strcasecmp((ask), "unsubscribe"))) | |
1691 | |
1692 /* | |
1693 * If a buddy is added or removed from the roster on another resource | |
1694 * jabber_handlebuddy is called | |
1695 * | |
1696 * Called with roster item node. | |
1697 */ | |
1698 static void jabber_handlebuddy(gjconn gjc, xmlnode x) | |
1699 { | |
1700 xmlnode g; | |
3311 | 1701 char *who, *name, *sub, *ask; |
1702 gaim_jid gjid; | |
2956 | 1703 struct buddy *b = NULL; |
3136 | 1704 char *buddyname, *groupname = NULL; |
2956 | 1705 |
3311 | 1706 who = xmlnode_get_attrib(x, "jid"); |
2956 | 1707 name = xmlnode_get_attrib(x, "name"); |
1708 sub = xmlnode_get_attrib(x, "subscription"); | |
1709 ask = xmlnode_get_attrib(x, "ask"); | |
3311 | 1710 |
1711 if((buddyname = get_realwho(gjc, who, FALSE, &gjid)) == NULL) | |
1712 return; | |
2956 | 1713 |
1714 /* JFIXME: jabber_handleroster() had a "FIXME: transport" at this | |
1715 * equivilent point. So... | |
1716 * | |
3311 | 1717 * We haven't done anything interesting to this point, so we'll |
1718 * violate Good Coding Structure here by simply bailing out. | |
2956 | 1719 */ |
3311 | 1720 if(!gjid->user) { |
1721 g_free(buddyname); | |
1722 gaim_jid_free(gjid); | |
2956 | 1723 return; |
1724 } | |
3311 | 1725 gaim_jid_free(gjid); |
2956 | 1726 |
3236 | 1727 if((g = xmlnode_get_tag(x, "group")) != NULL) { |
3136 | 1728 groupname = xmlnode_get_data(g); |
2956 | 1729 } |
1730 | |
3059 | 1731 /* |
3136 | 1732 * Add or remove a buddy? Change buddy's alias or group? |
3059 | 1733 */ |
3328 | 1734 if(name) |
1735 name = utf8_to_str(name); | |
1736 | |
2956 | 1737 if (BUD_SUB_TO_PEND(sub, ask) || BUD_SUBD_TO(sub, ask)) { |
1738 if ((b = find_buddy(GJ_GC(gjc), buddyname)) == NULL) { | |
1739 debug_printf("adding buddy [4]: %s\n", buddyname); | |
3136 | 1740 b = add_buddy(GJ_GC(gjc), groupname ? groupname : _("Buddies"), buddyname, |
2956 | 1741 name ? name : buddyname); |
1742 do_export(GJ_GC(gjc)); | |
3136 | 1743 } else { |
1744 struct group *c_grp = find_group_by_buddy(GJ_GC(gjc), buddyname); | |
1745 | |
1746 /* | |
1747 * If the buddy's in a new group or his/her alias is changed... | |
1748 */ | |
1749 if(groupname && c_grp && strcmp(c_grp->name, groupname)) { | |
1750 int present = b->present; /* save presence state */ | |
1751 int uc = b->uc; /* and away state (?) */ | |
3150 | 1752 int idle = b->idle; |
1753 int signon = b->signon; | |
3136 | 1754 |
1755 /* | |
1756 * seems rude, but it seems to be the only way... | |
1757 */ | |
1758 remove_buddy(GJ_GC(gjc), c_grp, b); | |
1759 b = add_buddy(GJ_GC(gjc), groupname, buddyname, | |
1760 name ? name : buddyname); | |
1761 do_export(GJ_GC(gjc)); | |
1762 if(present) { | |
3311 | 1763 serv_got_update(GJ_GC(gjc), buddyname, 1, 0, signon, idle, |
1764 uc, 0); | |
3136 | 1765 } |
1766 } else if(name != NULL && strcmp(b->show, name)) { | |
1767 strncpy(b->show, name, BUDDY_ALIAS_MAXLEN); | |
1768 b->show[BUDDY_ALIAS_MAXLEN - 1] = '\0'; /* cheap safety feature */ | |
1769 handle_buddy_rename(b, buddyname); | |
1770 } | |
2956 | 1771 } |
1772 } else if (BUD_USUB_TO_PEND(sub, ask) || BUD_USUBD_TO(sub, ask) || !strcasecmp(sub, "remove")) { | |
3236 | 1773 jabber_remove_gaim_buddy(GJ_GC(gjc), buddyname); |
2956 | 1774 } |
3328 | 1775 |
1776 if(name) | |
1777 g_free(name); | |
1778 | |
2956 | 1779 g_free(buddyname); |
1780 | |
1781 } | |
1782 | |
1783 static void jabber_handleroster(gjconn gjc, xmlnode querynode) | |
2086 | 1784 { |
1785 xmlnode x; | |
1786 | |
1787 x = xmlnode_get_firstchild(querynode); | |
1788 while (x) { | |
2956 | 1789 jabber_handlebuddy(gjc, x); |
2086 | 1790 x = xmlnode_get_nextsibling(x); |
1791 } | |
1792 | |
1793 x = jutil_presnew(0, NULL, "Online"); | |
2956 | 1794 gjab_send(gjc, x); |
2086 | 1795 xmlnode_free(x); |
1796 } | |
1797 | |
2956 | 1798 static void jabber_handleauthresp(gjconn gjc, jpacket p) |
2086 | 1799 { |
1800 if (jpacket_subtype(p) == JPACKET__RESULT) { | |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
1801 if (xmlnode_has_children(p->x)) { |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
1802 xmlnode query = xmlnode_get_tag(p->x, "query"); |
2975 | 1803 set_login_progress(GJ_GC(gjc), 4, _("Authenticating")); |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
1804 if (!xmlnode_get_tag(query, "digest")) { |
2956 | 1805 g_free(gjc->sid); |
1806 gjc->sid = NULL; | |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
1807 } |
2956 | 1808 gjab_auth(gjc); |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
1809 } else { |
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
1810 debug_printf("auth success\n"); |
2086 | 1811 |
2956 | 1812 account_online(GJ_GC(gjc)); |
1813 serv_finish_login(GJ_GC(gjc)); | |
1814 | |
1815 if (bud_list_cache_exists(GJ_GC(gjc))) | |
1816 do_import(GJ_GC(gjc), NULL); | |
1817 | |
1818 ((struct jabber_data *)GJ_GC(gjc)->proto_data)->did_import = TRUE; | |
1819 | |
1820 gjab_reqroster(gjc); | |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
1821 } |
2086 | 1822 } else { |
1823 xmlnode xerr; | |
1824 char *errmsg = NULL; | |
1825 int errcode = 0; | |
2956 | 1826 struct jabber_data *jd = GJ_GC(gjc)->proto_data; |
2086 | 1827 |
1828 debug_printf("auth failed\n"); | |
1829 xerr = xmlnode_get_tag(p->x, "error"); | |
1830 if (xerr) { | |
1831 char msg[BUF_LONG]; | |
1832 errmsg = xmlnode_get_data(xerr); | |
1833 if (xmlnode_get_attrib(xerr, "code")) { | |
1834 errcode = atoi(xmlnode_get_attrib(xerr, "code")); | |
1835 g_snprintf(msg, sizeof(msg), "Error %d: %s", errcode, errmsg); | |
1836 } else | |
1837 g_snprintf(msg, sizeof(msg), "%s", errmsg); | |
2956 | 1838 hide_login_progress(GJ_GC(gjc), msg); |
2086 | 1839 } else { |
2975 | 1840 hide_login_progress(GJ_GC(gjc), _("Unknown login error")); |
2086 | 1841 } |
1842 | |
2800
0ad63a625eec
[gaim-migrate @ 2813]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2791
diff
changeset
|
1843 jd->die = TRUE; |
2086 | 1844 } |
1845 } | |
1846 | |
2956 | 1847 static void jabber_handleversion(gjconn gjc, xmlnode iqnode) { |
2086 | 1848 xmlnode querynode, x; |
1849 char *id, *from; | |
1850 char os[1024]; | |
1851 struct utsname osinfo; | |
1852 | |
1853 uname(&osinfo); | |
1854 g_snprintf(os, sizeof os, "%s %s %s", osinfo.sysname, osinfo.release, osinfo.machine); | |
1855 | |
1856 id = xmlnode_get_attrib(iqnode, "id"); | |
1857 from = xmlnode_get_attrib(iqnode, "from"); | |
1858 | |
1859 x = jutil_iqnew(JPACKET__RESULT, NS_VERSION); | |
1860 | |
1861 xmlnode_put_attrib(x, "to", from); | |
1862 xmlnode_put_attrib(x, "id", id); | |
1863 querynode = xmlnode_get_tag(x, "query"); | |
1864 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "name"), PACKAGE, -1); | |
1865 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "version"), VERSION, -1); | |
1866 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "os"), os, -1); | |
1867 | |
2956 | 1868 gjab_send(gjc, x); |
2086 | 1869 |
1870 xmlnode_free(x); | |
1871 } | |
1872 | |
2956 | 1873 static void jabber_handletime(gjconn gjc, xmlnode iqnode) { |
2086 | 1874 xmlnode querynode, x; |
1875 char *id, *from; | |
1876 time_t now_t; | |
1877 struct tm *now; | |
1878 char buf[1024]; | |
1879 | |
1880 time(&now_t); | |
1881 now = localtime(&now_t); | |
1882 | |
1883 id = xmlnode_get_attrib(iqnode, "id"); | |
1884 from = xmlnode_get_attrib(iqnode, "from"); | |
1885 | |
1886 x = jutil_iqnew(JPACKET__RESULT, NS_TIME); | |
1887 | |
1888 xmlnode_put_attrib(x, "to", from); | |
1889 xmlnode_put_attrib(x, "id", id); | |
1890 querynode = xmlnode_get_tag(x, "query"); | |
1891 | |
1892 strftime(buf, 1024, "%Y%m%dT%T", now); | |
1893 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "utc"), buf, -1); | |
1894 strftime(buf, 1024, "%Z", now); | |
1895 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "tz"), buf, -1); | |
1896 strftime(buf, 1024, "%d %b %Y %T", now); | |
1897 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "display"), buf, -1); | |
1898 | |
2956 | 1899 gjab_send(gjc, x); |
2086 | 1900 |
1901 xmlnode_free(x); | |
1902 } | |
1903 | |
2956 | 1904 static void jabber_handlelast(gjconn gjc, xmlnode iqnode) { |
2086 | 1905 xmlnode x, querytag; |
1906 char *id, *from; | |
2956 | 1907 struct jabber_data *jd = GJ_GC(gjc)->proto_data; |
2086 | 1908 char idle_time[32]; |
1909 | |
1910 id = xmlnode_get_attrib(iqnode, "id"); | |
1911 from = xmlnode_get_attrib(iqnode, "from"); | |
1912 | |
1913 x = jutil_iqnew(JPACKET__RESULT, "jabber:iq:last"); | |
1914 | |
1915 xmlnode_put_attrib(x, "to", from); | |
1916 xmlnode_put_attrib(x, "id", id); | |
1917 querytag = xmlnode_get_tag(x, "query"); | |
1918 g_snprintf(idle_time, sizeof idle_time, "%ld", jd->idle ? time(NULL) - jd->idle : 0); | |
1919 xmlnode_put_attrib(querytag, "seconds", idle_time); | |
1920 | |
2956 | 1921 gjab_send(gjc, x); |
2086 | 1922 xmlnode_free(x); |
1923 } | |
1924 | |
2956 | 1925 /* |
1926 * delete == TRUE: delete found entry | |
1927 * | |
1928 * returns pointer to (local) copy of value if found, NULL otherwise | |
1929 * | |
1930 * Note: non-reentrant! Local static storage re-used on subsequent calls. | |
1931 * If you're going to need to keep the returned value, make a copy! | |
1932 */ | |
1933 static gchar *jabber_track_queries(GHashTable *queries, gchar *key, gboolean delete) | |
1934 { | |
1935 gpointer my_key, my_val; | |
1936 static gchar *ret_val = NULL; | |
1937 | |
1938 if(ret_val != NULL) { | |
1939 g_free(ret_val); | |
1940 ret_val = NULL; | |
1941 } | |
1942 | |
1943 /* self-protection */ | |
1944 if(queries != NULL && key != NULL) { | |
1945 if(g_hash_table_lookup_extended(queries, key, &my_key, &my_val)) { | |
1946 ret_val = g_strdup((gchar *) my_val); | |
1947 if(delete) { | |
1948 g_hash_table_remove(queries, key); | |
1949 g_free(my_key); | |
1950 g_free(my_val); | |
1951 } | |
1952 } | |
1953 } | |
1954 | |
1955 return(ret_val); | |
1956 } | |
1957 | |
1958 static void jabber_handlepacket(gjconn gjc, jpacket p) | |
2086 | 1959 { |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
1960 char *id; |
2086 | 1961 switch (p->type) { |
1962 case JPACKET_MESSAGE: | |
2956 | 1963 jabber_handlemessage(gjc, p); |
2086 | 1964 break; |
1965 case JPACKET_PRESENCE: | |
2956 | 1966 jabber_handlepresence(gjc, p); |
2086 | 1967 break; |
1968 case JPACKET_IQ: | |
1969 debug_printf("jpacket_subtype: %d\n", jpacket_subtype(p)); | |
1970 | |
2956 | 1971 id = xmlnode_get_attrib(p->x, "id"); |
1972 if (id != NULL && !strcmp(id, IQID_AUTH)) { | |
1973 jabber_handleauthresp(gjc, p); | |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
1974 break; |
2086 | 1975 } |
1976 | |
1977 if (jpacket_subtype(p) == JPACKET__SET) { | |
2956 | 1978 xmlnode querynode; |
1979 querynode = xmlnode_get_tag(p->x, "query"); | |
1980 if (NSCHECK(querynode, "jabber:iq:roster")) { | |
1981 jabber_handlebuddy(gjc, xmlnode_get_firstchild(querynode)); | |
1982 } | |
2086 | 1983 } else if (jpacket_subtype(p) == JPACKET__GET) { |
1984 xmlnode querynode; | |
1985 querynode = xmlnode_get_tag(p->x, "query"); | |
2814
f4f9e5a01890
[gaim-migrate @ 2827]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2800
diff
changeset
|
1986 if (NSCHECK(querynode, NS_VERSION)) { |
2956 | 1987 jabber_handleversion(gjc, p->x); |
2086 | 1988 } else if (NSCHECK(querynode, NS_TIME)) { |
2956 | 1989 jabber_handletime(gjc, p->x); |
2086 | 1990 } else if (NSCHECK(querynode, "jabber:iq:last")) { |
2956 | 1991 jabber_handlelast(gjc, p->x); |
2086 | 1992 } |
1993 } else if (jpacket_subtype(p) == JPACKET__RESULT) { | |
1994 xmlnode querynode, vcard; | |
1995 char *xmlns, *from; | |
1996 | |
2956 | 1997 /* |
1998 * TBD: ISTM maybe this part could use a serious re-work? | |
1999 */ | |
2086 | 2000 from = xmlnode_get_attrib(p->x, "from"); |
2001 querynode = xmlnode_get_tag(p->x, "query"); | |
2002 vcard = xmlnode_get_tag(p->x, "vCard"); | |
2316
ebb5ecb2cd5b
[gaim-migrate @ 2326]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2309
diff
changeset
|
2003 if (!vcard) |
ebb5ecb2cd5b
[gaim-migrate @ 2326]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2309
diff
changeset
|
2004 vcard = xmlnode_get_tag(p->x, "VCARD"); |
2086 | 2005 |
2006 if (NSCHECK(querynode, NS_ROSTER)) { | |
2956 | 2007 jabber_handleroster(gjc, querynode); |
2086 | 2008 } else if (NSCHECK(querynode, NS_VCARD)) { |
2956 | 2009 jabber_track_queries(gjc->queries, id, TRUE); /* delete query track */ |
2010 jabber_handlevcard(gjc, querynode, from); | |
2316
ebb5ecb2cd5b
[gaim-migrate @ 2326]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2309
diff
changeset
|
2011 } else if (vcard) { |
2956 | 2012 jabber_track_queries(gjc->queries, id, TRUE); /* delete query track */ |
2013 jabber_handlevcard(gjc, vcard, from); | |
2014 } else if((xmlns = xmlnode_get_attrib(querynode, "xmlns")) != NULL) { | |
2015 debug_printf("jabber:iq:query: %s\n", xmlns); | |
2086 | 2016 } else { |
2956 | 2017 char *val; |
2018 | |
2019 debug_printf("jabber:iq: %s\n", xmlnode2str(p->x)); | |
2020 | |
2021 /* handle "null" query results */ | |
2022 if((val = jabber_track_queries(gjc->queries, id, TRUE)) != NULL) { | |
2023 if(strcmp((char *) val, "vCard") == 0) { | |
2024 /* | |
2025 * No actual vCard, but there's other stuff. This | |
2026 * way the user always gets some kind of response. | |
2027 */ | |
2028 jabber_handlevcard(gjc, NULL, from); | |
3257 | 2029 } else if(!strcmp((char *) val, "change_password")) { |
2030 char buf[BUF_LONG]; | |
3311 | 2031 sprintf(buf, _("Password successfully changed.")); |
3257 | 2032 |
3427 | 2033 do_error_dialog(buf, NULL, GAIM_INFO); |
2956 | 2034 } |
2035 } | |
2086 | 2036 } |
2037 | |
2038 } else if (jpacket_subtype(p) == JPACKET__ERROR) { | |
2039 xmlnode xerr; | |
2040 char *from, *errmsg = NULL; | |
2041 int errcode = 0; | |
2042 | |
2043 from = xmlnode_get_attrib(p->x, "from"); | |
2044 xerr = xmlnode_get_tag(p->x, "error"); | |
2045 if (xerr) { | |
2046 errmsg = xmlnode_get_data(xerr); | |
2047 if (xmlnode_get_attrib(xerr, "code")) | |
2048 errcode = atoi(xmlnode_get_attrib(xerr, "code")); | |
2049 } | |
2050 | |
3427 | 2051 from = g_strdup_printf("Jabber Error %d (%s)", errcode, from); |
2052 do_error_dialog(from, errmsg, GAIM_ERROR); | |
2086 | 2053 g_free(from); |
2054 | |
2055 } | |
2056 | |
2057 break; | |
2058 case JPACKET_S10N: | |
2956 | 2059 jabber_handles10n(gjc, p); |
2086 | 2060 break; |
2061 default: | |
2062 debug_printf("jabber: packet type %d (%s)\n", p->type, xmlnode2str(p->x)); | |
2063 } | |
2064 | |
2065 xmlnode_free(p->x); | |
2066 | |
2067 return; | |
2068 } | |
2069 | |
2956 | 2070 static void jabber_handlestate(gjconn gjc, int state) |
2086 | 2071 { |
2072 switch (state) { | |
2073 case JCONN_STATE_OFF: | |
3074 | 2074 if(gjc->was_connected) { |
2075 hide_login_progress_error(GJ_GC(gjc), _("Connection lost")); | |
2076 } else { | |
2077 hide_login_progress(GJ_GC(gjc), _("Unable to connect")); | |
2078 } | |
2956 | 2079 signoff(GJ_GC(gjc)); |
2086 | 2080 break; |
2081 case JCONN_STATE_CONNECTED: | |
3074 | 2082 gjc->was_connected = 1; |
2975 | 2083 set_login_progress(GJ_GC(gjc), 2, _("Connected")); |
2086 | 2084 break; |
2085 case JCONN_STATE_ON: | |
2975 | 2086 set_login_progress(GJ_GC(gjc), 3, _("Requesting Authentication Method")); |
2956 | 2087 gjab_reqauth(gjc); |
2086 | 2088 break; |
2089 default: | |
2090 debug_printf("state change: %d\n", state); | |
2091 } | |
2092 return; | |
2093 } | |
2094 | |
2095 static void jabber_login(struct aim_user *user) | |
2096 { | |
2097 struct gaim_connection *gc = new_gaim_conn(user); | |
2098 struct jabber_data *jd = gc->proto_data = g_new0(struct jabber_data, 1); | |
2099 char *loginname = create_valid_jid(user->username, DEFAULT_SERVER, "GAIM"); | |
2100 | |
3311 | 2101 jd->buddies = g_hash_table_new(g_str_hash, g_str_equal); |
2956 | 2102 jd->chats = NULL; /* we have no chats yet */ |
2086 | 2103 |
2975 | 2104 set_login_progress(gc, 1, _("Connecting")); |
2086 | 2105 |
2956 | 2106 if (!(jd->gjc = gjab_new(loginname, user->password, gc))) { |
2086 | 2107 g_free(loginname); |
2108 debug_printf("jabber: unable to connect (jab_new failed)\n"); | |
2975 | 2109 hide_login_progress(gc, _("Unable to connect")); |
2086 | 2110 signoff(gc); |
2111 return; | |
2112 } | |
2113 | |
2114 g_free(loginname); | |
2956 | 2115 gjab_state_handler(jd->gjc, jabber_handlestate); |
2116 gjab_packet_handler(jd->gjc, jabber_handlepacket); | |
2117 jd->gjc->queries = g_hash_table_new(g_str_hash, g_str_equal); | |
2118 gjab_start(jd->gjc); | |
2086 | 2119 } |
2120 | |
2121 static gboolean jabber_destroy_hash(gpointer key, gpointer val, gpointer data) { | |
2122 g_free(key); | |
2123 g_free(val); | |
2124 return TRUE; | |
2125 } | |
2126 | |
3311 | 2127 static gboolean jabber_destroy_buddy_hash(gpointer key, gpointer val, gpointer data) { |
2128 struct jabber_buddy_data *jbd = val; | |
2129 while (jbd->resources) { | |
2130 g_free(((jab_res_info) ((GSList *)jbd->resources)->data)->name); | |
2131 if(((jab_res_info) ((GSList *)jbd->resources)->data)->away_msg) | |
2132 g_free(((jab_res_info) ((GSList *)jbd->resources)->data)->away_msg); | |
2133 g_free(((GSList *)jbd->resources)->data); | |
2134 jbd->resources = g_slist_remove(jbd->resources, ((GSList *)jbd->resources)->data); | |
2135 | |
2136 } | |
2137 if(jbd->error_msg) | |
2138 g_free(jbd->error_msg); | |
2139 g_free(key); | |
2140 g_free(jbd); | |
2141 return TRUE; | |
2142 } | |
2143 | |
2144 | |
2086 | 2145 static gboolean jabber_free(gpointer data) |
2146 { | |
2956 | 2147 struct jabber_data *jd = data; |
2148 | |
3236 | 2149 if(jd->gjc != NULL) { |
3486 | 2150 g_free(jd->gjc->sid); |
3236 | 2151 gjab_delete(jd->gjc); |
2152 jd->gjc = NULL; | |
2153 } | |
2956 | 2154 g_free(jd); |
2155 | |
2086 | 2156 return FALSE; |
2157 } | |
2158 | |
2159 static void jabber_close(struct gaim_connection *gc) | |
2160 { | |
2161 struct jabber_data *jd = gc->proto_data; | |
2956 | 2162 |
2163 if(jd) { | |
2164 GSList *jcs = jd->chats; | |
2165 | |
2166 /* Free-up the jabber_chat struct allocs and the list */ | |
2167 while (jcs) { | |
3311 | 2168 gaim_jid_free(((struct jabber_chat *)jcs->data)->gjid); |
2956 | 2169 g_free(jcs->data); |
2170 jcs = jcs->next; | |
2171 } | |
2172 g_slist_free(jd->chats); | |
2173 | |
3311 | 2174 /* Free-up the buddy data hash */ |
2175 if(jd->buddies != NULL) | |
2176 { | |
2177 g_hash_table_foreach_remove(jd->buddies, jabber_destroy_buddy_hash, NULL); | |
2178 g_hash_table_destroy(jd->buddies); | |
2179 jd->buddies = NULL; | |
2956 | 2180 } |
2181 | |
2182 /* Free-up the pending queries memories and the list */ | |
3236 | 2183 if(jd->gjc != NULL && jd->gjc->queries != NULL) { |
2956 | 2184 g_hash_table_foreach_remove(jd->gjc->queries, jabber_destroy_hash, NULL); |
2185 g_hash_table_destroy(jd->gjc->queries); | |
2186 jd->gjc->queries = NULL; | |
2187 } | |
2188 } | |
2300
d2686f757d6e
[gaim-migrate @ 2310]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2289
diff
changeset
|
2189 if (gc->inpa) |
d2686f757d6e
[gaim-migrate @ 2310]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2289
diff
changeset
|
2190 gaim_input_remove(gc->inpa); |
2956 | 2191 |
2192 if(jd) { | |
2193 g_timeout_add(50, jabber_free, jd); | |
3236 | 2194 if(jd->gjc != NULL) |
2195 xmlnode_free(jd->gjc->current); | |
2956 | 2196 } |
2086 | 2197 gc->proto_data = NULL; |
2198 } | |
2199 | |
3311 | 2200 static int jabber_send_typing(struct gaim_connection *gc, char *who, int typing) |
2201 { | |
2202 xmlnode x, y; | |
2203 char *realwho; | |
2204 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; | |
2205 jab_res_info jri = jabber_find_resource(gc, who); | |
2206 | |
2207 if(!jri || !jri->has_composing) | |
2208 return 0; | |
2209 | |
2210 if((realwho = get_realwho(gjc, who, FALSE, NULL)) == NULL) | |
2211 return 0; | |
2212 | |
2213 x = xmlnode_new_tag("message"); | |
2214 xmlnode_put_attrib(x, "to", realwho); | |
2215 xmlnode_insert_tag(x, "gaim"); | |
2216 | |
2217 y = xmlnode_insert_tag(x, "x"); | |
2218 xmlnode_put_attrib(y, "xmlns", "jabber:x:event"); | |
2219 | |
2220 if(typing) | |
2221 xmlnode_insert_tag(y, "composing"); | |
2222 | |
2223 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); | |
2224 xmlnode_free(x); | |
2225 g_free(realwho); | |
2226 return JABBER_TYPING_NOTIFY_INT; | |
2227 } | |
2228 | |
3033 | 2229 static int jabber_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags) |
2086 | 2230 { |
2231 xmlnode x, y; | |
2232 char *realwho; | |
3311 | 2233 char *thread_id = NULL; |
2956 | 2234 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
2086 | 2235 |
2236 if (!who || !message) | |
2123
56c4382f2909
[gaim-migrate @ 2133]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2112
diff
changeset
|
2237 return 0; |
2086 | 2238 |
3311 | 2239 if((realwho = get_realwho(gjc, who, FALSE, NULL)) == NULL) |
2240 return 0; | |
2241 | |
2086 | 2242 x = xmlnode_new_tag("message"); |
2243 xmlnode_put_attrib(x, "to", realwho); | |
3311 | 2244 |
2245 thread_id = jabber_get_convo_thread(gjc, realwho); | |
2246 if(thread_id) | |
2247 { | |
2248 y = xmlnode_insert_tag(x, "thread"); | |
2249 xmlnode_insert_cdata(y, thread_id, -1); | |
2250 g_free(thread_id); | |
2251 } | |
2252 | |
2086 | 2253 g_free(realwho); |
2254 | |
2278
00a8b7bcef6c
[gaim-migrate @ 2288]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2232
diff
changeset
|
2255 xmlnode_insert_tag(x, "gaim"); |
2086 | 2256 xmlnode_put_attrib(x, "type", "chat"); |
2257 | |
3311 | 2258 /* let other clients know we support typing notification */ |
2259 y = xmlnode_insert_tag(x, "x"); | |
2260 xmlnode_put_attrib(y, "xmlns", "jabber:x:event"); | |
2261 xmlnode_insert_tag(y, "composing"); | |
2262 | |
2086 | 2263 if (message && strlen(message)) { |
2264 char *utf8 = str_to_utf8(message); | |
2265 y = xmlnode_insert_tag(x, "body"); | |
2266 xmlnode_insert_cdata(y, utf8, -1); | |
2267 g_free(utf8); | |
2268 } | |
2269 | |
2956 | 2270 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); |
2086 | 2271 xmlnode_free(x); |
2303
f5bf315e6104
[gaim-migrate @ 2313]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2300
diff
changeset
|
2272 return 1; |
2086 | 2273 } |
2274 | |
3105 | 2275 /* |
2276 * Add/update buddy's roster entry on server | |
3349 | 2277 * |
2278 * If "alias" or "group" are NULL, gets them from Gaim's current buddylist values | |
2279 * for the buddy. | |
3105 | 2280 */ |
3349 | 2281 static void jabber_roster_update(struct gaim_connection *gc, char *name, char *alias, char *group) |
3105 | 2282 { |
2283 xmlnode x, y; | |
2284 char *realwho; | |
2285 gjconn gjc; | |
2286 struct buddy *buddy = NULL; | |
2287 struct group *buddy_group = NULL; | |
3349 | 2288 char *my_alias = NULL; |
2289 char *my_group = NULL; | |
3105 | 2290 |
2291 if(gc && gc->proto_data && ((struct jabber_data *)gc->proto_data)->gjc && name) { | |
3311 | 2292 gaim_jid gjid; |
3105 | 2293 gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
2294 | |
3311 | 2295 if((realwho = get_realwho(gjc, name, FALSE, &gjid)) == NULL) |
2296 return; | |
2297 | |
2298 /* FIXME: transport */ | |
2299 if(gjid->user == NULL) { | |
2300 g_free(realwho); | |
2301 gaim_jid_free(gjid); | |
2302 return; | |
3105 | 2303 } |
3311 | 2304 gaim_jid_free(gjid); |
3105 | 2305 |
2306 x = jutil_iqnew(JPACKET__SET, NS_ROSTER); | |
2307 y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item"); | |
2308 xmlnode_put_attrib(y, "jid", realwho); | |
2309 | |
3349 | 2310 /* |
2311 * See if there's an explict (new?) alias for the buddy or we can pull | |
2312 * one out of current Gaim buddylist data for him. | |
2313 */ | |
2314 if(alias && alias[0] != '\0') { | |
2315 my_alias = alias; | |
2316 } else if((buddy = find_buddy(gc, realwho)) != NULL) { | |
2317 my_alias = buddy->show; | |
2318 } | |
2319 | |
2320 /* If there's an alias for the buddy, it's not 0-length | |
3105 | 2321 * and it doesn't match his JID, add the "name" attribute. |
2322 */ | |
3349 | 2323 if(my_alias != NULL && my_alias[0] != '\0' && strcmp(realwho, my_alias)) |
3311 | 2324 { |
3349 | 2325 char *utf8 = str_to_utf8(my_alias); |
3311 | 2326 xmlnode_put_attrib(y, "name", utf8); |
2327 g_free(utf8); | |
3105 | 2328 } |
2329 | |
2330 /* | |
3349 | 2331 * See if there's an explict (new?) group for the buddy or pull |
2332 * one out of current Gaim buddylist data for him. | |
3105 | 2333 */ |
3349 | 2334 if(group && group[0] != '\0') { |
2335 my_group = group; | |
2336 } else if((buddy_group = find_group_by_buddy(gc, realwho)) != NULL) { | |
2337 my_group = buddy_group->name; | |
2338 } | |
2339 | |
2340 /* | |
2341 * Send what group the buddy's in along with the roster item. | |
2342 */ | |
2343 if(my_group != NULL && my_group[0] != '\0') { | |
2344 xmlnode z = xmlnode_insert_tag(y, "group"); | |
2345 xmlnode_insert_cdata(z, my_group, -1); | |
3105 | 2346 } |
2347 | |
2348 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); | |
2349 | |
2350 xmlnode_free(x); | |
2351 g_free(realwho); | |
2352 } | |
2353 } | |
2354 | |
3136 | 2355 /* |
3349 | 2356 * Add/update buddy's alias on server |
2357 * | |
2358 * This is just a roster update using existing, local buddylist data | |
2359 */ | |
2360 static void jabber_alias_buddy(struct gaim_connection *gc, char *name) | |
2361 { | |
2362 jabber_roster_update(gc, name, NULL, NULL); | |
2363 } | |
2364 | |
2365 /* | |
3136 | 2366 * Change buddy's group on server roster |
2367 */ | |
2368 static void jabber_group_change(struct gaim_connection *gc, char *name, char *old_group, char *new_group) | |
2369 { | |
3349 | 2370 if(old_group && new_group && strcmp(old_group, new_group)) |
2371 jabber_roster_update(gc, name, NULL, new_group); | |
2372 } | |
2373 | |
2374 /* | |
2375 * Group rename | |
2376 * | |
2377 * Jabber doesn't have "groups," per se. "Group" is simply a JID attribute. | |
2378 * So we iterate through the list of buddies that are in the group and change | |
2379 * the group attribute for each of them. | |
2380 */ | |
2381 static void jabber_rename_group(struct gaim_connection *gc, | |
2382 char *old_group, | |
2383 char *new_group, | |
2384 GList *members) | |
2385 { | |
2386 if(old_group && new_group && strcmp(old_group, new_group)) | |
2387 while(members) { | |
2388 jabber_group_change(gc, (char *)(members->data), old_group, new_group); | |
2389 members = members->next; | |
2390 } | |
3136 | 2391 } |
2392 | |
3466 | 2393 static void jabber_add_buddy(struct gaim_connection *gc, const char *name) |
2086 | 2394 { |
3136 | 2395 xmlnode x; |
2086 | 2396 char *realwho; |
2956 | 2397 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
3311 | 2398 gaim_jid gjid; |
2086 | 2399 |
2400 if (!((struct jabber_data *)gc->proto_data)->did_import) | |
2401 return; | |
2402 | |
3311 | 2403 /* |
2404 * If there's no name or the name is ourself | |
2405 */ | |
2406 if(!name || !strcmp(gc->username, name)) | |
2086 | 2407 return; |
2408 | |
3311 | 2409 if((realwho = get_realwho(gjc, name, FALSE, &gjid)) == NULL) { |
3427 | 2410 char *msg = g_strdup_printf(_("The user %s is an invalid Jabber I.D. and was " |
2411 "therefore not added."), name); | |
2412 do_error_dialog("Unable to add buddy.", _("Jabber Error"), GAIM_ERROR); | |
3311 | 2413 g_free(msg); |
2414 jabber_remove_gaim_buddy(gc, name); | |
2415 return; | |
2086 | 2416 } |
2417 | |
3311 | 2418 /* FIXME: transport */ |
2419 if(gjid->user == NULL) { | |
2420 g_free(realwho); | |
2421 gaim_jid_free(gjid); | |
2422 return; | |
2423 } | |
2424 gaim_jid_free(gjid); | |
2425 | |
2086 | 2426 x = xmlnode_new_tag("presence"); |
2427 xmlnode_put_attrib(x, "to", realwho); | |
2428 xmlnode_put_attrib(x, "type", "subscribe"); | |
2956 | 2429 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); |
2430 xmlnode_free(x); | |
2086 | 2431 |
3349 | 2432 jabber_roster_update(gc, realwho, NULL, NULL); |
3105 | 2433 |
2086 | 2434 g_free(realwho); |
2435 } | |
2436 | |
2681
37d80035e77f
[gaim-migrate @ 2694]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2635
diff
changeset
|
2437 static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group) |
2086 | 2438 { |
3048 | 2439 xmlnode x; |
2086 | 2440 char *realwho; |
2956 | 2441 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
2086 | 2442 |
3311 | 2443 if(!name || (realwho = get_realwho(gjc, name, FALSE, NULL)) == NULL) |
2086 | 2444 return; |
2445 | |
2956 | 2446 x = xmlnode_new_tag("presence"); |
2447 xmlnode_put_attrib(x, "to", realwho); | |
2448 xmlnode_put_attrib(x, "type", "unsubscribe"); | |
2449 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); | |
2086 | 2450 g_free(realwho); |
2451 xmlnode_free(x); | |
2452 } | |
2453 | |
3314 | 2454 /* |
2455 * Remove a buddy item from the roster entirely | |
2456 */ | |
2457 static void jabber_remove_buddy_roster_item(struct gaim_connection *gc, char *name) | |
2458 { | |
3340 | 2459 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
3314 | 2460 char *realwho; |
3340 | 2461 |
2462 if((realwho = get_realwho(gjc, name, FALSE, NULL)) != NULL) { | |
2463 xmlnode x = jutil_iqnew(JPACKET__SET, NS_ROSTER); | |
2464 xmlnode y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item"); | |
2465 xmlnode_put_attrib(y, "jid", realwho); | |
2466 xmlnode_put_attrib(y, "subscription", "remove"); | |
2467 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); | |
2468 g_free(realwho); | |
2469 xmlnode_free(x); | |
2470 } | |
2471 } | |
2472 | |
2473 /* | |
2474 * Unsubscribe a buddy from our presence | |
2475 */ | |
2476 static void jabber_unsubscribe_buddy_from_us(struct gaim_connection *gc, char *name) | |
2477 { | |
2478 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; | |
2479 char *realwho; | |
2480 | |
2481 if((realwho = get_realwho(gjc, name, FALSE, NULL)) != NULL) { | |
2482 xmlnode g = xmlnode_new_tag("presence"); | |
2483 xmlnode_put_attrib(g, "to", realwho); | |
2484 xmlnode_put_attrib(g, "type", "unsubscribed"); | |
2485 gjab_send(gjc, g); | |
2486 xmlnode_free(g); | |
2487 } | |
2488 } | |
2489 | |
2490 /* | |
2491 * Common code for setting ourselves invisible/visible to buddy | |
2492 */ | |
2493 static void jabber_invisible_to_buddy_common(struct gaim_connection *gc, char *name, gboolean invisible) | |
2494 { | |
3314 | 2495 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
3340 | 2496 char *realwho; |
2497 | |
2498 if((realwho = get_realwho(gjc, name, FALSE, NULL)) != NULL) { | |
2499 struct jabber_buddy_data *jbd = jabber_find_buddy(gc, realwho); | |
2500 xmlnode g = xmlnode_new_tag("presence"); | |
2501 | |
2502 xmlnode_put_attrib(g, "to", realwho); | |
2503 | |
2504 if(invisible) | |
2505 xmlnode_put_attrib(g, "type", "invisible"); | |
2506 | |
2507 gjab_send(gjc, g); | |
2508 | |
2509 g_free(realwho); | |
2510 xmlnode_free(g); | |
2511 | |
2512 if(jbd) { | |
2513 if(invisible) { | |
2514 jbd->invisible |= JABBER_BUD_INVIS; | |
2515 } else { | |
2516 jbd->invisible &= ~JABBER_BUD_INVIS; | |
2517 } | |
2518 } | |
2519 } | |
2520 } | |
2521 | |
2522 /* | |
2523 * Make ourselves temporarily invisible to a buddy | |
2524 */ | |
2525 static void jabber_invisible_to_buddy(struct gaim_connection *gc, char *name) | |
2526 { | |
2527 jabber_invisible_to_buddy_common(gc, name, TRUE); | |
2528 } | |
2529 | |
2530 /* | |
2531 * Make ourselves visible to a buddy | |
2532 */ | |
2533 static void jabber_visible_to_buddy(struct gaim_connection *gc, char *name) | |
2534 { | |
2535 jabber_invisible_to_buddy_common(gc, name, FALSE); | |
2536 } | |
2537 | |
2538 /* | |
2539 * Function used by the g_hash_table_foreach() in invisible_to_all_buddies() to | |
2540 * actually set the status. | |
2541 * | |
2542 * key is unused | |
2543 * value is the pointer to the jabber_buddy_data struct | |
2544 * data is gboolean: TRUE (invisible) or FALSE (not invisible) | |
2545 */ | |
2546 static void set_invisible_to_buddy_status(gpointer key, gpointer val, gpointer data) { | |
2547 struct jabber_buddy_data *jbd = val; | |
2548 gboolean invisible = (gboolean) data; | |
2549 | |
2550 if(jbd) { | |
2551 if(invisible) { | |
2552 jbd->invisible = JABBER_SERV_INVIS | JABBER_BUD_INVIS; | |
2553 } else { | |
2554 /* | |
2555 * If we've asserted server-level invisibility, cancelling | |
2556 * it removes explicit buddy invisibility settings too. | |
2557 */ | |
2558 if(jbd->invisible & JABBER_SERV_INVIS) | |
2559 jbd->invisible = JABBER_NOT_INVIS; | |
2560 } | |
2561 } | |
2562 } | |
2563 | |
2564 /* | |
2565 * Show we've set ourselves invisible/visible to all buddies on the server | |
2566 * | |
2567 * Used when we set server-wide invisibility so that individual buddy menu | |
2568 * entries show the proper option. | |
2569 */ | |
2570 static void invisible_to_all_buddies(struct gaim_connection *gc, gboolean invisible) | |
2571 { | |
2572 struct jabber_data *jd = gc->proto_data; | |
2573 | |
2574 if(jd->buddies != NULL) | |
2575 g_hash_table_foreach(jd->buddies, set_invisible_to_buddy_status, (gpointer) invisible); | |
3314 | 2576 } |
2577 | |
2086 | 2578 static char **jabber_list_icon(int uc) |
2579 { | |
2580 switch (uc) { | |
2581 case UC_AWAY: | |
2582 return available_away_xpm; | |
2583 case UC_CHAT: | |
2584 return available_chat_xpm; | |
2585 case UC_XA: | |
2586 return available_xa_xpm; | |
2587 case UC_DND: | |
2588 return available_dnd_xpm; | |
3259 | 2589 case UC_ERROR: |
2590 return available_error_xpm; | |
2086 | 2591 default: |
2592 return available_xpm; | |
2593 } | |
2594 } | |
2595 | |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2596 static GList *jabber_chat_info(struct gaim_connection *gc) |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2597 { |
2956 | 2598 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
2599 | |
2600 static char *confserv = NULL; /* this pointer must be persistent */ | |
2601 gchar *server; | |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2602 |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2603 GList *m = NULL; |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2604 struct proto_chat_entry *pce; |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2605 |
2956 | 2606 /* This is a scientific wild-ass guess... |
2607 * | |
2608 * If there are more than two "components" to the current server name, | |
2609 * lop-off the left-most component and replace with "conference." | |
2610 */ | |
2611 if(confserv != NULL) { | |
2612 g_free(confserv); /* dispose of the old value */ | |
2613 } | |
2614 | |
2615 if((server = g_strdup(gjc->user->server)) == NULL) { | |
2616 confserv = g_strdup(DEFAULT_GROUPCHAT); | |
2617 } else { | |
2618 gchar **splits, **index; | |
2619 gchar *tmp; | |
2620 int cnt = 0; | |
2621 | |
2622 | |
2623 index = splits = g_strsplit(server, ".", -1); /* split the connected server */ | |
2624 | |
2625 while(*(index++)) /* index to the end--counting the parts */ | |
2626 ++cnt; | |
2627 | |
2628 /* | |
2629 * If we've more than two parts, point to the second part. Else point | |
2630 * to the start. | |
2631 */ | |
2632 if(cnt > 2) { | |
2633 index -= cnt; | |
2634 } else { | |
2635 index = splits; | |
2636 } | |
2637 | |
2638 /* Put it together */ | |
2639 confserv = g_strjoin(".", "conference", (tmp = g_strjoinv(".", index)), NULL); | |
2640 | |
2641 g_free(server); /* we don't need this stuff no more */ | |
2642 g_free(tmp); | |
2643 g_strfreev(splits); | |
2644 } | |
2645 | |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2646 pce = g_new0(struct proto_chat_entry, 1); |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2647 pce->label = _("Room:"); |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2648 m = g_list_append(m, pce); |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2649 |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2650 pce = g_new0(struct proto_chat_entry, 1); |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2651 pce->label = _("Server:"); |
2956 | 2652 pce->def = confserv; |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2653 m = g_list_append(m, pce); |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2654 |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2655 pce = g_new0(struct proto_chat_entry, 1); |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2656 pce->label = _("Handle:"); |
2956 | 2657 pce->def = gjc->user->user; |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2658 m = g_list_append(m, pce); |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2659 |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2660 return m; |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2661 } |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2662 |
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2663 static void jabber_join_chat(struct gaim_connection *gc, GList *data) |
2086 | 2664 { |
2665 xmlnode x; | |
2666 char *realwho; | |
2956 | 2667 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
2668 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; | |
2086 | 2669 struct jabber_chat *jc; |
3311 | 2670 gaim_jid gjid; |
2086 | 2671 |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2672 if (!data || !data->next || !data->next->next) |
2086 | 2673 return; |
2674 | |
3311 | 2675 realwho = create_valid_jid(data->data, data->next->data, data->next->next->data); |
2086 | 2676 debug_printf("%s\n", realwho); |
2677 | |
3311 | 2678 if((gjid = gaim_jid_new(realwho)) == NULL) { |
3427 | 2679 char *msg = g_strdup_printf("The Jabber I.D. %s is invalid.", realwho); |
2680 do_error_dialog(_("Unable to join chat"), msg, GAIM_ERROR); | |
3236 | 2681 g_free(msg); |
3311 | 2682 g_free(realwho); |
3236 | 2683 return; |
2684 } | |
2956 | 2685 |
3311 | 2686 if((jc = find_any_chat(gc, gjid)) != NULL) { |
2956 | 2687 switch(jc->state) { |
2688 case JCS_PENDING: | |
2689 debug_printf("attempt to re-join already pending Jabber chat! (ignoring)\n"); | |
2690 g_free(realwho); /* yuck! */ | |
3311 | 2691 gaim_jid_free(gjid); |
2956 | 2692 return; |
2693 case JCS_ACTIVE: | |
2694 debug_printf("attempt to re-join already active Jabber chat! (ignoring)\n"); | |
2695 g_free(realwho); /* yuck! */ | |
3311 | 2696 gaim_jid_free(gjid); |
2956 | 2697 return; |
2698 case JCS_CLOSED: | |
2699 debug_printf("rejoining previously closed Jabber chat\n"); | |
2700 break; | |
2701 default: | |
2702 debug_printf("found Jabber chat in unknown state! (ignoring)\n"); | |
2703 g_free(realwho); /* yuck! */ | |
3311 | 2704 gaim_jid_free(gjid); |
2956 | 2705 return; |
2706 } | |
2707 } else { | |
2708 debug_printf("joining completely new Jabber chat\n"); | |
2709 jc = g_new0(struct jabber_chat, 1); | |
3311 | 2710 jc->gjid = gjid; |
2956 | 2711 jc->gc = gc; |
2712 ((struct jabber_data *)gc->proto_data)->chats = g_slist_append(jcs, jc); | |
3311 | 2713 add_buddy(gc, _("Chats"), realwho, realwho); |
2956 | 2714 } |
2715 | |
2716 jc->state = JCS_PENDING; | |
2717 | |
2086 | 2718 x = jutil_presnew(0, realwho, NULL); |
2956 | 2719 gjab_send(gjc, x); |
2086 | 2720 xmlnode_free(x); |
2721 g_free(realwho); | |
2722 } | |
2723 | |
3466 | 2724 static void jabber_chat_invite(struct gaim_connection *gc, int id, const char *message, const char *name) |
2086 | 2725 { |
2726 xmlnode x, y; | |
2727 struct jabber_data *jd = gc->proto_data; | |
2956 | 2728 gjconn gjc = jd->gjc; |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2729 struct jabber_chat *jc = NULL; |
2086 | 2730 char *realwho, *subject; |
2731 | |
3311 | 2732 if(!name || (realwho = get_realwho(gjc, name, FALSE, NULL)) == NULL) |
2086 | 2733 return; |
2734 | |
2735 /* find which chat we're inviting to */ | |
2956 | 2736 if(jabber_find_chat_by_convo_id(gc, id, &jc) != 0) |
2086 | 2737 return; |
2738 | |
2739 x = xmlnode_new_tag("message"); | |
2740 xmlnode_put_attrib(x, "to", realwho); | |
3311 | 2741 |
2086 | 2742 g_free(realwho); |
2743 | |
2744 y = xmlnode_insert_tag(x, "x"); | |
2745 xmlnode_put_attrib(y, "xmlns", "jabber:x:conference"); | |
3311 | 2746 subject = g_strdup_printf("%s@%s", jc->gjid->user, jc->gjid->server); |
2086 | 2747 xmlnode_put_attrib(y, "jid", subject); |
2748 g_free(subject); | |
2749 | |
2750 if (message && strlen(message)) { | |
2751 char *utf8 = str_to_utf8(message); | |
2752 y = xmlnode_insert_tag(x, "body"); | |
2753 xmlnode_insert_cdata(y, utf8, -1); | |
2754 g_free(utf8); | |
2755 } | |
2756 | |
2956 | 2757 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); |
2086 | 2758 xmlnode_free(x); |
2759 } | |
2760 | |
2761 static void jabber_chat_leave(struct gaim_connection *gc, int id) | |
2762 { | |
2763 struct jabber_data *jd = gc->proto_data; | |
2956 | 2764 gjconn gjc = jd->gjc; |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2765 struct jabber_chat *jc = NULL; |
3311 | 2766 char *chatname; |
2086 | 2767 xmlnode x; |
2768 | |
2956 | 2769 /* Find out which chat we're leaving */ |
2770 if(jabber_find_chat_by_convo_id(gc, id, &jc) != 0) | |
2086 | 2771 return; |
2772 | |
3311 | 2773 chatname = g_strdup_printf("%s@%s", jc->gjid->user, jc->gjid->server); |
2774 x = jutil_presnew(0, chatname, NULL); | |
2775 g_free(chatname); | |
2086 | 2776 xmlnode_put_attrib(x, "type", "unavailable"); |
2956 | 2777 gjab_send(gjc, x); |
2086 | 2778 xmlnode_free(x); |
2779 jc->b = NULL; | |
2780 } | |
2781 | |
2167
edf8c5a70e5b
[gaim-migrate @ 2177]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2162
diff
changeset
|
2782 static int jabber_chat_send(struct gaim_connection *gc, int id, char *message) |
2086 | 2783 { |
2784 xmlnode x, y; | |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2785 struct jabber_chat *jc = NULL; |
2086 | 2786 char *chatname; |
2956 | 2787 int retval = 0; |
2788 | |
2789 /* Find out which chat we're sending to */ | |
2790 if((retval = jabber_find_chat_by_convo_id(gc, id, &jc)) != 0) | |
2791 return(retval); | |
2086 | 2792 |
2793 x = xmlnode_new_tag("message"); | |
3311 | 2794 xmlnode_put_attrib(x, "from", jc->gjid->full); |
2795 chatname = g_strdup_printf("%s@%s", jc->gjid->user, jc->gjid->server); | |
2086 | 2796 xmlnode_put_attrib(x, "to", chatname); |
2797 g_free(chatname); | |
2798 xmlnode_put_attrib(x, "type", "groupchat"); | |
2799 | |
2289
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2800 if (message && strlen(message) > strlen("/topic ") && |
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2801 !g_strncasecmp(message, "/topic ", strlen("/topic "))) { |
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2802 char buf[8192]; |
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2803 char *utf8 = str_to_utf8(message + strlen("/topic ")); |
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2804 y = xmlnode_insert_tag(x, "subject"); |
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2805 xmlnode_insert_cdata(y, utf8, -1); |
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2806 y = xmlnode_insert_tag(x, "body"); |
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2807 g_snprintf(buf, sizeof(buf), "/me has changed the subject to: %s", utf8); |
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2808 xmlnode_insert_cdata(y, buf, -1); |
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2809 g_free(utf8); |
38e156136896
[gaim-migrate @ 2299]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2278
diff
changeset
|
2810 } else if (message && strlen(message)) { |
2086 | 2811 char *utf8 = str_to_utf8(message); |
2812 y = xmlnode_insert_tag(x, "body"); | |
2813 xmlnode_insert_cdata(y, utf8, -1); | |
2814 g_free(utf8); | |
2815 } | |
2816 | |
2956 | 2817 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); |
2086 | 2818 xmlnode_free(x); |
2167
edf8c5a70e5b
[gaim-migrate @ 2177]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2162
diff
changeset
|
2819 return 0; |
2086 | 2820 } |
2821 | |
2822 static void jabber_chat_whisper(struct gaim_connection *gc, int id, char *who, char *message) | |
2823 { | |
2824 xmlnode x, y; | |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
2825 struct jabber_chat *jc = NULL; |
2086 | 2826 char *chatname; |
2827 | |
2956 | 2828 /* Find out which chat we're whispering to */ |
2829 if(jabber_find_chat_by_convo_id(gc, id, &jc) != 0) | |
2086 | 2830 return; |
2831 | |
2832 x = xmlnode_new_tag("message"); | |
3311 | 2833 xmlnode_put_attrib(x, "from", jc->gjid->full); |
2834 chatname = g_strdup_printf("%s@%s/%s", jc->gjid->user, jc->gjid->server, who); | |
2086 | 2835 xmlnode_put_attrib(x, "to", chatname); |
2836 g_free(chatname); | |
2837 xmlnode_put_attrib(x, "type", "normal"); | |
2838 | |
2839 if (message && strlen(message)) { | |
2840 char *utf8 = str_to_utf8(message); | |
2841 y = xmlnode_insert_tag(x, "body"); | |
2842 xmlnode_insert_cdata(y, utf8, -1); | |
2843 g_free(utf8); | |
2844 } | |
2845 | |
2956 | 2846 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); |
2086 | 2847 xmlnode_free(x); |
2848 } | |
2849 | |
2850 static char *jabber_normalize(const char *s) | |
2851 { | |
2852 static char buf[BUF_LEN]; | |
2853 char *t, *u; | |
2854 int x = 0; | |
2855 | |
2856 g_return_val_if_fail((s != NULL), NULL); | |
2857 | |
2956 | 2858 /* Somebody called us with s == NULL once... */ |
2859 if(s == NULL) { | |
2860 return(NULL); | |
2861 } else { | |
2862 u = t = g_strdup(s); | |
2863 | |
2864 g_strdown(t); | |
2865 | |
2866 while (*t && (x < BUF_LEN - 1)) { | |
2867 if (*t != ' ') | |
2868 buf[x++] = *t; | |
2869 t++; | |
2870 } | |
2871 buf[x] = '\0'; | |
2872 g_free(u); | |
2873 | |
2874 if (!strchr(buf, '@')) { | |
2875 strcat(buf, "@jabber.org"); /* this isn't always right, but eh */ | |
2876 } else if ((u = strchr(strchr(buf, '@'), '/')) != NULL) { | |
2877 *u = '\0'; | |
2878 } | |
2879 | |
2880 return buf; | |
2086 | 2881 } |
2882 } | |
2883 | |
2884 static void jabber_get_info(struct gaim_connection *gc, char *who) { | |
2885 xmlnode x; | |
2886 char *id; | |
2956 | 2887 char *realwho; |
2086 | 2888 struct jabber_data *jd = gc->proto_data; |
2956 | 2889 gjconn gjc = jd->gjc; |
2086 | 2890 |
3311 | 2891 if((realwho = get_realwho(gjc, who, TRUE, NULL)) == NULL) |
2892 return; | |
2893 | |
2086 | 2894 x = jutil_iqnew(JPACKET__GET, NS_VCARD); |
2956 | 2895 xmlnode_put_attrib(x, "to", realwho); |
3311 | 2896 |
2956 | 2897 g_free(realwho); |
2898 | |
2899 id = gjab_getid(gjc); | |
2086 | 2900 xmlnode_put_attrib(x, "id", id); |
2901 | |
2956 | 2902 g_hash_table_insert(jd->gjc->queries, g_strdup(id), g_strdup("vCard")); |
2903 | |
2904 gjab_send(gjc, x); | |
2086 | 2905 |
2906 xmlnode_free(x); | |
3311 | 2907 } |
2908 | |
2909 static void jabber_get_error_msg(struct gaim_connection *gc, char *who) { | |
2910 struct jabber_data *jd = gc->proto_data; | |
2911 gjconn gjc = jd->gjc; | |
2912 gchar **str_arr = (gchar **) g_new(gpointer, 3); | |
2913 gchar **ap = str_arr; | |
2914 gchar *realwho, *final; | |
2915 struct jabber_buddy_data *jbd; | |
2916 | |
2917 if((realwho = get_realwho(gjc, who, FALSE, NULL)) == NULL) { | |
2918 g_strfreev(str_arr); | |
2919 return; | |
2920 } | |
2921 | |
2922 jbd = jabber_find_buddy(gc, realwho); | |
2923 | |
2924 *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho); | |
2925 *ap++ = g_strdup_printf("<B>Error:</B> %s<BR>\n", jbd->error_msg); | |
2926 *ap = NULL; | |
2086 | 2927 |
3311 | 2928 final= g_strjoinv(NULL, str_arr); |
2929 | |
2930 g_strfreev(str_arr); | |
2931 | |
2932 g_show_info_text(gc, realwho, 2, final, NULL); | |
2933 g_free(realwho); | |
2934 g_free(final); | |
2086 | 2935 } |
2936 | |
2956 | 2937 static void jabber_get_away_msg(struct gaim_connection *gc, char *who) { |
2938 struct jabber_data *jd = gc->proto_data; | |
2939 gjconn gjc = jd->gjc; | |
3311 | 2940 int num_resources; |
2941 gaim_jid gjid; | |
2942 char *buddy = get_realwho(gjc, who, FALSE, &gjid); | |
2943 struct jabber_buddy_data *jbd = jabber_find_buddy(gc, buddy); | |
2944 gchar **str_arr; | |
2945 gchar **ap; | |
2946 gchar *realwho, *final; | |
2947 GSList *resources; | |
2948 int i; | |
2949 | |
2950 if(!buddy) | |
2951 return; | |
2952 | |
2953 if(!gjid->resource) { | |
2954 num_resources = g_slist_length(jbd->resources); | |
2955 resources = jbd->resources; | |
2956 } else { | |
2957 num_resources = 1; | |
2958 resources = jbd->resources; | |
2959 while(strcasecmp(((jab_res_info)resources->data)->name, gjid->resource)) | |
2960 resources = resources->next; | |
2961 } | |
2962 | |
2963 gaim_jid_free(gjid); | |
2956 | 2964 |
2965 /* space for all elements: Jabber I.D. + "status" + NULL (list terminator) */ | |
3311 | 2966 str_arr = (gchar **) g_new(gpointer, num_resources*2 + 1); |
2967 ap = str_arr; | |
2968 | |
2969 for(i=0; i<num_resources; i++) | |
2970 { | |
2971 jab_res_info jri = resources->data; | |
2972 realwho = g_strdup_printf("%s/%s", buddy, jri->name); | |
2973 *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho); | |
2974 *ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", jabber_lookup_away(gjc, realwho)); | |
2975 g_free(realwho); | |
2976 resources = resources->next; | |
2956 | 2977 } |
2978 | |
2979 *ap = NULL; | |
3311 | 2980 |
2981 g_free(buddy); | |
2956 | 2982 |
2983 final= g_strjoinv(NULL, str_arr); | |
2984 g_strfreev(str_arr); | |
2985 | |
3311 | 2986 g_show_info_text(gc, who, 2, final, NULL); |
2956 | 2987 g_free(final); |
2988 | |
2989 } | |
2990 | |
2991 static void jabber_get_cb_info(struct gaim_connection *gc, int cid, char *who) { | |
2992 struct jabber_chat *jc = NULL; | |
2993 char *realwho; | |
2994 | |
2995 /* Find out which chat */ | |
2996 if(jabber_find_chat_by_convo_id(gc, cid, &jc) != 0) | |
2997 return; | |
2998 | |
3311 | 2999 realwho = g_strdup_printf("%s@%s/%s", jc->gjid->user, jc->gjid->server, who); |
2956 | 3000 |
3001 jabber_get_info(gc, realwho); | |
3002 g_free(realwho); | |
3003 } | |
3004 | |
3005 static void jabber_get_cb_away_msg(struct gaim_connection *gc, int cid, char *who) { | |
3006 struct jabber_chat *jc = NULL; | |
3007 char *realwho; | |
3008 | |
3009 /* Find out which chat */ | |
3010 if(jabber_find_chat_by_convo_id(gc, cid, &jc) != 0) | |
3011 return; | |
3012 | |
3311 | 3013 realwho = g_strdup_printf("%s@%s/%s", jc->gjid->user, jc->gjid->server, who); |
2956 | 3014 |
3015 jabber_get_away_msg(gc, realwho); | |
3016 g_free(realwho); | |
3017 | |
3018 } | |
3019 | |
2170
c24595d3c364
[gaim-migrate @ 2180]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2167
diff
changeset
|
3020 static GList *jabber_buddy_menu(struct gaim_connection *gc, char *who) { |
c24595d3c364
[gaim-migrate @ 2180]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2167
diff
changeset
|
3021 GList *m = NULL; |
c24595d3c364
[gaim-migrate @ 2180]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2167
diff
changeset
|
3022 struct proto_buddy_menu *pbm; |
3311 | 3023 struct buddy *b = find_buddy(gc, who); |
3024 | |
3025 if(b->uc == UC_ERROR) | |
3026 { | |
3027 pbm = g_new0(struct proto_buddy_menu, 1); | |
3028 pbm->label = _("View Error Msg"); | |
3029 pbm->callback = jabber_get_error_msg; | |
3030 pbm->gc = gc; | |
3031 m = g_list_append(m, pbm); | |
3032 } else { | |
3340 | 3033 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; |
3034 char *realwho = get_realwho(gjc, who, FALSE, NULL); | |
3035 struct jabber_buddy_data *jbd = jabber_find_buddy(gc, realwho); | |
3036 | |
3037 g_free(realwho); | |
3038 | |
3311 | 3039 pbm = g_new0(struct proto_buddy_menu, 1); |
3040 pbm->label = _("Get Info"); | |
3041 pbm->callback = jabber_get_info; | |
3042 pbm->gc = gc; | |
3043 m = g_list_append(m, pbm); | |
3044 pbm = g_new0(struct proto_buddy_menu, 1); | |
3045 pbm->label = _("Get Away Msg"); | |
3046 pbm->callback = jabber_get_away_msg; | |
3047 pbm->gc = gc; | |
3048 m = g_list_append(m, pbm); | |
3340 | 3049 |
3050 pbm = g_new0(struct proto_buddy_menu, 1); | |
3051 if(jbd && (jbd->invisible & JABBER_BUD_INVIS)) { | |
3052 pbm->label = _("Un-hide From"); | |
3053 pbm->callback = jabber_visible_to_buddy; | |
3054 } else { | |
3055 pbm->label = _("Temporarily Hide From"); | |
3056 pbm->callback = jabber_invisible_to_buddy; | |
3057 } | |
3058 pbm->gc = gc; | |
3059 m = g_list_append(m, pbm); | |
3311 | 3060 } |
2170
c24595d3c364
[gaim-migrate @ 2180]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2167
diff
changeset
|
3061 |
c24595d3c364
[gaim-migrate @ 2180]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2167
diff
changeset
|
3062 return m; |
2086 | 3063 } |
3064 | |
3314 | 3065 /* |
3066 * Jabber protocol-specific "edit buddy menu" item(s) | |
3067 */ | |
3068 static GList *jabber_edit_buddy_menu(struct gaim_connection *gc, char *who) { | |
3069 GList *m = NULL; | |
3070 struct proto_buddy_menu *pbm; | |
3071 | |
3072 pbm = g_new0(struct proto_buddy_menu, 1); | |
3456 | 3073 pbm->label = _("Get Info"); |
3074 pbm->callback = jabber_get_info; | |
3075 pbm->gc = gc; | |
3076 m = g_list_append(m, pbm); | |
3077 pbm = g_new0(struct proto_buddy_menu, 1); | |
3314 | 3078 pbm->label = _("Remove From Roster"); |
3079 pbm->callback = jabber_remove_buddy_roster_item; | |
3080 pbm->gc = gc; | |
3081 m = g_list_append(m, pbm); | |
3340 | 3082 pbm = g_new0(struct proto_buddy_menu, 1); |
3083 pbm->label = _("Cancel Presence Notification"); | |
3084 pbm->callback = jabber_unsubscribe_buddy_from_us; | |
3085 pbm->gc = gc; | |
3086 m = g_list_append(m, pbm); | |
3314 | 3087 |
3088 return m; | |
3089 } | |
3090 | |
2501
227cc42ffa6e
[gaim-migrate @ 2514]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2382
diff
changeset
|
3091 static GList *jabber_away_states(struct gaim_connection *gc) { |
2086 | 3092 GList *m = NULL; |
3093 | |
3094 m = g_list_append(m, "Online"); | |
3095 m = g_list_append(m, "Chatty"); | |
3096 m = g_list_append(m, "Away"); | |
3097 m = g_list_append(m, "Extended Away"); | |
3098 m = g_list_append(m, "Do Not Disturb"); | |
3340 | 3099 m = g_list_append(m, "Invisible"); |
2086 | 3100 |
3101 return m; | |
3102 } | |
3103 | |
3104 static void jabber_set_away(struct gaim_connection *gc, char *state, char *message) | |
3105 { | |
3106 xmlnode x, y; | |
3107 struct jabber_data *jd = gc->proto_data; | |
2956 | 3108 gjconn gjc = jd->gjc; |
3311 | 3109 GSList *jcs; |
3110 struct jabber_chat *jc; | |
3111 char *chatname; | |
3340 | 3112 gboolean invisible = FALSE; |
2086 | 3113 |
3114 gc->away = NULL; /* never send an auto-response */ | |
3115 | |
3116 x = xmlnode_new_tag("presence"); | |
3117 | |
3118 if (!strcmp(state, GAIM_AWAY_CUSTOM)) { | |
3119 /* oh goody. Gaim is telling us what to do. */ | |
3120 if (message) { | |
3121 /* Gaim wants us to be away */ | |
3122 y = xmlnode_insert_tag(x, "show"); | |
3123 xmlnode_insert_cdata(y, "away", -1); | |
3124 y = xmlnode_insert_tag(x, "status"); | |
3125 xmlnode_insert_cdata(y, message, -1); | |
3126 gc->away = ""; | |
3127 } else { | |
3128 /* Gaim wants us to not be away */ | |
3129 /* but for Jabber, we can just send presence with no other information. */ | |
3130 } | |
3131 } else { | |
3132 /* state is one of our own strings. it won't be NULL. */ | |
3133 if (!strcmp(state, "Online")) { | |
3134 /* once again, we don't have to put anything here */ | |
3135 } else if (!strcmp(state, "Chatty")) { | |
3136 y = xmlnode_insert_tag(x, "show"); | |
3137 xmlnode_insert_cdata(y, "chat", -1); | |
3138 } else if (!strcmp(state, "Away")) { | |
3139 y = xmlnode_insert_tag(x, "show"); | |
3140 xmlnode_insert_cdata(y, "away", -1); | |
3141 gc->away = ""; | |
3142 } else if (!strcmp(state, "Extended Away")) { | |
3143 y = xmlnode_insert_tag(x, "show"); | |
3144 xmlnode_insert_cdata(y, "xa", -1); | |
3145 gc->away = ""; | |
3146 } else if (!strcmp(state, "Do Not Disturb")) { | |
3147 y = xmlnode_insert_tag(x, "show"); | |
3148 xmlnode_insert_cdata(y, "dnd", -1); | |
3149 gc->away = ""; | |
3340 | 3150 } else if (!strcmp(state, "Invisible")) { |
3151 xmlnode_put_attrib(x, "type", "invisible"); | |
3152 gc->away = ""; | |
3153 invisible = TRUE; | |
2086 | 3154 } |
3155 } | |
3156 | |
3311 | 3157 gjab_send(gjc, x); /* Notify "individuals" */ |
3158 | |
3159 /* | |
3160 * As of jabberd-1.4.2: simply sending presence to the server doesn't result in | |
3161 * it being propagated to conference rooms. So we wade thru the list of chats, | |
3162 * sending our new presence status to each and every one. | |
3163 */ | |
3164 for(jcs = jd->chats; jcs; jcs = jcs->next) { | |
3165 jc = jcs->data; | |
3166 if(jc->state == JCS_ACTIVE) { | |
3167 xmlnode_put_attrib(x, "from", jc->gjid->full); | |
3168 chatname = g_strdup_printf("%s@%s", jc->gjid->user, jc->gjid->server); | |
3169 xmlnode_put_attrib(x, "to", chatname); | |
3170 gjab_send(gjc, x); | |
3171 g_free(chatname); | |
3172 } | |
3173 } | |
3174 | |
2086 | 3175 xmlnode_free(x); |
3340 | 3176 |
3177 invisible_to_all_buddies(gc, invisible); | |
2086 | 3178 } |
3179 | |
3180 static void jabber_set_idle(struct gaim_connection *gc, int idle) { | |
3181 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; | |
3182 debug_printf("jabber_set_idle: setting idle %i\n", idle); | |
3183 jd->idle = idle ? time(NULL) - idle : idle; | |
3184 } | |
3185 | |
3186 static void jabber_keepalive(struct gaim_connection *gc) { | |
3187 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; | |
2956 | 3188 gjab_send_raw(jd->gjc, " \t "); |
2086 | 3189 } |
3190 | |
2956 | 3191 /*---------------------------------------*/ |
3192 /* Jabber "set info" (vCard) support */ | |
3193 /*---------------------------------------*/ | |
3194 | |
3195 /* | |
3196 * V-Card format: | |
3197 * | |
3198 * <vCard prodid='' version='' xmlns=''> | |
3199 * <FN></FN> | |
3200 * <N> | |
3201 * <FAMILY/> | |
3202 * <GIVEN/> | |
3203 * </N> | |
3204 * <NICKNAME/> | |
3205 * <URL/> | |
3206 * <ADR> | |
3207 * <STREET/> | |
3208 * <EXTADD/> | |
3209 * <LOCALITY/> | |
3210 * <REGION/> | |
3211 * <PCODE/> | |
3212 * <COUNTRY/> | |
3213 * </ADR> | |
3214 * <TEL/> | |
3215 * <EMAIL/> | |
3216 * <ORG> | |
3217 * <ORGNAME/> | |
3218 * <ORGUNIT/> | |
3219 * </ORG> | |
3220 * <TITLE/> | |
3221 * <ROLE/> | |
3222 * <DESC/> | |
3223 * <BDAY/> | |
3224 * </vCard> | |
3225 * | |
3226 * See also: | |
3227 * | |
3228 * http://docs.jabber.org/proto/html/vcard-temp.html | |
3229 * http://www.vcard-xml.org/dtd/vCard-XML-v2-20010520.dtd | |
3230 */ | |
3231 | |
3232 /* | |
3233 * Cross-reference user-friendly V-Card entry labels to vCard XML tags | |
3234 * and attributes. | |
3235 * | |
3236 * Order is (or should be) unimportant. For example: we have no way of | |
3237 * knowing in what order real data will arrive. | |
3238 * | |
3239 * Format: Label, Pre-set text, "visible" flag, "editable" flag, XML tag | |
3240 * name, XML tag's parent tag "path" (relative to vCard node). | |
3241 * | |
3242 * List is terminated by a NULL label pointer. | |
3243 * | |
3244 * Entries with no label text, but with XML tag and parent tag | |
3245 * entries, are used by V-Card XML construction routines to | |
3246 * "automagically" construct the appropriate XML node tree. | |
3247 * | |
3248 * Thoughts on future direction/expansion | |
3249 * | |
3250 * This is a "simple" vCard. | |
3251 * | |
3252 * It is possible for nodes other than the "vCard" node to have | |
3253 * attributes. Should that prove necessary/desirable, add an | |
3254 * "attributes" pointer to the vcard_template struct, create the | |
3255 * necessary tag_attr structs, and add 'em to the vcard_dflt_data | |
3256 * array. | |
3257 * | |
3258 * The above changes will (obviously) require changes to the vCard | |
3259 * construction routines. | |
3260 */ | |
3261 | |
3262 struct vcard_template { | |
3263 char *label; /* label text pointer */ | |
3264 char *text; /* entry text pointer */ | |
3265 int visible; /* should entry field be "visible?" */ | |
3266 int editable; /* should entry field be editable? */ | |
3267 char *tag; /* tag text */ | |
3268 char *ptag; /* parent tag "path" text */ | |
3269 char *url; /* vCard display format if URL */ | |
3270 } vcard_template_data[] = { | |
2975 | 3271 {N_("Full Name"), NULL, TRUE, TRUE, "FN", NULL, NULL}, |
3272 {N_("Family Name"), NULL, TRUE, TRUE, "FAMILY", "N", NULL}, | |
3273 {N_("Given Name"), NULL, TRUE, TRUE, "GIVEN", "N", NULL}, | |
3274 {N_("Nickname"), NULL, TRUE, TRUE, "NICKNAME", NULL, NULL}, | |
3275 {N_("URL"), NULL, TRUE, TRUE, "URL", NULL, "<A HREF=\"%s\">%s</A>"}, | |
3276 {N_("Street Address"), NULL, TRUE, TRUE, "STREET", "ADR", NULL}, | |
3277 {N_("Extended Address"), NULL, TRUE, TRUE, "EXTADD", "ADR", NULL}, | |
3278 {N_("Locality"), NULL, TRUE, TRUE, "LOCALITY", "ADR", NULL}, | |
3279 {N_("Region"), NULL, TRUE, TRUE, "REGION", "ADR", NULL}, | |
3280 {N_("Postal Code"), NULL, TRUE, TRUE, "PCODE", "ADR", NULL}, | |
3281 {N_("Country"), NULL, TRUE, TRUE, "COUNTRY", "ADR", NULL}, | |
3282 {N_("Telephone"), NULL, TRUE, TRUE, "TELEPHONE", NULL, NULL}, | |
3283 {N_("Email"), NULL, TRUE, TRUE, "EMAIL", NULL, "<A HREF=\"mailto:%s\">%s</A>"}, | |
3284 {N_("Organization Name"), NULL, TRUE, TRUE, "ORGNAME", "ORG", NULL}, | |
3285 {N_("Organization Unit"), NULL, TRUE, TRUE, "ORGUNIT", "ORG", NULL}, | |
3286 {N_("Title"), NULL, TRUE, TRUE, "TITLE", NULL, NULL}, | |
3287 {N_("Role"), NULL, TRUE, TRUE, "ROLE", NULL, NULL}, | |
3288 {N_("Birthday"), NULL, TRUE, TRUE, "BDAY", NULL, NULL}, | |
3289 {N_("Description"), NULL, TRUE, TRUE, "DESC", NULL, NULL}, | |
2956 | 3290 {"", NULL, TRUE, TRUE, "N", NULL, NULL}, |
3291 {"", NULL, TRUE, TRUE, "ADR", NULL, NULL}, | |
3292 {"", NULL, TRUE, TRUE, "ORG", NULL, NULL}, | |
3293 {NULL, NULL, 0, 0, NULL, NULL, NULL} | |
3294 }; | |
3295 | |
3296 /* | |
3297 * The "vCard" tag's attibute list... | |
3298 */ | |
3299 struct tag_attr { | |
3300 char *attr; | |
3301 char *value; | |
3302 } vcard_tag_attr_list[] = { | |
3303 {"prodid", "-//HandGen//NONSGML vGen v1.0//EN"}, | |
3304 {"version", "2.0", }, | |
3305 {"xmlns", "vcard-temp", }, | |
3306 {NULL, NULL}, | |
3307 }; | |
3308 | |
3309 | |
3310 /* | |
3311 * V-Card user instructions | |
3312 */ | |
3313 static char *multi_entry_instructions = | |
2975 | 3314 N_("All items below are optional. Enter only the information with which you feel comfortable"); |
3315 static char *entries_title = N_("User Identity"); | |
2956 | 3316 |
3317 /* | |
3318 * Used by routines to parse an XML-encoded string into an xmlnode tree | |
3319 */ | |
3320 typedef struct { | |
3321 XML_Parser parser; | |
3322 xmlnode current; | |
3323 } *xmlstr2xmlnode_parser, xmlstr2xmlnode_parser_struct; | |
3324 | |
3325 | |
3326 /* | |
3327 * Display a Jabber vCard | |
3328 */ | |
3329 static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from) | |
3330 { | |
3331 struct gaim_connection *gc = GJ_GC(gjc); | |
3332 char *cdata, *status; | |
3333 struct vcard_template *vc_tp = vcard_template_data; | |
3334 | |
3335 /* space for all vCard elements + Jabber I.D. + "status" + NULL (list terminator) */ | |
3336 gchar **str_arr = (gchar **) g_new(gpointer, | |
3337 (sizeof(vcard_template_data)/sizeof(struct vcard_template)) + 3); | |
3338 gchar **ap = str_arr; | |
3339 gchar *buddy, *final; | |
3340 | |
3311 | 3341 if((buddy = get_realwho(gjc, from, TRUE, NULL)) == NULL) { |
3342 g_strfreev(str_arr); | |
3343 return; | |
2956 | 3344 } |
3311 | 3345 |
2956 | 3346 *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", buddy); |
3347 | |
3348 for(vc_tp = vcard_template_data; vc_tp->label != NULL; ++vc_tp) { | |
3349 if(strcmp(vc_tp->tag, "DESC") == 0) | |
3350 continue; /* special handling later */ | |
3351 if(vc_tp->ptag == NULL) { | |
3352 cdata = xmlnode_get_tag_data(querynode, vc_tp->tag); | |
3353 } else { | |
3354 gchar *tag = g_strdup_printf("%s/%s", vc_tp->ptag, vc_tp->tag); | |
3355 cdata = xmlnode_get_tag_data(querynode, tag); | |
3356 g_free(tag); | |
3357 } | |
3358 if(cdata != NULL) { | |
3359 if(vc_tp->url == NULL) { | |
3360 *ap++ = g_strdup_printf("<B>%s:</B> %s<BR>\n", vc_tp->label, cdata); | |
3361 } else { | |
3362 gchar *fmt = g_strdup_printf("<B>%%s:</B> %s<BR>\n", vc_tp->url); | |
3363 *ap++ = g_strdup_printf(fmt, vc_tp->label, cdata, cdata); | |
3364 g_free(fmt); | |
3365 } | |
3366 } | |
3367 } | |
3368 | |
3311 | 3369 status = jabber_lookup_away(gjc, buddy); |
3370 | |
2956 | 3371 *ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status); |
3372 | |
3373 /* | |
3374 * "Description" handled as a special case: get a copy of the | |
3375 * string and HTML-ize. | |
3376 */ | |
3377 if((cdata = xmlnode_get_tag_data(querynode, "DESC")) != NULL) { | |
3378 gchar *tmp = g_strdup_printf("<HR>%s<BR>", cdata); | |
3379 *ap++ = strdup_withhtml(tmp); | |
3380 g_free(tmp); | |
3381 } | |
3382 | |
3383 *ap = NULL; | |
3384 | |
3385 final= g_strjoinv(NULL, str_arr); | |
3386 g_strfreev(str_arr); | |
3387 | |
3388 g_show_info_text(gc, buddy, 2, final, NULL); | |
3389 g_free(buddy); | |
3390 g_free(final); | |
3391 } | |
3392 | |
3393 /* | |
3394 * Used by XML_Parse on parsing CDATA | |
3395 */ | |
3396 static void xmlstr2xmlnode_charData(void *userdata, const char *s, int slen) | |
3397 { | |
3398 xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata; | |
3399 | |
3400 if (xmlp->current) | |
3401 xmlnode_insert_cdata(xmlp->current, s, slen); | |
3402 } | |
3403 | |
3404 /* | |
3405 * Used by XML_Parse to start or append to an xmlnode | |
3406 */ | |
3407 static void xmlstr2xmlnode_startElement(void *userdata, const char *name, const char **attribs) | |
3408 { | |
3409 xmlnode x; | |
3410 xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata; | |
3411 | |
3412 if (xmlp->current) { | |
3413 /* Append the node to the current one */ | |
3414 x = xmlnode_insert_tag(xmlp->current, name); | |
3415 xmlnode_put_expat_attribs(x, attribs); | |
3416 | |
3417 xmlp->current = x; | |
3418 } else { | |
3419 x = xmlnode_new_tag(name); | |
3420 xmlnode_put_expat_attribs(x, attribs); | |
3421 xmlp->current = x; | |
3422 } | |
3423 } | |
3424 | |
3425 /* | |
3426 * Used by XML_Parse to end an xmlnode | |
3427 */ | |
3428 static void xmlstr2xmlnode_endElement(void *userdata, const char *name) | |
3429 { | |
3430 xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata; | |
3431 xmlnode x; | |
3432 | |
3433 if (xmlp->current != NULL && (x = xmlnode_get_parent(xmlp->current)) != NULL) { | |
3434 xmlp->current = x; | |
3435 } | |
3436 } | |
3437 | |
3438 /* | |
3439 * Parse an XML-encoded string into an xmlnode tree | |
3440 * | |
3441 * Caller is responsible for freeing the returned xmlnode | |
3442 */ | |
3443 static xmlnode xmlstr2xmlnode(char *xmlstring) | |
3444 { | |
3445 xmlstr2xmlnode_parser my_parser = g_new(xmlstr2xmlnode_parser_struct, 1); | |
3446 xmlnode x = NULL; | |
3447 | |
3448 my_parser->parser = XML_ParserCreate(NULL); | |
3449 my_parser->current = NULL; | |
3450 | |
3451 XML_SetUserData(my_parser->parser, (void *)my_parser); | |
3452 XML_SetElementHandler(my_parser->parser, xmlstr2xmlnode_startElement, xmlstr2xmlnode_endElement); | |
3453 XML_SetCharacterDataHandler(my_parser->parser, xmlstr2xmlnode_charData); | |
3454 XML_Parse(my_parser->parser, xmlstring, strlen(xmlstring), 0); | |
3455 | |
3456 x = my_parser->current; | |
3457 | |
3458 XML_ParserFree(my_parser->parser); | |
3459 g_free(my_parser); | |
3460 | |
3461 return(x); | |
3462 } | |
3463 | |
3464 /* | |
3465 * Insert a tag node into an xmlnode tree, recursively inserting parent tag | |
3466 * nodes as necessary | |
3467 * | |
3468 * Returns pointer to inserted node | |
3469 * | |
3470 * Note to hackers: this code is designed to be re-entrant (it's recursive--it | |
3471 * calls itself), so don't put any "static"s in here! | |
3472 */ | |
3473 static xmlnode insert_tag_to_parent_tag(xmlnode start, const char *parent_tag, const char *new_tag) | |
3474 { | |
3475 xmlnode x = NULL; | |
3476 | |
3477 /* | |
3478 * If the parent tag wasn't specified, see if we can get it | |
3479 * from the vCard template struct. | |
3480 */ | |
3481 if(parent_tag == NULL) { | |
3482 struct vcard_template *vc_tp = vcard_template_data; | |
3483 | |
3484 while(vc_tp->label != NULL) { | |
3485 if(strcmp(vc_tp->tag, new_tag) == 0) { | |
3486 parent_tag = vc_tp->ptag; | |
3487 break; | |
3488 } | |
3489 ++vc_tp; | |
3490 } | |
3491 } | |
3492 | |
3493 /* | |
3494 * If we have a parent tag... | |
3495 */ | |
3496 if(parent_tag != NULL ) { | |
3497 /* | |
3498 * Try to get the parent node for a tag | |
3499 */ | |
3500 if((x = xmlnode_get_tag(start, parent_tag)) == NULL) { | |
3501 /* | |
3502 * Descend? | |
3503 */ | |
3504 char *grand_parent = strcpy(g_malloc(strlen(parent_tag) + 1), parent_tag); | |
3505 char *parent; | |
3506 | |
3507 if((parent = strrchr(grand_parent, '/')) != NULL) { | |
3508 *(parent++) = '\0'; | |
3509 x = insert_tag_to_parent_tag(start, grand_parent, parent); | |
3510 } else { | |
3511 x = xmlnode_insert_tag(start, grand_parent); | |
3512 } | |
3513 g_free(grand_parent); | |
3514 } else { | |
3515 /* | |
3516 * We found *something* to be the parent node. | |
3517 * Note: may be the "root" node! | |
3518 */ | |
3519 xmlnode y; | |
3520 if((y = xmlnode_get_tag(x, new_tag)) != NULL) { | |
3521 return(y); | |
3522 } | |
3523 } | |
3524 } | |
3525 | |
3526 /* | |
3527 * insert the new tag into its parent node | |
3528 */ | |
3529 return(xmlnode_insert_tag((x == NULL? start : x), new_tag)); | |
3530 } | |
3531 | |
3532 /* | |
3533 * Find the tag name for a label | |
3534 * | |
3535 * Returns NULL on not found | |
3536 */ | |
3537 static char *tag_for_label(const char *label) | |
3538 { | |
3539 struct vcard_template *vc_tp = vcard_template_data; | |
3540 char *p = NULL; | |
3541 | |
3542 for(vc_tp = vcard_template_data; vc_tp->label != NULL; ++vc_tp) { | |
3543 if(strcmp(label, vc_tp->label) == 0) { | |
3544 p = vc_tp->tag; | |
3545 break; | |
3546 } | |
3547 } | |
3548 | |
3549 return(p); | |
3550 } | |
3551 | |
3552 /* | |
3553 * Send vCard info to Jabber server | |
3554 */ | |
3555 static void jabber_set_info(struct gaim_connection *gc, char *info) | |
3556 { | |
3557 xmlnode x, vc_node; | |
3558 char *id; | |
3559 struct jabber_data *jd = gc->proto_data; | |
3560 gjconn gjc = jd->gjc; | |
3561 | |
3562 x = xmlnode_new_tag("iq"); | |
3311 | 3563 xmlnode_put_attrib(x, "type", "set"); |
2956 | 3564 |
3565 id = gjab_getid(gjc); | |
3566 | |
3567 xmlnode_put_attrib(x, "id", id); | |
3568 | |
3569 /* | |
3570 * Send only if there's actually any *information* to send | |
3571 */ | |
3572 if((vc_node = xmlstr2xmlnode(info)) != NULL && xmlnode_get_name(vc_node) != NULL && | |
3573 g_strncasecmp(xmlnode_get_name(vc_node), "vcard", 5) == 0) { | |
3574 xmlnode_insert_tag_node(x, vc_node); | |
3575 debug_printf("jabber: vCard packet: %s\n", xmlnode2str(x)); | |
3576 gjab_send(gjc, x); | |
3577 } | |
3578 | |
3579 xmlnode_free(x); | |
3580 } | |
3581 | |
3582 /* | |
3583 * This is the callback from the "ok clicked" for "set vCard" | |
3584 * | |
3585 * Formats GSList data into XML-encoded string and returns a pointer | |
3586 * to said string. | |
3587 * | |
3588 * g_free()'ing the returned string space is the responsibility of | |
3589 * the caller. | |
3590 */ | |
3591 static gchar *jabber_format_info(MultiEntryDlg *b) | |
3592 { | |
3593 xmlnode vc_node; | |
3594 GSList *list; | |
3595 MultiEntryData *med; | |
3596 MultiTextData *mtd; | |
3597 char *p; | |
3598 | |
3599 struct tag_attr *tag_attr; | |
3600 | |
3601 vc_node = xmlnode_new_tag("vCard"); | |
3602 | |
3603 for(tag_attr = vcard_tag_attr_list; tag_attr->attr != NULL; ++tag_attr) | |
3604 xmlnode_put_attrib(vc_node, tag_attr->attr, tag_attr->value); | |
3605 | |
3606 for(list = b->multi_entry_items; list != NULL; list = list->next) { | |
3607 med = (MultiEntryData *) list->data; | |
3608 if(med->label != NULL && med->text != NULL && (med->text)[0] != '\0') { | |
3609 if((p = tag_for_label(med->label)) != NULL) { | |
3610 xmlnode xp; | |
3611 if((xp = insert_tag_to_parent_tag(vc_node, NULL, p)) != NULL) { | |
3612 xmlnode_insert_cdata(xp, med->text, -1); | |
3613 } | |
3614 } | |
3615 } | |
3616 } | |
3617 | |
3618 for(list = b->multi_text_items; list != NULL; list = list->next) { | |
3619 mtd = (MultiTextData *) list->data; | |
3620 if(mtd->label != NULL && mtd->text != NULL && (mtd->text)[0] != '\0') { | |
3621 if((p = tag_for_label(mtd->label)) != NULL) { | |
3622 xmlnode xp; | |
3623 if((xp = insert_tag_to_parent_tag(vc_node, NULL, p)) != NULL) { | |
3624 xmlnode_insert_cdata(xp, mtd->text, -1); | |
3625 } | |
3626 } | |
3627 } | |
3628 } | |
3629 | |
3630 | |
3631 p = g_strdup(xmlnode2str(vc_node)); | |
3632 xmlnode_free(vc_node); | |
3633 | |
3634 return(p); | |
3635 } | |
3636 | |
3637 /* | |
3638 * This gets executed by the proto action | |
3639 * | |
3640 * Creates a new MultiEntryDlg struct, gets the XML-formatted user_info | |
3641 * string (if any) into GSLists for the (multi-entry) edit dialog and | |
3642 * calls the set_vcard dialog. | |
3643 */ | |
3644 static void jabber_setup_set_info(struct gaim_connection *gc) | |
3645 { | |
3646 MultiEntryData *data; | |
3647 const struct vcard_template *vc_tp; | |
3648 char *user_info; | |
3649 MultiEntryDlg *b = multi_entry_dialog_new(); | |
3650 char *cdata; | |
3651 xmlnode x_vc_data = NULL; | |
3652 struct aim_user *tmp = gc->user; | |
3653 b->user = tmp; | |
3654 | |
3655 | |
3656 /* | |
3657 * Get existing, XML-formatted, user info | |
3658 */ | |
3659 if((user_info = g_malloc(strlen(tmp->user_info) + 1)) != NULL) { | |
3660 strcpy(user_info, tmp->user_info); | |
3661 x_vc_data = xmlstr2xmlnode(user_info); | |
3662 } | |
3663 | |
3664 /* | |
3665 * Set up GSLists for edit with labels from "template," data from user info | |
3666 */ | |
3667 for(vc_tp = vcard_template_data; vc_tp->label != NULL; ++vc_tp) { | |
3668 if((vc_tp->label)[0] == '\0') | |
3669 continue; | |
3670 if(vc_tp->ptag == NULL) { | |
3671 cdata = xmlnode_get_tag_data(x_vc_data, vc_tp->tag); | |
3672 } else { | |
3673 gchar *tag = g_strdup_printf("%s/%s", vc_tp->ptag, vc_tp->tag); | |
3674 cdata = xmlnode_get_tag_data(x_vc_data, tag); | |
3675 g_free(tag); | |
3676 } | |
3677 if(strcmp(vc_tp->tag, "DESC") == 0) { | |
3678 multi_text_list_update(&(b->multi_text_items), | |
3679 vc_tp->label, cdata, TRUE); | |
3680 } else { | |
3681 data = multi_entry_list_update(&(b->multi_entry_items), | |
3682 vc_tp->label, cdata, TRUE); | |
3683 data->visible = vc_tp->visible; | |
3684 data->editable = vc_tp->editable; | |
3685 } | |
3686 } | |
3687 | |
3688 | |
3689 if(x_vc_data != NULL) { | |
3690 xmlnode_free(x_vc_data); | |
3691 } else { | |
3692 /* | |
3693 * Early Beta versions had a different user_info storage format--let's | |
3694 * see if that works. | |
3695 * | |
3696 * This goes away RSN. | |
3697 */ | |
3698 const char *record_separator = "<BR>"; | |
3699 const char *field_separator = ": "; | |
3700 gchar **str_list, **str_list_ptr, **str_list2; | |
3701 | |
3702 if((str_list = g_strsplit(user_info, record_separator, 0)) != NULL) { | |
3703 for(str_list_ptr = str_list; *str_list_ptr != NULL; ++str_list_ptr) { | |
3704 str_list2 = g_strsplit(*str_list_ptr, field_separator, 2); | |
3705 if(str_list2[0] != NULL && str_list2[1] != NULL) { | |
3706 g_strstrip(str_list2[0]); | |
3707 g_strstrip(str_list2[1]); | |
3708 /* this is ugly--so far */ | |
3709 if(strcmp(str_list2[0], "Description") == 0) { | |
3710 multi_text_list_update(&(b->multi_text_items), | |
3711 str_list2[0], str_list2[1], FALSE); | |
3712 } else { | |
3713 multi_entry_list_update(&(b->multi_entry_items), | |
3714 str_list2[0], str_list2[1], FALSE); | |
3715 } | |
3716 } | |
3717 g_strfreev(str_list2); | |
3718 } | |
3719 g_strfreev(str_list); | |
3720 } | |
3721 } | |
3722 | |
3723 if(user_info != NULL) { | |
3724 g_free(user_info); | |
3725 } | |
3726 | |
2975 | 3727 b->title = _("Gaim - Edit Jabber vCard"); |
2956 | 3728 b->wmclass_name = "set_info"; |
3729 b->wmclass_class = "Gaim"; | |
3730 b->instructions->text = g_strdup(multi_entry_instructions); | |
2975 | 3731 b->entries_title = g_strdup(entries_title); |
2956 | 3732 |
3733 b->custom = (void *) jabber_format_info; | |
3734 | |
3735 show_set_vcard(b); | |
3736 } | |
3737 | |
3738 /*---------------------------------------*/ | |
3739 /* End Jabber "set info" (vCard) support */ | |
3740 /*---------------------------------------*/ | |
3741 | |
3742 /*----------------------------------------*/ | |
3743 /* Jabber "user registration" support */ | |
3744 /*----------------------------------------*/ | |
3745 | |
3746 /* | |
3747 * Three of the following four functions duplicate much of what | |
3748 * exists elsewhere: | |
3749 * | |
3750 * jabber_handleregresp() | |
3751 * gjab_reqreg() | |
3752 * jabber_handle_registration_state() | |
3753 * | |
3754 * It may be that an additional flag could be added to one of | |
3755 * the "local" structs and the duplicated code modified to | |
3756 * account for it--thus eliminating the duplication. Then again: | |
3757 * doing it the way it is may be much cleaner. | |
3758 * | |
3759 * TBD: Code to support requesting additional information server | |
3760 * wants at registration--incl. dialog. | |
3761 */ | |
3762 | |
3763 /* | |
3764 * Like jabber_handlepacket(), only different | |
3765 */ | |
3766 static void jabber_handleregresp(gjconn gjc, jpacket p) | |
3767 { | |
3768 if (jpacket_subtype(p) == JPACKET__RESULT) { | |
3769 xmlnode querynode; | |
3770 | |
3771 if((querynode = xmlnode_get_tag(p->x, "query")) != NULL) { | |
3772 char *xmlns; | |
3773 | |
3774 /* we damn well *better* have this! */ | |
3775 if((xmlns = xmlnode_get_attrib(querynode, "xmlns")) != NULL && | |
3776 strcmp(xmlns, NS_REGISTER) == 0) { | |
3777 | |
3778 char *tag; | |
3779 xmlnode child = xmlnode_get_firstchild(querynode); | |
3780 | |
3781 debug_printf("got registration requirments response!\n"); | |
3782 | |
3783 while(child != NULL) { | |
3784 if((tag = xmlnode_get_name(child)) != NULL) { | |
3785 char *data; | |
3786 | |
3787 fprintf(stderr, "DBG: got node: \"%s\"\n", tag); | |
3788 fflush(stderr); | |
3789 | |
3790 if((data = xmlnode_get_data(child)) != NULL) { | |
3791 fprintf(stderr, "DBG: got data: \"%s\"\n", data); | |
3792 fflush(stderr); | |
3793 } | |
3794 } | |
3795 child = xmlnode_get_nextsibling(child); | |
3796 } | |
3797 } | |
3798 } else { | |
3799 debug_printf("registration successful!\n"); | |
3800 | |
2975 | 3801 hide_login_progress_notice(GJ_GC(gjc), _("Server Registration successful!")); |
2956 | 3802 /* |
3803 * TBD: is this the correct way to do away with a | |
3804 * gaim_connection and all its associated memory | |
3805 * allocs, etc.? | |
3806 */ | |
3807 signoff(GJ_GC(gjc)); | |
3808 } | |
3809 | |
3810 } else { | |
3811 xmlnode xerr; | |
3812 char *errmsg = NULL; | |
3813 int errcode = 0; | |
3814 struct jabber_data *jd = GJ_GC(gjc)->proto_data; | |
3815 | |
3816 debug_printf("registration failed\n"); | |
3817 xerr = xmlnode_get_tag(p->x, "error"); | |
3818 if (xerr) { | |
3819 char msg[BUF_LONG]; | |
3820 errmsg = xmlnode_get_data(xerr); | |
3821 if (xmlnode_get_attrib(xerr, "code")) { | |
3822 errcode = atoi(xmlnode_get_attrib(xerr, "code")); | |
3823 g_snprintf(msg, sizeof(msg), "Error %d: %s", errcode, errmsg); | |
3824 } else | |
3825 g_snprintf(msg, sizeof(msg), "%s", errmsg); | |
3826 hide_login_progress(GJ_GC(gjc), msg); | |
3827 } else { | |
2975 | 3828 hide_login_progress(GJ_GC(gjc), _("Unknown registration error")); |
2956 | 3829 } |
3830 | |
3831 jd->die = TRUE; | |
3832 } | |
3833 } | |
3834 | |
3835 /* | |
3836 * Like gjab_reqauth(), only different | |
3837 */ | |
3838 static void gjab_reqreg(gjconn gjc) | |
3839 { | |
3840 xmlnode x, y, z; | |
3841 char *user; | |
3842 | |
3843 if (!gjc) | |
3844 return; | |
3845 | |
3846 x = jutil_iqnew(JPACKET__SET, NS_REGISTER); | |
3847 y = xmlnode_get_tag(x, "query"); | |
3848 | |
3849 user = gjc->user->user; | |
3850 | |
3851 if (user) { | |
3852 z = xmlnode_insert_tag(y, "username"); | |
3853 xmlnode_insert_cdata(z, user, -1); | |
3854 } | |
3855 z = xmlnode_insert_tag(y, "password"); | |
3856 xmlnode_insert_cdata(z, gjc->pass, -1); | |
3857 | |
3858 debug_printf("jabber: registration packet: %s\n", xmlnode2str(x)); | |
3859 gjab_send(gjc, x); | |
3860 xmlnode_free(x); | |
3861 } | |
3862 | |
3863 /* | |
3864 * Like jabber_handlestate(), only different | |
3865 */ | |
3866 static void jabber_handle_registration_state(gjconn gjc, int state) | |
3867 { | |
3868 switch (state) { | |
3869 case JCONN_STATE_OFF: | |
3074 | 3870 if(gjc->was_connected) { |
3871 hide_login_progress_error(GJ_GC(gjc), _("Connection lost")); | |
3872 } else { | |
3873 hide_login_progress(GJ_GC(gjc), _("Unable to connect")); | |
3874 } | |
2956 | 3875 signoff(GJ_GC(gjc)); |
3876 break; | |
3877 case JCONN_STATE_CONNECTED: | |
3074 | 3878 gjc->was_connected = 1; |
2956 | 3879 /* |
3880 * TBD? | |
2975 | 3881 set_login_progress(GJ_GC(gjc), 2, _("Connected")); |
2956 | 3882 */ |
3883 break; | |
3884 case JCONN_STATE_ON: | |
3885 /* | |
3886 * TBD? | |
2975 | 3887 set_login_progress(GJ_GC(gjc), 3, _("Requesting Authentication Method")); |
2956 | 3888 */ |
3889 gjab_reqreg(gjc); | |
3890 /* | |
3891 * TBD: A work-in-progress | |
3892 gjab_reqregreqs(gjc); | |
3893 */ | |
3894 break; | |
3895 default: | |
3896 debug_printf("state change: %d\n", state); | |
3897 } | |
3898 return; | |
3899 } | |
3900 | |
3901 /* | |
3902 * Like jabber_login(), only different | |
3903 */ | |
3904 void jabber_register_user(struct aim_user *au) | |
3905 { | |
3906 struct gaim_connection *gc = new_gaim_conn(au); | |
3907 struct jabber_data *jd = gc->proto_data = g_new0(struct jabber_data, 1); | |
3908 char *loginname = create_valid_jid(au->username, DEFAULT_SERVER, "GAIM"); | |
3909 | |
3910 /* | |
3911 * These do nothing during registration | |
3912 */ | |
3311 | 3913 jd->buddies = NULL; |
2956 | 3914 jd->chats = NULL; |
3915 | |
3916 if ((jd->gjc = gjab_new(loginname, au->password, gc)) == NULL) { | |
3917 g_free(loginname); | |
3918 debug_printf("jabber: unable to connect (jab_new failed)\n"); | |
2975 | 3919 hide_login_progress(gc, _("Unable to connect")); |
2956 | 3920 signoff(gc); |
3921 } else { | |
3922 gjab_state_handler(jd->gjc, jabber_handle_registration_state); | |
3923 gjab_packet_handler(jd->gjc, jabber_handleregresp); | |
3924 jd->gjc->queries = NULL; | |
3925 gjab_start(jd->gjc); | |
3926 } | |
3927 | |
3928 g_free(loginname); | |
3929 } | |
3930 | |
3931 /*----------------------------------------*/ | |
3932 /* End Jabber "user registration" support */ | |
3933 /*----------------------------------------*/ | |
3934 | |
3935 static void jabber_do_action(struct gaim_connection *gc, char *act) | |
3936 { | |
2975 | 3937 if (!strcmp(act, _("Set User Info"))) { |
2956 | 3938 jabber_setup_set_info(gc); |
3939 /* | |
2975 | 3940 } else if (!strcmp(act, _("Set Dir Info"))) { |
2956 | 3941 show_set_dir(gc); |
3257 | 3942 */ |
2975 | 3943 } else if (!strcmp(act, _("Change Password"))) { |
2956 | 3944 show_change_passwd(gc); |
3945 } | |
3946 } | |
3947 | |
3948 static GList *jabber_actions() | |
3949 { | |
3950 GList *m = NULL; | |
3951 | |
2975 | 3952 m = g_list_append(m, _("Set User Info")); |
2956 | 3953 /* |
2975 | 3954 m = g_list_append(m, _("Set Dir Info")); |
3257 | 3955 */ |
2975 | 3956 m = g_list_append(m, _("Change Password")); |
2956 | 3957 |
3958 return m; | |
3959 } | |
3960 | |
2086 | 3961 static struct prpl *my_protocol = NULL; |
3962 | |
3963 void jabber_init(struct prpl *ret) | |
3964 { | |
3965 /* the NULL's aren't required but they're nice to have */ | |
3572 | 3966 struct proto_user_opt *puo; |
2086 | 3967 ret->protocol = PROTO_JABBER; |
3968 ret->options = OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_CHAT_TOPIC; | |
3572 | 3969 ret->name = g_strdup("Jabber"); |
2086 | 3970 ret->list_icon = jabber_list_icon; |
3971 ret->away_states = jabber_away_states; | |
2956 | 3972 ret->actions = jabber_actions; |
3973 ret->do_action = jabber_do_action; | |
2086 | 3974 ret->buddy_menu = jabber_buddy_menu; |
3314 | 3975 ret->edit_buddy_menu = jabber_edit_buddy_menu; |
2086 | 3976 ret->login = jabber_login; |
3977 ret->close = jabber_close; | |
3978 ret->send_im = jabber_send_im; | |
2956 | 3979 ret->set_info = jabber_set_info; |
2086 | 3980 ret->get_info = jabber_get_info; |
2956 | 3981 ret->get_cb_info = jabber_get_cb_info; |
3982 ret->get_cb_away = jabber_get_cb_away_msg; | |
2086 | 3983 ret->set_away = jabber_set_away; |
3984 ret->set_dir = NULL; | |
3985 ret->get_dir = NULL; | |
3986 ret->dir_search = NULL; | |
3987 ret->set_idle = jabber_set_idle; | |
3257 | 3988 ret->change_passwd = jabber_change_passwd; |
2086 | 3989 ret->add_buddy = jabber_add_buddy; |
3990 ret->add_buddies = NULL; | |
3991 ret->remove_buddy = jabber_remove_buddy; | |
3992 ret->add_permit = NULL; | |
3993 ret->add_deny = NULL; | |
3994 ret->rem_permit = NULL; | |
3995 ret->rem_deny = NULL; | |
3996 ret->set_permit_deny = NULL; | |
3997 ret->warn = NULL; | |
2205
cff4fbe01c7b
[gaim-migrate @ 2215]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2170
diff
changeset
|
3998 ret->chat_info = jabber_chat_info; |
2086 | 3999 ret->join_chat = jabber_join_chat; |
4000 ret->chat_invite = jabber_chat_invite; | |
4001 ret->chat_leave = jabber_chat_leave; | |
4002 ret->chat_whisper = jabber_chat_whisper; | |
4003 ret->chat_send = jabber_chat_send; | |
4004 ret->keepalive = jabber_keepalive; | |
4005 ret->normalize = jabber_normalize; | |
2956 | 4006 ret->register_user = jabber_register_user; |
3349 | 4007 ret->alias_buddy = jabber_alias_buddy; |
3136 | 4008 ret->group_buddy = jabber_group_change; |
3311 | 4009 ret->send_typing = jabber_send_typing; |
4010 ret->convo_closed = jabber_convo_closed; | |
3349 | 4011 ret->rename_group = jabber_rename_group; |
3572 | 4012 |
4013 puo = g_new0(struct proto_user_opt, 1); | |
4014 puo->label = g_strdup("Port:"); | |
4015 puo->def = g_strdup("5222"); | |
4016 puo->pos = USEROPT_PORT; | |
4017 ret->user_opts = g_list_append(ret->user_opts, puo); | |
2086 | 4018 |
4019 my_protocol = ret; | |
4020 } | |
4021 | |
4022 #ifndef STATIC | |
4023 | |
3572 | 4024 void *gaim_prpl_init(struct prpl *prpl) |
2086 | 4025 { |
3572 | 4026 jabber_init(prpl); |
4027 prpl->plug->desc.api_version = PLUGIN_API_VERSION; | |
2086 | 4028 } |
4029 | |
4030 #endif | |
3311 | 4031 |
4032 /* | |
4033 * Local variables: | |
4034 * c-indentation-style: k&r | |
4035 * c-basic-offset: 8 | |
4036 * indent-tabs-mode: notnil | |
4037 * End: | |
4038 * | |
4039 * vim: shiftwidth=8: | |
4040 */ |