AnyEvent-MSN

 view release on metacpan or  search on metacpan

lib/AnyEvent/MSN.pm  view on Meta::CPAN

#use Data::Printer;
sub DEMOLISH {
    my $s = shift;
    $s->handle->destroy if $s->_has_handle && $s->handle;
    $s->_clear_soap_requests;
}

# Basic connection info
has host => (is      => 'ro',
             writer  => '_set_host',
             isa     => 'Str',
             default => 'messenger.hotmail.com'
);
has port => (is      => 'ro',
             writer  => '_set_port',
             isa     => 'Int',
             default => 1863
);

# Authentication info from user
has passport => (
    is       => 'ro',
    isa      => 'AnyEvent::MSN::Types::Passport',
    required => 1,
    handles  => {
        username => sub {
            shift->passport =~ m[^(.+)\@.+$];
            $1;
        },
        userhost => sub { shift->passport =~ m[^.+\@(.+)$]; $1 }
    }
);
has password => (is => 'ro', isa => 'Str', required => 1);

# Extra stuff from user
has [qw[friendly_name personal_message]] =>
    (is => 'ro', isa => 'Str', default => '');
has status => (
         is      => 'ro',
         isa     => 'AnyEvent::MSN::Types::OnlineStatus',
         default => 'NLN',
         writer  => 'set_status'                            # exposed publicly
);

# Client info for MSNP21
has protocol_version => (
    is  => 'ro',
    isa => subtype(
        as 'Str' => where {m[^(?:MSNP\d+\s*)+$]} => message {
            'Protocol versions look like: MSNP18 MSNP21';
        }
    ),
    writer  => '_set_protocol_version',
    clearer => '_reset_protocol_version',
    default => 'MSNP21',
    lazy    => 1
);
map { has $_->[0] => (is => 'ro', isa => 'Str', default => $_->[1]) }
    [qw[product_id PROD0120PW!CCV9@]],
    [qw[product_key C1BX{V4W}Q3*10SM]],
    [qw[locale_id 0x0409]],
    [qw[os_type winnt]],
    [qw[os_ver 6.1.1]],
    [qw[arch i386]],
    [qw[client_name MSNMSGR]],
    [qw[client_version 15.4.3508.1109]],
    [qw[client_string MSNMSGR]];
has guid => (
    is     => 'ro',
    => isa => subtype(
        as 'Str' => where {
            my $hex = qr[[\da-f]];
            m[{$hex{8}(?:-$hex{4}){3}-$hex{12}}$];
        } => message {
            'Malformed GUID. Should look like: {12345678-abcd-1234-abcd-123456789abc}';
        }
    ),
    builder => '_build_guid'
);

sub _build_guid {
    state $r //= sub {
        join '', map { ('a' .. 'f', 0 .. 9)[rand 15] } 1 .. shift;
    };
    sprintf '{%8s-%4s-%4s-%4s-%12s}', $r->(8), $r->(4), $r->(4), $r->(4),
        $r->(12);
}
has location => (is => 'ro', isa => 'Str', default => 'Perl/AnyEvent::MSN');

# Internals
has handle => (
    is  => 'ro',
    isa => 'Object',

    # weak_ref  => 1,
    predicate => '_has_handle',
    writer    => '_set_handle',
    clearer   => '_reset_handle',
    handles   => {
        send => sub {
            my $s = shift;
            $s->handle->push_write('AnyEvent::MSN::Protocol' => @_)
                if $s->_has_handle;    # XXX - Else mention it...
            }
    }
);
has tid => (is      => 'ro',
            isa     => 'Int',
            lazy    => 1,
            clearer => '_reset_tid',
            builder => '_build_tid',
            traits  => ['Counter'],
            handles => {'_inc_tid' => 'inc'}
);
sub _build_tid {0}
after tid => sub { shift->_inc_tid };    # Auto inc
has ping_timer => (is     => 'ro',
                   isa    => 'Ref',                     # AE::timer
                   writer => '_set_ping_timer'
);

lib/AnyEvent/MSN.pm  view on Meta::CPAN

                    );
                }

                #
                if ($policy =~ m[MBI]) {
                    my $token = $s->auth_token('messengerclear.live.com')
                        ;    # or http://Passport.NET/tb
                    my $token_
                        = __html_escape($token->{'wst:RequestedSecurityToken'}
                                     {'wsse:BinarySecurityToken'}{'content'});
                    $s->send('USR %d SSO S %s %s %s',
                             $s->tid,
                             $token->{'wst:RequestedSecurityToken'}
                                 {'wsse:BinarySecurityToken'}{'content'},
                             AnyEvent::MSN::Protocol::SSO(
                                           $nonce,
                                           $token->{'wst:RequestedProofToken'}
                                               {'wst:BinarySecret'}
                             ),
                             $s->guid
                    );
                }
                elsif ($policy =~ m[^\?]) {
                    ...;
                }
            }
        );
    }
    elsif ($subtype eq 'OK') {

        # XXX - logged in okay. What now?
    }
    else {
        ...;
    }
}

sub _handle_packet_ubx {    # Buddy has changed something
    my ($s, $passport, $len, $payload) = @_;
    my $xml = $s->_parse_xml($payload);
    if ($len == 0 && $passport eq '1:' . $s->passport) {
    }
    else {

        #dd $xml;
        my ($user) = ($passport =~ m[:(.+)$]);
        $s->_add_temporary_contact($user, $xml);
    }
}

sub _handle_packet_uux {    # ACK for UUX
}

sub _handle_packet_ver {    # Negotiated protocol version
    my ($s, $tid, $r) = @_;
    $s->_set_protocol_version($r);

    # Send basic client info
    $s->send('CVR %d %s %s %s %s %s %s %s %s%s',
             $s->tid,
             $s->locale_id,
             $s->os_type,
             $s->os_ver,
             $s->arch,
             $s->client_name,
             $s->client_version,
             $s->client_string,
             $s->passport,
             (' ' . ($s->_has_redirect ? $s->redirect : ' 0'))
    );
}

sub _handle_packet_xfr {    # Transver to another switchboard
    my $s = shift;
    my ($tid, $type, $addr, $u, $d, $redirect) = @_;
    $s->send('OUT');
    $s->handle->destroy;
    my ($host, $port) = ($addr =~ m[^(.+):(\d+)$]);
    $s->_set_host($host);
    $s->_set_port($port);
    $s->_set_redirect($redirect);
    $s->connect;
}

# SOAP client
has soap_requests => (isa     => 'HashRef[AnyEvent::Util::guard]',
                      traits  => ['Hash'],
                      handles => {_add_soap_request    => 'set',
                                  _del_soap_request    => 'delete',
                                  _clear_soap_requests => 'clear'
                      }
);

sub _soap_request {
    my ($s, $uri, $headers, $content, $cb) = @_;
    my %headers = (
           'user-agent'   => 'MSNPM 1.0',
           'content-type' => 'application/soap+xml; charset=utf-8; action=""',
           'Expect'       => '100-continue',
           'connection'   => 'Keep-Alive'
    );

    #warn $content;
    @headers{keys %$headers} = values %$headers;
    $s->_add_soap_request(
        $uri,
        AnyEvent::HTTP::http_request(
            POST       => $uri,
            headers    => \%headers,
            timeout    => 15,
            persistent => 1,
            body       => $content,
            sub {
                my ($body, $hdr) = @_;
                my $xml = $s->_parse_xml($body);
                $s->_del_soap_request($uri);
                return $cb->($xml)
                    if $hdr->{Status} =~ /^2/
                        && !defined $xml->{'S:Fault'};

                #dd $hdr;



( run in 1.817 second using v1.01-cache-2.11-cpan-ceb78f64989 )