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 Download script as zip

Tag Tags: python msn

Source

  • auth.py
  • exceptions.py
  • common.py
  1. #
  2. # Levon - MSN Messenger Client for Linux
  3. # Written by Lucas van Dijk. http://www.return1.net
  4. # (c) Copyright 2007-2008 Lucas van Dijk
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 3, or (at your option)
  9. # any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
  19. #
  20. # $Id$
  21. #
  22.  
  23. from urllib import urlencode, unquote
  24. from cgi import escape
  25. from exceptions import *
  26. import re, common
  27. import httplib
  28.  
  29. try:
  30.     from xml.etree import ElementTree
  31. except ImportError:
  32.     try:
  33.         from elementtree import ElementTree
  34.     except ImportError:
  35.         raise
  36.  
  37. class AuthBase:
  38.     """
  39.         This is the base authentication class
  40.        
  41.         Currently only Tween authentication is supported, but Single Sign On
  42.         will be implemented in the future
  43.     """
  44.    
  45.     def __init__(self, email, password):
  46.         self.email = email
  47.         self.password = password   
  48.    
  49.     def authenticate(self):
  50.         raise RuntimeError, 'Abstract class, this method must be overridden'
  51.    
  52.     def get_token(self):
  53.         raise RuntimeError, 'Abstract class, this method must be overridden'
  54.  
  55. class Tweener(AuthBase):
  56.     """
  57.         This class handles tween authentication
  58.         Sends the SOAP request, and checks the response
  59.        
  60.         You can get the token used in the USR command with get_token()
  61.     """
  62.    
  63.     token = None
  64.    
  65.     def authenticate(self, ticket):
  66.         self.ticket = unquote(ticket).replace(',', '&')
  67.        
  68.         self.request('https://loginnet.passport.com/RST.srf')
  69.                
  70.     def request(self, url):
  71.         regexp = re.compile(r'(https://)?([a-zA-Z0-9.-_]+?)/([A-Za-z0-9.-_/]+)')
  72.         match = regexp.match(url)
  73.        
  74.         if match == None:
  75.             raise ValueError, 'Not a valid URL given'
  76.         else:
  77.             host = match.group(2)
  78.             file = '/%s' % match.group(3)
  79.        
  80.         request = r"""<?xml version="1.0" encoding="UTF-8"?>
  81.         <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">
  82.           <Header>
  83.             <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
  84.               <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
  85.               <ps:BinaryVersion>4</ps:BinaryVersion>
  86.               <ps:UIVersion>1</ps:UIVersion>
  87.               <ps:Cookies></ps:Cookies>
  88.               <ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>
  89.             </ps:AuthInfo>
  90.             <wsse:Security>
  91.                <wsse:UsernameToken Id="user">
  92.                  <wsse:Username>%s</wsse:Username>
  93.                  <wsse:Password>%s</wsse:Password>
  94.                </wsse:UsernameToken>
  95.             </wsse:Security>
  96.           </Header>
  97.           <Body>
  98.             <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
  99.               <wst:RequestSecurityToken Id="RST0">
  100.                 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
  101.                 <wsp:AppliesTo>
  102.                   <wsa:EndpointReference>              
  103.                     <wsa:Address>http://Passport.NET/tb</wsa:Address>
  104.                   </wsa:EndpointReference>
  105.                 </wsp:AppliesTo>
  106.               </wst:RequestSecurityToken>
  107.               <wst:RequestSecurityToken Id="RST1">
  108.                <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
  109.                 <wsp:AppliesTo>
  110.                   <wsa:EndpointReference>
  111.                     <wsa:Address>messenger.msn.com</wsa:Address>
  112.                   </wsa:EndpointReference>
  113.                 </wsp:AppliesTo>
  114.                 <wsse:PolicyReference URI="?%s"></wsse:PolicyReference>
  115.               </wst:RequestSecurityToken>
  116.             </ps:RequestMultipleSecurityTokens>
  117.           </Body>
  118.         </Envelope>""" % (escape(self.email), escape(self.password), escape(self.ticket))
  119.        
  120.         https = httplib.HTTPSConnection(host)
  121.         https.request('POST', file, request)
  122.         response = https.getresponse()
  123.        
  124.         self.check_response(response.read())
  125.        
  126.     def check_response(self, response):    
  127.         xml = ElementTree.XML(response)
  128.        
  129.         body = xml[1][0]
  130.         token_elem = body[1].find('{http://schemas.xmlsoap.org/ws/2004/04/trust}RequestedSecurityToken/{http://schemas.xmlsoap.org/ws/2003/06/secext}BinarySecurityToken')
  131.        
  132.         if token_elem != None:
  133.             self.token = common.decode_htmlentities(token_elem.text)
  134.         else:
  135.             # Couldn't login
  136.             # Maybe redirect?
  137.             redirect_elem = body.find('{http://schemas.microsoft.com/Passport/SoapServices/SOAPFault}redirectUrl')
  138.             if redirect_elem != None:
  139.                 self.request(redirect_elem.text)
  140.                 return
  141.             else:
  142.                 pp_elem = xml[0][0]
  143.                
  144.                 error_code = pp_elem.find('{http://schemas.microsoft.com/Passport/SoapServices/SOAPFault}reqstatus').text              
  145.                
  146.                 if error_code == '0x80048821':
  147.                     raise MSNAuthException
  148.                 else:
  149.                     raise MSNException, (body[1].text, int(error_code.replace('0x', '')))
  150.                
  151.     def get_token(self):
  152.         if self.token == None:
  153.             raise ValueError, 'You must first request the token'
  154.        
  155.         return self.token
  156.    
  157.  
  1. #
  2. # Levon - MSN Messenger Client for Linux
  3. # Written by Lucas van Dijk. http://www.return1.net
  4. # (c) Copyright 2007-2008 Lucas van Dijk
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 3, or (at your option)
  9. # any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
  19. #
  20. # $Id$
  21. #
  22.  
  23. class MSNException(Exception):
  24.     """
  25.         Base exception for MSN exceptions
  26.     """
  27.    
  28.     def __init__(self, message, code):
  29.         self.message = message
  30.         self.code = code
  31.    
  32.     def __str__(self):
  33.         return unicode(self).encode('utf-8')
  34.    
  35.     def __unicode__(self):
  36.         return u"Error code: %d, %s" % (self.code, self.message)
  37.  
  38. class MSNAuthException(MSNException):
  39.     def __init__(self):
  40.         self.message = u"Wrong email/password"
  41.         self.code = 0
  42.        
  43.     def __unicode__(self):
  44.         return u"%s" % self.message
  1. #
  2. # Levon - MSN Messenger Client for Linux
  3. # Written by Lucas van Dijk. http://www.return1.net
  4. # (c) Copyright 2007-2008 Lucas van Dijk
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 3, or (at your option)
  9. # any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
  19. #
  20. # $Id$
  21. #
  22.  
  23. from htmlentitydefs import name2codepoint as n2cp
  24.  
  25. def substitute_entity(match):
  26.     ent = match.group(2)
  27.     if match.group(1) == "#":
  28.         return unichr(int(ent))
  29.     else:
  30.         cp = n2cp.get(ent)
  31.  
  32.         if cp:
  33.             return unichr(cp)
  34.         else:
  35.             return match.group()
  36.  
  37. def decode_htmlentities(string):
  38.     entity_re = re.compile("&(#?)(d{1,5}|w{1,8});")
  39.     return entity_re.subn(substitute_entity, string)[0]