Changeset 0fd8559


Ignore:
Timestamp:
2007-02-18T17:48:04Z (18 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
0f4c1bb5
Parents:
8de63c3 (diff), c7d0f41 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

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

Files:
8 added
28 deleted
14 edited

Legend:

Unmodified
Added
Removed
  • configure

    r8de63c3 r0fd8559  
    328328
    329329echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings
     330
     331for i in /lib /usr/lib /usr/local/lib; do
     332        if [ -e $i/libresolv.a ]; then
     333                echo '#define HAVE_RESOLV_A' >> config.h
     334                echo 'EFLAGS+='$i'/libresolv.a' >> Makefile.settings
     335                break
     336        fi
     337done
    330338
    331339STORAGES="text xml"
  • doc/CHANGES

    r8de63c3 r0fd8559  
    4141    and password for the existing connection.
    4242  * Per-account settings (see the new "account set" command).
     43- A brand new Jabber module. Besides the major code cleanup, it also has
     44  has these new features:
     45  * Pretty complete XMPP support: RFC3920, RFC3921 plus a number of XEPs
     46    including XEP73 and XEP85. (See http://www.xmpp.org/ for what all these
     47    things mean exactly.) Privacy lists are not supported for obvious
     48    reasons.
     49  * This complete support also includes TLS and SASL support and SRV record
     50    lookup. This means that specifying a server tag for connections should
     51    (almost) never be necessary anymore, BitlBee can find the server and can
     52    automatically convert plaintext connections to TLS-encrypted ones.
     53  * XEP85 means typing notifications. The older XEP22 (still used by some
     54    clients including Gaim <2.0) is not supported.
     55  * Better handling of buddies who have more than one resource on-line. As
     56    long as one resource is on-line (and visible), BitlBee will show this.
     57    (The previous module didn't keep track of resources and sent an offline
     58    event as soon as any resource disappears.)
     59  * You can now set your resource priority.
     60  * The info command now gives away state/message information for all
     61    resources available for that buddy. (Of course this only works if the
     62    buddy is in your contact list.)
    4363
    4464Version 1.0.3:
  • doc/user-guide/commands.xml

    r8de63c3 r0fd8559  
    3030                                <description>
    3131                                        <para>
    32                                                 Note that the servertag argument is optional. You only have to use it if the part after the @ in your handle isn't the hostname of your Jabber server, or if you want to use SSL/connect to a non-standard port number. The format is simple: [&lt;servername&gt;[:&lt;portnumber&gt;][:ssl]].
     32                                                The handle should be a full handle, including the domain name. You can specify a servername if necessary. Normally BitlBee doesn't need this though, since it's able to find out the server by doing DNS SRV lookups.
     33                                        </para>
     34
     35                                        <para>
     36                                                In previous versions it was also possible to specify port numbers and/or SSL in the server tag. This is deprecated and should now be done using the <emphasis>account set</emphasis> command. This also applies to specifying a resource in the handle (like <emphasis>wilmer@bitlbee.org/work</emphasis>).
    3337                                        </para>
    3438                                </description>
    35 
    36                                 <description>
    37                                         <para>
    38                                                 Google Talk uses the Jabber protocol. Please note that Google talk is SSL-only, but officially reachable over both port 5222 and 5223. Usually BitlBee users have to connect via port 5223, for example like this:
    39                                         </para>
    40                                 </description>
    41 
    42                                 <ircexample>
    43                                         <ircline nick="wilmer">account add jabber example@gmail.com hobbelmeeuw talk.google.com:5223:ssl</ircline>
    44                                         <ircline nick="root">Account successfully added</ircline>
    45                                 </ircexample>
    4639                        </bitlbee-command>
    4740
     
    517510        </bitlbee-setting>
    518511
     512        <bitlbee-setting name="priority" type="integer" scope="account">
     513                <default>0</default>
     514
     515                <description>
     516                        <para>
     517                                Can be set for Jabber connections. When connecting to one account from multiple places, this priority value will help the server to determine where to deliver incoming messages (that aren't addressed to a specific resource already).
     518                        </para>
     519
     520                        <para>
     521                                According to RFC 3921 servers will always deliver messages to the server with the highest priority value. Mmessages will not be delivered to resources with a negative priority setting (and should be saved as an off-line message if all available resources have a negative priority value).
     522                        </para>
     523                </description>
     524        </bitlbee-setting>
     525
    519526        <bitlbee-setting name="private" type="boolean" scope="global">
    520527                <default>true</default>
     
    556563        </bitlbee-setting>
    557564
     565        <bitlbee-setting name="resource_select" type="string" scope="account">
     566                <default>priority</default>
     567                <possible-values>priority, time</possible-values>
     568
     569                <description>
     570                        <para>
     571                                Because the IRC interface makes it pretty hard to specify the resource to talk to (when a buddy is online through different resources), this setting was added.
     572                        </para>
     573
     574                        <para>
     575                                Normally it's set to <emphasis>priority</emphasis> which means messages will always be delivered to the buddy's resource with the highest priority. If the setting is set to <emphasis>time</emphasis>, messages will be delivered to the resource that was last used to send you a message (or the resource that most recently connected).
     576                        </para>
     577                </description>
     578        </bitlbee-setting>
     579
    558580        <bitlbee-setting name="save_on_quit" type="boolean" scope="global">
    559581                <default>true</default>
     
    593615                        <para>
    594616                                If BitlBee fails to detect this sometimes (most likely in AIM messages over an ICQ connection), you can set this setting to <emphasis>always</emphasis>, but this might sometimes accidentally strip non-HTML things too.
     617                        </para>
     618                </description>
     619        </bitlbee-setting>
     620
     621        <bitlbee-setting name="tls" type="boolean" scope="account">
     622                <default>try</default>
     623
     624                <description>
     625                        <para>
     626                                Newer Jabber servers allow clients to convert a plain-text session to a TLS/SSL-encrypted session. Normally (with this setting set to <emphasis>try</emphasis>) BitlBee will do this, if possible.
     627                        </para>
     628
     629                        <para>
     630                                If you want to force BitlBee to use TLS sessions only (and to give up if that doesn't seem to be possible) you can set this setting to <emphasis>true</emphasis>. Set it to <emphasis>false</emphasis> if you want the session to remain plain-text.
    595631                        </para>
    596632                </description>
  • lib/misc.c

    r8de63c3 r0fd8559  
    3939#include <glib.h>
    4040#include <time.h>
     41
     42#ifdef HAVE_RESOLV_A
     43#include <arpa/nameser.h>
     44#include <resolv.h>
     45#endif
    4146
    4247void strip_linefeed(gchar *text)
     
    488493        return 0;
    489494}
     495
     496struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain )
     497{       
     498        struct ns_srv_reply *reply = NULL;
     499#ifdef HAVE_RESOLV_A
     500        char name[1024];
     501        unsigned char querybuf[1024];
     502        const unsigned char *buf;
     503        ns_msg nsh;
     504        ns_rr rr;
     505        int i, len, size;
     506       
     507        g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain );
     508       
     509        if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 )
     510                return NULL;
     511       
     512        if( ns_initparse( querybuf, size, &nsh ) != 0 )
     513                return NULL;
     514       
     515        if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 )
     516                return NULL;
     517       
     518        size = ns_rr_rdlen( rr );
     519        buf = ns_rr_rdata( rr );
     520       
     521        len = 0;
     522        for( i = 6; i < size && buf[i]; i += buf[i] + 1 )
     523                len += buf[i] + 1;
     524       
     525        if( i > size )
     526                return NULL;
     527       
     528        reply = g_malloc( sizeof( struct ns_srv_reply ) + len );
     529        memcpy( reply->name, buf + 7, len );
     530       
     531        for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 )
     532                reply->name[i] = '.';
     533       
     534        if( i > len )
     535        {
     536                g_free( reply );
     537                return NULL;
     538        }
     539       
     540        reply->prio = ( buf[0] << 8 ) | buf[1];
     541        reply->weight = ( buf[2] << 8 ) | buf[3];
     542        reply->port = ( buf[4] << 8 ) | buf[5];
     543#endif
     544       
     545        return reply;
     546}
  • lib/misc.h

    r8de63c3 r0fd8559  
    3030#include <time.h>
    3131
     32struct ns_srv_reply
     33{
     34        int prio;
     35        int weight;
     36        int port;
     37        char name[];
     38};
     39
    3240G_MODULE_EXPORT void strip_linefeed( gchar *text );
    3341G_MODULE_EXPORT char *add_cr( char *text );
     
    5462G_MODULE_EXPORT int bool2int( char *value );
    5563
     64G_MODULE_EXPORT struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain );
     65
    5666#endif
  • lib/ssl_client.h

    r8de63c3 r0fd8559  
    5252G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data );
    5353
     54/* Start an SSL session on an existing fd. Useful for STARTTLS functionality,
     55   for example in Jabber. */
     56G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer data );
     57
    5458/* Obviously you need special read/write functions to read data. */
    5559G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len );
  • lib/ssl_gnutls.c

    r8de63c3 r0fd8559  
    4949
    5050static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
     51static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
     52static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
    5153
    5254
     
    6365        {
    6466                g_free( conn );
    65                 return( NULL );
     67                return NULL;
     68        }
     69       
     70        return conn;
     71}
     72
     73void *ssl_starttls( int fd, ssl_input_function func, gpointer data )
     74{
     75        struct scd *conn = g_new0( struct scd, 1 );
     76       
     77        conn->fd = fd;
     78        conn->func = func;
     79        conn->data = data;
     80        conn->inpa = -1;
     81       
     82        /* This function should be called via a (short) timeout instead of
     83           directly from here, because these SSL calls are *supposed* to be
     84           *completely* asynchronous and not ready yet when this function
     85           (or *_connect, for examle) returns. Also, errors are reported via
     86           the callback function, not via this function's return value.
     87           
     88           In short, doing things like this makes the rest of the code a lot
     89           simpler. */
     90       
     91        b_timeout_add( 1, ssl_starttls_real, conn );
     92       
     93        return conn;
     94}
     95
     96static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond )
     97{
     98        struct scd *conn = data;
     99       
     100        return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE );
     101}
     102
     103static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
     104{
     105        struct scd *conn = data;
     106       
     107        if( source == -1 )
     108        {
     109                conn->func( conn->data, NULL, cond );
     110                g_free( conn );
     111                return FALSE;
    66112        }
    67113       
     
    77123        gnutls_set_default_priority( conn->session );
    78124        gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
    79        
    80         return( conn );
    81 }
    82 
    83 static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
    84 
    85 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
    86 {
    87         struct scd *conn = data;
    88        
    89         if( source == -1 )
    90         {
    91                 conn->func( conn->data, NULL, cond );
    92                
    93                 gnutls_deinit( conn->session );
    94                 gnutls_certificate_free_credentials( conn->xcred );
    95                
    96                 g_free( conn );
    97                
    98                 return FALSE;
    99         }
    100125       
    101126        sock_make_nonblocking( conn->fd );
  • lib/ssl_openssl.c

    r8de63c3 r0fd8559  
    5353
    5454static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
     55static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
     56static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
    5557
    5658
     
    5860{
    5961        struct scd *conn = g_new0( struct scd, 1 );
    60         SSL_METHOD *meth;
    6162       
    6263        conn->fd = proxy_connect( host, port, ssl_connected, conn );
    6364        conn->func = func;
    6465        conn->data = data;
     66        conn->inpa = -1;
    6567       
    6668        if( conn->fd < 0 )
    6769        {
    6870                g_free( conn );
    69                 return( NULL );
    70         }
     71                return NULL;
     72        }
     73       
     74        return conn;
     75}
     76
     77void *ssl_starttls( int fd, ssl_input_function func, gpointer data )
     78{
     79        struct scd *conn = g_new0( struct scd, 1 );
     80       
     81        conn->fd = fd;
     82        conn->func = func;
     83        conn->data = data;
     84        conn->inpa = -1;
     85       
     86        /* This function should be called via a (short) timeout instead of
     87           directly from here, because these SSL calls are *supposed* to be
     88           *completely* asynchronous and not ready yet when this function
     89           (or *_connect, for examle) returns. Also, errors are reported via
     90           the callback function, not via this function's return value.
     91           
     92           In short, doing things like this makes the rest of the code a lot
     93           simpler. */
     94       
     95        b_timeout_add( 1, ssl_starttls_real, conn );
     96       
     97        return conn;
     98}
     99
     100static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond )
     101{
     102        struct scd *conn = data;
     103       
     104        return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE );
     105}
     106
     107static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
     108{
     109        struct scd *conn = data;
     110        SSL_METHOD *meth;
     111       
     112        if( source == -1 )
     113                goto ssl_connected_failure;
    71114       
    72115        if( !initialized )
     
    79122        conn->ssl_ctx = SSL_CTX_new( meth );
    80123        if( conn->ssl_ctx == NULL )
    81         {
    82                 conn->fd = -1;
    83                 return( NULL );
    84         }
     124                goto ssl_connected_failure;
    85125       
    86126        conn->ssl = SSL_new( conn->ssl_ctx );
    87127        if( conn->ssl == NULL )
    88         {
    89                 conn->fd = -1;
    90                 return( NULL );
    91         }
    92        
    93         return( conn );
    94 }
    95 
    96 static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
    97 
    98 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
    99 {
    100         struct scd *conn = data;
    101        
    102         if( source == -1 )
    103                 return ssl_handshake( data, -1, cond );
     128                goto ssl_connected_failure;
    104129       
    105130        /* We can do at least the handshake with non-blocking I/O */
     
    108133       
    109134        return ssl_handshake( data, source, cond );
     135
     136ssl_connected_failure:
     137        conn->func( conn->data, NULL, cond );
     138       
     139        if( conn->ssl )
     140        {
     141                SSL_shutdown( conn->ssl );
     142                SSL_free( conn->ssl );
     143        }
     144        if( conn->ssl_ctx )
     145        {
     146                SSL_CTX_free( conn->ssl_ctx );
     147        }
     148        if( source >= 0 ) closesocket( source );
     149        g_free( conn );
     150       
     151        return FALSE;
     152
    110153}       
    111154
     
    119162                conn->lasterr = SSL_get_error( conn->ssl, st );
    120163                if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE )
    121                         goto ssl_connected_failure;
     164                {
     165                        conn->func( conn->data, NULL, cond );
     166                       
     167                        SSL_shutdown( conn->ssl );
     168                        SSL_free( conn->ssl );
     169                        SSL_CTX_free( conn->ssl_ctx );
     170                       
     171                        if( source >= 0 ) closesocket( source );
     172                        g_free( conn );
     173                       
     174                        return FALSE;
     175                }
    122176               
    123177                conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data );
     
    129183        conn->func( conn->data, conn, cond );
    130184        return FALSE;
    131        
    132 ssl_connected_failure:
    133         conn->func( conn->data, NULL, cond );
    134        
    135         if( conn->ssl )
    136         {
    137                 SSL_shutdown( conn->ssl );
    138                 SSL_free( conn->ssl );
    139         }
    140         if( conn->ssl_ctx )
    141         {
    142                 SSL_CTX_free( conn->ssl_ctx );
    143         }
    144         if( source >= 0 ) closesocket( source );
    145         g_free( conn );
    146        
    147         return FALSE;
    148185}
    149186
  • protocols/jabber/Makefile

    r8de63c3 r0fd8559  
    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 = io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o
    1313
    1414CFLAGS += -Wall
  • protocols/jabber/jabber.c

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

    r8de63c3 r0fd8559  
    1 /*
    2  *  This program is free software; you can redistribute it and/or modify
    3  *  it under the terms of the GNU General Public License as published by
    4  *  the Free Software Foundation; either version 2 of the License, or
    5  *  (at your option) any later version.
    6  *
    7  *  This program is distributed in the hope that it will be useful,
    8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    10  *  GNU General Public License for more details.
    11  *
    12  *  You should have received a copy of the GNU General Public License
    13  *  along with this program; if not, write to the Free Software
    14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    15  *
    16  *  Jabber
    17  *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
    18  */
     1/***************************************************************************\
     2*                                                                           *
     3*  BitlBee - An IRC to IM gateway                                           *
     4*  Jabber module - Main file                                                *
     5*                                                                           *
     6*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
     7*                                                                           *
     8*  This program is free software; you can redistribute it and/or modify     *
     9*  it under the terms of the GNU General Public License as published by     *
     10*  the Free Software Foundation; either version 2 of the License, or        *
     11*  (at your option) any later version.                                      *
     12*                                                                           *
     13*  This program is distributed in the hope that it will be useful,          *
     14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
     15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
     16*  GNU General Public License for more details.                             *
     17*                                                                           *
     18*  You should have received a copy of the GNU General Public License along  *
     19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
     20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
     21*                                                                           *
     22\***************************************************************************/
    1923
    20 #include <string.h>
    21 #include <stdlib.h>
    22 #include <sys/types.h>
    23 #include <stdio.h>
    24 #include <setjmp.h>
    25 #include <sys/stat.h>
    26 #include <fcntl.h>
    27 #include <errno.h>
    28 #include <signal.h>
    29 #include <stdarg.h>
    30 #include <time.h>
    31 #include <ctype.h>
    32 #ifdef _WIN32
    33 #undef DATADIR
    34 #include "sock.h"
     24#ifndef _JABBER_H
     25#define _JABBER_H
     26
     27#include <glib.h>
     28
     29#include "xmltree.h"
     30#include "bitlbee.h"
     31
     32typedef enum
     33{
     34        JFLAG_STREAM_STARTED = 1,       /* Set when we detected the beginning of the stream
     35                                           and want to do auth. */
     36        JFLAG_AUTHENTICATED = 2,        /* Set when we're successfully authenticatd. */
     37        JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after
     38                                           SASL or TLS). */
     39        JFLAG_WAIT_SESSION = 8,         /* Set if we sent a <session> tag and need a reply
     40                                           before we continue. */
     41        JFLAG_WAIT_BIND = 16,           /* ... for <bind> tag. */
     42        JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this
     43                                           activates all XEP-85 related code. */
     44} jabber_flags_t;
     45
     46typedef enum
     47{
     48        JBFLAG_PROBED_XEP85 = 1,        /* Set this when we sent our probe packet to make
     49                                           sure it gets sent only once. */
     50        JBFLAG_DOES_XEP85 = 2,          /* Set this when the resource seems to support
     51                                           XEP85 (typing notification shite). */
     52} jabber_buddy_flags_t;
     53
     54struct jabber_data
     55{
     56        struct gaim_connection *gc;
     57       
     58        int fd;
     59        void *ssl;
     60        char *txq;
     61        int tx_len;
     62        int r_inpa, w_inpa;
     63       
     64        struct xt_parser *xt;
     65        jabber_flags_t flags;
     66       
     67        char *username;         /* USERNAME@server */
     68        char *server;           /* username@SERVER -=> server/domain, not hostname */
     69       
     70        /* After changing one of these two (or the priority setting), call
     71           presence_send_update() to inform the server about the changes. */
     72        struct jabber_away_state *away_state;
     73        char *away_message;
     74       
     75        GHashTable *node_cache;
     76        GHashTable *buddies;
     77};
     78
     79struct jabber_away_state
     80{
     81        char code[5];
     82        char *full_name;
     83};
     84
     85typedef xt_status (*jabber_cache_event) ( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
     86
     87struct jabber_cache_entry
     88{
     89        struct xt_node *node;
     90        jabber_cache_event func;
     91};
     92
     93struct jabber_buddy
     94{
     95        char *bare_jid;
     96        char *full_jid;
     97        char *resource;
     98       
     99        int priority;
     100        struct jabber_away_state *away_state;
     101        char *away_message;
     102       
     103        time_t last_act;
     104        jabber_buddy_flags_t flags;
     105       
     106        struct jabber_buddy *next;
     107};
     108
     109/* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the
     110   first one should be used, but when storing a packet in the cache, a
     111   "special" kind of ID is assigned to make it easier later to figure out
     112   if we have to do call an event handler for the response packet. */
     113#define JABBER_PACKET_ID "BeeP"
     114#define JABBER_CACHED_ID "BeeC"
     115
     116/* RFC 392[01] stuff */
     117#define XMLNS_TLS          "urn:ietf:params:xml:ns:xmpp-tls"
     118#define XMLNS_SASL         "urn:ietf:params:xml:ns:xmpp-sasl"
     119#define XMLNS_BIND         "urn:ietf:params:xml:ns:xmpp-bind"
     120#define XMLNS_SESSION      "urn:ietf:params:xml:ns:xmpp-session"
     121#define XMLNS_STANZA_ERROR "urn:ietf:params:xml:ns:xmpp-stanzas"
     122#define XMLNS_STREAM_ERROR "urn:ietf:params:xml:ns:xmpp-streams"
     123#define XMLNS_ROSTER       "jabber:iq:roster"
     124
     125/* Some supported extensions/legacy stuff */
     126#define XMLNS_AUTH         "jabber:iq:auth"                     /* XEP-0078 */
     127#define XMLNS_VERSION      "jabber:iq:version"                  /* XEP-0092 */
     128#define XMLNS_TIME         "jabber:iq:time"                     /* XEP-0090 */
     129#define XMLNS_VCARD        "vcard-temp"                         /* XEP-0054 */
     130#define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"  /* 0085 */
     131#define XMLNS_DISCOVER     "http://jabber.org/protocol/disco#info"  /* 0030 */
     132
     133/* iq.c */
     134xt_status jabber_pkt_iq( struct xt_node *node, gpointer data );
     135int jabber_init_iq_auth( struct gaim_connection *gc );
     136xt_status jabber_pkt_bind_sess( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
     137int jabber_get_roster( struct gaim_connection *gc );
     138int jabber_get_vcard( struct gaim_connection *gc, char *bare_jid );
     139int jabber_add_to_roster( struct gaim_connection *gc, char *handle, char *name );
     140int jabber_remove_from_roster( struct gaim_connection *gc, char *handle );
     141
     142/* message.c */
     143xt_status jabber_pkt_message( struct xt_node *node, gpointer data );
     144
     145/* presence.c */
     146xt_status jabber_pkt_presence( struct xt_node *node, gpointer data );
     147int presence_send_update( struct gaim_connection *gc );
     148int presence_send_request( struct gaim_connection *gc, char *handle, char *request );
     149
     150/* jabber_util.c */
     151char *set_eval_priority( set_t *set, char *value );
     152char *set_eval_tls( set_t *set, char *value );
     153struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children );
     154struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type );
     155void jabber_cache_add( struct gaim_connection *gc, struct xt_node *node, jabber_cache_event func );
     156struct xt_node *jabber_cache_get( struct gaim_connection *gc, char *id );
     157void jabber_cache_entry_free( gpointer entry );
     158void jabber_cache_clean( struct gaim_connection *gc );
     159const struct jabber_away_state *jabber_away_state_by_code( char *code );
     160const struct jabber_away_state *jabber_away_state_by_name( char *name );
     161void jabber_buddy_ask( struct gaim_connection *gc, char *handle );
     162char *jabber_normalize( char *orig );
     163
     164typedef enum
     165{
     166        GET_BUDDY_CREAT = 1,    /* Try to create it, if necessary. */
     167        GET_BUDDY_EXACT = 2,    /* Get an exact message (only makes sense with bare JIDs). */
     168} get_buddy_flags_t;
     169
     170struct jabber_buddy *jabber_buddy_add( struct gaim_connection *gc, char *full_jid );
     171struct jabber_buddy *jabber_buddy_by_jid( struct gaim_connection *gc, char *jid, get_buddy_flags_t flags );
     172int jabber_buddy_remove( struct gaim_connection *gc, char *full_jid );
     173int jabber_buddy_remove_bare( struct gaim_connection *gc, char *bare_jid );
     174
     175extern const struct jabber_away_state jabber_away_state_list[];
     176
     177/* io.c */
     178int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node );
     179int jabber_write( struct gaim_connection *gc, char *buf, int len );
     180gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond );
     181gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond );
     182gboolean jabber_start_stream( struct gaim_connection *gc );
     183void jabber_end_stream( struct gaim_connection *gc );
     184
     185/* sasl.c */
     186xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data );
     187xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data );
     188xt_status sasl_pkt_result( struct xt_node *node, gpointer data );
     189gboolean sasl_supported( struct gaim_connection *gc );
     190
    35191#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/msn.c

    r8de63c3 r0fd8559  
    182182static GList *msn_away_states( struct gaim_connection *gc )
    183183{
    184         GList *l = NULL;
     184        static GList *l = NULL;
    185185        int i;
    186186       
    187         for( i = 0; msn_away_state_list[i].number > -1; i ++ )
    188                 l = g_list_append( l, (void*) msn_away_state_list[i].name );
    189        
    190         return( l );
     187        if( l == NULL )
     188                for( i = 0; msn_away_state_list[i].number > -1; i ++ )
     189                        l = g_list_append( l, (void*) msn_away_state_list[i].name );
     190       
     191        return l;
    191192}
    192193
  • protocols/nogaim.c

    r8de63c3 r0fd8559  
    280280        account_t *a;
    281281       
     282        /* Nested calls might happen sometimes, this is probably the best
     283           place to catch them. */
     284        if( gc->flags & OPT_LOGGING_OUT )
     285                return;
     286        else
     287                gc->flags |= OPT_LOGGING_OUT;
     288       
    282289        serv_got_crap( gc, "Signing off.." );
    283290       
    284291        b_event_remove( gc->keepalive );
    285         gc->flags |= OPT_LOGGING_OUT;
    286        
    287292        gc->keepalive = 0;
    288293        gc->acc->prpl->close( gc );
     
    10141019        }
    10151020       
    1016         g_list_free( ms );
    1017        
    10181021        return( 1 );
    10191022}
  • root_commands.c

    r8de63c3 r0fd8559  
    572572       
    573573        u->gc->acc->prpl->remove_buddy( u->gc, u->handle, NULL );
     574        nick_del( u->gc->acc, u->handle );
    574575        user_del( irc, cmd[1] );
    575         nick_del( u->gc->acc, s );
    576576       
    577577        irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] );
Note: See TracChangeset for help on using the changeset viewer.