aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/msn/ns.c
blob: ffb21c4779e2072cd5de308c79bfcaff6ec42e8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
<
  /********************************************************************\
  * BitlBee -- An IRC to other IM-networks gateway                     *
  *                                                                    *
  * Copyright 2002-2008 Wilmer van der Gaast and others                *
  \********************************************************************/

/* Keep track of chatrooms the user is interested in                    */

/*
  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
*/

#include "bitlbee.h"
#include "chat.h"

struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel )
{
	struct chat *c, *l;
	set_t *s;

	if( acc->prpl->chat_join == NULL || !chat_chanok( channel ) ||
	    chat_chancmp( channel, irc->channel ) == 0 )
	{
		return NULL;
	}
	
	for( c = irc->chatrooms; c; c = c->next )
	{
		if( chat_chancmp( channel, c->channel ) == 0 )
			return NULL;
		
		if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 )
			return NULL;
		
		l = c;
	}
	
	if( irc->chatrooms == NULL )
		irc->chatrooms = c = g_new0( struct chat, 1 );
	else
		l->next = c = g_new0( struct chat, 1 );
	
	c->acc = acc;
	c->handle = g_strdup( handle );
	c->channel = g_strdup( channel );
	
	s = set_add( &c->set, "auto_join", "false", set_eval_bool, c );
	/* s = set_add( &c->set, "auto_rejoin", "false", set_eval_bool, c ); */
	s = set_add( &c->set, "nick", NULL, NULL, c );
	s->flags |= SET_NULL_OK;
	
	return c;
}

struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle )
{
	struct chat *c;
	
	for( c = irc->chatrooms; c; c = c->next )
	{
		if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 )
			break;
	}
	
	return c;
}

struct chat *chat_bychannel( irc_t *irc, char *channel )
{
	struct chat *c;
	
	for( c = irc->chatrooms; c; c = c->next )
	{
		if( chat_chancmp( channel, c->channel ) == 0 )
			break;
	}
	
	return c;
}

pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
  /********************************************************************\
  * BitlBee -- An IRC to other IM-networks gateway                     *
  *                                                                    *
  * Copyright 2002-2004 Wilmer van der Gaast and others                *
  \********************************************************************/

/* MSN module - Notification server callbacks                           */

/*
  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
*/

#include <ctype.h>
#include "nogaim.h"
#include "msn.h"
#include "md5.h"
#include "soap.h"
#include "xmltree.h"

static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond );
static int msn_ns_command( gpointer data, char **cmd, int num_parts );
static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );

static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name );
static void msn_ns_send_adl( struct im_connection *ic );

gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )
{
	struct im_connection *ic = data;
	struct msn_data *md;
	char s[1024];
	
	if( !g_slist_find( msn_connections, ic ) )
		return FALSE;
	
	if( source == -1 )
	{
		imcb_error( ic, "Could not connect to server" );
		imc_logout( ic, TRUE );
		return FALSE;
	}
	
	md = ic->proto_data;
	
	if( !md->handler )
	{
		md->handler = g_new0( struct msn_handler_data, 1 );
		md->handler->data = ic;
		md->handler->exec_command = msn_ns_command;
		md->handler->exec_message = msn_ns_message;
	}
	else
	{
		if( md->handler->rxq )
			g_free( md->handler->rxq );
		
		md->handler->rxlen = 0;
	}
	
	md->handler->fd = md->fd;
	md->handler->rxq = g_new0( char, 1 );
	
	g_snprintf( s, sizeof( s ), "VER %d MSNP15 CVR0\r\n", ++md->trId );
	if( msn_write( ic, s, strlen( s ) ) )
	{
		ic->inpa = b_input_add( md->fd, B_EV_IO_READ, msn_ns_callback, ic );
		imcb_log( ic, "Connected to server, waiting for reply" );
	}
	
	return FALSE;
}

static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond )
{
	struct im_connection *ic = data;
	struct msn_data *md = ic->proto_data;
	
	if( msn_handler( md->handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */
	{
		imcb_error( ic, "Error while reading from server" );
		imc_logout( ic, TRUE );
		
		return FALSE;
	}
	else
		return TRUE;
}

static int msn_ns_command( gpointer data, char **cmd, int num_parts )
{
	struct im_connection *ic = data;
	struct msn_data *md = ic->proto_data;
	char buf[1024];
	
	if( num_parts == 0 )
	{
		/* Hrrm... Empty command...? Ignore? */
		return( 1 );
	}
	
	if( strcmp( cmd[0], "VER" ) == 0 )
	{
		if( cmd[2] && strncmp( cmd[2], "MSNP15", 5 ) != 0 )
		{
			imcb_error( ic, "Unsupported protocol" );
			imc_logout( ic, FALSE );
			return( 0 );
		}
		
		g_snprintf( buf, sizeof( buf ), "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n",
		                                ++md->trId, ic->acc->user );
		return( msn_write( ic, buf, strlen( buf ) ) );
	}
	else if( strcmp( cmd[0], "CVR" ) == 0 )
	{
		/* We don't give a damn about the information we just received */
		g_snprintf( buf, sizeof( buf ), "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user );
		return( msn_write( ic, buf, strlen( buf ) ) );
	}
	else if( strcmp( cmd[0], "XFR" ) == 0 )
	{
		char *server;
		int port;
		
		if( num_parts >= 6 && strcmp( cmd[2], "NS" ) == 0 )
		{
			b_event_remove( ic->inpa );
			ic->inpa = 0;
			closesocket( md->fd );
			
			server = strchr( cmd[3], ':' );
			if( !server )
			{
				imcb_error( ic, "Syntax error" );
				imc_logout( ic, TRUE );
				return( 0 );
			}
			*server = 0;
			port = atoi( server + 1 );
			server = cmd[3];
			
			imcb_log( ic, "Transferring to other server" );
			
			md->fd = proxy_connect( server, port, msn_ns_connected, ic );
		}
		else if( num_parts >= 6 && strcmp( cmd[2], "SB" ) == 0 )
		{
			struct msn_switchboard *sb;
			
			server = strchr( cmd[3], ':' );
			if( !server )
			{
				imcb_error( ic, "Syntax error" );
				imc_logout( ic, TRUE );
				return( 0 );
			}
			*server = 0;
			port = atoi( server + 1 );
			server = cmd[3];
			
			if( strcmp( cmd[4], "CKI" ) != 0 )
			{
				imcb_error( ic, "Unknown authentication method for switchboard" );
				imc_logout( ic, TRUE );
				return( 0 );
			}
			
			debug( "Connecting to a new switchboard with key %s", cmd[5] );

			if( ( sb = msn_sb_create( ic, server, port, cmd[5], MSN_SB_NEW ) ) == NULL )
			{
				/* Although this isn't strictly fatal for the NS connection, it's
				   definitely something serious (we ran out of file descriptors?). */
				imcb_error( ic, "Could not create new switchboard" );
				imc_logout( ic, TRUE );
				return( 0 );
			}
			
			if( md->msgq )
			{
				struct msn_message *m = md->msgq->data;
				GSList *l;
				
				sb->who = g_strdup( m->who );
				
				/* Move all the messages to the first user in the message
				   queue to the switchboard message queue. */
				l = md->msgq;
				while( l )
				{
					m = l->data;
					l = l->next;
					if( strcmp( m->who, sb->who ) == 0 )
					{
						sb->msgq = g_slist_append( sb->msgq, m );
						md->msgq = g_slist_remove( md->msgq, m );
					}
				}
			}
		}
		else
		{
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
	}
	else if( strcmp( cmd[0], "USR" ) == 0 )
	{
		if( num_parts >= 6 && strcmp( cmd[2], "SSO" ) == 0 &&
		    strcmp( cmd[3], "S" ) == 0 )
		{
			msn_soap_passport_sso_request( ic, cmd[4], cmd[5] );
		}
		else if( strcmp( cmd[2], "OK" ) == 0 )
		{
			if( num_parts == 7 )
				msn_ns_got_display_name( ic, cmd[4] );
			else
				imcb_log( ic, "Warning: Friendly name in server response was corrupted" );
			
			imcb_log( ic, "Authenticated, getting buddy list" );
			
			msn_soap_memlist_request( ic );
		}
		else
		{
			imcb_error( ic, "Unknown authentication type" );
			imc_logout( ic, FALSE );
			return( 0 );
		}
	}
	else if( strcmp( cmd[0], "MSG" ) == 0 )
	{
		if( num_parts != 4 )
		{
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
		
		md->handler->msglen = atoi( cmd[3] );
		
		if( md->handler->msglen <= 0 )
		{
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
	}
	else if( strcmp( cmd[0], "BLP" ) == 0 )
	{
		msn_ns_send_adl( ic );
	}
	else if( strcmp( cmd[0], "ADL" ) == 0 )
	{
		if( num_parts >= 3 && strcmp( cmd[2], "OK" ) == 0 )
		{
			char buf[1024];
			char *fn_raw = set_getstr( &ic->acc->set, "display_name" );
			char *fn;
			
			if( fn_raw == NULL )
				fn_raw = ic->acc->user;
			fn = g_malloc( strlen( fn_raw ) * 3 + 1 );
			strcpy( fn, fn_raw );
			http_encode( fn );
			
			g_snprintf( buf, sizeof( buf ), "PRP %d MFN %s\r\n",
			            ++md->trId, fn );
			g_free( fn );
			
			msn_write( ic, buf, strlen( buf ) );
		}
	}
	else if( strcmp( cmd[0], "PRP" ) == 0 )
	{
		imcb_connected( ic );
	}
	else if( strcmp( cmd[0], "CHL" ) == 0 )
	{
		md5_state_t state;
		md5_byte_t digest[16];
		int i;
		
		if( num_parts != 3 )
		{
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
		
		md5_init( &state );
		md5_append( &state, (const md5_byte_t *) cmd[2], strlen( cmd[2] ) );
		md5_append( &state, (const md5_byte_t *) QRY_CODE, strlen( QRY_CODE ) );
		md5_finish( &state, digest );
		
		g_snprintf( buf, sizeof( buf ), "QRY %d %s %d\r\n", ++md->trId, QRY_NAME, 32 );
		for( i = 0; i < 16; i ++ )
			g_snprintf( buf + strlen( buf ), 3, "%02x", digest[i] );
		
		return( msn_write( ic, buf, strlen( buf ) ) );
	}
	else if( strcmp( cmd[0], "ILN" ) == 0 )
	{
		const struct msn_away_state *st;
		
		if( num_parts < 6 )
		{
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
		
		http_decode( cmd[5] );
		imcb_rename_buddy( ic, cmd[3], cmd[5] );
		
		st = msn_away_state_by_code( cmd[2] );
		if( !st )
		{
			/* FIXME: Warn/Bomb about unknown away state? */
			st = msn_away_state_list + 1;
		}
		
		imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN | 
		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ),
		                   st->name, NULL );
	}
	else if( strcmp( cmd[0], "FLN" ) == 0 )
	{
		if( cmd[1] == NULL )
			return 1;
		
		imcb_buddy_status( ic, cmd[1], 0, NULL, NULL );
		
		msn_sb_start_keepalives( msn_sb_by_handle( ic, cmd[1] ), TRUE );
	}
	else if( strcmp( cmd[0], "NLN" ) == 0 )
	{
		const struct msn_away_state *st;
		
		if( num_parts != 5 )
		{
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
		
		http_decode( cmd[3] );
		imcb_rename_buddy( ic, cmd[2], cmd[3] );
		
		st = msn_away_state_by_code( cmd[1] );
		if( !st )
		{
			/* FIXME: Warn/Bomb about unknown away state? */
			st = msn_away_state_list + 1;
		}
		
		imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN | 
		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ),
		                   st->name, NULL );
		
		msn_sb_stop_keepalives( msn_sb_by_handle( ic, cmd[2] ) );
	}
	else if( strcmp( cmd[0], "RNG" ) == 0 )
	{
		struct msn_switchboard *sb;
		char *server;
		int session, port;
		
		if( num_parts != 7 )
		{
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
		
		session = atoi( cmd[1] );
		
		server = strchr( cmd[2], ':' );
		if( !server )
		{
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
		*server = 0;
		port = atoi( server + 1 );
		server = cmd[2];
		
		if( strcmp( cmd[3], "CKI" ) != 0 )
		{
			imcb_error( ic, "Unknown authentication method for switchboard" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
		
		debug( "Got a call from %s (session %d). Key = %s", cmd[5], session, cmd[4] );
		
		if( ( sb = msn_sb_create( ic, server, port, cmd[4], session ) ) == NULL )
		{
			/* Although this isn't strictly fatal for the NS connection, it's
			   definitely something serious (we ran out of file descriptors?). */
			imcb_error( ic, "Could not create new switchboard" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
		else
		{
			sb->who = g_strdup( cmd[5] );
		}
	}
	else if( strcmp( cmd[0], "ADD" ) == 0 )
	{
		if( num_parts == 6 && strcmp( cmd[2], "RL" ) == 0 )
		{
			GSList *l;
			
			http_decode( cmd[5] );
			
			if( strchr( cmd[4], '@' ) == NULL )
			{
				imcb_error( ic, "Syntax error" );
				imc_logout( ic, TRUE );
				return 0;
			}
			
			/* We got added by someone. If we don't have this
			   person in permit/deny yet, inform the user. */
			for( l = ic->permit; l; l = l->next )
				if( g_strcasecmp( l->data, cmd[4] ) == 0 )
					return 1;
			
			for( l = ic->deny; l; l = l->next )
				if( g_strcasecmp( l->data, cmd[4] ) == 0 )
					return 1;
			
			msn_buddy_ask( ic, cmd[4], cmd[5] );
		}
		else if( num_parts >= 6 && strcmp( cmd[2], "FL" ) == 0 )
		{
			const char *group = NULL;
			int num;
			
			if( cmd[6] != NULL && sscanf( cmd[6], "%d", &num ) == 1 && num < md->groupcount )
				group = md->grouplist[num];
			
			http_decode( cmd[5] );
			imcb_add_buddy( ic, cmd[4], group );
			imcb_rename_buddy( ic, cmd[4], cmd[5] );
		}
	}
	else if( strcmp( cmd[0], "OUT" ) == 0 )
	{
		int allow_reconnect = TRUE;
		
		if( cmd[1] && strcmp( cmd[1], "OTH" ) == 0 )
		{
			imcb_error( ic, "Someone else logged in with your account" );
			allow_reconnect = FALSE;
		}
		else if( cmd[1] && strcmp( cmd[1], "SSD" ) == 0 )
		{
			imcb_error( ic, "Terminating session because of server shutdown" );
		}
		else
		{
			imcb_error( ic, "Session terminated by remote server (reason unknown)" );
		}
		
		imc_logout( ic, allow_reconnect );
		return( 0 );
	}
#if 0
	/* Discard this one completely for now since I don't care about the ack
	   and since MSN servers can apparently screw up the formatting. */
	else if( strcmp( cmd[0], "REA" ) == 0 )
	{
		if( num_parts != 5 )
		{
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
		
		if( g_strcasecmp( cmd[3], ic->acc->user ) == 0 )
		{
			set_t *s;
			
			http_decode( cmd[4] );
			strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) );
			ic->displayname[sizeof(ic->displayname)-1] = 0;
			
			if( ( s = set_find( &ic->acc->set, "display_name" ) ) )
			{
				g_free( s->value );
				s->value = g_strdup( cmd[4] );
			}
		}
		else
		{
			/* This is not supposed to happen, but let's handle it anyway... */
			http_decode( cmd[4] );
			imcb_rename_buddy( ic, cmd[3], cmd[4] );
		}
	}
#endif
	else if( strcmp( cmd[0], "IPG" ) == 0 )
	{
		imcb_error( ic, "Received IPG command, we don't handle them yet." );
		
		md->handler->msglen = atoi( cmd[1] );
		
		if( md->handler->msglen <= 0 )
		{
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return( 0 );
		}
	}
	else if( strcmp( cmd[0], "ADG" ) == 0 )
	{
		char *group = g_strdup( cmd[3] );
		int groupnum, i;
		GSList *l, *next;
		
		http_decode( group );
		if( sscanf( cmd[4], "%d", &groupnum ) == 1 )
		{
			if( groupnum >= md->groupcount )
			{
				md->grouplist = g_renew( char *, md->grouplist, groupnum + 1 );
				for( i = md->groupcount; i <= groupnum; i ++ )
					md->grouplist[i] = NULL;
				md->groupcount = groupnum + 1;
			}
			g_free( md->grouplist[groupnum] );
			md->grouplist[groupnum] = group;
		}
		else
		{
			/* Shouldn't happen, but if it does, give up on the group. */
			g_free( group );
			imcb_error( ic, "Syntax error" );
			imc_logout( ic, TRUE );
			return 0;
		}
		
		for( l = md->grpq; l; l = next )
		{
			struct msn_groupadd *ga = l->data;
			next = l->next;
			if( g_strcasecmp( ga->group, group ) == 0 )
			{
				if( !msn_buddy_list_add( ic, "FL", ga->who, ga->who, group ) )
					return 0;
				
				g_free( ga->group );
				g_free( ga->who );
				g_free( ga );
				md->grpq = g_slist_remove( md->grpq, ga );
			}
		}
	}
	else if( strcmp( cmd[0], "GCF" ) == 0 )
	{
		/* Coming up is cmd[2] bytes of stuff we're supposed to
		   censore. Meh. */
		md->handler->msglen = atoi( cmd[2] );
	}
	else if( isdigit( cmd[0][0] ) )
	{
		int num = atoi( cmd[0] );
		const struct msn_status_code *err = msn_status_by_number( num );
		
		imcb_error( ic, "Error reported by MSN server: %s", err->text );
		
		if( err->flags & STATUS_FATAL )
		{
			imc_logout( ic, TRUE );
			return( 0 );
		}
	}
	else
	{
		/* debug( "Received unknown command from main server: %s", cmd[0] ); */
	}
	
	return( 1 );
}

static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts )
{
	struct im_connection *ic = data;
	char *body;
	int blen = 0;
	
	if( !num_parts )
		return( 1 );
	
	if( ( body = strstr( msg, "\r\n\r\n" ) ) )
	{
		body += 4;
		blen = msglen - ( body - msg );
	}
	
	if( strcmp( cmd[0], "MSG" ) == 0 )
	{
		if( g_strcasecmp( cmd[1], "Hotmail" ) == 0 )
		{
			char *ct = msn_findheader( msg, "Content-Type:", msglen );
			
			if( !ct )
				return( 1 );
			
			if( g_strncasecmp( ct, "application/x-msmsgssystemmessage", 33 ) == 0 )
			{
				char *mtype;
				char *arg1;
				
				if( !body )
					return( 1 );
				
				mtype = msn_findheader( body, "Type:", blen );
				arg1 = msn_findheader( body, "Arg1:", blen );
				
				if( mtype && strcmp( mtype, "1" ) == 0 )
				{
					if( arg1 )
						imcb_log( ic, "The server is going down for maintenance in %s minutes.", arg1 );
				}
				
				g_free( arg1 );
				g_free( mtype );
			}
			else if( g_strncasecmp( ct, "text/x-msmsgsprofile", 20 ) == 0 )
			{
				/* We don't care about this profile for now... */
			}
			else if( g_strncasecmp( ct, "text/x-msmsgsinitialemailnotification", 37 ) == 0 )
			{
				if( set_getbool( &ic->acc->set, "mail_notifications" ) )
				{
					char *inbox = msn_findheader( body, "Inbox-Unread:", blen );
					char *folders = msn_findheader( body, "Folders-Unread:", blen );

					if( inbox && folders )
						imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
					
					g_free( inbox );
					g_free( folders );
				}
			}
			else if( g_strncasecmp( ct, "text/x-msmsgsemailnotification", 30 ) == 0 )
			{
				if( set_getbool( &ic->acc->set, "mail_notifications" ) )
				{
					char *from = msn_findheader( body, "From-Addr:", blen );
					char *fromname = msn_findheader( body, "From:", blen );
					
					if( from && fromname )
						imcb_log( ic, "Received an e-mail message from %s <%s>.", fromname, from );

					g_free( from );
					g_free( fromname );
				}
			}
			else if( g_strncasecmp( ct, "text/x-msmsgsactivemailnotification", 35 ) == 0 )
			{
				/* Sorry, but this one really is *USELESS* */
			}
			else
			{
				debug( "Can't handle %s packet from notification server", ct );
			}
			
			g_free( ct );
		}
	}
	
	return( 1 );
}

void msn_auth_got_passport_token( struct im_connection *ic, char *token )
{
	struct msn_data *md;
	
	/* Dead connection? */
	if( g_slist_find( msn_connections, ic ) == NULL )
		return;
	
	md = ic->proto_data;
	
	{
		char buf[1024];
		
		g_snprintf( buf, sizeof( buf ), "USR %d SSO S %s %s\r\n", ++md->trId, md->tokens[0], token );
		msn_write( ic, buf, strlen( buf ) );
	}
}

void msn_auth_got_contact_list( struct im_connection *ic )
{
	char buf[64];
	struct msn_data *md;
	
	/* Dead connection? */
	if( g_slist_find( msn_connections, ic ) == NULL )
		return;
	
	md = ic->proto_data;
	
	
	g_snprintf( buf, sizeof( buf ), "BLP %d %s\r\n", ++md->trId, "BL" );
	msn_write( ic, buf, strlen( buf ) );
}

static gboolean msn_ns_send_adl_1( gpointer key, gpointer value, gpointer data )
{
	struct xt_node *adl = data, *d, *c;
	struct bee_user *bu = value;
	struct msn_buddy_data *bd = bu->data;
	char handle[strlen(bu->handle)];
	char *domain;
	char l[4];
	
	strcpy( handle, bu->handle );
	if( ( domain = strchr( handle, '@' ) ) == NULL ) /* WTF */
		return FALSE; 
	*domain = '\0';
	domain ++;
	
	if( ( d = adl->children ) == NULL ||
	    g_strcasecmp( xt_find_attr( d, "n" ), domain ) != 0 )
	{
		d = xt_new_node( "d", NULL, NULL );
		xt_add_attr( d, "n", domain );
		xt_insert_child( adl, d );
	}
	
	g_snprintf( l, sizeof( l ), "%d", bd->flags & 7 );
	c = xt_new_node( "c", NULL, NULL );
	xt_add_attr( c, "n", handle );
	xt_add_attr( c, "l", l );
	xt_add_attr( c, "t", "1" ); /* 1 means normal, 4 means mobile? */
	xt_insert_child( d, c );
	
	return FALSE;
}

static void msn_ns_send_adl( struct im_connection *ic )
{
	struct xt_node *adl;
	struct msn_data *md;
	char *adls, buf[64];
	
	/* Dead connection? */
	if( g_slist_find( msn_connections, ic ) == NULL )
		return;
	
	md = ic->proto_data;
	
	adl = xt_new_node( "ml", NULL, NULL );
	xt_add_attr( adl, "l", "1" );
	g_tree_foreach( md->domaintree, msn_ns_send_adl_1, adl );
	adls = xt_to_string( adl );
	
	g_snprintf( buf, sizeof( buf ), "ADL %d %zd\r\n", ++md->trId, strlen( adls ) );
	if( msn_write( ic, buf, strlen( buf ) ) )
		msn_write( ic, adls, strlen( adls ) );
	
	g_free( adls );
	xt_free_node( adl );
}

static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name )
{
	set_t *s;
	
	if( ( s = set_find( &ic->acc->set, "display_name" ) ) == NULL )
		return FALSE; /* Shouldn't happen.. */
	
	http_decode( name );
	
	if( s->value && strcmp( s->value, name ) == 0 )
	{
		return TRUE;
		/* The names match, nothing to worry about. */
	}
	else if( s->value != NULL &&
	         ( strcmp( name, ic->acc->user ) == 0 ||
	           set_getbool( &ic->acc->set, "local_display_name" ) ) )
	{
		/* The server thinks our display name is our e-mail address
		   which is probably wrong, or the user *wants* us to do this:
		   Always use the locally set display_name. */
		return msn_set_display_name( ic, s->value );
	}
	else
	{
		if( s->value && *s->value )
			imcb_log( ic, "BitlBee thinks your display name is `%s' but "
			              "the MSN server says it's `%s'. Using the MSN "
			              "server's name. Set local_display_name to true "
			              "to use the local name.", s->value, name );
		
		if( g_utf8_validate( name, -1, NULL ) )
		{
			g_free( s->value );
			s->value = g_strdup( name );
		}
		else
		{
			imcb_log( ic, "Warning: Friendly name in server response was corrupted" );
		}
		
		return TRUE;
	}
}