source: protocols/jabber/jabber.c @ d74c644

Last change on this file since d74c644 was 788a1af, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-15T20:24:01Z

Proper cleanup of jabber buddy structures when removing a buddy from the
list, proper checking (and handling) of events related to buddies that
aren't "hashed" yet, limit checks on priorityto setting, renamed JEP85
to XEP85, support for more XEP85 states.

  • Property mode set to 100644
File size: 9.5 KB
Line 
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>
25#include <string.h>
26#include <unistd.h>
27#include <ctype.h>
28#include <stdio.h>
29
30#include "ssl_client.h"
31#include "xmltree.h"
32#include "bitlbee.h"
33#include "jabber.h"
34
35static void jabber_acc_init( account_t *acc )
36{
37        set_t *s;
38       
39        s = set_add( &acc->set, "port", "5222", set_eval_int, acc );
40        s->flags |= ACC_SET_OFFLINE_ONLY;
41       
42        s = set_add( &acc->set, "priority", "0", set_eval_priority, acc );
43       
44        s = set_add( &acc->set, "resource", "BitlBee", NULL, acc );
45        s->flags |= ACC_SET_OFFLINE_ONLY;
46       
47        s = set_add( &acc->set, "resource_select", "priority", NULL, acc );
48       
49        s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
50        s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
51       
52        s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc );
53        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
59static void jabber_login( account_t *acc )
60{
61        struct gaim_connection *gc = new_gaim_conn( acc );
62        struct jabber_data *jd = g_new0( struct jabber_data, 1 );
63        struct ns_srv_reply *srv = NULL;
64        char *connect_to;
65       
66        jd->gc = gc;
67        gc->proto_data = jd;
68       
69        jd->username = g_strdup( acc->user );
70        jd->server = strchr( jd->username, '@' );
71       
72        if( jd->server == NULL )
73        {
74                hide_login_progress( gc, "Incomplete account name (format it like <username@jabberserver.name>)" );
75                signoff( gc );
76                return;
77        }
78       
79        /* So don't think of free()ing jd->server.. :-) */
80        *jd->server = 0;
81        jd->server ++;
82       
83        jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free );
84        jd->buddies = g_hash_table_new( g_str_hash, g_str_equal );
85       
86        /* Figure out the hostname to connect to. */
87        if( acc->server )
88                connect_to = acc->server;
89        else if( ( srv = srv_lookup( "xmpp-client", "tcp", jd->server ) ) ||
90                 ( srv = srv_lookup( "jabber-client", "tcp", jd->server ) ) )
91                connect_to = srv->name;
92        else
93                connect_to = jd->server;
94       
95        /* For non-SSL connections we can try to use the port # from the SRV
96           reply, but let's not do that when using SSL, SSL usually runs on
97           non-standard ports... */
98        if( set_getbool( &acc->set, "ssl" ) )
99        {
100                jd->ssl = ssl_connect( connect_to, set_getint( &acc->set, "port" ), jabber_connected_ssl, gc );
101                jd->fd = ssl_getfd( jd->ssl );
102        }
103        else
104        {
105                jd->fd = proxy_connect( connect_to, srv ? srv->port : set_getint( &acc->set, "port" ), jabber_connected_plain, gc );
106        }
107       
108        g_free( srv );
109}
110
111static void jabber_close( struct gaim_connection *gc )
112{
113        struct jabber_data *jd = gc->proto_data;
114       
115        jabber_end_stream( gc );
116       
117        if( jd->r_inpa >= 0 )
118                b_event_remove( jd->r_inpa );
119        if( jd->w_inpa >= 0 )
120                b_event_remove( jd->w_inpa );
121       
122        if( jd->ssl )
123                ssl_disconnect( jd->ssl );
124        if( jd->fd >= 0 )
125                closesocket( jd->fd );
126       
127        if( jd->tx_len )
128                g_free( jd->txq );
129       
130        g_hash_table_destroy( jd->node_cache );
131       
132        xt_free( jd->xt );
133       
134        g_free( jd->away_message );
135        g_free( jd->username );
136        g_free( jd );
137}
138
139static int jabber_send_im( struct gaim_connection *gc, char *who, char *message, int len, int away )
140{
141        struct jabber_data *jd = gc->proto_data;
142        struct jabber_buddy *bud;
143        struct xt_node *node;
144        int st;
145       
146        bud = jabber_buddy_by_jid( gc, who );
147       
148        node = xt_new_node( "body", message, NULL );
149        node = jabber_make_packet( "message", "chat", bud ? bud->full_jid : who, node );
150       
151        if( ( jd->flags & JFLAG_WANT_TYPING ) && bud &&
152            ( ( bud->flags & JBFLAG_DOES_XEP85 ) ||
153             !( bud->flags & JBFLAG_PROBED_XEP85 ) ) )
154        {
155                struct xt_node *act;
156               
157                /* If the user likes typing notification and if we don't know
158                   (and didn't probe before) if this resource supports XEP85,
159                   include a probe in this packet now. */
160                act = xt_new_node( "active", NULL, NULL );
161                xt_add_attr( act, "xmlns", "http://jabber.org/protocol/chatstates" );
162                xt_add_child( node, act );
163               
164                /* Just make sure we do this only once. */
165                bud->flags |= JBFLAG_PROBED_XEP85;
166        }
167       
168        st = jabber_write_packet( gc, node );
169        xt_free_node( node );
170       
171        return st;
172}
173
174static GList *jabber_away_states( struct gaim_connection *gc )
175{
176        static GList *l = NULL;
177        int i;
178       
179        if( l == NULL )
180                for( i = 0; jabber_away_state_list[i].full_name; i ++ )
181                        l = g_list_append( l, (void*) jabber_away_state_list[i].full_name );
182       
183        return l;
184}
185
186static void jabber_get_info( struct gaim_connection *gc, char *who )
187{
188        struct jabber_data *jd = gc->proto_data;
189        struct jabber_buddy *bud;
190       
191        if( strchr( who, '/' ) )
192                bud = jabber_buddy_by_jid( gc, who );
193        else
194                bud = g_hash_table_lookup( jd->buddies, who );
195       
196        while( bud )
197        {
198                serv_got_crap( gc, "Buddy %s/%s (%d) information:\nAway state: %s\nAway message: %s",
199                                   bud->handle, bud->resource, bud->priority,
200                                   bud->away_state ? bud->away_state->full_name : "(none)",
201                                   bud->away_message ? : "(none)" );
202                bud = bud->next;
203        }
204}
205
206static void jabber_set_away( struct gaim_connection *gc, char *state_txt, char *message )
207{
208        struct jabber_data *jd = gc->proto_data;
209        struct jabber_away_state *state;
210       
211        /* Save all this info. We need it, for example, when changing the priority setting. */
212        state = (void *) jabber_away_state_by_name( state_txt );
213        jd->away_state = state ? state : (void *) jabber_away_state_list; /* Fall back to "Away" if necessary. */
214        g_free( jd->away_message );
215        jd->away_message = ( message && *message ) ? g_strdup( message ) : NULL;
216       
217        presence_send_update( gc );
218}
219
220static void jabber_add_buddy( struct gaim_connection *gc, char *who )
221{
222        if( jabber_add_to_roster( gc, who, NULL ) )
223                presence_send_request( gc, who, "subscribe" );
224}
225
226static void jabber_remove_buddy( struct gaim_connection *gc, char *who, char *group )
227{
228        /* We should always do this part. Clean up our administration a little bit. */
229        jabber_buddy_remove_bare( gc, who );
230       
231        if( jabber_remove_from_roster( gc, who ) )
232                presence_send_request( gc, who, "unsubscribe" );
233}
234
235static void jabber_keepalive( struct gaim_connection *gc )
236{
237        /* Just any whitespace character is enough as a keepalive for XMPP sessions. */
238        jabber_write( gc, "\n", 1 );
239       
240        /* This runs the garbage collection every minute, which means every packet
241           is in the cache for about a minute (which should be enough AFAIK). */
242        jabber_cache_clean( gc );
243}
244
245static int jabber_send_typing( struct gaim_connection *gc, char *who, int typing )
246{
247        struct jabber_data *jd = gc->proto_data;
248        struct jabber_buddy *bud;
249       
250        /* Enable typing notification related code from now. */
251        jd->flags |= JFLAG_WANT_TYPING;
252       
253        if( ( bud = jabber_buddy_by_jid( gc, who ) ) == NULL )
254        {
255                /* Sending typing notifications to unknown buddies is
256                   unsupported for now. Shouldn't be a problem, I think. */
257                return 0;
258        }
259       
260        if( bud->flags & JBFLAG_DOES_XEP85 )
261        {
262                /* We're only allowed to send this stuff if we know the other
263                   side supports it. */
264               
265                struct xt_node *node;
266                char *type;
267                int st;
268               
269                if( typing == 0 )
270                        type = "active";
271                else if( typing == 2 )
272                        type = "paused";
273                else /* if( typing == 1 ) */
274                        type = "composing";
275               
276                node = xt_new_node( type, NULL, NULL );
277                xt_add_attr( node, "xmlns", "http://jabber.org/protocol/chatstates" );
278                node = jabber_make_packet( "message", "chat", bud->full_jid, node );
279               
280                st = jabber_write_packet( gc, node );
281                xt_free_node( node );
282               
283                return st;
284        }
285       
286        return 1;
287}
288
289void jabber_init()
290{
291        struct prpl *ret = g_new0( struct prpl, 1 );
292       
293        ret->name = "jabber";
294        ret->login = jabber_login;
295        ret->acc_init = jabber_acc_init;
296        ret->close = jabber_close;
297        ret->send_im = jabber_send_im;
298        ret->away_states = jabber_away_states;
299//      ret->get_status_string = jabber_get_status_string;
300        ret->set_away = jabber_set_away;
301//      ret->set_info = jabber_set_info;
302        ret->get_info = jabber_get_info;
303        ret->add_buddy = jabber_add_buddy;
304        ret->remove_buddy = jabber_remove_buddy;
305//      ret->chat_send = jabber_chat_send;
306//      ret->chat_invite = jabber_chat_invite;
307//      ret->chat_leave = jabber_chat_leave;
308//      ret->chat_open = jabber_chat_open;
309        ret->keepalive = jabber_keepalive;
310        ret->send_typing = jabber_send_typing;
311        ret->handle_cmp = g_strcasecmp;
312
313        register_protocol( ret );
314}
Note: See TracBrowser for help on using the repository browser.