Update .bzrignore.
[bitlbee.git] / nick.c
blob0d394ecb8fe59733a97ba7eae7121f64f73d1241
1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2002-2010 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* Some stuff to fetch, save and handle nicknames for your buddies */
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.
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.
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
26 #define BITLBEE_CORE
27 #include "bitlbee.h"
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. */
32 static char *nick_lc_chars = "0123456789abcdefghijklmnopqrstuvwxyz{}^`-_|";
33 static char *nick_uc_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]~`-_\\";
35 /* Store handles in lower case and strip spaces, because AIM is braindead. */
36 static char *clean_handle( const char *orig )
38 char *new = g_malloc( strlen( orig ) + 1 );
39 int i = 0;
41 do {
42 if (*orig != ' ')
43 new[i++] = tolower( *orig );
45 while (*(orig++));
47 return new;
50 void nick_set_raw( account_t *acc, const char *handle, const char *nick )
52 char *store_handle, *store_nick = g_malloc( MAX_NICK_LENGTH + 1 );
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 );
59 g_hash_table_replace( acc->nicks, store_handle, store_nick );
62 void nick_set( bee_user_t *bu, const char *nick )
64 nick_set_raw( bu->ic->acc, bu->handle, nick );
67 char *nick_get( bee_user_t *bu )
69 static char nick[MAX_NICK_LENGTH+1];
70 char *store_handle, *found_nick;
72 memset( nick, 0, MAX_NICK_LENGTH + 1 );
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 ) ) )
79 strncpy( nick, found_nick, MAX_NICK_LENGTH );
81 else if( ( found_nick = nick_gen( bu ) ) )
83 strncpy( nick, found_nick, MAX_NICK_LENGTH );
84 g_free( found_nick );
86 else
88 /* Keep this fallback since nick_gen() can return NULL in some cases. */
89 char *s;
91 g_snprintf( nick, MAX_NICK_LENGTH, "%s", bu->handle );
92 if( ( s = strchr( nick, '@' ) ) )
93 while( *s )
94 *(s++) = 0;
96 nick_strip( nick );
97 if( set_getbool( &bu->bee->set, "lcnicks" ) )
98 nick_lc( nick );
100 g_free( store_handle );
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 );
106 return nick;
109 char *nick_gen( bee_user_t *bu )
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" );
116 while( fmt && *fmt && ret->len < MAX_NICK_LENGTH )
118 char *part = NULL, chop = '\0', *asc = NULL;
119 int len = MAX_NICK_LENGTH;
121 if( *fmt != '%' )
123 g_string_append_c( ret, *fmt );
124 fmt ++;
125 continue;
128 fmt ++;
129 while( *fmt )
131 /* -char means chop off everything from char */
132 if( *fmt == '-' )
134 chop = fmt[1];
135 if( chop == '\0' )
136 return NULL;
137 fmt += 2;
139 else if( isdigit( *fmt ) )
141 len = 0;
142 /* Grab a number. */
143 while( isdigit( *fmt ) )
144 len = len * 10 + ( *(fmt++) - '0' );
146 else if( g_strncasecmp( fmt, "nick", 4 ) == 0 )
148 part = bu->nick ? : bu->handle;
149 fmt += 4;
150 ok |= TRUE;
151 break;
153 else if( g_strncasecmp( fmt, "handle", 6 ) == 0 )
155 part = bu->handle;
156 fmt += 6;
157 ok |= TRUE;
158 break;
160 else if( g_strncasecmp( fmt, "full_name", 9 ) == 0 )
162 part = bu->fullname;
163 fmt += 9;
164 ok |= part && *part;
165 break;
167 else if( g_strncasecmp( fmt, "first_name", 10 ) == 0 )
169 part = bu->fullname;
170 fmt += 10;
171 ok |= part && *part;
172 chop = ' ';
173 break;
175 else if( g_strncasecmp( fmt, "group", 5 ) == 0 )
177 part = bu->group ? bu->group->name : NULL;
178 fmt += 5;
179 break;
181 else if( g_strncasecmp( fmt, "account", 7 ) == 0 )
183 part = bu->ic->acc->tag;
184 fmt += 7;
185 break;
187 else
189 return NULL;
193 /* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT
194 should do lossy/approximate conversions, so letters with
195 accents don't just get stripped. Note that it depends on
196 LC_CTYPE being set to something other than C/POSIX. */
197 if( part )
198 part = asc = g_convert_with_fallback( part, -1, "ASCII//TRANSLIT",
199 "UTF-8", "", NULL, NULL, NULL );
201 if( ret->len == 0 && part && isdigit( *part ) )
202 g_string_append_c( ret, '_' );
204 while( part && *part && *part != chop && len > 0 )
206 if( strchr( nick_lc_chars, *part ) ||
207 strchr( nick_uc_chars, *part ) )
208 g_string_append_c( ret, *part );
210 part ++;
211 len --;
213 g_free( asc );
216 /* This returns NULL if the nick is empty or otherwise not ok. */
217 return g_string_free( ret, ret->len == 0 || !ok );
220 void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] )
222 irc_t *irc = (irc_t*) bu->bee->ui_data;
223 int inf_protection = 256;
224 irc_user_t *iu;
226 /* Now, find out if the nick is already in use at the moment, and make
227 subtle changes to make it unique. */
228 while( !nick_ok( nick ) ||
229 ( ( iu = irc_user_by_name( irc, nick ) ) && iu->bu != bu ) )
231 if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) )
233 nick[strlen(nick)+1] = 0;
234 nick[strlen(nick)] = '_';
236 else
238 nick[0] ++;
241 if( inf_protection-- == 0 )
243 int i;
245 irc_rootmsg( irc, "Warning: Almost had an infinite loop in nick_get()! "
246 "This used to be a fatal BitlBee bug, but we tried to fix it. "
247 "This message should *never* appear anymore. "
248 "If it does, please *do* send us a bug report! "
249 "Please send all the following lines in your report:" );
251 irc_rootmsg( irc, "Trying to get a sane nick for handle %s", bu->handle );
252 for( i = 0; i < MAX_NICK_LENGTH; i ++ )
253 irc_rootmsg( irc, "Char %d: %c/%d", i, nick[i], nick[i] );
255 irc_rootmsg( irc, "FAILED. Returning an insane nick now. Things might break. "
256 "Good luck, and please don't forget to paste the lines up here "
257 "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" );
259 g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() );
261 break;
266 /* Just check if there is a nickname set for this buddy or if we'd have to
267 generate one. */
268 int nick_saved( bee_user_t *bu )
270 char *store_handle, *found;
272 store_handle = clean_handle( bu->handle );
273 found = g_hash_table_lookup( bu->ic->acc->nicks, store_handle );
274 g_free( store_handle );
276 return found != NULL;
279 void nick_del( bee_user_t *bu )
281 g_hash_table_remove( bu->ic->acc->nicks, bu->handle );
285 void nick_strip( char *nick )
287 int i, j;
289 for( i = j = 0; nick[i] && j < MAX_NICK_LENGTH; i++ )
291 if( strchr( nick_lc_chars, nick[i] ) ||
292 strchr( nick_uc_chars, nick[i] ) )
294 nick[j] = nick[i];
295 j++;
298 if( isdigit( nick[0] ) )
300 char *orig;
302 orig = g_strdup( nick );
303 g_snprintf( nick, MAX_NICK_LENGTH, "_%s", orig );
304 g_free( orig );
305 j ++;
307 while( j <= MAX_NICK_LENGTH )
308 nick[j++] = '\0';
311 int nick_ok( const char *nick )
313 const char *s;
315 /* Empty/long nicks are not allowed, nor numbers at [0] */
316 if( !*nick || isdigit( nick[0] ) || strlen( nick ) > MAX_NICK_LENGTH )
317 return( 0 );
319 for( s = nick; *s; s ++ )
320 if( !strchr( nick_lc_chars, *s ) && !strchr( nick_uc_chars, *s ) )
321 return( 0 );
323 return( 1 );
326 int nick_lc( char *nick )
328 static char tab[128] = { 0 };
329 int i;
331 if( tab['A'] == 0 )
332 for( i = 0; nick_lc_chars[i]; i ++ )
334 tab[(int)nick_uc_chars[i]] = nick_lc_chars[i];
335 tab[(int)nick_lc_chars[i]] = nick_lc_chars[i];
338 for( i = 0; nick[i]; i ++ )
340 if( !tab[(int)nick[i]] )
341 return( 0 );
343 nick[i] = tab[(int)nick[i]];
346 return( 1 );
349 int nick_uc( char *nick )
351 static char tab[128] = { 0 };
352 int i;
354 if( tab['A'] == 0 )
355 for( i = 0; nick_lc_chars[i]; i ++ )
357 tab[(int)nick_uc_chars[i]] = nick_uc_chars[i];
358 tab[(int)nick_lc_chars[i]] = nick_uc_chars[i];
361 for( i = 0; nick[i]; i ++ )
363 if( !tab[(int)nick[i]] )
364 return( 0 );
366 nick[i] = tab[(int)nick[i]];
369 return( 1 );
372 int nick_cmp( const char *a, const char *b )
374 char aa[1024] = "", bb[1024] = "";
376 strncpy( aa, a, sizeof( aa ) - 1 );
377 strncpy( bb, b, sizeof( bb ) - 1 );
378 if( nick_lc( aa ) && nick_lc( bb ) )
380 return( strcmp( aa, bb ) );
382 else
384 return( -1 ); /* Hmm... Not a clear answer.. :-/ */
388 char *nick_dup( const char *nick )
390 return g_strndup( nick, MAX_NICK_LENGTH );