source: protocols/jabber/jabber_util.c @ 1ba7e8f

Last change on this file since 1ba7e8f was 1ba7e8f, checked in by ulim <a.sporto+bee@…>, at 2008-02-15T17:38:57Z

Merged with upstream r328

Wilmer van der Gaast 2008-02-11 Got rid of some noise at startup: complaining when the default configuration

Wilmer van der Gaast 2008-02-10 Added support for password-protected Jabber chatrooms.
Wilmer van der Gaast 2008-02-10 Making AI_ADDRCONFIG optional, it doesn't exist on at least NetBSD and
Wilmer van der Gaast 2008-02-09 Restored "add -tmp". A bit hackish, but it will do for now.
Wilmer van der Gaast 2008-02-07 Fixed getnameinfo() calls, this fixes Solaris stability issues. Thanks to
Wilmer van der Gaast 2008-02-04 Added bogus G_GNUC_MALLOC to restore GLib 2.4 compatibility (hopefully).
Wilmer van der Gaast 2008-02-03 Messages from the user are also included in backlogs when joining a Jabber
Wilmer van der Gaast 2008-02-03 Disabling "Unknown command" warnings since they're very noisy and pretty
Wilmer van der Gaast 2008-02-03 Implemented XEP-0115. This adds some info to the <presence/> tags so
Wilmer van der Gaast 2008-02-03 Saner garbage collection of cached packets in the Jabber module. Now
Wilmer van der Gaast 2008-02-02 Added help_free() and cleaned up some very stale help-related stuff I
Wilmer van der Gaast 2008-01-30 Fixed handling of OSCAR multi-part messages... They're not arrays, they're
Wilmer van der Gaast 2008-01-24 Keeping track of valid Jabber connections so _connected() events will be
Wilmer van der Gaast 2008-01-24 Fixed two valgrind warnings (partially uninitialized "struct tm" vars.)
Wilmer van der Gaast 2008-01-20 The Jabber module now uses imcb_chat_log() instead of imcb_log() where
Wilmer van der Gaast 2008-01-20 Added imcb_chat_log() for chatroom system messages, so they can be
Wilmer van der Gaast 2008-01-20 GET_BUDDY_FIRST wasn't actually implemented, even though it was in use
Wilmer van der Gaast 2008-01-19 Using test -f instead of test -e. This breaks if the include files are
Wilmer van der Gaast 2008-01-19 Added byte swapping code to the new MD5 checksumming code to make it work
Wilmer van der Gaast 2008-01-18 Moving imcb_chat_new() to a saner location (no code changes) and fixing
Wilmer van der Gaast 2008-01-17 Apparently ext_yahoo_got_im can be called with msg=NULL, so it should be
Wilmer van der Gaast 2008-01-17 Fixing some Solaris compiler warnings (u_int->uint, adding some typecasts
Wilmer van der Gaast 2008-01-13 Fixed handing of failed groupchat joins.
Wilmer van der Gaast 2008-01-13 Fixed "Conditional jump or move depends on uninitialised value(s)" at
Wilmer van der Gaast 2008-01-13 Fixed quickstart2. (Bug #349.)
Wilmer van der Gaast 2008-01-13 Different handling of charset mismatches before login time. Ignoring a
Wilmer van der Gaast 2008-01-12 When a switchboard connection dies (at the TCP level) and there are still
Wilmer van der Gaast 2008-01-12 Killed info_string_append() and now showing the IP address of ICQ users
Wilmer van der Gaast 2008-01-11 Fixing bug #344, now away states should always be correct, even when people
Wilmer van der Gaast 2008-01-11 Adding own handle to protocol name in blist output for people with multiple
Wilmer van der Gaast 2008-01-10 Now setting odata->icq properly again, this got lost some time ago, which
Wilmer van der Gaast 2008-01-06 More consistency in error/warning errors. Until now "WARNING:" was usually
Wilmer van der Gaast 2008-01-06 Changed warning message about unsent MSN messages. It should show the actual
Wilmer van der Gaast 2008-01-05 Added "mail_notifications" setting. Who needs those notifications anyway?
Wilmer van der Gaast 2008-01-05 Build fix from vmiklos.
Wilmer van der Gaast 2008-01-05 Added handling of MSN switchboard NAK messages. Untested, but hey, it
Wilmer van der Gaast 2008-01-05 Removed closure->result. I was planning to add some more stuff, but will
Miklos Vajna 2007-12-31 encode: md5.c is no longer in protocols/, it's in lib/
Wilmer van der Gaast 2007-12-28 Fixed return value check in proxy_connect(), since on some systems
Wilmer van der Gaast 2007-12-28 Added missing return in jabber_login().
Wilmer van der Gaast 2007-12-16 Implemented XEP-0199 (patch from misc@…).
Wilmer van der Gaast 2007-12-12 Checking conn->xcred before trying to clean it up since GnuTLS doesn't
Wilmer van der Gaast 2007-12-12 Killed the <server> parameter to "account add" and changed the default
Wilmer van der Gaast 2007-12-12 Fixed sockerr_again() usage in Jabber module to (hopefully) fix a 100% CPU
Wilmer van der Gaast 2007-12-10 Don't allow nicks that start with a number.
Wilmer van der Gaast 2007-12-10 Fixed "set xxx" syntax (it showed all settings instead of just xxx).
Wilmer van der Gaast 2007-12-09 If I keep forgetting to credit people in commit msgs I should probably add
Wilmer van der Gaast 2007-12-09 Added /invite support for Jabber chatrooms (and fixed the argument order

  • Property mode set to 100644
File size: 19.8 KB
RevLine 
[f06894d]1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
[21167d2]4*  Jabber module - Misc. stuff                                              *
[f06894d]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
[dfa41a4]26static unsigned int next_id = 1;
[21167d2]27
[ebe7b36]28char *set_eval_priority( set_t *set, char *value )
[f06894d]29{
30        account_t *acc = set->data;
[788a1af]31        int i;
[f06894d]32       
[788a1af]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;
[172a73f1]41       
42        /* Only run this stuff if the account is online ATM,
43           and if the setting seems to be acceptable. */
[0da65d5]44        if( acc->ic )
[f06894d]45        {
[ebe7b36]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
[e35d1a1]50                   after the (this) eval returns a non-NULL value.
[ebe7b36]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 );
[788a1af]56                set->value = g_strdup( value );
[ebe7b36]57               
58                /* (Yes, sorry, I prefer the hack. :-P) */
59               
[0da65d5]60                presence_send_update( acc->ic );
[f06894d]61        }
62       
[788a1af]63        return value;
[f06894d]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}
[21167d2]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       
[dfa41a4]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       
[fe7a554]96        return node;
97}
98
[2c2df7d]99struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type, char *err_code )
[259edd4]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 );
[47d3ac4]106        xt_add_attr( c, "xmlns", XMLNS_STANZA_ERROR );
[259edd4]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       
[2c2df7d]112        /* Add the error code, if present */
113        if (err_code)
114                xt_add_attr( c, "code", err_code );
115       
[259edd4]116        /* To make the actual error packet, we copy the original packet and
117           add our <error>/type="error" tag. Including the original packet
118           is recommended, so let's just do it. */
119        node = xt_dup( orig );
120        xt_add_child( node, c );
121        xt_add_attr( node, "type", "error" );
122       
123        /* Return to sender. */
124        if( ( to = xt_find_attr( node, "from" ) ) )
125        {
126                xt_add_attr( node, "to", to );
127                xt_remove_attr( node, "from" );
128        }
129               
130        return node;
131}
132
[dfa41a4]133/* Cache a node/packet for later use. Mainly useful for IQ packets if you need
[fe7a554]134   them when you receive the response. Use this BEFORE sending the packet so
[e35d1a1]135   it'll get a new id= tag, and do NOT free() the packet after sending it! */
[0da65d5]136void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func )
[fe7a554]137{
[0da65d5]138        struct jabber_data *jd = ic->proto_data;
[038d17f]139        struct jabber_cache_entry *entry = g_new0( struct jabber_cache_entry, 1 );
[608f8cf]140        char *id;
[fe7a554]141       
[608f8cf]142        id = g_strdup_printf( "%s%05x", jd->cached_id_prefix, ( next_id++ ) & 0xfffff );
[fe7a554]143        xt_add_attr( node, "id", id );
[21167d2]144        g_free( id );
[038d17f]145       
146        entry->node = node;
[861c199]147        entry->func = func;
[979cfb4]148        entry->saved_at = time( NULL );
[038d17f]149        g_hash_table_insert( jd->node_cache, xt_find_attr( node, "id" ), entry );
[fe7a554]150}
151
[038d17f]152void jabber_cache_entry_free( gpointer data )
153{
154        struct jabber_cache_entry *entry = data;
[21167d2]155       
[038d17f]156        xt_free_node( entry->node );
157        g_free( entry );
158}
159
160gboolean jabber_cache_clean_entry( gpointer key, gpointer entry, gpointer nullpointer );
161
[861c199]162/* This one should be called from time to time (from keepalive, in this case)
163   to make sure things don't stay in the node cache forever. By marking nodes
164   during the first run and deleting marked nodes during a next run, every
165   node should be available in the cache for at least a minute (assuming the
166   function is indeed called every minute). */
[0da65d5]167void jabber_cache_clean( struct im_connection *ic )
[038d17f]168{
[0da65d5]169        struct jabber_data *jd = ic->proto_data;
[979cfb4]170        time_t threshold = time( NULL ) - JABBER_CACHE_MAX_AGE;
[038d17f]171       
[979cfb4]172        g_hash_table_foreach_remove( jd->node_cache, jabber_cache_clean_entry, &threshold );
[038d17f]173}
174
[979cfb4]175gboolean jabber_cache_clean_entry( gpointer key, gpointer entry_, gpointer threshold_ )
[038d17f]176{
177        struct jabber_cache_entry *entry = entry_;
[979cfb4]178        time_t *threshold = threshold_;
[038d17f]179       
[979cfb4]180        return entry->saved_at < *threshold;
[21167d2]181}
[5e202b0]182
[4306d8b]183xt_status jabber_cache_handle_packet( struct im_connection *ic, struct xt_node *node )
184{
185        struct jabber_data *jd = ic->proto_data;
186        struct jabber_cache_entry *entry;
187        char *s;
188       
189        if( ( s = xt_find_attr( node, "id" ) ) == NULL ||
190            strncmp( s, jd->cached_id_prefix, strlen( jd->cached_id_prefix ) ) != 0 )
191        {
192                /* Silently ignore it, without an ID (or a non-cache
193                   ID) we don't know how to handle the packet and we
194                   probably don't have to. */
195                return XT_HANDLED;
196        }
197       
198        entry = g_hash_table_lookup( jd->node_cache, s );
199       
200        if( entry == NULL )
201        {
[43462708]202                imcb_log( ic, "Warning: Received %s-%s packet with unknown/expired ID %s!",
[4306d8b]203                              node->name, xt_find_attr( node, "type" ) ? : "(no type)", s );
204        }
205        else if( entry->func )
206        {
207                return entry->func( ic, node, entry->node );
208        }
209       
210        return XT_HANDLED;
211}
212
[5e202b0]213const struct jabber_away_state jabber_away_state_list[] =
214{
215        { "away",  "Away" },
216        { "chat",  "Free for Chat" },
217        { "dnd",   "Do not Disturb" },
218        { "xa",    "Extended Away" },
219        { "",      "Online" },
220        { "",      NULL }
221};
222
223const struct jabber_away_state *jabber_away_state_by_code( char *code )
224{
225        int i;
226       
227        for( i = 0; jabber_away_state_list[i].full_name; i ++ )
228                if( g_strcasecmp( jabber_away_state_list[i].code, code ) == 0 )
229                        return jabber_away_state_list + i;
230       
231        return NULL;
232}
233
234const struct jabber_away_state *jabber_away_state_by_name( char *name )
235{
236        int i;
237       
238        for( i = 0; jabber_away_state_list[i].full_name; i ++ )
239                if( g_strcasecmp( jabber_away_state_list[i].full_name, name ) == 0 )
240                        return jabber_away_state_list + i;
241       
242        return NULL;
243}
[8e5e2e9]244
245struct jabber_buddy_ask_data
246{
[0da65d5]247        struct im_connection *ic;
[8e5e2e9]248        char *handle;
249        char *realname;
250};
251
252static void jabber_buddy_ask_yes( gpointer w, struct jabber_buddy_ask_data *bla )
253{
[0da65d5]254        presence_send_request( bla->ic, bla->handle, "subscribed" );
[8e5e2e9]255       
[f0cb961]256        if( imcb_find_buddy( bla->ic, bla->handle ) == NULL )
[84b045d]257                imcb_ask_add( bla->ic, bla->handle, NULL );
[8e5e2e9]258       
259        g_free( bla->handle );
260        g_free( bla );
261}
262
263static void jabber_buddy_ask_no( gpointer w, struct jabber_buddy_ask_data *bla )
264{
[0da65d5]265        presence_send_request( bla->ic, bla->handle, "subscribed" );
[8e5e2e9]266       
267        g_free( bla->handle );
268        g_free( bla );
269}
270
[0da65d5]271void jabber_buddy_ask( struct im_connection *ic, char *handle )
[8e5e2e9]272{
273        struct jabber_buddy_ask_data *bla = g_new0( struct jabber_buddy_ask_data, 1 );
274        char *buf;
275       
[0da65d5]276        bla->ic = ic;
[8e5e2e9]277        bla->handle = g_strdup( handle );
278       
279        buf = g_strdup_printf( "The user %s wants to add you to his/her buddy list.", handle );
[84b045d]280        imcb_ask( ic, buf, bla, jabber_buddy_ask_yes, jabber_buddy_ask_no );
[6266fca]281        g_free( buf );
[8e5e2e9]282}
[6a1128d]283
[0d3f30f]284/* Returns a new string. Don't leak it! */
[e35d1a1]285char *jabber_normalize( const char *orig )
[0d3f30f]286{
287        int len, i;
288        char *new;
289       
290        len = strlen( orig );
291        new = g_new( char, len + 1 );
292        for( i = 0; i < len; i ++ )
[2ff2076]293        {
294                /* don't normalize the resource */
295                if( orig[i] == '/' )
296                        break;
[0d3f30f]297                new[i] = tolower( orig[i] );
[2ff2076]298        }
299        for( ; i < len; i ++ )
300                new[i] = orig[i];
[0d3f30f]301       
302        new[i] = 0;
303        return new;
304}
305
[6a1128d]306/* Adds a buddy/resource to our list. Returns NULL if full_jid is not really a
[0d3f30f]307   FULL jid or if we already have this buddy/resource. XXX: No, great, actually
308   buddies from transports don't (usually) have resources. So we'll really have
309   to deal with that properly. Set their ->resource property to NULL. Do *NOT*
310   allow to mix this stuff, though... */
[0da65d5]311struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid_ )
[6a1128d]312{
[0da65d5]313        struct jabber_data *jd = ic->proto_data;
[6a1128d]314        struct jabber_buddy *bud, *new, *bi;
[0d3f30f]315        char *s, *full_jid;
[6a1128d]316       
[0d3f30f]317        full_jid = jabber_normalize( full_jid_ );
318       
319        if( ( s = strchr( full_jid, '/' ) ) )
320                *s = 0;
[6a1128d]321       
322        new = g_new0( struct jabber_buddy, 1 );
323       
324        if( ( bud = g_hash_table_lookup( jd->buddies, full_jid ) ) )
325        {
[0d3f30f]326                /* If this is a transport buddy or whatever, it can't have more
327                   than one instance, so this is always wrong: */
328                if( s == NULL || bud->resource == NULL )
329                {
330                        if( s ) *s = '/';
331                        g_free( new );
332                        g_free( full_jid );
333                        return NULL;
334                }
335               
336                new->bare_jid = bud->bare_jid;
[6a1128d]337               
338                /* We already have another resource for this buddy, add the
339                   new one to the list. */
340                for( bi = bud; bi; bi = bi->next )
341                {
[0d3f30f]342                        /* Check for dupes. */
343                        if( g_strcasecmp( bi->resource, s + 1 ) == 0 )
[6a1128d]344                        {
345                                *s = '/';
346                                g_free( new );
[0d3f30f]347                                g_free( full_jid );
[6a1128d]348                                return NULL;
349                        }
350                        /* Append the new item to the list. */
351                        else if( bi->next == NULL )
352                        {
353                                bi->next = new;
354                                break;
355                        }
356                }
357        }
358        else
359        {
[b9f8b87]360                /* Keep in mind that full_jid currently isn't really
361                   a full JID... */
[0d3f30f]362                new->bare_jid = g_strdup( full_jid );
363                g_hash_table_insert( jd->buddies, new->bare_jid, new );
[6a1128d]364        }
365       
[0d3f30f]366        if( s )
367        {
368                *s = '/';
369                new->full_jid = full_jid;
370                new->resource = strchr( new->full_jid, '/' ) + 1;
371        }
372        else
373        {
374                /* Let's waste some more bytes of RAM instead of to make
[b9f8b87]375                   memory management a total disaster here. And it saves
376                   me one g_free() call in this function. :-P */
[0d3f30f]377                new->full_jid = full_jid;
378        }
[6a1128d]379       
380        return new;
381}
382
[788a1af]383/* Finds a buddy from our structures. Can find both full- and bare JIDs. When
384   asked for a bare JID, it uses the "resource_select" setting to see which
385   resource to pick. */
[0da65d5]386struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_, get_buddy_flags_t flags )
[6a1128d]387{
[0da65d5]388        struct jabber_data *jd = ic->proto_data;
[6a1128d]389        struct jabber_buddy *bud;
[0d3f30f]390        char *s, *jid;
391       
392        jid = jabber_normalize( jid_ );
[6a1128d]393       
394        if( ( s = strchr( jid, '/' ) ) )
395        {
[e35d1a1]396                int none_found = 0;
397               
[6a1128d]398                *s = 0;
399                if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )
[0d3f30f]400                {
[0adce21]401                        /* Just return the first one for this bare JID. */
402                        if( flags & GET_BUDDY_FIRST )
403                        {
404                                *s = '/';
405                                g_free( jid );
406                                return bud;
407                        }
408                       
[0d3f30f]409                        /* Is this one of those no-resource buddies? */
410                        if( bud->resource == NULL )
411                        {
[0adce21]412                                *s = '/';
[16b5f86]413                                g_free( jid );
414                                return NULL;
[0d3f30f]415                        }
[0adce21]416                       
417                        /* See if there's an exact match. */
418                        for( ; bud; bud = bud->next )
419                                if( g_strcasecmp( bud->resource, s + 1 ) == 0 )
420                                        break;
[0d3f30f]421                }
[e35d1a1]422                else
423                {
424                        /* This hack is there to make sure that O_CREAT will
425                           work if there's already another resouce present
426                           for this JID, even if it's an unknown buddy. This
427                           is done to handle conferences properly. */
428                        none_found = 1;
[0adce21]429                        /* TODO(wilmer): Find out what I was thinking when I
430                           wrote this??? And then fix it. This makes me sad... */
[e35d1a1]431                }
[0d3f30f]432               
[e35d1a1]433                if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && ( imcb_find_buddy( ic, jid ) || !none_found ) )
[16b5f86]434                {
435                        *s = '/';
[0da65d5]436                        bud = jabber_buddy_add( ic, jid );
[16b5f86]437                }
[0d3f30f]438               
439                g_free( jid );
440                return bud;
[6a1128d]441        }
442        else
443        {
[a21a8ac]444                struct jabber_buddy *best_prio, *best_time;
445                char *set;
446               
[0d3f30f]447                bud = g_hash_table_lookup( jd->buddies, jid );
448               
449                g_free( jid );
450               
451                if( bud == NULL )
[16b5f86]452                        /* No match. Create it now? */
[f0cb961]453                        return ( ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid_ ) ) ?
[0da65d5]454                                   jabber_buddy_add( ic, jid_ ) : NULL;
[16b5f86]455                else if( bud->resource && ( flags & GET_BUDDY_EXACT ) )
456                        /* We want an exact match, so in thise case there shouldn't be a /resource. */
457                        return NULL;
[0d3f30f]458                else if( ( bud->resource == NULL || bud->next == NULL ) )
[16b5f86]459                        /* No need for selection if there's only one option. */
[0d3f30f]460                        return bud;
[0adce21]461                else if( flags & GET_BUDDY_FIRST )
462                        /* Looks like the caller doesn't care about details. */
463                        return bud;
[0d3f30f]464               
465                best_prio = best_time = bud;
[a21a8ac]466                for( ; bud; bud = bud->next )
467                {
468                        if( bud->priority > best_prio->priority )
469                                best_prio = bud;
470                        if( bud->last_act > best_time->last_act )
471                                best_time = bud;
472                }
473               
[0da65d5]474                if( ( set = set_getstr( &ic->acc->set, "resource_select" ) ) == NULL )
[a21a8ac]475                        return NULL;
476                else if( strcmp( set, "activity" ) == 0 )
477                        return best_time;
478                else /* if( strcmp( set, "priority" ) == 0 ) */
479                        return best_prio;
[6a1128d]480        }
481}
482
[b9f8b87]483/* I'm keeping a separate ext_jid attribute to save a JID that makes sense
484   to export to BitlBee. This is mainly for groupchats right now. It's
485   a bit of a hack, but I just think having the user nickname in the hostname
486   part of the hostmask doesn't look nice on IRC. Normally you can convert
487   a normal JID to ext_jid by swapping the part before and after the / and
488   replacing the / with a =. But there should be some stripping (@s are
489   allowed in Jabber nicks...). */
490struct jabber_buddy *jabber_buddy_by_ext_jid( struct im_connection *ic, char *jid_, get_buddy_flags_t flags )
491{
492        struct jabber_buddy *bud;
493        char *s, *jid;
494       
495        jid = jabber_normalize( jid_ );
496       
497        if( ( s = strchr( jid, '=' ) ) == NULL )
498                return NULL;
499       
500        for( bud = jabber_buddy_by_jid( ic, s + 1, GET_BUDDY_FIRST ); bud; bud = bud->next )
501        {
502                /* Hmmm, could happen if not all people in the chat are anonymized? */
503                if( bud->ext_jid == NULL )
504                        continue;
505               
506                if( strcmp( bud->ext_jid, jid ) == 0 )
507                        break;
508        }
509       
510        g_free( jid );
511       
512        return bud;
513}
514
[788a1af]515/* Remove one specific full JID from our list. Use this when a buddy goes
[0d3f30f]516   off-line (because (s)he can still be online from a different location.
517   XXX: See above, we should accept bare JIDs too... */
[0da65d5]518int jabber_buddy_remove( struct im_connection *ic, char *full_jid_ )
[6a1128d]519{
[0da65d5]520        struct jabber_data *jd = ic->proto_data;
[6a1128d]521        struct jabber_buddy *bud, *prev, *bi;
[0d3f30f]522        char *s, *full_jid;
[6a1128d]523       
[0d3f30f]524        full_jid = jabber_normalize( full_jid_ );
525       
526        if( ( s = strchr( full_jid, '/' ) ) )
527                *s = 0;
[6a1128d]528       
529        if( ( bud = g_hash_table_lookup( jd->buddies, full_jid ) ) )
530        {
531                /* If there's only one item in the list (and if the resource
532                   matches), removing it is simple. (And the hash reference
533                   should be removed too!) */
[0d3f30f]534                if( bud->next == NULL && ( ( s == NULL || bud->resource == NULL ) || g_strcasecmp( bud->resource, s + 1 ) == 0 ) )
[6a1128d]535                {
[0d3f30f]536                        g_hash_table_remove( jd->buddies, bud->bare_jid );
537                        g_free( bud->bare_jid );
[6286f80]538                        g_free( bud->ext_jid );
[a21a8ac]539                        g_free( bud->full_jid );
[6a1128d]540                        g_free( bud->away_message );
541                        g_free( bud );
[0d3f30f]542                       
543                        g_free( full_jid );
544                       
545                        return 1;
546                }
547                else if( s == NULL || bud->resource == NULL )
548                {
549                        /* Tried to remove a bare JID while this JID does seem
550                           to have resources... (Or the opposite.) *sigh* */
551                        g_free( full_jid );
552                        return 0;
[6a1128d]553                }
554                else
555                {
556                        for( bi = bud, prev = NULL; bi; bi = (prev=bi)->next )
[0d3f30f]557                                if( g_strcasecmp( bi->resource, s + 1 ) == 0 )
[6a1128d]558                                        break;
559                       
[0d3f30f]560                        g_free( full_jid );
561                       
[6a1128d]562                        if( bi )
563                        {
564                                if( prev )
565                                        prev->next = bi->next;
566                                else
567                                        /* The hash table should point at the second
568                                           item, because we're removing the first. */
[0d3f30f]569                                        g_hash_table_replace( jd->buddies, bi->bare_jid, bi->next );
[6a1128d]570                               
[6286f80]571                                g_free( bi->ext_jid );
[a21a8ac]572                                g_free( bi->full_jid );
[6a1128d]573                                g_free( bi->away_message );
574                                g_free( bi );
[0d3f30f]575                               
576                                return 1;
[6a1128d]577                        }
578                        else
579                        {
580                                return 0;
581                        }
582                }
583        }
584        else
585        {
[0d3f30f]586                g_free( full_jid );
[6a1128d]587                return 0;
588        }
589}
[788a1af]590
591/* Remove a buddy completely; removes all resources that belong to the
592   specified bare JID. Use this when removing someone from the contact
593   list, for example. */
[9da0bbf]594int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid )
[788a1af]595{
[0da65d5]596        struct jabber_data *jd = ic->proto_data;
[788a1af]597        struct jabber_buddy *bud, *next;
598       
[9da0bbf]599        if( strchr( bare_jid, '/' ) )
[788a1af]600                return 0;
601       
[9da0bbf]602        if( ( bud = jabber_buddy_by_jid( ic, bare_jid, GET_BUDDY_FIRST ) ) )
[788a1af]603        {
604                /* Most important: Remove the hash reference. We don't know
605                   this buddy anymore. */
[0d3f30f]606                g_hash_table_remove( jd->buddies, bud->bare_jid );
[9da0bbf]607                g_free( bud->bare_jid );
[788a1af]608               
609                /* Deallocate the linked list of resources. */
610                while( bud )
611                {
[9da0bbf]612                        /* ext_jid && anonymous means that this buddy is
613                           specific to one groupchat (the one we're
614                           currently cleaning up) so it can be deleted
615                           completely. */
616                        if( bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS )
617                                imcb_remove_buddy( ic, bud->ext_jid, NULL );
618                       
[788a1af]619                        next = bud->next;
[6286f80]620                        g_free( bud->ext_jid );
[788a1af]621                        g_free( bud->full_jid );
622                        g_free( bud->away_message );
623                        g_free( bud );
624                        bud = next;
625                }
626               
627                return 1;
628        }
629        else
630        {
631                return 0;
632        }
633}
[e35d1a1]634
[43671b9]635time_t jabber_get_timestamp( struct xt_node *xt )
636{
637        struct tm tp, utc;
638        struct xt_node *c;
639        time_t res, tres;
640        char *s = NULL;
641       
642        for( c = xt->children; ( c = xt_find_node( c, "x" ) ); c = c->next )
643        {
644                if( ( s = xt_find_attr( c, "xmlns" ) ) && strcmp( s, XMLNS_DELAY ) == 0 )
645                        break;
646        }
647       
648        if( !c || !( s = xt_find_attr( c, "stamp" ) ) )
649                return 0;
650       
651        memset( &tp, 0, sizeof( tp ) );
652        if( sscanf( s, "%4d%2d%2dT%2d:%2d:%2d", &tp.tm_year, &tp.tm_mon, &tp.tm_mday,
653                                                &tp.tm_hour, &tp.tm_min, &tp.tm_sec ) != 6 )
654                return 0;
655       
656        tp.tm_year -= 1900;
657        tp.tm_mon --;
658        tp.tm_isdst = -1; /* GRRRRRRRRRRR */
659       
660        res = mktime( &tp );
661        /* Problem is, mktime() just gave us the GMT timestamp for the
662           given local time... While the given time WAS NOT local. So
663           we should fix this now.
664       
665           Now I could choose between messing with environment variables
666           (kludgy) or using timegm() (not portable)... Or doing the
667           following, which I actually prefer... */
668        gmtime_r( &res, &utc );
669        utc.tm_isdst = -1; /* Once more: GRRRRRRRRRRRRRRRRRR!!! */
670        if( utc.tm_hour == tp.tm_hour && utc.tm_min == tp.tm_min )
671                /* Sweet! We're in UTC right now... */
672                return res;
673       
674        tres = mktime( &utc );
675        res += res - tres;
676       
677        /* Yes, this is a hack. And it will go wrong around DST changes.
678           BUT this is more likely to be threadsafe than messing with
679           environment variables, and possibly more portable... */
680       
681        return res;
682}
[1baaef8]683
684struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns )
685{
[5bd21df]686        struct jabber_error *err;
[1baaef8]687        struct xt_node *c;
688        char *s;
689       
[5bd21df]690        if( node == NULL )
691                return NULL;
692       
693        err = g_new0( struct jabber_error, 1 );
[1baaef8]694        err->type = xt_find_attr( node, "type" );
695       
696        for( c = node->children; c; c = c->next )
697        {
698                if( !( s = xt_find_attr( c, "xmlns" ) ) ||
699                    strcmp( s, xmlns ) != 0 )
700                        continue;
701               
702                if( strcmp( c->name, "text" ) != 0 )
703                {
704                        err->code = c->name;
705                }
706                /* Only use the text if it doesn't have an xml:lang attribute,
707                   if it's empty or if it's set to something English. */
708                else if( !( s = xt_find_attr( c, "xml:lang" ) ) ||
709                         !*s || strncmp( s, "en", 2 ) == 0 )
710                {
711                        err->text = c->text;
712                }
713        }
714       
715        return err;
716}
717
718void jabber_error_free( struct jabber_error *err )
719{
720        g_free( err );
721}
Note: See TracBrowser for help on using the repository browser.