diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..97b0c84 Binary files /dev/null and b/.DS_Store differ diff --git a/backend/whatsapp.py b/backend/whatsapp.py index 07a125a..a7a7dfa 100755 --- a/backend/whatsapp.py +++ b/backend/whatsapp.py @@ -20,7 +20,11 @@ import hashlib; import hmac; import traceback; - +import binascii +from Crypto import Random +from whatsapp_defines import WATags, WASingleByteTokens, WADoubleByteTokens, WAWebMessageInfo; +from whatsapp_binary_writer import whatsappWriteBinary, WASingleByteTokens, WADoubleByteTokens, WAWebMessageInfo; +from whatsapp_defines import WAMetrics; import websocket; import curve25519; import pyqrcode; @@ -132,6 +136,10 @@ def onClose(self, ws): if self.onCloseCallback is not None and "func" in self.onCloseCallback: self.onCloseCallback["func"](self.onCloseCallback); eprint("WhatsApp backend Websocket closed."); + def keepAlive(self): + if self.activeWs is not None: + self.activeWs.send("?,,") + Timer(20.0, self.keepAlive).start() def onMessage(self, ws, message): try: @@ -145,7 +153,8 @@ def onMessage(self, ws, message): if messageContent[0] == 'Pong' and messageContent[1] == True: pend["callback"]({"Connected": True,"user":self.connInfo["me"],"pushname":self.connInfo["pushname"]}) elif pend["desc"] == "_restoresession": - eprint("") # TODO implement Challenge Solving + pend["callback"]["func"]({ "type": "restore_session" }, pend["callback"]); + elif pend["desc"] == "_login": eprint("Message after login: ", message); self.loginInfo["serverRef"] = json.loads(messageContent)["ref"]; @@ -182,7 +191,7 @@ def onMessage(self, ws, message): if isinstance(jsonObj, list) and len(jsonObj) > 0: # check if the result is an array eprint(json.dumps(jsonObj)); if jsonObj[0] == "Conn": - Timer(25, lambda: self.activeWs.send('?,,')).start() # Keepalive Request + Timer(20.0, self.keepAlive).start() # Keepalive Request self.connInfo["clientToken"] = jsonObj[1]["clientToken"]; self.connInfo["serverToken"] = jsonObj[1]["serverToken"]; self.connInfo["browserToken"] = jsonObj[1]["browserToken"]; @@ -200,6 +209,7 @@ def onMessage(self, ws, message): self.loginInfo["key"]["encKey"] = keysDecrypted[:32]; self.loginInfo["key"]["macKey"] = keysDecrypted[32:64]; + self.save_session(); # eprint("private key : ", base64.b64encode(self.loginInfo["privateKey"].serialize())); # eprint("secret : ", base64.b64encode(self.connInfo["secret"])); # eprint("shared secret : ", base64.b64encode(self.connInfo["sharedSecret"])); @@ -210,6 +220,14 @@ def onMessage(self, ws, message): eprint("set connection info: client, server and browser token; secret, shared secret, enc key, mac key"); eprint("logged in as " + jsonObj[1]["pushname"] + " (" + jsonObj[1]["wid"] + ")"); + elif jsonObj[0] == "Cmd": + if jsonObj[1]["type"] == "challenge": # Do challenge + challenge = WhatsAppEncrypt(self.loginInfo["key"]["encKey"], self.loginInfo["key"]["macKey"], base64.b64decode(jsonObj[1]["challenge"])) + + challenge = base64.b64encode(challenge) + messageTag = str(getTimestamp()); + eprint(json.dumps( [messageTag,["admin","challenge",challenge,self.connInfo["serverToken"],self.loginInfo["clientId"]]])) + self.activeWs.send(json.dumps( [messageTag,["admin","challenge",challenge,self.connInfo["serverToken"],self.loginInfo["clientId"]]])); elif jsonObj[0] == "Stream": pass; elif jsonObj[0] == "Props": @@ -239,17 +257,33 @@ def generateQRCode(self, callback=None): self.activeWs.send(message); def restoreSession(self, callback=None): + with open("session.json","r") as f: + session_file = f.read() + session = json.loads(session_file) + self.connInfo["clientToken"] = session['clientToken'] + self.connInfo["serverToken"] = session['serverToken'] + self.loginInfo["clientId"] = session['clientId'] + self.loginInfo["key"]["macKey"] = session['macKey'].encode("latin_1") + self.loginInfo["key"]["encKey"] = session['encKey'].encode("latin_1") + messageTag = str(getTimestamp()) - message = messageTag + ',["admin","init",['+ WHATSAPP_WEB_VERSION + '],["Chromium at ' + datetime.now().isoformat() + '","Chromium"],"' + self.loginInfo["clientId"] + '",true]' + message = messageTag + ',["admin","init",['+ WHATSAPP_WEB_VERSION + '],["StatusDownloader","Chromium"],"' + self.loginInfo["clientId"] + '",true]' self.activeWs.send(message) messageTag = str(getTimestamp()) - self.messageQueue[messageTag] = {"desc": "_restoresession"} + self.messageQueue[messageTag] = {"desc": "_restoresession","callback": callback} message = messageTag + ',["admin","login","' + self.connInfo["clientToken"] + '", "' + self.connInfo[ "serverToken"] + '", "' + self.loginInfo["clientId"] + '", "takeover"]' self.activeWs.send(message) - + def save_session(self): + session = {"clientToken":self.connInfo["clientToken"],"serverToken":self.connInfo["serverToken"], + "clientId":self.loginInfo["clientId"],"macKey": self.loginInfo["key"]["macKey"].decode("latin_1") + ,"encKey": self.loginInfo["key"]["encKey"].decode("latin_1")}; + f = open("./session.json","w") + f.write(json.dumps(session)) + f.close() + def getLoginInfo(self, callback): callback["func"]({ "type": "login_info", "data": self.loginInfo }, callback); @@ -266,7 +300,7 @@ def sendTextMessage(self, number, text): self.messageSentCount = self.messageSentCount + 1 self.messageQueue[messageId] = {"desc": "__sending"} self.activeWs.send(payload, websocket.ABNF.OPCODE_BINARY) - + def status(self, callback=None): if self.activeWs is not None: messageTag = str(getTimestamp()) diff --git a/backend/whatsapp_web_backend.py b/backend/whatsapp_web_backend.py index b75711d..0e33a6e 100755 --- a/backend/whatsapp_web_backend.py +++ b/backend/whatsapp_web_backend.py @@ -87,6 +87,8 @@ def handleMessage(self): cmd = obj["command"]; if cmd == "backend-generateQRCode": currWhatsAppInstance.generateQRCode(callback); + elif cmd == "backend-restoreSession": + currWhatsAppInstance.restoreSession(callback); elif cmd == "backend-getLoginInfo": currWhatsAppInstance.getLoginInfo(callback); elif cmd == "backend-getConnectionInfo": diff --git a/client/index.html b/client/index.html index 81bc7bf..24cd268 100644 --- a/client/index.html +++ b/client/index.html @@ -35,6 +35,7 @@
Loading JSON...
", + buttons: { + noclose: { + label: "Collapse/Expand All", + className: "btn-info", + callback: function () { + if (!tree) + return true; + + if (collapse === false) + tree.expand(); + else + tree.collapse(); + + collapse = !collapse; + + return false; + } + } + } + }); + dialog.init(() => { + tree = jsonTree.create(jsonData, dialog.find(".bootbox-body").empty()[0]); + }); + }); + + let tableRow = $("