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