diff --git a/yowsup/demos/cli/stack.py b/yowsup/demos/cli/stack.py index 4994fcfe2..fa5c84fdc 100644 --- a/yowsup/demos/cli/stack.py +++ b/yowsup/demos/cli/stack.py @@ -3,7 +3,7 @@ from yowsup.layers.auth import AuthError from yowsup.layers import YowLayerEvent from yowsup.layers.auth import YowAuthenticationProtocolLayer -from yowsup.layers.axolotl.layer import YowAxolotlLayer +from yowsup.layers.axolotl import AxolotlReceivelayer import sys class YowsupCliStack(object): @@ -17,7 +17,7 @@ def __init__(self, credentials, encryptionEnabled = True): # self.stack.setCredentials(credentials) self.stack.setCredentials(credentials) - self.stack.setProp(YowAxolotlLayer.PROP_IDENTITY_AUTOTRUST, True) + self.stack.setProp(AxolotlReceivelayer.PROP_IDENTITY_AUTOTRUST, True) def start(self): print("Yowsup Cli client\n==================\nType /help for available commands\n") diff --git a/yowsup/layers/axolotl/__init__.py b/yowsup/layers/axolotl/__init__.py index 46422dc4b..3b53b7894 100644 --- a/yowsup/layers/axolotl/__init__.py +++ b/yowsup/layers/axolotl/__init__.py @@ -1 +1,4 @@ -from .layer import YowAxolotlLayer \ No newline at end of file +# from .layer import YowAxolotlLayer +from .layer_send import AxolotlSendLayer +from .layer_control import AxolotlControlLayer +from .layer_receive import AxolotlReceivelayer diff --git a/yowsup/layers/axolotl/layer_base.py b/yowsup/layers/axolotl/layer_base.py new file mode 100644 index 000000000..6f6b2341f --- /dev/null +++ b/yowsup/layers/axolotl/layer_base.py @@ -0,0 +1,34 @@ +from yowsup.layers.axolotl.store.sqlite.liteaxolotlstore import LiteAxolotlStore +from yowsup.layers import YowProtocolLayer, YowLayerEvent, EventCallback +from yowsup.common.tools import StorageTools +from yowsup.layers.auth.layer_authentication import YowAuthenticationProtocolLayer + + +class AxolotlBaseLayer(YowProtocolLayer): + _DB = "axolotl.db" + def __init__(self): + super(AxolotlBaseLayer, self).__init__() + self.store = None + + def onNewStoreSet(self, store): + pass + + def send(self, node): + self.toLower(node) + + @property + def store(self): + if self._store is None: + self.store = LiteAxolotlStore( + StorageTools.constructPath( + self.getProp( + YowAuthenticationProtocolLayer.PROP_CREDENTIALS)[0], + self.__class__._DB + ) + ) + return self._store + + @store.setter + def store(self, store): + self._store = store + self.onNewStoreSet(self._store) diff --git a/yowsup/layers/axolotl/layer_control.py b/yowsup/layers/axolotl/layer_control.py new file mode 100644 index 000000000..e2e1a9236 --- /dev/null +++ b/yowsup/layers/axolotl/layer_control.py @@ -0,0 +1,136 @@ +from .layer_base import AxolotlBaseLayer +from yowsup.layers import YowLayerEvent, EventCallback +from yowsup.layers.network.layer import YowNetworkLayer +from axolotl.util.keyhelper import KeyHelper +from yowsup.layers.axolotl.protocolentities import * +from yowsup.layers.auth.layer_authentication import YowAuthenticationProtocolLayer +from axolotl.util.hexutil import HexUtil +from axolotl.ecc.curve import Curve +import logging +import binascii +import sys + +logger = logging.getLogger(__name__) + +class AxolotlControlLayer(AxolotlBaseLayer): + _STATE_INIT = 0 + _STATE_GENKEYS = 1 + _STATE_HASKEYS = 2 + _COUNT_PREKEYS = 200 + EVENT_PREKEYS_SET = "org.openwhatsapp.yowsup.events.axololt.setkeys" + + def __init__(self): + super(AxolotlControlLayer, self).__init__() + self.state = self.__class__._STATE_INIT + + def onNewStoreSet(self, store): + super(AxolotlControlLayer, self).onNewStoreSet(store) + if store is not None: + self.state = self.__class__._STATE_HASKEYS if store.getLocalRegistrationId() is not None \ + else self.__class__._STATE_INIT + + def receive(self, protocolTreeNode): + """ + :type protocolTreeNode: ProtocolTreeNode + """ + if not self.processIqRegistry(protocolTreeNode): + if protocolTreeNode.tag == "notification" and protocolTreeNode["type"] == "encrypt": + self.onEncryptNotification(protocolTreeNode) + return + self.toUpper(protocolTreeNode) + + def isInitState(self): + return self.store == None or self.state == self.__class__._STATE_INIT + + def isGenKeysState(self): + return self.state == self.__class__._STATE_GENKEYS + + + def onEncryptNotification(self, protocolTreeNode): + entity = EncryptNotification.fromProtocolTreeNode(protocolTreeNode) + ack = OutgoingAckProtocolEntity(protocolTreeNode["id"], "notification", protocolTreeNode["type"], protocolTreeNode["from"]) + self.toLower(ack.toProtocolTreeNode()) + self.sendKeys(fresh=False, countPreKeys = self.__class__._COUNT_PREKEYS - entity.getCount()) + + @EventCallback(EVENT_PREKEYS_SET) + def onPreKeysSet(self, yowLayerEvent): + self.sendKeys(fresh=False) + + @EventCallback(YowNetworkLayer.EVENT_STATE_CONNECTED) + def onConnected(self, yowLayerEvent): + if self.isInitState(): + self.setProp(YowAuthenticationProtocolLayer.PROP_PASSIVE, True) + + @EventCallback(YowAuthenticationProtocolLayer.EVENT_AUTHED) + def onAuthed(self, yowLayerEvent): + if yowLayerEvent.getArg("passive") and self.isInitState(): + logger.info("Axolotl layer is generating keys") + self.sendKeys() + + @EventCallback(YowNetworkLayer.EVENT_STATE_DISCONNECTED) + def onDisconnected(self, yowLayerEvent): + if self.isGenKeysState(): + #we requested this disconnect in this layer to switch off passive + #no need to traverse it to upper layers? + self.setProp(YowAuthenticationProtocolLayer.PROP_PASSIVE, False) + self.state = self.__class__._STATE_HASKEYS + self.getLayerInterface(YowNetworkLayer).connect() + else: + self.store = None + ### keys set and get + def sendKeys(self, fresh = True, countPreKeys = _COUNT_PREKEYS): + identityKeyPair = KeyHelper.generateIdentityKeyPair() if fresh else self.store.getIdentityKeyPair() + registrationId = KeyHelper.generateRegistrationId() if fresh else self.store.getLocalRegistrationId() + preKeys = KeyHelper.generatePreKeys(KeyHelper.getRandomSequence(), countPreKeys) + signedPreKey = KeyHelper.generateSignedPreKey(identityKeyPair, KeyHelper.getRandomSequence(65536)) + preKeysDict = {} + for preKey in preKeys: + keyPair = preKey.getKeyPair() + preKeysDict[self.adjustId(preKey.getId())] = self.adjustArray(keyPair.getPublicKey().serialize()[1:]) + + signedKeyTuple = (self.adjustId(signedPreKey.getId()), + self.adjustArray(signedPreKey.getKeyPair().getPublicKey().serialize()[1:]), + self.adjustArray(signedPreKey.getSignature())) + + setKeysIq = SetKeysIqProtocolEntity(self.adjustArray(identityKeyPair.getPublicKey().serialize()[1:]), signedKeyTuple, preKeysDict, Curve.DJB_TYPE, self.adjustId(registrationId)) + + onResult = lambda _, __: self.persistKeys(registrationId, identityKeyPair, preKeys, signedPreKey, fresh) + self._sendIq(setKeysIq, onResult, self.onSentKeysError) + + def persistKeys(self, registrationId, identityKeyPair, preKeys, signedPreKey, fresh): + total = len(preKeys) + curr = 0 + prevPercentage = 0 + + if fresh: + self.store.storeLocalData(registrationId, identityKeyPair) + self.store.storeSignedPreKey(signedPreKey.getId(), signedPreKey) + + for preKey in preKeys: + self.store.storePreKey(preKey.getId(), preKey) + curr += 1 + currPercentage = int((curr * 100) / total) + if currPercentage == prevPercentage: + continue + prevPercentage = currPercentage + #logger.debug("%s" % currPercentage + "%") + sys.stdout.write("Storing prekeys %d%% \r" % (currPercentage)) + sys.stdout.flush() + + if fresh: + self.state = self.__class__._STATE_GENKEYS + self.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_DISCONNECT)) + + def onSentKeysError(self, errorNode, keysEntity): + raise Exception("Sent keys were not accepted") + + def adjustArray(self, arr): + return HexUtil.decodeHex(binascii.hexlify(arr)) + + def adjustId(self, _id): + _id = format(_id, 'x') + zfiller = len(_id) if len(_id) % 2 == 0 else len(_id) + 1 + _id = _id.zfill(zfiller if zfiller > 6 else 6) + # if len(_id) % 2: + # _id = "0" + _id + return binascii.unhexlify(_id) diff --git a/yowsup/layers/axolotl/layer_receive.py b/yowsup/layers/axolotl/layer_receive.py new file mode 100644 index 000000000..d3f2c2ef5 --- /dev/null +++ b/yowsup/layers/axolotl/layer_receive.py @@ -0,0 +1,269 @@ +from .layer_base import AxolotlBaseLayer + +from yowsup.layers.protocol_receipts.protocolentities import OutgoingReceiptProtocolEntity +from yowsup.layers.protocol_messages.proto.wa_pb2 import * +from yowsup.layers.axolotl.protocolentities import * +from yowsup.structs import ProtocolTreeNode + +from axolotl.protocol.prekeywhispermessage import PreKeyWhisperMessage +from axolotl.protocol.whispermessage import WhisperMessage +from axolotl.sessioncipher import SessionCipher +from axolotl.groups.groupcipher import GroupCipher +from axolotl.invalidmessageexception import InvalidMessageException +from axolotl.duplicatemessagexception import DuplicateMessageException +from axolotl.invalidkeyidexception import InvalidKeyIdException +from axolotl.nosessionexception import NoSessionException +from axolotl.untrustedidentityexception import UntrustedIdentityException +from axolotl.axolotladdress import AxolotlAddress +from axolotl.groups.senderkeyname import SenderKeyName +from axolotl.groups.groupsessionbuilder import GroupSessionBuilder +from axolotl.protocol.senderkeydistributionmessage import SenderKeyDistributionMessage + +import logging +import copy +logger = logging.getLogger(__name__) + +class AxolotlReceivelayer(AxolotlBaseLayer): + PROP_IDENTITY_AUTOTRUST = "org.openwhatsapp.yowsup.prop.axolotl.INDENTITY_AUTOTRUST" + def __init__(self): + super(AxolotlReceivelayer, self).__init__() + self.v2Jids = [] #people we're going to send v2 enc messages + self.sessionCiphers = {} + self.groupCiphers = {} + self.pendingIncomingMessages = {} + + def receive(self, protocolTreeNode): + """ + :type protocolTreeNode: ProtocolTreeNode + """ + if not self.processIqRegistry(protocolTreeNode): + if protocolTreeNode.tag == "message": + self.onMessage(protocolTreeNode) + return + elif protocolTreeNode.tag == "receipt" and protocolTreeNode["type"] == "retry": + # should bring up that message, resend it, but in upper layer? + # as it might have to be fetched from a persistent storage + pass + self.toUpper(protocolTreeNode) + + + def processPendingIncomingMessages(self, jid): + if jid in self.pendingIncomingMessages: + for messageNode in self.pendingIncomingMessages[jid]: + self.onMessage(messageNode) + + del self.pendingIncomingMessages[jid] + + ##### handling received data ##### + + def onMessage(self, protocolTreeNode): + encNode = protocolTreeNode.getChild("enc") + if encNode: + self.handleEncMessage(protocolTreeNode) + else: + self.toUpper(protocolTreeNode) + + def handleEncMessage(self, node): + encMessageProtocolEntity = EncryptedMessageProtocolEntity.fromProtocolTreeNode(node) + isGroup = node["participant"] is not None + senderJid = node["participant"] if isGroup else node["from"] + encNode = None + if node.getChild("enc")["v"] == "2" and senderJid not in self.v2Jids: + self.v2Jids.append(senderJid) + try: + if encMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_PKMSG): + self.handlePreKeyWhisperMessage(node) + elif encMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_MSG): + self.handleWhisperMessage(node) + if encMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_SKMSG): + self.handleSenderKeyMessage(node) + except InvalidMessageException as e: + logger.error(e) + retry = RetryOutgoingReceiptProtocolEntity.fromMessageNode(node) + retry.setRegData(self.store.getLocalRegistrationId()) + self.toLower(retry.toProtocolTreeNode()) + except InvalidKeyIdException as e: + logger.error(e) + retry = RetryOutgoingReceiptProtocolEntity.fromMessageNode(node) + retry.setRegData(self.store.getLocalRegistrationId()) + self.toLower(retry.toProtocolTreeNode()) + except NoSessionException as e: + logger.error(e) + entity = GetKeysIqProtocolEntity([senderJid]) + if senderJid not in self.pendingIncomingMessages: + self.pendingIncomingMessages[senderJid] = [] + self.pendingIncomingMessages[senderJid].append(node) + + self._sendIq(entity, lambda a, b: self.onGetKeysResult(a, b, self.processPendingIncomingMessages), self.onGetKeysError) + except DuplicateMessageException as e: + logger.error(e) + logger.warning("Going to send the delivery receipt myself !") + self.toLower(OutgoingReceiptProtocolEntity(node["id"], node["from"]).toProtocolTreeNode()) + + except UntrustedIdentityException as e: + if(self.getProp(self.__class__.PROP_IDENTITY_AUTOTRUST, False)): + logger.warning("Autotrusting identity for %s" % e.getName()) + self.store.saveIdentity(e.getName(), e.getIdentityKey()) + return self.handleEncMessage(node) + else: + logger.error(e) + logger.warning("Ignoring message with untrusted identity") + def handlePreKeyWhisperMessage(self, node): + pkMessageProtocolEntity = EncryptedMessageProtocolEntity.fromProtocolTreeNode(node) + enc = pkMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_PKMSG) + preKeyWhisperMessage = PreKeyWhisperMessage(serialized=enc.getData()) + sessionCipher = self.getSessionCipher(pkMessageProtocolEntity.getAuthor(False)) + plaintext = sessionCipher.decryptPkmsg(preKeyWhisperMessage) + if enc.getVersion() == 2: + padding = ord(plaintext[-1]) & 0xFF + self.parseAndHandleMessageProto(pkMessageProtocolEntity, plaintext[:-padding]) + else: + self.handleConversationMessage(node, plaintext) + + def handleWhisperMessage(self, node): + encMessageProtocolEntity = EncryptedMessageProtocolEntity.fromProtocolTreeNode(node) + + enc = encMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_MSG) + whisperMessage = WhisperMessage(serialized=enc.getData()) + sessionCipher = self.getSessionCipher(encMessageProtocolEntity.getAuthor(False)) + plaintext = sessionCipher.decryptMsg(whisperMessage) + + if enc.getVersion() == 2: + padding = ord(plaintext[-1]) & 0xFF + self.parseAndHandleMessageProto(encMessageProtocolEntity, plaintext[:-padding]) + else: + self.handleConversationMessage(encMessageProtocolEntity.toProtocolTreeNode(), plaintext) + + def handleSenderKeyMessage(self, node): + encMessageProtocolEntity = EncryptedMessageProtocolEntity.fromProtocolTreeNode(node) + enc = encMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_SKMSG) + + senderKeyName = SenderKeyName(encMessageProtocolEntity.getFrom(True), AxolotlAddress(encMessageProtocolEntity.getParticipant(False), 0)) + groupCipher = GroupCipher(self.store, senderKeyName) + try: + plaintext = groupCipher.decrypt(enc.getData()) + padding = ord(plaintext[-1]) & 0xFF + self.parseAndHandleMessageProto(encMessageProtocolEntity, plaintext[:-padding]) + except NoSessionException as e: + logger.error(e) + retry = RetryOutgoingReceiptProtocolEntity.fromMessageNode(node) + retry.setRegData(self.store.getLocalRegistrationId()) + self.toLower(retry.toProtocolTreeNode()) + + + def parseAndHandleMessageProto(self, encMessageProtocolEntity, serializedData): + node = encMessageProtocolEntity.toProtocolTreeNode() + m = Message() + try: + m.ParseFromString(serializedData) + except: + print("DUMP:") + print(serializedData) + print([s for s in serializedData]) + print([ord(s) for s in serializedData]) + raise + if not m or not serializedData: + raise ValueError("Empty message") + + if m.HasField("sender_key_distribution_message"): + axolotlAddress = AxolotlAddress(encMessageProtocolEntity.getParticipant(False), 0) + self.handleSenderKeyDistributionMessage(m.sender_key_distribution_message, axolotlAddress) + elif m.HasField("conversation"): + self.handleConversationMessage(node, m.conversation) + elif m.HasField("contact_message"): + self.handleContactMessage(node, m.contact_message) + elif m.HasField("url_message"): + self.handleUrlMessage(node, m.url_message) + elif m.HasField("location_message"): + self.handleLocationMessage(node, m.location_message) + elif m.HasField("image_message"): + self.handleImageMessage(node, m.image_message) + else: + print(m) + raise ValueError("Unhandled") + + def handleSenderKeyDistributionMessage(self, senderKeyDistributionMessage, axolotlAddress): + groupId = senderKeyDistributionMessage.groupId + axolotlSenderKeyDistributionMessage = SenderKeyDistributionMessage(serialized=senderKeyDistributionMessage.axolotl_sender_key_distribution_message) + groupSessionBuilder = GroupSessionBuilder(self.store) + senderKeyName = SenderKeyName(groupId, axolotlAddress) + groupSessionBuilder.process(senderKeyName, axolotlSenderKeyDistributionMessage) + + def handleConversationMessage(self, originalEncNode, text): + messageNode = copy.deepcopy(originalEncNode) + messageNode.children = [] + messageNode.addChild(ProtocolTreeNode("body", data = text)) + self.toUpper(messageNode) + + def handleImageMessage(self, originalEncNode, imageMessage): + messageNode = copy.deepcopy(originalEncNode) + messageNode["type"] = "media" + mediaNode = ProtocolTreeNode("media", { + "type": "image", + "filehash": imageMessage.file_sha256, + "size": str(imageMessage.file_length), + "url": imageMessage.url, + "mimetype": imageMessage.mime_type, + "width": imageMessage.width, + "height": imageMessage.height, + "caption": imageMessage.caption, + "encoding": "raw", + "file": "enc", + "ip": "0" + }, data = imageMessage.jpeg_thumbnail) + messageNode.addChild(mediaNode) + + self.toUpper(messageNode) + + def handleUrlMessage(self, originalEncNode, urlMessage): + #convert to ?? + pass + + def handleDocumentMessage(self, originalEncNode, documentMessage): + #convert to ?? + pass + + def handleLocationMessage(self, originalEncNode, locationMessage): + messageNode = copy.deepycopy(originalEncNode) + messageNode["type"] = "media" + mediaNode = ProtocolTreeNode("media", { + "latitude": locationMessage.degrees_latitude, + "longitude": locationMessage.degress_longitude, + "name": "%s %s" % (locationMessage.name, locationMessage.address), + "url": locationMessage.url, + "encoding": "raw", + "type": "location" + }, data=locationMessage.jpeg_thumbnail) + messageNode.addChild(mediaNode) + self.toUpper(messageNode) + + def handleContactMessage(self, originalEncNode, contactMessage): + messageNode = copy.deepycopy(originalEncNode) + messageNode["type"] = "media" + mediaNode = ProtocolTreeNode("media", { + "type": "vcard" + }, [ + ProtocolTreeNode("vcard", {"name": contactMessage.display_name}, data = contactMessage.vcard) + ] ) + messageNode.addChild(mediaNode) + self.toUpper(messageNode) + + + + def getSessionCipher(self, recipientId): + if recipientId in self.sessionCiphers: + sessionCipher = self.sessionCiphers[recipientId] + else: + sessionCipher = SessionCipher(self.store, self.store, self.store, self.store, recipientId, 1) + self.sessionCiphers[recipientId] = sessionCipher + + return sessionCipher + + def getGroupCipher(self, groupId, senderId): + senderKeyName = SenderKeyName(groupId, AxolotlAddress(senderId, 1)) + if senderKeyName in self.groupCiphers: + groupCipher = self.groupCiphers[senderKeyName] + else: + groupCipher = GroupCipher(self.store, senderKeyName) + self.groupCiphers[senderKeyName] = groupCipher + return groupCipher diff --git a/yowsup/layers/axolotl/layer_send.py b/yowsup/layers/axolotl/layer_send.py new file mode 100644 index 000000000..1e8653fe3 --- /dev/null +++ b/yowsup/layers/axolotl/layer_send.py @@ -0,0 +1,197 @@ +from yowsup.layers.protocol_messages.proto.wa_pb2 import * +from yowsup.layers.axolotl.protocolentities import * + +from axolotl.sessionbuilder import SessionBuilder +from axolotl.protocol.whispermessage import WhisperMessage +from axolotl.sessioncipher import SessionCipher +from axolotl.groups.groupcipher import GroupCipher +from axolotl.axolotladdress import AxolotlAddress +from axolotl.groups.senderkeyname import SenderKeyName + +import copy +import logging + +from .layer_base import AxolotlBaseLayer + +logger = logging.getLogger(__name__) + +class AxolotlSendLayer(AxolotlBaseLayer): + def __init__(self): + super(AxolotlSendLayer, self).__init__() + + self.sessionCiphers = {} + self.groupCiphers = {} + self.pendingMessages = {} + self.skipEncJids = [] + self.v2Jids = [] + + def __str__(self): + return "Axolotl Layer" + + + def send(self, node): + if node.tag == "message" and node["to"] not in self.skipEncJids: + self.handlePlaintextNode(node) + return + self.toLower(node) + + + def processPendingMessages(self, jid): + if jid in self.pendingMessages: + for messageNode in self.pendingMessages[jid]: + if jid in self.skipEncJids: + self.toLower(messageNode) + else: + self.handlePlaintextNode(messageNode) + + del self.pendingMessages[jid] + + + + #### handling message types + + def handlePlaintextNode(self, node): + recipient_id = node["to"].split('@')[0] + v2 = node["to"] in self.v2Jids + if not self.store.containsSession(recipient_id, 1): + entity = GetKeysIqProtocolEntity([node["to"]]) + if node["to"] not in self.pendingMessages: + self.pendingMessages[node["to"]] = [] + self.pendingMessages[node["to"]].append(node) + + self._sendIq(entity, lambda a, b: self.onGetKeysResult(a, b, self.processPendingMessages), self.onGetKeysError) + elif v2 or not node.getChild("media"): #media enc is only for v2 messsages + messageData = self.serializeToProtobuf(node) if v2 else node.getChild("body").getData() + if messageData: + sessionCipher = self.getSessionCipher(recipient_id) + ciphertext = sessionCipher.encrypt(messageData) + + mediaType = node.getChild("media")["type"] if node.getChild("media") else None + + encEntity = EncryptedMessageProtocolEntity( + [ + EncProtocolEntity(EncProtocolEntity.TYPE_MSG if ciphertext.__class__ == WhisperMessage else EncProtocolEntity.TYPE_PKMSG , + 2 if v2 else 1, + ciphertext.serialize(), mediaType)], + "text" if not mediaType else "media", + _id= node["id"], + to = node["to"], + notify = node["notify"], + timestamp= node["timestamp"], + participant=node["participant"], + offline=node["offline"], + retry=node["retry"] + ) + self.toLower(encEntity.toProtocolTreeNode()) + else: #case of unserializable messages (audio, video) ? + self.toLower(node) + else: + self.toLower(node) + + def serializeToProtobuf(self, node): + if node.getChild("body"): + return self.serializeTextToProtobuf(node) + elif node.getChild("media"): + return self.serializeMediaToProtobuf(node.getChild("media")) + else: + raise ValueError("No body or media nodes found") + + def serializeTextToProtobuf(self, node): + m = Message() + m.conversation = node.getChild("body").getData() + return m.SerializeToString() + + def serializeMediaToProtobuf(self, mediaNode): + if mediaNode["type"] == "image": + return self.serializeImageToProtobuf(mediaNode) + if mediaNode["type"] == "location": + return self.serializeLocationToProtobuf(mediaNode) + if mediaNode["type"] == "vcard": + return self.serializeContactToProtobuf(mediaNode) + + return None + + def serializeLocationToProtobuf(self, mediaNode): + m = Message() + location_message = LocationMessage() + location_message.degress_latitude = float(mediaNode["latitude"]) + location_message.degress_longitude = float(mediaNode["longitude"]) + location_message.address = mediaNode["name"] + location_message.name = mediaNode["name"] + location_message.url = mediaNode["url"] + + m.location_message.MergeFrom(location_message) + return m.SerializeToString() + + def serializeContactToProtobuf(self, mediaNode): + vcardNode = mediaNode.getChild("vcard") + m = Message() + contact_message = ContactMessage() + contact_message.display_name = vcardNode["name"] + m.vcard = vcardNode.getData() + m.contact_message.MergeFrom(contact_message) + + return m.SerializeToString() + + def serializeImageToProtobuf(self, mediaNode): + m = Message() + image_message = ImageMessage() + image_message.url = mediaNode["url"] + image_message.width = int(mediaNode["width"]) + image_message.height = int(mediaNode["height"]) + image_message.mime_type = mediaNode["mimetype"] + image_message.file_sha256 = mediaNode["filehash"] + image_message.file_length = int(mediaNode["size"]) + image_message.caption = mediaNode["caption"] or "" + image_message.jpeg_thumbnail = mediaNode.getData() + + m.image_message.MergeFrom(image_message) + return m.SerializeToString() + + def serializeUrlToProtobuf(self, node): + pass + + def serializeDocumentToProtobuf(self, node): + pass + + def onGetKeysResult(self, resultNode, getKeysEntity, processPendingFn): + entity = ResultGetKeysIqProtocolEntity.fromProtocolTreeNode(resultNode) + + resultJids = entity.getJids() + for jid in getKeysEntity.getJids(): + + if jid not in resultJids: + self.skipEncJids.append(jid) + self.processPendingMessages(jid) + continue + + recipient_id = jid.split('@')[0] + preKeyBundle = entity.getPreKeyBundleFor(jid) + + sessionBuilder = SessionBuilder(self.store, self.store, self.store, + self.store, recipient_id, 1) + sessionBuilder.processPreKeyBundle(preKeyBundle) + + processPendingFn(jid) + + def onGetKeysError(self, errorNode, getKeysEntity): + pass + ### + + def getSessionCipher(self, recipientId): + if recipientId in self.sessionCiphers: + sessionCipher = self.sessionCiphers[recipientId] + else: + sessionCipher = SessionCipher(self.store, self.store, self.store, self.store, recipientId, 1) + self.sessionCiphers[recipientId] = sessionCipher + + return sessionCipher + + def getGroupCipher(self, groupId, senderId): + senderKeyName = SenderKeyName(groupId, AxolotlAddress(senderId, 1)) + if senderKeyName in self.groupCiphers: + groupCipher = self.groupCiphers[senderKeyName] + else: + groupCipher = GroupCipher(self.store, senderKeyName) + self.groupCiphers[senderKeyName] = groupCipher + return groupCipher diff --git a/yowsup/stacks/yowstack.py b/yowsup/stacks/yowstack.py index 81236e7dc..9142a53e2 100644 --- a/yowsup/stacks/yowstack.py +++ b/yowsup/stacks/yowstack.py @@ -70,8 +70,9 @@ def getDefaultLayers(axolotl = False, groups = True, media = True, privacy = Tru allLayers = coreLayers if axolotl: - from yowsup.layers.axolotl import YowAxolotlLayer - allLayers += (YowAxolotlLayer,) + from yowsup.layers.axolotl import AxolotlSendLayer, AxolotlControlLayer, AxolotlReceivelayer + allLayers += (AxolotlControlLayer,) + allLayers += (YowParallelLayer((AxolotlSendLayer, AxolotlReceivelayer)),) allLayers += (YowParallelLayer(protocolLayers),)