source: nick.c @ e277e80

Last change on this file since e277e80 was e277e80, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-04-20T22:50:31Z

Add irc_t* argument to all relevant nick_*() functions.

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