aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/jabber/jabber_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/jabber/jabber_util.c')
-rw-r--r--protocols/jabber/jabber_util.c178
1 files changed, 165 insertions, 13 deletions
diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c
index 3c0e71f4..56491c4f 100644
--- a/protocols/jabber/jabber_util.c
+++ b/protocols/jabber/jabber_util.c
@@ -47,7 +47,7 @@ char *set_eval_priority( set_t *set, char *value )
convenient, they have one disadvantage: If I would just
call p_s_u() now to send the new prio setting, it would
send the old setting because the set->value gets changed
- when the eval returns a non-NULL value.
+ after the (this) eval returns a non-NULL value.
So now I can choose between implementing post-set
functions next to evals, or just do this little hack: */
@@ -128,7 +128,7 @@ struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond,
/* Cache a node/packet for later use. Mainly useful for IQ packets if you need
them when you receive the response. Use this BEFORE sending the packet so
- it'll get a new id= tag, and do NOT free() the packet after writing it! */
+ it'll get a new id= tag, and do NOT free() the packet after sending it! */
void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func )
{
struct jabber_data *jd = ic->proto_data;
@@ -251,7 +251,7 @@ void jabber_buddy_ask( struct im_connection *ic, char *handle )
}
/* Returns a new string. Don't leak it! */
-char *jabber_normalize( char *orig )
+char *jabber_normalize( const char *orig )
{
int len, i;
char *new;
@@ -319,6 +319,8 @@ struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid_
}
else
{
+ /* Keep in mind that full_jid currently isn't really
+ a full JID... */
new->bare_jid = g_strdup( full_jid );
g_hash_table_insert( jd->buddies, new->bare_jid, new );
}
@@ -332,7 +334,8 @@ struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid_
else
{
/* Let's waste some more bytes of RAM instead of to make
- memory management a total disaster here.. */
+ memory management a total disaster here. And it saves
+ me one g_free() call in this function. :-P */
new->full_jid = full_jid;
}
@@ -352,6 +355,8 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,
if( ( s = strchr( jid, '/' ) ) )
{
+ int none_found = 0;
+
*s = 0;
if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )
{
@@ -369,8 +374,16 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,
break;
}
}
+ else
+ {
+ /* This hack is there to make sure that O_CREAT will
+ work if there's already another resouce present
+ for this JID, even if it's an unknown buddy. This
+ is done to handle conferences properly. */
+ none_found = 1;
+ }
- if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid ) )
+ if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && ( imcb_find_buddy( ic, jid ) || !none_found ) )
{
*s = '/';
bud = jabber_buddy_add( ic, jid );
@@ -417,6 +430,38 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,
}
}
+/* I'm keeping a separate ext_jid attribute to save a JID that makes sense
+ to export to BitlBee. This is mainly for groupchats right now. It's
+ a bit of a hack, but I just think having the user nickname in the hostname
+ part of the hostmask doesn't look nice on IRC. Normally you can convert
+ a normal JID to ext_jid by swapping the part before and after the / and
+ replacing the / with a =. But there should be some stripping (@s are
+ allowed in Jabber nicks...). */
+struct jabber_buddy *jabber_buddy_by_ext_jid( struct im_connection *ic, char *jid_, get_buddy_flags_t flags )
+{
+ struct jabber_buddy *bud;
+ char *s, *jid;
+
+ jid = jabber_normalize( jid_ );
+
+ if( ( s = strchr( jid, '=' ) ) == NULL )
+ return NULL;
+
+ for( bud = jabber_buddy_by_jid( ic, s + 1, GET_BUDDY_FIRST ); bud; bud = bud->next )
+ {
+ /* Hmmm, could happen if not all people in the chat are anonymized? */
+ if( bud->ext_jid == NULL )
+ continue;
+
+ if( strcmp( bud->ext_jid, jid ) == 0 )
+ break;
+ }
+
+ g_free( jid );
+
+ return bud;
+}
+
/* Remove one specific full JID from our list. Use this when a buddy goes
off-line (because (s)he can still be online from a different location.
XXX: See above, we should accept bare JIDs too... */
@@ -440,6 +485,7 @@ int jabber_buddy_remove( struct im_connection *ic, char *full_jid_ )
{
g_hash_table_remove( jd->buddies, bud->bare_jid );
g_free( bud->bare_jid );
+ g_free( bud->ext_jid );
g_free( bud->full_jid );
g_free( bud->away_message );
g_free( bud );
@@ -472,6 +518,7 @@ int jabber_buddy_remove( struct im_connection *ic, char *full_jid_ )
item, because we're removing the first. */
g_hash_table_replace( jd->buddies, bi->bare_jid, bi->next );
+ g_free( bi->ext_jid );
g_free( bi->full_jid );
g_free( bi->away_message );
g_free( bi );
@@ -494,39 +541,144 @@ int jabber_buddy_remove( struct im_connection *ic, char *full_jid_ )
/* Remove a buddy completely; removes all resources that belong to the
specified bare JID. Use this when removing someone from the contact
list, for example. */
-int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid_ )
+int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid )
{
struct jabber_data *jd = ic->proto_data;
struct jabber_buddy *bud, *next;
- char *bare_jid;
- if( strchr( bare_jid_, '/' ) )
+ if( strchr( bare_jid, '/' ) )
return 0;
- bare_jid = jabber_normalize( bare_jid_ );
-
- if( ( bud = g_hash_table_lookup( jd->buddies, bare_jid ) ) )
+ if( ( bud = jabber_buddy_by_jid( ic, bare_jid, GET_BUDDY_FIRST ) ) )
{
/* Most important: Remove the hash reference. We don't know
this buddy anymore. */
g_hash_table_remove( jd->buddies, bud->bare_jid );
+ g_free( bud->bare_jid );
/* Deallocate the linked list of resources. */
while( bud )
{
+ /* ext_jid && anonymous means that this buddy is
+ specific to one groupchat (the one we're
+ currently cleaning up) so it can be deleted
+ completely. */
+ if( bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS )
+ imcb_remove_buddy( ic, bud->ext_jid, NULL );
+
next = bud->next;
+ g_free( bud->ext_jid );
g_free( bud->full_jid );
g_free( bud->away_message );
g_free( bud );
bud = next;
}
- g_free( bare_jid );
return 1;
}
else
{
- g_free( bare_jid );
return 0;
}
}
+
+struct groupchat *jabber_chat_by_name( struct im_connection *ic, const char *name )
+{
+ char *normalized = jabber_normalize( name );
+ struct groupchat *ret;
+ struct jabber_chat *jc;
+
+ for( ret = ic->groupchats; ret; ret = ret->next )
+ {
+ jc = ret->data;
+ if( strcmp( normalized, jc->name ) == 0 )
+ break;
+ }
+ g_free( normalized );
+
+ return ret;
+}
+
+time_t jabber_get_timestamp( struct xt_node *xt )
+{
+ struct tm tp, utc;
+ struct xt_node *c;
+ time_t res, tres;
+ char *s = NULL;
+
+ for( c = xt->children; ( c = xt_find_node( c, "x" ) ); c = c->next )
+ {
+ if( ( s = xt_find_attr( c, "xmlns" ) ) && strcmp( s, XMLNS_DELAY ) == 0 )
+ break;
+ }
+
+ if( !c || !( s = xt_find_attr( c, "stamp" ) ) )
+ return 0;
+
+ memset( &tp, 0, sizeof( tp ) );
+ if( sscanf( s, "%4d%2d%2dT%2d:%2d:%2d", &tp.tm_year, &tp.tm_mon, &tp.tm_mday,
+ &tp.tm_hour, &tp.tm_min, &tp.tm_sec ) != 6 )
+ return 0;
+
+ tp.tm_year -= 1900;
+ tp.tm_mon --;
+ tp.tm_isdst = -1; /* GRRRRRRRRRRR */
+
+ res = mktime( &tp );
+ /* Problem is, mktime() just gave us the GMT timestamp for the
+ given local time... While the given time WAS NOT local. So
+ we should fix this now.
+
+ Now I could choose between messing with environment variables
+ (kludgy) or using timegm() (not portable)... Or doing the
+ following, which I actually prefer... */
+ gmtime_r( &res, &utc );
+ utc.tm_isdst = -1; /* Once more: GRRRRRRRRRRRRRRRRRR!!! */
+ if( utc.tm_hour == tp.tm_hour && utc.tm_min == tp.tm_min )
+ /* Sweet! We're in UTC right now... */
+ return res;
+
+ tres = mktime( &utc );
+ res += res - tres;
+
+ /* Yes, this is a hack. And it will go wrong around DST changes.
+ BUT this is more likely to be threadsafe than messing with
+ environment variables, and possibly more portable... */
+
+ return res;
+}
+
+struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns )
+{
+ struct jabber_error *err = g_new0( struct jabber_error, 1 );
+ struct xt_node *c;
+ char *s;
+
+ err->type = xt_find_attr( node, "type" );
+
+ for( c = node->children; c; c = c->next )
+ {
+ if( !( s = xt_find_attr( c, "xmlns" ) ) ||
+ strcmp( s, xmlns ) != 0 )
+ continue;
+
+ if( strcmp( c->name, "text" ) != 0 )
+ {
+ err->code = c->name;
+ }
+ /* Only use the text if it doesn't have an xml:lang attribute,
+ if it's empty or if it's set to something English. */
+ else if( !( s = xt_find_attr( c, "xml:lang" ) ) ||
+ !*s || strncmp( s, "en", 2 ) == 0 )
+ {
+ err->text = c->text;
+ }
+ }
+
+ return err;
+}
+
+void jabber_error_free( struct jabber_error *err )
+{
+ g_free( err );
+}