aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/nogaim.c
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/nogaim.c')
-rw-r--r--protocols/nogaim.c264
1 files changed, 196 insertions, 68 deletions
diff --git a/protocols/nogaim.c b/protocols/nogaim.c
index a9eb207a..0c2094e2 100644
--- a/protocols/nogaim.c
+++ b/protocols/nogaim.c
@@ -1,7 +1,7 @@
/********************************************************************\
* BitlBee -- An IRC to other IM-networks gateway *
* *
- * Copyright 2002-2006 Wilmer van der Gaast and others *
+ * Copyright 2002-2010 Wilmer van der Gaast and others *
\********************************************************************/
/*
@@ -38,6 +38,7 @@
#include "chat.h"
static int remove_chat_buddy_silent( struct groupchat *b, const char *handle );
+static char *format_timestamp( irc_t *irc, time_t msg_ts );
GSList *connections;
@@ -97,7 +98,19 @@ GList *protocols = NULL;
void register_protocol (struct prpl *p)
{
- protocols = g_list_append(protocols, p);
+ int i;
+ gboolean refused = global.conf->protocols != NULL;
+
+ for (i = 0; global.conf->protocols && global.conf->protocols[i]; i++)
+ {
+ if (g_strcasecmp(p->name, global.conf->protocols[i]) == 0)
+ refused = FALSE;
+ }
+
+ if (refused)
+ log_message(LOGLVL_WARNING, "Protocol %s disabled\n", p->name);
+ else
+ protocols = g_list_append(protocols, p);
}
struct prpl *find_protocol(const char *name)
@@ -272,9 +285,8 @@ void imcb_connected( struct im_connection *ic )
ic->keepalive = b_timeout_add( 60000, send_keepalive, ic );
ic->flags |= OPT_LOGGED_IN;
- /* Also necessary when we're not away, at least for some of the
- protocols. */
- imc_set_away( ic, u->away );
+ /* Necessary to send initial presence status, even if we're not away. */
+ imc_away_send_update( ic );
/* Apparently we're connected successfully, so reset the
exponential backoff timer. */
@@ -376,7 +388,7 @@ void imcb_ask( struct im_connection *ic, char *msg, void *data,
/* list.c */
-void imcb_add_buddy( struct im_connection *ic, char *handle, char *group )
+void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group )
{
user_t *u;
char nick[MAX_NICK_LENGTH+1], *s;
@@ -450,9 +462,10 @@ struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle )
return( b );
}
-void imcb_rename_buddy( struct im_connection *ic, char *handle, char *realname )
+void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname )
{
user_t *u = user_findhandle( ic, handle );
+ char *set;
if( !u || !realname ) return;
@@ -465,9 +478,26 @@ void imcb_rename_buddy( struct im_connection *ic, char *handle, char *realname )
if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->irc->set, "display_namechanges" ) )
imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname );
}
+
+ set = set_getstr( &ic->acc->set, "nick_source" );
+ if( strcmp( set, "handle" ) != 0 )
+ {
+ char *name = g_strdup( realname );
+
+ if( strcmp( set, "first_name" ) == 0 )
+ {
+ int i;
+ for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {}
+ name[i] = '\0';
+ }
+
+ imcb_buddy_nick_hint( ic, handle, name );
+
+ g_free( name );
+ }
}
-void imcb_remove_buddy( struct im_connection *ic, char *handle, char *group )
+void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group )
{
user_t *u;
@@ -477,7 +507,7 @@ void imcb_remove_buddy( struct im_connection *ic, char *handle, char *group )
/* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM
modules to suggest a nickname for a handle. */
-void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick )
+void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick )
{
user_t *u = user_findhandle( ic, handle );
char newnick[MAX_NICK_LENGTH+1], *orig_nick;
@@ -622,11 +652,9 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags,
oa = u->away != NULL;
oo = u->online;
- if( u->away )
- {
- g_free( u->away );
- u->away = NULL;
- }
+ g_free( u->away );
+ g_free( u->status_msg );
+ u->away = u->status_msg = NULL;
if( ( flags & OPT_LOGGED_IN ) && !u->online )
{
@@ -664,7 +692,10 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags,
u->away = g_strdup( "Away" );
}
}
- /* else waste_any_state_information_for_now(); */
+ else
+ {
+ u->status_msg = g_strdup( message );
+ }
/* LISPy... */
if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */
@@ -689,10 +720,10 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags,
}
}
-void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t flags, time_t sent_at )
+void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at )
{
irc_t *irc = ic->irc;
- char *wrapped;
+ char *wrapped, *ts;
user_t *u;
u = user_findhandle( ic, handle );
@@ -734,10 +765,18 @@ void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t
if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
strip_html( msg );
-
+
+ if( ( ts = format_timestamp( irc, sent_at ) ) )
+ {
+ char *new = g_strconcat( ts, msg, NULL );
+ g_free( ts );
+ ts = msg = new;
+ }
+
wrapped = word_wrap( msg, 425 );
irc_msgfrom( irc, u->nick, wrapped );
g_free( wrapped );
+ g_free( ts );
}
void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags )
@@ -822,7 +861,7 @@ void imcb_chat_free( struct groupchat *c )
}
}
-void imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at )
+void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at )
{
struct im_connection *ic = c->ic;
char *wrapped;
@@ -841,7 +880,9 @@ void imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, t
wrapped = word_wrap( msg, 425 );
if( c && u )
{
- irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, "", wrapped );
+ char *ts = format_timestamp( ic->irc, sent_at );
+ irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, ts ? : "", wrapped );
+ g_free( ts );
}
else
{
@@ -894,7 +935,7 @@ void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at
/* buddy_chat.c */
-void imcb_chat_add_buddy( struct groupchat *b, char *handle )
+void imcb_chat_add_buddy( struct groupchat *b, const char *handle )
{
user_t *u = user_findhandle( b->ic, handle );
int me = 0;
@@ -929,7 +970,7 @@ void imcb_chat_add_buddy( struct groupchat *b, char *handle )
}
/* This function is one BIG hack... :-( EREWRITE */
-void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason )
+void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason )
{
user_t *u;
int me = 0;
@@ -1035,8 +1076,94 @@ char *set_eval_away_devoice( set_t *set, char *value )
return value;
}
+char *set_eval_timezone( set_t *set, char *value )
+{
+ char *s;
+
+ if( strcmp( value, "local" ) == 0 ||
+ strcmp( value, "gmt" ) == 0 || strcmp( value, "utc" ) == 0 )
+ return value;
+
+ /* Otherwise: +/- at the beginning optional, then one or more numbers,
+ possibly followed by a colon and more numbers. Don't bother bound-
+ checking them since users are free to shoot themselves in the foot. */
+ s = value;
+ if( *s == '+' || *s == '-' )
+ s ++;
+
+ /* \d+ */
+ if( !isdigit( *s ) )
+ return SET_INVALID;
+ while( *s && isdigit( *s ) ) s ++;
+
+ /* EOS? */
+ if( *s == '\0' )
+ return value;
+
+ /* Otherwise, colon */
+ if( *s != ':' )
+ return SET_INVALID;
+ s ++;
+
+ /* \d+ */
+ if( !isdigit( *s ) )
+ return SET_INVALID;
+ while( *s && isdigit( *s ) ) s ++;
+
+ /* EOS */
+ return *s == '\0' ? value : SET_INVALID;
+}
-
+static char *format_timestamp( irc_t *irc, time_t msg_ts )
+{
+ time_t now_ts = time( NULL );
+ struct tm now, msg;
+ char *set;
+
+ /* If the timestamp is <= 0 or less than a minute ago, discard it as
+ it doesn't seem to add to much useful info and/or might be noise. */
+ if( msg_ts <= 0 || msg_ts > now_ts - 60 )
+ return NULL;
+
+ set = set_getstr( &irc->set, "timezone" );
+ if( strcmp( set, "local" ) == 0 )
+ {
+ localtime_r( &now_ts, &now );
+ localtime_r( &msg_ts, &msg );
+ }
+ else
+ {
+ int hr, min = 0, sign = 60;
+
+ if( set[0] == '-' )
+ {
+ sign *= -1;
+ set ++;
+ }
+ else if( set[0] == '+' )
+ {
+ set ++;
+ }
+
+ if( sscanf( set, "%d:%d", &hr, &min ) >= 1 )
+ {
+ msg_ts += sign * ( hr * 60 + min );
+ now_ts += sign * ( hr * 60 + min );
+ }
+
+ gmtime_r( &now_ts, &now );
+ gmtime_r( &msg_ts, &msg );
+ }
+
+ if( msg.tm_year == now.tm_year && msg.tm_yday == now.tm_yday )
+ return g_strdup_printf( "\x02[\x02\x02\x02%02d:%02d:%02d\x02]\x02 ",
+ msg.tm_hour, msg.tm_min, msg.tm_sec );
+ else
+ return g_strdup_printf( "\x02[\x02\x02\x02%04d-%02d-%02d "
+ "%02d:%02d:%02d\x02]\x02 ",
+ msg.tm_year + 1900, msg.tm_mon, msg.tm_mday,
+ msg.tm_hour, msg.tm_min, msg.tm_sec );
+}
/* The plan is to not allow straight calls to prpl functions anymore, but do
them all from some wrappers. We'll start to define some down here: */
@@ -1074,51 +1201,30 @@ int imc_chat_msg( struct groupchat *c, char *msg, int flags )
return 1;
}
-static char *imc_away_alias_find( GList *gcm, char *away );
+static char *imc_away_state_find( GList *gcm, char *away, char **message );
-int imc_set_away( struct im_connection *ic, char *away )
+int imc_away_send_update( struct im_connection *ic )
{
- GList *m, *ms;
- char *s;
-
- if( !away ) away = "";
- ms = m = ic->acc->prpl->away_states( ic );
+ char *away, *msg = NULL;
- while( m )
+ away = set_getstr( &ic->acc->set, "away" ) ?
+ : set_getstr( &ic->irc->set, "away" );
+ if( away && *away )
{
- if( *away )
- {
- if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
- break;
- }
- else
- {
- if( g_strcasecmp( m->data, "Available" ) == 0 )
- break;
- if( g_strcasecmp( m->data, "Online" ) == 0 )
- break;
- }
- m = m->next;
+ GList *m = ic->acc->prpl->away_states( ic );
+ msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
+ away = imc_away_state_find( m, away, &msg ) ? : m->data;
}
-
- if( m )
+ else if( ic->acc->flags & ACC_FLAG_STATUS_MESSAGE )
{
- ic->acc->prpl->set_away( ic, m->data, *away ? away : NULL );
- }
- else
- {
- s = imc_away_alias_find( ms, away );
- if( s )
- {
- ic->acc->prpl->set_away( ic, s, away );
- if( set_getbool( &ic->irc->set, "debug" ) )
- imcb_log( ic, "Setting away state to %s", s );
- }
- else
- ic->acc->prpl->set_away( ic, GAIM_AWAY_CUSTOM, away );
+ away = NULL;
+ msg = set_getstr( &ic->acc->set, "status" ) ?
+ : set_getstr( &ic->irc->set, "status" );
}
- return( 1 );
+ ic->acc->prpl->set_away( ic, away, msg );
+
+ return 1;
}
static char *imc_away_alias_list[8][5] =
@@ -1133,16 +1239,33 @@ static char *imc_away_alias_list[8][5] =
{ NULL }
};
-static char *imc_away_alias_find( GList *gcm, char *away )
+static char *imc_away_state_find( GList *gcm, char *away, char **message )
{
GList *m;
int i, j;
+ for( m = gcm; m; m = m->next )
+ if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
+ {
+ /* At least the Yahoo! module works better if message
+ contains no data unless it adds something to what
+ we have in state already. */
+ if( strlen( m->data ) == strlen( away ) )
+ *message = NULL;
+
+ return m->data;
+ }
+
for( i = 0; *imc_away_alias_list[i]; i ++ )
{
+ int keep_message;
+
for( j = 0; imc_away_alias_list[i][j]; j ++ )
if( g_strncasecmp( away, imc_away_alias_list[i][j], strlen( imc_away_alias_list[i][j] ) ) == 0 )
+ {
+ keep_message = strlen( away ) != strlen( imc_away_alias_list[i][j] );
break;
+ }
if( !imc_away_alias_list[i][j] ) /* If we reach the end, this row */
continue; /* is not what we want. Next! */
@@ -1150,17 +1273,22 @@ static char *imc_away_alias_find( GList *gcm, char *away )
/* Now find an entry in this row which exists in gcm */
for( j = 0; imc_away_alias_list[i][j]; j ++ )
{
- m = gcm;
- while( m )
- {
+ for( m = gcm; m; m = m->next )
if( g_strcasecmp( imc_away_alias_list[i][j], m->data ) == 0 )
- return( imc_away_alias_list[i][j] );
- m = m->next;
- }
+ {
+ if( !keep_message )
+ *message = NULL;
+
+ return imc_away_alias_list[i][j];
+ }
}
+
+ /* No need to look further, apparently this state doesn't
+ have any good alias for this protocol. */
+ break;
}
- return( NULL );
+ return NULL;
}
void imc_add_allow( struct im_connection *ic, char *handle )