""" Python wrapper for Microsoft.com web services 1) Get a developer's token from http://msdn.microsoft.com/webservices/building/livewebservices/mscomservices/default.aspx 2) Create a file called mstoken.py formatted like this: token = '2346iusdflkjah9873562' pin = 'aksjdhfliaugw34' 3) Call msweb.getVersion, msweb.getCultures, or msweb.getTopDownloads """ __AUTHOR__ = (('Mark Pilgrim', 'http://diveintomark.org/'), ('Sam Ruby', 'http://intertwingly.net/')) __LICENSE__ = 'Python' import random, binascii, urllib2, time, sha, types from xml.sax.saxutils import escape from xml.dom import minidom import mstoken # required, see module doc try: from htmlentitydefs import codepoint2name except: import htmlentitydefs codepoint2name={} for (name,codepoint) in htmlentitydefs.entitydefs.iteritems(): if codepoint.startswith('&#'): codepoint=unichr(int(codepoint[2:-1])) codepoint2name[ord(codepoint)]=name endpoint = 'http://ws.microsoft.com/mscomservice/mscom.asmx' def buildHeader(token=mstoken.token, pin=mstoken.pin): nonce = sha.new(str(random.random())).digest() created = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(time.time())) digest = sha.new(nonce + created + pin).digest() securityParams = { 'PasswordDigest': binascii.b2a_base64(digest), 'Username': token, 'Nonce': binascii.b2a_base64(nonce), 'Created': created, 'Expires': time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(time.time()+180)) } return """ %(Username)s %(PasswordDigest)s %(Nonce)s %(Created)s %(Expires)s """ % securityParams def buildBody(methodName, argDict): soapHeader = buildHeader() methodParams = '' for k, v in argDict.items(): v = escape(str(v)) methodParams += '<%s>%s\n' % (k, escape(v), k) return """ %(soapHeader)s <%(methodName)s xmlns="http://www.microsoft.com"> %(methodParams)s """ % vars() def sendRequest(methodName, argDict): request = urllib2.Request(endpoint, buildBody(methodName, argDict)) request.add_header("Content-type", "text/xml") request.add_header("SOAPAction", '"http://www.microsoft.com/%s"' % methodName) doc = minidom.parse(urllib2.urlopen(request)).documentElement doc = doc.getElementsByTagNameNS("http://schemas.xmlsoap.org/soap/envelope/","Body")[0] doc.normalize() strip(doc) doc = doc.childNodes[0].childNodes[0] return doc def strip(e): for n in list(e.childNodes): if n.nodeType == n.ELEMENT_NODE: strip(n) elif len(e.childNodes)>1: e.removeChild(n) def htmlescape(data): "escape all entities and non-7bit ASCII characters" for i in range(len(data)-1,-1,-1): n=ord(data[i]) if n<128: continue if n in codepoint2name: data = '%s&%s;%s' % (data[:i], codepoint2name[n], data[i+1:]) else: data = '%s&#%d;%s' % (data[:i], n, data[i+1:]) return str(data) def getVersion(): data = sendRequest('GetVersion', {}) return htmlescape(data.childNodes[0].nodeValue) def getCultures(): data = sendRequest('GetCultures', {}) return dict([[htmlescape(node.childNodes[0].nodeValue) for node in child.childNodes] for child in data.childNodes]) def getTopDownloads(type="Popular", topN=10, cultureID="en-US"): """type in ('Popular', 'Recent') 1 <= topN <= 25 cultureID one of keys returned from getCultures """ data = sendRequest('GetTopDownloads', vars()) return [dict([(str(t.nodeName),htmlescape(t.childNodes[0].nodeValue)) for t in node.childNodes]) for node in data.childNodes] def getDownloadDetail(downloadID, cultureID="en-US"): """downloadID one of IDs returned from getTopDownloads cultureID one of keys returned from getCultures """ data = sendRequest('GetDownloadDetail', vars()) data = dict([(str(node.nodeName),node.childNodes) for node in data.childNodes]) for (name,value) in data.items(): if len(value)==0: continue if value[0].nodeType == value[0].TEXT_NODE: data[name]=htmlescape(value[0].nodeValue) elif name=='Releases': data[name] = [dict([(str(t.nodeName), [dict([(str(y.nodeName),htmlescape(y.childNodes[0].nodeValue)) for y in x.childNodes]) for x in t.childNodes]) for t in node.childNodes]) for node in value] else: data[name]=[dict([(str(t.nodeName),htmlescape(t.childNodes[0].nodeValue)) for t in node.childNodes]) for node in value] return data if __name__ == '__main__': from pprint import pprint print getVersion() pprint(getCultures()) pprint(getTopDownloads()) pprint(getDownloadDetail('141d5f9e-07c1-462a-baef-5eab5c851cf5'))