Changeset 85d7b85 for protocols


Ignore:
Timestamp:
2008-04-02T14:22:57Z (13 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.

Location:
protocols
Files:
7 added
34 deleted
35 edited

Legend:

Unmodified
Added
Removed
  • protocols/Makefile

    r875ad42 r85d7b85  
    1010
    1111# [SH] Program variables
    12 objects = http_client.o md5.o nogaim.o proxy.o sha.o $(SSL_CLIENT)
     12objects = nogaim.o
    1313
    1414# [SH] The next two lines should contain the directory name (in $(subdirs))
     
    2626# [SH] Phony targets
    2727all: protocols.o
     28check: all
     29lcov: check
     30gcov:
     31        gcov *.c
    2832
    2933.PHONY: all clean distclean $(subdirs)
  • protocols/jabber/Makefile

    r875ad42 r85d7b85  
    1010
    1111# [SH] Program variables
    12 objects = expat.o hashtable.o jid.o jpacket.o jutil.o log.o pool.o str.o xmlnode.o xmlparse.o xmlrole.o xmltok.o jabber.o
     12objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o
    1313
    1414CFLAGS += -Wall
     
    1717# [SH] Phony targets
    1818all: jabber_mod.o
     19check: all
     20lcov: check
     21gcov:
     22        gcov *.c
    1923
    2024.PHONY: all clean distclean
  • 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}
  • protocols/jabber/jabber.h

    r875ad42 r85d7b85  
    1 /*
    2  *  This program is free software; you can redistribute it and/or modify
    3  *  it under the terms of the GNU General Public License as published by
    4  *  the Free Software Foundation; either version 2 of the License, or
    5  *  (at your option) any later version.
    6  *
    7  *  This program is distributed in the hope that it will be useful,
    8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    10  *  GNU General Public License for more details.
    11  *
    12  *  You should have received a copy of the GNU General Public License
    13  *  along with this program; if not, write to the Free Software
    14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    15  *
    16  *  Jabber
    17  *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
    18  */
    19 
    20 #include <string.h>
    21 #include <stdlib.h>
    22 #include <sys/types.h>
    23 #include <stdio.h>
    24 #include <setjmp.h>
    25 #include <sys/stat.h>
    26 #include <fcntl.h>
    27 #include <errno.h>
    28 #include <signal.h>
    29 #include <stdarg.h>
    30 #include <time.h>
    31 #include <ctype.h>
    32 #ifdef _WIN32
    33 #undef DATADIR
    34 #include "sock.h"
     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#ifndef _JABBER_H
     25#define _JABBER_H
     26
     27#include <glib.h>
     28
     29#include "xmltree.h"
     30#include "bitlbee.h"
     31
     32extern GSList *jabber_connections;
     33
     34typedef enum
     35{
     36        JFLAG_STREAM_STARTED = 1,       /* Set when we detected the beginning of the stream
     37                                           and want to do auth. */
     38        JFLAG_AUTHENTICATED = 2,        /* Set when we're successfully authenticatd. */
     39        JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after
     40                                           SASL or TLS). */
     41        JFLAG_WAIT_SESSION = 8,         /* Set if we sent a <session> tag and need a reply
     42                                           before we continue. */
     43        JFLAG_WAIT_BIND = 16,           /* ... for <bind> tag. */
     44        JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this
     45                                           activates all XEP-85 related code. */
     46        JFLAG_XMLCONSOLE = 64,          /* If the user added an xmlconsole buddy. */
     47} jabber_flags_t;
     48
     49typedef enum
     50{
     51        JBFLAG_PROBED_XEP85 = 1,        /* Set this when we sent our probe packet to make
     52                                           sure it gets sent only once. */
     53        JBFLAG_DOES_XEP85 = 2,          /* Set this when the resource seems to support
     54                                           XEP85 (typing notification shite). */
     55        JBFLAG_IS_CHATROOM = 4,         /* It's convenient to use this JID thingy for
     56                                           groupchat state info too. */
     57        JBFLAG_IS_ANONYMOUS = 8,        /* For anonymous chatrooms, when we don't have
     58                                           have a real JID. */
     59} jabber_buddy_flags_t;
     60
     61typedef enum
     62{
     63        JCFLAG_MESSAGE_SENT = 1,        /* Set this after sending the first message, so
     64                                           we can detect echoes/backlogs. */
     65} jabber_chat_flags_t;
     66
     67struct jabber_data
     68{
     69        struct im_connection *ic;
     70       
     71        int fd;
     72        void *ssl;
     73        char *txq;
     74        int tx_len;
     75        int r_inpa, w_inpa;
     76       
     77        struct xt_parser *xt;
     78        jabber_flags_t flags;
     79       
     80        char *username;         /* USERNAME@server */
     81        char *server;           /* username@SERVER -=> server/domain, not hostname */
     82       
     83        /* After changing one of these two (or the priority setting), call
     84           presence_send_update() to inform the server about the changes. */
     85        struct jabber_away_state *away_state;
     86        char *away_message;
     87       
     88        char *cached_id_prefix;
     89        GHashTable *node_cache;
     90        GHashTable *buddies;
     91};
     92
     93struct jabber_away_state
     94{
     95        char code[5];
     96        char *full_name;
     97};
     98
     99typedef xt_status (*jabber_cache_event) ( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
     100
     101struct jabber_cache_entry
     102{
     103        time_t saved_at;
     104        struct xt_node *node;
     105        jabber_cache_event func;
     106};
     107
     108struct jabber_buddy
     109{
     110        char *bare_jid;
     111        char *full_jid;
     112        char *resource;
     113       
     114        char *ext_jid; /* The JID to use in BitlBee. The real JID if possible, */
     115                       /* otherwise something similar to the conference JID. */
     116       
     117        int priority;
     118        struct jabber_away_state *away_state;
     119        char *away_message;
     120       
     121        time_t last_act;
     122        jabber_buddy_flags_t flags;
     123       
     124        struct jabber_buddy *next;
     125};
     126
     127struct jabber_chat
     128{
     129        int flags;
     130        char *name;
     131        char *my_full_jid; /* Separate copy because of case sensitivity. */
     132        struct jabber_buddy *me;
     133};
     134
     135#define JABBER_XMLCONSOLE_HANDLE "xmlconsole"
     136
     137#define JABBER_PORT_DEFAULT "5222"
     138#define JABBER_PORT_MIN 5220
     139#define JABBER_PORT_MAX 5229
     140
     141/* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the
     142   first one should be used, but when storing a packet in the cache, a
     143   "special" kind of ID is assigned to make it easier later to figure out
     144   if we have to do call an event handler for the response packet. Also
     145   we'll append a hash to make sure we won't trigger on cached packets from
     146   other BitlBee users. :-) */
     147#define JABBER_PACKET_ID "BeeP"
     148#define JABBER_CACHED_ID "BeeC"
     149
     150/* The number of seconds to keep cached packets before garbage collecting
     151   them. This gc is done on every keepalive (every minute). */
     152#define JABBER_CACHE_MAX_AGE 600
     153
     154/* RFC 392[01] stuff */
     155#define XMLNS_TLS          "urn:ietf:params:xml:ns:xmpp-tls"
     156#define XMLNS_SASL         "urn:ietf:params:xml:ns:xmpp-sasl"
     157#define XMLNS_BIND         "urn:ietf:params:xml:ns:xmpp-bind"
     158#define XMLNS_SESSION      "urn:ietf:params:xml:ns:xmpp-session"
     159#define XMLNS_STANZA_ERROR "urn:ietf:params:xml:ns:xmpp-stanzas"
     160#define XMLNS_STREAM_ERROR "urn:ietf:params:xml:ns:xmpp-streams"
     161#define XMLNS_ROSTER       "jabber:iq:roster"
     162
     163/* Some supported extensions/legacy stuff */
     164#define XMLNS_AUTH         "jabber:iq:auth"                     /* XEP-0078 */
     165#define XMLNS_VERSION      "jabber:iq:version"                  /* XEP-0092 */
     166#define XMLNS_TIME         "jabber:iq:time"                     /* XEP-0090 */
     167#define XMLNS_PING         "urn:xmpp:ping"                      /* XEP-0199 */
     168#define XMLNS_VCARD        "vcard-temp"                         /* XEP-0054 */
     169#define XMLNS_DELAY        "jabber:x:delay"                     /* XEP-0091 */
     170#define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"  /* 0085 */
     171#define XMLNS_DISCOVER     "http://jabber.org/protocol/disco#info"  /* 0030 */
     172#define XMLNS_MUC          "http://jabber.org/protocol/muc"     /* XEP-0045 */
     173#define XMLNS_MUC_USER     "http://jabber.org/protocol/muc#user"/* XEP-0045 */
     174#define XMLNS_CAPS         "http://jabber.org/protocol/caps"    /* XEP-0115 */
     175
     176/* iq.c */
     177xt_status jabber_pkt_iq( struct xt_node *node, gpointer data );
     178int jabber_init_iq_auth( struct im_connection *ic );
     179xt_status jabber_pkt_bind_sess( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
     180int jabber_get_roster( struct im_connection *ic );
     181int jabber_get_vcard( struct im_connection *ic, char *bare_jid );
     182int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name );
     183int jabber_remove_from_roster( struct im_connection *ic, char *handle );
     184
     185/* message.c */
     186xt_status jabber_pkt_message( struct xt_node *node, gpointer data );
     187
     188/* presence.c */
     189xt_status jabber_pkt_presence( struct xt_node *node, gpointer data );
     190int presence_send_update( struct im_connection *ic );
     191int presence_send_request( struct im_connection *ic, char *handle, char *request );
     192
     193/* jabber_util.c */
     194char *set_eval_priority( set_t *set, char *value );
     195char *set_eval_tls( set_t *set, char *value );
     196struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children );
     197struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type );
     198void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func );
     199struct xt_node *jabber_cache_get( struct im_connection *ic, char *id );
     200void jabber_cache_entry_free( gpointer entry );
     201void jabber_cache_clean( struct im_connection *ic );
     202xt_status jabber_cache_handle_packet( struct im_connection *ic, struct xt_node *node );
     203const struct jabber_away_state *jabber_away_state_by_code( char *code );
     204const struct jabber_away_state *jabber_away_state_by_name( char *name );
     205void jabber_buddy_ask( struct im_connection *ic, char *handle );
     206char *jabber_normalize( const char *orig );
     207
     208typedef enum
     209{
     210        GET_BUDDY_CREAT = 1,    /* Try to create it, if necessary. */
     211        GET_BUDDY_EXACT = 2,    /* Get an exact match (only makes sense with bare JIDs). */
     212        GET_BUDDY_FIRST = 4,    /* No selection, simply get the first resource for this JID. */
     213} get_buddy_flags_t;
     214
     215struct jabber_error
     216{
     217        char *code, *text, *type;
     218};
     219
     220struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid );
     221struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid, get_buddy_flags_t flags );
     222struct jabber_buddy *jabber_buddy_by_ext_jid( struct im_connection *ic, char *jid, get_buddy_flags_t flags );
     223int jabber_buddy_remove( struct im_connection *ic, char *full_jid );
     224int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid );
     225time_t jabber_get_timestamp( struct xt_node *xt );
     226struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns );
     227void jabber_error_free( struct jabber_error *err );
     228
     229extern const struct jabber_away_state jabber_away_state_list[];
     230
     231/* io.c */
     232int jabber_write_packet( struct im_connection *ic, struct xt_node *node );
     233int jabber_write( struct im_connection *ic, char *buf, int len );
     234gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond );
     235gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond );
     236gboolean jabber_start_stream( struct im_connection *ic );
     237void jabber_end_stream( struct im_connection *ic );
     238
     239/* sasl.c */
     240xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data );
     241xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data );
     242xt_status sasl_pkt_result( struct xt_node *node, gpointer data );
     243gboolean sasl_supported( struct im_connection *ic );
     244
     245/* conference.c */
     246struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password );
     247struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name );
     248void jabber_chat_free( struct groupchat *c );
     249int jabber_chat_msg( struct groupchat *ic, char *message, int flags );
     250int jabber_chat_topic( struct groupchat *c, char *topic );
     251int jabber_chat_leave( struct groupchat *c, const char *reason );
     252void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node );
     253void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node );
     254void jabber_chat_invite( struct groupchat *c, char *who, char *message );
     255
    35256#endif
    36 
    37 #include "lib.h"
    38 
    39 
    40 #ifndef INCL_JABBER_H
    41 #define INCL_JABBER_H
    42 
    43 #ifdef __cplusplus
    44 extern "C" {
    45 #endif
    46 
    47 /* --------------------------------------------------------- */
    48 /*                                                           */
    49 /* JID structures & constants                                */
    50 /*                                                           */
    51 /* --------------------------------------------------------- */
    52 #define JID_RESOURCE 1
    53 #define JID_USER     2
    54 #define JID_SERVER   4
    55 
    56 typedef struct jid_struct
    57 {
    58     pool               p;
    59     char*              resource;
    60     char*              user;
    61     char*              server;
    62     char*              full;
    63     struct jid_struct *next; /* for lists of jids */
    64 } *jid;
    65  
    66 jid     jid_new(pool p, char *idstr);          /* Creates a jabber id from the idstr */
    67 void    jid_set(jid id, char *str, int item);  /* Individually sets jid components */
    68 char*   jid_full(jid id);                      /* Builds a string type=user/resource@server from the jid data */
    69 int     jid_cmp(jid a, jid b);                 /* Compares two jid's, returns 0 for perfect match */
    70 int     jid_cmpx(jid a, jid b, int parts);     /* Compares just the parts specified as JID_|JID_ */
    71 jid     jid_append(jid a, jid b);              /* Appending b to a (list), no dups */
    72 xmlnode jid_xres(jid id);                      /* Returns xmlnode representation of the resource?query=string */
    73 xmlnode jid_nodescan(jid id, xmlnode x);       /* Scans the children of the node for a matching jid attribute */
    74 jid     jid_user(jid a);                       /* returns the same jid but just of the user@host part */
    75 
    76 
    77 /* --------------------------------------------------------- */
    78 /*                                                           */
    79 /* JPacket structures & constants                            */
    80 /*                                                           */
    81 /* --------------------------------------------------------- */
    82 #define JPACKET_UNKNOWN   0x00
    83 #define JPACKET_MESSAGE   0x01
    84 #define JPACKET_PRESENCE  0x02
    85 #define JPACKET_IQ        0x04
    86 #define JPACKET_S10N      0x08
    87 
    88 #define JPACKET__UNKNOWN      0
    89 #define JPACKET__NONE         1
    90 #define JPACKET__ERROR        2
    91 #define JPACKET__CHAT         3
    92 #define JPACKET__GROUPCHAT    4
    93 #define JPACKET__GET          5
    94 #define JPACKET__SET          6
    95 #define JPACKET__RESULT       7
    96 #define JPACKET__SUBSCRIBE    8
    97 #define JPACKET__SUBSCRIBED   9
    98 #define JPACKET__UNSUBSCRIBE  10
    99 #define JPACKET__UNSUBSCRIBED 11
    100 #define JPACKET__AVAILABLE    12
    101 #define JPACKET__UNAVAILABLE  13
    102 #define JPACKET__PROBE        14
    103 #define JPACKET__HEADLINE     15
    104 #define JPACKET__INVISIBLE    16
    105 
    106 typedef struct jpacket_struct
    107 {
    108     unsigned char type;
    109     int           subtype;
    110     int           flag;
    111     void*         aux1;
    112     xmlnode       x;
    113     jid           to;
    114     jid           from;
    115     char*         iqns;
    116     xmlnode       iq;
    117     pool          p;
    118 } *jpacket, _jpacket;
    119  
    120 jpacket jpacket_new(xmlnode x);     /* Creates a jabber packet from the xmlnode */
    121 int     jpacket_subtype(jpacket p); /* Returns the subtype value (looks at xmlnode for it) */
    122 
    123 
    124 /* --------------------------------------------------------- */
    125 /*                                                           */
    126 /* Presence Proxy DB structures & constants                  */
    127 /*                                                           */
    128 /* --------------------------------------------------------- */
    129 typedef struct ppdb_struct
    130 {                             
    131     jid     id;                /* entry data */
    132     int     pri;
    133     xmlnode x;
    134     struct ppdb_struct* user;  /* linked list for user@server */
    135     pool                p;     /* db-level data */
    136     struct ppdb_struct* next;
    137 } _ppdb, *ppdb;
    138 
    139 ppdb    ppdb_insert(ppdb db, jid id, xmlnode x); /* Inserts presence into the proxy */
    140 xmlnode ppdb_primary(ppdb db, jid id);           /* Fetches the matching primary presence for the id */
    141 void    ppdb_free(ppdb db);                      /* Frees the db and all entries */
    142 xmlnode ppdb_get(ppdb db, jid id);               /* Called successively to return each presence xmlnode */
    143                                                  /*   for the id and children, returns NULL at the end */
    144 
    145 
    146 /* --------------------------------------------------------- */
    147 /*                                                           */
    148 /* Simple Jabber Rate limit functions                        */
    149 /*                                                           */
    150 /* --------------------------------------------------------- */
    151 typedef struct jlimit_struct
    152 {
    153     char *key;
    154     int start;
    155     int points;
    156     int maxt, maxp;
    157     pool p;
    158 } *jlimit, _jlimit;
    159  
    160 jlimit jlimit_new(int maxt, int maxp);
    161 void jlimit_free(jlimit r);
    162 int jlimit_check(jlimit r, char *key, int points);
    163 
    164 
    165 /* --------------------------------------------------------- */
    166 /*                                                           */
    167 /* Error structures & constants                              */
    168 /*                                                           */
    169 /* --------------------------------------------------------- */
    170 typedef struct terror_struct
    171 {
    172     int  code;
    173     char msg[64];
    174 } terror;
    175 
    176 #define TERROR_BAD           (terror){400,"Bad Request"}
    177 #define TERROR_AUTH          (terror){401,"Unauthorized"}
    178 #define TERROR_PAY           (terror){402,"Payment Required"}
    179 #define TERROR_FORBIDDEN     (terror){403,"Forbidden"}
    180 #define TERROR_NOTFOUND      (terror){404,"Not Found"}
    181 #define TERROR_NOTALLOWED    (terror){405,"Not Allowed"}
    182 #define TERROR_NOTACCEPTABLE (terror){406,"Not Acceptable"}
    183 #define TERROR_REGISTER      (terror){407,"Registration Required"}
    184 #define TERROR_REQTIMEOUT    (terror){408,"Request Timeout"}
    185 #define TERROR_CONFLICT      (terror){409,"Conflict"}
    186 
    187 #define TERROR_INTERNAL   (terror){500,"Internal Server Error"}
    188 #define TERROR_NOTIMPL    (terror){501,"Not Implemented"}
    189 #define TERROR_EXTERNAL   (terror){502,"Remote Server Error"}
    190 #define TERROR_UNAVAIL    (terror){503,"Service Unavailable"}
    191 #define TERROR_EXTTIMEOUT (terror){504,"Remote Server Timeout"}
    192 #define TERROR_DISCONNECTED (terror){510,"Disconnected"}
    193 
    194 /* --------------------------------------------------------- */
    195 /*                                                           */
    196 /* Namespace constants                                       */
    197 /*                                                           */
    198 /* --------------------------------------------------------- */
    199 #define NSCHECK(x,n) (j_strcmp(xmlnode_get_attrib(x,"xmlns"),n) == 0)
    200 
    201 #define NS_CLIENT    "jabber:client"
    202 #define NS_SERVER    "jabber:server"
    203 #define NS_AUTH      "jabber:iq:auth"
    204 #define NS_REGISTER  "jabber:iq:register"
    205 #define NS_ROSTER    "jabber:iq:roster"
    206 #define NS_OFFLINE   "jabber:x:offline"
    207 #define NS_AGENT     "jabber:iq:agent"
    208 #define NS_AGENTS    "jabber:iq:agents"
    209 #define NS_DELAY     "jabber:x:delay"
    210 #define NS_VERSION   "jabber:iq:version"
    211 #define NS_TIME      "jabber:iq:time"
    212 #define NS_VCARD     "vcard-temp"
    213 #define NS_PRIVATE   "jabber:iq:private"
    214 #define NS_SEARCH    "jabber:iq:search"
    215 #define NS_OOB       "jabber:iq:oob"
    216 #define NS_XOOB      "jabber:x:oob"
    217 #define NS_ADMIN     "jabber:iq:admin"
    218 #define NS_FILTER    "jabber:iq:filter"
    219 #define NS_AUTH_0K   "jabber:iq:auth:0k"
    220 
    221 
    222 /* --------------------------------------------------------- */
    223 /*                                                           */
    224 /* Message Types                                             */
    225 /*                                                           */
    226 /* --------------------------------------------------------- */
    227 #define TMSG_NORMAL     "normal"
    228 #define TMSG_ERROR      "error"
    229 #define TMSG_CHAT       "chat"
    230 #define TMSG_GROUPCHAT  "groupchat"
    231 #define TMSG_HEADLINE   "headline"
    232 
    233 
    234 /* --------------------------------------------------------- */
    235 /*                                                           */
    236 /* JUtil functions                                           */
    237 /*                                                           */
    238 /* --------------------------------------------------------- */
    239 xmlnode jutil_presnew(int type, char *to, char *status); /* Create a skeleton presence packet */
    240 xmlnode jutil_iqnew(int type, char *ns);                 /* Create a skeleton iq packet */
    241 xmlnode jutil_msgnew(char *type, char *to, char *subj, char *body);
    242                                                          /* Create a skeleton message packet */
    243 xmlnode jutil_header(char* xmlns, char* server);         /* Create a skeleton stream packet */
    244 int     jutil_priority(xmlnode x);                       /* Determine priority of this packet */
    245 void    jutil_tofrom(xmlnode x);                         /* Swaps to/from fields on a packet */
    246 xmlnode jutil_iqresult(xmlnode x);                       /* Generate a skeleton iq/result, given a iq/query */
    247 char*   jutil_timestamp(void);                           /* Get stringified timestamp */
    248 void    jutil_error(xmlnode x, terror E);                /* Append an <error> node to x */
    249 void    jutil_delay(xmlnode msg, char *reason);          /* Append a delay packet to msg */
    250 char*   jutil_regkey(char *key, char *seed);             /* pass a seed to generate a key, pass the key again to validate (returns it) */
    251 
    252 
    253 /* --------------------------------------------------------- */
    254 /*                                                           */
    255 /* JConn structures & functions                              */
    256 /*                                                           */
    257 /* --------------------------------------------------------- */
    258 #define JCONN_STATE_OFF       0
    259 #define JCONN_STATE_CONNECTED 1
    260 #define JCONN_STATE_ON        2
    261 #define JCONN_STATE_AUTH      3
    262 
    263 typedef struct jconn_struct
    264 {
    265     /* Core structure */
    266     pool        p;             /* Memory allocation pool */
    267     int         state;     /* Connection state flag */
    268     int         fd;            /* Connection file descriptor */
    269     jid         user;      /* User info */
    270     char        *pass;     /* User passwd */
    271 
    272     /* Stream stuff */
    273     int         id;        /* id counter for jab_getid() function */
    274     char        idbuf[9];  /* temporary storage for jab_getid() */
    275     char        *sid;      /* stream id from server, for digest auth */
    276     XML_Parser  parser;    /* Parser instance */
    277     xmlnode     current;   /* Current node in parsing instance.. */
    278 
    279     /* Event callback ptrs */
    280     void (*on_state)(struct jconn_struct *j, int state);
    281     void (*on_packet)(struct jconn_struct *j, jpacket p);
    282 
    283 } *jconn, jconn_struct;
    284 
    285 typedef void (*jconn_state_h)(jconn j, int state);
    286 typedef void (*jconn_packet_h)(jconn j, jpacket p);
    287 
    288 
    289 jconn jab_new(char *user, char *pass);
    290 void jab_delete(jconn j);
    291 void jab_state_handler(jconn j, jconn_state_h h);
    292 void jab_packet_handler(jconn j, jconn_packet_h h);
    293 void jab_start(jconn j);
    294 void jab_stop(jconn j);
    295 
    296 int jab_getfd(jconn j);
    297 jid jab_getjid(jconn j);
    298 char *jab_getsid(jconn j);
    299 char *jab_getid(jconn j);
    300 
    301 void jab_send(jconn j, xmlnode x);
    302 void jab_send_raw(jconn j, const char *str);
    303 void jab_recv(jconn j);
    304 void jab_poll(jconn j, int timeout);
    305 
    306 char *jab_auth(jconn j);
    307 char *jab_reg(jconn j);
    308 
    309 
    310 
    311 #ifdef __cplusplus
    312 }
    313 #endif
    314 
    315 #endif  /* INCL_JABBER_H */
  • protocols/msn/Makefile

    r875ad42 r85d7b85  
    1717# [SH] Phony targets
    1818all: msn_mod.o
     19check: all
     20lcov: check
     21gcov:
     22        gcov *.c
    1923       
    2024.PHONY: all clean distclean
  • protocols/msn/msn.c

    r875ad42 r85d7b85  
    2727#include "msn.h"
    2828
    29 static void msn_login( struct aim_user *acct )
    30 {
    31         struct gaim_connection *gc = new_gaim_conn( acct );
     29static char *msn_set_display_name( set_t *set, char *value );
     30
     31static void msn_init( account_t *acc )
     32{
     33        set_t *s;
     34       
     35        s = set_add( &acc->set, "display_name", NULL, msn_set_display_name, acc );
     36        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
     37
     38        s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
     39}
     40
     41static void msn_login( account_t *acc )
     42{
     43        struct im_connection *ic = imcb_new( acc );
    3244        struct msn_data *md = g_new0( struct msn_data, 1 );
    3345       
    34         set_login_progress( gc, 1, "Connecting" );
    35        
    36         gc->proto_data = md;
     46        ic->proto_data = md;
    3747        md->fd = -1;
    3848       
    39         if( strchr( acct->username, '@' ) == NULL )
    40         {
    41                 hide_login_progress( gc, "Invalid account name" );
    42                 signoff( gc );
     49        if( strchr( acc->user, '@' ) == NULL )
     50        {
     51                imcb_error( ic, "Invalid account name" );
     52                imc_logout( ic, FALSE );
    4353                return;
    4454        }
    4555       
    46         md->fd = proxy_connect( "messenger.hotmail.com", 1863, msn_ns_connected, gc );
     56        imcb_log( ic, "Connecting" );
     57       
     58        md->fd = proxy_connect( "messenger.hotmail.com", 1863, msn_ns_connected, ic );
    4759        if( md->fd < 0 )
    4860        {
    49                 hide_login_progress( gc, "Could not connect to server" );
    50                 signoff( gc );
    51         }
    52         else
    53         {
    54                 md->gc = gc;
    55                 md->away_state = msn_away_state_list;
    56                
    57                 msn_connections = g_slist_append( msn_connections, gc );
    58         }
    59 }
    60 
    61 static void msn_close( struct gaim_connection *gc )
    62 {
    63         struct msn_data *md = gc->proto_data;
     61                imcb_error( ic, "Could not connect to server" );
     62                imc_logout( ic, TRUE );
     63                return;
     64        }
     65       
     66        md->ic = ic;
     67        md->away_state = msn_away_state_list;
     68       
     69        msn_connections = g_slist_append( msn_connections, ic );
     70}
     71
     72static void msn_logout( struct im_connection *ic )
     73{
     74        struct msn_data *md = ic->proto_data;
    6475        GSList *l;
    6576       
    66         if( md->fd >= 0 )
    67                 closesocket( md->fd );
    68        
    69         if( md->handler )
    70         {
    71                 if( md->handler->rxq ) g_free( md->handler->rxq );
    72                 if( md->handler->cmd_text ) g_free( md->handler->cmd_text );
    73                 g_free( md->handler );
    74         }
    75        
    76         while( md->switchboards )
    77                 msn_sb_destroy( md->switchboards->data );
    78        
    79         if( md->msgq )
    80         {
    81                 struct msn_message *m;
    82                
    83                 for( l = md->msgq; l; l = l->next )
     77        if( md )
     78        {
     79                if( md->fd >= 0 )
     80                        closesocket( md->fd );
     81               
     82                if( md->handler )
    8483                {
    85                         m = l->data;
    86                
    87                         serv_got_crap( gc, "Warning: Closing down MSN connection with unsent message to %s, you'll have to resend it.", m->who );
    88                         g_free( m->who );
    89                         g_free( m->text );
    90                         g_free( m );
     84                        if( md->handler->rxq ) g_free( md->handler->rxq );
     85                        if( md->handler->cmd_text ) g_free( md->handler->cmd_text );
     86                        g_free( md->handler );
    9187                }
    92                 g_slist_free( md->msgq );
    93         }
    94        
    95         for( l = gc->permit; l; l = l->next )
     88               
     89                while( md->switchboards )
     90                        msn_sb_destroy( md->switchboards->data );
     91               
     92                msn_msgq_purge( ic, &md->msgq );
     93               
     94                while( md->groupcount > 0 )
     95                        g_free( md->grouplist[--md->groupcount] );
     96                g_free( md->grouplist );
     97               
     98                g_free( md );
     99        }
     100       
     101        for( l = ic->permit; l; l = l->next )
    96102                g_free( l->data );
    97         g_slist_free( gc->permit );
    98        
    99         for( l = gc->deny; l; l = l->next )
     103        g_slist_free( ic->permit );
     104       
     105        for( l = ic->deny; l; l = l->next )
    100106                g_free( l->data );
    101         g_slist_free( gc->deny );
    102        
    103         g_free( md );
    104        
    105         msn_connections = g_slist_remove( msn_connections, gc );
    106 }
    107 
    108 static int msn_send_im( struct gaim_connection *gc, char *who, char *message, int len, int away )
     107        g_slist_free( ic->deny );
     108       
     109        msn_connections = g_slist_remove( msn_connections, ic );
     110}
     111
     112static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, int away )
    109113{
    110114        struct msn_switchboard *sb;
    111         struct msn_data *md = gc->proto_data;
    112        
    113         if( ( sb = msn_sb_by_handle( gc, who ) ) )
     115        struct msn_data *md = ic->proto_data;
     116       
     117        if( ( sb = msn_sb_by_handle( ic, who ) ) )
    114118        {
    115119                return( msn_sb_sendmessage( sb, message ) );
     
    126130               
    127131                /* FIXME: *CHECK* the reliability of using spare sb's! */
    128                 if( ( sb = msn_sb_spare( gc ) ) )
     132                if( ( sb = msn_sb_spare( ic ) ) )
    129133                {
    130134                        debug( "Trying to use a spare switchboard to message %s", who );
     
    144148                /* If we reach this line, there was no spare switchboard, so let's make one. */
    145149                g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
    146                 if( !msn_write( gc, buf, strlen( buf ) ) )
     150                if( !msn_write( ic, buf, strlen( buf ) ) )
    147151                {
    148152                        g_free( m->who );
     
    164168}
    165169
    166 static GList *msn_away_states( struct gaim_connection *gc )
    167 {
    168         GList *l = NULL;
     170static GList *msn_away_states( struct im_connection *ic )
     171{
     172        static GList *l = NULL;
    169173        int i;
    170174       
    171         for( i = 0; msn_away_state_list[i].number > -1; i ++ )
    172                 l = g_list_append( l, (void*) msn_away_state_list[i].name );
    173        
    174         return( l );
    175 }
    176 
    177 static char *msn_get_status_string( struct gaim_connection *gc, int number )
    178 {
    179         const struct msn_away_state *st = msn_away_state_by_number( number );
    180        
    181         if( st )
    182                 return( (char*) st->name );
    183         else
    184                 return( "" );
    185 }
    186 
    187 static void msn_set_away( struct gaim_connection *gc, char *state, char *message )
     175        if( l == NULL )
     176                for( i = 0; msn_away_state_list[i].number > -1; i ++ )
     177                        l = g_list_append( l, (void*) msn_away_state_list[i].name );
     178       
     179        return l;
     180}
     181
     182static void msn_set_away( struct im_connection *ic, char *state, char *message )
    188183{
    189184        char buf[1024];
    190         struct msn_data *md = gc->proto_data;
     185        struct msn_data *md = ic->proto_data;
    191186        const struct msn_away_state *st;
    192187       
     
    200195       
    201196        g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code );
    202         msn_write( gc, buf, strlen( buf ) );
    203 }
    204 
    205 static void msn_set_info( struct gaim_connection *gc, char *info )
    206 {
    207         int i;
    208         char buf[1024], *fn, *s;
    209         struct msn_data *md = gc->proto_data;
    210        
    211         if( strlen( info ) > 129 )
    212         {
    213                 do_error_dialog( gc, "Maximum name length exceeded", "MSN" );
    214                 return;
    215         }
    216        
    217         /* Of course we could use http_encode() here, but when we encode
    218            every character, the server is less likely to complain about the
    219            chosen name. However, the MSN server doesn't seem to like escaped
    220            non-ASCII chars, so we keep those unescaped. */
    221         s = fn = g_new0( char, strlen( info ) * 3 + 1 );
    222         for( i = 0; info[i]; i ++ )
    223                 if( info[i] & 128 )
    224                 {
    225                         *s = info[i];
    226                         s ++;
    227                 }
    228                 else
    229                 {
    230                         g_snprintf( s, 4, "%%%02X", info[i] );
    231                         s += 3;
    232                 }
    233        
    234         g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, gc->username, fn );
    235         msn_write( gc, buf, strlen( buf ) );
    236         g_free( fn );
    237 }
    238 
    239 static void msn_get_info(struct gaim_connection *gc, char *who)
     197        msn_write( ic, buf, strlen( buf ) );
     198}
     199
     200static void msn_set_my_name( struct im_connection *ic, char *info )
     201{
     202        msn_set_display_name( set_find( &ic->acc->set, "display_name" ), info );
     203}
     204
     205static void msn_get_info(struct im_connection *ic, char *who)
    240206{
    241207        /* Just make an URL and let the user fetch the info */
    242         serv_got_crap( gc, "%s\n%s: %s%s", _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who );
    243 }
    244 
    245 static void msn_add_buddy( struct gaim_connection *gc, char *who )
    246 {
    247         msn_buddy_list_add( gc, "FL", who, who );
    248 }
    249 
    250 static void msn_remove_buddy( struct gaim_connection *gc, char *who, char *group )
    251 {
    252         msn_buddy_list_remove( gc, "FL", who );
    253 }
    254 
    255 static int msn_chat_send( struct gaim_connection *gc, int id, char *message )
    256 {
    257         struct msn_switchboard *sb = msn_sb_by_id( gc, id );
     208        imcb_log( ic, "%s\n%s: %s%s", _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who );
     209}
     210
     211static void msn_add_buddy( struct im_connection *ic, char *who, char *group )
     212{
     213        msn_buddy_list_add( ic, "FL", who, who );
     214}
     215
     216static void msn_remove_buddy( struct im_connection *ic, char *who, char *group )
     217{
     218        msn_buddy_list_remove( ic, "FL", who );
     219}
     220
     221static void msn_chat_msg( struct groupchat *c, char *message, int flags )
     222{
     223        struct msn_switchboard *sb = msn_sb_by_chat( c );
    258224       
    259225        if( sb )
    260                 return( msn_sb_sendmessage( sb, message ) );
    261         else
    262                 return( 0 );
    263 }
    264 
    265 static void msn_chat_invite( struct gaim_connection *gc, int id, char *msg, char *who )
    266 {
    267         struct msn_switchboard *sb = msn_sb_by_id( gc, id );
     226                msn_sb_sendmessage( sb, message );
     227        /* FIXME: Error handling (although this can't happen unless something's
     228           already severely broken) disappeared here! */
     229}
     230
     231static void msn_chat_invite( struct groupchat *c, char *who, char *message )
     232{
     233        struct msn_switchboard *sb = msn_sb_by_chat( c );
    268234        char buf[1024];
    269235       
     
    275241}
    276242
    277 static void msn_chat_leave( struct gaim_connection *gc, int id )
    278 {
    279         struct msn_switchboard *sb = msn_sb_by_id( gc, id );
     243static void msn_chat_leave( struct groupchat *c )
     244{
     245        struct msn_switchboard *sb = msn_sb_by_chat( c );
    280246       
    281247        if( sb )
     
    283249}
    284250
    285 static int msn_chat_open( struct gaim_connection *gc, char *who )
     251static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )
    286252{
    287253        struct msn_switchboard *sb;
    288         struct msn_data *md = gc->proto_data;
     254        struct msn_data *md = ic->proto_data;
    289255        char buf[1024];
    290256       
    291         if( ( sb = msn_sb_by_handle( gc, who ) ) )
     257        if( ( sb = msn_sb_by_handle( ic, who ) ) )
    292258        {
    293259                debug( "Converting existing switchboard to %s to a groupchat", who );
    294                 msn_sb_to_chat( sb );
    295                 return( 1 );
     260                return msn_sb_to_chat( sb );
    296261        }
    297262        else
     
    299264                struct msn_message *m;
    300265               
    301                 if( ( sb = msn_sb_spare( gc ) ) )
     266                if( ( sb = msn_sb_spare( ic ) ) )
    302267                {
    303268                        debug( "Trying to reuse an existing switchboard as a groupchat with %s", who );
    304269                        g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
    305270                        if( msn_sb_write( sb, buf, strlen( buf ) ) )
    306                         {
    307                                 msn_sb_to_chat( sb );
    308                                 return( 1 );
    309                         }
     271                                return msn_sb_to_chat( sb );
    310272                }
    311273               
     
    315277                /* Request a new switchboard. */
    316278                g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
    317                 if( !msn_write( gc, buf, strlen( buf ) ) )
     279                if( !msn_write( ic, buf, strlen( buf ) ) )
    318280                        return( 0 );
    319281               
     
    326288                md->msgq = g_slist_append( md->msgq, m );
    327289               
    328                 return( 1 );
    329         }
    330        
    331         return( 0 );
    332 }
    333 
    334 static void msn_keepalive( struct gaim_connection *gc )
    335 {
    336         msn_write( gc, "PNG\r\n", strlen( "PNG\r\n" ) );
    337 }
    338 
    339 static void msn_add_permit( struct gaim_connection *gc, char *who )
    340 {
    341         msn_buddy_list_add( gc, "AL", who, who );
    342 }
    343 
    344 static void msn_rem_permit( struct gaim_connection *gc, char *who )
    345 {
    346         msn_buddy_list_remove( gc, "AL", who );
    347 }
    348 
    349 static void msn_add_deny( struct gaim_connection *gc, char *who )
     290                /* FIXME: Can I try to return something here already? */
     291                return NULL;
     292        }
     293       
     294        return NULL;
     295}
     296
     297static void msn_keepalive( struct im_connection *ic )
     298{
     299        msn_write( ic, "PNG\r\n", strlen( "PNG\r\n" ) );
     300}
     301
     302static void msn_add_permit( struct im_connection *ic, char *who )
     303{
     304        msn_buddy_list_add( ic, "AL", who, who );
     305}
     306
     307static void msn_rem_permit( struct im_connection *ic, char *who )
     308{
     309        msn_buddy_list_remove( ic, "AL", who );
     310}
     311
     312static void msn_add_deny( struct im_connection *ic, char *who )
    350313{
    351314        struct msn_switchboard *sb;
    352315       
    353         msn_buddy_list_add( gc, "BL", who, who );
     316        msn_buddy_list_add( ic, "BL", who, who );
    354317       
    355318        /* If there's still a conversation with this person, close it. */
    356         if( ( sb = msn_sb_by_handle( gc, who ) ) )
     319        if( ( sb = msn_sb_by_handle( ic, who ) ) )
    357320        {
    358321                msn_sb_destroy( sb );
     
    360323}
    361324
    362 static void msn_rem_deny( struct gaim_connection *gc, char *who )
    363 {
    364         msn_buddy_list_remove( gc, "BL", who );
    365 }
    366 
    367 static int msn_send_typing( struct gaim_connection *gc, char *who, int typing )
    368 {
    369         if( typing )
    370                 return( msn_send_im( gc, who, TYPING_NOTIFICATION_MESSAGE, strlen( TYPING_NOTIFICATION_MESSAGE ), 0 ) );
     325static void msn_rem_deny( struct im_connection *ic, char *who )
     326{
     327        msn_buddy_list_remove( ic, "BL", who );
     328}
     329
     330static int msn_send_typing( struct im_connection *ic, char *who, int typing )
     331{
     332        if( typing & OPT_TYPING )
     333                return( msn_buddy_msg( ic, who, TYPING_NOTIFICATION_MESSAGE, 0 ) );
    371334        else
    372335                return( 1 );
    373336}
    374337
    375 void msn_init()
     338static char *msn_set_display_name( set_t *set, char *value )
     339{
     340        account_t *acc = set->data;
     341        struct im_connection *ic = acc->ic;
     342        struct msn_data *md;
     343        char buf[1024], *fn;
     344       
     345        /* Double-check. */
     346        if( ic == NULL )
     347                return NULL;
     348       
     349        md = ic->proto_data;
     350       
     351        if( strlen( value ) > 129 )
     352        {
     353                imcb_log( ic, "Maximum name length exceeded" );
     354                return NULL;
     355        }
     356       
     357        fn = msn_http_encode( value );
     358       
     359        g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn );
     360        msn_write( ic, buf, strlen( buf ) );
     361        g_free( fn );
     362       
     363        /* Returning NULL would be better, because the server still has to
     364           confirm the name change. However, it looks a bit confusing to the
     365           user. */
     366        return value;
     367}
     368
     369void msn_initmodule()
    376370{
    377371        struct prpl *ret = g_new0(struct prpl, 1);
     372       
    378373        ret->name = "msn";
    379374        ret->login = msn_login;
    380         ret->close = msn_close;
    381         ret->send_im = msn_send_im;
     375        ret->init = msn_init;
     376        ret->logout = msn_logout;
     377        ret->buddy_msg = msn_buddy_msg;
    382378        ret->away_states = msn_away_states;
    383         ret->get_status_string = msn_get_status_string;
    384379        ret->set_away = msn_set_away;
    385         ret->set_info = msn_set_info;
    386380        ret->get_info = msn_get_info;
     381        ret->set_my_name = msn_set_my_name;
    387382        ret->add_buddy = msn_add_buddy;
    388383        ret->remove_buddy = msn_remove_buddy;
    389         ret->chat_send = msn_chat_send;
     384        ret->chat_msg = msn_chat_msg;
    390385        ret->chat_invite = msn_chat_invite;
    391386        ret->chat_leave = msn_chat_leave;
    392         ret->chat_open = msn_chat_open;
     387        ret->chat_with = msn_chat_with;
    393388        ret->keepalive = msn_keepalive;
    394389        ret->add_permit = msn_add_permit;
     
    397392        ret->rem_deny = msn_rem_deny;
    398393        ret->send_typing = msn_send_typing;
    399         ret->cmp_buddynames = g_strcasecmp;
     394        ret->handle_cmp = g_strcasecmp;
    400395
    401396        register_protocol(ret);
  • protocols/msn/msn.h

    r875ad42 r85d7b85  
    2929#define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r"
    3030
    31 #ifdef _WIN32
    32 #define debug 
     31#ifdef DEBUG
     32#define debug( text... ) imcb_log( ic, text );
    3333#else
    34 #define debug( text... ) irc_usermsg( IRC, text );
    35 #undef debug
    3634#define debug( text... )
    3735#endif
     
    5755struct msn_data
    5856{
    59         struct gaim_connection *gc;
     57        struct im_connection *ic;
    6058       
    6159        int fd;
     
    6664        GSList *msgq;
    6765        GSList *switchboards;
     66        int sb_failures;
     67        time_t first_sb_failure;
     68       
    6869        const struct msn_away_state *away_state;
    69        
    7070        int buddycount;
    7171        int groupcount;
     
    7575struct msn_switchboard
    7676{
    77         struct gaim_connection *gc;
     77        struct im_connection *ic;
    7878       
    7979        int fd;
     
    8989        GSList *msgq;
    9090        char *who;
    91         struct conversation *chat;
     91        struct groupchat *chat;
    9292};
    9393
     
    146146
    147147/* ns.c */
    148 void msn_ns_connected( gpointer data, gint source, GaimInputCondition cond );
     148gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond );
    149149
    150150/* msn_util.c */
    151 int msn_write( struct gaim_connection *gc, char *s, int len );
    152 int msn_logged_in( struct gaim_connection *gc );
    153 int msn_buddy_list_add( struct gaim_connection *gc, char *list, char *who, char *realname );
    154 int msn_buddy_list_remove( struct gaim_connection *gc, char *list, char *who );
    155 void msn_buddy_ask( struct gaim_connection *gc, char *handle, char *realname );
     151int msn_write( struct im_connection *ic, char *s, int len );
     152int msn_logged_in( struct im_connection *ic );
     153int msn_buddy_list_add( struct im_connection *ic, char *list, char *who, char *realname );
     154int msn_buddy_list_remove( struct im_connection *ic, char *list, char *who );
     155void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname );
    156156char *msn_findheader( char *text, char *header, int len );
    157157char **msn_linesplit( char *line );
    158158int msn_handler( struct msn_handler_data *h );
     159char *msn_http_encode( const char *input );
     160void msn_msgq_purge( struct im_connection *ic, GSList **list );
    159161
    160162/* tables.c */
     
    166168/* sb.c */
    167169int msn_sb_write( struct msn_switchboard *sb, char *s, int len );
    168 struct msn_switchboard *msn_sb_create( struct gaim_connection *gc, char *host, int port, char *key, int session );
    169 struct msn_switchboard *msn_sb_by_handle( struct gaim_connection *gc, char *handle );
    170 struct msn_switchboard *msn_sb_by_id( struct gaim_connection *gc, int id );
    171 struct msn_switchboard *msn_sb_spare( struct gaim_connection *gc );
     170struct msn_switchboard *msn_sb_create( struct im_connection *ic, char *host, int port, char *key, int session );
     171struct msn_switchboard *msn_sb_by_handle( struct im_connection *ic, char *handle );
     172struct msn_switchboard *msn_sb_by_chat( struct groupchat *c );
     173struct msn_switchboard *msn_sb_spare( struct im_connection *ic );
    172174int msn_sb_sendmessage( struct msn_switchboard *sb, char *text );
    173 void msn_sb_to_chat( struct msn_switchboard *sb );
     175struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb );
    174176void msn_sb_destroy( struct msn_switchboard *sb );
    175 void msn_sb_connected( gpointer data, gint source, GaimInputCondition cond );
     177gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond );
  • protocols/msn/msn_util.c

    r875ad42 r85d7b85  
    2828#include <ctype.h>
    2929
    30 int msn_write( struct gaim_connection *gc, char *s, int len )
    31 {
    32         struct msn_data *md = gc->proto_data;
     30int msn_write( struct im_connection *ic, char *s, int len )
     31{
     32        struct msn_data *md = ic->proto_data;
    3333        int st;
    3434       
     
    3636        if( st != len )
    3737        {
    38                 hide_login_progress_error( gc, "Short write() to main server" );
    39                 signoff( gc );
     38                imcb_error( ic, "Short write() to main server" );
     39                imc_logout( ic, TRUE );
    4040                return( 0 );
    4141        }
     
    4444}
    4545
    46 int msn_logged_in( struct gaim_connection *gc )
    47 {
    48         account_online( gc );
     46int msn_logged_in( struct im_connection *ic )
     47{
     48        imcb_connected( ic );
    4949       
    5050        return( 0 );
    5151}
    5252
    53 int msn_buddy_list_add( struct gaim_connection *gc, char *list, char *who, char *realname_ )
    54 {
    55         struct msn_data *md = gc->proto_data;
    56         GSList *l, **lp = NULL;
     53int msn_buddy_list_add( struct im_connection *ic, char *list, char *who, char *realname_ )
     54{
     55        struct msn_data *md = ic->proto_data;
    5756        char buf[1024], *realname;
    5857       
    59         if( strcmp( list, "AL" ) == 0 )
    60                 lp = &gc->permit;
    61         else if( strcmp( list, "BL" ) == 0 )
    62                 lp = &gc->deny;
    63        
    64         if( lp )
    65                 for( l = *lp; l; l = l->next )
    66                         if( g_strcasecmp( l->data, who ) == 0 )
    67                                 return( 1 );
    68        
    69         realname = g_new0( char, strlen( realname_ ) * 3 + 1 );
    70         strcpy( realname, realname_ );
    71         http_encode( realname );
     58        realname = msn_http_encode( realname_ );
    7259       
    7360        g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s\r\n", ++md->trId, list, who, realname );
    74         if( msn_write( gc, buf, strlen( buf ) ) )
     61        if( msn_write( ic, buf, strlen( buf ) ) )
    7562        {
    7663                g_free( realname );
    7764               
    78                 if( lp )
    79                         *lp = g_slist_append( *lp, g_strdup( who ) );
    80                
    8165                return( 1 );
    8266        }
     
    8771}
    8872
    89 int msn_buddy_list_remove( struct gaim_connection *gc, char *list, char *who )
    90 {
    91         struct msn_data *md = gc->proto_data;
    92         GSList *l = NULL, **lp = NULL;
     73int msn_buddy_list_remove( struct im_connection *ic, char *list, char *who )
     74{
     75        struct msn_data *md = ic->proto_data;
    9376        char buf[1024];
    9477       
    95         if( strcmp( list, "AL" ) == 0 )
    96                 lp = &gc->permit;
    97         else if( strcmp( list, "BL" ) == 0 )
    98                 lp = &gc->deny;
    99        
    100         if( lp )
    101         {
    102                 for( l = *lp; l; l = l->next )
    103                         if( g_strcasecmp( l->data, who ) == 0 )
    104                                 break;
    105                
    106                 if( !l )
    107                         return( 1 );
    108         }
    109        
    11078        g_snprintf( buf, sizeof( buf ), "REM %d %s %s\r\n", ++md->trId, list, who );
    111         if( msn_write( gc, buf, strlen( buf ) ) )
    112         {
    113                 if( lp )
    114                         *lp = g_slist_remove( *lp, l->data );
    115                
     79        if( msn_write( ic, buf, strlen( buf ) ) )
    11680                return( 1 );
    117         }
    11881       
    11982        return( 0 );
     
    12285struct msn_buddy_ask_data
    12386{
    124         struct gaim_connection *gc;
     87        struct im_connection *ic;
    12588        char *handle;
    12689        char *realname;
     
    12992static void msn_buddy_ask_yes( gpointer w, struct msn_buddy_ask_data *bla )
    13093{
    131         msn_buddy_list_add( bla->gc, "AL", bla->handle, bla->realname );
    132        
    133         if( find_buddy( bla->gc, bla->handle ) == NULL )
    134                 show_got_added( bla->gc, bla->handle, NULL );
     94        msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname );
     95       
     96        if( imcb_find_buddy( bla->ic, bla->handle ) == NULL )
     97                imcb_ask_add( bla->ic, bla->handle, NULL );
    13598       
    13699        g_free( bla->handle );
     
    141104static void msn_buddy_ask_no( gpointer w, struct msn_buddy_ask_data *bla )
    142105{
    143         msn_buddy_list_add( bla->gc, "BL", bla->handle, bla->realname );
     106        msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname );
    144107       
    145108        g_free( bla->handle );
     
    148111}
    149112
    150 void msn_buddy_ask( struct gaim_connection *gc, char *handle, char *realname )
     113void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname )
    151114{
    152115        struct msn_buddy_ask_data *bla = g_new0( struct msn_buddy_ask_data, 1 );
    153116        char buf[1024];
    154117       
    155         bla->gc = gc;
     118        bla->ic = ic;
    156119        bla->handle = g_strdup( handle );
    157120        bla->realname = g_strdup( realname );
     
    160123                    "The user %s (%s) wants to add you to his/her buddy list.",
    161124                    handle, realname );
    162         do_ask_dialog( gc, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no );
     125        imcb_ask( ic, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no );
    163126}
    164127
     
    350313        return( 1 );
    351314}
     315
     316/* The difference between this function and the normal http_encode() function
     317   is that this one escapes every 7-bit ASCII character because this is said
     318   to avoid some lame server-side checks when setting a real-name. Also,
     319   non-ASCII characters are not escaped because MSN servers don't seem to
     320   appreciate that! */
     321char *msn_http_encode( const char *input )
     322{
     323        char *ret, *s;
     324        int i;
     325       
     326        ret = s = g_new0( char, strlen( input ) * 3 + 1 );
     327        for( i = 0; input[i]; i ++ )
     328                if( input[i] & 128 )
     329                {
     330                        *s = input[i];
     331                        s ++;
     332                }
     333                else
     334                {
     335                        g_snprintf( s, 4, "%%%02X", input[i] );
     336                        s += 3;
     337                }
     338       
     339        return ret;
     340}
     341
     342void msn_msgq_purge( struct im_connection *ic, GSList **list )
     343{
     344        struct msn_message *m;
     345        GString *ret;
     346        GSList *l;
     347       
     348        l = *list;
     349        if( l == NULL )
     350                return;
     351       
     352        m = l->data;
     353        ret = g_string_sized_new( 1024 );
     354        g_string_printf( ret, "Warning: Cleaning up MSN (switchboard) connection with unsent "
     355                              "messages to %s:", m->who ? m->who : "unknown recipient" );
     356       
     357        while( l )
     358        {
     359                m = l->data;
     360               
     361                g_string_append_printf( ret, "\n%s", m->text );
     362               
     363                g_free( m->who );
     364                g_free( m->text );
     365                g_free( m );
     366               
     367                l = l->next;
     368        }
     369        g_slist_free( *list );
     370        *list = NULL;
     371       
     372        imcb_log( ic, ret->str );
     373        g_string_free( ret, TRUE );
     374}
  • protocols/msn/ns.c

    r875ad42 r85d7b85  
    3030#include "md5.h"
    3131
    32 static void msn_ns_callback( gpointer data, gint source, GaimInputCondition cond );
     32static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond );
    3333static int msn_ns_command( gpointer data, char **cmd, int num_parts );
    3434static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );
    3535
    36 static void msn_auth_got_passport_id( struct passport_reply *rep );
    37 
    38 void msn_ns_connected( gpointer data, gint source, GaimInputCondition cond )
     36static void msn_auth_got_passport_token( struct msn_auth_data *mad );
     37
     38gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )
    3939{
    40         struct gaim_connection *gc = data;
     40        struct im_connection *ic = data;
    4141        struct msn_data *md;
    4242        char s[1024];
    4343       
    44         if( !g_slist_find( msn_connections, gc ) )
    45                 return;
     44        if( !g_slist_find( msn_connections, ic ) )
     45                return FALSE;
    4646       
    4747        if( source == -1 )
    4848        {
    49                 hide_login_progress( gc, "Could not connect to server" );
    50                 signoff( gc );
    51                 return;
    52         }
    53        
    54         md = gc->proto_data;
     49                imcb_error( ic, "Could not connect to server" );
     50                imc_logout( ic, TRUE );
     51                return FALSE;
     52        }
     53       
     54        md = ic->proto_data;
    5555       
    5656        if( !md->handler )
    5757        {
    5858                md->handler = g_new0( struct msn_handler_data, 1 );
    59                 md->handler->data = gc;
     59                md->handler->data = ic;
    6060                md->handler->exec_command = msn_ns_command;
    6161                md->handler->exec_message = msn_ns_message;
     
    7373       
    7474        g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId );
    75         if( msn_write( gc, s, strlen( s ) ) )
    76         {
    77                 gc->inpa = gaim_input_add( md->fd, GAIM_INPUT_READ, msn_ns_callback, gc );
    78                 set_login_progress( gc, 1, "Connected to server, waiting for reply" );
    79         }
     75        if( msn_write( ic, s, strlen( s ) ) )
     76        {
     77                ic->inpa = b_input_add( md->fd, GAIM_INPUT_READ, msn_ns_callback, ic );
     78                imcb_log( ic, "Connected to server, waiting for reply" );
     79        }
     80       
     81        return FALSE;
    8082}
    8183
    82 void msn_ns_callback( gpointer data, gint source, GaimInputCondition cond )
     84static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond )
    8385{
    84         struct gaim_connection *gc = data;
    85         struct msn_data *md = gc->proto_data;
     86        struct im_connection *ic = data;
     87        struct msn_data *md = ic->proto_data;
    8688       
    8789        if( msn_handler( md->handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */
    8890        {
    89                 hide_login_progress( gc, "Error while reading from server" );
    90                 signoff( gc );
    91         }
     91                imcb_error( ic, "Error while reading from server" );
     92                imc_logout( ic, TRUE );
     93               
     94                return FALSE;
     95        }
     96        else
     97                return TRUE;
    9298}
    9399
    94100static int msn_ns_command( gpointer data, char **cmd, int num_parts )
    95101{
    96         struct gaim_connection *gc = data;
    97         struct msn_data *md = gc->proto_data;
     102        struct im_connection *ic = data;
     103        struct msn_data *md = ic->proto_data;
    98104        char buf[1024];
    99105       
     
    108114                if( cmd[2] && strncmp( cmd[2], "MSNP8", 5 ) != 0 )
    109115                {
    110                         hide_login_progress( gc, "Unsupported protocol" );
    111                         signoff( gc );
     116                        imcb_error( ic, "Unsupported protocol" );
     117                        imc_logout( ic, FALSE );
    112118                        return( 0 );
    113119                }
    114120               
    115121                g_snprintf( buf, sizeof( buf ), "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n",
    116                                                 ++md->trId, gc->username );
    117                 return( msn_write( gc, buf, strlen( buf ) ) );
     122                                                ++md->trId, ic->acc->user );
     123                return( msn_write( ic, buf, strlen( buf ) ) );
    118124        }
    119125        else if( strcmp( cmd[0], "CVR" ) == 0 )
    120126        {
    121127                /* We don't give a damn about the information we just received */
    122                 g_snprintf( buf, sizeof( buf ), "USR %d TWN I %s\r\n", ++md->trId, gc->username );
    123                 return( msn_write( gc, buf, strlen( buf ) ) );
     128                g_snprintf( buf, sizeof( buf ), "USR %d TWN I %s\r\n", ++md->trId, ic->acc->user );
     129                return( msn_write( ic, buf, strlen( buf ) ) );
    124130        }
    125131        else if( strcmp( cmd[0], "XFR" ) == 0 )
     
    130136                if( num_parts == 6 && strcmp( cmd[2], "NS" ) == 0 )
    131137                {
    132                         gaim_input_remove( gc->inpa );
    133                         gc->inpa = 0;
     138                        b_event_remove( ic->inpa );
     139                        ic->inpa = 0;
    134140                        closesocket( md->fd );
    135141                       
     
    137143                        if( !server )
    138144                        {
    139                                 hide_login_progress_error( gc, "Syntax error" );
    140                                 signoff( gc );
     145                                imcb_error( ic, "Syntax error" );
     146                                imc_logout( ic, TRUE );
    141147                                return( 0 );
    142148                        }
     
    145151                        server = cmd[3];
    146152                       
    147                         set_login_progress( gc, 1, "Transferring to other server" );
    148                        
    149                         md->fd = proxy_connect( server, port, msn_ns_connected, gc );
     153                        imcb_log( ic, "Transferring to other server" );
     154                       
     155                        md->fd = proxy_connect( server, port, msn_ns_connected, ic );
    150156                }
    151157                else if( num_parts == 6 && strcmp( cmd[2], "SB" ) == 0 )
     
    156162                        if( !server )
    157163                        {
    158                                 hide_login_progress_error( gc, "Syntax error" );
    159                                 signoff( gc );
     164                                imcb_error( ic, "Syntax error" );
     165                                imc_logout( ic, TRUE );
    160166                                return( 0 );
    161167                        }
     
    166172                        if( strcmp( cmd[4], "CKI" ) != 0 )
    167173                        {
    168                                 hide_login_progress_error( gc, "Unknown authentication method for switchboard" );
    169                                 signoff( gc );
     174                                imcb_error( ic, "Unknown authentication method for switchboard" );
     175                                imc_logout( ic, TRUE );
    170176                                return( 0 );
    171177                        }
    172178                       
    173179                        debug( "Connecting to a new switchboard with key %s", cmd[5] );
    174                         sb = msn_sb_create( gc, server, port, cmd[5], MSN_SB_NEW );
     180                        sb = msn_sb_create( ic, server, port, cmd[5], MSN_SB_NEW );
    175181                       
    176182                        if( md->msgq )
     
    198204                else
    199205                {
    200                         hide_login_progress_error( gc, "Syntax error" );
    201                         signoff( gc );
     206                        imcb_error( ic, "Syntax error" );
     207                        imc_logout( ic, TRUE );
    202208                        return( 0 );
    203209                }
     
    208214                {
    209215                        /* Time for some Passport black magic... */
    210                         if( !passport_get_id( msn_auth_got_passport_id, gc, gc->username, gc->password, cmd[4] ) )
    211                         {
    212                                 hide_login_progress_error( gc, "Error while contacting Passport server" );
    213                                 signoff( gc );
     216                        if( !passport_get_token( msn_auth_got_passport_token, ic, ic->acc->user, ic->acc->pass, cmd[4] ) )
     217                        {
     218                                imcb_error( ic, "Error while contacting Passport server" );
     219                                imc_logout( ic, TRUE );
    214220                                return( 0 );
    215221                        }
     
    217223                else if( num_parts == 7 && strcmp( cmd[2], "OK" ) == 0 )
    218224                {
     225                        set_t *s;
     226                       
    219227                        http_decode( cmd[4] );
    220228                       
    221                         strncpy( gc->displayname, cmd[4], sizeof( gc->displayname ) );
    222                         gc->displayname[sizeof(gc->displayname)-1] = 0;
    223                        
    224                         set_login_progress( gc, 1, "Authenticated, getting buddy list" );
     229                        strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) );
     230                        ic->displayname[sizeof(ic->displayname)-1] = 0;
     231                       
     232                        if( ( s = set_find( &ic->acc->set, "display_name" ) ) )
     233                        {
     234                                g_free( s->value );
     235                                s->value = g_strdup( cmd[4] );
     236                        }
     237                       
     238                        imcb_log( ic, "Authenticated, getting buddy list" );
    225239                       
    226240                        g_snprintf( buf, sizeof( buf ), "SYN %d 0\r\n", ++md->trId );
    227                         return( msn_write( gc, buf, strlen( buf ) ) );
     241                        return( msn_write( ic, buf, strlen( buf ) ) );
    228242                }
    229243                else
    230244                {
    231                         hide_login_progress( gc, "Unknown authentication type" );
    232                         signoff( gc );
     245                        imcb_error( ic, "Unknown authentication type" );
     246                        imc_logout( ic, FALSE );
    233247                        return( 0 );
    234248                }
     
    238252                if( num_parts != 4 )
    239253                {
    240                         hide_login_progress_error( gc, "Syntax error" );
    241                         signoff( gc );
     254                        imcb_error( ic, "Syntax error" );
     255                        imc_logout( ic, TRUE );
    242256                        return( 0 );
    243257                }
     
    247261                if( md->handler->msglen <= 0 )
    248262                {
    249                         hide_login_progress_error( gc, "Syntax error" );
    250                         signoff( gc );
     263                        imcb_error( ic, "Syntax error" );
     264                        imc_logout( ic, TRUE );
    251265                        return( 0 );
    252266                }
     
    262276                       
    263277                        if( !*cmd[3] || md->buddycount == 0 )
    264                                 msn_logged_in( gc );
     278                                msn_logged_in( ic );
    265279                }
    266280                else
     
    269283                           Let's assume everything is okay. */
    270284                       
    271                         msn_logged_in( gc );
     285                        msn_logged_in( ic );
    272286                }
    273287        }
     
    278292                if( num_parts != 4 && num_parts != 5 )
    279293                {
    280                         hide_login_progress( gc, "Syntax error" );
    281                         signoff( gc );
     294                        imcb_error( ic, "Syntax error" );
     295                        imc_logout( ic, TRUE );
    282296                        return( 0 );
    283297                }
     
    291305                        int num;
    292306                       
    293                         if( cmd[4] != NULL && sscanf( cmd[4], "%d", &num ) == 1 )
     307                        if( cmd[4] != NULL && sscanf( cmd[4], "%d", &num ) == 1 && num < md->groupcount )
    294308                                group = md->grouplist[num];
    295309                       
    296                         add_buddy( gc, group, cmd[1], cmd[2] );
     310                        imcb_add_buddy( ic, cmd[1], group );
     311                        imcb_rename_buddy( ic, cmd[1], cmd[2] );
    297312                }
    298313                if( list & 2 ) /* AL */
    299314                {
    300                         gc->permit = g_slist_append( gc->permit, g_strdup( cmd[1] ) );
     315                        ic->permit = g_slist_append( ic->permit, g_strdup( cmd[1] ) );
    301316                }
    302317                if( list & 4 ) /* BL */
    303318                {
    304                         gc->deny = g_slist_append( gc->deny, g_strdup( cmd[1] ) );
     319                        ic->deny = g_slist_append( ic->deny, g_strdup( cmd[1] ) );
    305320                }
    306321                if( list & 8 ) /* RL */
    307322                {
    308323                        if( ( list & 6 ) == 0 )
    309                                 msn_buddy_ask( gc, cmd[1], cmd[2] );
     324                                msn_buddy_ask( ic, cmd[1], cmd[2] );
    310325                }
    311326               
    312327                if( --md->buddycount == 0 )
    313328                {
    314                         if( gc->flags & OPT_LOGGED_IN )
    315                         {
    316                                 serv_got_crap( gc, "Successfully transferred to different server" );
     329                        if( ic->flags & OPT_LOGGED_IN )
     330                        {
     331                                imcb_log( ic, "Successfully transferred to different server" );
    317332                                g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 );
    318                                 return( msn_write( gc, buf, strlen( buf ) ) );
     333                                return( msn_write( ic, buf, strlen( buf ) ) );
    319334                        }
    320335                        else
    321336                        {
    322                                 msn_logged_in( gc );
     337                                msn_logged_in( ic );
    323338                        }
    324339                }
     
    330345                if( num_parts != 4 )
    331346                {
    332                         hide_login_progress_error( gc, "Syntax error" );
    333                         signoff( gc );
     347                        imcb_error( ic, "Syntax error" );
     348                        imc_logout( ic, TRUE );
    334349                        return( 0 );
    335350                }
     
    349364                if( num_parts != 3 )
    350365                {
    351                         hide_login_progress_error( gc, "Syntax error" );
    352                         signoff( gc );
     366                        imcb_error( ic, "Syntax error" );
     367                        imc_logout( ic, TRUE );
    353368                        return( 0 );
    354369                }
     
    363378                        g_snprintf( buf + strlen( buf ), 3, "%02x", digest[i] );
    364379               
    365                 return( msn_write( gc, buf, strlen( buf ) ) );
     380                return( msn_write( ic, buf, strlen( buf ) ) );
    366381        }
    367382        else if( strcmp( cmd[0], "ILN" ) == 0 )
     
    371386                if( num_parts != 6 )
    372387                {
    373                         hide_login_progress_error( gc, "Syntax error" );
    374                         signoff( gc );
     388                        imcb_error( ic, "Syntax error" );
     389                        imc_logout( ic, TRUE );
    375390                        return( 0 );
    376391                }
    377392               
    378393                http_decode( cmd[4] );
    379                 serv_buddy_rename( gc, cmd[3], cmd[4] );
     394                imcb_rename_buddy( ic, cmd[3], cmd[4] );
    380395               
    381396                st = msn_away_state_by_code( cmd[2] );
     
    386401                }
    387402               
    388                 serv_got_update( gc, cmd[3], 1, 0, 0, 0, st->number, 0 );
     403                imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN |
     404                                   ( st->number ? OPT_AWAY : 0 ), st->name, NULL );
    389405        }
    390406        else if( strcmp( cmd[0], "FLN" ) == 0 )
    391407        {
    392408                if( cmd[1] )
    393                         serv_got_update( gc, cmd[1], 0, 0, 0, 0, 0, 0 );
     409                        imcb_buddy_status( ic, cmd[1], 0, NULL, NULL );
    394410        }
    395411        else if( strcmp( cmd[0], "NLN" ) == 0 )
     
    399415                if( num_parts != 5 )
    400416                {
    401                         hide_login_progress_error( gc, "Syntax error" );
    402                         signoff( gc );
     417                        imcb_error( ic, "Syntax error" );
     418                        imc_logout( ic, TRUE );
    403419                        return( 0 );
    404420                }
    405421               
    406422                http_decode( cmd[3] );
    407                 serv_buddy_rename( gc, cmd[2], cmd[3] );
     423                imcb_rename_buddy( ic, cmd[2], cmd[3] );
    408424               
    409425                st = msn_away_state_by_code( cmd[1] );
     
    414430                }
    415431               
    416                 serv_got_update( gc, cmd[2], 1, 0, 0, 0, st->number, 0 );
     432                imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN |
     433                                   ( st->number ? OPT_AWAY : 0 ), st->name, NULL );
    417434        }
    418435        else if( strcmp( cmd[0], "RNG" ) == 0 )
     
    424441                if( num_parts != 7 )
    425442                {
    426                         hide_login_progress_error( gc, "Syntax error" );
    427                         signoff( gc );
     443                        imcb_error( ic, "Syntax error" );
     444                        imc_logout( ic, TRUE );
    428445                        return( 0 );
    429446                }
     
    434451                if( !server )
    435452                {
    436                         hide_login_progress_error( gc, "Syntax error" );
    437                         signoff( gc );
     453                        imcb_error( ic, "Syntax error" );
     454                        imc_logout( ic, TRUE );
    438455                        return( 0 );
    439456                }
     
    444461                if( strcmp( cmd[3], "CKI" ) != 0 )
    445462                {
    446                         hide_login_progress_error( gc, "Unknown authentication method for switchboard" );
    447                         signoff( gc );
     463                        imcb_error( ic, "Unknown authentication method for switchboard" );
     464                        imc_logout( ic, TRUE );
    448465                        return( 0 );
    449466                }
     
    451468                debug( "Got a call from %s (session %d). Key = %s", cmd[5], session, cmd[4] );
    452469               
    453                 sb = msn_sb_create( gc, server, port, cmd[4], session );
     470                sb = msn_sb_create( ic, server, port, cmd[4], session );
    454471                sb->who = g_strdup( cmd[5] );
    455472        }
     
    464481                        if( strchr( cmd[4], '@' ) == NULL )
    465482                        {
    466                                 hide_login_progress_error( gc, "Syntax error" );
    467                                 signoff( gc );
    468                                 return( 0 );
    469                         }
    470                        
    471                         /* We got added by someone. If we don't have this person in permit/deny yet, inform the user. */
    472                         for( l = gc->permit; l; l = l->next )
     483                                imcb_error( ic, "Syntax error" );
     484                                imc_logout( ic, TRUE );
     485                                return 0;
     486                        }
     487                       
     488                        /* We got added by someone. If we don't have this
     489                           person in permit/deny yet, inform the user. */
     490                        for( l = ic->permit; l; l = l->next )
    473491                                if( g_strcasecmp( l->data, cmd[4] ) == 0 )
    474                                         return( 1 );
    475                        
    476                         for( l = gc->deny; l; l = l->next )
     492                                        return 1;
     493                       
     494                        for( l = ic->deny; l; l = l->next )
    477495                                if( g_strcasecmp( l->data, cmd[4] ) == 0 )
    478                                         return( 1 );
    479                        
    480                         msn_buddy_ask( gc, cmd[4], cmd[5] );
     496                                        return 1;
     497                       
     498                        msn_buddy_ask( ic, cmd[4], cmd[5] );
     499                }
     500                else if( num_parts >= 6 && strcmp( cmd[2], "FL" ) == 0 )
     501                {
     502                        http_decode( cmd[5] );
     503                        imcb_add_buddy( ic, cmd[4], NULL );
     504                        imcb_rename_buddy( ic, cmd[4], cmd[5] );
    481505                }
    482506        }
    483507        else if( strcmp( cmd[0], "OUT" ) == 0 )
    484508        {
     509                int allow_reconnect = TRUE;
     510               
    485511                if( cmd[1] && strcmp( cmd[1], "OTH" ) == 0 )
    486512                {
    487                         hide_login_progress_error( gc, "Someone else logged in with your account" );
    488                         gc->wants_to_die = 1;
     513                        imcb_error( ic, "Someone else logged in with your account" );
     514                        allow_reconnect = FALSE;
    489515                }
    490516                else if( cmd[1] && strcmp( cmd[1], "SSD" ) == 0 )
    491517                {
    492                         hide_login_progress_error( gc, "Terminating session because of server shutdown" );
     518                        imcb_error( ic, "Terminating session because of server shutdown" );
    493519                }
    494520                else
    495521                {
    496                         hide_login_progress_error( gc, "Session terminated by remote server (reason unknown)" );
    497                 }
    498                
    499                 signoff( gc );
     522                        imcb_error( ic, "Session terminated by remote server (reason unknown)" );
     523                }
     524               
     525                imc_logout( ic, allow_reconnect );
    500526                return( 0 );
    501527        }
     
    504530                if( num_parts != 5 )
    505531                {
    506                         hide_login_progress_error( gc, "Syntax error" );
    507                         signoff( gc );
    508                         return( 0 );
    509                 }
    510                
    511                 if( g_strcasecmp( cmd[3], gc->username ) == 0 )
    512                 {
     532                        imcb_error( ic, "Syntax error" );
     533                        imc_logout( ic, TRUE );
     534                        return( 0 );
     535                }
     536               
     537                if( g_strcasecmp( cmd[3], ic->acc->user ) == 0 )
     538                {
     539                        set_t *s;
     540                       
    513541                        http_decode( cmd[4] );
    514                         strncpy( gc->displayname, cmd[4], sizeof( gc->displayname ) );
    515                         gc->displayname[sizeof(gc->displayname)-1] = 0;
     542                        strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) );
     543                        ic->displayname[sizeof(ic->displayname)-1] = 0;
     544                       
     545                        if( ( s = set_find( &ic->acc->set, "display_name" ) ) )
     546                        {
     547                                g_free( s->value );
     548                                s->value = g_strdup( cmd[4] );
     549                        }
    516550                }
    517551                else
     
    519553                        /* This is not supposed to happen, but let's handle it anyway... */
    520554                        http_decode( cmd[4] );
    521                         serv_buddy_rename( gc, cmd[3], cmd[4] );
     555                        imcb_rename_buddy( ic, cmd[3], cmd[4] );
    522556                }
    523557        }
    524558        else if( strcmp( cmd[0], "IPG" ) == 0 )
    525559        {
    526                 do_error_dialog( gc, "Received IPG command, we don't handle them yet.", "MSN" );
     560                imcb_error( ic, "Received IPG command, we don't handle them yet." );
    527561               
    528562                md->handler->msglen = atoi( cmd[1] );
     
    530564                if( md->handler->msglen <= 0 )
    531565                {
    532                         hide_login_progress_error( gc, "Syntax error" );
    533                         signoff( gc );
     566                        imcb_error( ic, "Syntax error" );
     567                        imc_logout( ic, TRUE );
    534568                        return( 0 );
    535569                }
     
    540574                const struct msn_status_code *err = msn_status_by_number( num );
    541575               
    542                 g_snprintf( buf, sizeof( buf ), "Error reported by MSN server: %s", err->text );
    543                 do_error_dialog( gc, buf, "MSN" );
     576                imcb_error( ic, "Error reported by MSN server: %s", err->text );
    544577               
    545578                if( err->flags & STATUS_FATAL )
    546579                {
    547                         signoff( gc );
     580                        imc_logout( ic, TRUE );
    548581                        return( 0 );
    549582                }
     
    551584        else
    552585        {
    553                 debug( "Received unknown command from main server: %s", cmd[0] );
     586                /* debug( "Received unknown command from main server: %s", cmd[0] ); */
    554587        }
    555588       
     
    559592static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts )
    560593{
    561         struct gaim_connection *gc = data;
     594        struct im_connection *ic = data;
    562595        char *body;
    563596        int blen = 0;
     
    595628                                {
    596629                                        if( arg1 )
    597                                                 serv_got_crap( gc, "The server is going down for maintenance in %s minutes.", arg1 );
     630                                                imcb_log( ic, "The server is going down for maintenance in %s minutes.", arg1 );
    598631                                }
    599632                               
     
    610643                                char *folders = msn_findheader( body, "Folders-Unread:", blen );
    611644                               
    612                                 if( inbox && folders )
     645                                if( inbox && folders && set_getbool( &ic->acc->set, "mail_notifications" ) )
    613646                                {
    614                                         serv_got_crap( gc, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
     647                                        imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
    615648                                }
    616649                        }
     
    620653                                char *fromname = msn_findheader( body, "From:", blen );
    621654                               
    622                                 if( from && fromname )
     655                                if( from && fromname && set_getbool( &ic->acc->set, "mail_notifications" ) )
    623656                                {
    624                                         serv_got_crap( gc, "Received an e-mail message from %s <%s>.", fromname, from );
     657                                        imcb_log( ic, "Received an e-mail message from %s <%s>.", fromname, from );
    625658                                }
    626659                        }
     
    641674}
    642675
    643 static void msn_auth_got_passport_id( struct passport_reply *rep )
     676static void msn_auth_got_passport_token( struct msn_auth_data *mad )
    644677{
    645         struct gaim_connection *gc = rep->data;
    646         struct msn_data *md = gc->proto_data;
    647         char *key = rep->result;
    648         char buf[1024];
    649        
    650         if( key == NULL )
    651         {
    652                 hide_login_progress( gc, "Error during Passport authentication" );
    653                 signoff( gc );
     678        struct im_connection *ic = mad->data;
     679        struct msn_data *md;
     680       
     681        /* Dead connection? */
     682        if( g_slist_find( msn_connections, ic ) == NULL )
     683                return;
     684       
     685        md = ic->proto_data;
     686        if( mad->token )
     687        {
     688                char buf[1024];
     689               
     690                g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token );
     691                msn_write( ic, buf, strlen( buf ) );
    654692        }
    655693        else
    656694        {
    657                 g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, key );
    658                 msn_write( gc, buf, strlen( buf ) );
     695                imcb_error( ic, "Error during Passport authentication: %s", mad->error );
     696                imc_logout( ic, TRUE );
    659697        }
    660698}
  • protocols/msn/passport.c

    r875ad42 r85d7b85  
    1 /* passport.c
     1/** passport.c
    22 *
    3  * Functions to login to microsoft passport service for Messenger
    4  * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>
    5  * Copyright (C) 2004 Wilmer van der Gaast <wilmer@gaast.net>
     3 * Functions to login to Microsoft Passport service for Messenger
     4 * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net>
    65 *
    76 * This program is free software; you can redistribute it and/or modify             
     
    2423#include "msn.h"
    2524#include "bitlbee.h"
     25#include "url.h"
     26#include "misc.h"
     27#include "xmltree.h"
    2628#include <ctype.h>
    2729#include <errno.h>
    2830
    29 #define MSN_BUF_LEN 8192
     31static int passport_get_token_real( struct msn_auth_data *mad );
     32static void passport_get_token_ready( struct http_request *req );
    3033
    31 static char *prd_cached = NULL;
    32 
    33 static int passport_get_id_real( gpointer func, gpointer data, char *header );
    34 static void passport_get_id_ready( struct http_request *req );
    35 
    36 static int passport_retrieve_dalogin( gpointer data, gpointer func, char *header );
    37 static void passport_retrieve_dalogin_ready( struct http_request *req );
    38 
    39 static char *passport_create_header( char *cookie, char *email, char *pwd );
    40 static void destroy_reply( struct passport_reply *rep );
    41 
    42 int passport_get_id( gpointer func, gpointer data, char *username, char *password, char *cookie )
     34int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie )
    4335{
    44         char *header = passport_create_header( cookie, username, password );
     36        struct msn_auth_data *mad = g_new0( struct msn_auth_data, 1 );
     37        int i;
    4538       
    46         if( prd_cached == NULL )
    47                 return passport_retrieve_dalogin( func, data, header );
    48         else
    49                 return passport_get_id_real( func, data, header );
     39        mad->username = g_strdup( username );
     40        mad->password = g_strdup( password );
     41        mad->cookie = g_strdup( cookie );
     42       
     43        mad->callback = func;
     44        mad->data = data;
     45       
     46        mad->url = g_strdup( SOAP_AUTHENTICATION_URL );
     47        mad->ttl = 3; /* Max. # of redirects. */
     48       
     49        /* HTTP-escape stuff and s/,/&/ */
     50        http_decode( mad->cookie );
     51        for( i = 0; mad->cookie[i]; i ++ )
     52                if( mad->cookie[i] == ',' )
     53                        mad->cookie[i] = '&';
     54       
     55        /* Microsoft doesn't allow password longer than 16 chars and silently
     56           fails authentication if you give the "full version" of your passwd. */
     57        if( strlen( mad->password ) > MAX_PASSPORT_PWLEN )
     58                mad->password[MAX_PASSPORT_PWLEN] = 0;
     59       
     60        return passport_get_token_real( mad );
    5061}
    5162
    52 static int passport_get_id_real( gpointer func, gpointer data, char *header )
     63static int passport_get_token_real( struct msn_auth_data *mad )
    5364{
    54         struct passport_reply *rep;
    55         char *server, *dummy, *reqs;
     65        char *post_payload, *post_request;
    5666        struct http_request *req;
     67        url_t url;
    5768       
    58         rep = g_new0( struct passport_reply, 1 );
    59         rep->data = data;
    60         rep->func = func;
     69        url_set( &url, mad->url );
    6170       
    62         server = g_strdup( prd_cached );
    63         dummy = strchr( server, '/' );
     71        post_payload = g_markup_printf_escaped( SOAP_AUTHENTICATION_PAYLOAD,
     72                                                mad->username,
     73                                                mad->password,
     74                                                mad->cookie );
    6475       
    65         if( dummy == NULL )
    66         {
    67                 destroy_reply( rep );
    68                 return( 0 );
    69         }
     76        post_request = g_strdup_printf( SOAP_AUTHENTICATION_REQUEST,
     77                                        url.file, url.host,
     78                                        (int) strlen( post_payload ),
     79                                        post_payload );
     80                                       
     81        req = http_dorequest( url.host, url.port, 1, post_request,
     82                              passport_get_token_ready, mad );
    7083       
    71         reqs = g_malloc( strlen( header ) + strlen( dummy ) + 128 );
    72         sprintf( reqs, "GET %s HTTP/1.0\r\n%s\r\n\r\n", dummy, header );
     84        g_free( post_request );
     85        g_free( post_payload );
    7386       
    74         *dummy = 0;
    75         req = http_dorequest( server, 443, 1, reqs, passport_get_id_ready, rep );
    76        
    77         g_free( server );
    78         g_free( reqs );
    79        
    80         if( req == NULL )
    81                 destroy_reply( rep );
    82        
    83         return( req != NULL );
     87        return req != NULL;
    8488}
    8589
    86 static void passport_get_id_ready( struct http_request *req )
     90static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data );
     91static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data );
     92
     93static const struct xt_handler_entry passport_xt_handlers[] = {
     94        { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", passport_xt_extract_token },
     95        { "S:Fault",                  "S:Envelope",                 passport_xt_handle_fault  },
     96        { NULL,                       NULL,                         NULL                      }
     97};
     98
     99static void passport_get_token_ready( struct http_request *req )
    87100{
    88         struct passport_reply *rep = req->data;
     101        struct msn_auth_data *mad = req->data;
     102        struct xt_parser *parser;
    89103       
    90         if( !g_slist_find( msn_connections, rep->data ) || !req->finished || !req->reply_headers )
    91         {
    92                 destroy_reply( rep );
    93                 return;
    94         }
     104        g_free( mad->url );
     105        g_free( mad->error );
     106        mad->url = mad->error = NULL;
    95107       
    96108        if( req->status_code == 200 )
    97109        {
    98                 char *dummy;
    99                
    100                 if( ( dummy = strstr( req->reply_headers, "from-PP='" ) ) )
    101                 {
    102                         char *responseend;
    103                        
    104                         dummy += strlen( "from-PP='" );
    105                         responseend = strchr( dummy, '\'' );
    106                         if( responseend )
    107                                 *responseend = 0;
    108                        
    109                         rep->result = g_strdup( dummy );
    110                 }
     110                parser = xt_new( passport_xt_handlers, mad );
     111                xt_feed( parser, req->reply_body, req->body_size );
     112                xt_handle( parser, NULL, -1 );
     113                xt_free( parser );
     114        }
     115        else
     116        {
     117                mad->error = g_strdup_printf( "HTTP error %d (%s)", req->status_code,
     118                                              req->status_string ? req->status_string : "unknown" );
    111119        }
    112120       
    113         rep->func( rep );
    114         destroy_reply( rep );
     121        if( mad->error == NULL && mad->token == NULL )
     122                mad->error = g_strdup( "Could not parse Passport server response" );
     123       
     124        if( mad->url && mad->token == NULL )
     125        {
     126                passport_get_token_real( mad );
     127        }
     128        else
     129        {
     130                mad->callback( mad );
     131               
     132                g_free( mad->url );
     133                g_free( mad->username );
     134                g_free( mad->password );
     135                g_free( mad->cookie );
     136                g_free( mad->token );
     137                g_free( mad->error );
     138                g_free( mad );
     139        }
    115140}
    116141
    117 static char *passport_create_header( char *cookie, char *email, char *pwd )
     142static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data )
    118143{
    119         char *buffer = g_new0( char, 2048 );
    120         char *currenttoken;
    121         char *email_enc, *pwd_enc;
     144        struct msn_auth_data *mad = data;
     145        char *s;
    122146       
    123         email_enc = g_new0( char, strlen( email ) * 3 + 1 );
    124         strcpy( email_enc, email );
    125         http_encode( email_enc );
     147        if( ( s = xt_find_attr( node, "Id" ) ) && strcmp( s, "PPToken1" ) == 0 )
     148                mad->token = g_memdup( node->text, node->text_len + 1 );
    126149       
    127         pwd_enc = g_new0( char, strlen( pwd ) * 3 + 1 );
    128         strcpy( pwd_enc, pwd );
    129         http_encode( pwd_enc );
    130        
    131         currenttoken = strstr( cookie, "lc=" );
    132         if( currenttoken == NULL )
    133                 return( NULL );
    134        
    135         g_snprintf( buffer, 2048,
    136                     "Authorization: Passport1.4 OrgVerb=GET,"
    137                     "OrgURL=http%%3A%%2F%%2Fmessenger%%2Emsn%%2Ecom,"
    138                     "sign-in=%s,pwd=%s,%s", email_enc, pwd_enc,
    139                     currenttoken );
    140        
    141         g_free( email_enc );
    142         g_free( pwd_enc );
    143        
    144         return( buffer );
     150        return XT_HANDLED;
    145151}
    146152
    147 #define PPR_REQUEST "GET /rdr/pprdr.asp HTTP/1.0\r\n\r\n"
    148 static int passport_retrieve_dalogin( gpointer func, gpointer data, char *header )
     153static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data )
    149154{
    150         struct passport_reply *rep = g_new0( struct passport_reply, 1 );
    151         struct http_request *req;
     155        struct msn_auth_data *mad = data;
     156        struct xt_node *code = xt_find_node( node->children, "faultcode" );
     157        struct xt_node *string = xt_find_node( node->children, "faultstring" );
     158        struct xt_node *redirect = xt_find_node( node->children, "psf:redirectUrl" );
    152159       
    153         rep->data = data;
    154         rep->func = func;
    155         rep->header = header;
     160        if( redirect && redirect->text_len && mad->ttl-- > 0 )
     161                mad->url = g_memdup( redirect->text, redirect->text_len + 1 );
    156162       
    157         req = http_dorequest( "nexus.passport.com", 443, 1, PPR_REQUEST, passport_retrieve_dalogin_ready, rep );
     163        if( code == NULL || code->text_len == 0 )
     164                mad->error = g_strdup( "Unknown error" );
     165        else
     166                mad->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ?
     167                                              string->text : "no description available" );
    158168       
    159         if( !req )
    160                 destroy_reply( rep );
    161        
    162         return( req != NULL );
     169        return XT_HANDLED;
    163170}
    164 
    165 static void passport_retrieve_dalogin_ready( struct http_request *req )
    166 {
    167         struct passport_reply *rep = req->data;
    168         char *dalogin;
    169         char *urlend;
    170        
    171         if( !g_slist_find( msn_connections, rep->data ) || !req->finished || !req->reply_headers )
    172         {
    173                 destroy_reply( rep );
    174                 return;
    175         }
    176        
    177         dalogin = strstr( req->reply_headers, "DALogin=" );     
    178        
    179         if( !dalogin )
    180                 goto failure;
    181        
    182         dalogin += strlen( "DALogin=" );
    183         urlend = strchr( dalogin, ',' );
    184         if( urlend )
    185                 *urlend = 0;
    186        
    187         /* strip the http(s):// part from the url */
    188         urlend = strstr( urlend, "://" );
    189         if( urlend )
    190                 dalogin = urlend + strlen( "://" );
    191        
    192         if( prd_cached == NULL )
    193                 prd_cached = g_strdup( dalogin );
    194        
    195         if( passport_get_id_real( rep->func, rep->data, rep->header ) )
    196         {
    197                 destroy_reply( rep );
    198                 return;
    199         }
    200        
    201 failure:       
    202         rep->func( rep );
    203         destroy_reply( rep );
    204 }
    205 
    206 static void destroy_reply( struct passport_reply *rep )
    207 {
    208         g_free( rep->result );
    209         g_free( rep->header );
    210         g_free( rep );
    211 }
  • protocols/msn/passport.h

    r875ad42 r85d7b85  
    1 #ifndef __PASSPORT_H__
    2 #define __PASSPORT_H__
    31/* passport.h
    42 *
    5  * Functions to login to Microsoft Passport Service for Messenger
    6  * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>,
    7  *                    Wilmer van der Gaast <wilmer@gaast.net>
     3 * Functions to login to Microsoft Passport service for Messenger
     4 * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net>
    85 *
    96 * This program is free software; you can redistribute it and/or modify             
     
    1815 * You should have received a copy of the GNU General Public License               
    1916 * along with this program; if not, write to the Free Software                     
    20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA         
     17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA         
    2118 */
     19
     20/* Thanks to http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener
     21   for the specs! */
     22
     23#ifndef __PASSPORT_H__
     24#define __PASSPORT_H__
    2225
    2326#include <stdio.h>
     
    3336#include "nogaim.h"
    3437
    35 struct passport_reply
     38#define MAX_PASSPORT_PWLEN 16
     39
     40struct msn_auth_data
    3641{
    37         void (*func)( struct passport_reply * );
    38         void *data;
    39         char *result;
    40         char *header;
     42        char *url;
     43        int ttl;
     44       
     45        char *username;
     46        char *password;
     47        char *cookie;
     48       
     49        /* The end result, the only thing we'll really be interested in
     50           once finished. */
     51        char *token;
     52        char *error; /* Yeah, or that... */
     53       
     54        void (*callback)( struct msn_auth_data *mad );
     55        gpointer data;
    4156};
    4257
    43 int passport_get_id( gpointer func, gpointer data, char *username, char *password, char *cookie );
     58#define SOAP_AUTHENTICATION_URL "https://loginnet.passport.com/RST.srf"
     59
     60#define SOAP_AUTHENTICATION_REQUEST \
     61"POST %s HTTP/1.0\r\n" \
     62"Accept: text/*\r\n" \
     63"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \
     64"Host: %s\r\n" \
     65"Content-Length: %d\r\n" \
     66"Cache-Control: no-cache\r\n" \
     67"\r\n" \
     68"%s"
     69
     70#define SOAP_AUTHENTICATION_PAYLOAD \
     71"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \
     72"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">" \
     73  "<Header>" \
     74    "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">" \
     75      "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>" \
     76      "<ps:BinaryVersion>4</ps:BinaryVersion>" \
     77      "<ps:UIVersion>1</ps:UIVersion>" \
     78      "<ps:Cookies></ps:Cookies>" \
     79      "<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>" \
     80    "</ps:AuthInfo>" \
     81    "<wsse:Security>" \
     82       "<wsse:UsernameToken Id=\"user\">" \
     83         "<wsse:Username>%s</wsse:Username>" \
     84         "<wsse:Password>%s</wsse:Password>" \
     85       "</wsse:UsernameToken>" \
     86    "</wsse:Security>" \
     87  "</Header>" \
     88  "<Body>" \
     89    "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">" \
     90      "<wst:RequestSecurityToken Id=\"RST0\">" \
     91        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
     92        "<wsp:AppliesTo>" \
     93          "<wsa:EndpointReference>" \
     94            "<wsa:Address>http://Passport.NET/tb</wsa:Address>" \
     95          "</wsa:EndpointReference>" \
     96        "</wsp:AppliesTo>" \
     97      "</wst:RequestSecurityToken>" \
     98      "<wst:RequestSecurityToken Id=\"RST1\">" \
     99       "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
     100        "<wsp:AppliesTo>" \
     101          "<wsa:EndpointReference>" \
     102            "<wsa:Address>messenger.msn.com</wsa:Address>" \
     103          "</wsa:EndpointReference>" \
     104        "</wsp:AppliesTo>" \
     105        "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>" \
     106      "</wst:RequestSecurityToken>" \
     107    "</ps:RequestMultipleSecurityTokens>" \
     108  "</Body>" \
     109"</Envelope>"
     110
     111int passport_get_token( gpointer func, gpointer data