Changes in / [e8a6211:6237ded]


Ignore:
Files:
28 added
8 deleted
13 edited

Legend:

Unmodified
Added
Removed
  • configure

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

    re8a6211 r6237ded  
    3030                                <description>
    3131                                        <para>
    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>).
     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]].
    3733                                        </para>
    3834                                </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>
    3946                        </bitlbee-command>
    4047
     
    510517        </bitlbee-setting>
    511518
    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 
    526519        <bitlbee-setting name="private" type="boolean" scope="global">
    527520                <default>true</default>
     
    563556        </bitlbee-setting>
    564557
    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 
    580558        <bitlbee-setting name="save_on_quit" type="boolean" scope="global">
    581559                <default>true</default>
     
    615593                        <para>
    616594                                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.
    631595                        </para>
    632596                </description>
  • lib/misc.c

    re8a6211 r6237ded  
    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
    4641
    4742void strip_linefeed(gchar *text)
     
    493488        return 0;
    494489}
    495 
    496 struct 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

    re8a6211 r6237ded  
    3030#include <time.h>
    3131
    32 struct ns_srv_reply
    33 {
    34         int prio;
    35         int weight;
    36         int port;
    37         char name[];
    38 };
    39 
    4032G_MODULE_EXPORT void strip_linefeed( gchar *text );
    4133G_MODULE_EXPORT char *add_cr( char *text );
     
    6254G_MODULE_EXPORT int bool2int( char *value );
    6355
    64 G_MODULE_EXPORT struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain );
    65 
    6656#endif
  • lib/ssl_client.h

    re8a6211 r6237ded  
    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. */
    56 G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer data );
    57 
    5854/* Obviously you need special read/write functions to read data. */
    5955G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len );
  • lib/ssl_gnutls.c

    re8a6211 r6237ded  
    4949
    5050static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
    51 static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
    52 static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
    5351
    5452
     
    6563        {
    6664                g_free( conn );
    67                 return NULL;
    68         }
    69        
    70         return conn;
    71 }
    72 
    73 void *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 
    96 static 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 
    103 static 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;
     65                return( NULL );
    11266        }
    11367       
     
    12377        gnutls_set_default_priority( conn->session );
    12478        gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
     79       
     80        return( conn );
     81}
     82
     83static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
     84
     85static 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        }
    125100       
    126101        sock_make_nonblocking( conn->fd );
  • lib/ssl_openssl.c

    re8a6211 r6237ded  
    5353
    5454static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
    55 static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
    56 static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
    5755
    5856
     
    6058{
    6159        struct scd *conn = g_new0( struct scd, 1 );
     60        SSL_METHOD *meth;
    6261       
    6362        conn->fd = proxy_connect( host, port, ssl_connected, conn );
    6463        conn->func = func;
    6564        conn->data = data;
    66         conn->inpa = -1;
    6765       
    6866        if( conn->fd < 0 )
    6967        {
    7068                g_free( conn );
    71                 return NULL;
    72         }
    73        
    74         return conn;
    75 }
    76 
    77 void *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 
    100 static 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 
    107 static 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;
     69                return( NULL );
     70        }
    11471       
    11572        if( !initialized )
     
    12279        conn->ssl_ctx = SSL_CTX_new( meth );
    12380        if( conn->ssl_ctx == NULL )
    124                 goto ssl_connected_failure;
     81        {
     82                conn->fd = -1;
     83                return( NULL );
     84        }
    12585       
    12686        conn->ssl = SSL_new( conn->ssl_ctx );
    12787        if( conn->ssl == NULL )
    128                 goto ssl_connected_failure;
     88        {
     89                conn->fd = -1;
     90                return( NULL );
     91        }
     92       
     93        return( conn );
     94}
     95
     96static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
     97
     98static 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 );
    129104       
    130105        /* We can do at least the handshake with non-blocking I/O */
     
    133108       
    134109        return ssl_handshake( data, source, cond );
    135 
    136 ssl_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 
    153110}       
    154111
     
    162119                conn->lasterr = SSL_get_error( conn->ssl, st );
    163120                if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE )
    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                 }
     121                        goto ssl_connected_failure;
    176122               
    177123                conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data );
     
    183129        conn->func( conn->data, conn, cond );
    184130        return FALSE;
     131       
     132ssl_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;
    185148}
    186149
  • protocols/jabber/Makefile

    re8a6211 r6237ded  
    1010
    1111# [SH] Program variables
    12 objects = io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o
     12objects = 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
    1313
    1414CFLAGS += -Wall
  • protocols/jabber/jabber.c

    re8a6211 r6237ded  
    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>
     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>
    2528#include <string.h>
    26 #include <unistd.h>
    27 #include <ctype.h>
     29#include <stdlib.h>
    2830#include <stdio.h>
    29 
     31#include <time.h>
     32#include <sys/stat.h>
     33#include "jabber.h"
     34#include "nogaim.h"
     35#include "bitlbee.h"
     36#include "proxy.h"
    3037#include "ssl_client.h"
    31 #include "xmltree.h"
    32 #include "bitlbee.h"
    33 #include "jabber.h"
    34 
    35 static void jabber_acc_init( account_t *acc )
     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 */
     69typedef 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
     95typedef void (*gjconn_state_h)(gjconn gjc, int state);
     96typedef void (*gjconn_packet_h)(gjconn gjc, jpacket p);
     97
     98static gjconn gjab_new(char *user, char *pass, void *priv);
     99static void gjab_delete(gjconn gjc);
     100static void gjab_state_handler(gjconn gjc, gjconn_state_h h);
     101static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h);
     102static void gjab_start(gjconn gjc);
     103static void gjab_stop(gjconn gjc);
     104/*
     105static int gjab_getfd(gjconn gjc);
     106static jid gjab_getjid(gjconn gjc);
     107static char *gjab_getsid(gjconn gjc);
     108*/
     109static char *gjab_getid(gjconn gjc);
     110static void gjab_send(gjconn gjc, xmlnode x);
     111static void gjab_send_raw(gjconn gjc, const char *str);
     112static void gjab_recv(gjconn gjc);
     113static void gjab_auth(gjconn gjc);
     114
     115/*
     116 * It is *this* to which we point the gaim_connection proto_data
     117 */
     118struct 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 */
     131struct 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
     160static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group);
     161static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from);
     162
     163static 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
     177static 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
     211static void gjab_delete(gjconn gjc)
     212{
     213        if (!gjc)
     214                return;
     215
     216        gjab_stop(gjc);
     217        pool_free(gjc->p);
     218}
     219
     220static void gjab_state_handler(gjconn gjc, gjconn_state_h h)
     221{
     222        if (!gjc)
     223                return;
     224
     225        gjc->on_state = h;
     226}
     227
     228static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h)
     229{
     230        if (!gjc)
     231                return;
     232
     233        gjc->on_packet = h;
     234}
     235
     236static 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/*
     256static int gjab_getfd(gjconn gjc)
     257{
     258        if (gjc)
     259                return gjc->fd;
     260        else
     261                return -1;
     262}
     263
     264static jid gjab_getjid(gjconn gjc)
     265{
     266        if (gjc)
     267                return (gjc->user);
     268        else
     269                return NULL;
     270}
     271
     272static char *gjab_getsid(gjconn gjc)
     273{
     274        if (gjc)
     275                return (gjc->sid);
     276        else
     277                return NULL;
     278}
     279*/
     280
     281static char *gjab_getid(gjconn gjc)
     282{
     283        g_snprintf(gjc->idbuf, 8, "%d", gjc->id++);
     284        return &gjc->idbuf[0];
     285}
     286
     287static 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
     300static 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
     322static 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
     333static 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
     356static 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
     396static 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
     420static 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
     446static 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
     473static 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
     483static 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
     491static 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
     539static 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
     561static 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 */
     607static 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 */
     626static 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
     642static 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 */
     658static 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 */
     674static 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
     719static 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
     744static 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           
     860static 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 */
     999struct 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 */
     1009static 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 */
     1023static 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 */
     1040static 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 */
     1050static 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 */
     1134static 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
     1208static 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
     1221static 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
     1261static 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
     1292static 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
     1323static 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 */
     1352static 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
     1377static 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
     1475static 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
     1498static void jabber_acc_init(account_t *acc)
    361499{
    371500        set_t *s;
     
    401503        s->flags |= ACC_SET_OFFLINE_ONLY;
    411504       
    42         s = set_add( &acc->set, "priority", "0", set_eval_priority, acc );
    43        
    441505        s = set_add( &acc->set, "resource", "BitlBee", NULL, acc );
    451506        s->flags |= ACC_SET_OFFLINE_ONLY;
    461507       
    47         s = set_add( &acc->set, "resource_select", "priority", NULL, acc );
    48        
    491508        s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
    501509        s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
     
    521511        s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc );
    531512        s->flags |= ACC_SET_OFFLINE_ONLY;
    54        
    55         s = set_add( &acc->set, "tls", "try", set_eval_tls, acc );
    56         s->flags |= ACC_SET_OFFLINE_ONLY;
    57 }
    58 
    59 static void jabber_login( account_t *acc )
    60 {
    61         struct gaim_connection *gc = new_gaim_conn( acc );
    62         struct jabber_data *jd = g_new0( struct jabber_data, 1 );
    63         struct ns_srv_reply *srv = NULL;
    64         char *connect_to, *s;
    65        
    66         jd->gc = gc;
    67         gc->proto_data = jd;
    68        
    69         jd->username = g_strdup( acc->user );
    70         jd->server = strchr( jd->username, '@' );
    71        
    72         if( jd->server == NULL )
     1513}
     1514
     1515static 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 )
    731552        {
    74                 hide_login_progress( gc, "Incomplete account name (format it like <username@jabberserver.name>)" );
     1553                hide_login_progress( gc, "Invalid account name" );
    751554                signoff( gc );
    761555                return;
    771556        }
    781557       
    79         /* So don't think of free()ing jd->server.. :-) */
    80         *jd->server = 0;
    81         jd->server ++;
    82        
    83         if( ( s = strchr( jd->server, '/' ) ) )
     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
     1580static gboolean jabber_destroy_hash(gpointer key, gpointer val, gpointer data) {
     1581        g_free(key);
     1582        g_free(val);
     1583        return TRUE;
     1584}
     1585
     1586static 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
     1602static 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;
     1613                }
     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;
     1621                }
     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;
     1628                }
     1629        }
     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
     1641static 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)
     1648                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);
     1669        return 1;
     1670}
     1671
     1672/*
     1673 * Add/update buddy's roster entry on server
     1674 */
     1675static 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 */
     1734static 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
     1741static 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
     1786static 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
     1808static 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
     1836static 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
     1869static 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
     1881static 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
     1930static 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
     2006static 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 */
     2043typedef 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 */
     2052static 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 */
     2063static 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 */
     2084static 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 */
     2099static 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 */
     2129static 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 */
     2191static 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 */
     2220static 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)
    842249        {
    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 );
    115                 }
    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;
    139                 }
    140                
    141                 s = strchr( s, ':' );
    142                 if( s )
    143                 {
    144                         *s = 0;
    145                         s ++;
    146                 }
    147         }
    148        
    149         jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free );
    150         jd->buddies = g_hash_table_new( g_str_hash, g_str_equal );
    151        
    152         /* Figure out the hostname to connect to. */
    153         if( acc->server && *acc->server )
    154                 connect_to = acc->server;
    155         else if( ( srv = srv_lookup( "xmpp-client", "tcp", jd->server ) ) ||
    156                  ( srv = srv_lookup( "jabber-client", "tcp", jd->server ) ) )
    157                 connect_to = srv->name;
    158         else
    159                 connect_to = jd->server;
    160        
    161         set_login_progress( gc, 0, "Connecting" );
    162        
    163         /* For non-SSL connections we can try to use the port # from the SRV
    164            reply, but let's not do that when using SSL, SSL usually runs on
    165            non-standard ports... */
    166         if( set_getbool( &acc->set, "ssl" ) )
    167         {
    168                 jd->ssl = ssl_connect( connect_to, set_getint( &acc->set, "port" ), jabber_connected_ssl, gc );
    169                 jd->fd = jd->ssl ? ssl_getfd( jd->ssl ) : -1;
    170         }
    171         else
    172         {
    173                 jd->fd = proxy_connect( connect_to, srv ? srv->port : set_getint( &acc->set, "port" ), jabber_connected_plain, gc );
    174         }
    175         g_free( srv );
    176        
    177         if( jd->fd == -1 )
    178         {
    179                 hide_login_progress( gc, "Could not connect to server" );
    180                 signoff( gc );
    181         }
    182 }
    183 
    184 static void jabber_close( struct gaim_connection *gc )
    185 {
    186         struct jabber_data *jd = gc->proto_data;
    187        
    188         jabber_end_stream( gc );
    189        
    190         if( jd->r_inpa >= 0 )
    191                 b_event_remove( jd->r_inpa );
    192         if( jd->w_inpa >= 0 )
    193                 b_event_remove( jd->w_inpa );
    194        
    195         if( jd->ssl )
    196                 ssl_disconnect( jd->ssl );
    197         if( jd->fd >= 0 )
    198                 closesocket( jd->fd );
    199        
    200         if( jd->tx_len )
    201                 g_free( jd->txq );
    202        
    203         g_hash_table_destroy( jd->node_cache );
    204        
    205         xt_free( jd->xt );
    206        
    207         g_free( jd->away_message );
    208         g_free( jd->username );
    209         g_free( jd );
    210 }
    211 
    212 static int jabber_send_im( struct gaim_connection *gc, char *who, char *message, int len, int away )
    213 {
    214         struct jabber_data *jd = gc->proto_data;
    215         struct jabber_buddy *bud;
    216         struct xt_node *node;
    217         int st;
    218        
    219         bud = jabber_buddy_by_jid( gc, who );
    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( ( jd->flags & JFLAG_WANT_TYPING ) && bud &&
    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. */
    233                 act = xt_new_node( "active", NULL, NULL );
    234                 xt_add_attr( act, "xmlns", "http://jabber.org/protocol/chatstates" );
    235                 xt_add_child( node, act );
    236                
    237                 /* Just make sure we do this only once. */
    238                 bud->flags |= JBFLAG_PROBED_XEP85;
    239         }
    240        
    241         st = jabber_write_packet( gc, node );
    242         xt_free_node( node );
    243        
    244         return st;
    245 }
    246 
    247 static GList *jabber_away_states( struct gaim_connection *gc )
    248 {
    249         static GList *l = NULL;
    250         int i;
    251        
    252         if( l == NULL )
    253                 for( i = 0; jabber_away_state_list[i].full_name; i ++ )
    254                         l = g_list_append( l, (void*) jabber_away_state_list[i].full_name );
    255        
    256         return l;
    257 }
    258 
    259 static void jabber_get_info( struct gaim_connection *gc, char *who )
    260 {
    261         struct jabber_data *jd = gc->proto_data;
    262         struct jabber_buddy *bud;
    263        
    264         if( strchr( who, '/' ) )
    265                 bud = jabber_buddy_by_jid( gc, who );
    266         else
    267                 bud = g_hash_table_lookup( jd->buddies, who );
    268        
    269         while( bud )
    270         {
    271                 serv_got_crap( gc, "Buddy %s/%s (%d) information:\nAway state: %s\nAway message: %s",
    272                                    bud->handle, bud->resource, bud->priority,
    273                                    bud->away_state ? bud->away_state->full_name : "(none)",
    274                                    bud->away_message ? : "(none)" );
    275                 bud = bud->next;
    276         }
    277        
    278         jabber_get_vcard( gc, bud ? bud->handle : who );
    279 }
    280 
    281 static void jabber_set_away( struct gaim_connection *gc, char *state_txt, char *message )
    282 {
    283         struct jabber_data *jd = gc->proto_data;
    284         struct jabber_away_state *state;
    285        
    286         /* Save all this info. We need it, for example, when changing the priority setting. */
    287         state = (void *) jabber_away_state_by_name( state_txt );
    288         jd->away_state = state ? state : (void *) jabber_away_state_list; /* Fall back to "Away" if necessary. */
    289         g_free( jd->away_message );
    290         jd->away_message = ( message && *message ) ? g_strdup( message ) : NULL;
    291        
    292         presence_send_update( gc );
    293 }
    294 
    295 static void jabber_add_buddy( struct gaim_connection *gc, char *who )
    296 {
    297         if( jabber_add_to_roster( gc, who, NULL ) )
    298                 presence_send_request( gc, who, "subscribe" );
    299 }
    300 
    301 static void jabber_remove_buddy( struct gaim_connection *gc, char *who, char *group )
    302 {
    303         /* We should always do this part. Clean up our administration a little bit. */
    304         jabber_buddy_remove_bare( gc, who );
    305        
    306         if( jabber_remove_from_roster( gc, who ) )
    307                 presence_send_request( gc, who, "unsubscribe" );
    308 }
    309 
    310 static void jabber_keepalive( struct gaim_connection *gc )
    311 {
    312         /* Just any whitespace character is enough as a keepalive for XMPP sessions. */
    313         jabber_write( gc, "\n", 1 );
    314        
    315         /* This runs the garbage collection every minute, which means every packet
    316            is in the cache for about a minute (which should be enough AFAIK). */
    317         jabber_cache_clean( gc );
    318 }
    319 
    320 static int jabber_send_typing( struct gaim_connection *gc, char *who, int typing )
    321 {
    322         struct jabber_data *jd = gc->proto_data;
    323         struct jabber_buddy *bud;
    324        
    325         /* Enable typing notification related code from now. */
    326         jd->flags |= JFLAG_WANT_TYPING;
    327        
    328         if( ( bud = jabber_buddy_by_jid( gc, who ) ) == NULL )
    329         {
    330                 /* Sending typing notifications to unknown buddies is
    331                    unsupported for now. Shouldn't be a problem, I think. */
    332                 return 0;
    333         }
    334        
    335         if( bud->flags & JBFLAG_DOES_XEP85 )
    336         {
    337                 /* We're only allowed to send this stuff if we know the other
    338                    side supports it. */
    339                
    340                 struct xt_node *node;
    341                 char *type;
    342                 int st;
    343                
    344                 if( typing == 0 )
    345                         type = "active";
    346                 else if( typing == 2 )
    347                         type = "paused";
    348                 else /* if( typing == 1 ) */
    349                         type = "composing";
    350                
    351                 node = xt_new_node( type, NULL, NULL );
    352                 xt_add_attr( node, "xmlns", "http://jabber.org/protocol/chatstates" );
    353                 node = jabber_make_packet( "message", "chat", bud->full_jid, node );
    354                
    355                 st = jabber_write_packet( gc, node );
    356                 xt_free_node( node );
    357                
    358                 return st;
    359         }
    360        
    361         return 1;
     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);
    3622368}
    3632369
    3642370void jabber_init()
    3652371{
    366         struct prpl *ret = g_new0( struct prpl, 1 );
    367        
     2372        struct prpl *ret = g_new0(struct prpl, 1);
     2373
    3682374        ret->name = "jabber";
     2375        ret->away_states = jabber_away_states;
     2376        ret->acc_init = jabber_acc_init;
    3692377        ret->login = jabber_login;
    370         ret->acc_init = jabber_acc_init;
    3712378        ret->close = jabber_close;
    3722379        ret->send_im = jabber_send_im;
    373         ret->away_states = jabber_away_states;
    374 //      ret->get_status_string = jabber_get_status_string;
     2380        ret->set_info = jabber_set_info;
     2381        ret->get_info = jabber_get_info;
    3752382        ret->set_away = jabber_set_away;
    376 //      ret->set_info = jabber_set_info;
    377         ret->get_info = jabber_get_info;
     2383        ret->get_away = jabber_get_away_msg;
    3782384        ret->add_buddy = jabber_add_buddy;
    3792385        ret->remove_buddy = jabber_remove_buddy;
    380 //      ret->chat_send = jabber_chat_send;
    381 //      ret->chat_invite = jabber_chat_invite;
    382 //      ret->chat_leave = jabber_chat_leave;
    383 //      ret->chat_open = jabber_chat_open;
    3842386        ret->keepalive = jabber_keepalive;
    385         ret->send_typing = jabber_send_typing;
     2387        ret->alias_buddy = jabber_roster_update;
     2388        ret->group_buddy = jabber_group_change;
    3862389        ret->handle_cmp = g_strcasecmp;
    3872390
    388         register_protocol( ret );
    389 }
     2391        register_protocol (ret);
     2392}
  • protocols/jabber/jabber.h

    re8a6211 r6237ded  
    1 /***************************************************************************\
    2 *                                                                           *
    3 *  BitlBee - An IRC to IM gateway                                           *
    4 *  Jabber module - Main file                                                *
    5 *                                                                           *
    6 *  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
    7 *                                                                           *
    8 *  This program is free software; you can redistribute it and/or modify     *
    9 *  it under the terms of the GNU General Public License as published by     *
    10 *  the Free Software Foundation; either version 2 of the License, or        *
    11 *  (at your option) any later version.                                      *
    12 *                                                                           *
    13 *  This program is distributed in the hope that it will be useful,          *
    14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
    15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
    16 *  GNU General Public License for more details.                             *
    17 *                                                                           *
    18 *  You should have received a copy of the GNU General Public License along  *
    19 *  with this program; if not, write to the Free Software Foundation, Inc.,  *
    20 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
    21 *                                                                           *
    22 \***************************************************************************/
    23 
    24 #ifndef _JABBER_H
    25 #define _JABBER_H
    26 
    27 #include <glib.h>
    28 
    29 #include "xmltree.h"
    30 #include "bitlbee.h"
    31 
    32 typedef enum
     1/*
     2 *  This program is free software; you can redistribute it and/or modify
     3 *  it under the terms of the GNU General Public License as published by
     4 *  the Free Software Foundation; either version 2 of the License, or
     5 *  (at your option) any later version.
     6 *
     7 *  This program is distributed in the hope that it will be useful,
     8 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     9 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 *  GNU General Public License for more details.
     11 *
     12 *  You should have received a copy of the GNU General Public License
     13 *  along with this program; if not, write to the Free Software
     14 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     15 *
     16 *  Jabber
     17 *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
     18 */
     19
     20#include <string.h>
     21#include <stdlib.h>
     22#include <sys/types.h>
     23#include <stdio.h>
     24#include <setjmp.h>
     25#include <sys/stat.h>
     26#include <fcntl.h>
     27#include <errno.h>
     28#include <signal.h>
     29#include <stdarg.h>
     30#include <time.h>
     31#include <ctype.h>
     32#ifdef _WIN32
     33#undef DATADIR
     34#include "sock.h"
     35#endif
     36
     37#include "lib.h"
     38
     39
     40#ifndef INCL_JABBER_H
     41#define INCL_JABBER_H
     42
     43#ifdef __cplusplus
     44extern "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
     56typedef 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 
     66jid     jid_new(pool p, char *idstr);          /* Creates a jabber id from the idstr */
     67void    jid_set(jid id, char *str, int item);  /* Individually sets jid components */
     68char*   jid_full(jid id);                      /* Builds a string type=user/resource@server from the jid data */
     69int     jid_cmp(jid a, jid b);                 /* Compares two jid's, returns 0 for perfect match */
     70int     jid_cmpx(jid a, jid b, int parts);     /* Compares just the parts specified as JID_|JID_ */
     71jid     jid_append(jid a, jid b);              /* Appending b to a (list), no dups */
     72xmlnode jid_xres(jid id);                      /* Returns xmlnode representation of the resource?query=string */
     73xmlnode jid_nodescan(jid id, xmlnode x);       /* Scans the children of the node for a matching jid attribute */
     74jid     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
     106typedef struct jpacket_struct
    33107{
    34         JFLAG_STREAM_STARTED = 1,       /* Set when we detected the beginning of the stream
    35                                            and want to do auth. */
    36         JFLAG_AUTHENTICATED = 2,        /* Set when we're successfully authenticatd. */
    37         JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after
    38                                            SASL or TLS). */
    39         JFLAG_WAIT_SESSION = 8,         /* Set if we sent a <session> tag and need a reply
    40                                            before we continue. */
    41         JFLAG_WAIT_BIND = 16,           /* ... for <bind> tag. */
    42         JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this
    43                                            activates all XEP-85 related code. */
    44 } jabber_flags_t;
    45 
    46 typedef enum
     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 
     120jpacket jpacket_new(xmlnode x);     /* Creates a jabber packet from the xmlnode */
     121int     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/* --------------------------------------------------------- */
     129typedef 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
     139ppdb    ppdb_insert(ppdb db, jid id, xmlnode x); /* Inserts presence into the proxy */
     140xmlnode ppdb_primary(ppdb db, jid id);           /* Fetches the matching primary presence for the id */
     141void    ppdb_free(ppdb db);                      /* Frees the db and all entries */
     142xmlnode 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/* --------------------------------------------------------- */
     151typedef struct jlimit_struct
    47152{
    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_flag_t;
    53 
    54 struct jabber_data
     153    char *key;
     154    int start;
     155    int points;
     156    int maxt, maxp;
     157    pool p;
     158} *jlimit, _jlimit;
     159 
     160jlimit jlimit_new(int maxt, int maxp);
     161void jlimit_free(jlimit r);
     162int jlimit_check(jlimit r, char *key, int points);
     163
     164
     165/* --------------------------------------------------------- */
     166/*                                                           */
     167/* Error structures & constants                              */
     168/*                                                           */
     169/* --------------------------------------------------------- */
     170typedef struct terror_struct
    55171{
    56         struct gaim_connection *gc;
    57        
    58         int fd;
    59         void *ssl;
    60         char *txq;
    61         int tx_len;
    62         int r_inpa, w_inpa;
    63        
    64         struct xt_parser *xt;
    65         jabber_flags_t flags;
    66        
    67         char *username;         /* USERNAME@server */
    68         char *server;           /* username@SERVER -=> server/domain, not hostname */
    69        
    70         /* After changing one of these two (or the priority setting), call
    71            presence_send_update() to inform the server about the changes. */
    72         struct jabber_away_state *away_state;
    73         char *away_message;
    74        
    75         GHashTable *node_cache;
    76         GHashTable *buddies;
    77 };
    78 
    79 struct jabber_away_state
     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/* --------------------------------------------------------- */
     239xmlnode jutil_presnew(int type, char *to, char *status); /* Create a skeleton presence packet */
     240xmlnode jutil_iqnew(int type, char *ns);                 /* Create a skeleton iq packet */
     241xmlnode jutil_msgnew(char *type, char *to, char *subj, char *body);
     242                                                         /* Create a skeleton message packet */
     243xmlnode jutil_header(char* xmlns, char* server);         /* Create a skeleton stream packet */
     244int     jutil_priority(xmlnode x);                       /* Determine priority of this packet */
     245void    jutil_tofrom(xmlnode x);                         /* Swaps to/from fields on a packet */
     246xmlnode jutil_iqresult(xmlnode x);                       /* Generate a skeleton iq/result, given a iq/query */
     247char*   jutil_timestamp(void);                           /* Get stringified timestamp */
     248void    jutil_error(xmlnode x, terror E);                /* Append an <error> node to x */
     249void    jutil_delay(xmlnode msg, char *reason);          /* Append a delay packet to msg */
     250char*   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
     263typedef struct jconn_struct
    80264{
    81         char code[5];
    82         char *full_name;
    83 };
    84 
    85 typedef xt_status (*jabber_cache_event) ( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
    86 
    87 struct jabber_cache_entry
    88 {
    89         struct xt_node *node;
    90         jabber_cache_event func;
    91 };
    92 
    93 struct jabber_buddy
    94 {
    95         char *handle;
    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_flag_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 /* iq.c */
    117 xt_status jabber_pkt_iq( struct xt_node *node, gpointer data );
    118 int jabber_init_iq_auth( struct gaim_connection *gc );
    119 xt_status jabber_pkt_bind_sess( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
    120 int jabber_get_roster( struct gaim_connection *gc );
    121 int jabber_get_vcard( struct gaim_connection *gc, char *bare_jid );
    122 int jabber_add_to_roster( struct gaim_connection *gc, char *handle, char *name );
    123 int jabber_remove_from_roster( struct gaim_connection *gc, char *handle );
    124 
    125 /* message.c */
    126 xt_status jabber_pkt_message( struct xt_node *node, gpointer data );
    127 
    128 /* presence.c */
    129 xt_status jabber_pkt_presence( struct xt_node *node, gpointer data );
    130 int presence_send_update( struct gaim_connection *gc );
    131 int presence_send_request( struct gaim_connection *gc, char *handle, char *request );
    132 
    133 /* jabber_util.c */
    134 char *set_eval_priority( set_t *set, char *value );
    135 char *set_eval_tls( set_t *set, char *value );
    136 struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children );
    137 struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type );
    138 void jabber_cache_add( struct gaim_connection *gc, struct xt_node *node, jabber_cache_event func );
    139 struct xt_node *jabber_cache_get( struct gaim_connection *gc, char *id );
    140 void jabber_cache_entry_free( gpointer entry );
    141 void jabber_cache_clean( struct gaim_connection *gc );
    142 const struct jabber_away_state *jabber_away_state_by_code( char *code );
    143 const struct jabber_away_state *jabber_away_state_by_name( char *name );
    144 void jabber_buddy_ask( struct gaim_connection *gc, char *handle );
    145 struct jabber_buddy *jabber_buddy_add( struct gaim_connection *gc, char *full_jid );
    146 struct jabber_buddy *jabber_buddy_by_jid( struct gaim_connection *gc, char *jid );
    147 int jabber_buddy_remove( struct gaim_connection *gc, char *full_jid );
    148 int jabber_buddy_remove_bare( struct gaim_connection *gc, char *bare_jid );
    149 
    150 extern const struct jabber_away_state jabber_away_state_list[];
    151 
    152 /* io.c */
    153 int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node );
    154 int jabber_write( struct gaim_connection *gc, char *buf, int len );
    155 gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond );
    156 gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond );
    157 gboolean jabber_start_stream( struct gaim_connection *gc );
    158 void jabber_end_stream( struct gaim_connection *gc );
    159 
    160 /* sasl.c */
    161 xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data );
    162 xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data );
    163 xt_status sasl_pkt_result( struct xt_node *node, gpointer data );
    164 gboolean sasl_supported( struct gaim_connection *gc );
    165 
     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
     285typedef void (*jconn_state_h)(jconn j, int state);
     286typedef void (*jconn_packet_h)(jconn j, jpacket p);
     287
     288
     289jconn jab_new(char *user, char *pass);
     290void jab_delete(jconn j);
     291void jab_state_handler(jconn j, jconn_state_h h);
     292void jab_packet_handler(jconn j, jconn_packet_h h);
     293void jab_start(jconn j);
     294void jab_stop(jconn j);
     295
     296int jab_getfd(jconn j);
     297jid jab_getjid(jconn j);
     298char *jab_getsid(jconn j);
     299char *jab_getid(jconn j);
     300
     301void jab_send(jconn j, xmlnode x);
     302void jab_send_raw(jconn j, const char *str);
     303void jab_recv(jconn j);
     304void jab_poll(jconn j, int timeout);
     305
     306char *jab_auth(jconn j);
     307char *jab_reg(jconn j);
     308
     309
     310
     311#ifdef __cplusplus
     312}
    166313#endif
     314
     315#endif  /* INCL_JABBER_H */
  • protocols/msn/msn.c

    re8a6211 r6237ded  
    182182static GList *msn_away_states( struct gaim_connection *gc )
    183183{
    184         static GList *l = NULL;
     184        GList *l = NULL;
    185185        int i;
    186186       
    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;
     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 );
    192191}
    193192
  • protocols/nogaim.c

    re8a6211 r6237ded  
    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        
    289282        serv_got_crap( gc, "Signing off.." );
    290283       
    291284        b_event_remove( gc->keepalive );
     285        gc->flags |= OPT_LOGGING_OUT;
     286       
    292287        gc->keepalive = 0;
    293288        gc->acc->prpl->close( gc );
     
    10191014        }
    10201015       
     1016        g_list_free( ms );
     1017       
    10211018        return( 1 );
    10221019}
  • root_commands.c

    re8a6211 r6237ded  
    572572       
    573573        u->gc->acc->prpl->remove_buddy( u->gc, u->handle, NULL );
    574         nick_del( u->gc->acc, u->handle );
    575574        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.