Index: irc.c
===================================================================
--- irc.c	(revision e277e80022e9cad3f7a3dbadbc25a6a2da9bf40d)
+++ irc.c	(revision c6088915e5628bca1de0e5464f54d9d8915e8984)
@@ -35,4 +35,5 @@
 static char *set_eval_password( set_t *set, char *value );
 static char *set_eval_bw_compat( set_t *set, char *value );
+static char *set_eval_utf8_nicks( set_t *set, char *value );
 
 irc_t *irc_new( int fd )
@@ -134,4 +135,5 @@
 	s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc );
 	s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc );
+	s = set_add( &b->set, "utf8_nicks", "false", set_eval_utf8_nicks, irc );
 
 	irc->root = iu = irc_user_new( irc, ROOT_NICK );
@@ -962,4 +964,21 @@
 }
 
+static char *set_eval_utf8_nicks( set_t *set, char *value )
+{
+	irc_t *irc = set->data;
+	gboolean val = bool2int( value );
+	
+	/* Do *NOT* unset this flag in the middle of a session. There will
+	   be UTF-8 nicks around already so if we suddenly disable support
+	   for them, various functions might behave strangely. */
+	if( val )
+		irc->status |= IRC_UTF8_NICKS;
+	else if( irc->status & IRC_UTF8_NICKS )
+		irc_rootmsg( irc, "You need to reconnect to BitlBee for this "
+		                  "change to take effect." );
+	
+	return set_eval_bool( set, value );
+}
+
 void register_irc_plugin( const struct irc_plugin *p )
 {
Index: irc.h
===================================================================
--- irc.h	(revision e277e80022e9cad3f7a3dbadbc25a6a2da9bf40d)
+++ irc.h	(revision c6088915e5628bca1de0e5464f54d9d8915e8984)
@@ -62,4 +62,6 @@
 	OPER_HACK_ACCOUNT_ADD = 0x400,
 	OPER_HACK_ANY = 0x3700,	/* To check for them all at once. */
+	
+	IRC_UTF8_NICKS = 0x10000, /* Disable ASCII restrictions on buddy nicks. */
 } irc_status_t;
 
Index: nick.c
===================================================================
--- nick.c	(revision e277e80022e9cad3f7a3dbadbc25a6a2da9bf40d)
+++ nick.c	(revision c6088915e5628bca1de0e5464f54d9d8915e8984)
@@ -112,5 +112,7 @@
 {
 	gboolean ok = FALSE; /* Set to true once the nick contains something unique. */
-	GString *ret = g_string_new( "" );
+	GString *ret = g_string_sized_new( MAX_NICK_LENGTH + 1 );
+	char *rets;
+	irc_t *irc = (irc_t *) bu->bee->ui_data;
 	char *fmt = set_getstr( &bu->ic->acc->set, "nick_format" ) ? :
 	            set_getstr( &bu->bee->set, "nick_format" );
@@ -119,5 +121,4 @@
 	{
 		char *part = NULL, chop = '\0', *asc = NULL;
-		int len = MAX_NICK_LENGTH;
 		
 		if( *fmt != '%' )
@@ -142,11 +143,4 @@
 				fmt += 2;
 			}
-			else if( isdigit( *fmt ) )
-			{
-				len = 0;
-				/* Grab a number. */
-				while( isdigit( *fmt ) )
-					len = len * 10 + ( *(fmt++) - '0' );
-			}
 			else if( g_strncasecmp( fmt, "nick", 4 ) == 0 )
 			{
@@ -197,29 +191,29 @@
 		}
 		
+		if( !part )
+			continue;
+		
 		/* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT
 		   should do lossy/approximate conversions, so letters with
 		   accents don't just get stripped. Note that it depends on
 		   LC_CTYPE being set to something other than C/POSIX. */
-		if( part )
+		if( !( irc && irc->status & IRC_UTF8_NICKS ) )
 			part = asc = g_convert_with_fallback( part, -1, "ASCII//TRANSLIT",
 			                                      "UTF-8", "", NULL, NULL, NULL );
 		
-		if( ret->len == 0 && part && isdigit( *part ) )
-			g_string_append_c( ret, '_' );
-		
-		while( part && *part && *part != chop && len > 0 )
-		{
-			if( strchr( nick_lc_chars, *part ) ||
-			    strchr( nick_uc_chars, *part ) )
-				g_string_append_c( ret, *part );
-			
-			part ++;
-			len --;
-		}
+		if( part )
+			g_string_append( ret, part );
 		g_free( asc );
 	}
 	
-	/* This returns NULL if the nick is empty or otherwise not ok. */
-	return g_string_free( ret, ret->len == 0 || !ok );
+	rets = g_string_free( ret, FALSE );
+	if( ok && rets && *rets )
+	{
+		nick_strip( irc, rets );
+		rets[MAX_NICK_LENGTH] = '\0';
+		return rets;
+	}
+	g_free( rets );
+	return NULL;
 }
 
@@ -247,21 +241,12 @@
 		if( inf_protection-- == 0 )
 		{
-			int i;
+			g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() );
 			
-			irc_rootmsg( irc, "Warning: Almost had an infinite loop in nick_get()! "
-			                  "This used to be a fatal BitlBee bug, but we tried to fix it. "
-			                  "This message should *never* appear anymore. "
-			                  "If it does, please *do* send us a bug report! "
-			                  "Please send all the following lines in your report:" );
-			
-			irc_rootmsg( irc, "Trying to get a sane nick for handle %s", bu->handle );
-			for( i = 0; i < MAX_NICK_LENGTH; i ++ )
-				irc_rootmsg( irc, "Char %d: %c/%d", i, nick[i], nick[i] );
-			
-			irc_rootmsg( irc, "FAILED. Returning an insane nick now. Things might break. "
-			                  "Good luck, and please don't forget to paste the lines up here "
-			                  "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" );
-			
-			g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() );
+			irc_rootmsg( irc, "Warning: Something went wrong while trying "
+			                  "to generate a nickname for contact %s on %s.",
+			                  bu->handle, bu->ic->acc->tag );
+			irc_rootmsg( irc, "This might be a bug in BitlBee, or the result "
+			                  "of a faulty nick_format setting. Will use %s "
+			                  "instead.", nick );
 			
 			break;
@@ -291,13 +276,41 @@
 void nick_strip( irc_t *irc, char *nick )
 {
-	int i, j;
-	
-	for( i = j = 0; nick[i] && j < MAX_NICK_LENGTH; i++ )
-	{
-		if( strchr( nick_lc_chars, nick[i] ) || 
-		    strchr( nick_uc_chars, nick[i] ) )
-		{
-			nick[j] = nick[i];
-			j++;
+	int len = 0;
+	
+	if( irc && ( irc->status & IRC_UTF8_NICKS ) )
+	{
+		gunichar c;
+		char *p = nick, *n, tmp[strlen(nick)+1];
+		
+		while( p && *p )
+		{
+			c = g_utf8_get_char_validated( p, -1 );
+			n = g_utf8_find_next_char( p, NULL );
+			
+			if( ( c < 0x7f && !( strchr( nick_lc_chars, c ) ||
+			                     strchr( nick_uc_chars, c ) ) ) ||
+			    !g_unichar_isgraph( c ) )
+			{
+				strcpy( tmp, n );
+				strcpy( p, tmp );
+			}
+			else
+				p = n;
+		}
+		if( p )
+			len = p - nick;
+	}
+	else
+	{
+		int i;
+		
+		for( i = len = 0; nick[i] && len < MAX_NICK_LENGTH; i++ )
+		{
+			if( strchr( nick_lc_chars, nick[i] ) || 
+			    strchr( nick_uc_chars, nick[i] ) )
+			{
+				nick[len] = nick[i];
+				len++;
+			}
 		}
 	}
@@ -306,14 +319,16 @@
 		char *orig;
 		
+		/* First character of a nick can't be a digit, so insert an
+		   underscore if necessary. */
 		orig = g_strdup( nick );
 		g_snprintf( nick, MAX_NICK_LENGTH, "_%s", orig );
 		g_free( orig );
-		j ++;
-	}
-	while( j <= MAX_NICK_LENGTH )
-		nick[j++] = '\0';
-}
-
-int nick_ok( irc_t *irc, const char *nick )
+		len ++;
+	}
+	while( len <= MAX_NICK_LENGTH )
+		nick[len++] = '\0';
+}
+
+gboolean nick_ok( irc_t *irc, const char *nick )
 {
 	const char *s;
@@ -321,11 +336,33 @@
 	/* Empty/long nicks are not allowed, nor numbers at [0] */
 	if( !*nick || isdigit( nick[0] ) || strlen( nick ) > MAX_NICK_LENGTH )
-		return( 0 );
-	
-	for( s = nick; *s; s ++ )
-		if( !strchr( nick_lc_chars, *s ) && !strchr( nick_uc_chars, *s ) )
-			return( 0 );
-	
-	return( 1 );
+		return 0;
+	
+	if( irc && ( irc->status & IRC_UTF8_NICKS ) )
+	{
+		gunichar c;
+		const char *p = nick, *n;
+		
+		while( p && *p )
+		{
+			c = g_utf8_get_char_validated( p, -1 );
+			n = g_utf8_find_next_char( p, NULL );
+			
+			if( ( c < 0x7f && !( strchr( nick_lc_chars, c ) ||
+			                     strchr( nick_uc_chars, c ) ) ) ||
+			    !g_unichar_isgraph( c ) )
+			{
+				return FALSE;
+			}
+			p = n;
+		}
+	}
+	else
+	{
+		for( s = nick; *s; s ++ )
+			if( !strchr( nick_lc_chars, *s ) && !strchr( nick_uc_chars, *s ) )
+				return FALSE;
+	}
+	
+	return TRUE;
 }
 
@@ -342,36 +379,20 @@
 		}
 	
+	if( irc && ( irc->status & IRC_UTF8_NICKS ) )
+	{
+		gchar *down = g_utf8_strdown( nick, -1 );
+		if( strlen( down ) > strlen( nick ) )
+		{
+			/* Well crap. Corrupt it if we have to. */
+			down[strlen(nick)] = '\0';
+		}
+		strcpy( nick, down );
+		g_free( down );
+	}
+	
 	for( i = 0; nick[i]; i ++ )
-	{
-		if( !tab[(int)nick[i]] )
-			return( 0 );
-		
 		nick[i] = tab[(int)nick[i]];
-	}
-	
-	return( 1 );
-}
-
-int nick_uc( irc_t *irc, char *nick )
-{
-	static char tab[128] = { 0 };
-	int i;
-	
-	if( tab['A'] == 0 )
-		for( i = 0; nick_lc_chars[i]; i ++ )
-		{
-			tab[(int)nick_uc_chars[i]] = nick_uc_chars[i];
-			tab[(int)nick_lc_chars[i]] = nick_uc_chars[i];
-		}
-	
-	for( i = 0; nick[i]; i ++ )
-	{
-		if( !tab[(int)nick[i]] )
-			return( 0 );
-		
-		nick[i] = tab[(int)nick[i]];
-	}
-	
-	return( 1 );
+	
+	return nick_ok( irc, nick );
 }
 
Index: nick.h
===================================================================
--- nick.h	(revision e277e80022e9cad3f7a3dbadbc25a6a2da9bf40d)
+++ nick.h	(revision c6088915e5628bca1de0e5464f54d9d8915e8984)
@@ -33,5 +33,5 @@
 
 void nick_strip( irc_t *irc, char *nick );
-int nick_ok( irc_t *irc, const char *nick );
+gboolean nick_ok( irc_t *irc, const char *nick );
 int nick_lc( irc_t *irc, char *nick );
 int nick_uc( irc_t *irc, char *nick );
