MSN Tween Auth Class
This class is used for authenticating with Passport 3.0, used in the MSN protocol.
See this page for more information.
Python 2.5 or the elementtree module is required.
Download script as zip
Tags:
python
msn
See this page for more information.
Python 2.5 or the elementtree module is required.
Download script as zip
Source
- auth.py
- exceptions.py
- common.py
- #
- # Levon - MSN Messenger Client for Linux
- # Written by Lucas van Dijk. http://www.return1.net
- # (c) Copyright 2007-2008 Lucas van Dijk
- #
- # 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 3, 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
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
- #
- # $Id$
- #
- from urllib import urlencode, unquote
- from cgi import escape
- from exceptions import *
- import re, common
- import httplib
- try:
- from xml.etree import ElementTree
- except ImportError:
- try:
- from elementtree import ElementTree
- except ImportError:
- raise
- class AuthBase:
- """
- This is the base authentication class
- Currently only Tween authentication is supported, but Single Sign On
- will be implemented in the future
- """
- def __init__(self, email, password):
- self.email = email
- self.password = password
- def authenticate(self):
- raise RuntimeError, 'Abstract class, this method must be overridden'
- def get_token(self):
- raise RuntimeError, 'Abstract class, this method must be overridden'
- class Tweener(AuthBase):
- """
- This class handles tween authentication
- Sends the SOAP request, and checks the response
- You can get the token used in the USR command with get_token()
- """
- token = None
- def authenticate(self, ticket):
- self.ticket = unquote(ticket).replace(',', '&')
- self.request('https://loginnet.passport.com/RST.srf')
- def request(self, url):
- regexp = re.compile(r'(https://)?([a-zA-Z0-9.-_]+?)/([A-Za-z0-9.-_/]+)')
- match = regexp.match(url)
- if match == None:
- raise ValueError, 'Not a valid URL given'
- else:
- host = match.group(2)
- file = '/%s' % match.group(3)
- request = r"""<?xml version="1.0" encoding="UTF-8"?>
- <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc" xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
- <Header>
- <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
- <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
- <ps:BinaryVersion>4</ps:BinaryVersion>
- <ps:UIVersion>1</ps:UIVersion>
- <ps:Cookies></ps:Cookies>
- <ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>
- </ps:AuthInfo>
- <wsse:Security>
- <wsse:UsernameToken Id="user">
- <wsse:Username>%s</wsse:Username>
- <wsse:Password>%s</wsse:Password>
- </wsse:UsernameToken>
- </wsse:Security>
- </Header>
- <Body>
- <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
- <wst:RequestSecurityToken Id="RST0">
- <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
- <wsp:AppliesTo>
- <wsa:EndpointReference>
- <wsa:Address>http://Passport.NET/tb</wsa:Address>
- </wsa:EndpointReference>
- </wsp:AppliesTo>
- </wst:RequestSecurityToken>
- <wst:RequestSecurityToken Id="RST1">
- <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
- <wsp:AppliesTo>
- <wsa:EndpointReference>
- <wsa:Address>messenger.msn.com</wsa:Address>
- </wsa:EndpointReference>
- </wsp:AppliesTo>
- <wsse:PolicyReference URI="?%s"></wsse:PolicyReference>
- </wst:RequestSecurityToken>
- </ps:RequestMultipleSecurityTokens>
- </Body>
- </Envelope>""" % (escape(self.email), escape(self.password), escape(self.ticket))
- https = httplib.HTTPSConnection(host)
- https.request('POST', file, request)
- response = https.getresponse()
- self.check_response(response.read())
- def check_response(self, response):
- xml = ElementTree.XML(response)
- body = xml[1][0]
- token_elem = body[1].find('{http://schemas.xmlsoap.org/ws/2004/04/trust}RequestedSecurityToken/{http://schemas.xmlsoap.org/ws/2003/06/secext}BinarySecurityToken')
- if token_elem != None:
- self.token = common.decode_htmlentities(token_elem.text)
- else:
- # Couldn't login
- # Maybe redirect?
- redirect_elem = body.find('{http://schemas.microsoft.com/Passport/SoapServices/SOAPFault}redirectUrl')
- if redirect_elem != None:
- self.request(redirect_elem.text)
- return
- else:
- pp_elem = xml[0][0]
- error_code = pp_elem.find('{http://schemas.microsoft.com/Passport/SoapServices/SOAPFault}reqstatus').text
- if error_code == '0x80048821':
- raise MSNAuthException
- else:
- raise MSNException, (body[1].text, int(error_code.replace('0x', '')))
- def get_token(self):
- if self.token == None:
- raise ValueError, 'You must first request the token'
- return self.token
- #
- # Levon - MSN Messenger Client for Linux
- # Written by Lucas van Dijk. http://www.return1.net
- # (c) Copyright 2007-2008 Lucas van Dijk
- #
- # 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 3, 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
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
- #
- # $Id$
- #
- class MSNException(Exception):
- """
- Base exception for MSN exceptions
- """
- def __init__(self, message, code):
- self.message = message
- self.code = code
- def __str__(self):
- return unicode(self).encode('utf-8')
- def __unicode__(self):
- return u"Error code: %d, %s" % (self.code, self.message)
- class MSNAuthException(MSNException):
- def __init__(self):
- self.message = u"Wrong email/password"
- self.code = 0
- def __unicode__(self):
- return u"%s" % self.message
- #
- # Levon - MSN Messenger Client for Linux
- # Written by Lucas van Dijk. http://www.return1.net
- # (c) Copyright 2007-2008 Lucas van Dijk
- #
- # 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 3, 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
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
- #
- # $Id$
- #
- from htmlentitydefs import name2codepoint as n2cp
- def substitute_entity(match):
- ent = match.group(2)
- if match.group(1) == "#":
- return unichr(int(ent))
- else:
- cp = n2cp.get(ent)
- if cp:
- return unichr(cp)
- else:
- return match.group()
- def decode_htmlentities(string):
- entity_re = re.compile("&(#?)(d{1,5}|w{1,8});")
- return entity_re.subn(substitute_entity, string)[0]

