Changeset 85d7b85 for protocols/jabber/jabber.c
- Timestamp:
- 2008-04-02T14:22:57Z (17 years ago)
- Branches:
- master
- Children:
- f9dbc99
- Parents:
- 875ad42 (diff), dd34575 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
protocols/jabber/jabber.c
r875ad42 r85d7b85 1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ 2 /* 3 * gaim 4 * 5 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> 6 * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 */ 23 24 #ifndef _WIN32 25 #include <sys/utsname.h> 26 #endif 27 #include <errno.h> 1 /***************************************************************************\ 2 * * 3 * BitlBee - An IRC to IM gateway * 4 * Jabber module - Main file * 5 * * 6 * Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * 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 along * 19 * with this program; if not, write to the Free Software Foundation, Inc., * 20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 21 * * 22 \***************************************************************************/ 23 24 #include <glib.h> 28 25 #include <string.h> 29 #include <stdlib.h> 26 #include <unistd.h> 27 #include <ctype.h> 30 28 #include <stdio.h> 31 #include <time.h> 32 #include <sys/stat.h> 29 30 #include "ssl_client.h" 31 #include "xmltree.h" 32 #include "bitlbee.h" 33 33 #include "jabber.h" 34 #include "nogaim.h" 35 #include "bitlbee.h" 36 #include "proxy.h" 37 #include "ssl_client.h" 38 39 /* The priv member of gjconn's is a gaim_connection for now. */ 40 #define GJ_GC(x) ((struct gaim_connection *)(x)->priv) 41 42 #define IQID_AUTH "__AUTH__" 43 44 #define IQ_NONE -1 45 #define IQ_AUTH 0 46 #define IQ_ROSTER 1 47 48 #define UC_AWAY (0x02 | UC_UNAVAILABLE) 49 #define UC_CHAT 0x04 50 #define UC_XA (0x08 | UC_UNAVAILABLE) 51 #define UC_DND (0x10 | UC_UNAVAILABLE) 52 53 #define DEFAULT_SERVER "jabber.org" 54 #define DEFAULT_GROUPCHAT "conference.jabber.org" 55 #define DEFAULT_PORT 5222 56 #define DEFAULT_PORT_SSL 5223 57 #define JABBER_PORT_MIN 5220 58 #define JABBER_PORT_MAX 5229 59 60 #define JABBER_GROUP "Friends" 61 62 /* i18n disabled - Bitlbee */ 63 #define N_(String) String 64 65 /* 66 * Note: "was_connected" may seem redundant, but it was needed and I 67 * didn't want to touch the Jabber state stuff not specific to Gaim. 68 */ 69 typedef struct gjconn_struct { 70 /* Core structure */ 71 pool p; /* Memory allocation pool */ 72 int state; /* Connection state flag */ 73 int was_connected; /* We were once connected */ 74 int fd; /* Connection file descriptor */ 75 void *ssl; /* SSL connection */ 76 jid user; /* User info */ 77 char *pass; /* User passwd */ 78 79 /* Stream stuff */ 80 int id; /* id counter for jab_getid() function */ 81 char idbuf[9]; /* temporary storage for jab_getid() */ 82 char *sid; /* stream id from server, for digest auth */ 83 XML_Parser parser; /* Parser instance */ 84 xmlnode current; /* Current node in parsing instance.. */ 85 86 /* Event callback ptrs */ 87 void (*on_state)(struct gjconn_struct *gjc, int state); 88 void (*on_packet)(struct gjconn_struct *gjc, jpacket p); 89 90 GHashTable *queries; /* query tracker */ 91 92 void *priv; 93 } *gjconn, gjconn_struct; 94 95 typedef void (*gjconn_state_h)(gjconn gjc, int state); 96 typedef void (*gjconn_packet_h)(gjconn gjc, jpacket p); 97 98 static gjconn gjab_new(char *user, char *pass, void *priv); 99 static void gjab_delete(gjconn gjc); 100 static void gjab_state_handler(gjconn gjc, gjconn_state_h h); 101 static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h); 102 static void gjab_start(gjconn gjc); 103 static void gjab_stop(gjconn gjc); 104 /* 105 static int gjab_getfd(gjconn gjc); 106 static jid gjab_getjid(gjconn gjc); 107 static char *gjab_getsid(gjconn gjc); 108 */ 109 static char *gjab_getid(gjconn gjc); 110 static void gjab_send(gjconn gjc, xmlnode x); 111 static void gjab_send_raw(gjconn gjc, const char *str); 112 static void gjab_recv(gjconn gjc); 113 static void gjab_auth(gjconn gjc); 114 115 /* 116 * It is *this* to which we point the gaim_connection proto_data 117 */ 118 struct jabber_data { 119 gjconn gjc; 120 gboolean did_import; 121 GSList *chats; 122 GHashTable *hash; 123 time_t idle; 124 gboolean die; 125 }; 126 127 /* 128 * Jabber "chat group" info. Pointers to these go in jabber_data 129 * pending and existing chats lists. 130 */ 131 struct jabber_chat { 132 jid Jid; 133 struct gaim_connection *gc; 134 struct conversation *b; 135 int id; 136 int state; 137 }; 138 139 /* 140 * Jabber chat states... 141 * 142 * Note: due to a bug in one version of the Jabber server, subscriptions 143 * to chat groups aren't (always?) properly removed at the server. The 144 * result is clients receive Jabber "presence" notifications for JIDs 145 * they no longer care about. The problem with such vestigial notifies is 146 * that we really have no way of telling if it's vestigial or if it's a 147 * valid "buddy" presence notification. So we keep jabber_chat structs 148 * around after leaving a chat group and simply mark them "closed." That 149 * way we can test for such errant presence notifications. I.e.: if we 150 * get a presence notfication from a JID that matches a chat group JID, 151 * we disregard it. 152 */ 153 #define JCS_PENDING 1 /* pending */ 154 #define JCS_ACTIVE 2 /* active */ 155 #define JCS_CLOSED 3 /* closed */ 156 157 158 #define STATE_EVT(arg) if(gjc->on_state) { (gjc->on_state)(gjc, (arg) ); } 159 160 static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group); 161 static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from); 162 163 static char *create_valid_jid(const char *given, char *server, char *resource) 164 { 165 char *valid; 166 167 if (!strchr(given, '@')) 168 valid = g_strdup_printf("%s@%s/%s", given, server, resource); 169 else if (!strchr(strchr(given, '@'), '/')) 170 valid = g_strdup_printf("%s/%s", given, resource); 171 else 172 valid = g_strdup(given); 173 174 return valid; 175 } 176 177 static gjconn gjab_new(char *user, char *pass, void *priv) 178 { 179 pool p; 180 gjconn gjc; 181 182 if (!user) 183 return (NULL); 184 185 p = pool_new(); 186 if (!p) 187 return (NULL); 188 gjc = pmalloc_x(p, sizeof(gjconn_struct), 0); 189 if (!gjc) { 190 pool_free(p); /* no need for this anymore! */ 191 return (NULL); 192 } 193 gjc->p = p; 194 195 if((gjc->user = jid_new(p, user)) == NULL) { 196 pool_free(p); /* no need for this anymore! */ 197 return (NULL); 198 } 199 gjc->pass = pstrdup(p, pass); 200 201 gjc->state = JCONN_STATE_OFF; 202 gjc->was_connected = 0; 203 gjc->id = 1; 204 gjc->fd = -1; 205 206 gjc->priv = priv; 207 208 return gjc; 209 } 210 211 static void gjab_delete(gjconn gjc) 212 { 213 if (!gjc) 34 #include "md5.h" 35 #include "base64.h" 36 37 GSList *jabber_connections; 38 39 static void jabber_init( account_t *acc ) 40 { 41 set_t *s; 42 43 s = set_add( &acc->set, "port", JABBER_PORT_DEFAULT, set_eval_int, acc ); 44 s->flags |= ACC_SET_OFFLINE_ONLY; 45 46 s = set_add( &acc->set, "priority", "0", set_eval_priority, acc ); 47 48 s = set_add( &acc->set, "resource", "BitlBee", NULL, acc ); 49 s->flags |= ACC_SET_OFFLINE_ONLY; 50 51 s = set_add( &acc->set, "resource_select", "priority", NULL, acc ); 52 53 s = set_add( &acc->set, "server", NULL, set_eval_account, acc ); 54 s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; 55 56 s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc ); 57 s->flags |= ACC_SET_OFFLINE_ONLY; 58 59 s = set_add( &acc->set, "tls", "try", set_eval_tls, acc ); 60 s->flags |= ACC_SET_OFFLINE_ONLY; 61 62 s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc ); 63 s->flags |= ACC_SET_OFFLINE_ONLY; 64 } 65 66 static void jabber_generate_id_hash( struct jabber_data *jd ); 67 68 static void jabber_login( account_t *acc ) 69 { 70 struct im_connection *ic = imcb_new( acc ); 71 struct jabber_data *jd = g_new0( struct jabber_data, 1 ); 72 struct ns_srv_reply *srv = NULL; 73 char *connect_to, *s; 74 75 /* For now this is needed in the _connected() handlers if using 76 GLib event handling, to make sure we're not handling events 77 on dead connections. */ 78 jabber_connections = g_slist_prepend( jabber_connections, ic ); 79 80 jd->ic = ic; 81 ic->proto_data = jd; 82 83 jd->username = g_strdup( acc->user ); 84 jd->server = strchr( jd->username, '@' ); 85 86 jd->fd = jd->r_inpa = jd->w_inpa = -1; 87 88 if( jd->server == NULL ) 89 { 90 imcb_error( ic, "Incomplete account name (format it like <username@jabberserver.name>)" ); 91 imc_logout( ic, FALSE ); 214 92 return; 215 216 gjab_stop(gjc); 217 pool_free(gjc->p); 218 } 219 220 static void gjab_state_handler(gjconn gjc, gjconn_state_h h) 221 { 222 if (!gjc) 223 return; 224 225 gjc->on_state = h; 226 } 227 228 static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h) 229 { 230 if (!gjc) 231 return; 232 233 gjc->on_packet = h; 234 } 235 236 static void gjab_stop(gjconn gjc) 237 { 238 if (!gjc || gjc->state == JCONN_STATE_OFF) 239 return; 240 241 gjab_send_raw(gjc, "</stream:stream>"); 242 gjc->state = JCONN_STATE_OFF; 243 gjc->was_connected = 0; 244 if (gjc->ssl) { 245 ssl_disconnect(gjc->ssl); 246 gjc->ssl = NULL; 247 } else { 248 closesocket(gjc->fd); 249 } 250 gjc->fd = -1; 251 XML_ParserFree(gjc->parser); 252 gjc->parser = NULL; 253 } 254 255 /* 256 static int gjab_getfd(gjconn gjc) 257 { 258 if (gjc) 259 return gjc->fd; 260 else 261 return -1; 262 } 263 264 static jid gjab_getjid(gjconn gjc) 265 { 266 if (gjc) 267 return (gjc->user); 268 else 269 return NULL; 270 } 271 272 static char *gjab_getsid(gjconn gjc) 273 { 274 if (gjc) 275 return (gjc->sid); 276 else 277 return NULL; 278 } 279 */ 280 281 static char *gjab_getid(gjconn gjc) 282 { 283 g_snprintf(gjc->idbuf, 8, "%d", gjc->id++); 284 return &gjc->idbuf[0]; 285 } 286 287 static void gjab_send(gjconn gjc, xmlnode x) 288 { 289 if (gjc && gjc->state != JCONN_STATE_OFF) { 290 char *buf = xmlnode2str(x); 291 if (!buf) 292 return; 293 else if (gjc->ssl) 294 ssl_write(gjc->ssl, buf, strlen(buf)); 295 else 296 write(gjc->fd, buf, strlen(buf)); 297 } 298 } 299 300 static void gjab_send_raw(gjconn gjc, const char *str) 301 { 302 if (gjc && gjc->state != JCONN_STATE_OFF) { 303 int len; 304 305 /* 306 * JFIXME: No error detection?!?! 307 */ 308 if (gjc->ssl) 309 len = ssl_write(gjc->ssl, str, strlen(str)); 310 else 311 len = write(gjc->fd, str, strlen(str)); 93 } 94 95 /* So don't think of free()ing jd->server.. :-) */ 96 *jd->server = 0; 97 jd->server ++; 98 99 if( ( s = strchr( jd->server, '/' ) ) ) 100 { 101 *s = 0; 102 set_setstr( &acc->set, "resource", s + 1 ); 103 104 /* Also remove the /resource from the original variable so we 105 won't have to do this again every time. */ 106 s = strchr( acc->user, '/' ); 107 *s = 0; 108 } 109 110 /* This code isn't really pretty. Backwards compatibility never is... */ 111 s = acc->server; 112 while( s ) 113 { 114 static int had_port = 0; 115 116 if( strncmp( s, "ssl", 3 ) == 0 ) 117 { 118 set_setstr( &acc->set, "ssl", "true" ); 312 119 313 if(len < 0) { 314 /* Do NOT write to stdout/stderr directly, IRC clients 315 might get confused, and we don't want that... 316 fprintf(stderr, "DBG: Problem sending. Error: %d\n", errno); 317 fflush(stderr); */ 120 /* Flush this part so that (if this was the first 121 part of the server string) acc->server gets 122 flushed. We don't want to have to do this another 123 time. :-) */ 124 *s = 0; 125 s ++; 126 127 /* Only set this if the user didn't specify a custom 128 port number already... */ 129 if( !had_port ) 130 set_setint( &acc->set, "port", 5223 ); 318 131 } 319 } 320 } 321 322 static void gjab_reqroster(gjconn gjc) 323 { 324 xmlnode x; 325 326 x = jutil_iqnew(JPACKET__GET, NS_ROSTER); 327 xmlnode_put_attrib(x, "id", gjab_getid(gjc)); 328 329 gjab_send(gjc, x); 330 xmlnode_free(x); 331 } 332 333 static void gjab_reqauth(gjconn gjc) 334 { 335 xmlnode x, y, z; 336 char *user; 337 338 if (!gjc) 339 return; 340 341 x = jutil_iqnew(JPACKET__GET, NS_AUTH); 342 xmlnode_put_attrib(x, "id", IQID_AUTH); 343 y = xmlnode_get_tag(x, "query"); 344 345 user = gjc->user->user; 346 347 if (user) { 348 z = xmlnode_insert_tag(y, "username"); 349 xmlnode_insert_cdata(z, user, -1); 350 } 351 352 gjab_send(gjc, x); 353 xmlnode_free(x); 354 } 355 356 static void gjab_auth(gjconn gjc) 357 { 358 xmlnode x, y, z; 359 char *hash, *user; 360 361 if (!gjc) 362 return; 363 364 x = jutil_iqnew(JPACKET__SET, NS_AUTH); 365 xmlnode_put_attrib(x, "id", IQID_AUTH); 366 y = xmlnode_get_tag(x, "query"); 367 368 user = gjc->user->user; 369 370 if (user) { 371 z = xmlnode_insert_tag(y, "username"); 372 xmlnode_insert_cdata(z, user, -1); 373 } 374 375 z = xmlnode_insert_tag(y, "resource"); 376 xmlnode_insert_cdata(z, gjc->user->resource, -1); 377 378 if (gjc->sid) { 379 z = xmlnode_insert_tag(y, "digest"); 380 hash = pmalloc(x->p, strlen(gjc->sid) + strlen(gjc->pass) + 1); 381 strcpy(hash, gjc->sid); 382 strcat(hash, gjc->pass); 383 hash = shahash(hash); 384 xmlnode_insert_cdata(z, hash, 40); 385 } else { 386 z = xmlnode_insert_tag(y, "password"); 387 xmlnode_insert_cdata(z, gjc->pass, -1); 388 } 389 390 gjab_send(gjc, x); 391 xmlnode_free(x); 392 393 return; 394 } 395 396 static void gjab_recv(gjconn gjc) 397 { 398 static char buf[4096]; 399 int len; 400 401 if (!gjc || gjc->state == JCONN_STATE_OFF) 402 return; 403 404 if (gjc->ssl) 405 len = ssl_read(gjc->ssl, buf, sizeof(buf) - 1); 406 else 407 len = read(gjc->fd, buf, sizeof(buf) - 1); 408 409 if (len > 0) { 410 struct jabber_data *jd = GJ_GC(gjc)->proto_data; 411 buf[len] = '\0'; 412 XML_Parse(gjc->parser, buf, len, 0); 413 if (jd->die) 414 signoff(GJ_GC(gjc)); 415 } else if (len == 0 || (len < 0 && (!sockerr_again() || gjc->ssl))) { 416 STATE_EVT(JCONN_STATE_OFF) 417 } 418 } 419 420 static void startElement(void *userdata, const char *name, const char **attribs) 421 { 422 xmlnode x; 423 gjconn gjc = (gjconn) userdata; 424 425 if (gjc->current) { 426 /* Append the node to the current one */ 427 x = xmlnode_insert_tag(gjc->current, name); 428 xmlnode_put_expat_attribs(x, attribs); 429 430 gjc->current = x; 431 } else { 432 x = xmlnode_new_tag(name); 433 xmlnode_put_expat_attribs(x, attribs); 434 if (strcmp(name, "stream:stream") == 0) { 435 /* special case: name == stream:stream */ 436 /* id attrib of stream is stored for digest auth */ 437 gjc->sid = g_strdup(xmlnode_get_attrib(x, "id")); 438 /* STATE_EVT(JCONN_STATE_AUTH) */ 439 xmlnode_free(x); 440 } else { 441 gjc->current = x; 442 } 443 } 444 } 445 446 static void endElement(void *userdata, const char *name) 447 { 448 gjconn gjc = (gjconn) userdata; 449 xmlnode x; 450 jpacket p; 451 452 if (gjc->current == NULL) { 453 /* we got </stream:stream> */ 454 STATE_EVT(JCONN_STATE_OFF) 455 return; 456 } 457 458 x = xmlnode_get_parent(gjc->current); 459 460 if (!x) { 461 /* it is time to fire the event */ 462 p = jpacket_new(gjc->current); 463 464 if (gjc->on_packet) 465 (gjc->on_packet) (gjc, p); 466 else 467 xmlnode_free(gjc->current); 468 } 469 470 gjc->current = x; 471 } 472 473 static void jabber_callback(gpointer data, gint source, GaimInputCondition condition) 474 { 475 struct gaim_connection *gc = (struct gaim_connection *)data; 476 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; 477 478 gjab_recv(jd->gjc); 479 } 480 481 static void charData(void *userdata, const char *s, int slen) 482 { 483 gjconn gjc = (gjconn) userdata; 484 485 if (gjc->current) 486 xmlnode_insert_cdata(gjc->current, s, slen); 487 } 488 489 static void gjab_connected(gpointer data, gint source, GaimInputCondition cond) 490 { 491 xmlnode x; 492 char *t, *t2; 493 struct gaim_connection *gc = data; 494 struct jabber_data *jd; 495 gjconn gjc; 496 497 if (!g_slist_find(get_connections(), gc)) { 498 closesocket(source); 499 return; 500 } 501 502 jd = gc->proto_data; 503 gjc = jd->gjc; 504 505 if (gjc->fd != source) 506 gjc->fd = source; 507 508 if (source == -1) { 509 STATE_EVT(JCONN_STATE_OFF) 510 return; 511 } 512 513 gjc->state = JCONN_STATE_CONNECTED; 514 STATE_EVT(JCONN_STATE_CONNECTED) 515 516 /* start stream */ 517 x = jutil_header(NS_CLIENT, gjc->user->server); 518 t = xmlnode2str(x); 519 /* this is ugly, we can create the string here instead of jutil_header */ 520 /* what do you think about it? -madcat */ 521 t2 = strstr(t, "/>"); 522 *t2++ = '>'; 523 *t2 = '\0'; 524 gjab_send_raw(gjc, "<?xml version='1.0'?>"); 525 gjab_send_raw(gjc, t); 526 xmlnode_free(x); 527 528 gjc->state = JCONN_STATE_ON; 529 STATE_EVT(JCONN_STATE_ON); 530 531 gc = GJ_GC(gjc); 532 gc->inpa = gaim_input_add(gjc->fd, GAIM_INPUT_READ, jabber_callback, gc); 533 } 534 535 static void gjab_connected_ssl(gpointer data, void *source, GaimInputCondition cond) 536 { 537 struct gaim_connection *gc = data; 538 struct jabber_data *jd; 539 gjconn gjc; 540 541 jd = gc->proto_data; 542 gjc = jd->gjc; 543 544 if (source == NULL) { 545 STATE_EVT(JCONN_STATE_OFF) 546 return; 547 } 548 549 if (!g_slist_find(get_connections(), gc)) { 550 ssl_disconnect(source); 551 return; 552 } 553 554 gjab_connected(data, gjc->fd, cond); 555 } 556 557 static void gjab_start(gjconn gjc) 558 { 559 struct aim_user *user; 560 int port = -1, ssl = 0; 561 char *server = NULL, *s; 562 563 if (!gjc || gjc->state != JCONN_STATE_OFF) 564 return; 565 566 user = GJ_GC(gjc)->user; 567 if (*user->proto_opt[0]) { 568 /* If there's a dot, assume there's a hostname in the beginning */ 569 if (strchr(user->proto_opt[0], '.')) { 570 server = g_strdup(user->proto_opt[0]); 571 if ((s = strchr(server, ':'))) 132 else if( isdigit( *s ) ) 133 { 134 int i; 135 136 /* The first character is a digit. It could be an 137 IP address though. Only accept this as a port# 138 if there are only digits. */ 139 for( i = 0; isdigit( s[i] ); i ++ ); 140 141 /* If the first non-digit character is a colon or 142 the end of the string, save the port number 143 where it should be. */ 144 if( s[i] == ':' || s[i] == 0 ) 145 { 146 sscanf( s, "%d", &i ); 147 set_setint( &acc->set, "port", i ); 148 149 /* See above. */ 572 150 *s = 0; 573 } 574 575 /* After the hostname, there can be a port number */ 576 s = strchr(user->proto_opt[0], ':'); 577 if (s && isdigit(s[1])) 578 sscanf(s + 1, "%d", &port); 579 580 /* And if there's the string ssl, the user wants an SSL-connection */ 581 if (strstr(user->proto_opt[0], ":ssl") || g_strcasecmp(user->proto_opt[0], "ssl") == 0) 582 ssl = 1; 583 } 584 585 if (port == -1 && !ssl) 586 port = DEFAULT_PORT; 587 else if (port == -1 && ssl) 588 port = DEFAULT_PORT_SSL; 589 else if (port < JABBER_PORT_MIN || port > JABBER_PORT_MAX) { 590 serv_got_crap(GJ_GC(gjc), "For security reasons, the Jabber port number must be in the %d-%d range.", JABBER_PORT_MIN, JABBER_PORT_MAX); 591 STATE_EVT(JCONN_STATE_OFF) 592 return; 593 } 594 595 if (server == NULL) 596 server = g_strdup(gjc->user->server); 597 598 gjc->parser = XML_ParserCreate(NULL); 599 XML_SetUserData(gjc->parser, (void *)gjc); 600 XML_SetElementHandler(gjc->parser, startElement, endElement); 601 XML_SetCharacterDataHandler(gjc->parser, charData); 602 603 if (ssl) { 604 if ((gjc->ssl = ssl_connect(server, port, gjab_connected_ssl, GJ_GC(gjc)))) 605 gjc->fd = ssl_getfd(gjc->ssl); 606 else 607 gjc->fd = -1; 608 } else { 609 gjc->fd = proxy_connect(server, port, gjab_connected, GJ_GC(gjc)); 610 } 611 612 g_free(server); 613 614 if (!user->gc || (gjc->fd < 0)) { 615 STATE_EVT(JCONN_STATE_OFF) 616 return; 617 } 618 } 619 620 /* 621 * Find existing/active Jabber chat 622 */ 623 static struct jabber_chat *find_existing_chat(struct gaim_connection *gc, jid chat) 624 { 625 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; 626 struct jabber_chat *jc = NULL; 627 628 while (jcs) { 629 jc = jcs->data; 630 if (jc->state == JCS_ACTIVE && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) 631 break; 632 jc = NULL; 633 jcs = jcs->next; 634 } 635 636 return jc; 637 } 638 639 /* 640 * Find pending chat 641 */ 642 static struct jabber_chat *find_pending_chat(struct gaim_connection *gc, jid chat) 643 { 644 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; 645 struct jabber_chat *jc = NULL; 646 647 while (jcs) { 648 jc = jcs->data; 649 if (jc->state == JCS_PENDING && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) 650 break; 651 jc = NULL; 652 jcs = jcs->next; 653 } 654 655 return jc; 656 } 657 658 static gboolean find_chat_buddy(struct conversation *b, char *name) 659 { 660 GList *m = b->in_room; 661 662 while (m) { 663 if (!strcmp(m->data, name)) 664 return TRUE; 665 m = m->next; 666 } 667 668 return FALSE; 669 } 670 671 /* 672 * Remove a buddy from the (gaim) buddylist (if he's on it) 673 */ 674 static void jabber_remove_gaim_buddy(struct gaim_connection *gc, char *buddyname) 675 { 676 struct buddy *b; 677 678 if ((b = find_buddy(gc, buddyname)) != NULL) { 679 /* struct group *group; 680 681 group = find_group_by_buddy(gc, buddyname); 682 remove_buddy(gc, group, b); */ 683 jabber_remove_buddy(gc, b->name, JABBER_GROUP); 684 } 685 } 686 687 /* 688 * keep track of away msg same as yahoo plugin 689 */ 690 static void jabber_track_away(gjconn gjc, jpacket p, char *name, char *type) 691 { 692 struct jabber_data *jd = GJ_GC(gjc)->proto_data; 693 gpointer val = g_hash_table_lookup(jd->hash, name); 694 char *show; 695 char *vshow = NULL; 696 char *status = NULL; 697 char *msg = NULL; 698 699 if (type && (g_strcasecmp(type, "unavailable") == 0)) { 700 vshow = _("Unavailable"); 701 } else { 702 if((show = xmlnode_get_tag_data(p->x, "show")) != NULL) { 703 if (!g_strcasecmp(show, "away")) { 704 vshow = _("Away"); 705 } else if (!g_strcasecmp(show, "chat")) { 706 vshow = _("Online"); 707 } else if (!g_strcasecmp(show, "xa")) { 708 vshow = _("Extended Away"); 709 } else if (!g_strcasecmp(show, "dnd")) { 710 vshow = _("Do Not Disturb"); 711 } 712 } 713 } 714 715 status = xmlnode_get_tag_data(p->x, "status"); 716 717 if(vshow != NULL || status != NULL ) { 718 /* kinda hokey, but it works :-) */ 719 msg = g_strdup_printf("%s%s%s", 720 (vshow == NULL? "" : vshow), 721 (vshow == NULL || status == NULL? "" : ": "), 722 (status == NULL? "" : status)); 723 } else { 724 msg = g_strdup(_("Online")); 725 } 726 727 if (val) { 728 g_free(val); 729 g_hash_table_insert(jd->hash, name, msg); 730 } else { 731 g_hash_table_insert(jd->hash, g_strdup(name), msg); 732 } 733 } 734 735 static time_t iso8601_to_time(char *timestamp) 736 { 737 struct tm t; 738 time_t retval = 0; 739 740 if(sscanf(timestamp,"%04d%02d%02dT%02d:%02d:%02d", 741 &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec)) 742 { 743 t.tm_year -= 1900; 744 t.tm_mon -= 1; 745 t.tm_isdst = 0; 746 retval = mktime(&t); 747 # ifdef HAVE_TM_GMTOFF 748 retval += t.tm_gmtoff; 749 # else 750 # ifdef HAVE_TIMEZONE 751 tzset(); /* making sure */ 752 retval -= timezone; 753 # endif 754 # endif 755 } 756 757 return retval; 758 } 759 760 static void jabber_handlemessage(gjconn gjc, jpacket p) 761 { 762 xmlnode y, xmlns, z; 763 time_t time_sent = time(NULL); 764 765 char *from = NULL, *msg = NULL, *type = NULL; 766 char m[BUF_LONG * 2]; 767 768 type = xmlnode_get_attrib(p->x, "type"); 769 770 z = xmlnode_get_firstchild(p->x); 771 772 while(z) 773 { 774 if(NSCHECK(z,NS_DELAY)) 775 { 776 char *timestamp = xmlnode_get_attrib(z,"stamp"); 777 time_sent = iso8601_to_time(timestamp); 778 } 779 z = xmlnode_get_nextsibling(z); 780 } 781 782 if (!type || !g_strcasecmp(type, "normal") || !g_strcasecmp(type, "chat")) { 783 784 /* XXX namespaces could be handled better. (mid) */ 785 if ((xmlns = xmlnode_get_tag(p->x, "x"))) 786 type = xmlnode_get_attrib(xmlns, "xmlns"); 787 788 from = jid_full(p->from); 789 /* 790 if ((y = xmlnode_get_tag(p->x, "html"))) { 791 msg = xmlnode_get_data(y); 792 } else 793 */ 794 if ((y = xmlnode_get_tag(p->x, "body"))) { 795 msg = xmlnode_get_data(y); 796 } 797 798 799 if (!from) 800 return; 801 802 if (type && !g_strcasecmp(type, "jabber:x:conference")) { 803 /* do nothing */ 804 } else if (msg) { /* whisper */ 805 struct jabber_chat *jc; 806 g_snprintf(m, sizeof(m), "%s", msg); 807 if (((jc = find_existing_chat(GJ_GC(gjc), p->from)) != NULL) && jc->b) 808 serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 1, m, time_sent); 809 else { 810 int flags = 0; 811 812 if(p->from->user) { 813 from = g_strdup_printf("%s@%s", p->from->user, p->from->server); 814 } else { 815 /* server message? */ 816 from = g_strdup(p->from->server); 817 } 818 serv_got_im(GJ_GC(gjc), from, m, flags, time_sent, -1); 819 g_free(from); 820 } 821 } 822 823 } else if (!g_strcasecmp(type, "error")) { 824 if ((y = xmlnode_get_tag(p->x, "error"))) { 825 type = xmlnode_get_attrib(y, "code"); 826 msg = xmlnode_get_data(y); 827 } 828 829 if (msg) { 830 from = g_strdup_printf("Error %s", type ? type : ""); 831 do_error_dialog(GJ_GC(gjc), msg, from); 832 g_free(from); 833 } 834 } else if (!g_strcasecmp(type, "headline")) { 835 char *subject, *body, *url; 836 837 y = xmlnode_get_tag( p->x, "body" ); 838 body = y ? g_strdup( xmlnode_get_data( y ) ) : NULL; 839 840 y = xmlnode_get_tag( p->x, "subject" ); 841 subject = y ? g_strdup( xmlnode_get_data( y ) ) : NULL; 842 843 url = NULL; 844 z = xmlnode_get_firstchild(p->x); 845 while( z ) 846 { 847 char *xtype = xmlnode_get_attrib( z, "xmlns" ); 848 849 if( xtype && g_strcasecmp( xtype, "jabber:x:oob" ) == 0 && 850 ( y = xmlnode_get_tag( z, "url" ) ) ) 851 { 852 url = g_strdup( xmlnode_get_data( y ) ); 853 break; 151 s ++; 854 152 } 855 153 856 z = xmlnode_get_nextsibling( z );154 had_port = 1; 857 155 } 858 156 859 g_snprintf( m, BUF_LONG, "Subject: %s\nURL: %s\nMessage:\n%s", subject ? subject : "(none)", 860 url ? url : "(none)", body ? body : "(none)" ); 861 862 if( p->from->user ) 863 from = g_strdup_printf( "%s@%s", p->from->user, p->from->server ); 157 s = strchr( s, ':' ); 158 if( s ) 159 { 160 *s = 0; 161 s ++; 162 } 163 } 164 165 jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free ); 166 jd->buddies = g_hash_table_new( g_str_hash, g_str_equal ); 167 168 /* Figure out the hostname to connect to. */ 169 if( acc->server && *acc->server ) 170 connect_to = acc->server; 171 else if( ( srv = srv_lookup( "xmpp-client", "tcp", jd->server ) ) || 172 ( srv = srv_lookup( "jabber-client", "tcp", jd->server ) ) ) 173 connect_to = srv->name; 174 else 175 connect_to = jd->server; 176 177 imcb_log( ic, "Connecting" ); 178 179 if( set_getint( &acc->set, "port" ) < JABBER_PORT_MIN || 180 set_getint( &acc->set, "port" ) > JABBER_PORT_MAX ) 181 { 182 imcb_log( ic, "Incorrect port number, must be in the %d-%d range", 183 JABBER_PORT_MIN, JABBER_PORT_MAX ); 184 imc_logout( ic, FALSE ); 185 return; 186 } 187 188 /* For non-SSL connections we can try to use the port # from the SRV 189 reply, but let's not do that when using SSL, SSL usually runs on 190 non-standard ports... */ 191 if( set_getbool( &acc->set, "ssl" ) ) 192 { 193 jd->ssl = ssl_connect( connect_to, set_getint( &acc->set, "port" ), jabber_connected_ssl, ic ); 194 jd->fd = jd->ssl ? ssl_getfd( jd->ssl ) : -1; 195 } 196 else 197 { 198 jd->fd = proxy_connect( connect_to, srv ? srv->port : set_getint( &acc->set, "port" ), jabber_connected_plain, ic ); 199 } 200 g_free( srv ); 201 202 if( jd->fd == -1 ) 203 { 204 imcb_error( ic, "Could not connect to server" ); 205 imc_logout( ic, TRUE ); 206 207 return; 208 } 209 210 if( set_getbool( &acc->set, "xmlconsole" ) ) 211 { 212 jd->flags |= JFLAG_XMLCONSOLE; 213 /* Shouldn't really do this at this stage already, maybe. But 214 I think this shouldn't break anything. */ 215 imcb_add_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL ); 216 } 217 218 jabber_generate_id_hash( jd ); 219 } 220 221 static void jabber_generate_id_hash( struct jabber_data *jd ) 222 { 223 md5_state_t id_hash; 224 md5_byte_t binbuf[16]; 225 char *s; 226 227 md5_init( &id_hash ); 228 md5_append( &id_hash, (unsigned char *) jd->username, strlen( jd->username ) ); 229 md5_append( &id_hash, (unsigned char *) jd->server, strlen( jd->server ) ); 230 s = set_getstr( &jd->ic->acc->set, "resource" ); 231 md5_append( &id_hash, (unsigned char *) s, strlen( s ) ); 232 random_bytes( binbuf, 16 ); 233 md5_append( &id_hash, binbuf, 16 ); 234 md5_finish( &id_hash, binbuf ); 235 236 s = base64_encode( binbuf, 9 ); 237 jd->cached_id_prefix = g_strdup_printf( "%s%s", JABBER_CACHED_ID, s ); 238 g_free( s ); 239 } 240 241 static void jabber_logout( struct im_connection *ic ) 242 { 243 struct jabber_data *jd = ic->proto_data; 244 245 if( jd->fd >= 0 ) 246 jabber_end_stream( ic ); 247 248 while( ic->groupchats ) 249 jabber_chat_free( ic->groupchats ); 250 251 if( jd->r_inpa >= 0 ) 252 b_event_remove( jd->r_inpa ); 253 if( jd->w_inpa >= 0 ) 254 b_event_remove( jd->w_inpa ); 255 256 if( jd->ssl ) 257 ssl_disconnect( jd->ssl ); 258 if( jd->fd >= 0 ) 259 closesocket( jd->fd ); 260 261 if( jd->tx_len ) 262 g_free( jd->txq ); 263 264 if( jd->node_cache ) 265 g_hash_table_destroy( jd->node_cache ); 266 267 xt_free( jd->xt ); 268 269 g_free( jd->cached_id_prefix ); 270 g_free( jd->away_message ); 271 g_free( jd->username ); 272 g_free( jd ); 273 274 jabber_connections = g_slist_remove( jabber_connections, ic ); 275 } 276 277 static int jabber_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) 278 { 279 struct jabber_data *jd = ic->proto_data; 280 struct jabber_buddy *bud; 281 struct xt_node *node; 282 char *s; 283 int st; 284 285 if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 ) 286 return jabber_write( ic, message, strlen( message ) ); 287 288 if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) ) 289 bud = jabber_buddy_by_ext_jid( ic, who, 0 ); 290 else 291 bud = jabber_buddy_by_jid( ic, who, 0 ); 292 293 node = xt_new_node( "body", message, NULL ); 294 node = jabber_make_packet( "message", "chat", bud ? bud->full_jid : who, node ); 295 296 if( bud && ( jd->flags & JFLAG_WANT_TYPING ) && 297 ( ( bud->flags & JBFLAG_DOES_XEP85 ) || 298 !( bud->flags & JBFLAG_PROBED_XEP85 ) ) ) 299 { 300 struct xt_node *act; 301 302 /* If the user likes typing notification and if we don't know 303 (and didn't probe before) if this resource supports XEP85, 304 include a probe in this packet now. Also, if we know this 305 buddy does support XEP85, we have to send this <active/> 306 tag to tell that the user stopped typing (well, that's what 307 we guess when s/he pressed Enter...). */ 308 act = xt_new_node( "active", NULL, NULL ); 309 xt_add_attr( act, "xmlns", XMLNS_CHATSTATES ); 310 xt_add_child( node, act ); 311 312 /* Just make sure we do this only once. */ 313 bud->flags |= JBFLAG_PROBED_XEP85; 314 } 315 316 st = jabber_write_packet( ic, node ); 317 xt_free_node( node ); 318 319 return st; 320 } 321 322 static GList *jabber_away_states( struct im_connection *ic ) 323 { 324 static GList *l = NULL; 325 int i; 326 327 if( l == NULL ) 328 for( i = 0; jabber_away_state_list[i].full_name; i ++ ) 329 l = g_list_append( l, (void*) jabber_away_state_list[i].full_name ); 330 331 return l; 332 } 333 334 static void jabber_get_info( struct im_connection *ic, char *who ) 335 { 336 struct jabber_data *jd = ic->proto_data; 337 struct jabber_buddy *bud; 338 339 if( strchr( who, '/' ) ) 340 bud = jabber_buddy_by_jid( ic, who, 0 ); 341 else 342 { 343 char *s = jabber_normalize( who ); 344 bud = g_hash_table_lookup( jd->buddies, s ); 345 g_free( s ); 346 } 347 348 while( bud ) 349 { 350 imcb_log( ic, "Buddy %s (%d) information:\nAway state: %s\nAway message: %s", 351 bud->full_jid, bud->priority, 352 bud->away_state ? bud->away_state->full_name : "(none)", 353 bud->away_message ? : "(none)" ); 354 bud = bud->next; 355 } 356 357 jabber_get_vcard( ic, bud ? bud->full_jid : who ); 358 } 359 360 static void jabber_set_away( struct im_connection *ic, char *state_txt, char *message ) 361 { 362 struct jabber_data *jd = ic->proto_data; 363 struct jabber_away_state *state; 364 365 /* Save all this info. We need it, for example, when changing the priority setting. */ 366 state = (void *) jabber_away_state_by_name( state_txt ); 367 jd->away_state = state ? state : (void *) jabber_away_state_list; /* Fall back to "Away" if necessary. */ 368 g_free( jd->away_message ); 369 jd->away_message = ( message && *message ) ? g_strdup( message ) : NULL; 370 371 presence_send_update( ic ); 372 } 373 374 static void jabber_add_buddy( struct im_connection *ic, char *who, char *group ) 375 { 376 struct jabber_data *jd = ic->proto_data; 377 378 if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 ) 379 { 380 jd->flags |= JFLAG_XMLCONSOLE; 381 imcb_add_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL ); 382 return; 383 } 384 385 if( jabber_add_to_roster( ic, who, NULL ) ) 386 presence_send_request( ic, who, "subscribe" ); 387 } 388 389 static void jabber_remove_buddy( struct im_connection *ic, char *who, char *group ) 390 { 391 struct jabber_data *jd = ic->proto_data; 392 393 if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 ) 394 { 395 jd->flags &= ~JFLAG_XMLCONSOLE; 396 /* Not necessary for now. And for now the code isn't too 397 happy if the buddy is completely gone right after calling 398 this function already. 399 imcb_remove_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL ); 400 */ 401 return; 402 } 403 404 /* We should always do this part. Clean up our administration a little bit. */ 405 jabber_buddy_remove_bare( ic, who ); 406 407 if( jabber_remove_from_roster( ic, who ) ) 408 presence_send_request( ic, who, "unsubscribe" ); 409 } 410 411 static struct groupchat *jabber_chat_join_( struct im_connection *ic, char *room, char *nick, char *password ) 412 { 413 if( strchr( room, '@' ) == NULL ) 414 imcb_error( ic, "Invalid room name: %s", room ); 415 else if( jabber_chat_by_jid( ic, room ) ) 416 imcb_error( ic, "Already present in chat `%s'", room ); 417 else 418 return jabber_chat_join( ic, room, nick, password ); 419 420 return NULL; 421 } 422 423 static void jabber_chat_msg_( struct groupchat *c, char *message, int flags ) 424 { 425 if( c && message ) 426 jabber_chat_msg( c, message, flags ); 427 } 428 429 static void jabber_chat_topic_( struct groupchat *c, char *topic ) 430 { 431 if( c && topic ) 432 jabber_chat_topic( c, topic ); 433 } 434 435 static void jabber_chat_leave_( struct groupchat *c ) 436 { 437 if( c ) 438 jabber_chat_leave( c, NULL ); 439 } 440 441 static void jabber_chat_invite_( struct groupchat *c, char *who, char *msg ) 442 { 443 struct jabber_chat *jc = c->data; 444 gchar *msg_alt = NULL; 445 446 if( msg == NULL ) 447 msg_alt = g_strdup_printf( "%s invited you to %s", c->ic->acc->user, jc->name ); 448 449 if( c && who ) 450 jabber_chat_invite( c, who, msg ? msg : msg_alt ); 451 452 g_free( msg_alt ); 453 } 454 455 static void jabber_keepalive( struct im_connection *ic ) 456 { 457 /* Just any whitespace character is enough as a keepalive for XMPP sessions. */ 458 jabber_write( ic, "\n", 1 ); 459 460 /* This runs the garbage collection every minute, which means every packet 461 is in the cache for about a minute (which should be enough AFAIK). */ 462 jabber_cache_clean( ic ); 463 } 464 465 static int jabber_send_typing( struct im_connection *ic, char *who, int typing ) 466 { 467 struct jabber_data *jd = ic->proto_data; 468 struct jabber_buddy *bud; 469 470 /* Enable typing notification related code from now. */ 471 jd->flags |= JFLAG_WANT_TYPING; 472 473 if( ( bud = jabber_buddy_by_jid( ic, who, 0 ) ) == NULL ) 474 { 475 /* Sending typing notifications to unknown buddies is 476 unsupported for now. Shouldn't be a problem, I think. */ 477 return 0; 478 } 479 480 if( bud->flags & JBFLAG_DOES_XEP85 ) 481 { 482 /* We're only allowed to send this stuff if we know the other 483 side supports it. */ 484 485 struct xt_node *node; 486 char *type; 487 int st; 488 489 if( typing & OPT_TYPING ) 490 type = "composing"; 491 else if( typing & OPT_THINKING ) 492 type = "paused"; 864 493 else 865 from = g_strdup( p->from->server ); 866 867 serv_got_im( GJ_GC(gjc), from, m, 0, time_sent, -1 ); 868 869 g_free( from ); 870 g_free( subject ); 871 g_free( body ); 872 g_free( url ); 873 } 874 } 875 876 static void jabber_handlepresence(gjconn gjc, jpacket p) 877 { 878 char *to, *from, *type; 879 struct buddy *b = NULL; 880 jid who; 881 char *buddy; 882 xmlnode y; 883 char *show; 884 int state = 0; 885 GSList *resources; 886 char *res; 887 struct conversation *cnv = NULL; 888 struct jabber_chat *jc = NULL; 889 890 to = xmlnode_get_attrib(p->x, "to"); 891 from = xmlnode_get_attrib(p->x, "from"); 892 type = xmlnode_get_attrib(p->x, "type"); 893 894 if (type && g_strcasecmp(type, "error") == 0) { 895 return; 896 } 897 else if ((y = xmlnode_get_tag(p->x, "show"))) { 898 show = xmlnode_get_data(y); 899 if (!show) { 900 state = 0; 901 } else if (!g_strcasecmp(show, "away")) { 902 state = UC_AWAY; 903 } else if (!g_strcasecmp(show, "chat")) { 904 state = UC_CHAT; 905 } else if (!g_strcasecmp(show, "xa")) { 906 state = UC_XA; 907 } else if (!g_strcasecmp(show, "dnd")) { 908 state = UC_DND; 909 } 910 } else { 911 state = 0; 912 } 913 914 who = jid_new(gjc->p, from); 915 if (who->user == NULL) { 916 /* FIXME: transport */ 917 return; 918 } 919 920 buddy = g_strdup_printf("%s@%s", who->user, who->server); 921 922 /* um. we're going to check if it's a chat. if it isn't, and there are pending 923 * chats, create the chat. if there aren't pending chats and we don't have the 924 * buddy on our list, simply bail out. */ 925 if ((cnv = NULL) == NULL) { 926 static int i = 0x70; 927 if ((jc = find_pending_chat(GJ_GC(gjc), who)) != NULL) { 928 jc->b = cnv = serv_got_joined_chat(GJ_GC(gjc), i++, who->user); 929 jc->id = jc->b->id; 930 jc->state = JCS_ACTIVE; 931 } else if ((b = find_buddy(GJ_GC(gjc), buddy)) == NULL) { 932 g_free(buddy); 933 return; 934 } 935 } 936 937 if (!cnv) { 938 resources = b->proto_data; 939 res = who->resource; 940 if (res) 941 while (resources) { 942 if (!strcmp(res, resources->data)) 943 break; 944 resources = resources->next; 945 } 946 947 /* keep track of away msg same as yahoo plugin */ 948 jabber_track_away(gjc, p, normalize(b->name), type); 949 950 if (type && (g_strcasecmp(type, "unavailable") == 0)) { 951 if (resources) { 952 g_free(resources->data); 953 b->proto_data = g_slist_remove(b->proto_data, resources->data); 954 } 955 if (!b->proto_data) { 956 serv_got_update(GJ_GC(gjc), buddy, 0, 0, 0, 0, 0, 0); 957 } 958 } else { 959 if (!resources) { 960 b->proto_data = g_slist_append(b->proto_data, g_strdup(res)); 961 } 962 963 serv_got_update(GJ_GC(gjc), buddy, 1, 0, b->signon, b->idle, state, 0); 964 965 } 966 } else { 967 if (who->resource) { 968 char *buf; 969 970 buf = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource); 971 jabber_track_away(gjc, p, buf, type); 972 g_free(buf); 973 974 if (type && !g_strcasecmp(type, "unavailable")) { 975 struct jabber_data *jd; 976 if (!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) { 977 g_free(buddy); 978 return; 979 } 980 jd = jc->gc->proto_data; 981 /* if it's not ourselves...*/ 982 if (strcmp(who->resource, jc->Jid->resource) && jc->b) { 983 remove_chat_buddy(jc->b, who->resource, NULL); 984 g_free(buddy); 985 return; 986 } 987 988 jc->state = JCS_CLOSED; 989 serv_got_chat_left(GJ_GC(gjc), jc->id); 990 /* 991 * TBD: put back some day? 992 jd->chats = g_slist_remove(jd->chats, jc); 993 g_free(jc); 994 */ 995 } else { 996 if ((!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) || !jc->b) { 997 g_free(buddy); 998 return; 999 } 1000 if (!find_chat_buddy(jc->b, who->resource)) { 1001 add_chat_buddy(jc->b, who->resource); 1002 } 1003 } 1004 } 1005 } 1006 1007 g_free(buddy); 1008 1009 return; 1010 } 1011 1012 /* 1013 * Used only by Jabber accept/deny add stuff just below 1014 */ 1015 struct jabber_add_permit { 1016 gjconn gjc; 1017 gchar *user; 1018 }; 1019 1020 /* 1021 * Common part for Jabber accept/deny adds 1022 * 1023 * "type" says whether we'll permit/deny the subscribe request 1024 */ 1025 static void jabber_accept_deny_add(struct jabber_add_permit *jap, const char *type) 1026 { 1027 xmlnode g = xmlnode_new_tag("presence"); 1028 1029 xmlnode_put_attrib(g, "to", jap->user); 1030 xmlnode_put_attrib(g, "type", type); 1031 gjab_send(jap->gjc, g); 1032 1033 xmlnode_free(g); 1034 } 1035 1036 /* 1037 * Callback from "accept" in do_ask_dialog() invoked by jabber_handles10n() 1038 */ 1039 static void jabber_accept_add(gpointer w, struct jabber_add_permit *jap) 1040 { 1041 jabber_accept_deny_add(jap, "subscribed"); 1042 /* 1043 * If we don't already have the buddy on *our* buddylist, 1044 * ask if we want him or her added. 1045 */ 1046 if(find_buddy(GJ_GC(jap->gjc), jap->user) == NULL) { 1047 show_got_added(GJ_GC(jap->gjc), jap->user, NULL); 1048 } 1049 g_free(jap->user); 1050 g_free(jap); 1051 } 1052 1053 /* 1054 * Callback from "deny/cancel" in do_ask_dialog() invoked by jabber_handles10n() 1055 */ 1056 static void jabber_deny_add(gpointer w, struct jabber_add_permit *jap) 1057 { 1058 jabber_accept_deny_add(jap, "unsubscribed"); 1059 g_free(jap->user); 1060 g_free(jap); 1061 } 1062 1063 /* 1064 * Handle subscription requests 1065 */ 1066 static void jabber_handles10n(gjconn gjc, jpacket p) 1067 { 1068 xmlnode g; 1069 char *Jid = xmlnode_get_attrib(p->x, "from"); 1070 char *type = xmlnode_get_attrib(p->x, "type"); 1071 1072 g = xmlnode_new_tag("presence"); 1073 xmlnode_put_attrib(g, "to", Jid); 1074 1075 if (!strcmp(type, "subscribe")) { 1076 /* 1077 * A "subscribe to us" request was received - put up the approval dialog 1078 */ 1079 struct jabber_add_permit *jap = g_new0(struct jabber_add_permit, 1); 1080 gchar *msg = g_strdup_printf(_("The user %s wants to add you to his/her buddy list."), 1081 Jid); 1082 1083 jap->gjc = gjc; 1084 jap->user = g_strdup(Jid); 1085 do_ask_dialog(GJ_GC(gjc), msg, jap, jabber_accept_add, jabber_deny_add); 1086 1087 g_free(msg); 1088 xmlnode_free(g); /* Never needed it here anyway */ 1089 return; 1090 1091 } else if (!strcmp(type, "unsubscribe")) { 1092 /* 1093 * An "unsubscribe to us" was received - simply "approve" it 1094 */ 1095 xmlnode_put_attrib(g, "type", "unsubscribed"); 1096 } else { 1097 /* 1098 * Did we attempt to subscribe to somebody and they do not exist? 1099 */ 1100 if (!strcmp(type, "unsubscribed")) { 1101 xmlnode y; 1102 char *status; 1103 if((y = xmlnode_get_tag(p->x, "status")) && (status = xmlnode_get_data(y)) && 1104 !strcmp(status, "Not Found")) { 1105 char *msg = g_strdup_printf("%s: \"%s\"", _("No such user"), 1106 xmlnode_get_attrib(p->x, "from")); 1107 do_error_dialog(GJ_GC(gjc), msg, _("Jabber Error")); 1108 g_free(msg); 1109 } 1110 } 1111 1112 xmlnode_free(g); 1113 return; 1114 } 1115 1116 gjab_send(gjc, g); 1117 xmlnode_free(g); 1118 } 1119 1120 /* 1121 * Pending subscription to a buddy? 1122 */ 1123 #define BUD_SUB_TO_PEND(sub, ask) ((!g_strcasecmp((sub), "none") || !g_strcasecmp((sub), "from")) && \ 1124 (ask) != NULL && !g_strcasecmp((ask), "subscribe")) 1125 1126 /* 1127 * Subscribed to a buddy? 1128 */ 1129 #define BUD_SUBD_TO(sub, ask) ((!g_strcasecmp((sub), "to") || !g_strcasecmp((sub), "both")) && \ 1130 ((ask) == NULL || !g_strcasecmp((ask), "subscribe"))) 1131 1132 /* 1133 * Pending unsubscription to a buddy? 1134 */ 1135 #define BUD_USUB_TO_PEND(sub, ask) ((!g_strcasecmp((sub), "to") || !g_strcasecmp((sub), "both")) && \ 1136 (ask) != NULL && !g_strcasecmp((ask), "unsubscribe")) 1137 1138 /* 1139 * Unsubscribed to a buddy? 1140 */ 1141 #define BUD_USUBD_TO(sub, ask) ((!g_strcasecmp((sub), "none") || !g_strcasecmp((sub), "from")) && \ 1142 ((ask) == NULL || !g_strcasecmp((ask), "unsubscribe"))) 1143 1144 /* 1145 * If a buddy is added or removed from the roster on another resource 1146 * jabber_handlebuddy is called 1147 * 1148 * Called with roster item node. 1149 */ 1150 static void jabber_handlebuddy(gjconn gjc, xmlnode x) 1151 { 1152 xmlnode g; 1153 char *Jid, *name, *sub, *ask; 1154 jid who; 1155 struct buddy *b = NULL; 1156 char *buddyname, *groupname = NULL; 1157 1158 Jid = xmlnode_get_attrib(x, "jid"); 1159 name = xmlnode_get_attrib(x, "name"); 1160 sub = xmlnode_get_attrib(x, "subscription"); 1161 ask = xmlnode_get_attrib(x, "ask"); 1162 who = jid_new(gjc->p, Jid); 1163 1164 /* JFIXME: jabber_handleroster() had a "FIXME: transport" at this 1165 * equivilent point. So... 1166 * 1167 * We haven't allocated any memory or done anything interesting to 1168 * this point, so we'll violate Good Coding Structure here by 1169 * simply bailing out. 1170 */ 1171 if (!who || !who->user) { 1172 return; 1173 } 1174 1175 buddyname = g_strdup_printf("%s@%s", who->user, who->server); 1176 1177 if((g = xmlnode_get_tag(x, "group")) != NULL) { 1178 groupname = xmlnode_get_data(g); 1179 } 1180 1181 /* 1182 * Add or remove a buddy? Change buddy's alias or group? 1183 */ 1184 if (BUD_SUB_TO_PEND(sub, ask) || BUD_SUBD_TO(sub, ask)) { 1185 if ((b = find_buddy(GJ_GC(gjc), buddyname)) == NULL) { 1186 add_buddy(GJ_GC(gjc), groupname ? groupname : _("Buddies"), buddyname, 1187 name ? name : buddyname); 1188 } else { 1189 /* struct group *c_grp = find_group_by_buddy(GJ_GC(gjc), buddyname); */ 1190 1191 /* 1192 * If the buddy's in a new group or his/her alias is changed... 1193 */ 1194 if(groupname) { 1195 int present = b->present; /* save presence state */ 1196 int uc = b->uc; /* and away state (?) */ 1197 int idle = b->idle; 1198 int signon = b->signon; 1199 1200 /* 1201 * seems rude, but it seems to be the only way... 1202 */ 1203 /* remove_buddy(GJ_GC(gjc), c_grp, b); */ 1204 jabber_remove_buddy(GJ_GC(gjc), b->name, JABBER_GROUP); 1205 1206 add_buddy(GJ_GC(gjc), groupname, buddyname, 1207 name ? name : buddyname); 1208 if(present) { 1209 serv_got_update(GJ_GC(gjc), buddyname, 1, 0, signon, idle, uc, 0); 1210 } 1211 } else if(name != NULL && strcmp(b->show, name)) { 1212 strncpy(b->show, name, BUDDY_ALIAS_MAXLEN); 1213 b->show[BUDDY_ALIAS_MAXLEN - 1] = '\0'; /* cheap safety feature */ 1214 serv_buddy_rename(GJ_GC(gjc), buddyname, b->show); 1215 } 1216 } 1217 } else if (BUD_USUB_TO_PEND(sub, ask) || BUD_USUBD_TO(sub, ask) || !g_strcasecmp(sub, "remove")) { 1218 jabber_remove_gaim_buddy(GJ_GC(gjc), buddyname); 1219 } 1220 g_free(buddyname); 1221 1222 } 1223 1224 static void jabber_handleroster(gjconn gjc, xmlnode querynode) 1225 { 1226 xmlnode x; 1227 1228 x = xmlnode_get_firstchild(querynode); 1229 while (x) { 1230 jabber_handlebuddy(gjc, x); 1231 x = xmlnode_get_nextsibling(x); 1232 } 1233 1234 account_online(GJ_GC(gjc)); 1235 } 1236 1237 static void jabber_handleauthresp(gjconn gjc, jpacket p) 1238 { 1239 if (jpacket_subtype(p) == JPACKET__RESULT) { 1240 if (xmlnode_has_children(p->x)) { 1241 xmlnode query = xmlnode_get_tag(p->x, "query"); 1242 set_login_progress(GJ_GC(gjc), 4, _("Authenticating")); 1243 if (!xmlnode_get_tag(query, "digest")) { 1244 g_free(gjc->sid); 1245 gjc->sid = NULL; 1246 } 1247 gjab_auth(gjc); 1248 } else { 1249 gjab_reqroster(gjc); 1250 1251 ((struct jabber_data *)GJ_GC(gjc)->proto_data)->did_import = TRUE; 1252 } 1253 } else { 1254 xmlnode xerr; 1255 char *errmsg = NULL; 1256 int errcode = 0; 1257 struct jabber_data *jd = GJ_GC(gjc)->proto_data; 1258 1259 xerr = xmlnode_get_tag(p->x, "error"); 1260 if (xerr) { 1261 char msg[BUF_LONG]; 1262 errmsg = xmlnode_get_data(xerr); 1263 if (xmlnode_get_attrib(xerr, "code")) { 1264 errcode = atoi(xmlnode_get_attrib(xerr, "code")); 1265 g_snprintf(msg, sizeof(msg), "Error %d: %s", errcode, errmsg ? errmsg : "Unknown error"); 1266 } else 1267 g_snprintf(msg, sizeof(msg), "%s", errmsg); 1268 hide_login_progress(GJ_GC(gjc), msg); 1269 } else { 1270 hide_login_progress(GJ_GC(gjc), _("Unknown login error")); 1271 } 1272 1273 jd->die = TRUE; 1274 } 1275 } 1276 1277 static void jabber_handleversion(gjconn gjc, xmlnode iqnode) { 1278 xmlnode querynode, x; 1279 char *id, *from; 1280 char os[1024]; 1281 #ifndef _WIN32 1282 struct utsname osinfo; 1283 1284 uname(&osinfo); 1285 g_snprintf(os, sizeof os, "%s %s %s", osinfo.sysname, osinfo.release, osinfo.machine); 1286 #else 1287 g_snprintf(os, sizeof os, "Windows %d %d", _winmajor, _winminor); 1288 #endif 1289 1290 1291 id = xmlnode_get_attrib(iqnode, "id"); 1292 from = xmlnode_get_attrib(iqnode, "from"); 1293 1294 x = jutil_iqnew(JPACKET__RESULT, NS_VERSION); 1295 1296 xmlnode_put_attrib(x, "to", from); 1297 xmlnode_put_attrib(x, "id", id); 1298 querynode = xmlnode_get_tag(x, "query"); 1299 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "name"), PACKAGE, -1); 1300 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "version"), BITLBEE_VERSION, -1); 1301 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "os"), os, -1); 1302 1303 gjab_send(gjc, x); 1304 1305 xmlnode_free(x); 1306 } 1307 1308 static void jabber_handletime(gjconn gjc, xmlnode iqnode) { 1309 xmlnode querynode, x; 1310 char *id, *from; 1311 time_t now_t; 1312 struct tm *now; 1313 char buf[1024]; 1314 1315 time(&now_t); 1316 now = localtime(&now_t); 1317 1318 id = xmlnode_get_attrib(iqnode, "id"); 1319 from = xmlnode_get_attrib(iqnode, "from"); 1320 1321 x = jutil_iqnew(JPACKET__RESULT, NS_TIME); 1322 1323 xmlnode_put_attrib(x, "to", from); 1324 xmlnode_put_attrib(x, "id", id); 1325 querynode = xmlnode_get_tag(x, "query"); 1326 1327 strftime(buf, 1024, "%Y%m%dT%T", now); 1328 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "utc"), buf, -1); 1329 strftime(buf, 1024, "%Z", now); 1330 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "tz"), buf, -1); 1331 strftime(buf, 1024, "%d %b %Y %T", now); 1332 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "display"), buf, -1); 1333 1334 gjab_send(gjc, x); 1335 1336 xmlnode_free(x); 1337 } 1338 1339 static void jabber_handlelast(gjconn gjc, xmlnode iqnode) { 1340 xmlnode x, querytag; 1341 char *id, *from; 1342 struct jabber_data *jd = GJ_GC(gjc)->proto_data; 1343 char idle_time[32]; 1344 1345 id = xmlnode_get_attrib(iqnode, "id"); 1346 from = xmlnode_get_attrib(iqnode, "from"); 1347 1348 x = jutil_iqnew(JPACKET__RESULT, "jabber:iq:last"); 1349 1350 xmlnode_put_attrib(x, "to", from); 1351 xmlnode_put_attrib(x, "id", id); 1352 querytag = xmlnode_get_tag(x, "query"); 1353 g_snprintf(idle_time, sizeof idle_time, "%ld", jd->idle ? time(NULL) - jd->idle : 0); 1354 xmlnode_put_attrib(querytag, "seconds", idle_time); 1355 1356 gjab_send(gjc, x); 1357 xmlnode_free(x); 1358 } 1359 1360 /* 1361 * delete == TRUE: delete found entry 1362 * 1363 * returns pointer to (local) copy of value if found, NULL otherwise 1364 * 1365 * Note: non-reentrant! Local static storage re-used on subsequent calls. 1366 * If you're going to need to keep the returned value, make a copy! 1367 */ 1368 static gchar *jabber_track_queries(GHashTable *queries, gchar *key, gboolean delete) 1369 { 1370 gpointer my_key, my_val; 1371 static gchar *ret_val = NULL; 1372 1373 if(ret_val != NULL) { 1374 g_free(ret_val); 1375 ret_val = NULL; 1376 } 1377 1378 /* self-protection */ 1379 if(queries != NULL && key != NULL) { 1380 if(g_hash_table_lookup_extended(queries, key, &my_key, &my_val)) { 1381 ret_val = g_strdup((gchar *) my_val); 1382 if(delete) { 1383 g_hash_table_remove(queries, key); 1384 g_free(my_key); 1385 g_free(my_val); 1386 } 1387 } 1388 } 1389 1390 return(ret_val); 1391 } 1392 1393 static void jabber_handlepacket(gjconn gjc, jpacket p) 1394 { 1395 char *id; 1396 switch (p->type) { 1397 case JPACKET_MESSAGE: 1398 jabber_handlemessage(gjc, p); 1399 break; 1400 case JPACKET_PRESENCE: 1401 jabber_handlepresence(gjc, p); 1402 break; 1403 case JPACKET_IQ: 1404 id = xmlnode_get_attrib(p->x, "id"); 1405 if (id != NULL && !strcmp(id, IQID_AUTH)) { 1406 jabber_handleauthresp(gjc, p); 1407 break; 1408 } 1409 1410 if (jpacket_subtype(p) == JPACKET__SET) { 1411 xmlnode querynode; 1412 querynode = xmlnode_get_tag(p->x, "query"); 1413 if (NSCHECK(querynode, "jabber:iq:roster")) { 1414 jabber_handlebuddy(gjc, xmlnode_get_firstchild(querynode)); 1415 } 1416 } else if (jpacket_subtype(p) == JPACKET__GET) { 1417 xmlnode querynode; 1418 querynode = xmlnode_get_tag(p->x, "query"); 1419 if (NSCHECK(querynode, NS_VERSION)) { 1420 jabber_handleversion(gjc, p->x); 1421 } else if (NSCHECK(querynode, NS_TIME)) { 1422 jabber_handletime(gjc, p->x); 1423 } else if (NSCHECK(querynode, "jabber:iq:last")) { 1424 jabber_handlelast(gjc, p->x); 1425 } 1426 } else if (jpacket_subtype(p) == JPACKET__RESULT) { 1427 xmlnode querynode, vcard; 1428 /* char *xmlns; */ 1429 char *from; 1430 1431 /* 1432 * TBD: ISTM maybe this part could use a serious re-work? 1433 */ 1434 from = xmlnode_get_attrib(p->x, "from"); 1435 querynode = xmlnode_get_tag(p->x, "query"); 1436 vcard = xmlnode_get_tag(p->x, "vCard"); 1437 if (!vcard) 1438 vcard = xmlnode_get_tag(p->x, "VCARD"); 1439 1440 if (NSCHECK(querynode, NS_ROSTER)) { 1441 jabber_handleroster(gjc, querynode); 1442 } else if (NSCHECK(querynode, NS_VCARD)) { 1443 jabber_track_queries(gjc->queries, id, TRUE); /* delete query track */ 1444 jabber_handlevcard(gjc, querynode, from); 1445 } else if (vcard) { 1446 jabber_track_queries(gjc->queries, id, TRUE); /* delete query track */ 1447 jabber_handlevcard(gjc, vcard, from); 1448 } else { 1449 char *val; 1450 1451 /* handle "null" query results */ 1452 if((val = jabber_track_queries(gjc->queries, id, TRUE)) != NULL) { 1453 if (!g_strncasecmp(val, "vcard", 5)) { 1454 jabber_handlevcard(gjc, NULL, from); 1455 } 1456 1457 /* No-op */ 1458 } 1459 } 1460 1461 } else if (jpacket_subtype(p) == JPACKET__ERROR) { 1462 xmlnode xerr; 1463 char *from, *errmsg = NULL; 1464 int errcode = 0; 1465 1466 from = xmlnode_get_attrib(p->x, "from"); 1467 xerr = xmlnode_get_tag(p->x, "error"); 1468 if (xerr) { 1469 errmsg = xmlnode_get_data(xerr); 1470 if (xmlnode_get_attrib(xerr, "code")) 1471 errcode = atoi(xmlnode_get_attrib(xerr, "code")); 1472 } 1473 1474 from = g_strdup_printf("Error %d (%s)", errcode, from); 1475 do_error_dialog(GJ_GC(gjc), errmsg, from); 1476 g_free(from); 1477 1478 } 1479 1480 break; 1481 case JPACKET_S10N: 1482 jabber_handles10n(gjc, p); 1483 break; 1484 } 1485 1486 xmlnode_free(p->x); 1487 1488 return; 1489 } 1490 1491 static void jabber_handlestate(gjconn gjc, int state) 1492 { 1493 switch (state) { 1494 case JCONN_STATE_OFF: 1495 if(gjc->was_connected) { 1496 hide_login_progress_error(GJ_GC(gjc), _("Connection lost")); 1497 } else { 1498 hide_login_progress(GJ_GC(gjc), _("Unable to connect")); 1499 } 1500 signoff(GJ_GC(gjc)); 1501 break; 1502 case JCONN_STATE_CONNECTED: 1503 gjc->was_connected = 1; 1504 set_login_progress(GJ_GC(gjc), 2, _("Connected")); 1505 break; 1506 case JCONN_STATE_ON: 1507 set_login_progress(GJ_GC(gjc), 3, _("Requesting Authentication Method")); 1508 gjab_reqauth(gjc); 1509 break; 1510 } 1511 return; 1512 } 1513 1514 static void jabber_login(struct aim_user *user) 1515 { 1516 struct gaim_connection *gc = new_gaim_conn(user); 1517 struct jabber_data *jd = gc->proto_data = g_new0(struct jabber_data, 1); 1518 char *loginname = create_valid_jid(user->username, DEFAULT_SERVER, "BitlBee"); 1519 1520 jd->hash = g_hash_table_new(g_str_hash, g_str_equal); 1521 jd->chats = NULL; /* we have no chats yet */ 1522 1523 set_login_progress(gc, 1, _("Connecting")); 1524 1525 if (!(jd->gjc = gjab_new(loginname, user->password, gc))) { 1526 g_free(loginname); 1527 hide_login_progress(gc, _("Unable to connect")); 1528 signoff(gc); 1529 return; 1530 } 1531 1532 g_free(loginname); 1533 gjab_state_handler(jd->gjc, jabber_handlestate); 1534 gjab_packet_handler(jd->gjc, jabber_handlepacket); 1535 jd->gjc->queries = g_hash_table_new(g_str_hash, g_str_equal); 1536 gjab_start(jd->gjc); 1537 } 1538 1539 static gboolean jabber_destroy_hash(gpointer key, gpointer val, gpointer data) { 1540 g_free(key); 1541 g_free(val); 1542 return TRUE; 1543 } 1544 1545 static gboolean jabber_free(gpointer data) 1546 { 1547 struct jabber_data *jd = data; 1548 1549 if(jd->gjc != NULL) { 1550 gjab_delete(jd->gjc); 1551 /* YAY for modules with their own memory pool managers!... 1552 g_free(jd->gjc->sid); 1553 And a less sarcastic yay for valgrind. :-) */ 1554 jd->gjc = NULL; 1555 } 1556 g_free(jd); 1557 1558 return FALSE; 1559 } 1560 1561 static void jabber_close(struct gaim_connection *gc) 1562 { 1563 struct jabber_data *jd = gc->proto_data; 1564 1565 if(jd) { 1566 GSList *jcs = jd->chats; 1567 1568 /* Free-up the jabber_chat struct allocs and the list */ 1569 while (jcs) { 1570 g_free(jcs->data); 1571 jcs = jcs->next; 1572 } 1573 g_slist_free(jd->chats); 1574 1575 /* Free-up the away status memories and the list */ 1576 if(jd->hash != NULL) { 1577 g_hash_table_foreach_remove(jd->hash, jabber_destroy_hash, NULL); 1578 g_hash_table_destroy(jd->hash); 1579 jd->hash = NULL; 1580 } 1581 1582 /* Free-up the pending queries memories and the list */ 1583 if(jd->gjc != NULL && jd->gjc->queries != NULL) { 1584 g_hash_table_foreach_remove(jd->gjc->queries, jabber_destroy_hash, NULL); 1585 g_hash_table_destroy(jd->gjc->queries); 1586 jd->gjc->queries = NULL; 1587 } 1588 } 1589 if (gc->inpa) 1590 gaim_input_remove(gc->inpa); 1591 1592 if(jd) { 1593 g_timeout_add(50, jabber_free, jd); 1594 if(jd->gjc != NULL) 1595 xmlnode_free(jd->gjc->current); 1596 } 1597 gc->proto_data = NULL; 1598 } 1599 1600 static int jabber_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags) 1601 { 1602 xmlnode x, y; 1603 char *realwho; 1604 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; 1605 1606 if (!who || !message) 1607 return 0; 1608 1609 x = xmlnode_new_tag("message"); 1610 /* Bare username and "username" not the server itself? */ 1611 if (!strchr(who, '@') && strcmp(who, gjc->user->server) != 0) 1612 realwho = g_strdup_printf("%s@%s", who, gjc->user->server); 1613 else 1614 realwho = g_strdup(who); 1615 xmlnode_put_attrib(x, "to", realwho); 1616 g_free(realwho); 1617 1618 xmlnode_insert_tag(x, "bitlbee"); 1619 xmlnode_put_attrib(x, "type", "chat"); 1620 1621 if (message && strlen(message)) { 1622 y = xmlnode_insert_tag(x, "body"); 1623 xmlnode_insert_cdata(y, message, -1); 1624 } 1625 1626 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); 1627 xmlnode_free(x); 494 type = "active"; 495 496 node = xt_new_node( type, NULL, NULL ); 497 xt_add_attr( node, "xmlns", XMLNS_CHATSTATES ); 498 node = jabber_make_packet( "message", "chat", bud->full_jid, node ); 499 500 st = jabber_write_packet( ic, node ); 501 xt_free_node( node ); 502 503 return st; 504 } 505 1628 506 return 1; 1629 507 } 1630 508 1631 /* 1632 * Add/update buddy's roster entry on server 1633 */ 1634 static void jabber_roster_update(struct gaim_connection *gc, char *name) 1635 { 1636 xmlnode x, y; 1637 char *realwho; 1638 gjconn gjc; 1639 struct buddy *buddy = NULL; 1640 /* struct group *buddy_group = NULL; */ 1641 1642 if(gc && gc->proto_data && ((struct jabber_data *)gc->proto_data)->gjc && name) { 1643 gjc = ((struct jabber_data *)gc->proto_data)->gjc; 1644 1645 if (!strchr(name, '@')) 1646 realwho = g_strdup_printf("%s@%s", name, gjc->user->server); 1647 else { 1648 jid who = jid_new(gjc->p, name); 1649 if (who->user == NULL) { 1650 /* FIXME: transport */ 1651 return; 1652 } 1653 realwho = g_strdup_printf("%s@%s", who->user, who->server); 1654 } 1655 1656 1657 x = jutil_iqnew(JPACKET__SET, NS_ROSTER); 1658 y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item"); 1659 xmlnode_put_attrib(y, "jid", realwho); 1660 1661 1662 /* If we can find the buddy, there's an alias for him, it's not 0-length 1663 * and it doesn't match his JID, add the "name" attribute. 1664 */ 1665 if((buddy = find_buddy(gc, realwho)) != NULL && 1666 buddy->show != NULL && buddy->show[0] != '\0' && strcmp(realwho, buddy->show)) { 1667 1668 xmlnode_put_attrib(y, "name", buddy->show); 1669 } 1670 1671 /* 1672 * Find out what group the buddy's in and send that along 1673 * with the roster item. 1674 */ 1675 /* ** Bitlbee disabled ** 1676 if((buddy_group = NULL) != NULL) { 1677 xmlnode z; 1678 z = xmlnode_insert_tag(y, "group"); 1679 xmlnode_insert_cdata(z, buddy_group->name, -1); 1680 } 1681 ** End - Bitlbee ** */ 1682 1683 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); 1684 1685 xmlnode_free(x); 1686 g_free(realwho); 1687 } 1688 } 1689 1690 /* 1691 * Change buddy's group on server roster 1692 */ 1693 static void jabber_group_change(struct gaim_connection *gc, char *name, char *old_group, char *new_group) 1694 { 1695 if(strcmp(old_group, new_group)) { 1696 jabber_roster_update(gc, name); 1697 } 1698 } 1699 1700 static void jabber_add_buddy(struct gaim_connection *gc, char *name) 1701 { 1702 xmlnode x; 1703 char *realwho; 1704 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; 1705 1706 if (!((struct jabber_data *)gc->proto_data)->did_import) 1707 return; 1708 1709 if (!name) 1710 return; 1711 1712 if (!strcmp(gc->username, name)) 1713 return; 1714 1715 if (!strchr(name, '@')) 1716 realwho = g_strdup_printf("%s@%s", name, gjc->user->server); 1717 else { 1718 jid who; 1719 1720 if((who = jid_new(gjc->p, name)) == NULL) { 1721 char *msg = g_strdup_printf("%s: \"%s\"", _("Invalid Jabber I.D."), name); 1722 do_error_dialog(GJ_GC(gjc), msg, _("Jabber Error")); 1723 g_free(msg); 1724 jabber_remove_gaim_buddy(gc, name); 1725 return; 1726 } 1727 if (who->user == NULL) { 1728 /* FIXME: transport */ 1729 return; 1730 } 1731 realwho = g_strdup_printf("%s@%s", who->user, who->server); 1732 } 1733 1734 x = xmlnode_new_tag("presence"); 1735 xmlnode_put_attrib(x, "to", realwho); 1736 xmlnode_put_attrib(x, "type", "subscribe"); 1737 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); 1738 xmlnode_free(x); 1739 1740 jabber_roster_update(gc, realwho); 1741 1742 g_free(realwho); 1743 } 1744 1745 static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group) 1746 { 1747 xmlnode x; 1748 char *realwho; 1749 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; 1750 1751 if (!name) 1752 return; 1753 1754 if (!strchr(name, '@')) 1755 realwho = g_strdup_printf("%s@%s", name, gjc->user->server); 1756 else 1757 realwho = g_strdup(name); 1758 1759 x = xmlnode_new_tag("presence"); 1760 xmlnode_put_attrib(x, "to", realwho); 1761 xmlnode_put_attrib(x, "type", "unsubscribe"); 1762 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); 1763 g_free(realwho); 1764 xmlnode_free(x); 1765 } 1766 1767 static void jabber_get_info(struct gaim_connection *gc, char *who) { 1768 xmlnode x; 1769 char *id; 1770 char *realwho; 1771 struct jabber_data *jd = gc->proto_data; 1772 gjconn gjc = jd->gjc; 1773 1774 x = jutil_iqnew(JPACKET__GET, NS_VCARD); 1775 /* Bare username? */ 1776 if (!strchr(who, '@')) { 1777 realwho = g_strdup_printf("%s@%s", who, gjc->user->server); 1778 } else { 1779 realwho = g_strdup(who); 1780 } 1781 xmlnode_put_attrib(x, "to", realwho); 1782 g_free(realwho); 1783 1784 id = gjab_getid(gjc); 1785 xmlnode_put_attrib(x, "id", id); 1786 1787 g_hash_table_insert(jd->gjc->queries, g_strdup(id), g_strdup("vCard")); 1788 1789 gjab_send(gjc, x); 1790 1791 xmlnode_free(x); 1792 1793 } 1794 1795 static void jabber_get_away_msg(struct gaim_connection *gc, char *who) { 1796 struct jabber_data *jd = gc->proto_data; 1797 gjconn gjc = jd->gjc; 1798 char *status; 1799 1800 /* space for all elements: Jabber I.D. + "status" + NULL (list terminator) */ 1801 gchar **str_arr = (gchar **) g_new(gpointer, 3); 1802 gchar **ap = str_arr; 1803 gchar *realwho, *final; 1804 1805 /* Bare username? */ 1806 if (!strchr(who, '@')) { 1807 realwho = g_strdup_printf("%s@%s", who, gjc->user->server); 1808 } else { 1809 realwho = g_strdup(who); 1810 } 1811 *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho); 1812 1813 if((status = g_hash_table_lookup(jd->hash, realwho)) == NULL) { 1814 status = _("Unknown"); 1815 } 1816 *ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status); 1817 1818 *ap = NULL; 1819 1820 final= g_strjoinv(NULL, str_arr); 1821 g_strfreev(str_arr); 1822 1823 g_free(realwho); 1824 g_free(final); 1825 1826 } 1827 1828 static GList *jabber_away_states(struct gaim_connection *gc) { 1829 GList *m = NULL; 1830 1831 m = g_list_append(m, "Online"); 1832 m = g_list_append(m, "Chatty"); 1833 m = g_list_append(m, "Away"); 1834 m = g_list_append(m, "Extended Away"); 1835 m = g_list_append(m, "Do Not Disturb"); 1836 1837 return m; 1838 } 1839 1840 static void jabber_set_away(struct gaim_connection *gc, char *state, char *message) 1841 { 1842 xmlnode x, y; 1843 struct jabber_data *jd = gc->proto_data; 1844 gjconn gjc = jd->gjc; 1845 1846 gc->away = NULL; /* never send an auto-response */ 1847 1848 x = xmlnode_new_tag("presence"); 1849 1850 if (!strcmp(state, GAIM_AWAY_CUSTOM)) { 1851 /* oh goody. Gaim is telling us what to do. */ 1852 if (message) { 1853 /* Gaim wants us to be away */ 1854 y = xmlnode_insert_tag(x, "show"); 1855 xmlnode_insert_cdata(y, "away", -1); 1856 y = xmlnode_insert_tag(x, "status"); 1857 xmlnode_insert_cdata(y, message, -1); 1858 gc->away = ""; 1859 } else { 1860 /* Gaim wants us to not be away */ 1861 /* but for Jabber, we can just send presence with no other information. */ 1862 } 1863 } else { 1864 /* state is one of our own strings. it won't be NULL. */ 1865 if (!g_strcasecmp(state, "Online")) { 1866 /* once again, we don't have to put anything here */ 1867 } else if (!g_strcasecmp(state, "Chatty")) { 1868 y = xmlnode_insert_tag(x, "show"); 1869 xmlnode_insert_cdata(y, "chat", -1); 1870 } else if (!g_strcasecmp(state, "Away")) { 1871 y = xmlnode_insert_tag(x, "show"); 1872 xmlnode_insert_cdata(y, "away", -1); 1873 gc->away = ""; 1874 } else if (!g_strcasecmp(state, "Extended Away")) { 1875 y = xmlnode_insert_tag(x, "show"); 1876 xmlnode_insert_cdata(y, "xa", -1); 1877 gc->away = ""; 1878 } else if (!g_strcasecmp(state, "Do Not Disturb")) { 1879 y = xmlnode_insert_tag(x, "show"); 1880 xmlnode_insert_cdata(y, "dnd", -1); 1881 gc->away = ""; 1882 } 1883 } 1884 1885 gjab_send(gjc, x); 1886 xmlnode_free(x); 1887 } 1888 1889 static void jabber_keepalive(struct gaim_connection *gc) { 1890 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; 1891 gjab_send_raw(jd->gjc, " \t "); 1892 } 1893 1894 /*---------------------------------------*/ 1895 /* Jabber "set info" (vCard) support */ 1896 /*---------------------------------------*/ 1897 1898 /* 1899 * V-Card format: 1900 * 1901 * <vCard prodid='' version='' xmlns=''> 1902 * <FN></FN> 1903 * <N> 1904 * <FAMILY/> 1905 * <GIVEN/> 1906 * </N> 1907 * <NICKNAME/> 1908 * <URL/> 1909 * <ADR> 1910 * <STREET/> 1911 * <EXTADD/> 1912 * <LOCALITY/> 1913 * <REGION/> 1914 * <PCODE/> 1915 * <COUNTRY/> 1916 * </ADR> 1917 * <TEL/> 1918 * <EMAIL/> 1919 * <ORG> 1920 * <ORGNAME/> 1921 * <ORGUNIT/> 1922 * </ORG> 1923 * <TITLE/> 1924 * <ROLE/> 1925 * <DESC/> 1926 * <BDAY/> 1927 * </vCard> 1928 * 1929 * See also: 1930 * 1931 * http://docs.jabber.org/proto/html/vcard-temp.html 1932 * http://www.vcard-xml.org/dtd/vCard-XML-v2-20010520.dtd 1933 */ 1934 1935 /* 1936 * Cross-reference user-friendly V-Card entry labels to vCard XML tags 1937 * and attributes. 1938 * 1939 * Order is (or should be) unimportant. For example: we have no way of 1940 * knowing in what order real data will arrive. 1941 * 1942 * Format: Label, Pre-set text, "visible" flag, "editable" flag, XML tag 1943 * name, XML tag's parent tag "path" (relative to vCard node). 1944 * 1945 * List is terminated by a NULL label pointer. 1946 * 1947 * Entries with no label text, but with XML tag and parent tag 1948 * entries, are used by V-Card XML construction routines to 1949 * "automagically" construct the appropriate XML node tree. 1950 * 1951 * Thoughts on future direction/expansion 1952 * 1953 * This is a "simple" vCard. 1954 * 1955 * It is possible for nodes other than the "vCard" node to have 1956 * attributes. Should that prove necessary/desirable, add an 1957 * "attributes" pointer to the vcard_template struct, create the 1958 * necessary tag_attr structs, and add 'em to the vcard_dflt_data 1959 * array. 1960 * 1961 * The above changes will (obviously) require changes to the vCard 1962 * construction routines. 1963 */ 1964 1965 static struct vcard_template { 1966 char *label; /* label text pointer */ 1967 char *text; /* entry text pointer */ 1968 int visible; /* should entry field be "visible?" */ 1969 int editable; /* should entry field be editable? */ 1970 char *tag; /* tag text */ 1971 char *ptag; /* parent tag "path" text */ 1972 char *url; /* vCard display format if URL */ 1973 } vcard_template_data[] = { 1974 {N_("Full Name"), NULL, TRUE, TRUE, "FN", NULL, NULL}, 1975 {N_("Family Name"), NULL, TRUE, TRUE, "FAMILY", "N", NULL}, 1976 {N_("Given Name"), NULL, TRUE, TRUE, "GIVEN", "N", NULL}, 1977 {N_("Nickname"), NULL, TRUE, TRUE, "NICKNAME", NULL, NULL}, 1978 {N_("URL"), NULL, TRUE, TRUE, "URL", NULL, "<A HREF=\"%s\">%s</A>"}, 1979 {N_("Street Address"), NULL, TRUE, TRUE, "STREET", "ADR", NULL}, 1980 {N_("Extended Address"), NULL, TRUE, TRUE, "EXTADD", "ADR", NULL}, 1981 {N_("Locality"), NULL, TRUE, TRUE, "LOCALITY", "ADR", NULL}, 1982 {N_("Region"), NULL, TRUE, TRUE, "REGION", "ADR", NULL}, 1983 {N_("Postal Code"), NULL, TRUE, TRUE, "PCODE", "ADR", NULL}, 1984 {N_("Country"), NULL, TRUE, TRUE, "COUNTRY", "ADR", NULL}, 1985 {N_("Telephone"), NULL, TRUE, TRUE, "TELEPHONE", NULL, NULL}, 1986 {N_("Email"), NULL, TRUE, TRUE, "EMAIL", NULL, "<A HREF=\"mailto:%s\">%s</A>"}, 1987 {N_("Organization Name"), NULL, TRUE, TRUE, "ORGNAME", "ORG", NULL}, 1988 {N_("Organization Unit"), NULL, TRUE, TRUE, "ORGUNIT", "ORG", NULL}, 1989 {N_("Title"), NULL, TRUE, TRUE, "TITLE", NULL, NULL}, 1990 {N_("Role"), NULL, TRUE, TRUE, "ROLE", NULL, NULL}, 1991 {N_("Birthday"), NULL, TRUE, TRUE, "BDAY", NULL, NULL}, 1992 {N_("Description"), NULL, TRUE, TRUE, "DESC", NULL, NULL}, 1993 {"", NULL, TRUE, TRUE, "N", NULL, NULL}, 1994 {"", NULL, TRUE, TRUE, "ADR", NULL, NULL}, 1995 {"", NULL, TRUE, TRUE, "ORG", NULL, NULL}, 1996 {NULL, NULL, 0, 0, NULL, NULL, NULL} 1997 }; 1998 1999 /* 2000 * Used by routines to parse an XML-encoded string into an xmlnode tree 2001 */ 2002 typedef struct { 2003 XML_Parser parser; 2004 xmlnode current; 2005 } *xmlstr2xmlnode_parser, xmlstr2xmlnode_parser_struct; 2006 2007 2008 /* 2009 * Used by XML_Parse on parsing CDATA 2010 */ 2011 static void xmlstr2xmlnode_charData(void *userdata, const char *s, int slen) 2012 { 2013 xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata; 2014 2015 if (xmlp->current) 2016 xmlnode_insert_cdata(xmlp->current, s, slen); 2017 } 2018 2019 /* 2020 * Used by XML_Parse to start or append to an xmlnode 2021 */ 2022 static void xmlstr2xmlnode_startElement(void *userdata, const char *name, const char **attribs) 2023 { 2024 xmlnode x; 2025 xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata; 2026 2027 if (xmlp->current) { 2028 /* Append the node to the current one */ 2029 x = xmlnode_insert_tag(xmlp->current, name); 2030 xmlnode_put_expat_attribs(x, attribs); 2031 2032 xmlp->current = x; 2033 } else { 2034 x = xmlnode_new_tag(name); 2035 xmlnode_put_expat_attribs(x, attribs); 2036 xmlp->current = x; 2037 } 2038 } 2039 2040 /* 2041 * Used by XML_Parse to end an xmlnode 2042 */ 2043 static void xmlstr2xmlnode_endElement(void *userdata, const char *name) 2044 { 2045 xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata; 2046 xmlnode x; 2047 2048 if (xmlp->current != NULL && (x = xmlnode_get_parent(xmlp->current)) != NULL) { 2049 xmlp->current = x; 2050 } 2051 } 2052 2053 /* 2054 * Parse an XML-encoded string into an xmlnode tree 2055 * 2056 * Caller is responsible for freeing the returned xmlnode 2057 */ 2058 static xmlnode xmlstr2xmlnode(char *xmlstring) 2059 { 2060 xmlstr2xmlnode_parser my_parser = g_new(xmlstr2xmlnode_parser_struct, 1); 2061 xmlnode x = NULL; 2062 2063 my_parser->parser = XML_ParserCreate(NULL); 2064 my_parser->current = NULL; 2065 2066 XML_SetUserData(my_parser->parser, (void *)my_parser); 2067 XML_SetElementHandler(my_parser->parser, xmlstr2xmlnode_startElement, xmlstr2xmlnode_endElement); 2068 XML_SetCharacterDataHandler(my_parser->parser, xmlstr2xmlnode_charData); 2069 XML_Parse(my_parser->parser, xmlstring, strlen(xmlstring), 0); 2070 2071 x = my_parser->current; 2072 2073 XML_ParserFree(my_parser->parser); 2074 g_free(my_parser); 2075 2076 return(x); 2077 } 2078 2079 /* 2080 * Insert a tag node into an xmlnode tree, recursively inserting parent tag 2081 * nodes as necessary 2082 * 2083 * Returns pointer to inserted node 2084 * 2085 * Note to hackers: this code is designed to be re-entrant (it's recursive--it 2086 * calls itself), so don't put any "static"s in here! 2087 */ 2088 static xmlnode insert_tag_to_parent_tag(xmlnode start, const char *parent_tag, const char *new_tag) 2089 { 2090 xmlnode x = NULL; 2091 2092 /* 2093 * If the parent tag wasn't specified, see if we can get it 2094 * from the vCard template struct. 2095 */ 2096 if(parent_tag == NULL) { 2097 struct vcard_template *vc_tp = vcard_template_data; 2098 2099 while(vc_tp->label != NULL) { 2100 if(strcmp(vc_tp->tag, new_tag) == 0) { 2101 parent_tag = vc_tp->ptag; 2102 break; 2103 } 2104 ++vc_tp; 2105 } 2106 } 2107 2108 /* 2109 * If we have a parent tag... 2110 */ 2111 if(parent_tag != NULL ) { 2112 /* 2113 * Try to get the parent node for a tag 2114 */ 2115 if((x = xmlnode_get_tag(start, parent_tag)) == NULL) { 2116 /* 2117 * Descend? 2118 */ 2119 char *grand_parent = strcpy(g_malloc(strlen(parent_tag) + 1), parent_tag); 2120 char *parent; 2121 2122 if((parent = strrchr(grand_parent, '/')) != NULL) { 2123 *(parent++) = '\0'; 2124 x = insert_tag_to_parent_tag(start, grand_parent, parent); 2125 } else { 2126 x = xmlnode_insert_tag(start, grand_parent); 2127 } 2128 g_free(grand_parent); 2129 } else { 2130 /* 2131 * We found *something* to be the parent node. 2132 * Note: may be the "root" node! 2133 */ 2134 xmlnode y; 2135 if((y = xmlnode_get_tag(x, new_tag)) != NULL) { 2136 return(y); 2137 } 2138 } 2139 } 2140 2141 /* 2142 * insert the new tag into its parent node 2143 */ 2144 return(xmlnode_insert_tag((x == NULL? start : x), new_tag)); 2145 } 2146 2147 /* 2148 * Send vCard info to Jabber server 2149 */ 2150 static void jabber_set_info(struct gaim_connection *gc, char *info) 2151 { 2152 xmlnode x, vc_node; 2153 char *id; 2154 struct jabber_data *jd = gc->proto_data; 2155 gjconn gjc = jd->gjc; 2156 2157 x = xmlnode_new_tag("iq"); 2158 xmlnode_put_attrib(x,"type","set"); 2159 2160 id = gjab_getid(gjc); 2161 2162 xmlnode_put_attrib(x, "id", id); 2163 2164 /* 2165 * Send only if there's actually any *information* to send 2166 */ 2167 if((vc_node = xmlstr2xmlnode(info)) != NULL && xmlnode_get_name(vc_node) != NULL && 2168 g_strncasecmp(xmlnode_get_name(vc_node), "vcard", 5) == 0) { 2169 xmlnode_insert_tag_node(x, vc_node); 2170 gjab_send(gjc, x); 2171 } 2172 2173 xmlnode_free(x); 2174 } 2175 2176 /* 2177 * displays a Jabber vCard 2178 */ 2179 static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from) 2180 { 2181 struct jabber_data *jd = GJ_GC(gjc)->proto_data; 2182 jid who = jid_new(gjc->p, from); 2183 char *status = NULL, *text = NULL; 2184 GString *str = g_string_sized_new(100); 2185 xmlnode child; 2186 2187 gchar *buddy = NULL; 2188 2189 if(querynode == NULL) { 2190 serv_got_crap(GJ_GC(gjc), "%s - Received empty info reply from %s", _("User Info"), from); 2191 return; 2192 } 2193 2194 if(who->resource != NULL && (who->resource)[0] != '\0') { 2195 buddy = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource); 2196 } else { 2197 buddy = g_strdup_printf("%s@%s", who->user, who->server); 2198 } 2199 2200 if((status = g_hash_table_lookup(jd->hash, buddy)) == NULL) { 2201 status = _("Unknown"); 2202 } 2203 2204 g_string_sprintfa(str, "%s: %s - %s: %s", _("Jabber ID"), buddy, _("Status"), 2205 status); 2206 2207 for(child = querynode->firstchild; child; child = child->next) 2208 { 2209 xmlnode child2; 2210 2211 if(child->type != NTYPE_TAG) 2212 continue; 2213 2214 text = xmlnode_get_data(child); 2215 if(text && !strcmp(child->name, "FN")) { 2216 info_string_append(str, "\n", _("Full Name"), text); 2217 } else if (!strcmp(child->name, "N")) { 2218 for (child2 = child->firstchild; child2; child2 = child2->next) { 2219 char *text2 = NULL; 2220 2221 if (child2->type != NTYPE_TAG) 2222 continue; 2223 2224 text2 = xmlnode_get_data(child2); 2225 if (text2 && !strcmp(child2->name, "FAMILY")) { 2226 info_string_append(str, "\n", _("Family Name"), text2); 2227 } else if (text2 && !strcmp(child2->name, "GIVEN")) { 2228 info_string_append(str, "\n", _("Given Name"), text2); 2229 } else if (text2 && !strcmp(child2->name, "MIDDLE")) { 2230 info_string_append(str, "\n", _("Middle Name"), text2); 2231 } 2232 } 2233 } else if (text && !strcmp(child->name, "NICKNAME")) { 2234 info_string_append(str, "\n", _("Nickname"), text); 2235 } else if (text && !strcmp(child->name, "BDAY")) { 2236 info_string_append(str, "\n", _("Birthday"), text); 2237 } else if (!strcmp(child->name, "ADR")) { 2238 /* show wich address it is */ 2239 /* Just for the beauty of bitlbee 2240 if (child->firstchild) 2241 g_string_sprintfa(str, "%s:\n", _("Address")); 2242 */ 2243 for(child2 = child->firstchild; child2; child2 = child2->next) { 2244 char *text2 = NULL; 2245 2246 if(child2->type != NTYPE_TAG) 2247 continue; 2248 2249 text2 = xmlnode_get_data(child2); 2250 if(text2 && !strcmp(child2->name, "POBOX")) { 2251 info_string_append(str, "\n", 2252 _("P.O. Box"), text2); 2253 } else if(text2 && !strcmp(child2->name, "EXTADR")) { 2254 info_string_append(str, "\n", 2255 _("Extended Address"), text2); 2256 } else if(text2 && !strcmp(child2->name, "STREET")) { 2257 info_string_append(str, "\n", 2258 _("Street Address"), text2); 2259 } else if(text2 && !strcmp(child2->name, "LOCALITY")) { 2260 info_string_append(str, "\n", 2261 _("Locality"), text2); 2262 } else if(text2 && !strcmp(child2->name, "REGION")) { 2263 info_string_append(str, "\n", 2264 _("Region"), text2); 2265 } else if(text2 && !strcmp(child2->name, "PCODE")) { 2266 info_string_append(str, "\n", 2267 _("Postal Code"), text2); 2268 } else if(text2 && (!strcmp(child2->name, "CTRY") 2269 || !strcmp(child2->name, "COUNTRY"))) { 2270 info_string_append(str, "\n", _("Country"), text2); 2271 } 2272 } 2273 } else if(!strcmp(child->name, "TEL")) { 2274 char *number = NULL; 2275 if ((child2 = xmlnode_get_tag(child, "NUMBER"))) { 2276 /* show what kind of number it is */ 2277 number = xmlnode_get_data(child2); 2278 if(number) { 2279 info_string_append(str, "\n", _("Telephone"), number); 2280 } 2281 } else if((number = xmlnode_get_data(child))) { 2282 /* lots of clients (including gaim) do this, 2283 * but it's out of spec */ 2284 info_string_append(str, "\n", _("Telephone"), number); 2285 } 2286 } else if(!strcmp(child->name, "EMAIL")) { 2287 char *userid = NULL; 2288 if((child2 = xmlnode_get_tag(child, "USERID"))) { 2289 /* show what kind of email it is */ 2290 userid = xmlnode_get_data(child2); 2291 if(userid) { 2292 info_string_append(str, "\n", _("Email"), userid); 2293 } 2294 } else if((userid = xmlnode_get_data(child))) { 2295 /* lots of clients (including gaim) do this, 2296 * but it's out of spec */ 2297 info_string_append(str, "\n", _("Email"), userid); 2298 } 2299 } else if(!strcmp(child->name, "ORG")) { 2300 for(child2 = child->firstchild; child2; child2 = child2->next) { 2301 char *text2 = NULL; 2302 2303 if(child2->type != NTYPE_TAG) 2304 continue; 2305 2306 text2 = xmlnode_get_data(child2); 2307 if(text2 && !strcmp(child2->name, "ORGNAME")) { 2308 info_string_append(str, "\n", _("Organization Name"), text2); 2309 } else if(text2 && !strcmp(child2->name, "ORGUNIT")) { 2310 info_string_append(str, "\n", _("Organization Unit"), text2); 2311 } 2312 } 2313 } else if(text && !strcmp(child->name, "TITLE")) { 2314 info_string_append(str, "\n", _("Title"), text); 2315 } else if(text && !strcmp(child->name, "ROLE")) { 2316 info_string_append(str, "\n", _("Role"), text); 2317 } else if(text && !strcmp(child->name, "DESC")) { 2318 g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Description"), 2319 text, _("End of Description")); 2320 } 2321 } 2322 2323 serv_got_crap(GJ_GC(gjc), "%s\n%s", _("User Info"), str->str); 2324 2325 g_free(buddy); 2326 g_string_free(str, TRUE); 2327 } 2328 2329 void jabber_init() 2330 { 2331 struct prpl *ret = g_new0(struct prpl, 1); 2332 509 void jabber_initmodule() 510 { 511 struct prpl *ret = g_new0( struct prpl, 1 ); 512 2333 513 ret->name = "jabber"; 514 ret->login = jabber_login; 515 ret->init = jabber_init; 516 ret->logout = jabber_logout; 517 ret->buddy_msg = jabber_buddy_msg; 2334 518 ret->away_states = jabber_away_states; 2335 ret->login = jabber_login; 2336 ret->close = jabber_close; 2337 ret->send_im = jabber_send_im; 2338 ret->set_info = jabber_set_info; 519 ret->set_away = jabber_set_away; 520 // ret->set_info = jabber_set_info; 2339 521 ret->get_info = jabber_get_info; 2340 ret->set_away = jabber_set_away;2341 ret->get_away = jabber_get_away_msg;2342 522 ret->add_buddy = jabber_add_buddy; 2343 523 ret->remove_buddy = jabber_remove_buddy; 524 ret->chat_msg = jabber_chat_msg_; 525 ret->chat_topic = jabber_chat_topic_; 526 ret->chat_invite = jabber_chat_invite_; 527 ret->chat_leave = jabber_chat_leave_; 528 ret->chat_join = jabber_chat_join_; 2344 529 ret->keepalive = jabber_keepalive; 2345 ret->alias_buddy = jabber_roster_update; 2346 ret->group_buddy = jabber_group_change; 2347 ret->cmp_buddynames = g_strcasecmp; 2348 2349 register_protocol (ret); 2350 } 530 ret->send_typing = jabber_send_typing; 531 ret->handle_cmp = g_strcasecmp; 532 533 register_protocol( ret ); 534 }
Note: See TracChangeset
for help on using the changeset viewer.