Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision f0cb961652cbd639e89dcd88a86f20a2414146c4)
+++ protocols/jabber/jabber_util.c	(revision 1baaef858136cd3a4799b3ccf1d9961534e0017c)
@@ -48,5 +48,5 @@
 		   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
@@ -129,5 +129,5 @@
 /* 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 )
 {
@@ -252,5 +252,5 @@
 
 /* Returns a new string. Don't leak it! */
-char *jabber_normalize( char *orig )
+char *jabber_normalize( const char *orig )
 {
 	int len, i;
@@ -320,4 +320,6 @@
 	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 );
@@ -333,5 +335,6 @@
 	{
 		/* 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;
 	}
@@ -353,4 +356,6 @@
 	if( ( s = strchr( jid, '/' ) ) )
 	{
+		int none_found = 0;
+		
 		*s = 0;
 		if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )
@@ -370,6 +375,14 @@
 			}
 		}
-		
-		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid ) )
+		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 ) || !none_found ) )
 		{
 			*s = '/';
@@ -418,4 +431,36 @@
 }
 
+/* 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.
@@ -441,4 +486,5 @@
 			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 );
@@ -473,4 +519,5 @@
 					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 );
@@ -495,25 +542,31 @@
    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 );
@@ -522,11 +575,110 @@
 		}
 		
-		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 );
+}
