Ignore:
Timestamp:
2007-02-18T17:48:04Z (14 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
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.
Message:

Merging new Jabber module. See the massive changelog for all the cool
improvements. :-)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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>
    2825#include <string.h>
    29 #include <stdlib.h>
     26#include <unistd.h>
     27#include <ctype.h>
    3028#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"
    3333#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
     35static void jabber_acc_init( account_t *acc )
    149936{
    150037        set_t *s;
     
    150340        s->flags |= ACC_SET_OFFLINE_ONLY;
    150441       
     42        s = set_add( &acc->set, "priority", "0", set_eval_priority, acc );
     43       
    150544        s = set_add( &acc->set, "resource", "BitlBee", NULL, acc );
    150645        s->flags |= ACC_SET_OFFLINE_ONLY;
    150746       
     47        s = set_add( &acc->set, "resource_select", "priority", NULL, acc );
     48       
    150849        s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
    150950        s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
     
    151152        s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc );
    151253        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
     59static 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>)" );
    155475                signoff( gc );
    155576                return;
    155677        }
    155778       
    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 );
    1613115                }
    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;
    1621139                }
    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 ++;
    1628146                }
    1629147        }
    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
     184static 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
     212static 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
     250static 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
     262static 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
     288static 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
     302static 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
     308static 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
     317static 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
     327static 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. */
    1648339                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       
    1669368        return 1;
    1670369}
    1671370
    1672 /*
    1673  * Add/update buddy's roster entry on server
    1674  */
    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-length
    1704                  * 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 along
    1714                  * 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 roster
    1733  */
    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         else
    1798                 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.html
    1973  *      http://www.vcard-xml.org/dtd/vCard-XML-v2-20010520.dtd
    1974  */
    1975 
    1976 /*
    1977  * Cross-reference user-friendly V-Card entry labels to vCard XML tags
    1978  * and attributes.
    1979  *
    1980  * Order is (or should be) unimportant.  For example: we have no way of
    1981  * knowing in what order real data will arrive.
    1982  *
    1983  * Format: Label, Pre-set text, "visible" flag, "editable" flag, XML tag
    1984  *         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 tag
    1989  *         entries, are used by V-Card XML construction routines to
    1990  *         "automagically" construct the appropriate XML node tree.
    1991  *
    1992  * Thoughts on future direction/expansion
    1993  *
    1994  *      This is a "simple" vCard.
    1995  *
    1996  *      It is possible for nodes other than the "vCard" node to have
    1997  *      attributes.  Should that prove necessary/desirable, add an
    1998  *      "attributes" pointer to the vcard_template struct, create the
    1999  *      necessary tag_attr structs, and add 'em to the vcard_dflt_data
    2000  *      array.
    2001  *
    2002  *      The above changes will (obviously) require changes to the vCard
    2003  *      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 tree
    2042  */
    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 CDATA
    2051  */
    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 xmlnode
    2062  */
    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 xmlnode
    2083  */
    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 tree
    2096  *
    2097  * Caller is responsible for freeing the returned xmlnode
    2098  */
    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 tag
    2122  * nodes as necessary
    2123  *
    2124  * Returns pointer to inserted node
    2125  *
    2126  * Note to hackers: this code is designed to be re-entrant (it's recursive--it
    2127  * 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 it
    2135          * 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 tag
    2155                  */
    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 node
    2184          */
    2185         return(xmlnode_insert_tag((x == NULL? start : x), new_tag));
    2186 }
    2187 
    2188 /*
    2189  * Send vCard info to Jabber server
    2190  */
    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 send
    2207          */
    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 vCard
    2219  */
    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 bitlbee
    2281                         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 
    2370371void jabber_init()
    2371372{
    2372         struct prpl *ret = g_new0(struct prpl, 1);
    2373 
     373        struct prpl *ret = g_new0( struct prpl, 1 );
     374       
    2374375        ret->name = "jabber";
    2375         ret->away_states = jabber_away_states;
     376        ret->login = jabber_login;
    2376377        ret->acc_init = jabber_acc_init;
    2377         ret->login = jabber_login;
    2378378        ret->close = jabber_close;
    2379379        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;
    2381384        ret->get_info = jabber_get_info;
    2382         ret->set_away = jabber_set_away;
    2383         ret->get_away = jabber_get_away_msg;
    2384385        ret->add_buddy = jabber_add_buddy;
    2385386        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;
    2386391        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;
    2389393        ret->handle_cmp = g_strcasecmp;
    2390394
    2391         register_protocol (ret);
    2392 }
     395        register_protocol( ret );
     396}
Note: See TracChangeset for help on using the changeset viewer.