source: nick.c @ 9ed0081

Last change on this file since 9ed0081 was 9ed0081, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-11-25T18:39:11Z

Fixed one potential memory leak, and re-remembered that for GLib + valgrind
you REALLY need to set G_SLICE=always-malloc. Argh!

  • Property mode set to 100644
File size: 9.6 KB
RevLine 
[b7d3cc34]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[b1f818b]4  * Copyright 2002-2010 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;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
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 != ' ')
43                        new[i++] = 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 );
[b7d3cc34]53       
[5b52a48]54        store_handle = clean_handle( handle );
[b1f818b]55        store_nick[MAX_NICK_LENGTH] = '\0';
[5b52a48]56        strncpy( store_nick, nick, MAX_NICK_LENGTH );
57        nick_strip( store_nick );
[b7d3cc34]58       
[5b52a48]59        g_hash_table_replace( acc->nicks, store_handle, store_nick );
[b7d3cc34]60}
61
[b1f818b]62void nick_set( bee_user_t *bu, const char *nick )
63{
64        nick_set_raw( bu->ic->acc, bu->handle, nick );
65}
66
67char *nick_get( bee_user_t *bu )
[b7d3cc34]68{
69        static char nick[MAX_NICK_LENGTH+1];
[5b52a48]70        char *store_handle, *found_nick;
[b7d3cc34]71       
72        memset( nick, 0, MAX_NICK_LENGTH + 1 );
73       
[b1f818b]74        store_handle = clean_handle( bu->handle );
[5b52a48]75        /* Find out if we stored a nick for this person already. If not, try
76           to generate a sane nick automatically. */
[b1f818b]77        if( ( found_nick = g_hash_table_lookup( bu->ic->acc->nicks, store_handle ) ) )
[5b52a48]78        {
79                strncpy( nick, found_nick, MAX_NICK_LENGTH );
80        }
[2e0eaac]81        else if( ( found_nick = nick_gen( bu ) ) )
82        {
83                strncpy( nick, found_nick, MAX_NICK_LENGTH );
84                g_free( found_nick );
85        }
[5b52a48]86        else
[b7d3cc34]87        {
[2e0eaac]88                /* Keep this fallback since nick_gen() can return NULL in some cases. */
[b7d3cc34]89                char *s;
90               
[b1f818b]91                g_snprintf( nick, MAX_NICK_LENGTH, "%s", bu->handle );
[b7d3cc34]92                if( ( s = strchr( nick, '@' ) ) )
93                        while( *s )
94                                *(s++) = 0;
95               
96                nick_strip( nick );
[b1f818b]97                if( set_getbool( &bu->bee->set, "lcnicks" ) )
[b7d3cc34]98                        nick_lc( nick );
99        }
[5b52a48]100        g_free( store_handle );
[b7d3cc34]101       
[d06eabf]102        /* Make sure the nick doesn't collide with an existing one by adding
103           underscores and that kind of stuff, if necessary. */
[b1f818b]104        nick_dedupe( bu, nick );
[d06eabf]105       
106        return nick;
107}
108
[b1f818b]109char *nick_gen( bee_user_t *bu )
[d06eabf]110{
[2e0eaac]111        gboolean ok = FALSE; /* Set to true once the nick contains something unique. */
112        GString *ret = g_string_new( "" );
113        char *fmt = set_getstr( &bu->ic->acc->set, "nick_format" ) ? :
114                    set_getstr( &bu->bee->set, "nick_format" );
115       
116        while( fmt && *fmt && ret->len < MAX_NICK_LENGTH )
117        {
[f7d12f7]118                char *part = NULL, chop = '\0', *asc = NULL;
[6f0ea57]119                int len = MAX_NICK_LENGTH;
[2e0eaac]120               
121                if( *fmt != '%' )
122                {
123                        g_string_append_c( ret, *fmt );
124                        fmt ++;
125                        continue;
126                }
127               
128                fmt ++;
129                while( *fmt )
130                {
131                        /* -char means chop off everything from char */
132                        if( *fmt == '-' )
133                        {
134                                chop = fmt[1];
135                                if( chop == '\0' )
[9ed0081]136                                {
137                                        g_string_free( ret, TRUE );
[2e0eaac]138                                        return NULL;
[9ed0081]139                                }
[2e0eaac]140                                fmt += 2;
141                        }
[6f0ea57]142                        else if( isdigit( *fmt ) )
143                        {
144                                len = 0;
145                                /* Grab a number. */
146                                while( isdigit( *fmt ) )
147                                        len = len * 10 + ( *(fmt++) - '0' );
148                        }
[09dfb68]149                        else if( g_strncasecmp( fmt, "nick", 4 ) == 0 )
150                        {
151                                part = bu->nick ? : bu->handle;
152                                fmt += 4;
153                                ok |= TRUE;
154                                break;
155                        }
[2e0eaac]156                        else if( g_strncasecmp( fmt, "handle", 6 ) == 0 )
157                        {
158                                part = bu->handle;
159                                fmt += 6;
160                                ok |= TRUE;
161                                break;
162                        }
163                        else if( g_strncasecmp( fmt, "full_name", 9 ) == 0 )
164                        {
165                                part = bu->fullname;
166                                fmt += 9;
167                                ok |= part && *part;
168                                break;
169                        }
170                        else if( g_strncasecmp( fmt, "first_name", 10 ) == 0 )
171                        {
172                                part = bu->fullname;
173                                fmt += 10;
174                                ok |= part && *part;
175                                chop = ' ';
176                                break;
177                        }
[09dfb68]178                        else if( g_strncasecmp( fmt, "group", 5 ) == 0 )
179                        {
180                                part = bu->group ? bu->group->name : NULL;
181                                fmt += 5;
182                                break;
183                        }
[3b3c50d9]184                        else if( g_strncasecmp( fmt, "account", 7 ) == 0 )
185                        {
186                                part = bu->ic->acc->tag;
187                                fmt += 7;
188                                break;
189                        }
[2e0eaac]190                        else
191                        {
[9ed0081]192                                g_string_free( ret, TRUE );
[2e0eaac]193                                return NULL;
194                        }
195                }
196               
[badd148]197                /* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT
198                   should do lossy/approximate conversions, so letters with
199                   accents don't just get stripped. Note that it depends on
200                   LC_CTYPE being set to something other than C/POSIX. */
201                if( part )
[560129a]202                        part = asc = g_convert_with_fallback( part, -1, "ASCII//TRANSLIT",
203                                                              "UTF-8", "", NULL, NULL, NULL );
[badd148]204               
[6ef19f7]205                if( ret->len == 0 && part && isdigit( *part ) )
[177ffd7]206                        g_string_append_c( ret, '_' );
207               
[6f0ea57]208                while( part && *part && *part != chop && len > 0 )
[2e0eaac]209                {
210                        if( strchr( nick_lc_chars, *part ) ||
211                            strchr( nick_uc_chars, *part ) )
212                                g_string_append_c( ret, *part );
213                       
214                        part ++;
[6f0ea57]215                        len --;
[2e0eaac]216                }
[badd148]217                g_free( asc );
[2e0eaac]218        }
219       
220        /* This returns NULL if the nick is empty or otherwise not ok. */
221        return g_string_free( ret, ret->len == 0 || !ok );
[b1f818b]222}
223
224void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] )
225{
226        irc_t *irc = (irc_t*) bu->bee->ui_data;
[d06eabf]227        int inf_protection = 256;
[badd148]228        irc_user_t *iu;
[d06eabf]229       
[5b52a48]230        /* Now, find out if the nick is already in use at the moment, and make
231           subtle changes to make it unique. */
[badd148]232        while( !nick_ok( nick ) ||
233               ( ( iu = irc_user_by_name( irc, nick ) ) && iu->bu != bu ) )
[b7d3cc34]234        {
235                if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) )
236                {
237                        nick[strlen(nick)+1] = 0;
238                        nick[strlen(nick)] = '_';
239                }
240                else
241                {
242                        nick[0] ++;
243                }
244               
245                if( inf_protection-- == 0 )
246                {
247                        int i;
248                       
[e67e513]249                        irc_rootmsg( irc, "Warning: Almost had an infinite loop in nick_get()! "
[81e04e1]250                                          "This used to be a fatal BitlBee bug, but we tried to fix it. "
251                                          "This message should *never* appear anymore. "
252                                          "If it does, please *do* send us a bug report! "
253                                          "Please send all the following lines in your report:" );
[b7d3cc34]254                       
[e67e513]255                        irc_rootmsg( irc, "Trying to get a sane nick for handle %s", bu->handle );
[b7d3cc34]256                        for( i = 0; i < MAX_NICK_LENGTH; i ++ )
[e67e513]257                                irc_rootmsg( irc, "Char %d: %c/%d", i, nick[i], nick[i] );
[b7d3cc34]258                       
[e67e513]259                        irc_rootmsg( irc, "FAILED. Returning an insane nick now. Things might break. "
[81e04e1]260                                          "Good luck, and please don't forget to paste the lines up here "
261                                          "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" );
[b7d3cc34]262                       
263                        g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() );
264                       
265                        break;
266                }
267        }
268}
269
[d323394c]270/* Just check if there is a nickname set for this buddy or if we'd have to
271   generate one. */
[b1f818b]272int nick_saved( bee_user_t *bu )
[d323394c]273{
274        char *store_handle, *found;
275       
[b1f818b]276        store_handle = clean_handle( bu->handle );
277        found = g_hash_table_lookup( bu->ic->acc->nicks, store_handle );
[d323394c]278        g_free( store_handle );
279       
280        return found != NULL;
281}
282
[b1f818b]283void nick_del( bee_user_t *bu )
[b7d3cc34]284{
[b1f818b]285        g_hash_table_remove( bu->ic->acc->nicks, bu->handle );
[b7d3cc34]286}
287
288
[0f47613]289void nick_strip( char *nick )
[b7d3cc34]290{
291        int i, j;
292       
[1fc2958]293        for( i = j = 0; nick[i] && j < MAX_NICK_LENGTH; i++ )
[b7d3cc34]294        {
295                if( strchr( nick_lc_chars, nick[i] ) || 
296                    strchr( nick_uc_chars, nick[i] ) )
297                {
298                        nick[j] = nick[i];
299                        j++;
300                }
301        }
[0f47613]302        if( isdigit( nick[0] ) )
303        {
304                char *orig;
305               
306                orig = g_strdup( nick );
307                g_snprintf( nick, MAX_NICK_LENGTH, "_%s", orig );
308                g_free( orig );
309                j ++;
310        }
[1fc2958]311        while( j <= MAX_NICK_LENGTH )
[b7d3cc34]312                nick[j++] = '\0';
313}
314
[1eddf6b]315int nick_ok( const char *nick )
[b7d3cc34]316{
[1eddf6b]317        const char *s;
[b7d3cc34]318       
[0f47613]319        /* Empty/long nicks are not allowed, nor numbers at [0] */
320        if( !*nick || isdigit( nick[0] ) || strlen( nick ) > MAX_NICK_LENGTH )
[b7d3cc34]321                return( 0 );
322       
323        for( s = nick; *s; s ++ )
324                if( !strchr( nick_lc_chars, *s ) && !strchr( nick_uc_chars, *s ) )
325                        return( 0 );
326       
327        return( 1 );
328}
329
330int nick_lc( char *nick )
331{
[d323394c]332        static char tab[128] = { 0 };
[b7d3cc34]333        int i;
334       
335        if( tab['A'] == 0 )
336                for( i = 0; nick_lc_chars[i]; i ++ )
337                {
338                        tab[(int)nick_uc_chars[i]] = nick_lc_chars[i];
339                        tab[(int)nick_lc_chars[i]] = nick_lc_chars[i];
340                }
341       
342        for( i = 0; nick[i]; i ++ )
343        {
344                if( !tab[(int)nick[i]] )
345                        return( 0 );
346               
347                nick[i] = tab[(int)nick[i]];
348        }
349       
350        return( 1 );
351}
352
353int nick_uc( char *nick )
354{
355        static char tab[128] = { 0 };
356        int i;
357       
358        if( tab['A'] == 0 )
359                for( i = 0; nick_lc_chars[i]; i ++ )
360                {
361                        tab[(int)nick_uc_chars[i]] = nick_uc_chars[i];
362                        tab[(int)nick_lc_chars[i]] = nick_uc_chars[i];
363                }
364       
365        for( i = 0; nick[i]; i ++ )
366        {
367                if( !tab[(int)nick[i]] )
368                        return( 0 );
369               
370                nick[i] = tab[(int)nick[i]];
371        }
372       
373        return( 1 );
374}
375
[1eddf6b]376int nick_cmp( const char *a, const char *b )
[b7d3cc34]377{
378        char aa[1024] = "", bb[1024] = "";
379       
380        strncpy( aa, a, sizeof( aa ) - 1 );
381        strncpy( bb, b, sizeof( bb ) - 1 );
382        if( nick_lc( aa ) && nick_lc( bb ) )
383        {
384                return( strcmp( aa, bb ) );
385        }
386        else
387        {
388                return( -1 );   /* Hmm... Not a clear answer.. :-/ */
389        }
390}
391
[1eddf6b]392char *nick_dup( const char *nick )
[b7d3cc34]393{
[1eddf6b]394        return g_strndup( nick, MAX_NICK_LENGTH );
[b7d3cc34]395}
Note: See TracBrowser for help on using the repository browser.