def __init__(self, on_message_callback, skypename, password): """ :param callable on_message_callback: :return: """ self.on_message_callback = on_message_callback self.skypename = skypename self.login_instance = PyLogin(skypename, password) # TODO: @kuptsov - maybe move to the top of `connect` method
class Connection(object): """ Connection to Skype server. """ registration = '' on_message_callback = None login_instance = None skypename = None def __init__(self, on_message_callback, skypename, password): """ :param callable on_message_callback: :return: """ self.on_message_callback = on_message_callback self.skypename = skypename self.login_instance = PyLogin(skypename, password) # TODO: @kuptsov - maybe move to the top of `connect` method def connect(self, host='s.gateway.messenger.live.com', port=443): print "Connecting to %s:%d" % (host, port) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.ssl_sock = ssl.wrap_socket(s) self.ssl_sock.connect((host, port)) self.sock_file = self.ssl_sock.makefile() self.cnt = 0 self.queue = Queue.Queue(-1) self.pending_requests = { } self.sender = threading.Thread(target=self.sender_loop) self.sender.setDaemon(True) self.sender.start() self.receiver = threading.Thread(target=self.receiver_loop) self.receiver.setDaemon(True) self.receiver.start() self.start_handshake() def shutdown(self): self.ssl_sock.close() self.send(None, None, None, None) def send(self, cmd, url, body, callback=None): if callback == None: callback = self.nop self.queue.put((cmd, url, body, callback)) def sender_loop(self): global cnt while True: try: cmd, url, body, callback = self.queue.get(True, 500) if cmd == None: break self.cnt = self.cnt + 1 self.pending_requests[self.cnt] = callback p = "%s %d %s %d\r\n%s" % (cmd, self.cnt, url, len(body), body) print ">>>>>>> %d pending requests" % len(self.pending_requests) print p self.ssl_sock.send(p) except Queue.Empty: self.send("PNG", "CON", self._reg()) def handle(self, cmd, cnt, url, hdr, body): print '\n\n\ngot MESSAGE\n\n\n' print "inbound " + cmd if cmd == 'SDG': self.on_message_callback(body) def receiver_loop(self): while True: l = self.sock_file.readline() print "<<<<<<<" print l cmd, cnt, url, bsz = l.split() cnt = int(cnt) body = self.sock_file.read(int(bsz)) print body, '-|||||||||' if body[:2] == "\r\n": hdr = dict() body = body[2:] else: cut_here = body.find("\r\n\r\n") hdr = body[:cut_here] body = body[cut_here+4:] hdr = hdr.split("\r\n") if hdr[0] == "": hdr = hdr[1:] hdr = dict(x.split(": ") for x in hdr) if "Set-Registration" in hdr: self.registration = hdr["Set-Registration"] if cmd == "XFR": self.handle_XFR(cmd, url, hdr, body) break if cnt: callback = self.pending_requests[cnt] callback(cmd, url, hdr, body) del self.pending_requests[cnt] else: self.handle(cmd, cnt, url, hdr, body) def nop(self, cmd, url, hdr, body): print "ignoring reply " + cmd def handle_XFR(self, cmd, url, hdr, body): self.shutdown() root = etree.parse(StringIO(body)) target = root.xpath("/xfr/target")[0].text target = target.split(':') self.connect(target[0], int(target[1])) def handle_CNT_reply(self, cmd, url, hdr, body): assert cmd == "CNT" root=etree.parse(StringIO(body)) nonce=root.xpath("/connect-response/nonce")[0].text self.send("ATH", "CON\USER", "\r\n<user><uic>" + self.login_instance.uic(nonce, "WS-SecureConversationSESSION KEY TOKEN") + "</uic><id>" + self.skypename + "</id></user>\r\n", self.handle_ATH_reply); def handle_ATH_reply(self, cmd, url, hdr, body): assert cmd == "ATH" self.send("BND", "CON\MSGR", "\r\n<msgr><ver>2</ver><altVersions><ver>1</ver></altVersions><client><name>Skype</name><ver>2/4.3.0.37/172</ver></client><epid>" + EPID + "</epid></msgr>\r\n", self.handle_BND_reply) def handle_BND_reply(self, cmd, url, hdr, body): assert cmd == "BND" root=etree.parse(StringIO(body)) nonce=root.xpath("/msgr-response/nonce")[0].text rsp=funnydigest(nonce) self.send("PUT", "MSGR\CHALLENGE", self._reg() + "<challenge><appId>PROD0090YUAUV{2B</appId><response>" + rsp + "</response></challenge>\r\n") self.send("PUT", "MSGR\PRESENCE", self._reg() + self._routing(self.skypename) + RELIABILITY + self._publication()) self.send("PUT", "MSGR\SUBSCRIPTIONS", self._reg() + "<subscribe><presence><buddies><all /></buddies></presence><messaging><im /><conversations /></messaging></subscribe>") self.send("GET", "MSGR\RECENTCONVERSATIONS", self._reg() + "<recentconversations><pagesize>100</pagesize></recentconversations>") def start_handshake(self): self.send("CNT", "CON", "\r\n<connect><ver>2</ver><agent><os>Linux</os><osVer>Linux 3.11.0-12-gene</osVer><proc>2 1800 I-586-6-15-13 Intel Core2</proc><lcid>en-US</lcid><country>nz</country></agent></connect>\r\n", self.handle_CNT_reply) def msgr(self, body): self.send("SDG", "MSGR", self._reg() + body) def wrapped_send(self, target_skypename, message): self.msgr( self._routing(target_skypename) + RELIABILITY + messaging_rich(message) ) def _reg(self): return "Registration: " + self.registration + "\r\n\r\n" def _publication(self, hdr={}): defaults = { 'Uri': '/user', 'Content-Type': 'application/user+xml' } defaults.update(hdr) body='<user><sep n="PE" epid="{' + EPID + '}"><VER>2/4.3.0.37/172</VER><TYP>14</TYP><Capabilities>0:0</Capabilities></sep><s n="IM"><Status>NLN</Status></s><sep n="IM" epid="{' + EPID + '}"><Capabilities>0:4194560</Capabilities></sep><sep n="PD" epid="{' + EPID + '}"><EpName>ubuntu</EpName><ClientType>14</ClientType></sep><s n="SKP"><Mood/><Skypename>' + self.skypename + '</Skypename></s><sep n="SKP" epid="{' + EPID + '}"><NodeInfo>x8b24a10d59cbd00e01c0a80137d4126fdd4d8f9c4cb6308989d4120801</NodeInfo><Version>24</Version><Seamless>true</Seamless></sep></user>' return entity("Publication: 1.0", defaults, body) def _routing(self, to, hdr={}): """ :param to: :param hdr: :return: """ defaults = { 'To': '8:' + to, 'From': "8:" + self.skypename + ";epid={" + EPID + "}" } defaults.update(hdr) return entity("Routing: 1.0", defaults)