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