aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bitlbee.c4
-rw-r--r--doc/user-guide/misc.xml4
-rw-r--r--irc.c30
-rw-r--r--irc.h1
-rw-r--r--irc_commands.c6
-rw-r--r--nick.c14
-rw-r--r--nick.h1
-rw-r--r--protocols/jabber/Makefile2
-rw-r--r--protocols/jabber/conference.c246
-rw-r--r--protocols/jabber/io.c60
-rw-r--r--protocols/jabber/iq.c35
-rw-r--r--protocols/jabber/jabber.c65
-rw-r--r--protocols/jabber/jabber.h64
-rw-r--r--protocols/jabber/jabber_util.c178
-rw-r--r--protocols/jabber/message.c31
-rw-r--r--protocols/jabber/presence.c71
-rw-r--r--protocols/jabber/xmltree.c8
-rw-r--r--protocols/jabber/xmltree.h8
-rw-r--r--protocols/msn/sb.c2
-rw-r--r--protocols/nogaim.c142
-rw-r--r--protocols/nogaim.h9
-rw-r--r--protocols/oscar/oscar.c3
-rw-r--r--protocols/yahoo/yahoo.c14
-rw-r--r--root_commands.c33
-rw-r--r--user.c1
25 files changed, 855 insertions, 177 deletions
diff --git a/bitlbee.c b/bitlbee.c
index 6a3625ee..cbccd9ea 100644
--- a/bitlbee.c
+++ b/bitlbee.c
@@ -69,6 +69,7 @@ int bitlbee_daemon_init()
#ifdef IPV6
memset( &listen_addr6, 0, sizeof( listen_addr6 ) );
+ memset( &listen_addr, 0, sizeof( listen_addr ) );
listen_addr6.sin6_family = AF_INET6;
listen_addr6.sin6_port = htons( global.conf->port );
if( ( i = inet_pton( AF_INET6, ipv6_wrap( global.conf->iface ), &listen_addr6.sin6_addr ) ) != 1 )
@@ -76,7 +77,6 @@ int bitlbee_daemon_init()
/* Forget about IPv6 in this function. */
use_ipv6 = 0;
#endif
- memset( &listen_addr, 0, sizeof( listen_addr ) );
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons( global.conf->port );
if( strcmp( global.conf->iface, "::" ) == 0 )
@@ -124,7 +124,7 @@ int bitlbee_daemon_init()
else if( i != 0 )
exit( 0 );
- chdir( "/" );
+// chdir( "/" );
/* Sometimes std* are already closed (for example when we're in a RESTARTed
BitlBee process. So let's only close TTY-fds. */
diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml
index f90ce538..d387d4b3 100644
--- a/doc/user-guide/misc.xml
+++ b/doc/user-guide/misc.xml
@@ -80,6 +80,10 @@ Of course a channel with only two people isn't really exciting yet. So the next
</para>
<para>
+Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>join_chat</emphasis> command to join them. See <emphasis>help join_chat</emphasis> for more information.
+</para>
+
+<para>
This is all you'll probably need to know. If you have any problems, please read <emphasis>help groupchats3</emphasis>.
</para>
diff --git a/irc.c b/irc.c
index e1928497..72253595 100644
--- a/irc.c
+++ b/irc.c
@@ -658,7 +658,7 @@ void irc_names( irc_t *irc, char *channel )
strcat( namelist, " " );
}
}
- else if( ( c = chat_by_channel( channel ) ) )
+ else if( ( c = irc_chat_by_channel( irc, channel ) ) )
{
GList *l;
@@ -811,7 +811,7 @@ void irc_topic( irc_t *irc, char *channel )
}
else
{
- struct groupchat *c = chat_by_channel( channel );
+ struct groupchat *c = irc_chat_by_channel( irc, channel );
if( c )
irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
@@ -949,7 +949,7 @@ int irc_send( irc_t *irc, char *nick, char *s, int flags )
if( *nick == '#' || *nick == '&' )
{
- if( !( c = chat_by_channel( nick ) ) )
+ if( !( c = irc_chat_by_channel( irc, nick ) ) )
{
irc_reply( irc, 403, "%s :Channel does not exist", nick );
return( 0 );
@@ -1215,3 +1215,27 @@ static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond )
return TRUE;
}
+
+struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
+{
+ struct groupchat *c;
+ account_t *a;
+
+ /* This finds the connection which has a conversation which belongs to this channel */
+ for( a = irc->accounts; a; a = a->next )
+ {
+ if( a->ic == NULL )
+ continue;
+
+ c = a->ic->groupchats;
+ while( c )
+ {
+ if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
+ return c;
+
+ c = c->next;
+ }
+ }
+
+ return NULL;
+}
diff --git a/irc.h b/irc.h
index 5b770180..8be3579e 100644
--- a/irc.h
+++ b/irc.h
@@ -139,5 +139,6 @@ int irc_msgfrom( irc_t *irc, char *nick, char *msg );
int irc_noticefrom( irc_t *irc, char *nick, char *msg );
void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags );
+struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel );
#endif
diff --git a/irc_commands.c b/irc_commands.c
index 8d841aaa..266d9732 100644
--- a/irc_commands.c
+++ b/irc_commands.c
@@ -143,7 +143,7 @@ static void irc_cmd_part( irc_t *irc, char **cmd )
irc_part( irc, u, irc->channel );
irc_join( irc, u, irc->channel );
}
- else if( ( c = chat_by_channel( cmd[1] ) ) )
+ else if( ( c = irc_chat_by_channel( irc, cmd[1] ) ) )
{
user_t *u = user_find( irc, irc->nick );
@@ -200,7 +200,7 @@ static void irc_cmd_join( irc_t *irc, char **cmd )
static void irc_cmd_invite( irc_t *irc, char **cmd )
{
char *nick = cmd[1], *channel = cmd[2];
- struct groupchat *c = chat_by_channel( channel );
+ struct groupchat *c = irc_chat_by_channel( irc, channel );
user_t *u = user_find( irc, nick );
if( u && c && ( u->ic == c->ic ) )
@@ -286,7 +286,7 @@ static void irc_cmd_who( irc_t *irc, char **cmd )
irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname );
u = u->next;
}
- else if( ( c = chat_by_channel( channel ) ) )
+ else if( ( c = irc_chat_by_channel( irc, channel ) ) )
for( l = c->in_room; l; l = l->next )
{
if( ( u = user_findhandle( c->ic, l->data ) ) )
diff --git a/nick.c b/nick.c
index 5e24547a..88c3faea 100644
--- a/nick.c
+++ b/nick.c
@@ -56,7 +56,6 @@ char *nick_get( account_t *acc, const char *handle )
{
static char nick[MAX_NICK_LENGTH+1];
char *store_handle, *found_nick;
- int inf_protection = 256;
memset( nick, 0, MAX_NICK_LENGTH + 1 );
@@ -82,6 +81,17 @@ char *nick_get( account_t *acc, const char *handle )
}
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( acc, handle, nick );
+
+ return nick;
+}
+
+void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+1] )
+{
+ int inf_protection = 256;
+
/* 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 ) || user_find( acc->irc, nick ) )
@@ -119,8 +129,6 @@ char *nick_get( account_t *acc, const char *handle )
break;
}
}
-
- return nick;
}
/* Just check if there is a nickname set for this buddy or if we'd have to
diff --git a/nick.h b/nick.h
index bb7816f0..31298275 100644
--- a/nick.h
+++ b/nick.h
@@ -25,6 +25,7 @@
void nick_set( account_t *acc, const char *handle, const char *nick );
char *nick_get( account_t *acc, const char *handle );
+void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+1] );
int nick_saved( account_t *acc, const char *handle );
void nick_del( account_t *acc, const char *handle );
void nick_strip( char *nick );
diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile
index e81e6c5a..e042f812 100644
--- a/protocols/jabber/Makefile
+++ b/protocols/jabber/Makefile
@@ -9,7 +9,7 @@
-include ../../Makefile.settings
# [SH] Program variables
-objects = io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o
+objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o
CFLAGS += -Wall
LFLAGS += -r
diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c
new file mode 100644
index 00000000..3fc9ee70
--- /dev/null
+++ b/protocols/jabber/conference.c
@@ -0,0 +1,246 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Jabber module - Conference rooms *
+* *
+* Copyright 2007 Wilmer van der Gaast <wilmer@gaast.net> *
+* *
+* 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 along *
+* with this program; if not, write to the Free Software Foundation, Inc., *
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
+* *
+\***************************************************************************/
+
+#include "jabber.h"
+
+struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password )
+{
+ struct jabber_chat *jc;
+ struct xt_node *node;
+ struct groupchat *c;
+ char *roomjid;
+
+ roomjid = g_strdup_printf( "%s/%s", room, nick );
+ node = xt_new_node( "x", NULL, NULL );
+ xt_add_attr( node, "xmlns", XMLNS_MUC );
+ node = jabber_make_packet( "presence", NULL, roomjid, node );
+
+ if( !jabber_write_packet( ic, node ) )
+ {
+ g_free( roomjid );
+ xt_free_node( node );
+ return NULL;
+ }
+ xt_free_node( node );
+
+ jc = g_new0( struct jabber_chat, 1 );
+ jc->name = jabber_normalize( room );
+
+ if( ( jc->me = jabber_buddy_add( ic, roomjid ) ) == NULL )
+ {
+ g_free( roomjid );
+ g_free( jc->name );
+ g_free( jc );
+ return NULL;
+ }
+
+ /* roomjid isn't normalized yet, and we need an original version
+ of the nick to send a proper presence update. */
+ jc->my_full_jid = roomjid;
+
+ c = imcb_chat_new( ic, room );
+ c->data = jc;
+
+ return c;
+}
+
+void jabber_chat_free( struct groupchat *c )
+{
+ struct jabber_chat *jc = c->data;
+
+ jabber_buddy_remove_bare( c->ic, jc->name );
+
+ g_free( jc->my_full_jid );
+ g_free( jc->name );
+ g_free( jc );
+
+ imcb_chat_free( c );
+}
+
+int jabber_chat_msg( struct groupchat *c, char *message, int flags )
+{
+ struct im_connection *ic = c->ic;
+ struct jabber_chat *jc = c->data;
+ struct xt_node *node;
+
+ node = xt_new_node( "body", message, NULL );
+ node = jabber_make_packet( "message", "groupchat", jc->name, node );
+
+ if( !jabber_write_packet( ic, node ) )
+ {
+ xt_free_node( node );
+ return 0;
+ }
+ xt_free_node( node );
+
+ return 1;
+}
+
+int jabber_chat_leave( struct groupchat *c, const char *reason )
+{
+ struct im_connection *ic = c->ic;
+ struct jabber_chat *jc = c->data;
+ struct xt_node *node;
+
+ node = xt_new_node( "x", NULL, NULL );
+ xt_add_attr( node, "xmlns", XMLNS_MUC );
+ node = jabber_make_packet( "presence", "unavailable", jc->my_full_jid, node );
+
+ if( !jabber_write_packet( ic, node ) )
+ {
+ xt_free_node( node );
+ return 0;
+ }
+ xt_free_node( node );
+
+ return 1;
+}
+
+/* Not really the same syntax as the normal pkt_ functions, but this isn't
+ called by the xmltree parser directly and this way I can add some extra
+ parameters so we won't have to repeat too many things done by the caller
+ already. */
+void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node )
+{
+ struct groupchat *chat;
+ struct xt_node *c;
+ char *type = xt_find_attr( node, "type" );
+ struct jabber_chat *jc;
+ char *s;
+
+ if( ( chat = jabber_chat_by_name( ic, bud->bare_jid ) ) == NULL )
+ {
+ /* How could this happen?? We could do kill( self, 11 )
+ now or just wait for the OS to do it. :-) */
+ return;
+ }
+
+ jc = chat->data;
+
+ if( type == NULL && !( bud->flags & JBFLAG_IS_CHATROOM ) )
+ {
+ bud->flags |= JBFLAG_IS_CHATROOM;
+ /* If this one wasn't set yet, this buddy just joined the chat.
+ Slightly hackish way of finding out eh? ;-) */
+
+ /* This is pretty messy... Here it sets ext_jid to the real
+ JID of the participant. Works for non-anonymized channels.
+ Might break if someone joins a chat twice, though. */
+ for( c = node->children; ( c = xt_find_node( c, "x" ) ); c = c->next )
+ if( ( s = xt_find_attr( c, "xmlns" ) ) &&
+ ( strcmp( s, XMLNS_MUC_USER ) == 0 ) )
+ {
+ c = xt_find_node( c->children, "item" );
+ if( ( s = xt_find_attr( c, "jid" ) ) )
+ {
+ /* Yay, found what we need. :-) */
+ bud->ext_jid = jabber_normalize( s );
+ break;
+ }
+ }
+
+ /* Make up some other handle, if necessary. */
+ if( bud->ext_jid == NULL )
+ {
+ if( bud == jc->me )
+ {
+ bud->ext_jid = jabber_normalize( ic->acc->user );
+ }
+ else
+ {
+ int i;
+
+ /* Don't want the nick to be at the end, so let's
+ think of some slightly different notation to use
+ for anonymous groupchat participants in BitlBee. */
+ bud->ext_jid = g_strdup_printf( "%s=%s", bud->resource, bud->bare_jid );
+
+ /* And strip any unwanted characters. */
+ for( i = 0; bud->resource[i]; i ++ )
+ if( bud->ext_jid[i] == '=' || bud->ext_jid[i] == '@' )
+ bud->ext_jid[i] = '_';
+
+ /* Some program-specific restrictions. */
+ imcb_clean_handle( ic, bud->ext_jid );
+ }
+ bud->flags |= JBFLAG_IS_ANONYMOUS;
+ }
+
+ if( bud != jc->me )
+ {
+ imcb_add_buddy( ic, bud->ext_jid, NULL );
+ imcb_buddy_nick_hint( ic, bud->ext_jid, bud->resource );
+ }
+
+ s = strchr( bud->ext_jid, '/' );
+ if( s ) *s = 0; /* Should NEVER be NULL, but who knows... */
+ imcb_chat_add_buddy( chat, bud->ext_jid );
+ if( s ) *s = '/';
+ }
+ else if( type ) /* type can only be NULL or "unavailable" in this function */
+ {
+ s = strchr( bud->ext_jid, '/' );
+ if( s ) *s = 0;
+ imcb_chat_remove_buddy( chat, bud->ext_jid, NULL );
+ if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS )
+ imcb_remove_buddy( ic, bud->ext_jid, NULL );
+ if( s ) *s = '/';
+
+ if( bud == jc->me )
+ jabber_chat_free( chat );
+ }
+}
+
+void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node )
+{
+ struct xt_node *body = xt_find_node( node->children, "body" );
+ struct groupchat *chat;
+ char *s;
+
+ if( bud == NULL )
+ {
+ s = xt_find_attr( node, "from" ); /* pkt_message() already NULL-checked this one. */
+ if( strchr( s, '/' ) == NULL )
+ /* This is fine, the groupchat itself isn't in jd->buddies. */
+ imcb_log( ic, "System message from groupchat %s: %s", s, body? body->text : "NULL" );
+ else
+ /* This, however, isn't fine! */
+ imcb_log( ic, "Groupchat message from unknown participant %s: %s", s, body ? body->text : "NULL" );
+
+ return;
+ }
+ else if( ( chat = jabber_chat_by_name( ic, bud->bare_jid ) ) == NULL )
+ {
+ /* How could this happen?? We could do kill( self, 11 )
+ now or just wait for the OS to do it. :-) */
+ return;
+ }
+
+ if( body && body->text_len > 0 )
+ {
+ s = strchr( bud->ext_jid, '/' );
+ if( s ) *s = 0;
+ imcb_chat_msg( chat, bud->ext_jid, body->text, 0, jabber_get_timestamp( node ) );
+ if( s ) *s = '/';
+ }
+}
diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c
index 67deb3a6..cf71ff87 100644
--- a/protocols/jabber/io.c
+++ b/protocols/jabber/io.c
@@ -44,6 +44,15 @@ int jabber_write( struct im_connection *ic, char *buf, int len )
struct jabber_data *jd = ic->proto_data;
gboolean ret;
+ if( jd->flags & JFLAG_XMLCONSOLE )
+ {
+ char *msg;
+
+ msg = g_strdup_printf( "TX: %s", buf );
+ imcb_buddy_msg( ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0 );
+ g_free( msg );
+ }
+
if( jd->tx_len == 0 )
{
/* If the queue is empty, allocate a new buffer. */
@@ -426,50 +435,35 @@ static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data )
static xt_status jabber_pkt_stream_error( struct xt_node *node, gpointer data )
{
struct im_connection *ic = data;
- struct xt_node *c;
- char *s, *type = NULL, *text = NULL;
int allow_reconnect = TRUE;
+ struct jabber_error *err;
- for( c = node->children; c; c = c->next )
- {
- if( !( s = xt_find_attr( c, "xmlns" ) ) ||
- strcmp( s, XMLNS_STREAM_ERROR ) != 0 )
- continue;
-
- if( strcmp( c->name, "text" ) != 0 )
- {
- type = c->name;
- }
- /* Only use the text if it doesn't have an xml:lang attribute,
- if it's empty or if it's set to something English. */
- else if( !( s = xt_find_attr( c, "xml:lang" ) ) ||
- !*s || strncmp( s, "en", 2 ) == 0 )
- {
- text = c->text;
- }
- }
+ err = jabber_error_parse( node, XMLNS_STREAM_ERROR );
/* Tssk... */
- if( type == NULL )
+ if( err->code == NULL )
{
imcb_error( ic, "Unknown stream error reported by server" );
imc_logout( ic, allow_reconnect );
+ jabber_error_free( err );
return XT_ABORT;
}
/* We know that this is a fatal error. If it's a "conflict" error, we
should turn off auto-reconnect to make sure we won't get some nasty
infinite loop! */
- if( strcmp( type, "conflict" ) == 0 )
+ if( strcmp( err->code, "conflict" ) == 0 )
{
imcb_error( ic, "Account and resource used from a different location" );
allow_reconnect = FALSE;
}
else
{
- imcb_error( ic, "Stream error: %s%s%s", type, text ? ": " : "", text ? text : "" );
+ imcb_error( ic, "Stream error: %s%s%s", err->code, err->text ? ": " : "",
+ err->text ? err->text : "" );
}
+ jabber_error_free( err );
imc_logout( ic, allow_reconnect );
return XT_ABORT;
@@ -483,7 +477,27 @@ static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data )
return XT_HANDLED;
}
+static xt_status jabber_xmlconsole( struct xt_node *node, gpointer data )
+{
+ struct im_connection *ic = data;
+ struct jabber_data *jd = ic->proto_data;
+
+ if( jd->flags & JFLAG_XMLCONSOLE )
+ {
+ char *msg, *pkt;
+
+ pkt = xt_to_string( node );
+ msg = g_strdup_printf( "RX: %s", pkt );
+ imcb_buddy_msg( ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0 );
+ g_free( msg );
+ g_free( pkt );
+ }
+
+ return XT_NEXT;
+}
+
static const struct xt_handler_entry jabber_handlers[] = {
+ { NULL, "stream:stream", jabber_xmlconsole },
{ "stream:stream", "<root>", jabber_end_of_stream },
{ "message", "stream:stream", jabber_pkt_message },
{ "presence", "stream:stream", jabber_pkt_presence },
diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c
index 2aa9d432..4738817a 100644
--- a/protocols/jabber/iq.c
+++ b/protocols/jabber/iq.c
@@ -98,26 +98,25 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )
}
else if( strcmp( s, XMLNS_DISCOVER ) == 0 )
{
+ const char *features[] = { XMLNS_VERSION,
+ XMLNS_TIME,
+ XMLNS_CHATSTATES,
+ XMLNS_MUC,
+ NULL };
+ const char **f;
+
c = xt_new_node( "identity", NULL, NULL );
xt_add_attr( c, "category", "client" );
xt_add_attr( c, "type", "pc" );
xt_add_attr( c, "name", "BitlBee" );
xt_add_child( reply, c );
- c = xt_new_node( "feature", NULL, NULL );
- xt_add_attr( c, "var", XMLNS_VERSION );
- xt_add_child( reply, c );
-
- c = xt_new_node( "feature", NULL, NULL );
- xt_add_attr( c, "var", XMLNS_TIME );
- xt_add_child( reply, c );
-
- c = xt_new_node( "feature", NULL, NULL );
- xt_add_attr( c, "var", XMLNS_CHATSTATES );
- xt_add_child( reply, c );
-
- /* Later this can be useful to announce things like
- MUC support. */
+ for( f = features; *f; f ++ )
+ {
+ c = xt_new_node( "feature", NULL, NULL );
+ xt_add_attr( c, "var", *f );
+ xt_add_child( reply, c );
+ }
}
else
{
@@ -372,15 +371,13 @@ static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *
imcb_add_buddy( ic, jid, ( group && group->text_len ) ?
group->text : NULL );
- imcb_rename_buddy( ic, jid, name );
+ if( name )
+ imcb_rename_buddy( ic, jid, name );
}
else if( strcmp( sub, "remove" ) == 0 )
{
- /* Don't have any API call for this yet! So let's
- just try to handle this as well as we can. */
jabber_buddy_remove_bare( ic, jid );
- imcb_buddy_status( ic, jid, 0, NULL, NULL );
- /* FIXME! */
+ imcb_remove_buddy( ic, jid, NULL );
}
}
diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c
index edad5dbd..9176cd68 100644
--- a/protocols/jabber/jabber.c
+++ b/protocols/jabber/jabber.c
@@ -196,6 +196,9 @@ static void jabber_logout( struct im_connection *ic )
jabber_end_stream( ic );
+ while( ic->groupchats )
+ jabber_chat_free( ic->groupchats );
+
if( jd->r_inpa >= 0 )
b_event_remove( jd->r_inpa );
if( jd->w_inpa >= 0 )
@@ -223,9 +226,16 @@ static int jabber_buddy_msg( struct im_connection *ic, char *who, char *message,
struct jabber_data *jd = ic->proto_data;
struct jabber_buddy *bud;
struct xt_node *node;
+ char *s;
int st;
- bud = jabber_buddy_by_jid( ic, who, 0 );
+ if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
+ return jabber_write( ic, message, strlen( message ) );
+
+ if( ( s = strchr( who, '=' ) ) && jabber_chat_by_name( ic, s + 1 ) )
+ bud = jabber_buddy_by_ext_jid( ic, who, 0 );
+ else
+ bud = jabber_buddy_by_jid( ic, who, 0 );
node = xt_new_node( "body", message, NULL );
node = jabber_make_packet( "message", "chat", bud ? bud->full_jid : who, node );
@@ -310,12 +320,34 @@ static void jabber_set_away( struct im_connection *ic, char *state_txt, char *me
static void jabber_add_buddy( struct im_connection *ic, char *who, char *group )
{
+ struct jabber_data *jd = ic->proto_data;
+
+ if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
+ {
+ jd->flags |= JFLAG_XMLCONSOLE;
+ imcb_add_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
+ return;
+ }
+
if( jabber_add_to_roster( ic, who, NULL ) )
presence_send_request( ic, who, "subscribe" );
}
static void jabber_remove_buddy( struct im_connection *ic, char *who, char *group )
{
+ struct jabber_data *jd = ic->proto_data;
+
+ if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
+ {
+ jd->flags &= ~JFLAG_XMLCONSOLE;
+ /* Not necessary for now. And for now the code isn't too
+ happy if the buddy is completely gone right after calling
+ this function already.
+ imcb_remove_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
+ */
+ return;
+ }
+
/* We should always do this part. Clean up our administration a little bit. */
jabber_buddy_remove_bare( ic, who );
@@ -323,6 +355,30 @@ static void jabber_remove_buddy( struct im_connection *ic, char *who, char *grou
presence_send_request( ic, who, "unsubscribe" );
}
+static struct groupchat *jabber_chat_join_( struct im_connection *ic, char *room, char *nick, char *password )
+{
+ if( strchr( room, '@' ) == NULL )
+ imcb_error( ic, "Invalid room name: %s", room );
+ else if( jabber_chat_by_name( ic, room ) )
+ imcb_error( ic, "Already present in chat `%s'", room );
+ else
+ return jabber_chat_join( ic, room, nick, password );
+
+ return NULL;
+}
+
+static void jabber_chat_msg_( struct groupchat *c, char *message, int flags )
+{
+ if( c && message )
+ jabber_chat_msg( c, message, flags );
+}
+
+static void jabber_chat_leave_( struct groupchat *c )
+{
+ if( c )
+ jabber_chat_leave( c, NULL );
+}
+
static void jabber_keepalive( struct im_connection *ic )
{
/* Just any whitespace character is enough as a keepalive for XMPP sessions. */
@@ -387,16 +443,15 @@ void jabber_initmodule()
ret->logout = jabber_logout;
ret->buddy_msg = jabber_buddy_msg;
ret->away_states = jabber_away_states;
-// ret->get_status_string = jabber_get_status_string;
ret->set_away = jabber_set_away;
// ret->set_info = jabber_set_info;
ret->get_info = jabber_get_info;
ret->add_buddy = jabber_add_buddy;
ret->remove_buddy = jabber_remove_buddy;
-// ret->chat_msg = jabber_chat_msg;
+ ret->chat_msg = jabber_chat_msg_;
// ret->chat_invite = jabber_chat_invite;
-// ret->chat_leave = jabber_chat_leave;
-// ret->chat_open = jabber_chat_open;
+ ret->chat_leave = jabber_chat_leave_;
+ ret->chat_join = jabber_chat_join_;
ret->keepalive = jabber_keepalive;
ret->send_typing = jabber_send_typing;
ret->handle_cmp = g_strcasecmp;
diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h
index 42f57ae1..7af7f98e 100644
--- a/protocols/jabber/jabber.h
+++ b/protocols/jabber/jabber.h
@@ -31,16 +31,17 @@
typedef enum
{
- JFLAG_STREAM_STARTED = 1, /* Set when we detected the beginning of the stream
+ JFLAG_STREAM_STARTED = 1, /* Set when we detected the beginning of the stream
and want to do auth. */
- JFLAG_AUTHENTICATED = 2, /* Set when we're successfully authenticatd. */
- JFLAG_STREAM_RESTART = 4, /* Set when we want to restart the stream (after
+ JFLAG_AUTHENTICATED = 2, /* Set when we're successfully authenticatd. */
+ JFLAG_STREAM_RESTART = 4, /* Set when we want to restart the stream (after
SASL or TLS). */
- JFLAG_WAIT_SESSION = 8, /* Set if we sent a <session> tag and need a reply
+ JFLAG_WAIT_SESSION = 8, /* Set if we sent a <session> tag and need a reply
before we continue. */
- JFLAG_WAIT_BIND = 16, /* ... for <bind> tag. */
- JFLAG_WANT_TYPING = 32, /* Set if we ever sent a typing notification, this
+ JFLAG_WAIT_BIND = 16, /* ... for <bind> tag. */
+ JFLAG_WANT_TYPING = 32, /* Set if we ever sent a typing notification, this
activates all XEP-85 related code. */
+ JFLAG_XMLCONSOLE = 64, /* If the user added an xmlconsole buddy. */
} jabber_flags_t;
typedef enum
@@ -49,12 +50,12 @@ typedef enum
sure it gets sent only once. */
JBFLAG_DOES_XEP85 = 2, /* Set this when the resource seems to support
XEP85 (typing notification shite). */
+ JBFLAG_IS_CHATROOM = 4, /* It's convenient to use this JID thingy for
+ groupchat state info too. */
+ JBFLAG_IS_ANONYMOUS = 8, /* For anonymous chatrooms, when we don't have
+ have a real JID. */
} jabber_buddy_flags_t;
-#define JABBER_PORT_DEFAULT "5222"
-#define JABBER_PORT_MIN 5220
-#define JABBER_PORT_MAX 5229
-
struct jabber_data
{
struct im_connection *ic;
@@ -100,6 +101,9 @@ struct jabber_buddy
char *full_jid;
char *resource;
+ char *ext_jid; /* The JID to use in BitlBee. The real JID if possible, */
+ /* otherwise something similar to the conference JID. */
+
int priority;
struct jabber_away_state *away_state;
char *away_message;
@@ -110,6 +114,20 @@ struct jabber_buddy
struct jabber_buddy *next;
};
+struct jabber_chat
+{
+ int flags;
+ char *name;
+ char *my_full_jid; /* Separate copy because of case sensitivity. */
+ struct jabber_buddy *me;
+};
+
+#define JABBER_XMLCONSOLE_HANDLE "xmlconsole"
+
+#define JABBER_PORT_DEFAULT "5222"
+#define JABBER_PORT_MIN 5220
+#define JABBER_PORT_MAX 5229
+
/* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the
first one should be used, but when storing a packet in the cache, a
"special" kind of ID is assigned to make it easier later to figure out
@@ -131,8 +149,11 @@ struct jabber_buddy
#define XMLNS_VERSION "jabber:iq:version" /* XEP-0092 */
#define XMLNS_TIME "jabber:iq:time" /* XEP-0090 */
#define XMLNS_VCARD "vcard-temp" /* XEP-0054 */
+#define XMLNS_DELAY "jabber:x:delay" /* XEP-0091 */
#define XMLNS_CHATSTATES "http://jabber.org/protocol/chatstates" /* 0085 */
#define XMLNS_DISCOVER "http://jabber.org/protocol/disco#info" /* 0030 */
+#define XMLNS_MUC "http://jabber.org/protocol/muc" /* XEP-0045 */
+#define XMLNS_MUC_USER "http://jabber.org/protocol/muc#user"/* XEP-0045 */
/* iq.c */
xt_status jabber_pkt_iq( struct xt_node *node, gpointer data );
@@ -163,18 +184,29 @@ void jabber_cache_clean( struct im_connection *ic );
const struct jabber_away_state *jabber_away_state_by_code( char *code );
const struct jabber_away_state *jabber_away_state_by_name( char *name );
void jabber_buddy_ask( struct im_connection *ic, char *handle );
-char *jabber_normalize( char *orig );
+char *jabber_normalize( const char *orig );
typedef enum
{
GET_BUDDY_CREAT = 1, /* Try to create it, if necessary. */
- GET_BUDDY_EXACT = 2, /* Get an exact message (only makes sense with bare JIDs). */
+ GET_BUDDY_EXACT = 2, /* Get an exact match (only makes sense with bare JIDs). */
+ GET_BUDDY_FIRST = 4, /* No selection, simply get the first resource for this JID. */
} get_buddy_flags_t;
+struct jabber_error
+{
+ char *code, *text, *type;
+};
+
struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid );
struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid, get_buddy_flags_t flags );
+struct jabber_buddy *jabber_buddy_by_ext_jid( struct im_connection *ic, char *jid, get_buddy_flags_t flags );
int jabber_buddy_remove( struct im_connection *ic, char *full_jid );
int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid );
+struct groupchat *jabber_chat_by_name( struct im_connection *ic, const char *name );
+time_t jabber_get_timestamp( struct xt_node *xt );
+struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns );
+void jabber_error_free( struct jabber_error *err );
extern const struct jabber_away_state jabber_away_state_list[];
@@ -192,4 +224,12 @@ xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data );
xt_status sasl_pkt_result( struct xt_node *node, gpointer data );
gboolean sasl_supported( struct im_connection *ic );
+/* conference.c */
+struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password );
+void jabber_chat_free( struct groupchat *c );
+int jabber_chat_msg( struct groupchat *ic, char *message, int flags );
+int jabber_chat_leave( struct groupchat *c, const char *reason );
+void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node );
+void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node );
+
#endif
diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c
index 3c0e71f4..56491c4f 100644
--- a/protocols/jabber/jabber_util.c
+++ b/protocols/jabber/jabber_util.c
@@ -47,7 +47,7 @@ char *set_eval_priority( set_t *set, char *value )
convenient, they have one disadvantage: If I would just
call p_s_u() now to send the new prio setting, it would
send the old setting because the set->value gets changed
- when the eval returns a non-NULL value.
+ after the (this) eval returns a non-NULL value.
So now I can choose between implementing post-set
functions next to evals, or just do this little hack: */
@@ -128,7 +128,7 @@ struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond,
/* Cache a node/packet for later use. Mainly useful for IQ packets if you need
them when you receive the response. Use this BEFORE sending the packet so
- it'll get a new id= tag, and do NOT free() the packet after writing it! */
+ it'll get a new id= tag, and do NOT free() the packet after sending it! */
void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func )
{
struct jabber_data *jd = ic->proto_data;
@@ -251,7 +251,7 @@ void jabber_buddy_ask( struct im_connection *ic, char *handle )
}
/* Returns a new string. Don't leak it! */
-char *jabber_normalize( char *orig )
+char *jabber_normalize( const char *orig )
{
int len, i;
char *new;
@@ -319,6 +319,8 @@ struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid_
}
else
{
+ /* Keep in mind that full_jid currently isn't really
+ a full JID... */
new->bare_jid = g_strdup( full_jid );
g_hash_table_insert( jd->buddies, new->bare_jid, new );
}
@@ -332,7 +334,8 @@ struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid_
else
{
/* Let's waste some more bytes of RAM instead of to make
- memory management a total disaster here.. */
+ memory management a total disaster here. And it saves
+ me one g_free() call in this function. :-P */
new->full_jid = full_jid;
}
@@ -352,6 +355,8 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,
if( ( s = strchr( jid, '/' ) ) )
{
+ int none_found = 0;
+
*s = 0;
if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )
{
@@ -369,8 +374,16 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,
break;
}
}
+ else
+ {
+ /* This hack is there to make sure that O_CREAT will
+ work if there's already another resouce present
+ for this JID, even if it's an unknown buddy. This
+ is done to handle conferences properly. */
+ none_found = 1;
+ }
- if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid ) )
+ if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && ( imcb_find_buddy( ic, jid ) || !none_found ) )
{
*s = '/';
bud = jabber_buddy_add( ic, jid );
@@ -417,6 +430,38 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,
}
}
+/* I'm keeping a separate ext_jid attribute to save a JID that makes sense
+ to export to BitlBee. This is mainly for groupchats right now. It's
+ a bit of a hack, but I just think having the user nickname in the hostname
+ part of the hostmask doesn't look nice on IRC. Normally you can convert
+ a normal JID to ext_jid by swapping the part before and after the / and
+ replacing the / with a =. But there should be some stripping (@s are
+ allowed in Jabber nicks...). */
+struct jabber_buddy *jabber_buddy_by_ext_jid( struct im_connection *ic, char *jid_, get_buddy_flags_t flags )
+{
+ struct jabber_buddy *bud;
+ char *s, *jid;
+
+ jid = jabber_normalize( jid_ );
+
+ if( ( s = strchr( jid, '=' ) ) == NULL )
+ return NULL;
+
+ for( bud = jabber_buddy_by_jid( ic, s + 1, GET_BUDDY_FIRST ); bud; bud = bud->next )
+ {
+ /* Hmmm, could happen if not all people in the chat are anonymized? */
+ if( bud->ext_jid == NULL )
+ continue;
+
+ if( strcmp( bud->ext_jid, jid ) == 0 )
+ break;
+ }
+
+ g_free( jid );
+
+ return bud;
+}
+
/* Remove one specific full JID from our list. Use this when a buddy goes
off-line (because (s)he can still be online from a different location.
XXX: See above, we should accept bare JIDs too... */
@@ -440,6 +485,7 @@ int jabber_buddy_remove( struct im_connection *ic, char *full_jid_ )
{
g_hash_table_remove( jd->buddies, bud->bare_jid );
g_free( bud->bare_jid );
+ g_free( bud->ext_jid );
g_free( bud->full_jid );
g_free( bud->away_message );
g_free( bud );
@@ -472,6 +518,7 @@ int jabber_buddy_remove( struct im_connection *ic, char *full_jid_ )
item, because we're removing the first. */
g_hash_table_replace( jd->buddies, bi->bare_jid, bi->next );
+ g_free( bi->ext_jid );
g_free( bi->full_jid );
g_free( bi->away_message );
g_free( bi );
@@ -494,39 +541,144 @@ int jabber_buddy_remove( struct im_connection *ic, char *full_jid_ )
/* Remove a buddy completely; removes all resources that belong to the
specified bare JID. Use this when removing someone from the contact
list, for example. */
-int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid_ )
+int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid )
{
struct jabber_data *jd = ic->proto_data;
struct jabber_buddy *bud, *next;
- char *bare_jid;
- if( strchr( bare_jid_, '/' ) )
+ if( strchr( bare_jid, '/' ) )
return 0;
- bare_jid = jabber_normalize( bare_jid_ );
-
- if( ( bud = g_hash_table_lookup( jd->buddies, bare_jid ) ) )
+ if( ( bud = jabber_buddy_by_jid( ic, bare_jid, GET_BUDDY_FIRST ) ) )
{
/* Most important: Remove the hash reference. We don't know
this buddy anymore. */
g_hash_table_remove( jd->buddies, bud->bare_jid );
+ g_free( bud->bare_jid );
/* Deallocate the linked list of resources. */
while( bud )
{
+ /* ext_jid && anonymous means that this buddy is
+ specific to one groupchat (the one we're
+ currently cleaning up) so it can be deleted
+ completely. */
+ if( bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS )
+ imcb_remove_buddy( ic, bud->ext_jid, NULL );
+
next = bud->next;
+ g_free( bud->ext_jid );
g_free( bud->full_jid );
g_free( bud->away_message );
g_free( bud );
bud = next;
}
- g_free( bare_jid );
return 1;
}
else
{
- g_free( bare_jid );
return 0;
}
}
+
+struct groupchat *jabber_chat_by_name( struct im_connection *ic, const char *name )
+{
+ char *normalized = jabber_normalize( name );
+ struct groupchat *ret;
+ struct jabber_chat *jc;
+
+ for( ret = ic->groupchats; ret; ret = ret->next )
+ {
+ jc = ret->data;
+ if( strcmp( normalized, jc->name ) == 0 )
+ break;
+ }
+ g_free( normalized );
+
+ return ret;
+}
+
+time_t jabber_get_timestamp( struct xt_node *xt )
+{
+ struct tm tp, utc;
+ struct xt_node *c;
+ time_t res, tres;
+ char *s = NULL;
+
+ for( c = xt->children; ( c = xt_find_node( c, "x" ) ); c = c->next )
+ {
+ if( ( s = xt_find_attr( c, "xmlns" ) ) && strcmp( s, XMLNS_DELAY ) == 0 )
+ break;
+ }
+
+ if( !c || !( s = xt_find_attr( c, "stamp" ) ) )
+ return 0;
+
+ memset( &tp, 0, sizeof( tp ) );
+ if( sscanf( s, "%4d%2d%2dT%2d:%2d:%2d", &tp.tm_year, &tp.tm_mon, &tp.tm_mday,
+ &tp.tm_hour, &tp.tm_min, &tp.tm_sec ) != 6 )
+ return 0;
+
+ tp.tm_year -= 1900;
+ tp.tm_mon --;
+ tp.tm_isdst = -1; /* GRRRRRRRRRRR */
+
+ res = mktime( &tp );
+ /* Problem is, mktime() just gave us the GMT timestamp for the
+ given local time... While the given time WAS NOT local. So
+ we should fix this now.
+
+ Now I could choose between messing with environment variables
+ (kludgy) or using timegm() (not portable)... Or doing the
+ following, which I actually prefer... */
+ gmtime_r( &res, &utc );
+ utc.tm_isdst = -1; /* Once more: GRRRRRRRRRRRRRRRRRR!!! */
+ if( utc.tm_hour == tp.tm_hour && utc.tm_min == tp.tm_min )
+ /* Sweet! We're in UTC right now... */
+ return res;
+
+ tres = mktime( &utc );
+ res += res - tres;
+
+ /* Yes, this is a hack. And it will go wrong around DST changes.
+ BUT this is more likely to be threadsafe than messing with
+ environment variables, and possibly more portable... */
+
+ return res;
+}
+
+struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns )
+{
+ struct jabber_error *err = g_new0( struct jabber_error, 1 );
+ struct xt_node *c;
+ char *s;
+
+ err->type = xt_find_attr( node, "type" );
+
+ for( c = node->children; c; c = c->next )
+ {
+ if( !( s = xt_find_attr( c, "xmlns" ) ) ||
+ strcmp( s, xmlns ) != 0 )
+ continue;
+
+ if( strcmp( c->name, "text" ) != 0 )
+ {
+ err->code = c->name;
+ }
+ /* Only use the text if it doesn't have an xml:lang attribute,
+ if it's empty or if it's set to something English. */
+ else if( !( s = xt_find_attr( c, "xml:lang" ) ) ||
+ !*s || strncmp( s, "en", 2 ) == 0 )
+ {
+ err->text = c->text;
+ }
+ }
+
+ return err;
+}
+
+void jabber_error_free( struct jabber_error *err )
+{
+ g_free( err );
+}
diff --git a/protocols/jabber/message.c b/protocols/jabber/message.c
index 19edbdfd..fab62a91 100644
--- a/protocols/jabber/message.c
+++ b/protocols/jabber/message.c
@@ -29,25 +29,33 @@ xt_status jabber_pkt_message( struct xt_node *node, gpointer data )
char *from = xt_find_attr( node, "from" );
char *type = xt_find_attr( node, "type" );
struct xt_node *body = xt_find_node( node->children, "body" ), *c;
+ struct jabber_buddy *bud = NULL;
char *s;
+ if( !from )
+ return XT_HANDLED; /* Consider this packet corrupted. */
+
+ bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT );
+
if( type && strcmp( type, "error" ) == 0 )
{
/* Handle type=error packet. */
}
- else if( type && strcmp( type, "groupchat" ) == 0 )
+ else if( type && from && strcmp( type, "groupchat" ) == 0 )
{
- /* TODO! */
+ jabber_chat_pkt_message( ic, bud, node );
}
else /* "chat", "normal", "headline", no-type or whatever. Should all be pretty similar. */
{
- struct jabber_buddy *bud = NULL;
GString *fullmsg = g_string_new( "" );
if( ( s = strchr( from, '/' ) ) )
{
- if( ( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) ) )
+ if( bud )
+ {
bud->last_act = time( NULL );
+ from = bud->ext_jid ? : bud->bare_jid;
+ }
else
*s = 0; /* We need to generate a bare JID now. */
}
@@ -75,26 +83,31 @@ xt_status jabber_pkt_message( struct xt_node *node, gpointer data )
fullmsg = g_string_append( fullmsg, body->text );
if( fullmsg->len > 0 )
- imcb_buddy_msg( ic, bud ? bud->bare_jid : from, fullmsg->str, 0, 0 );
+ imcb_buddy_msg( ic, from, fullmsg->str,
+ 0, jabber_get_timestamp( node ) );
g_string_free( fullmsg, TRUE );
/* Handling of incoming typing notifications. */
- if( xt_find_node( node->children, "composing" ) )
+ if( bud == NULL )
+ {
+ /* Can't handle these for unknown buddies. */
+ }
+ else if( xt_find_node( node->children, "composing" ) )
{
bud->flags |= JBFLAG_DOES_XEP85;
- imcb_buddy_typing( ic, bud ? bud->bare_jid : from, OPT_TYPING );
+ imcb_buddy_typing( ic, from, OPT_TYPING );
}
/* No need to send a "stopped typing" signal when there's a message. */
else if( xt_find_node( node->children, "active" ) && ( body == NULL ) )
{
bud->flags |= JBFLAG_DOES_XEP85;
- imcb_buddy_typing( ic, bud ? bud->bare_jid : from, 0 );
+ imcb_buddy_typing( ic, from, 0 );
}
else if( xt_find_node( node->children, "paused" ) )
{
bud->flags |= JBFLAG_DOES_XEP85;
- imcb_buddy_typing( ic, bud ? bud->bare_jid : from, OPT_THINKING );
+ imcb_buddy_typing( ic, from, OPT_THINKING );
}
if( s )
diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c
index ef92740a..cbfcedae 100644
--- a/protocols/jabber/presence.c
+++ b/protocols/jabber/presence.c
@@ -30,15 +30,22 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )
char *type = xt_find_attr( node, "type" ); /* NULL should mean the person is online. */
struct xt_node *c;
struct jabber_buddy *bud;
+ int is_chat = 0, is_away = 0;
char *s;
if( !from )
return XT_HANDLED;
+ if( ( s = strchr( from, '/' ) ) )
+ {
+ *s = 0;
+ if( jabber_chat_by_name( ic, from ) )
+ is_chat = 1;
+ *s = '/';
+ }
+
if( type == NULL )
{
- int is_away = 0;
-
if( !( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT | GET_BUDDY_CREAT ) ) )
{
if( set_getbool( &ic->irc->set, "debug" ) )
@@ -71,30 +78,55 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )
else
bud->priority = 0;
- if( bud == jabber_buddy_by_jid( ic, bud->bare_jid, 0 ) )
+ if( is_chat )
+ jabber_chat_pkt_presence( ic, bud, node );
+ else if( bud == jabber_buddy_by_jid( ic, bud->bare_jid, 0 ) )
imcb_buddy_status( ic, bud->bare_jid, OPT_LOGGED_IN | is_away,
( is_away && bud->away_state ) ? bud->away_state->full_name : NULL,
bud->away_message );
}
else if( strcmp( type, "unavailable" ) == 0 )
{
- if( jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) == NULL )
+ if( ( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) ) == NULL )
{
if( set_getbool( &ic->irc->set, "debug" ) )
imcb_log( ic, "WARNING: Received presence information from unknown JID: %s", from );
return XT_HANDLED;
}
+ /* Handle this before we delete the JID. */
+ if( is_chat )
+ {
+ jabber_chat_pkt_presence( ic, bud, node );
+ }
+
jabber_buddy_remove( ic, from );
- if( ( s = strchr( from, '/' ) ) )
+ if( is_chat )
+ {
+ /* Nothing else to do for now? */
+ }
+ else if( ( s = strchr( from, '/' ) ) )
{
*s = 0;
- /* Only count this as offline if there's no other resource
- available anymore. */
- if( jabber_buddy_by_jid( ic, from, 0 ) == NULL )
+ /* If another resource is still available, send its presence
+ information. */
+ if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) )
+ {
+ if( bud->away_state && ( *bud->away_state->code == 0 ||
+ strcmp( bud->away_state->code, "chat" ) == 0 ) )
+ is_away = OPT_AWAY;
+
+ imcb_buddy_status( ic, bud->bare_jid, OPT_LOGGED_IN | is_away,
+ ( is_away && bud->away_state ) ? bud->away_state->full_name : NULL,
+ bud->away_message );
+ }
+ else
+ {
+ /* Otherwise, count him/her as offline now. */
imcb_buddy_status( ic, from, 0, NULL, NULL );
+ }
*s = '/';
}
@@ -125,7 +157,17 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )
}
else if( strcmp( type, "error" ) == 0 )
{
- /* What to do with it? */
+ struct jabber_error *err;
+
+ if( ( c = xt_find_node( node->children, "error" ) ) )
+ {
+ err = jabber_error_parse( c, XMLNS_STANZA_ERROR );
+ imcb_error( ic, "Stanza (%s) error: %s%s%s", node->name,
+ err->code, err->text ? ": " : "",
+ err->text ? err->text : "" );
+ jabber_error_free( err );
+ }
+ /* What else to do with it? */
}
else
{
@@ -144,6 +186,7 @@ int presence_send_update( struct im_connection *ic )
struct xt_node *node;
char *show = jd->away_state->code;
char *status = jd->away_message;
+ struct groupchat *c;
int st;
node = jabber_make_packet( "presence", NULL, NULL, NULL );
@@ -155,6 +198,16 @@ int presence_send_update( struct im_connection *ic )
st = jabber_write_packet( ic, node );
+ /* Have to send this update to all groupchats too, the server won't
+ do this automatically. */
+ for( c = ic->groupchats; c && st; c = c->next )
+ {
+ struct jabber_chat *jc = c->data;
+
+ xt_add_attr( node, "to", jc->my_full_jid );
+ st = jabber_write_packet( ic, node );
+ }
+
xt_free_node( node );
return st;
}
diff --git a/protocols/jabber/xmltree.c b/protocols/jabber/xmltree.c
index 7a165a1e..b1edd55d 100644
--- a/protocols/jabber/xmltree.c
+++ b/protocols/jabber/xmltree.c
@@ -441,7 +441,7 @@ void xt_free( struct xt_parser *xt )
/* To find a node's child with a specific name, pass the node's children
list, not the node itself! The reason you have to do this by hand: So
that you can also use this function as a find-next. */
-struct xt_node *xt_find_node( struct xt_node *node, char *name )
+struct xt_node *xt_find_node( struct xt_node *node, const char *name )
{
while( node )
{
@@ -454,7 +454,7 @@ struct xt_node *xt_find_node( struct xt_node *node, char *name )
return node;
}
-char *xt_find_attr( struct xt_node *node, char *key )
+char *xt_find_attr( struct xt_node *node, const char *key )
{
int i;
@@ -523,7 +523,7 @@ void xt_add_child( struct xt_node *parent, struct xt_node *child )
}
}
-void xt_add_attr( struct xt_node *node, char *key, char *value )
+void xt_add_attr( struct xt_node *node, const char *key, const char *value )
{
int i;
@@ -550,7 +550,7 @@ void xt_add_attr( struct xt_node *node, char *key, char *value )
node->attr[i].value = g_strdup( value );
}
-int xt_remove_attr( struct xt_node *node, char *key )
+int xt_remove_attr( struct xt_node *node, const char *key )
{
int i, last;
diff --git a/protocols/jabber/xmltree.h b/protocols/jabber/xmltree.h
index 70850c1d..b8b61641 100644
--- a/protocols/jabber/xmltree.h
+++ b/protocols/jabber/xmltree.h
@@ -86,12 +86,12 @@ void xt_print( struct xt_node *node );
struct xt_node *xt_dup( struct xt_node *node );
void xt_free_node( struct xt_node *node );
void xt_free( struct xt_parser *xt );
-struct xt_node *xt_find_node( struct xt_node *node, char *name );
-char *xt_find_attr( struct xt_node *node, char *key );
+struct xt_node *xt_find_node( struct xt_node *node, const char *name );
+char *xt_find_attr( struct xt_node *node, const char *key );
struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children );
void xt_add_child( struct xt_node *parent, struct xt_node *child );
-void xt_add_attr( struct xt_node *node, char *key, char *value );
-int xt_remove_attr( struct xt_node *node, char *key );
+void xt_add_attr( struct xt_node *node, const char *key, const char *value );
+int xt_remove_attr( struct xt_node *node, const char *key );
#endif
diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c
index 1693cb95..cb9e2cab 100644
--- a/protocols/msn/sb.c
+++ b/protocols/msn/sb.c
@@ -231,7 +231,7 @@ void msn_sb_destroy( struct msn_switchboard *sb )
if( sb->chat )
{
- imcb_chat_removed( sb->chat );
+ imcb_chat_free( sb->chat );
}
if( sb->handler )
diff --git a/protocols/nogaim.c b/protocols/nogaim.c
index d90870ad..3307b0f5 100644
--- a/protocols/nogaim.c
+++ b/protocols/nogaim.c
@@ -98,7 +98,6 @@ void register_protocol (struct prpl *p)
protocols = g_list_append(protocols, p);
}
-
struct prpl *find_protocol(const char *name)
{
GList *gl;
@@ -288,7 +287,7 @@ void cancel_auto_reconnect( account_t *a )
void imc_logout( struct im_connection *ic, int allow_reconnect )
{
irc_t *irc = ic->irc;
- user_t *t, *u = irc->users;
+ user_t *t, *u;
account_t *a;
/* Nested calls might happen sometimes, this is probably the best
@@ -305,6 +304,7 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )
ic->acc->prpl->logout( ic );
b_event_remove( ic->inpa );
+ u = irc->users;
while( u )
{
if( u->ic == ic )
@@ -427,7 +427,6 @@ struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle )
void imcb_rename_buddy( struct im_connection *ic, char *handle, char *realname )
{
user_t *u = user_findhandle( ic, handle );
- char *s, newnick[MAX_NICK_LENGTH+1];
if( !u || !realname ) return;
@@ -439,31 +438,54 @@ 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 );
+ }
+}
+
+void imcb_remove_buddy( struct im_connection *ic, char *handle, char *group )
+{
+ user_t *u;
+
+ if( ( u = user_findhandle( ic, handle ) ) )
+ user_del( ic->irc, u->nick );
+}
+
+/* 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 )
+{
+ user_t *u = user_findhandle( ic, handle );
+ char newnick[MAX_NICK_LENGTH+1], *orig_nick;
+
+ if( u && !u->online && !nick_saved( ic->acc, handle ) )
+ {
+ /* Only do this if the person isn't online yet (which should
+ be the case if we just added it) and if the user hasn't
+ assigned a nickname to this buddy already. */
+
+ strncpy( newnick, nick, MAX_NICK_LENGTH );
+ newnick[MAX_NICK_LENGTH] = 0;
- if( !u->online && !nick_saved( ic->acc, handle ) )
+ /* Some processing to make sure this string is a valid IRC nickname. */
+ nick_strip( newnick );
+ if( set_getbool( &ic->irc->set, "lcnicks" ) )
+ nick_lc( newnick );
+
+ if( strcmp( u->nick, newnick ) != 0 )
{
- /* Detect numeric handles: */
- for( s = u->user; isdigit( *s ); s++ );
+ /* Only do this if newnick is different from the current one.
+ If rejoining a channel, maybe we got this nick already
+ (and dedupe would only add an underscore. */
+ nick_dedupe( ic->acc, handle, newnick );
- if( *s == 0 )
- {
- /* If we reached the end of the string, it only contained numbers.
- Seems to be an ICQ# then, so hopefully realname contains
- something more useful. */
- strcpy( newnick, realname );
-
- /* Some processing to make sure this string is a valid IRC nickname. */
- nick_strip( newnick );
- if( set_getbool( &ic->irc->set, "lcnicks" ) )
- nick_lc( newnick );
-
- u->nick = g_strdup( newnick );
- }
+ /* u->nick will be freed halfway the process, so it can't be
+ passed as an argument. */
+ orig_nick = g_strdup( u->nick );
+ user_rename( ic->irc, orig_nick, newnick );
+ g_free( orig_nick );
}
}
}
-
/* prpl.c */
struct show_got_added_data
@@ -553,8 +575,8 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags,
irc_kill( ic->irc, u );
u->online = 0;
- /* Remove him/her from the conversations to prevent PART messages after he/she QUIT already */
- for( c = ic->conversations; c; c = c->next )
+ /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */
+ for( c = ic->groupchats; c; c = c->next )
remove_chat_buddy_silent( c, (char*) handle );
}
@@ -669,10 +691,10 @@ void imcb_buddy_typing( struct im_connection *ic, char *handle, u_int32_t flags
}
}
-void imcb_chat_removed( struct groupchat *c )
+void imcb_chat_free( struct groupchat *c )
{
struct im_connection *ic = c->ic;
- struct groupchat *l = NULL;
+ struct groupchat *l;
GList *ir;
if( set_getbool( &ic->irc->set, "debug" ) )
@@ -692,10 +714,13 @@ void imcb_chat_removed( struct groupchat *c )
/* irc_part( ic->irc, u, c->channel ); */
}
+ /* Find the previous chat in the linked list. */
+ for( l = ic->groupchats; l && l->next != c; l = l->next );
+
if( l )
l->next = c->next;
else
- ic->conversations = c->next;
+ ic->groupchats = c->next;
for( ir = c->in_room; ir; ir = ir->next )
g_free( ir->data );
@@ -740,13 +765,13 @@ struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle )
/* This one just creates the conversation structure, user won't see anything yet */
- if( ic->conversations )
+ if( ic->groupchats )
{
- for( c = ic->conversations; c->next; c = c->next );
+ for( c = ic->groupchats; c->next; c = c->next );
c = c->next = g_new0( struct groupchat, 1 );
}
else
- ic->conversations = c = g_new0( struct groupchat, 1 );
+ ic->groupchats = c = g_new0( struct groupchat, 1 );
c->ic = ic;
c->title = g_strdup( handle );
@@ -795,6 +820,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 )
{
user_t *u;
@@ -806,6 +832,9 @@ void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason )
/* It might be yourself! */
if( g_strcasecmp( handle, b->ic->acc->user ) == 0 )
{
+ if( b->joined == 0 )
+ return;
+
u = user_find( b->ic->irc, b->ic->irc->nick );
b->joined = 0;
me = 1;
@@ -815,9 +844,8 @@ void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason )
u = user_findhandle( b->ic, handle );
}
- if( remove_chat_buddy_silent( b, handle ) )
- if( ( b->joined || me ) && u )
- irc_part( b->ic->irc, u, b->channel );
+ if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) )
+ irc_part( b->ic->irc, u, b->channel );
}
static int remove_chat_buddy_silent( struct groupchat *b, char *handle )
@@ -844,24 +872,6 @@ static int remove_chat_buddy_silent( struct groupchat *b, char *handle )
/* Misc. BitlBee stuff which shouldn't really be here */
-struct groupchat *chat_by_channel( char *channel )
-{
- struct im_connection *ic;
- struct groupchat *c;
- GSList *l;
-
- /* This finds the connection which has a conversation which belongs to this channel */
- for( l = connections; l; l = l->next )
- {
- ic = l->data;
- for( c = ic->conversations; c && g_strcasecmp( c->channel, channel ) != 0; c = c->next );
- if( c )
- return c;
- }
-
- return NULL;
-}
-
char *set_eval_away_devoice( set_t *set, char *value )
{
irc_t *irc = set->data;
@@ -1094,3 +1104,35 @@ void imc_rem_block( struct im_connection *ic, char *handle )
ic->acc->prpl->rem_deny( ic, handle );
}
+
+void imcb_clean_handle( struct im_connection *ic, char *handle )
+{
+ /* Accepts a handle and does whatever is necessary to make it
+ BitlBee-friendly. Currently this means removing everything
+ outside 33-127 (ASCII printable excl spaces), @ (only one
+ is allowed) and ! and : */
+ char out[strlen(handle)+1];
+ int s, d;
+
+ s = d = 0;
+ while( handle[s] )
+ {
+ if( handle[s] > ' ' && handle[s] != '!' && handle[s] != ':' &&
+ ( handle[s] & 0x80 ) == 0 )
+ {
+ if( handle[s] == '@' )
+ {
+ /* See if we got an @ already? */
+ out[d] = 0;
+ if( strchr( out, '@' ) )
+ continue;
+ }
+
+ out[d++] = handle[s];
+ }
+ s ++;
+ }
+ out[d] = handle[s];
+
+ strcpy( handle, out );
+}
diff --git a/protocols/nogaim.h b/protocols/nogaim.h
index ff4cdb96..5bf6d922 100644
--- a/protocols/nogaim.h
+++ b/protocols/nogaim.h
@@ -60,6 +60,7 @@
#define OPT_LOGGING_OUT 0x00000002
#define OPT_AWAY 0x00000004
#define OPT_DOES_HTML 0x00000010
+#define OPT_LOCALBUDDY 0x00000020 /* For nicks local to one groupchat */
#define OPT_TYPING 0x00000100 /* Some pieces of code make assumptions */
#define OPT_THINKING 0x00000200 /* about these values... Stupid me! */
@@ -90,7 +91,7 @@ struct im_connection
/* BitlBee */
irc_t *irc;
- struct groupchat *conversations;
+ struct groupchat *groupchats;
};
struct groupchat {
@@ -103,7 +104,6 @@ struct groupchat {
GList *in_room;
GList *ignored;
- /* BitlBee */
struct groupchat *next;
char *channel;
/* The title variable contains the ID you gave when you created the
@@ -264,6 +264,7 @@ G_MODULE_EXPORT void imcb_add_buddy( struct im_connection *ic, char *handle, cha
G_MODULE_EXPORT void imcb_remove_buddy( struct im_connection *ic, char *handle, char *group );
G_MODULE_EXPORT struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle );
G_MODULE_EXPORT void imcb_rename_buddy( struct im_connection *ic, char *handle, char *realname );
+G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick );
/* Buddy activity */
/* To manipulate the status of a handle.
@@ -275,6 +276,7 @@ G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *ha
/* Call when a handle says something. 'flags' and 'sent_at may be just 0. */
G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, u_int32_t flags, time_t sent_at );
G_MODULE_EXPORT void imcb_buddy_typing( struct im_connection *ic, char *handle, u_int32_t flags );
+G_MODULE_EXPORT void imcb_clean_handle( struct im_connection *ic, char *handle );
/* Groupchats */
G_MODULE_EXPORT void imcb_chat_invited( struct im_connection *ic, char *handle, char *who, char *msg, GList *data );
@@ -290,8 +292,7 @@ G_MODULE_EXPORT void imcb_chat_add_buddy( struct groupchat *b, char *handle );
G_MODULE_EXPORT void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason );
/* To tell BitlBee 'who' said 'msg' in 'c'. 'flags' and 'sent_at' can be 0. */
G_MODULE_EXPORT void imcb_chat_msg( struct groupchat *c, char *who, char *msg, u_int32_t flags, time_t sent_at );
-G_MODULE_EXPORT void imcb_chat_removed( struct groupchat *c );
-struct groupchat *chat_by_channel( char *channel );
+G_MODULE_EXPORT void imcb_chat_free( struct groupchat *c );
/* Actions, or whatever. */
int imc_set_away( struct im_connection *ic, char *away );
diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c
index 426dd4a2..4d18e832 100644
--- a/protocols/oscar/oscar.c
+++ b/protocols/oscar/oscar.c
@@ -2021,6 +2021,7 @@ static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
imcb_add_buddy(ic, curitem->name, NULL);
if (realname) {
+ imcb_buddy_nick_hint(ic, curitem->name, realname);
imcb_rename_buddy(ic, curitem->name, realname);
g_free(realname);
}
@@ -2513,7 +2514,7 @@ void oscar_chat_kill(struct im_connection *ic, struct chat_connection *cc)
struct oscar_data *od = (struct oscar_data *)ic->proto_data;
/* Notify the conversation window that we've left the chat */
- imcb_chat_removed(cc->cnv);
+ imcb_chat_free(cc->cnv);
/* Destroy the chat_connection */
od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c
index 69fc29bb..28a72877 100644
--- a/protocols/yahoo/yahoo.c
+++ b/protocols/yahoo/yahoo.c
@@ -144,8 +144,8 @@ static void byahoo_logout( struct im_connection *ic )
struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data;
GSList *l;
- while( ic->conversations )
- imcb_chat_removed( ic->conversations );
+ while( ic->groupchats )
+ imcb_chat_free( ic->groupchats );
for( l = yd->buddygroups; l; l = l->next )
{
@@ -317,7 +317,7 @@ static void byahoo_chat_leave( struct groupchat *c )
struct byahoo_data *yd = (struct byahoo_data *) c->ic->proto_data;
yahoo_conference_logoff( yd->y2_id, NULL, c->data, c->title );
- imcb_chat_removed( c );
+ imcb_chat_free( c );
}
static struct groupchat *byahoo_chat_with( struct im_connection *ic, char *who )
@@ -797,7 +797,7 @@ static void byahoo_accept_conf( gpointer w, struct byahoo_conf_invitation *inv )
static void byahoo_reject_conf( gpointer w, struct byahoo_conf_invitation *inv )
{
yahoo_conference_decline( inv->yid, NULL, inv->members, inv->name, "User rejected groupchat" );
- imcb_chat_removed( inv->c );
+ imcb_chat_free( inv->c );
g_free( inv->name );
g_free( inv );
}
@@ -840,7 +840,7 @@ void ext_yahoo_conf_userjoin( int id, const char *ignored, const char *who, cons
struct im_connection *ic = byahoo_get_ic_by_id( id );
struct groupchat *c;
- for( c = ic->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
+ for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next );
if( c )
imcb_chat_add_buddy( c, (char*) who );
@@ -852,7 +852,7 @@ void ext_yahoo_conf_userleave( int id, const char *ignored, const char *who, con
struct im_connection *ic = byahoo_get_ic_by_id( id );
struct groupchat *c;
- for( c = ic->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
+ for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next );
if( c )
imcb_chat_remove_buddy( c, (char*) who, "" );
@@ -864,7 +864,7 @@ void ext_yahoo_conf_message( int id, const char *ignored, const char *who, const
char *m = byahoo_strip( msg );
struct groupchat *c;
- for( c = ic->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
+ for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next );
if( c )
imcb_chat_msg( c, (char*) who, (char*) m, 0, 0 );
diff --git a/root_commands.c b/root_commands.c
index baaf3354..e8c796d3 100644
--- a/root_commands.c
+++ b/root_commands.c
@@ -911,24 +911,49 @@ static void cmd_join_chat( irc_t *irc, char **cmd )
chat = cmd[2];
if( cmd[3] )
{
- channel = g_strdup( cmd[3] );
+ if( cmd[3][0] != '#' && cmd[3][0] != '&' )
+ channel = g_strdup_printf( "&%s", cmd[3] );
+ else
+ channel = g_strdup( cmd[3] );
}
else
{
char *s;
- channel = g_strdup( chat );
+ channel = g_strdup_printf( "&%s", chat );
if( ( s = strchr( channel, '@' ) ) )
*s = 0;
}
if( cmd[3] && cmd[4] )
nick = cmd[4];
+ else
+ nick = irc->nick;
if( cmd[3] && cmd[4] && cmd[5] )
password = cmd[5];
- c = a->prpl->chat_join( ic, chat, nick, password );
+ if( !nick_ok( channel + 1 ) )
+ {
+ irc_usermsg( irc, "Invalid channel name: %s", channel );
+ g_free( channel );
+ return;
+ }
+ else if( g_strcasecmp( channel, irc->channel ) == 0 || irc_chat_by_channel( irc, channel ) )
+ {
+ irc_usermsg( irc, "Channel already exists: %s", channel );
+ g_free( channel );
+ return;
+ }
- g_free( channel );
+ if( ( c = a->prpl->chat_join( ic, chat, nick, password ) ) )
+ {
+ g_free( c->channel );
+ c->channel = channel;
+ }
+ else
+ {
+ irc_usermsg( irc, "Tried to join chat, not sure if this was successful" );
+ g_free( channel );
+ }
}
const command_t commands[] = {
diff --git a/user.c b/user.c
index 232e3a58..26676dd4 100644
--- a/user.c
+++ b/user.c
@@ -160,6 +160,7 @@ user_t *user_findhandle( struct im_connection *ic, char *handle )
return NULL;
}
+/* DO NOT PASS u->nick FOR oldnick !!! */
void user_rename( irc_t *irc, char *oldnick, char *newnick )
{
user_t *u = user_find( irc, oldnick );