source: nick.c @ eb4ad8d

Last change on this file since eb4ad8d was eb4ad8d, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-01-17T20:13:19Z

Merging random other fixes/cleanups.

  • Property mode set to 100644
File size: 10.5 KB
RevLine 
[b7d3cc34]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
[b7d3cc34]5  \********************************************************************/
6
7/* Some stuff to fetch, save and handle nicknames for your buddies      */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
[6f10697]22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
[b7d3cc34]24*/
25
26#define BITLBEE_CORE
27#include "bitlbee.h"
28
[2e0eaac]29/* Character maps, _lc_[x] == _uc_[x] (but uppercase), according to the RFC's.
30   With one difference, we allow dashes. These are used to do uc/lc conversions
31   and strip invalid chars. */
32static char *nick_lc_chars = "0123456789abcdefghijklmnopqrstuvwxyz{}^`-_|";
33static char *nick_uc_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]~`-_\\";
34
[5b52a48]35/* Store handles in lower case and strip spaces, because AIM is braindead. */
36static char *clean_handle( const char *orig )
[b7d3cc34]37{
[5b52a48]38        char *new = g_malloc( strlen( orig ) + 1 );
39        int i = 0;
[b7d3cc34]40       
[5b52a48]41        do {
42                if (*orig != ' ')
[6b13103]43                        new[i++] = g_ascii_tolower( *orig );
[b7d3cc34]44        }
[5b52a48]45        while (*(orig++));
[b7d3cc34]46       
[5b52a48]47        return new;
48}
49
[b1f818b]50void nick_set_raw( account_t *acc, const char *handle, const char *nick )
[5b52a48]51{
52        char *store_handle, *store_nick = g_malloc( MAX_NICK_LENGTH + 1 );
[e277e80]53        irc_t *irc = (irc_t *) acc->bee->ui_data;
[b7d3cc34]54       
[5b52a48]55        store_handle = clean_handle( handle );
[b1f818b]56        store_nick[MAX_NICK_LENGTH] = '\0';
[5b52a48]57        strncpy( store_nick, nick, MAX_NICK_LENGTH );
[e277e80]58        nick_strip( irc, store_nick );
[b7d3cc34]59       
[5b52a48]60        g_hash_table_replace( acc->nicks, store_handle, store_nick );
[b7d3cc34]61}
62
[b1f818b]63void nick_set( bee_user_t *bu, const char *nick )
64{
65        nick_set_raw( bu->ic->acc, bu->handle, nick );
66}
67
68char *nick_get( bee_user_t *bu )
[b7d3cc34]69{
70        static char nick[MAX_NICK_LENGTH+1];
[5b52a48]71        char *store_handle, *found_nick;
[e277e80]72        irc_t *irc = (irc_t *) bu->bee->ui_data;
[b7d3cc34]73       
74        memset( nick, 0, MAX_NICK_LENGTH + 1 );
75       
[b1f818b]76        store_handle = clean_handle( bu->handle );
[5b52a48]77        /* Find out if we stored a nick for this person already. If not, try
78           to generate a sane nick automatically. */
[b1f818b]79        if( ( found_nick = g_hash_table_lookup( bu->ic->acc->nicks, store_handle ) ) )
[5b52a48]80        {
81                strncpy( nick, found_nick, MAX_NICK_LENGTH );
82        }
[2e0eaac]83        else if( ( found_nick = nick_gen( bu ) ) )
84        {
85                strncpy( nick, found_nick, MAX_NICK_LENGTH );
86                g_free( found_nick );
87        }
[5b52a48]88        else
[b7d3cc34]89        {
[2e0eaac]90                /* Keep this fallback since nick_gen() can return NULL in some cases. */
[b7d3cc34]91                char *s;
92               
[b1f818b]93                g_snprintf( nick, MAX_NICK_LENGTH, "%s", bu->handle );
[b7d3cc34]94                if( ( s = strchr( nick, '@' ) ) )
95                        while( *s )
96                                *(s++) = 0;
97               
[e277e80]98                nick_strip( irc, nick );
[b1f818b]99                if( set_getbool( &bu->bee->set, "lcnicks" ) )
[e277e80]100                        nick_lc( irc, nick );
[b7d3cc34]101        }
[5b52a48]102        g_free( store_handle );
[b7d3cc34]103       
[d06eabf]104        /* Make sure the nick doesn't collide with an existing one by adding
105           underscores and that kind of stuff, if necessary. */
[b1f818b]106        nick_dedupe( bu, nick );
[d06eabf]107       
108        return nick;
109}
110
[b1f818b]111char *nick_gen( bee_user_t *bu )
[d06eabf]112{
[2e0eaac]113        gboolean ok = FALSE; /* Set to true once the nick contains something unique. */
[c608891]114        GString *ret = g_string_sized_new( MAX_NICK_LENGTH + 1 );
115        char *rets;
116        irc_t *irc = (irc_t *) bu->bee->ui_data;
[2e0eaac]117        char *fmt = set_getstr( &bu->ic->acc->set, "nick_format" ) ? :
118                    set_getstr( &bu->bee->set, "nick_format" );
119       
120        while( fmt && *fmt && ret->len < MAX_NICK_LENGTH )
121        {
[7e84168]122                char *part = NULL, chop = '\0', *asc = NULL, *s;
123                int len = INT_MAX;
[2e0eaac]124               
125                if( *fmt != '%' )
126                {
127                        g_string_append_c( ret, *fmt );
128                        fmt ++;
129                        continue;
130                }
131               
132                fmt ++;
133                while( *fmt )
134                {
135                        /* -char means chop off everything from char */
136                        if( *fmt == '-' )
137                        {
138                                chop = fmt[1];
139                                if( chop == '\0' )
[9ed0081]140                                {
141                                        g_string_free( ret, TRUE );
[2e0eaac]142                                        return NULL;
[9ed0081]143                                }
[2e0eaac]144                                fmt += 2;
145                        }
[6b13103]146                        else if( g_ascii_isdigit( *fmt ) )
[7e84168]147                        {
148                                len = 0;
149                                /* Grab a number. */
[6b13103]150                                while( g_ascii_isdigit( *fmt ) )
[7e84168]151                                        len = len * 10 + ( *(fmt++) - '0' );
152                        }
[09dfb68]153                        else if( g_strncasecmp( fmt, "nick", 4 ) == 0 )
154                        {
155                                part = bu->nick ? : bu->handle;
156                                fmt += 4;
157                                ok |= TRUE;
158                                break;
159                        }
[2e0eaac]160                        else if( g_strncasecmp( fmt, "handle", 6 ) == 0 )
161                        {
162                                part = bu->handle;
163                                fmt += 6;
164                                ok |= TRUE;
165                                break;
166                        }
167                        else if( g_strncasecmp( fmt, "full_name", 9 ) == 0 )
168                        {
169                                part = bu->fullname;
170                                fmt += 9;
171                                ok |= part && *part;
172                                break;
173                        }
174                        else if( g_strncasecmp( fmt, "first_name", 10 ) == 0 )
175                        {
176                                part = bu->fullname;
177                                fmt += 10;
178                                ok |= part && *part;
179                                chop = ' ';
180                                break;
181                        }
[09dfb68]182                        else if( g_strncasecmp( fmt, "group", 5 ) == 0 )
183                        {
184                                part = bu->group ? bu->group->name : NULL;
185                                fmt += 5;
186                                break;
187                        }
[3b3c50d9]188                        else if( g_strncasecmp( fmt, "account", 7 ) == 0 )
189                        {
190                                part = bu->ic->acc->tag;
191                                fmt += 7;
192                                break;
193                        }
[2e0eaac]194                        else
195                        {
[9ed0081]196                                g_string_free( ret, TRUE );
[2e0eaac]197                                return NULL;
198                        }
199                }
200               
[c608891]201                if( !part )
202                        continue;
203               
[badd148]204                /* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT
205                   should do lossy/approximate conversions, so letters with
206                   accents don't just get stripped. Note that it depends on
207                   LC_CTYPE being set to something other than C/POSIX. */
[c608891]208                if( !( irc && irc->status & IRC_UTF8_NICKS ) )
[560129a]209                        part = asc = g_convert_with_fallback( part, -1, "ASCII//TRANSLIT",
210                                                              "UTF-8", "", NULL, NULL, NULL );
[badd148]211               
[cce1b63]212                if( part && chop && ( s = strchr( part, chop ) ) )
[7e84168]213                        len = MIN( len, s - part );
214               
[c608891]215                if( part )
[7e84168]216                {
217                        if( len < INT_MAX )
218                                g_string_append_len( ret, part, len );
219                        else
220                                g_string_append( ret, part );
221                }
[badd148]222                g_free( asc );
[2e0eaac]223        }
224       
[c608891]225        rets = g_string_free( ret, FALSE );
226        if( ok && rets && *rets )
227        {
228                nick_strip( irc, rets );
[fed4f76]229                truncate_utf8( rets, MAX_NICK_LENGTH );
[c608891]230                return rets;
231        }
232        g_free( rets );
233        return NULL;
[b1f818b]234}
235
236void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] )
237{
238        irc_t *irc = (irc_t*) bu->bee->ui_data;
[d06eabf]239        int inf_protection = 256;
[badd148]240        irc_user_t *iu;
[d06eabf]241       
[5b52a48]242        /* Now, find out if the nick is already in use at the moment, and make
243           subtle changes to make it unique. */
[e277e80]244        while( !nick_ok( irc, nick ) ||
[badd148]245               ( ( iu = irc_user_by_name( irc, nick ) ) && iu->bu != bu ) )
[b7d3cc34]246        {
247                if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) )
248                {
249                        nick[strlen(nick)+1] = 0;
250                        nick[strlen(nick)] = '_';
251                }
252                else
253                {
[fed4f76]254                        /* We've got no more space for underscores,
255                           so truncate it and replace the last three
256                           chars with a random "_XX" suffix */
257                        int len = truncate_utf8( nick, MAX_NICK_LENGTH - 3 );
258                        nick[len] = '_';
259                        g_snprintf(nick + len + 1, 3, "%2x", rand() );
[b7d3cc34]260                }
261               
262                if( inf_protection-- == 0 )
263                {
264                        g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() );
265                       
[c608891]266                        irc_rootmsg( irc, "Warning: Something went wrong while trying "
267                                          "to generate a nickname for contact %s on %s.",
268                                          bu->handle, bu->ic->acc->tag );
269                        irc_rootmsg( irc, "This might be a bug in BitlBee, or the result "
270                                          "of a faulty nick_format setting. Will use %s "
271                                          "instead.", nick );
272                       
[b7d3cc34]273                        break;
274                }
275        }
276}
277
[d323394c]278/* Just check if there is a nickname set for this buddy or if we'd have to
279   generate one. */
[b1f818b]280int nick_saved( bee_user_t *bu )
[d323394c]281{
282        char *store_handle, *found;
283       
[b1f818b]284        store_handle = clean_handle( bu->handle );
285        found = g_hash_table_lookup( bu->ic->acc->nicks, store_handle );
[d323394c]286        g_free( store_handle );
287       
288        return found != NULL;
289}
290
[b1f818b]291void nick_del( bee_user_t *bu )
[b7d3cc34]292{
[b1f818b]293        g_hash_table_remove( bu->ic->acc->nicks, bu->handle );
[b7d3cc34]294}
295
296
[e277e80]297void nick_strip( irc_t *irc, char *nick )
[b7d3cc34]298{
[c608891]299        int len = 0;
[b7d3cc34]300       
[c608891]301        if( irc && ( irc->status & IRC_UTF8_NICKS ) )
[b7d3cc34]302        {
[c608891]303                gunichar c;
304                char *p = nick, *n, tmp[strlen(nick)+1];
305               
306                while( p && *p )
[b7d3cc34]307                {
[c608891]308                        c = g_utf8_get_char_validated( p, -1 );
309                        n = g_utf8_find_next_char( p, NULL );
310                       
311                        if( ( c < 0x7f && !( strchr( nick_lc_chars, c ) ||
312                                             strchr( nick_uc_chars, c ) ) ) ||
313                            !g_unichar_isgraph( c ) )
314                        {
315                                strcpy( tmp, n );
316                                strcpy( p, tmp );
317                        }
318                        else
319                                p = n;
320                }
321                if( p )
322                        len = p - nick;
323        }
324        else
325        {
326                int i;
327               
328                for( i = len = 0; nick[i] && len < MAX_NICK_LENGTH; i++ )
329                {
330                        if( strchr( nick_lc_chars, nick[i] ) || 
331                            strchr( nick_uc_chars, nick[i] ) )
332                        {
333                                nick[len] = nick[i];
334                                len++;
335                        }
[b7d3cc34]336                }
337        }
[6b13103]338        if( g_ascii_isdigit( nick[0] ) )
[0f47613]339        {
340                char *orig;
341               
[c608891]342                /* First character of a nick can't be a digit, so insert an
343                   underscore if necessary. */
[0f47613]344                orig = g_strdup( nick );
345                g_snprintf( nick, MAX_NICK_LENGTH, "_%s", orig );
346                g_free( orig );
[c608891]347                len ++;
[0f47613]348        }
[c608891]349        while( len <= MAX_NICK_LENGTH )
350                nick[len++] = '\0';
[b7d3cc34]351}
352
[c608891]353gboolean nick_ok( irc_t *irc, const char *nick )
[b7d3cc34]354{
[1eddf6b]355        const char *s;
[b7d3cc34]356       
[0f47613]357        /* Empty/long nicks are not allowed, nor numbers at [0] */
[6b13103]358        if( !*nick || g_ascii_isdigit( nick[0] ) || strlen( nick ) > MAX_NICK_LENGTH )
[c608891]359                return 0;
[b7d3cc34]360       
[c608891]361        if( irc && ( irc->status & IRC_UTF8_NICKS ) )
362        {
363                gunichar c;
364                const char *p = nick, *n;
365               
366                while( p && *p )
367                {
368                        c = g_utf8_get_char_validated( p, -1 );
369                        n = g_utf8_find_next_char( p, NULL );
370                       
371                        if( ( c < 0x7f && !( strchr( nick_lc_chars, c ) ||
372                                             strchr( nick_uc_chars, c ) ) ) ||
373                            !g_unichar_isgraph( c ) )
374                        {
375                                return FALSE;
376                        }
377                        p = n;
378                }
379        }
380        else
381        {
382                for( s = nick; *s; s ++ )
383                        if( !strchr( nick_lc_chars, *s ) && !strchr( nick_uc_chars, *s ) )
384                                return FALSE;
385        }
[b7d3cc34]386       
[c608891]387        return TRUE;
[b7d3cc34]388}
389
[e277e80]390int nick_lc( irc_t *irc, char *nick )
[b7d3cc34]391{
[d323394c]392        static char tab[128] = { 0 };
[b7d3cc34]393        int i;
394       
395        if( tab['A'] == 0 )
396                for( i = 0; nick_lc_chars[i]; i ++ )
397                {
398                        tab[(int)nick_uc_chars[i]] = nick_lc_chars[i];
399                        tab[(int)nick_lc_chars[i]] = nick_lc_chars[i];
400                }
401       
[c608891]402        if( irc && ( irc->status & IRC_UTF8_NICKS ) )
[b7d3cc34]403        {
[c608891]404                gchar *down = g_utf8_strdown( nick, -1 );
405                if( strlen( down ) > strlen( nick ) )
[b7d3cc34]406                {
[eb4ad8d]407                        truncate_utf8( down, strlen( nick ) );
[b7d3cc34]408                }
[c608891]409                strcpy( nick, down );
410                g_free( down );
411        }
[b7d3cc34]412       
413        for( i = 0; nick[i]; i ++ )
[46511b3]414                if( ((guchar)nick[i]) < 0x7f )
415                        nick[i] = tab[(guchar)nick[i]];
[b7d3cc34]416       
[c608891]417        return nick_ok( irc, nick );
[b7d3cc34]418}
419
[e277e80]420int nick_cmp( irc_t *irc, const char *a, const char *b )
[b7d3cc34]421{
422        char aa[1024] = "", bb[1024] = "";
423       
424        strncpy( aa, a, sizeof( aa ) - 1 );
425        strncpy( bb, b, sizeof( bb ) - 1 );
[e277e80]426        if( nick_lc( irc, aa ) && nick_lc( irc, bb ) )
[b7d3cc34]427        {
428                return( strcmp( aa, bb ) );
429        }
430        else
431        {
432                return( -1 );   /* Hmm... Not a clear answer.. :-/ */
433        }
434}
Note: See TracBrowser for help on using the repository browser.