package Net::Msmgr;

use 5.006;
use strict;
use warnings;

require Exporter;

use LWP::UserAgent;
use HTTP::Request;
use HTTP::Response;
use URI::Escape;

use vars qw / $TRID $dalogin /;

our @ISA = qw(Exporter);

our %EXPORT_TAGS = ( 'debug' => [ qw ( DEBUG_PACKET_SEND
				       DEBUG_PACKET_RECV
				       DEBUG_COMMAND_SEND
				       DEBUG_COMMAND_RECV
				       DEBUG_OPEN
				       DEBUG_CLOSE
				       DEBUG_CONFUSED
				       DEBUG_HANDLER
				       DEBUG_NOTIFICATION)  ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'debug'} }, qw { GetVersion8Response } );

our @EXPORT = qw();
our $VERSION = substr(q$Revision: 0.16 $,10);


use constant DEBUG_PACKET_SEND => 	    1;
use constant DEBUG_PACKET_RECV =>	    2;
use constant DEBUG_COMMAND_SEND =>	    4;
use constant DEBUG_COMMAND_RECV =>	    8;
use constant DEBUG_OPEN =>		   16;
use constant DEBUG_CLOSE =>		   32;
use constant DEBUG_CONFUSED =>		   64;
use constant DEBUG_HANDLER =>		  128;
use constant DEBUG_NOTIFICATION =>	  256;



$TRID = 0 ; 
$dalogin = undef;		# cache this for speedier connections

sub TRID
{
    $TRID++;
    return $TRID;
}

sub GetVersion8Response
{
    my $user = shift;
    my ($trid, $scheme, $state, $string )  = @_;
    my %challenge_part = map { split '=' } split(',', $string) ;


    unless ($dalogin)
    {
	my $ua = new LWP::UserAgent;
	my $response = $ua->get('https://nexus.passport.com/rdr/pprdr.asp');
	my %passport_urls =
	    map { split '=' } split(',',($response->headers->header('PassportURLs')));
	$dalogin = $passport_urls{'DALogin'};
    }

    warn "No dalogin" unless $dalogin;
    return  unless $dalogin;

    my $username = uri_escape($user->user);
    my $password = uri_escape($user->password);
    my $auth_string = 'Passport1.4 ' . join(',',
			   qq {OrgVerb=GET},
			   qq {OrgURL=$challenge_part{ru}} ,
			   qq {sign-in=$username},
			   qq {pwd=$password},
			   qq {lc=$challenge_part{lc}},
			   qq {id=$challenge_part{id}},
			   qq {tw=$challenge_part{tw}},
			   qq {fs=$challenge_part{fs}},
##			   qq {ru=$challenge_part{ru}},
			   qq {ct=$challenge_part{ct}},
			   qq {kpp=$challenge_part{kpp}},
			   qq {kv=$challenge_part{kv}},
			   qq {ver=$challenge_part{ver}},
			   qq {tpf=$challenge_part{tpf}} );

    my $ua = new LWP::UserAgent;
    my $request = new HTTP::Request ( GET => 'https://' . $dalogin );
    $request->headers->header('Authorization' => $auth_string);

    my $response = $ua->request($request);
    if ($response->is_success)
    {
	if (my $auth_info = $response->header('authentication-info'))
	{
	    $auth_info =~ m/(t=.*\$\$\&p=.*\$\$)/;
	    if (my $magic_string = $1)
	    {
		return $magic_string;
	    }
	}
    }
    return undef;
} 

1;

=pod

=head1 NAME

Net::Msmgr - Microsoft Network Chat Client Toolkit

This is the documentation for $Revision: 0.16 $

=head1 SYNOPSIS

 use Net::Msmgr;
 use Net::Msmgr::Sesssion;
 use Net::Msmgr::User;

 our $session = new Net::Msmgr::Session;
 our $user = new Net::Msmgr::User ( user => 'username@msn.com',
                             password => 'my_password' ) ;

 $session->user($user);
 $session->login_handler( sub { shift->Logout } ) ;
 $session->connect_handler ( ... ) ; 
 $session->Login;


=head1 OVERVIEW

This is a set of perl modules for encapsulating interactions with the
Microsoft Network "Messenger" chat system.  You might use it to
develop clients, or robots.  The components are, this module, Net::Msmgr, 
which contains some non-object helper routines (mostly the
authentication chain for using MSNP8 (Protocol version 8) and a
handful of manifest constants for debugging.

Other modules include:

=over

=item	Net::Msmgr::Session

Encapsulates the entirety of a session.

=item	Net::Msmgr::User

Holds user authentication credentials.

=item	Net::Msmgr::Command

Used to hold command objects sent to or received from the servers.

=item	Net::Msmgr::Connection

Used to encapsulate server connections.

=item	Net::Msmgr::Switchboard

Derived from Net::Msmgr::Connection

=item   Net::Msmgr::Conversation

A higher level view of conversation - contains (but is not) a Switchboard

=item	Net::Msmgr::Object

Pure base class from which all of the others are derived.  Direct from
the perltoot manpage.


=head1	SERVER PROTOCOL

The entire protocol consists of a series of discrete messages that are
passed between the client and the various servers that make up the
system.  Messages come in a variety of broad classes (Normal,
Asyncronous, and Payload), and those are subdivided into more specific
types (Transfer Requests, Chat Messages, State Change Notifications.)

There are three servers you will deal with during a basic session, the
Dispatch Server, which is a meta server to distribute inbound
sessions, the Notification Server, which will hold a single connection
for the life of the session, and Switchboard Servers, which you will hold
as many connections as you have chat groups active.  

Technically, there is no difference between the Dispatch Server and
the Notification Server, except that the Dispatch Server will
(historically) always refer you to a Notification Server.  There is
nothing in the protocol to prohibit a Notification Server from ALSO
refering you to a third Notification Server, although this author has
never seen that happen.

Because of this, we tend to think of the DS and the NS as dissimilar
entities, but there is no need for them to be so, and in the interest
of flexibility they are treated the same.  There is no limit, besides
end-user patience to how many XFR messages you can receive.

=head2 DISPATCH SERVER

This is the first-base server.  Your minimum action here is to request
a session, and act on the instructions from the server.

=head2 NOTIFICATION SERVER

This is the center of your session, and when you have connected here,
most (other) clients, and this library will consider you "connected"
to MSN Chat.  

=head2 SWITCHBOARD SERVER

To send or receive messages from other clients, there must be a
connection to one or more Switchboard Servers.  Each one of these
connections is a 'party line', and all users currently connected to
the same session (referenced by what the library calls a $ssid
Switchboard Session ID) will see all messages sent by any user.  The
number of users that can be attached to a SSID appears to be
reaosonably unlimited (on the order of dozens).

=head1 COMMAND SUMMARY

Here is a quick summary of all of the messages used in this library 
between the client and the servers.

=over

=item VER -- Version

Optionally sent from client to DS / NS for protocol version negotiation.

=item INF -- Information

Optionally sent from client to DS / NS, asking what Encryption
Technique to use.  In MSNP7 and lower, it is always MD5.  MSNP8 uses a
different technique, but does not use this command to negotiate it.
Go figure.

=item USR -- User Information

Used in two variants from client to server as part of
the login procedure, in a slightly different variant from server to client as
part of that same procedure, and again later during the authentication
with Switchboard Servers.

=item XFR -- Transfer

Used in one variant from server to client as part of
the login procedure, referring you from DS to NS.  Used again later
from client to server to request a connection to a switchboard server.

=item CHG -- Change

Sent from client to server to alter your 'presence' (online, out to lunch, etc.)

=item ILN -- Inital online

Sent from server to client in response to your first change to online
status, with a list of visible users already on the system.

=item SYN -- Synchonize

Optionally sent from client to server to request a download of all of your user lists.

=item GTC -- no known mnemonic

Part of the bundle of information sent from server to client, it
advises the client of a user-set preference for dealing with new
users.  It is stored on the server, but not acted on in any way.  Can
be sent as a command to the server to alter this setting.

=item BLP -- Blocking Preference

Part of the bundle of information sent from server to client as part
of a SYN.  Used by the server to determine behavior if an unkonwn user
attempts to invite you to a switchboard session.

=item PRP -- Personal Phone Number

Sent from server to client during SYN, and sent from client to server
to change the settings.  Designed to hold telephone numbers on the
server in URI-encoded strings, and a few variants to hold some mobile
device preferences.

=item LST -- List

Sent from server to client during SYN, and in resposne to a LST
command.  One variant for each of the four lists (Allow, Block, Forward
and Reverse) the server maintains for each client.

=item ADD -- Add

Sent from client to server to add a user to a list.  Echoed from
server to client with new list serial-number.  The server maintains
this serial-number, such that the client may cache the list locally.

=item REM -- Remove

Sent from client to server to remove a user from a list.

=item REA -- Rename

Sent from client to server to change the Friendly Name associated with
a user in your lists.  Also used to change your own friendly name.

=item MSG -- Message

Sent from DS/NS server to client at login, and sometimes for
administrative (shutdown) messages.  Also, the core of what this
protocol is about - sending messages to other users and receiving
messages from other users via Switchboard Servers.

=item ANS -- Answer

Sent from client to switchboard server in resposne to a switchboard
invitation.

=item IRO -- Initial Roster

Sent from switchboard to client upon connection to a switchboard
server informing client of other users attached to that switchboard
session.

=item CAL -- Call

Sent from client to switchboard to invite another user to join the
switchboard session.

=item OUT -- Out

Async command sent from client to NS/DS/SB server to terminate their session.  

=item NLN -- Online

Async command sent from NS to client to advise of another user coming online.

=item FLN -- Offline

Async command sent from NS to client to advise of another user going offline.

=item PNG -- Ping

Async command sent from client to NS to make sure it is still there.

=item QNG -- Pong

Async command sent from NS to client to acknowledge its presence.

=item RNG -- Ring

Async command sent from NS to client to advise of another user inviting you to a Switchboard Session.

=item JOI -- Join

Async command sent from SB to client to advise of another user joining a Switchboard Session.

=item BYE -- Bye

Async command sent from SB to client to advise of another user leaving a Switchboard Session.

=back

=head1 COMMAND FORMATS

Commands sent in the protocols come in three (and a theoretically
possible fourth) variants.  This library refers to them as Normal,
Async, and Payload.  

=head2 NORMAL COMMANDS

The vast bulk of commands are Normal, and each
one is tagged with a numeric identifier by the client.  This
identifier will be used by the server to correlate its responses to
your requests.  This library does not currently verify any of these
transaction identifiers (TRIDs), but does send each command with a
unique monotonically-increasing number.  Library users can feel free
to use the TRID in Normal messages as a unique identifier, within the
rules of the protocol.  (That is: Sometimes a single Normal command
from client to server will result in many related responses, all of
which will contain the TRID of that single request).

=head2 ASYNC COMMANDS

Another block of commands are those sent from server to client in
resposne to asyncronous events, such as users in your Forward List
changing their status, invitations by other users to Switchboard
Sessions, and users joining and leaving Switchboard Sessions.

=head2 PAYLOAD COMMANDS

The final type of command is that which contains message data.  This
library only (currently) supports one, the MSG command, which is used
to encapsulate messages from server to client, and peer to peer.

=head1 ASYNCRONOUS INPUT

The library user is responsible for dealing with non-blocking IO, and
there are several ways you might do this.  If you are writing a
Perl/Tk you would probably use fileevent, or you might want to use
Joshua Pritikin's Event package (which I use), or you can roll your
own with select and poll.  You could even use alarm and signals to
periodically sweep all of the inbound sessions.

To help you with this, there are a pair of handlers in the
Net::Msmgr::Session object, $session->connect_handler, and
$session->disconnect_handler - which are called just after the TCP
connect() call and just before the TCP close() call respectively.

Each of these will be called with a single pointer to the Net::Msmgr::Connection object.  

It is the users' responsibility to call $connection->_recv_message
whenever input is available on $connection->socket.  

With Tk this would be something like

 sub Connect_handler
 {
     my $connection = shift;
     $mainwindow->fileevent($connection->socket,
			    'readable',
			    sub { $connection->_recv_message });
 }
 $session->connect_hanlder(\&Connect_handler);
 $session->Login;
 MainLoop;

Under Joshua Pritikin's Event package, you might use 

 our %watcher;

 sub ConnectHandler
 {
    my ($connection) = @_;
    my $socket = $connection->socket;
    $watcher{$connection} = Event->io(fd => $socket,
				      cb => [ $connection , '_recv_message' ],
				      poll => 're',
				      desc => 'recv_watcher',
				      repeat => 1);
 }

 sub DisconnectHandler
 {
    my $connection = shift;
    $watcher{$connection}->cancel;
 }

 $session->connect_handler(\&ConnectHandler);
 $session->disconnect_handler(\&DisconnectHandler);


A third handler Net::Msmgr::Session::switchboard_handler() will be called
with a Net::Msmgr::Connection object and an ssid for each switchboard session
you are invited to, or instantiate through
Net::Msmgr::Session::ui_new_switchboard().


=cut


#
# $Log: Msmgr.pm,v $
# Revision 0.16  2003/08/07 00:01:59  lawrence
# Initial Release
#
#