source: protocols/jabber/jabber.c @ 34afea7

Last change on this file since 34afea7 was 34afea7, checked in by dequis <dx@…>, at 2015-01-31T23:58:57Z

Use glib's GChecksum for md5/sha1

This changes behavior slightly:

  • md5_init()/sha1_init() allocate a GChecksum
  • md5_finish()/sha1_finish() close and free() it
  • md5_digest_keep() was added (no sha1 equivalent needed)

And yes, glib has this concept of "closing" the GChecksum, which means
it can't be used anymore after g_checksum_get_digest().

jabber_cache_add() actually seems to need to do that to generate some
random-ish values, so i kept that working by adding a md5_digest_keep()
function that copies the GChecksum before it gets closed

GChecksum was introduced in glib 2.16, so the configure script version
was bumped. We were already depending on glib 2.16 accidentally
(some post-3.2.2 code uses GHashTableIter)

  • Property mode set to 100644
File size: 19.1 KB
RevLine 
[f06894d]1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - Main file                                                *
5*                                                                           *
[0e788f5]6*  Copyright 2006-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
[f06894d]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>
25#include <string.h>
26#include <unistd.h>
27#include <ctype.h>
28#include <stdio.h>
29
[21167d2]30#include "ssl_client.h"
[f06894d]31#include "xmltree.h"
32#include "bitlbee.h"
33#include "jabber.h"
[e14b47b8]34#include "oauth.h"
[608f8cf]35#include "md5.h"
[f06894d]36
[b5c8a34]37GSList *jabber_connections;
38
[7f69740]39/* First enty is the default */
40static const int jabber_port_list[] = {
41        5222,
42        5223,
43        5220,
44        5221,
45        5224,
46        5225,
47        5226,
48        5227,
49        5228,
50        5229,
51        80,
52        443,
53        0
54};
55
[0da65d5]56static void jabber_init( account_t *acc )
[f06894d]57{
58        set_t *s;
[7f69740]59        char str[16];
[f06894d]60       
[76c85b4c]61        s = set_add( &acc->set, "activity_timeout", "600", set_eval_int, acc );
62       
[ce199b7]63        s = set_add( &acc->set, "oauth", "false", set_eval_oauth, acc );
[57b4525]64
[fcb2c2e]65        s = set_add( &acc->set, "display_name", NULL, NULL, acc );
66       
[7f69740]67        g_snprintf( str, sizeof( str ), "%d", jabber_port_list[0] );
68        s = set_add( &acc->set, "port", str, set_eval_int, acc );
[f06894d]69        s->flags |= ACC_SET_OFFLINE_ONLY;
70       
[ebe7b36]71        s = set_add( &acc->set, "priority", "0", set_eval_priority, acc );
[1c3008a]72
73        s = set_add( &acc->set, "proxy", "<local>;<auto>", NULL, acc );
[f06894d]74       
[ebe7b36]75        s = set_add( &acc->set, "resource", "BitlBee", NULL, acc );
76        s->flags |= ACC_SET_OFFLINE_ONLY;
[f06894d]77       
[76c85b4c]78        s = set_add( &acc->set, "resource_select", "activity", NULL, acc );
[a21a8ac]79       
[ce199b7]80        s = set_add( &acc->set, "sasl", "true", set_eval_bool, acc );
81        s->flags |= ACC_SET_OFFLINE_ONLY | SET_HIDDEN_DEFAULT;
82       
[f06894d]83        s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
[bb5ce568]84        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
[f06894d]85       
86        s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc );
87        s->flags |= ACC_SET_OFFLINE_ONLY;
88       
[955aad8]89        s = set_add( &acc->set, "tls", "true", set_eval_tls, acc );
[f06894d]90        s->flags |= ACC_SET_OFFLINE_ONLY;
[a6df0b5]91       
[486ddb5]92        s = set_add( &acc->set, "tls_verify", "true", set_eval_bool, acc );
93        s->flags |= ACC_SET_OFFLINE_ONLY;
[34ded90]94
[4eef271]95        s = set_add( &acc->set, "user_agent", "BitlBee", NULL, acc );
96       
[a6df0b5]97        s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc );
98        s->flags |= ACC_SET_OFFLINE_ONLY;
[840bba8]99       
[06eef80]100        acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE |
101                      ACC_FLAG_HANDLE_DOMAINS;
[f06894d]102}
103
[608f8cf]104static void jabber_generate_id_hash( struct jabber_data *jd );
105
[f06894d]106static void jabber_login( account_t *acc )
107{
[84b045d]108        struct im_connection *ic = imcb_new( acc );
[21167d2]109        struct jabber_data *jd = g_new0( struct jabber_data, 1 );
[4a5d885]110        char *s;
[21167d2]111       
[b5c8a34]112        /* For now this is needed in the _connected() handlers if using
113           GLib event handling, to make sure we're not handling events
114           on dead connections. */
115        jabber_connections = g_slist_prepend( jabber_connections, ic );
116       
[0da65d5]117        jd->ic = ic;
118        ic->proto_data = jd;
[21167d2]119       
[68286eb]120        jabber_set_me( ic, acc->user );
[21167d2]121       
[de03374]122        jd->fd = jd->r_inpa = jd->w_inpa = -1;
123       
[21167d2]124        if( jd->server == NULL )
125        {
[84b045d]126                imcb_error( ic, "Incomplete account name (format it like <username@jabberserver.name>)" );
[c2fb3809]127                imc_logout( ic, FALSE );
[21167d2]128                return;
129        }
130       
[3b3cd693]131        if( ( s = strchr( jd->server, '/' ) ) )
132        {
133                *s = 0;
134                set_setstr( &acc->set, "resource", s + 1 );
135               
136                /* Also remove the /resource from the original variable so we
137                   won't have to do this again every time. */
138                s = strchr( acc->user, '/' );
139                *s = 0;
140        }
141       
[038d17f]142        jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free );
[6a1128d]143        jd->buddies = g_hash_table_new( g_str_hash, g_str_equal );
[fe7a554]144       
[4a5d885]145        if( set_getbool( &acc->set, "oauth" ) )
146        {
[e14b47b8]147                GSList *p_in = NULL;
148                const char *tok;
149               
[f138bd2]150                jd->fd = jd->r_inpa = jd->w_inpa = -1;
151               
[c27a923]152                if( strstr( jd->server, ".facebook.com" ) )
[18c6d36]153                        jd->oauth2_service = &oauth2_service_facebook;
154                else
155                        jd->oauth2_service = &oauth2_service_google;
156               
[e14b47b8]157                oauth_params_parse( &p_in, ic->acc->pass );
158               
[64b6635]159                /* First see if we have a refresh token, in which case any
160                   access token we *might* have has probably expired already
161                   anyway. */
[e14b47b8]162                if( ( tok = oauth_params_get( &p_in, "refresh_token" ) ) )
[64b6635]163                {
[e14b47b8]164                        sasl_oauth2_refresh( ic, tok );
[64b6635]165                }
166                /* If we don't have a refresh token, let's hope the access
167                   token is still usable. */
[e14b47b8]168                else if( ( tok = oauth_params_get( &p_in, "access_token" ) ) )
[64b6635]169                {
[e14b47b8]170                        jd->oauth2_access_token = g_strdup( tok );
[64b6635]171                        jabber_connect( ic );
172                }
173                /* If we don't have any, start the OAuth process now. Don't
174                   even open an XMPP connection yet. */
175                else
[f988ad3]176                {
[4a5d885]177                        sasl_oauth2_init( ic );
[f988ad3]178                        ic->flags |= OPT_SLOW_LOGIN;
179                }
[e14b47b8]180               
181                oauth_params_free( &p_in );
[4a5d885]182        }
183        else
184                jabber_connect( ic );
185}
186
187/* Separate this from jabber_login() so we can do OAuth first if necessary.
188   Putting this in io.c would probably be more correct. */
189void jabber_connect( struct im_connection *ic )
190{
191        account_t *acc = ic->acc;
192        struct jabber_data *jd = ic->proto_data;
193        int i;
194        char *connect_to;
195        struct ns_srv_reply **srvl = NULL, *srv = NULL;
196       
[36e9f62]197        /* Figure out the hostname to connect to. */
[3b3cd693]198        if( acc->server && *acc->server )
[36e9f62]199                connect_to = acc->server;
[ffdf2e7]200        else if( ( srvl = srv_lookup( "xmpp-client", "tcp", jd->server ) ) ||
201                 ( srvl = srv_lookup( "jabber-client", "tcp", jd->server ) ) )
202        {
203                /* Find the lowest-priority one. These usually come
204                   back in random/shuffled order. Not looking at
205                   weights etc for now. */
206                srv = *srvl;
207                for( i = 1; srvl[i]; i ++ )
208                        if( srvl[i]->prio < srv->prio )
209                                srv = srvl[i];
210               
[36e9f62]211                connect_to = srv->name;
[ffdf2e7]212        }
[36e9f62]213        else
214                connect_to = jd->server;
215       
[84b045d]216        imcb_log( ic, "Connecting" );
[35f6677]217       
[7f69740]218        for( i = 0; jabber_port_list[i] > 0; i ++ )
219                if( set_getint( &acc->set, "port" ) == jabber_port_list[i] )
220                        break;
221
222        if( jabber_port_list[i] == 0 )
[0f4c1bb5]223        {
[7f69740]224                imcb_log( ic, "Illegal port number" );
[c2fb3809]225                imc_logout( ic, FALSE );
[0f4c1bb5]226                return;
227        }
228       
[36e9f62]229        /* For non-SSL connections we can try to use the port # from the SRV
230           reply, but let's not do that when using SSL, SSL usually runs on
231           non-standard ports... */
[21167d2]232        if( set_getbool( &acc->set, "ssl" ) )
233        {
[8f976e6]234                jd->ssl = ssl_connect( connect_to, set_getint( &acc->set, "port" ), set_getbool( &acc->set, "tls_verify" ), jabber_connected_ssl, ic );
[3b3cd693]235                jd->fd = jd->ssl ? ssl_getfd( jd->ssl ) : -1;
[21167d2]236        }
237        else
238        {
[0da65d5]239                jd->fd = proxy_connect( connect_to, srv ? srv->port : set_getint( &acc->set, "port" ), jabber_connected_plain, ic );
[21167d2]240        }
[ffdf2e7]241        srv_free( srvl );
[35f6677]242       
243        if( jd->fd == -1 )
244        {
[84b045d]245                imcb_error( ic, "Could not connect to server" );
[c2fb3809]246                imc_logout( ic, TRUE );
[fb4ebcc5]247               
248                return;
[35f6677]249        }
[a6df0b5]250       
251        if( set_getbool( &acc->set, "xmlconsole" ) )
252        {
253                jd->flags |= JFLAG_XMLCONSOLE;
254                /* Shouldn't really do this at this stage already, maybe. But
255                   I think this shouldn't break anything. */
256                imcb_add_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
257        }
[608f8cf]258       
259        jabber_generate_id_hash( jd );
260}
261
[89d736a]262/* This generates an unfinished md5_state_t variable. Every time we generate
263   an ID, we finish the state by adding a sequence number and take the hash. */
[608f8cf]264static void jabber_generate_id_hash( struct jabber_data *jd )
265{
[89d736a]266        md5_byte_t binbuf[4];
[608f8cf]267        char *s;
268       
[89d736a]269        md5_init( &jd->cached_id_prefix );
270        md5_append( &jd->cached_id_prefix, (unsigned char *) jd->username, strlen( jd->username ) );
271        md5_append( &jd->cached_id_prefix, (unsigned char *) jd->server, strlen( jd->server ) );
[608f8cf]272        s = set_getstr( &jd->ic->acc->set, "resource" );
[89d736a]273        md5_append( &jd->cached_id_prefix, (unsigned char *) s, strlen( s ) );
274        random_bytes( binbuf, 4 );
275        md5_append( &jd->cached_id_prefix, binbuf, 4 );
[f06894d]276}
277
[0da65d5]278static void jabber_logout( struct im_connection *ic )
[f06894d]279{
[0da65d5]280        struct jabber_data *jd = ic->proto_data;
[21167d2]281       
[4ac647d]282        while( jd->filetransfers )
[17a6ee9]283                imcb_file_canceled( ic, ( ( struct jabber_transfer *) jd->filetransfers->data )->ft, "Logging out" );
[4ac647d]284
285        while( jd->streamhosts )
286        {
287                jabber_streamhost_t *sh = jd->streamhosts->data;
288                jd->streamhosts = g_slist_remove( jd->streamhosts, sh );
289                g_free( sh->jid );
290                g_free( sh->host );
291                g_free( sh );
292        }
293
[de03374]294        if( jd->fd >= 0 )
295                jabber_end_stream( ic );
[4a0614e]296       
[c377417]297        while( ic->groupchats )
[aea8b68]298                jabber_chat_free( ic->groupchats->data );
[c377417]299       
[21167d2]300        if( jd->r_inpa >= 0 )
301                b_event_remove( jd->r_inpa );
302        if( jd->w_inpa >= 0 )
303                b_event_remove( jd->w_inpa );
304       
305        if( jd->ssl )
306                ssl_disconnect( jd->ssl );
307        if( jd->fd >= 0 )
308                closesocket( jd->fd );
309       
[fe7a554]310        if( jd->tx_len )
311                g_free( jd->txq );
312       
[de03374]313        if( jd->node_cache )
314                g_hash_table_destroy( jd->node_cache );
[038d17f]315       
[04a927c]316        jabber_buddy_remove_all( ic );
317       
[70f6aab8]318        xt_free( jd->xt );
[34afea7]319
320        md5_free( &jd->cached_id_prefix );
[70f6aab8]321       
[f138bd2]322        g_free( jd->oauth2_access_token );
[5e202b0]323        g_free( jd->away_message );
[be1efa3]324        g_free( jd->internal_jid );
[21167d2]325        g_free( jd->username );
[68286eb]326        g_free( jd->me );
[21167d2]327        g_free( jd );
[b5c8a34]328       
329        jabber_connections = g_slist_remove( jabber_connections, ic );
[f06894d]330}
331
[f6c963b]332static int jabber_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
[f06894d]333{
[0da65d5]334        struct jabber_data *jd = ic->proto_data;
[a21a8ac]335        struct jabber_buddy *bud;
[cc2cb2d]336        struct xt_node *node;
[b9f8b87]337        char *s;
[4a0614e]338        int st;
339       
[bb95d43]340        if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
341                return jabber_write( ic, message, strlen( message ) );
342       
[34ded90]343        if( g_strcasecmp( who, JABBER_OAUTH_HANDLE ) == 0 &&
344            !( jd->flags & OPT_LOGGED_IN ) && jd->fd == -1 )
[4a5d885]345        {
346                if( sasl_oauth2_get_refresh_token( ic, message ) )
347                {
348                        return 1;
349                }
350                else
351                {
352                        imcb_error( ic, "OAuth failure" );
353                        imc_logout( ic, TRUE );
[911d97a]354                        return 0;
[4a5d885]355                }
356        }
357       
[5bd21df]358        if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) )
[b9f8b87]359                bud = jabber_buddy_by_ext_jid( ic, who, 0 );
360        else
[76c85b4c]361                bud = jabber_buddy_by_jid( ic, who, GET_BUDDY_BARE_OK );
[a214954]362       
[4a0614e]363        node = xt_new_node( "body", message, NULL );
[788a1af]364        node = jabber_make_packet( "message", "chat", bud ? bud->full_jid : who, node );
[a21a8ac]365       
[0d3f30f]366        if( bud && ( jd->flags & JFLAG_WANT_TYPING ) &&
[788a1af]367            ( ( bud->flags & JBFLAG_DOES_XEP85 ) ||
368             !( bud->flags & JBFLAG_PROBED_XEP85 ) ) )
[a21a8ac]369        {
370                struct xt_node *act;
371               
372                /* If the user likes typing notification and if we don't know
[788a1af]373                   (and didn't probe before) if this resource supports XEP85,
[abbd8ed]374                   include a probe in this packet now. Also, if we know this
375                   buddy does support XEP85, we have to send this <active/>
376                   tag to tell that the user stopped typing (well, that's what
377                   we guess when s/he pressed Enter...). */
[a21a8ac]378                act = xt_new_node( "active", NULL, NULL );
[47d3ac4]379                xt_add_attr( act, "xmlns", XMLNS_CHATSTATES );
[a21a8ac]380                xt_add_child( node, act );
381               
382                /* Just make sure we do this only once. */
[788a1af]383                bud->flags |= JBFLAG_PROBED_XEP85;
[a21a8ac]384        }
385       
[0da65d5]386        st = jabber_write_packet( ic, node );
[4a0614e]387        xt_free_node( node );
388       
389        return st;
[f06894d]390}
391
[0da65d5]392static GList *jabber_away_states( struct im_connection *ic )
[dd788bb]393{
[5e202b0]394        static GList *l = NULL;
395        int i;
[dd788bb]396       
[5e202b0]397        if( l == NULL )
398                for( i = 0; jabber_away_state_list[i].full_name; i ++ )
399                        l = g_list_append( l, (void*) jabber_away_state_list[i].full_name );
[dd788bb]400       
[5e202b0]401        return l;
[dd788bb]402}
403
[0da65d5]404static void jabber_get_info( struct im_connection *ic, char *who )
[038d17f]405{
[6a1128d]406        struct jabber_buddy *bud;
[038d17f]407       
[76c85b4c]408        bud = jabber_buddy_by_jid( ic, who, GET_BUDDY_FIRST );
[7e83adca]409       
[6a1128d]410        while( bud )
411        {
[63770b4]412                imcb_log( ic, "Buddy %s (%d) information:", bud->full_jid, bud->priority );
413                if( bud->away_state )
414                        imcb_log( ic, "Away state: %s", bud->away_state->full_name );
[daae10f]415                imcb_log( ic, "Status message: %s", bud->away_message ? bud->away_message : "(none)" );
[63770b4]416               
[6a1128d]417                bud = bud->next;
418        }
[1991be6]419       
[0da65d5]420        jabber_get_vcard( ic, bud ? bud->full_jid : who );
[038d17f]421}
422
[0da65d5]423static void jabber_set_away( struct im_connection *ic, char *state_txt, char *message )
[dd788bb]424{
[0da65d5]425        struct jabber_data *jd = ic->proto_data;
[5e202b0]426       
[840bba8]427        /* state_txt == NULL -> Not away.
428           Unknown state -> fall back to the first defined away state. */
[daae10f]429        if( state_txt == NULL )
430                jd->away_state = NULL;
431        else if( ( jd->away_state = jabber_away_state_by_name( state_txt ) ) == NULL )
432                jd->away_state = jabber_away_state_list;
[840bba8]433       
[5e202b0]434        g_free( jd->away_message );
435        jd->away_message = ( message && *message ) ? g_strdup( message ) : NULL;
436       
[0da65d5]437        presence_send_update( ic );
[deff040]438}
439
[0da65d5]440static void jabber_add_buddy( struct im_connection *ic, char *who, char *group )
[cfbb3a6]441{
[bb95d43]442        struct jabber_data *jd = ic->proto_data;
443       
444        if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
445        {
446                jd->flags |= JFLAG_XMLCONSOLE;
447                imcb_add_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
448                return;
449        }
450       
[46d215d]451        if( jabber_add_to_roster( ic, who, NULL, group ) )
[0da65d5]452                presence_send_request( ic, who, "subscribe" );
[cfbb3a6]453}
454
[0da65d5]455static void jabber_remove_buddy( struct im_connection *ic, char *who, char *group )
[cfbb3a6]456{
[bb95d43]457        struct jabber_data *jd = ic->proto_data;
458       
459        if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
460        {
461                jd->flags &= ~JFLAG_XMLCONSOLE;
[a3d5766]462                /* Not necessary for now. And for now the code isn't too
463                   happy if the buddy is completely gone right after calling
464                   this function already.
[998b103]465                imcb_remove_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
[a3d5766]466                */
[bb95d43]467                return;
468        }
469       
[788a1af]470        /* We should always do this part. Clean up our administration a little bit. */
[0da65d5]471        jabber_buddy_remove_bare( ic, who );
[788a1af]472       
[0da65d5]473        if( jabber_remove_from_roster( ic, who ) )
474                presence_send_request( ic, who, "unsubscribe" );
[cfbb3a6]475}
476
[03f3828]477static struct groupchat *jabber_chat_join_( struct im_connection *ic, const char *room, const char *nick, const char *password, set_t **sets )
[e35d1a1]478{
[441a67e]479        struct jabber_data *jd = ic->proto_data;
[fcb2c2e]480        char *final_nick;
[441a67e]481       
[fcb2c2e]482        /* Ignore the passed nick parameter if we have our own default */
483        if ( !( final_nick = set_getstr( sets, "nick" ) ) &&
484             !( final_nick = set_getstr( &ic->acc->set, "display_name" ) ) ) {
485                /* Well, whatever, actually use the provided default, then */
486                final_nick = (char *) nick;
487        }
488
[e35d1a1]489        if( strchr( room, '@' ) == NULL )
[441a67e]490                imcb_error( ic, "%s is not a valid Jabber room name. Maybe you mean %s@conference.%s?",
491                            room, room, jd->server );
[5bd21df]492        else if( jabber_chat_by_jid( ic, room ) )
[e35d1a1]493                imcb_error( ic, "Already present in chat `%s'", room );
494        else
[fcb2c2e]495                return jabber_chat_join( ic, room, final_nick, set_getstr( sets, "password" ) );
[e35d1a1]496       
497        return NULL;
498}
499
[fc0640e]500static struct groupchat *jabber_chat_with_( struct im_connection *ic, char *who )
501{
502        return jabber_chat_with( ic, who );
503}
504
[43671b9]505static void jabber_chat_msg_( struct groupchat *c, char *message, int flags )
506{
507        if( c && message )
508                jabber_chat_msg( c, message, flags );
509}
510
[ef5c185]511static void jabber_chat_topic_( struct groupchat *c, char *topic )
512{
513        if( c && topic )
514                jabber_chat_topic( c, topic );
515}
516
[e35d1a1]517static void jabber_chat_leave_( struct groupchat *c )
518{
519        if( c )
520                jabber_chat_leave( c, NULL );
521}
522
[c058ff9]523static void jabber_chat_invite_( struct groupchat *c, char *who, char *msg )
524{
[68286eb]525        struct jabber_data *jd = c->ic->proto_data;
[c058ff9]526        struct jabber_chat *jc = c->data;
527        gchar *msg_alt = NULL;
528
529        if( msg == NULL )
[68286eb]530                msg_alt = g_strdup_printf( "%s invited you to %s", jd->me, jc->name );
[c058ff9]531       
532        if( c && who )
533                jabber_chat_invite( c, who, msg ? msg : msg_alt );
534       
535        g_free( msg_alt );
536}
537
[0da65d5]538static void jabber_keepalive( struct im_connection *ic )
[deff040]539{
540        /* Just any whitespace character is enough as a keepalive for XMPP sessions. */
[38ff846]541        if( !jabber_write( ic, "\n", 1 ) )
542                return;
[a214954]543       
[038d17f]544        /* This runs the garbage collection every minute, which means every packet
545           is in the cache for about a minute (which should be enough AFAIK). */
[0da65d5]546        jabber_cache_clean( ic );
[dd788bb]547}
548
[0da65d5]549static int jabber_send_typing( struct im_connection *ic, char *who, int typing )
[a21a8ac]550{
[0da65d5]551        struct jabber_data *jd = ic->proto_data;
[a21a8ac]552        struct jabber_buddy *bud;
553       
554        /* Enable typing notification related code from now. */
555        jd->flags |= JFLAG_WANT_TYPING;
556       
[0da65d5]557        if( ( bud = jabber_buddy_by_jid( ic, who, 0 ) ) == NULL )
[788a1af]558        {
559                /* Sending typing notifications to unknown buddies is
560                   unsupported for now. Shouldn't be a problem, I think. */
561                return 0;
562        }
563       
564        if( bud->flags & JBFLAG_DOES_XEP85 )
[a21a8ac]565        {
566                /* We're only allowed to send this stuff if we know the other
567                   side supports it. */
568               
569                struct xt_node *node;
570                char *type;
571                int st;
572               
[df1fb67]573                if( typing & OPT_TYPING )
[a21a8ac]574                        type = "composing";
[df1fb67]575                else if( typing & OPT_THINKING )
576                        type = "paused";
577                else
578                        type = "active";
[a21a8ac]579               
580                node = xt_new_node( type, NULL, NULL );
[47d3ac4]581                xt_add_attr( node, "xmlns", XMLNS_CHATSTATES );
[a21a8ac]582                node = jabber_make_packet( "message", "chat", bud->full_jid, node );
583               
[0da65d5]584                st = jabber_write_packet( ic, node );
[a21a8ac]585                xt_free_node( node );
586               
587                return st;
588        }
589       
590        return 1;
591}
592
[6d544a1]593void jabber_chat_add_settings( account_t *acc, set_t **head )
594{
595        /* Meh. Stupid room passwords. Not trying to obfuscate/hide
596           them from the user for now. */
597        set_add( head, "password", NULL, NULL, NULL );
598}
599
600void jabber_chat_free_settings( account_t *acc, set_t **head )
601{
602        set_del( head, "password" );
603}
604
[d88c92a]605GList *jabber_buddy_action_list( bee_user_t *bu )
606{
607        static GList *ret = NULL;
608       
609        if( ret == NULL )
610        {
[a97a336]611                static const struct buddy_action ba[2] = {
[d88c92a]612                        { "VERSION", "Get client (version) information" },
613                };
614               
[a97a336]615                ret = g_list_prepend( ret, (void*) ba + 0 );
[d88c92a]616        }
617       
618        return ret;
619}
620
621void *jabber_buddy_action( struct bee_user *bu, const char *action, char * const args[], void *data )
622{
623        if( g_strcasecmp( action, "VERSION" ) == 0 )
624        {
625                struct jabber_buddy *bud;
626               
627                if( ( bud = jabber_buddy_by_ext_jid( bu->ic, bu->handle, 0 ) ) == NULL )
628                        bud = jabber_buddy_by_jid( bu->ic, bu->handle, GET_BUDDY_FIRST );
629                for( ; bud; bud = bud->next )
630                        jabber_iq_version_send( bu->ic, bud, data );
631        }
632       
633        return NULL;
634}
635
[be1efa3]636gboolean jabber_handle_is_self( struct im_connection *ic, const char *who ) {
637        struct jabber_data *jd = ic->proto_data;
638        return ( ( g_strcasecmp( who, ic->acc->user ) == 0 ) ||
639                 ( jd->internal_jid &&
640                   g_strcasecmp( who, jd->internal_jid ) == 0 ) );
641}
642
[0da65d5]643void jabber_initmodule()
[f06894d]644{
[deff040]645        struct prpl *ret = g_new0( struct prpl, 1 );
[f06894d]646       
647        ret->name = "jabber";
[6d544a1]648        ret->mms = 0;                        /* no limit */
[f06894d]649        ret->login = jabber_login;
[0da65d5]650        ret->init = jabber_init;
651        ret->logout = jabber_logout;
[f6c963b]652        ret->buddy_msg = jabber_buddy_msg;
[dd788bb]653        ret->away_states = jabber_away_states;
654        ret->set_away = jabber_set_away;
[f06894d]655//      ret->set_info = jabber_set_info;
[038d17f]656        ret->get_info = jabber_get_info;
[cfbb3a6]657        ret->add_buddy = jabber_add_buddy;
658        ret->remove_buddy = jabber_remove_buddy;
[43671b9]659        ret->chat_msg = jabber_chat_msg_;
[ef5c185]660        ret->chat_topic = jabber_chat_topic_;
[c058ff9]661        ret->chat_invite = jabber_chat_invite_;
[e35d1a1]662        ret->chat_leave = jabber_chat_leave_;
663        ret->chat_join = jabber_chat_join_;
[fc0640e]664        ret->chat_with = jabber_chat_with_;
[6d544a1]665        ret->chat_add_settings = jabber_chat_add_settings;
666        ret->chat_free_settings = jabber_chat_free_settings;
[deff040]667        ret->keepalive = jabber_keepalive;
[a21a8ac]668        ret->send_typing = jabber_send_typing;
[f06894d]669        ret->handle_cmp = g_strcasecmp;
[be1efa3]670        ret->handle_is_self = jabber_handle_is_self;
[2ff2076]671        ret->transfer_request = jabber_si_transfer_request;
[d88c92a]672        ret->buddy_action_list = jabber_buddy_action_list;
673        ret->buddy_action = jabber_buddy_action;
[f06894d]674
[deff040]675        register_protocol( ret );
[f06894d]676}
Note: See TracBrowser for help on using the repository browser.