source: nick.c @ 5e98ff0

Last change on this file since 5e98ff0 was 6ef19f7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-17T14:02:47Z

Fixed bug in r712. Check part, not *part.

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