aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/msn/msn.c
blob: aa05dbdd6cd57f824a2fc37a46ae1a9d064cc54f (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
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                *
  \********************************************************************/

/* SSL module                                                           */

/*
  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 <glib.h>
#include "proxy.h"

#define SSL_OK            0
#define SSL_NOHANDSHAKE   1
#define SSL_AGAIN         2

extern int ssl_errno;

typedef void (*ssl_input_function)(gpointer, void*, GaimInputCondition);

G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data );
G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len );
G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len );
G_MODULE_EXPORT void ssl_disconnect( void *conn_ );
G_MODULE_EXPORT int ssl_getfd( void *conn );
G_MODULE_EXPORT GaimInputCondition ssl_getdirection( void *conn );
id='n383' href='#n383'>383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
  /********************************************************************\
  * BitlBee -- An IRC to other IM-networks gateway                     *
  *                                                                    *
  * Copyright 2002-2004 Wilmer van der Gaast and others                *
  \********************************************************************/

/* MSN module - Main file; functions to be called from BitlBee          */

/*
  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 "nogaim.h"
#include "msn.h"

static char *msn_set_display_name( set_t *set, char *value );

static void msn_init( account_t *acc )
{
	set_t *s;
	
	s = set_add( &acc->set, "display_name", NULL, msn_set_display_name, acc );
	s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
}

static void msn_login( account_t *acc )
{
	struct im_connection *ic = imcb_new( acc );
	struct msn_data *md = g_new0( struct msn_data, 1 );
	
	ic->proto_data = md;
	md->fd = -1;
	
	if( strchr( acc->user, '@' ) == NULL )
	{
		imcb_error( ic, "Invalid account name" );
		imc_logout( ic, FALSE );
		return;
	}
	
	imcb_log( ic, "Connecting" );
	
	md->fd = proxy_connect( "messenger.hotmail.com", 1863, msn_ns_connected, ic );
	if( md->fd < 0 )
	{
		imcb_error( ic, "Could not connect to server" );
		imc_logout( ic, TRUE );
		return;
	}
	
	md->ic = ic;
	md->away_state = msn_away_state_list;
	
	msn_connections = g_slist_append( msn_connections, ic );
}

static void msn_logout( struct im_connection *ic )
{
	struct msn_data *md = ic->proto_data;
	GSList *l;
	
	if( md )
	{
		if( md->fd >= 0 )
			closesocket( md->fd );
		
		if( md->handler )
		{
			if( md->handler->rxq ) g_free( md->handler->rxq );
			if( md->handler->cmd_text ) g_free( md->handler->cmd_text );
			g_free( md->handler );
		}
		
		while( md->switchboards )
			msn_sb_destroy( md->switchboards->data );
		
		if( md->msgq )
		{
			struct msn_message *m;
			
			for( l = md->msgq; l; l = l->next )
			{
				m = l->data;
			
				imcb_log( ic, "Warning: Closing down MSN connection with unsent message to %s, you'll have to resend it.", m->who );
				g_free( m->who );
				g_free( m->text );
				g_free( m );
			}
			g_slist_free( md->msgq );
		}
		
		while( md->groupcount > 0 )
			g_free( md->grouplist[--md->groupcount] );
		g_free( md->grouplist );
		
		g_free( md );
	}
	
	for( l = ic->permit; l; l = l->next )
		g_free( l->data );
	g_slist_free( ic->permit );
	
	for( l = ic->deny; l; l = l->next )
		g_free( l->data );
	g_slist_free( ic->deny );
	
	msn_connections = g_slist_remove( msn_connections, ic );
}

static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, int away )
{
	struct msn_switchboard *sb;
	struct msn_data *md = ic->proto_data;
	
	if( ( sb = msn_sb_by_handle( ic, who ) ) )
	{
		return( msn_sb_sendmessage( sb, message ) );
	}
	else
	{
		struct msn_message *m;
		char buf[1024];
		
		/* Create a message. We have to arrange a usable switchboard, and send the message later. */
		m = g_new0( struct msn_message, 1 );
		m->who = g_strdup( who );
		m->text = g_strdup( message );
		
		/* FIXME: *CHECK* the reliability of using spare sb's! */
		if( ( sb = msn_sb_spare( ic ) ) )
		{
			debug( "Trying to use a spare switchboard to message %s", who );
			
			sb->who = g_strdup( who );
			g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
			if( msn_sb_write( sb, buf, strlen( buf ) ) )
			{
				/* He/She should join the switchboard soon, let's queue the message. */
				sb->msgq = g_slist_append( sb->msgq, m );
				return( 1 );
			}
		}
		
		debug( "Creating a new switchboard to message %s", who );
		
		/* If we reach this line, there was no spare switchboard, so let's make one. */
		g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
		if( !msn_write( ic, buf, strlen( buf ) ) )
		{
			g_free( m->who );
			g_free( m->text );
			g_free( m );
			
			return( 0 );
		}
		
		/* And queue the message to md. We'll pick it up when the switchboard comes up. */
		md->msgq = g_slist_append( md->msgq, m );
		
		/* FIXME: If the switchboard creation fails, the message will not be sent. */
		
		return( 1 );
	}
	
	return( 0 );
}

static GList *msn_away_states( struct im_connection *ic )
{
	static GList *l = NULL;
	int i;
	
	if( l == NULL )
		for( i = 0; msn_away_state_list[i].number > -1; i ++ )
			l = g_list_append( l, (void*) msn_away_state_list[i].name );
	
	return l;
}

static void msn_set_away( struct im_connection *ic, char *state, char *message )
{
	char buf[1024];
	struct msn_data *md = ic->proto_data;
	const struct msn_away_state *st;
	
	if( strcmp( state, GAIM_AWAY_CUSTOM ) == 0 )
		st = msn_away_state_by_name( "Away" );
	else
		st = msn_away_state_by_name( state );
	
	if( !st ) st = msn_away_state_list;
	md->away_state = st;
	
	g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code );
	msn_write( ic, buf, strlen( buf ) );
}

static void msn_set_my_name( struct im_connection *ic, char *info )
{
	msn_set_display_name( set_find( &ic->acc->set, "display_name" ), info );
}

static void msn_get_info(struct im_connection *ic, char *who) 
{
	/* Just make an URL and let the user fetch the info */
	imcb_log( ic, "%s\n%s: %s%s", _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who );
}

static void msn_add_buddy( struct im_connection *ic, char *who, char *group )
{
	msn_buddy_list_add( ic, "FL", who, who );
}

static void msn_remove_buddy( struct im_connection *ic, char *who, char *group )
{
	msn_buddy_list_remove( ic, "FL", who );
}

static void msn_chat_msg( struct groupchat *c, char *message, int flags )
{
	struct msn_switchboard *sb = msn_sb_by_chat( c );
	
	if( sb )
		msn_sb_sendmessage( sb, message );
	/* FIXME: Error handling (although this can't happen unless something's
	   already severely broken) disappeared here! */
}

static void msn_chat_invite( struct groupchat *c, char *who, char *message )
{
	struct msn_switchboard *sb = msn_sb_by_chat( c );
	char buf[1024];
	
	if( sb )
	{
		g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
		msn_sb_write( sb, buf, strlen( buf ) );
	}
}

static void msn_chat_leave( struct groupchat *c )
{
	struct msn_switchboard *sb = msn_sb_by_chat( c );
	
	if( sb )
		msn_sb_write( sb, "OUT\r\n", 5 );
}

static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )
{
	struct msn_switchboard *sb;
	struct msn_data *md = ic->proto_data;
	char buf[1024];
	
	if( ( sb = msn_sb_by_handle( ic, who ) ) )
	{
		debug( "Converting existing switchboard to %s to a groupchat", who );
		return msn_sb_to_chat( sb );
	}
	else
	{
		struct msn_message *m;
		
		if( ( sb = msn_sb_spare( ic ) ) )
		{
			debug( "Trying to reuse an existing switchboard as a groupchat with %s", who );
			g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
			if( msn_sb_write( sb, buf, strlen( buf ) ) )
				return msn_sb_to_chat( sb );
		}
		
		/* If the stuff above failed for some reason: */
		debug( "Creating a new switchboard to groupchat with %s", who );
		
		/* Request a new switchboard. */
		g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
		if( !msn_write( ic, buf, strlen( buf ) ) )
			return( 0 );
		
		/* Create a magic message. This is quite hackish, but who cares? :-P */
		m = g_new0( struct msn_message, 1 );
		m->who = g_strdup( who );
		m->text = g_strdup( GROUPCHAT_SWITCHBOARD_MESSAGE );
		
		/* Queue the magic message and cross your fingers. */
		md->msgq = g_slist_append( md->msgq, m );
		
		/* FIXME: Can I try to return something here already? */
		return NULL;
	}
	
	return NULL;
}

static void msn_keepalive( struct im_connection *ic )
{
	msn_write( ic, "PNG\r\n", strlen( "PNG\r\n" ) );
}

static void msn_add_permit( struct im_connection *ic, char *who )
{
	msn_buddy_list_add( ic, "AL", who, who );
}

static void msn_rem_permit( struct im_connection *ic, char *who )
{
	msn_buddy_list_remove( ic, "AL", who );
}

static void msn_add_deny( struct im_connection *ic, char *who )
{
	struct msn_switchboard *sb;
	
	msn_buddy_list_add( ic, "BL", who, who );
	
	/* If there's still a conversation with this person, close it. */
	if( ( sb = msn_sb_by_handle( ic, who ) ) )
	{
		msn_sb_destroy( sb );
	}
}

static void msn_rem_deny( struct im_connection *ic, char *who )
{
	msn_buddy_list_remove( ic, "BL", who );
}

static int msn_send_typing( struct im_connection *ic, char *who, int typing )
{
	if( typing & OPT_TYPING )
		return( msn_buddy_msg( ic, who, TYPING_NOTIFICATION_MESSAGE, 0 ) );
	else
		return( 1 );
}

static char *msn_set_display_name( set_t *set, char *value )
{
	account_t *acc = set->data;
	struct im_connection *ic = acc->ic;
	struct msn_data *md;
	char buf[1024], *fn;
	
	/* Double-check. */
	if( ic == NULL )
		return NULL;
	
	md = ic->proto_data;
	
	if( strlen( value ) > 129 )
	{
		imcb_log( ic, "Maximum name length exceeded" );
		return NULL;
	}
	
	fn = msn_http_encode( value );
	
	g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn );
	msn_write( ic, buf, strlen( buf ) );
	g_free( fn );
	
	/* Returning NULL would be better, because the server still has to
	   confirm the name change. However, it looks a bit confusing to the
	   user. */
	return value;
}

void msn_initmodule()
{
	struct prpl *ret = g_new0(struct prpl, 1);
	
	ret->name = "msn";
	ret->login = msn_login;
	ret->init = msn_init;
	ret->logout = msn_logout;
	ret->buddy_msg = msn_buddy_msg;
	ret->away_states = msn_away_states;
	ret->set_away = msn_set_away;
	ret->get_info = msn_get_info;
	ret->set_my_name = msn_set_my_name;
	ret->add_buddy = msn_add_buddy;
	ret->remove_buddy = msn_remove_buddy;
	ret->chat_msg = msn_chat_msg;
	ret->chat_invite = msn_chat_invite;
	ret->chat_leave = msn_chat_leave;
	ret->chat_with = msn_chat_with;
	ret->keepalive = msn_keepalive;
	ret->add_permit = msn_add_permit;
	ret->rem_permit = msn_rem_permit;
	ret->add_deny = msn_add_deny;
	ret->rem_deny = msn_rem_deny;
	ret->send_typing = msn_send_typing;
	ret->handle_cmp = g_strcasecmp;

	register_protocol(ret);
}