diff options
Diffstat (limited to 'nick.c')
| -rw-r--r-- | nick.c | 391 | 
1 files changed, 391 insertions, 0 deletions
| @@ -0,0 +1,391 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2010 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Some stuff to fetch, save and handle nicknames for your buddies      */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" + +/* Character maps, _lc_[x] == _uc_[x] (but uppercase), according to the RFC's. +   With one difference, we allow dashes. These are used to do uc/lc conversions +   and strip invalid chars. */ +static char *nick_lc_chars = "0123456789abcdefghijklmnopqrstuvwxyz{}^`-_|"; +static char *nick_uc_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]~`-_\\"; + +/* Store handles in lower case and strip spaces, because AIM is braindead. */ +static char *clean_handle( const char *orig ) +{ +	char *new = g_malloc( strlen( orig ) + 1 ); +	int i = 0; +	 +	do { +		if (*orig != ' ') +			new[i++] = tolower( *orig ); +	} +	while (*(orig++)); +	 +	return new; +} + +void nick_set_raw( account_t *acc, const char *handle, const char *nick ) +{ +	char *store_handle, *store_nick = g_malloc( MAX_NICK_LENGTH + 1 ); +	 +	store_handle = clean_handle( handle ); +	store_nick[MAX_NICK_LENGTH] = '\0'; +	strncpy( store_nick, nick, MAX_NICK_LENGTH ); +	nick_strip( store_nick ); +	 +	g_hash_table_replace( acc->nicks, store_handle, store_nick ); +} + +void nick_set( bee_user_t *bu, const char *nick ) +{ +	nick_set_raw( bu->ic->acc, bu->handle, nick ); +} + +char *nick_get( bee_user_t *bu ) +{ +	static char nick[MAX_NICK_LENGTH+1]; +	char *store_handle, *found_nick; +	 +	memset( nick, 0, MAX_NICK_LENGTH + 1 ); +	 +	store_handle = clean_handle( bu->handle ); +	/* Find out if we stored a nick for this person already. If not, try +	   to generate a sane nick automatically. */ +	if( ( found_nick = g_hash_table_lookup( bu->ic->acc->nicks, store_handle ) ) ) +	{ +		strncpy( nick, found_nick, MAX_NICK_LENGTH ); +	} +	else if( ( found_nick = nick_gen( bu ) ) ) +	{ +		strncpy( nick, found_nick, MAX_NICK_LENGTH ); +		g_free( found_nick ); +	} +	else +	{ +		/* Keep this fallback since nick_gen() can return NULL in some cases. */ +		char *s; +		 +		g_snprintf( nick, MAX_NICK_LENGTH, "%s", bu->handle ); +		if( ( s = strchr( nick, '@' ) ) ) +			while( *s ) +				*(s++) = 0; +		 +		nick_strip( nick ); +		if( set_getbool( &bu->bee->set, "lcnicks" ) ) +			nick_lc( nick ); +	} +	g_free( store_handle ); +	 +	/* Make sure the nick doesn't collide with an existing one by adding +	   underscores and that kind of stuff, if necessary. */ +	nick_dedupe( bu, nick ); +	 +	return nick; +} + +char *nick_gen( bee_user_t *bu ) +{ +	gboolean ok = FALSE; /* Set to true once the nick contains something unique. */ +	GString *ret = g_string_new( "" ); +	char *fmt = set_getstr( &bu->ic->acc->set, "nick_format" ) ? : +	            set_getstr( &bu->bee->set, "nick_format" ); +	 +	while( fmt && *fmt && ret->len < MAX_NICK_LENGTH ) +	{ +		char *part = NULL, chop = '\0', *asc = NULL; +		int len = MAX_NICK_LENGTH; +		 +		if( *fmt != '%' ) +		{ +			g_string_append_c( ret, *fmt ); +			fmt ++; +			continue; +		} +		 +		fmt ++; +		while( *fmt ) +		{ +			/* -char means chop off everything from char */ +			if( *fmt == '-' ) +			{ +				chop = fmt[1]; +				if( chop == '\0' ) +					return NULL; +				fmt += 2; +			} +			else if( isdigit( *fmt ) ) +			{ +				len = 0; +				/* Grab a number. */ +				while( isdigit( *fmt ) ) +					len = len * 10 + ( *(fmt++) - '0' ); +			} +			else if( g_strncasecmp( fmt, "nick", 4 ) == 0 ) +			{ +				part = bu->nick ? : bu->handle; +				fmt += 4; +				ok |= TRUE; +				break; +			} +			else if( g_strncasecmp( fmt, "handle", 6 ) == 0 ) +			{ +				part = bu->handle; +				fmt += 6; +				ok |= TRUE; +				break; +			} +			else if( g_strncasecmp( fmt, "full_name", 9 ) == 0 ) +			{ +				part = bu->fullname; +				fmt += 9; +				ok |= part && *part; +				break; +			} +			else if( g_strncasecmp( fmt, "first_name", 10 ) == 0 ) +			{ +				part = bu->fullname; +				fmt += 10; +				ok |= part && *part; +				chop = ' '; +				break; +			} +			else if( g_strncasecmp( fmt, "group", 5 ) == 0 ) +			{ +				part = bu->group ? bu->group->name : NULL; +				fmt += 5; +				break; +			} +			else if( g_strncasecmp( fmt, "account", 7 ) == 0 ) +			{ +				part = bu->ic->acc->tag; +				fmt += 7; +				break; +			} +			else +			{ +				return NULL; +			} +		} +		 +		/* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT +		   should do lossy/approximate conversions, so letters with +		   accents don't just get stripped. Note that it depends on +		   LC_CTYPE being set to something other than C/POSIX. */ +		if( part ) +			part = asc = g_convert_with_fallback( part, -1, "ASCII//TRANSLIT", +			                                      "UTF-8", "", NULL, NULL, NULL ); +		 +		if( ret->len == 0 && part && isdigit( *part ) ) +			g_string_append_c( ret, '_' ); +		 +		while( part && *part && *part != chop && len > 0 ) +		{ +			if( strchr( nick_lc_chars, *part ) || +			    strchr( nick_uc_chars, *part ) ) +				g_string_append_c( ret, *part ); +			 +			part ++; +			len --; +		} +		g_free( asc ); +	} +	 +	/* This returns NULL if the nick is empty or otherwise not ok. */ +	return g_string_free( ret, ret->len == 0 || !ok ); +} + +void nick_dedupe( bee_user_t *bu, char nick[MAX_NICK_LENGTH+1] ) +{ +	irc_t *irc = (irc_t*) bu->bee->ui_data; +	int inf_protection = 256; +	irc_user_t *iu; +	 +	/* Now, find out if the nick is already in use at the moment, and make +	   subtle changes to make it unique. */ +	while( !nick_ok( nick ) || +	       ( ( iu = irc_user_by_name( irc, nick ) ) && iu->bu != bu ) ) +	{ +		if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) ) +		{ +			nick[strlen(nick)+1] = 0; +			nick[strlen(nick)] = '_'; +		} +		else +		{ +			nick[0] ++; +		} +		 +		if( inf_protection-- == 0 ) +		{ +			int i; +			 +			irc_usermsg( irc, "Warning: Almost had an infinite loop in nick_get()! " +			                  "This used to be a fatal BitlBee bug, but we tried to fix it. " +			                  "This message should *never* appear anymore. " +			                  "If it does, please *do* send us a bug report! " +			                  "Please send all the following lines in your report:" ); +			 +			irc_usermsg( irc, "Trying to get a sane nick for handle %s", bu->handle ); +			for( i = 0; i < MAX_NICK_LENGTH; i ++ ) +				irc_usermsg( irc, "Char %d: %c/%d", i, nick[i], nick[i] ); +			 +			irc_usermsg( irc, "FAILED. Returning an insane nick now. Things might break. " +			                  "Good luck, and please don't forget to paste the lines up here " +			                  "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" ); +			 +			g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() ); +			 +			break; +		} +	} +} + +/* Just check if there is a nickname set for this buddy or if we'd have to +   generate one. */ +int nick_saved( bee_user_t *bu ) +{ +	char *store_handle, *found; +	 +	store_handle = clean_handle( bu->handle ); +	found = g_hash_table_lookup( bu->ic->acc->nicks, store_handle ); +	g_free( store_handle ); +	 +	return found != NULL; +} + +void nick_del( bee_user_t *bu ) +{ +	g_hash_table_remove( bu->ic->acc->nicks, bu->handle ); +} + + +void nick_strip( char *nick ) +{ +	int i, j; +	 +	for( i = j = 0; nick[i] && j < MAX_NICK_LENGTH; i++ ) +	{ +		if( strchr( nick_lc_chars, nick[i] ) ||  +		    strchr( nick_uc_chars, nick[i] ) ) +		{ +			nick[j] = nick[i]; +			j++; +		} +	} +	if( isdigit( nick[0] ) ) +	{ +		char *orig; +		 +		orig = g_strdup( nick ); +		g_snprintf( nick, MAX_NICK_LENGTH, "_%s", orig ); +		g_free( orig ); +		j ++; +	} +	while( j <= MAX_NICK_LENGTH ) +		nick[j++] = '\0'; +} + +int nick_ok( const char *nick ) +{ +	const char *s; +	 +	/* Empty/long nicks are not allowed, nor numbers at [0] */ +	if( !*nick || isdigit( nick[0] ) || strlen( nick ) > MAX_NICK_LENGTH ) +		return( 0 ); +	 +	for( s = nick; *s; s ++ ) +		if( !strchr( nick_lc_chars, *s ) && !strchr( nick_uc_chars, *s ) ) +			return( 0 ); +	 +	return( 1 ); +} + +int nick_lc( char *nick ) +{ +	static char tab[128] = { 0 }; +	int i; +	 +	if( tab['A'] == 0 ) +		for( i = 0; nick_lc_chars[i]; i ++ ) +		{ +			tab[(int)nick_uc_chars[i]] = nick_lc_chars[i]; +			tab[(int)nick_lc_chars[i]] = nick_lc_chars[i]; +		} +	 +	for( i = 0; nick[i]; i ++ ) +	{ +		if( !tab[(int)nick[i]] ) +			return( 0 ); +		 +		nick[i] = tab[(int)nick[i]]; +	} +	 +	return( 1 ); +} + +int nick_uc( char *nick ) +{ +	static char tab[128] = { 0 }; +	int i; +	 +	if( tab['A'] == 0 ) +		for( i = 0; nick_lc_chars[i]; i ++ ) +		{ +			tab[(int)nick_uc_chars[i]] = nick_uc_chars[i]; +			tab[(int)nick_lc_chars[i]] = nick_uc_chars[i]; +		} +	 +	for( i = 0; nick[i]; i ++ ) +	{ +		if( !tab[(int)nick[i]] ) +			return( 0 ); +		 +		nick[i] = tab[(int)nick[i]]; +	} +	 +	return( 1 ); +} + +int nick_cmp( const char *a, const char *b ) +{ +	char aa[1024] = "", bb[1024] = ""; +	 +	strncpy( aa, a, sizeof( aa ) - 1 ); +	strncpy( bb, b, sizeof( bb ) - 1 ); +	if( nick_lc( aa ) && nick_lc( bb ) ) +	{ +		return( strcmp( aa, bb ) ); +	} +	else +	{ +		return( -1 );	/* Hmm... Not a clear answer.. :-/ */ +	} +} + +char *nick_dup( const char *nick ) +{ +	return g_strndup( nick, MAX_NICK_LENGTH ); +} | 
