From e277e80022e9cad3f7a3dbadbc25a6a2da9bf40d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 20 Apr 2013 23:50:31 +0100 Subject: Add irc_t* argument to all relevant nick_*() functions. --- ipc.c | 4 ++-- irc_commands.c | 8 ++++---- irc_im.c | 2 +- irc_user.c | 6 +++--- nick.c | 27 ++++++++++++--------------- nick.h | 10 +++++----- root_commands.c | 4 ++-- storage_xml.c | 6 +++--- 8 files changed, 32 insertions(+), 35 deletions(-) diff --git a/ipc.c b/ipc.c index 8388214a..0356ad75 100644 --- a/ipc.c +++ b/ipc.c @@ -151,7 +151,7 @@ void ipc_master_cmd_identify( irc_t *data, char **cmd ) { old = l->data; if( child != old && - old->nick && nick_cmp( old->nick, child->nick ) == 0 && + old->nick && nick_cmp( NULL, old->nick, child->nick ) == 0 && old->password && strcmp( old->password, child->password ) == 0 ) break; } @@ -297,7 +297,7 @@ static void ipc_child_cmd_kill( irc_t *irc, char **cmd ) if( !( irc->status & USTATUS_LOGGED_IN ) ) return; - if( nick_cmp( cmd[1], irc->user->nick ) != 0 ) + if( nick_cmp( NULL, cmd[1], irc->user->nick ) != 0 ) return; /* It's not for us. */ irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->root->nick, irc->root->nick, irc->root->host, irc->user->nick, cmd[2] ); diff --git a/irc_commands.c b/irc_commands.c index 740d3eb2..3db243bf 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -79,7 +79,7 @@ static void irc_cmd_nick( irc_t *irc, char **cmd ) { irc_send_num( irc, 433, "%s :This nick is already in use", cmd[1] ); } - else if( !nick_ok( cmd[1] ) ) + else if( !nick_ok( NULL, cmd[1] ) ) { /* [SH] Invalid characters. */ irc_send_num( irc, 432, "%s :This nick contains invalid characters", cmd[1] ); @@ -290,7 +290,7 @@ static void irc_cmd_mode( irc_t *irc, char **cmd ) } else { - if( nick_cmp( cmd[1], irc->user->nick ) == 0 ) + if( nick_cmp( NULL, cmd[1], irc->user->nick ) == 0 ) { if( cmd[2] ) irc_umode_set( irc, cmd[2], 0 ); @@ -391,7 +391,7 @@ static void irc_cmd_notice( irc_t *irc, char **cmd ) /* At least for now just echo. IIRC some IRC clients use self-notices for lag checks, so try to support that. */ - if( nick_cmp( cmd[1], irc->user->nick ) == 0 ) + if( nick_cmp( NULL, cmd[1], irc->user->nick ) == 0 ) irc_send_msg( irc->user, "NOTICE", irc->user->nick, cmd[2], NULL ); else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) ) iu->f->privmsg( iu, cmd[2] ); @@ -592,7 +592,7 @@ static void irc_cmd_watch( irc_t *irc, char **cmd ) break; nick = g_strdup( cmd[i] + 1 ); - nick_lc( nick ); + nick_lc( irc, nick ); iu = irc_user_by_name( irc, nick ); diff --git a/irc_im.c b/irc_im.c index 6f221280..bc1ce91d 100644 --- a/irc_im.c +++ b/irc_im.c @@ -696,7 +696,7 @@ static gboolean bee_irc_chat_name_hint( bee_t *bee, struct groupchat *c, const c stripped[MAX_NICK_LENGTH] = '\0'; irc_channel_name_strip( stripped ); if( set_getbool( &bee->set, "lcnicks" ) ) - nick_lc( stripped ); + nick_lc( irc, stripped ); if( stripped[0] == '\0' ) return FALSE; diff --git a/irc_user.c b/irc_user.c index 8ff9243c..94dcf4aa 100644 --- a/irc_user.c +++ b/irc_user.c @@ -35,7 +35,7 @@ irc_user_t *irc_user_new( irc_t *irc, const char *nick ) iu->user = iu->host = iu->fullname = iu->nick; iu->key = g_strdup( nick ); - nick_lc( iu->key ); + nick_lc( irc, iu->key ); /* Using the hash table for speed and irc->users for easy iteration through the list (since the GLib API doesn't have anything sane for that.) */ @@ -106,7 +106,7 @@ irc_user_t *irc_user_by_name( irc_t *irc, const char *nick ) char key[strlen(nick)+1]; strcpy( key, nick ); - if( nick_lc( key ) ) + if( nick_lc( irc, key ) ) return g_hash_table_lookup( irc->nick_user_hash, key ); else return NULL; @@ -120,7 +120,7 @@ int irc_user_set_nick( irc_user_t *iu, const char *new ) GSList *cl; strcpy( key, new ); - if( iu == NULL || !nick_lc( key ) || + if( iu == NULL || !nick_lc( irc, key ) || ( ( new_iu = irc_user_by_name( irc, new ) ) && new_iu != iu ) ) return 0; diff --git a/nick.c b/nick.c index 8268d4a7..aa9466f9 100644 --- a/nick.c +++ b/nick.c @@ -50,11 +50,12 @@ static char *clean_handle( const char *orig ) void nick_set_raw( account_t *acc, const char *handle, const char *nick ) { char *store_handle, *store_nick = g_malloc( MAX_NICK_LENGTH + 1 ); + irc_t *irc = (irc_t *) acc->bee->ui_data; store_handle = clean_handle( handle ); store_nick[MAX_NICK_LENGTH] = '\0'; strncpy( store_nick, nick, MAX_NICK_LENGTH ); - nick_strip( store_nick ); + nick_strip( irc, store_nick ); g_hash_table_replace( acc->nicks, store_handle, store_nick ); } @@ -68,6 +69,7 @@ char *nick_get( bee_user_t *bu ) { static char nick[MAX_NICK_LENGTH+1]; char *store_handle, *found_nick; + irc_t *irc = (irc_t *) bu->bee->ui_data; memset( nick, 0, MAX_NICK_LENGTH + 1 ); @@ -93,9 +95,9 @@ char *nick_get( bee_user_t *bu ) while( *s ) *(s++) = 0; - nick_strip( nick ); + nick_strip( irc, nick ); if( set_getbool( &bu->bee->set, "lcnicks" ) ) - nick_lc( nick ); + nick_lc( irc, nick ); } g_free( store_handle ); @@ -229,7 +231,7 @@ void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] ) /* Now, find out if the nick is already in use at the moment, and make subtle changes to make it unique. */ - while( !nick_ok( nick ) || + while( !nick_ok( irc, nick ) || ( ( iu = irc_user_by_name( irc, nick ) ) && iu->bu != bu ) ) { if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) ) @@ -286,7 +288,7 @@ void nick_del( bee_user_t *bu ) } -void nick_strip( char *nick ) +void nick_strip( irc_t *irc, char *nick ) { int i, j; @@ -312,7 +314,7 @@ void nick_strip( char *nick ) nick[j++] = '\0'; } -int nick_ok( const char *nick ) +int nick_ok( irc_t *irc, const char *nick ) { const char *s; @@ -327,7 +329,7 @@ int nick_ok( const char *nick ) return( 1 ); } -int nick_lc( char *nick ) +int nick_lc( irc_t *irc, char *nick ) { static char tab[128] = { 0 }; int i; @@ -350,7 +352,7 @@ int nick_lc( char *nick ) return( 1 ); } -int nick_uc( char *nick ) +int nick_uc( irc_t *irc, char *nick ) { static char tab[128] = { 0 }; int i; @@ -373,13 +375,13 @@ int nick_uc( char *nick ) return( 1 ); } -int nick_cmp( const char *a, const char *b ) +int nick_cmp( irc_t *irc, const char *a, const char *b ) { char aa[1024] = "", bb[1024] = ""; strncpy( aa, a, sizeof( aa ) - 1 ); strncpy( bb, b, sizeof( bb ) - 1 ); - if( nick_lc( aa ) && nick_lc( bb ) ) + if( nick_lc( irc, aa ) && nick_lc( irc, bb ) ) { return( strcmp( aa, bb ) ); } @@ -388,8 +390,3 @@ int nick_cmp( const char *a, const char *b ) return( -1 ); /* Hmm... Not a clear answer.. :-/ */ } } - -char *nick_dup( const char *nick ) -{ - return g_strndup( nick, MAX_NICK_LENGTH ); -} diff --git a/nick.h b/nick.h index e1b506e1..47adbab5 100644 --- a/nick.h +++ b/nick.h @@ -30,10 +30,10 @@ char *nick_gen( bee_user_t *bu ); void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] ); int nick_saved( bee_user_t *bu ); void nick_del( bee_user_t *bu ); -void nick_strip( char *nick ); -int nick_ok( const char *nick ); -int nick_lc( char *nick ); -int nick_uc( char *nick ); -int nick_cmp( const char *a, const char *b ); +void nick_strip( irc_t *irc, char *nick ); +int 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 ); char *nick_dup( const char *nick ); diff --git a/root_commands.c b/root_commands.c index 77f40060..0bd16163 100644 --- a/root_commands.c +++ b/root_commands.c @@ -701,7 +701,7 @@ static void cmd_add( irc_t *irc, char **cmd ) if( cmd[3] ) { - if( !nick_ok( cmd[3] ) ) + if( !nick_ok( irc, cmd[3] ) ) { irc_rootmsg( irc, "The requested nick `%s' is invalid", cmd[3] ); return; @@ -843,7 +843,7 @@ static void cmd_rename( irc_t *irc, char **cmd ) { irc_rootmsg( irc, "Use /nick to change your own nickname" ); } - else if( !nick_ok( cmd[2] ) ) + else if( !nick_ok( irc, cmd[2] ) ) { irc_rootmsg( irc, "Nick `%s' is invalid", cmd[2] ); } diff --git a/storage_xml.c b/storage_xml.c index 1f2d4a51..3ee8ae1d 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -180,7 +180,7 @@ static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const ch xd->irc = irc; strncpy( xd->given_nick, my_nick, MAX_NICK_LENGTH ); xd->given_nick[MAX_NICK_LENGTH] = '\0'; - nick_lc( xd->given_nick ); + nick_lc( NULL, xd->given_nick ); xd->given_pass = (char*) password; fn = g_strconcat( global.conf->configdir, xd->given_nick, ".xml", NULL ); @@ -367,7 +367,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) int fd; path2 = g_strdup( irc->user->nick ); - nick_lc( path2 ); + nick_lc( NULL, path2 ); g_snprintf( path, sizeof( path ) - 20, "%s%s%s", global.conf->configdir, path2, ".xml" ); g_free( path2 ); @@ -423,7 +423,7 @@ static storage_status_t xml_remove( const char *nick, const char *password ) return status; lc = g_strdup( nick ); - nick_lc( lc ); + nick_lc( NULL, lc ); g_snprintf( s, 511, "%s%s%s", global.conf->configdir, lc, ".xml" ); g_free( lc ); -- cgit v1.2.3 From c6088915e5628bca1de0e5464f54d9d8915e8984 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 23 Apr 2013 11:20:06 -0500 Subject: Simple (and possibly still fragile) support for UTF-8 nicknames. --- irc.c | 19 +++++++ irc.h | 2 + nick.c | 181 ++++++++++++++++++++++++++++++++++++----------------------------- nick.h | 2 +- 4 files changed, 123 insertions(+), 81 deletions(-) diff --git a/irc.c b/irc.c index d590e63f..187004c7 100644 --- a/irc.c +++ b/irc.c @@ -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 ); diff --git a/irc.h b/irc.h index 404f5394..866e85c3 100644 --- a/irc.h +++ b/irc.h @@ -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; diff --git a/nick.c b/nick.c index aa9466f9..744a6131 100644 --- a/nick.c +++ b/nick.c @@ -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 ) diff --git a/nick.h b/nick.h index 47adbab5..20edb845 100644 --- a/nick.h +++ b/nick.h @@ -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 ); -- cgit v1.2.3 From 5cb946132871ef97fe9eabacafa62f1064d80423 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 23 Apr 2013 11:28:10 -0500 Subject: Documentation for utf8_nicks setting. --- doc/user-guide/commands.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 3af469c6..62e1e855 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -1619,6 +1619,20 @@ + + false + + + + Officially, IRC nicknames are restricted to ASCII. Recently some clients and servers started supporting Unicode nicknames though. To enable UTF-8 nickname support (contacts only) in BitlBee, enable this setting. + + + + To avoid confusing old clients, this setting is disabled by default. Be careful when you try it, and be prepared to be locked out of your BitlBee in case your client interacts poorly with UTF-8 nicknames. + + + + false -- cgit v1.2.3