diff options
author | Wilmer van der Gaast <wilmer@gaast.net> | 2013-04-23 11:20:06 -0500 |
---|---|---|
committer | Wilmer van der Gaast <wilmer@gaast.net> | 2013-04-23 11:20:06 -0500 |
commit | c6088915e5628bca1de0e5464f54d9d8915e8984 (patch) | |
tree | 1234862348cd50f35840b1a413c5ba337931946c | |
parent | e277e80022e9cad3f7a3dbadbc25a6a2da9bf40d (diff) |
Simple (and possibly still fragile) support for UTF-8 nicknames.
-rw-r--r-- | irc.c | 19 | ||||
-rw-r--r-- | irc.h | 2 | ||||
-rw-r--r-- | nick.c | 181 | ||||
-rw-r--r-- | nick.h | 2 |
4 files changed, 123 insertions, 81 deletions
@@ -34,6 +34,7 @@ static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond ); static char *set_eval_charset( set_t *set, char *value ); 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 ) { @@ -133,6 +134,7 @@ irc_t *irc_new( int fd ) s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc ); 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 ); iu->host = g_strdup( myhost ); @@ -961,6 +963,23 @@ static char *set_eval_bw_compat( set_t *set, char *value ) return SET_INVALID; } +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 ) { irc_plugins = g_slist_prepend( irc_plugins, (gpointer) p ); @@ -61,6 +61,8 @@ typedef enum OPER_HACK_REGISTER = 0x200, 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; struct irc_user; @@ -111,14 +111,15 @@ char *nick_get( bee_user_t *bu ) char *nick_gen( bee_user_t *bu ) { 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" ); while( fmt && *fmt && ret->len < MAX_NICK_LENGTH ) { char *part = NULL, chop = '\0', *asc = NULL; - int len = MAX_NICK_LENGTH; if( *fmt != '%' ) { @@ -141,13 +142,6 @@ char *nick_gen( bee_user_t *bu ) } 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 ) { part = bu->nick ? : bu->handle; @@ -196,31 +190,31 @@ char *nick_gen( bee_user_t *bu ) } } + 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; } void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] ) @@ -246,24 +240,15 @@ void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] ) if( inf_protection-- == 0 ) { - int i; - - 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; } } @@ -290,43 +275,95 @@ void nick_del( bee_user_t *bu ) void nick_strip( irc_t *irc, char *nick ) { - int i, j; + int len = 0; - for( i = j = 0; nick[i] && j < MAX_NICK_LENGTH; i++ ) + if( irc && ( irc->status & IRC_UTF8_NICKS ) ) { - if( strchr( nick_lc_chars, nick[i] ) || - strchr( nick_uc_chars, nick[i] ) ) + gunichar c; + char *p = nick, *n, tmp[strlen(nick)+1]; + + while( p && *p ) { - nick[j] = nick[i]; - j++; + 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++; + } } } if( isdigit( nick[0] ) ) { 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 ++; + len ++; } - while( j <= MAX_NICK_LENGTH ) - nick[j++] = '\0'; + while( len <= MAX_NICK_LENGTH ) + nick[len++] = '\0'; } -int nick_ok( irc_t *irc, const char *nick ) +gboolean nick_ok( irc_t *irc, const char *nick ) { const char *s; /* Empty/long nicks are not allowed, nor numbers at [0] */ if( !*nick || isdigit( nick[0] ) || strlen( nick ) > MAX_NICK_LENGTH ) - return( 0 ); + return 0; - for( s = nick; *s; s ++ ) - if( !strchr( nick_lc_chars, *s ) && !strchr( nick_uc_chars, *s ) ) - 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( 1 ); + return TRUE; } int nick_lc( irc_t *irc, char *nick ) @@ -341,38 +378,22 @@ int nick_lc( irc_t *irc, char *nick ) tab[(int)nick_lc_chars[i]] = nick_lc_chars[i]; } - for( i = 0; nick[i]; i ++ ) + if( irc && ( irc->status & IRC_UTF8_NICKS ) ) { - 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 ++ ) + gchar *down = g_utf8_strdown( nick, -1 ); + if( strlen( down ) > strlen( nick ) ) { - tab[(int)nick_uc_chars[i]] = nick_uc_chars[i]; - tab[(int)nick_lc_chars[i]] = nick_uc_chars[i]; + /* 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 ); + return nick_ok( irc, nick ); } int nick_cmp( irc_t *irc, const char *a, const char *b ) @@ -32,7 +32,7 @@ int nick_saved( bee_user_t *bu ); void nick_del( bee_user_t *bu ); 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 ); int nick_cmp( irc_t *irc, const char *a, const char *b ); |