You are viewing lindner

Tue, Sep. 18th, 2007, 05:31 pm
On Vox: Windows Live API, better than page scraping

So we use this toolkit from Octazen to scrape contact lists off of various sites.  Our ever eager users (ab)used this feature so much that hotmail blocked us.

So I waded through reams of API docs over at http://dev.live.com and finally came up with this prototype perl script to talk to their API servers.  Gets the job done for now.  Will want to rewrite it in native Java and add decent error handling soon.  Hopefully this post here will help other folks needing to talk to Live API:

#!/usr/bin/perl
#
# Output tab separated values for a given hotmail username/password
#
# Implementation of Windows Live Contacts API
#    http://msdn2.microsoft.com/en-us/library/bb463974.aspx
#
# Uses RPS authentication described here:
#    http://msdn2.microsoft.com/en-us/library/bb447721.aspx
#

use HTTP::Request;
use LWP::UserAgent;

my $username = shift || die "Need a username\n";
my $password = shift || die "Need a password\n";
my $apikey = 'YOURAPIKEY';

my $ua = LWP::UserAgent->new;

my $uri = 'https://dev.login.live.com/wstlogin.srf';
my $req = HTTP::Request->new(POST => $uri);
   $req->content_type('application/soap+xml');
my $xml = <<EOF
<s:Envelope
    xmlns:s = "http://www.w3.org/2003/05/soap-envelope"
    xmlns:wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    xmlns:saml = "urn:oasis:names:tc:SAML:1.0:assertion"
    xmlns:wsp = "http://schemas.xmlsoap.org/ws/2004/09/policy"
    xmlns:wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:wsa = "http://www.w3.org/2005/08/addressing"
    xmlns:wssc = "http://schemas.xmlsoap.org/ws/2005/02/sc"
    xmlns:wst = "http://schemas.xmlsoap.org/ws/2005/02/trust">
    <s:Header>
        <wlid:ClientInfo xmlns:wlid = "http://schemas.microsoft.com/wlid">
            <wlid:ApplicationID>$apikey</wlid:ApplicationID>
        </wlid:ClientInfo>
        <wsa:Action s:mustUnderstand = "1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</wsa:Action>
        <wsa:To s:mustUnderstand = "1">https://dev.login.live.com/wstlogin.srf</wsa:To>
        <wsse:Security>
            <wsse:UsernameToken wsu:Id = "user">
                <wsse:Username>$username</wsse:Username>
                <wsse:Password>$password</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </s:Header>
    <s:Body>
        <wst:RequestSecurityToken Id = "RST0">
            <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>
            <wsp:AppliesTo>
                <wsa:EndpointReference>
                    <wsa:Address>http://live.com</wsa:Address>
                </wsa:EndpointReference>
            </wsp:AppliesTo>
            <wsp:PolicyReference URI = "MBI"></wsp:PolicyReference>
        </wst:RequestSecurityToken>
    </s:Body>
</s:Envelope>
EOF
;


$req->content_length(length $xml);
$req->content($xml);

my $res = $ua->request($req);

# Ugly way of hacking out the binarysecuritytoken
my $resultxml = $res->content();
$resultxml =~ m,<wsse:BinarySecurityToken[^>]*>(.*)</wsse:BinarySecurityToken>,si;
my $binarytoken = $1;

# Request contacts
my $contactsurl = "https://cumulus.services.live.com/$username/LiveContacts/Contacts";
my $authheader = 'WLID1.0 t="' . $binarytoken . '"';
my $contactsreq = HTTP::Request->new(GET => $contactsurl, ['Authorization' => $authheader]);

my $contactres = $ua->request($contactsreq);
my $contactxml = $contactres->content();

use XML::Simple;
my $result = XMLin($contactxml, 'ForceArray' => ['Email', 'Contact']);

# parse emails
foreach my $c (@{$result->{'Contact'}}) {
    my $fname = $c->{Profiles}->{Personal}->{FirstName};
    my $lname = $c->{Profiles}->{Personal}->{LastName};
    foreach my $a (@{$c->{Emails}->{Email}}) {
        print "$fname\t$lname\t" . $a->{Address} . "\n";
    }
}

Originally posted on paul.vox.com

Fri, Nov. 9th, 2007 10:50 am (UTC)
(Anonymous): Very nice

Very nice indeed.

We've used another protocol called DeltaSync to fetch/update contacts as well in another project, but that was more for contacts sync.

I guess this seems simpler for fetching conacts.
We'll add them to our Hotmail importer implementation for your benefit :)

Regards
Octazen Solutions