source: protocols/jabber/jabber_util.c @ f0071b7

Last change on this file since f0071b7 was dfa41a4, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-20T19:12:14Z

Now all IQ packets get an ID and cached packets get a "special" ID. This
makes it easier to find out if an event handler has to be called for a
reply packet.

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