source: protocols/jabber/jabber_util.c @ ee5c355

Last change on this file since ee5c355 was 259edd4, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-12T17:48:58Z

Special message when the XMPP session is ended because of a concurrent
login, and now sending proper error responses to IQ packets we can't
handle.

  • Property mode set to 100644
File size: 10.4 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - Misc. stuff                                              *
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 "jabber.h"
25
26static int next_id = 1;
27
28char *set_eval_priority( set_t *set, char *value )
29{
30        account_t *acc = set->data;
31        char *ret;
32       
33        ret = set_eval_int( set, value );
34       
35        /* Only run this stuff if the account is online ATM,
36           and if the setting seems to be acceptable. */
37        if( acc->gc && ret )
38        {
39                /* Although set_eval functions usually are very nice and
40                   convenient, they have one disadvantage: If I would just
41                   call p_s_u() now to send the new prio setting, it would
42                   send the old setting because the set->value gets changed
43                   when the eval returns a non-NULL value.
44                   
45                   So now I can choose between implementing post-set
46                   functions next to evals, or just do this little hack: */
47               
48                g_free( set->value );
49                set->value = g_strdup( ret );
50               
51                /* (Yes, sorry, I prefer the hack. :-P) */
52               
53                presence_send_update( acc->gc );
54        }
55       
56        return ret;
57}
58
59char *set_eval_tls( set_t *set, char *value )
60{
61        if( g_strcasecmp( value, "try" ) == 0 )
62                return value;
63        else
64                return set_eval_bool( set, value );
65}
66
67struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children )
68{
69        struct xt_node *node;
70       
71        node = xt_new_node( name, NULL, children );
72       
73        if( type )
74                xt_add_attr( node, "type", type );
75        if( to )
76                xt_add_attr( node, "to", to );
77       
78        return node;
79}
80
81struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type )
82{
83        struct xt_node *node, *c;
84        char *to;
85       
86        /* Create the "defined-condition" tag. */
87        c = xt_new_node( err_cond, NULL, NULL );
88        xt_add_attr( c, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas" );
89       
90        /* Put it in an <error> tag. */
91        c = xt_new_node( "error", NULL, c );
92        xt_add_attr( c, "type", err_type );
93       
94        /* To make the actual error packet, we copy the original packet and
95           add our <error>/type="error" tag. Including the original packet
96           is recommended, so let's just do it. */
97        node = xt_dup( orig );
98        xt_add_child( node, c );
99        xt_add_attr( node, "type", "error" );
100       
101        /* Return to sender. */
102        if( ( to = xt_find_attr( node, "from" ) ) )
103        {
104                xt_add_attr( node, "to", to );
105                xt_remove_attr( node, "from" );
106        }
107               
108        return node;
109}
110
111/* Cache a node/epacket for later use. Mainly useful for IQ packets if you need
112   them when you receive the response. Use this BEFORE sending the packet so
113   it'll get an id= tag, and do NOT free() the packet after writing it! */
114void jabber_cache_add( struct gaim_connection *gc, struct xt_node *node, jabber_cache_event func )
115{
116        struct jabber_data *jd = gc->proto_data;
117        char *id = g_strdup_printf( "BeeX%04x", next_id++ );
118        struct jabber_cache_entry *entry = g_new0( struct jabber_cache_entry, 1 );
119       
120        xt_add_attr( node, "id", id );
121        g_free( id );
122       
123        entry->node = node;
124        entry->func = func;
125        g_hash_table_insert( jd->node_cache, xt_find_attr( node, "id" ), entry );
126}
127
128void jabber_cache_entry_free( gpointer data )
129{
130        struct jabber_cache_entry *entry = data;
131       
132        xt_free_node( entry->node );
133        g_free( entry );
134}
135
136gboolean jabber_cache_clean_entry( gpointer key, gpointer entry, gpointer nullpointer );
137
138/* This one should be called from time to time (from keepalive, in this case)
139   to make sure things don't stay in the node cache forever. By marking nodes
140   during the first run and deleting marked nodes during a next run, every
141   node should be available in the cache for at least a minute (assuming the
142   function is indeed called every minute). */
143void jabber_cache_clean( struct gaim_connection *gc )
144{
145        struct jabber_data *jd = gc->proto_data;
146       
147        g_hash_table_foreach_remove( jd->node_cache, jabber_cache_clean_entry, NULL );
148}
149
150gboolean jabber_cache_clean_entry( gpointer key, gpointer entry_, gpointer nullpointer )
151{
152        struct jabber_cache_entry *entry = entry_;
153        struct xt_node *node = entry->node;
154       
155        if( node->flags & XT_SEEN )
156                return TRUE;
157        else
158        {
159                node->flags |= XT_SEEN;
160                return FALSE;
161        }
162}
163
164const struct jabber_away_state jabber_away_state_list[] =
165{
166        { "away",  "Away" },
167        { "chat",  "Free for Chat" },
168        { "dnd",   "Do not Disturb" },
169        { "xa",    "Extended Away" },
170        { "",      "Online" },
171        { "",      NULL }
172};
173
174const struct jabber_away_state *jabber_away_state_by_code( char *code )
175{
176        int i;
177       
178        for( i = 0; jabber_away_state_list[i].full_name; i ++ )
179                if( g_strcasecmp( jabber_away_state_list[i].code, code ) == 0 )
180                        return jabber_away_state_list + i;
181       
182        return NULL;
183}
184
185const struct jabber_away_state *jabber_away_state_by_name( char *name )
186{
187        int i;
188       
189        for( i = 0; jabber_away_state_list[i].full_name; i ++ )
190                if( g_strcasecmp( jabber_away_state_list[i].full_name, name ) == 0 )
191                        return jabber_away_state_list + i;
192       
193        return NULL;
194}
195
196struct jabber_buddy_ask_data
197{
198        struct gaim_connection *gc;
199        char *handle;
200        char *realname;
201};
202
203static void jabber_buddy_ask_yes( gpointer w, struct jabber_buddy_ask_data *bla )
204{
205        presence_send_request( bla->gc, bla->handle, "subscribed" );
206       
207        if( find_buddy( bla->gc, bla->handle ) == NULL )
208                show_got_added( bla->gc, bla->handle, NULL );
209       
210        g_free( bla->handle );
211        g_free( bla );
212}
213
214static void jabber_buddy_ask_no( gpointer w, struct jabber_buddy_ask_data *bla )
215{
216        presence_send_request( bla->gc, bla->handle, "subscribed" );
217       
218        g_free( bla->handle );
219        g_free( bla );
220}
221
222void jabber_buddy_ask( struct gaim_connection *gc, char *handle )
223{
224        struct jabber_buddy_ask_data *bla = g_new0( struct jabber_buddy_ask_data, 1 );
225        char *buf;
226       
227        bla->gc = gc;
228        bla->handle = g_strdup( handle );
229       
230        buf = g_strdup_printf( "The user %s wants to add you to his/her buddy list.", handle );
231        do_ask_dialog( gc, buf, bla, jabber_buddy_ask_yes, jabber_buddy_ask_no );
232        g_free( buf );
233}
234
235/* Adds a buddy/resource to our list. Returns NULL if full_jid is not really a
236   FULL jid or if we already have this buddy/resource. */
237struct jabber_buddy *jabber_buddy_add( struct gaim_connection *gc, char *full_jid )
238{
239        struct jabber_data *jd = gc->proto_data;
240        struct jabber_buddy *bud, *new, *bi;
241        char *s;
242       
243        if( !( s = strchr( full_jid, '/' ) ) )
244                return NULL;
245       
246        new = g_new0( struct jabber_buddy, 1 );
247       
248        *s = 0;
249        if( ( bud = g_hash_table_lookup( jd->buddies, full_jid ) ) )
250        {
251                new->handle = bud->handle;
252               
253                /* We already have another resource for this buddy, add the
254                   new one to the list. */
255                for( bi = bud; bi; bi = bi->next )
256                {
257                        /* Check for dupes. Resource seem to be case sensitive. */
258                        if( strcmp( bi->resource, s + 1 ) == 0 )
259                        {
260                                *s = '/';
261                                g_free( new );
262                                return NULL;
263                        }
264                        /* Append the new item to the list. */
265                        else if( bi->next == NULL )
266                        {
267                                bi->next = new;
268                                break;
269                        }
270                }
271        }
272        else
273        {
274                new->handle = g_strdup( full_jid );
275                g_hash_table_insert( jd->buddies, new->handle, new );
276        }
277       
278        *s = '/';
279        new->full_jid = g_strdup( full_jid );
280        new->resource = strchr( new->full_jid, '/' ) + 1;
281       
282        return new;
283}
284
285struct jabber_buddy *jabber_buddy_by_jid( struct gaim_connection *gc, char *jid )
286{
287        struct jabber_data *jd = gc->proto_data;
288        struct jabber_buddy *bud;
289        char *s;
290       
291        if( ( s = strchr( jid, '/' ) ) )
292        {
293                *s = 0;
294                if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )
295                        for( ; bud; bud = bud->next )
296                                if( strcmp( bud->resource, s + 1 ) == 0 )
297                                        break;
298        }
299        else
300        {
301                struct jabber_buddy *best_prio, *best_time;
302                char *set;
303               
304                best_prio = best_time = bud = g_hash_table_lookup( jd->buddies, jid );
305                for( ; bud; bud = bud->next )
306                {
307                        if( bud->priority > best_prio->priority )
308                                best_prio = bud;
309                        if( bud->last_act > best_time->last_act )
310                                best_time = bud;
311                }
312               
313                if( ( set = set_getstr( &gc->acc->set, "resource_select" ) ) == NULL )
314                        return NULL;
315                else if( strcmp( set, "activity" ) == 0 )
316                        return best_time;
317                else /* if( strcmp( set, "priority" ) == 0 ) */
318                        return best_prio;
319        }
320       
321        *s = '/';
322        return bud;
323}
324
325int jabber_buddy_remove( struct gaim_connection *gc, char *full_jid )
326{
327        struct jabber_data *jd = gc->proto_data;
328        struct jabber_buddy *bud, *prev, *bi;
329        char *s;
330       
331        if( !( s = strchr( full_jid, '/' ) ) )
332                return 0;
333       
334        *s = 0;
335        if( ( bud = g_hash_table_lookup( jd->buddies, full_jid ) ) )
336        {
337                /* If there's only one item in the list (and if the resource
338                   matches), removing it is simple. (And the hash reference
339                   should be removed too!) */
340                if( bud->next == NULL && strcmp( bud->resource, s + 1 ) == 0 )
341                {
342                        g_hash_table_remove( jd->buddies, bud->handle );
343                        g_free( bud->handle );
344                        g_free( bud->full_jid );
345                        g_free( bud->away_message );
346                        g_free( bud );
347                }
348                else
349                {
350                        for( bi = bud, prev = NULL; bi; bi = (prev=bi)->next )
351                                if( strcmp( bi->resource, s + 1 ) == 0 )
352                                        break;
353                       
354                        if( bi )
355                        {
356                                if( prev )
357                                        prev->next = bi->next;
358                                else
359                                        /* The hash table should point at the second
360                                           item, because we're removing the first. */
361                                        g_hash_table_replace( jd->buddies, bi->handle, bi->next );
362                               
363                                g_free( bi->full_jid );
364                                g_free( bi->away_message );
365                                g_free( bi );
366                        }
367                        else
368                        {
369                                *s = '/';
370                                return 0;
371                        }
372                }
373               
374                *s = '/';
375                return 1;
376        }
377        else
378        {
379                *s = '/';
380                return 0;
381        }
382}
Note: See TracBrowser for help on using the repository browser.