source: nick.c @ 420ddc00

Last change on this file since 420ddc00 was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

  • Property mode set to 100644
File size: 9.6 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 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 = NULL, chop = '\0', *asc = NULL;
119                int len = MAX_NICK_LENGTH;
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' )
136                                {
137                                        g_string_free( ret, TRUE );
138                                        return NULL;
139                                }
140                                fmt += 2;
141                        }
142                        else if( isdigit( *fmt ) )
143                        {
144                                len = 0;
145                                /* Grab a number. */
146                                while( isdigit( *fmt ) )
147                                        len = len * 10 + ( *(fmt++) - '0' );
148                        }
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                        }
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                        }
178                        else if( g_strncasecmp( fmt, "group", 5 ) == 0 )
179                        {
180                                part = bu->group ? bu->group->name : NULL;
181                                fmt += 5;
182                                break;
183                        }
184                        else if( g_strncasecmp( fmt, "account", 7 ) == 0 )
185                        {
186                                part = bu->ic->acc->tag;
187                                fmt += 7;
188                                break;
189                        }
190                        else
191                        {
192                                g_string_free( ret, TRUE );
193                                return NULL;
194                        }
195                }
196               
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 )
202                        part = asc = g_convert_with_fallback( part, -1, "ASCII//TRANSLIT",
203                                                              "UTF-8", "", NULL, NULL, NULL );
204               
205                if( ret->len == 0 && part && isdigit( *part ) )
206                        g_string_append_c( ret, '_' );
207               
208                while( part && *part && *part != chop && len > 0 )
209                {
210                        if( strchr( nick_lc_chars, *part ) ||
211                            strchr( nick_uc_chars, *part ) )
212                                g_string_append_c( ret, *part );
213                       
214                        part ++;
215                        len --;
216                }
217                g_free( asc );
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 );
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;
227        int inf_protection = 256;
228        irc_user_t *iu;
229       
230        /* Now, find out if the nick is already in use at the moment, and make
231           subtle changes to make it unique. */
232        while( !nick_ok( nick ) ||
233               ( ( iu = irc_user_by_name( irc, nick ) ) && iu->bu != bu ) )
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                       
249                        irc_rootmsg( irc, "Warning: Almost had an infinite loop in nick_get()! "
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:" );
254                       
255                        irc_rootmsg( irc, "Trying to get a sane nick for handle %s", bu->handle );
256                        for( i = 0; i < MAX_NICK_LENGTH; i ++ )
257                                irc_rootmsg( irc, "Char %d: %c/%d", i, nick[i], nick[i] );
258                       
259                        irc_rootmsg( irc, "FAILED. Returning an insane nick now. Things might break. "
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" );
262                       
263                        g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() );
264                       
265                        break;
266                }
267        }
268}
269
270/* Just check if there is a nickname set for this buddy or if we'd have to
271   generate one. */
272int nick_saved( bee_user_t *bu )
273{
274        char *store_handle, *found;
275       
276        store_handle = clean_handle( bu->handle );
277        found = g_hash_table_lookup( bu->ic->acc->nicks, store_handle );
278        g_free( store_handle );
279       
280        return found != NULL;
281}
282
283void nick_del( bee_user_t *bu )
284{
285        g_hash_table_remove( bu->ic->acc->nicks, bu->handle );
286}
287
288
289void nick_strip( char *nick )
290{
291        int i, j;
292       
293        for( i = j = 0; nick[i] && j < MAX_NICK_LENGTH; i++ )
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        }
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        }
311        while( j <= MAX_NICK_LENGTH )
312                nick[j++] = '\0';
313}
314
315int nick_ok( const char *nick )
316{
317        const char *s;
318       
319        /* Empty/long nicks are not allowed, nor numbers at [0] */
320        if( !*nick || isdigit( nick[0] ) || strlen( nick ) > MAX_NICK_LENGTH )
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{
332        static char tab[128] = { 0 };
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
376int nick_cmp( const char *a, const char *b )
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
392char *nick_dup( const char *nick )
393{
394        return g_strndup( nick, MAX_NICK_LENGTH );
395}
Note: See TracBrowser for help on using the repository browser.