Ignore:
Timestamp:
2008-04-02T14:22:57Z (16 years ago)
Author:
Jelmer Vernooij <jelmer@…>
Branches:
master
Children:
f9dbc99
Parents:
875ad42 (diff), dd34575 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge trunk.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • protocols/jabber/jabber.c

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