diff --git a/README.md b/README.md
index eee64ae07..46a374d0b 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,10 @@
-## Updates (August 01, 2015)
+## Updates (September 07, 2015)
+Yowsup v2.4 is out, See [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.4)
+
+### Updates (August 01, 2015)
Yowsup v2.3.185 is out, contains fixes in axolotl integration. See [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.3.185)
### Updates (July 27, 2015)
diff --git a/yowsup-cli b/yowsup-cli
index 7a29bb64f..010cece54 100755
--- a/yowsup-cli
+++ b/yowsup-cli
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-__version__ = "2.0.12"
+__version__ = "2.0.13"
__author__ = "Tarek Galal"
import sys, argparse, yowsup, logging
@@ -205,8 +205,7 @@ class DemosArgParser(YowArgParser):
credentialsOpts.add_argument("-c", "--config", action="store",
help = "Path to config file containing authentication info. For more info about config format use --help-config")
- configGroup.add_argument("-m", "--moxie", action="store_true", help="Enable experimental support for the"
- " new WhatsApp encryption")
+ configGroup.add_argument("-M", "--unmoxie", action="store_true", help="Disable E2E Encryption")
cmdopts = self.add_argument_group("Command line interface demo")
cmdopts.add_argument('-y', '--yowsup', action = "store_true", help = "Start the Yowsup command line client")
@@ -224,9 +223,6 @@ class DemosArgParser(YowArgParser):
def process(self):
super(DemosArgParser, self).process()
- if self.args["moxie"]:
- logger.warning("--moxie/-m is deprecated and will be removed soon as e2e encryption is now almost mandatory.")
-
if self.args["yowsup"]:
self.startCmdline()
elif self.args["echo"]:
@@ -255,7 +251,7 @@ class DemosArgParser(YowArgParser):
if not credentials:
print("Error: You must specify a configuration method")
sys.exit(1)
- stack = cli.YowsupCliStack(credentials, self.args["moxie"])
+ stack = cli.YowsupCliStack(credentials, not self.args["unmoxie"])
stack.start()
def startEcho(self):
@@ -265,7 +261,7 @@ class DemosArgParser(YowArgParser):
print("Error: You must specify a configuration method")
sys.exit(1)
try:
- stack = echoclient.YowsupEchoStack(credentials, self.args["moxie"])
+ stack = echoclient.YowsupEchoStack(credentials, not self.args["unmoxie"])
stack.start()
except KeyboardInterrupt:
print("\nYowsdown")
@@ -280,7 +276,7 @@ class DemosArgParser(YowArgParser):
try:
stack = sendclient.YowsupSendStack(credentials, [([self.args["send"][0], self.args["send"][1]])],
- self.args["moxie"])
+ not self.args["unmoxie"])
stack.start()
except KeyboardInterrupt:
print("\nYowsdown")
@@ -293,7 +289,7 @@ class DemosArgParser(YowArgParser):
print("Error: You must specify a configuration method")
sys.exit(1)
try:
- stack = contacts.YowsupSyncStack(credentials,self.args["sync"].split(','),self.args["moxie"])
+ stack = contacts.YowsupSyncStack(credentials,self.args["sync"].split(','), not self.args["unmoxie"])
stack.start()
except KeyboardInterrupt:
print("\nYowsdown")
@@ -326,4 +322,3 @@ if __name__ == "__main__":
parser = modeDict[mode]()
if not parser.process():
parser.print_help()
-
diff --git a/yowsup/__init__.py b/yowsup/__init__.py
index d8d7cfc06..c9bd9263a 100644
--- a/yowsup/__init__.py
+++ b/yowsup/__init__.py
@@ -1,2 +1,2 @@
-__version__ = "2.3.185"
+__version__ = "2.4"
__author__ = "Tarek Galal"
diff --git a/yowsup/demos/cli/layer.py b/yowsup/demos/cli/layer.py
index 1754c11b2..186eb2e38 100644
--- a/yowsup/demos/cli/layer.py
+++ b/yowsup/demos/cli/layer.py
@@ -48,6 +48,7 @@ def __init__(self):
self.username = None
self.sendReceipts = True
self.disconnectAction = self.__class__.DISCONNECT_ACTION_PROMPT
+ self.credentials = None
#add aliases to make it user to use commands. for example you can then do:
# /message send foobar "HI"
@@ -77,6 +78,9 @@ def normalizeJid(self, number):
return "%s@s.whatsapp.net" % number
+ def setCredentials(self, username, password):
+ self.getLayerInterface(YowAuthenticationProtocolLayer).setCredentials(username, password)
+
def onEvent(self, layerEvent):
if layerEvent.getName() == self.__class__.EVENT_START:
self.startInput()
@@ -406,23 +410,20 @@ def contacts_sync(self, contacts):
@clicmd("Disconnect")
def disconnect(self):
if self.assertConnected():
+
self.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_DISCONNECT))
@clicmd("Quick login")
def L(self):
- return self.login(*self.getProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS))
-
- @clicmd("Login to WhatsApp", 0)
- def login(self, username, b64password):
-
if self.connected:
return self.output("Already connected, disconnect first")
+ self.getLayerInterface(YowNetworkLayer).connect()
+ return True
- self.getStack().setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS, (username, b64password))
- connectEvent = YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT)
- self.broadcastEvent(connectEvent)
- return True #prompt will wait until notified
-
+ @clicmd("Login to WhatsApp", 0)
+ def login(self, username, b64password):
+ self.setCredentials(username, b64password)
+ return self.L()
######## receive #########
diff --git a/yowsup/demos/cli/stack.py b/yowsup/demos/cli/stack.py
index de06a2d59..715110a8e 100644
--- a/yowsup/demos/cli/stack.py
+++ b/yowsup/demos/cli/stack.py
@@ -1,9 +1,8 @@
-from yowsup.stacks import YowStack, YowStackBuilder
+from yowsup.stacks import YowStackBuilder
from .layer import YowsupCliLayer
from yowsup.layers.auth import AuthError
from yowsup.layers import YowLayerEvent
-from yowsup import env
-from yowsup.env import S40YowsupEnv
+from yowsup.layers.auth import YowAuthenticationProtocolLayer
import sys
class YowsupCliStack(object):
@@ -15,6 +14,7 @@ def __init__(self, credentials, encryptionEnabled = True):
.push(YowsupCliLayer)\
.build()
+ # self.stack.setCredentials(credentials)
self.stack.setCredentials(credentials)
def start(self):
@@ -27,4 +27,4 @@ def start(self):
print("Auth Error, reason %s" % e)
except KeyboardInterrupt:
print("\nYowsdown")
- sys.exit(0)
\ No newline at end of file
+ sys.exit(0)
diff --git a/yowsup/env/env_s40.py b/yowsup/env/env_s40.py
index 5b312b2c1..8ef3d6602 100644
--- a/yowsup/env/env_s40.py
+++ b/yowsup/env/env_s40.py
@@ -2,11 +2,11 @@
import base64
import hashlib
class S40YowsupEnv(YowsupEnv):
- _VERSION = "2.12.89"
+ _VERSION = "2.12.96"
_OS_NAME= "S40"
_OS_VERSION = "14.26"
_DEVICE_NAME = "Nokia302"
- _TOKEN_STRING = "PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk1435688727801{phone}"
+ _TOKEN_STRING = "PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk1439921717185{phone}"
_AXOLOTL = True
def getVersion(self):
@@ -34,4 +34,3 @@ def getUserAgent(self):
OS_VERSION = self.getOSVersion(),
DEVICE_NAME = self.getDeviceName()
)
-
diff --git a/yowsup/layers/__init__.py b/yowsup/layers/__init__.py
index 8c86d4843..a59954761 100644
--- a/yowsup/layers/__init__.py
+++ b/yowsup/layers/__init__.py
@@ -33,6 +33,10 @@ class YowLayer(object):
def __init__(self):
self.setLayers(None, None)
+ self.interface = None
+
+ def getLayerInterface(self, YowLayerClass = None):
+ return self.interface if YowLayerClass is None else self.__stack.getLayerInterface(YowLayerClass)
def setStack(self, stack):
self.__stack = stack
@@ -149,6 +153,11 @@ def __init__(self, sublayers = None):
s.emitEvent = self.subEmitEvent
+ def getLayerInterface(self, YowLayerClass):
+ for s in self.sublayers:
+ if s.__class__ == YowLayerClass:
+ return s
+
def setStack(self, stack):
super(YowParallelLayer, self).setStack(stack)
for s in self.sublayers:
@@ -182,6 +191,10 @@ def onEvent(self, yowLayerEvent):
def __str__(self):
return " - ".join([l.__str__() for l in self.sublayers])
+class YowLayerInterface(object):
+ def __init__(self, layer):
+ self._layer = layer
+
class YowLayerTest(unittest.TestCase):
def __init__(self, *args):
diff --git a/yowsup/layers/auth/layer_authentication.py b/yowsup/layers/auth/layer_authentication.py
index 1d239424a..af198d00c 100644
--- a/yowsup/layers/auth/layer_authentication.py
+++ b/yowsup/layers/auth/layer_authentication.py
@@ -1,4 +1,4 @@
-from yowsup.layers import YowLayer, YowLayerEvent, YowProtocolLayer
+from yowsup.layers import YowLayerEvent, YowProtocolLayer
from .keystream import KeyStream
from yowsup.common.tools import TimeTools
from .layer_crypt import YowCryptLayer
@@ -6,6 +6,7 @@
from .autherror import AuthError
from .protocolentities import *
from yowsup.common.tools import StorageTools
+from .layer_interface_authentication import YowAuthenticationProtocolLayerInterface
import base64
class YowAuthenticationProtocolLayer(YowProtocolLayer):
EVENT_LOGIN = "org.openwhatsapp.yowsup.event.auth.login"
@@ -22,29 +23,38 @@ def __init__(self):
"stream:error": (self.handleStreamError, None),
}
super(YowAuthenticationProtocolLayer, self).__init__(handleMap)
- self.credentials = None
+ self.interface = YowAuthenticationProtocolLayerInterface(self)
+ self.credentials = None #left for backwards-compat
+ self._credentials = None #new style set
def __str__(self):
return "Authentication Layer"
- def __getCredentials(self):
- u, pb64 = self.getProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS)
+ def __getCredentials(self, credentials = None):
+ u, pb64 = credentials or self.getProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS)
if type(pb64) is str:
pb64 = pb64.encode()
password = base64.b64decode(pb64)
return (u, bytearray(password))
+ def setCredentials(self, credentials):
+ self.setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS, credentials) #keep for now
+ self._credentials = self.__getCredentials(credentials)
+
+ def getUsername(self, full = False):
+ if self._credentials:
+ return self._credentials[0] if not full else ("%s@s.whatsapp.net" % self._credentials[0])
+ else:
+ prop = self.getProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS)
+ return prop[0] if prop else None
+
def onEvent(self, event):
if event.getName() == YowNetworkLayer.EVENT_STATE_CONNECTED:
self.login()
- elif event.getName() == YowNetworkLayer.EVENT_STATE_CONNECT:
- self.credentials = self.__getCredentials()
- if not self.credentials:
- raise AuthError("Auth stopped connection signal as no credentials have been set")
## general methods
def login(self):
-
+ self.credentials = self._credentials or self.__getCredentials()
self._sendFeatures()
self._sendAuth()
@@ -102,7 +112,7 @@ def _sendResponse(self,nonce):
responseEntity = ResponseProtocolEntity(authBlob)
#to prevent enr whole response
- self.broadcastEvent(YowLayerEvent(YowCryptLayer.EVENT_KEYS_READY, keys = (inputKey, None)))
+ self.broadcastEvent(YowLayerEvent(YowCryptLayer.EVENT_KEYS_READY, keys = (inputKey, None)))
self.entityToLower(responseEntity)
self.broadcastEvent(YowLayerEvent(YowCryptLayer.EVENT_KEYS_READY, keys = (inputKey, outputKey)))
#YowCryptLayer.setProp("outputKey", outputKey)
diff --git a/yowsup/layers/auth/layer_crypt.py b/yowsup/layers/auth/layer_crypt.py
index f0e6e8762..9d7822670 100644
--- a/yowsup/layers/auth/layer_crypt.py
+++ b/yowsup/layers/auth/layer_crypt.py
@@ -13,7 +13,7 @@ def __init__(self):
self.keys = (None,None)
def onEvent(self, yowLayerEvent):
- if yowLayerEvent.getName() == YowNetworkLayer.EVENT_STATE_CONNECT:
+ if yowLayerEvent.getName() == YowNetworkLayer.EVENT_STATE_CONNECTED:
self.keys = (None,None)
elif yowLayerEvent.getName() == YowCryptLayer.EVENT_KEYS_READY:
self.keys = yowLayerEvent.getArg("keys")
diff --git a/yowsup/layers/auth/layer_interface_authentication.py b/yowsup/layers/auth/layer_interface_authentication.py
new file mode 100644
index 000000000..49d72f668
--- /dev/null
+++ b/yowsup/layers/auth/layer_interface_authentication.py
@@ -0,0 +1,8 @@
+from yowsup.layers import YowLayerInterface
+
+class YowAuthenticationProtocolLayerInterface(YowLayerInterface):
+ def setCredentials(self, phone, password):
+ self._layer.setCredentials(phone, password)
+
+ def getUsername(self, full = False):
+ return self._layer.getUsername(full)
\ No newline at end of file
diff --git a/yowsup/layers/axolotl/layer.py b/yowsup/layers/axolotl/layer.py
index 6088980be..732211f67 100644
--- a/yowsup/layers/axolotl/layer.py
+++ b/yowsup/layers/axolotl/layer.py
@@ -4,6 +4,7 @@
from .store.sqlite.liteaxolotlstore import LiteAxolotlStore
from axolotl.sessionbuilder import SessionBuilder
from yowsup.layers.protocol_messages.protocolentities.message import MessageProtocolEntity
+from yowsup.layers.protocol_receipts.protocolentities import OutgoingReceiptProtocolEntity
from yowsup.layers.network.layer import YowNetworkLayer
from yowsup.layers.auth.layer_authentication import YowAuthenticationProtocolLayer
from axolotl.ecc.curve import Curve
@@ -16,10 +17,12 @@
from .protocolentities import GetKeysIqProtocolEntity, ResultGetKeysIqProtocolEntity
from axolotl.util.hexutil import HexUtil
from axolotl.invalidmessageexception import InvalidMessageException
+from axolotl.duplicatemessagexception import DuplicateMessageException
from .protocolentities import EncryptNotification
from yowsup.layers.protocol_acks.protocolentities import OutgoingAckProtocolEntity
from axolotl.invalidkeyidexception import InvalidKeyIdException
from axolotl.nosessionexception import NoSessionException
+from axolotl.untrustedidentityexception import UntrustedIdentityException
from .protocolentities.receipt_outgoing_retry import RetryOutgoingReceiptProtocolEntity
import binascii
import sys
@@ -46,23 +49,33 @@ def __init__(self):
self.skipEncJids = []
self.v2Jids = [] #people we're going to send v2 enc messages
+ @property
+ def store(self):
+ if self._store is None:
+ self.store = LiteAxolotlStore(
+ StorageTools.constructPath(
+ self.getProp(
+ YowAuthenticationProtocolLayer.PROP_CREDENTIALS)[0],
+ self.__class__._DB
+ )
+ )
+ self.state = self.__class__._STATE_HASKEYS if self.store.getLocalRegistrationId() is not None \
+ else self.__class__._STATE_INIT
+
+ return self._store
+
+ @store.setter
+ def store(self, store):
+ self._store = store
+
def __str__(self):
return "Axolotl Layer"
### store and state
- def initStore(self):
- self.store = LiteAxolotlStore(
- StorageTools.constructPath(
- self.getProp(
- YowAuthenticationProtocolLayer.PROP_CREDENTIALS)[0],
- self.__class__._DB
- )
- )
- self.state = self.__class__._STATE_HASKEYS if self.store.getLocalRegistrationId() is not None \
- else self.__class__._STATE_INIT
+
def isInitState(self):
- return self.state == self.__class__._STATE_INIT
+ return self.store == None or self.state == self.__class__._STATE_INIT
def isGenKeysState(self):
return self.state == self.__class__._STATE_GENKEYS
@@ -72,8 +85,7 @@ def isGenKeysState(self):
def onEvent(self, yowLayerEvent):
if yowLayerEvent.getName() == self.__class__.EVENT_PREKEYS_SET:
self.sendKeys(fresh=False)
- elif yowLayerEvent.getName() == YowNetworkLayer.EVENT_STATE_CONNECT:
- self.initStore()
+ elif yowLayerEvent.getName() == YowNetworkLayer.EVENT_STATE_CONNECTED:
if self.isInitState():
self.setProp(YowAuthenticationProtocolLayer.PROP_PASSIVE, True)
elif yowLayerEvent.getName() == YowAuthenticationProtocolLayer.EVENT_AUTHED:
@@ -86,7 +98,9 @@ def onEvent(self, yowLayerEvent):
#no need to traverse it to upper layers?
self.setProp(YowAuthenticationProtocolLayer.PROP_PASSIVE, False)
self.state = self.__class__._STATE_HASKEYS
- self.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
+ self.getLayerInterface(YowNetworkLayer).connect()
+ else:
+ self.store = None
def send(self, node):
if node.tag == "message" and node["type"] == "text" and node["to"] not in self.skipEncJids:
@@ -228,8 +242,14 @@ def handleEncMessage(self, node):
self.pendingIncomingMessages[node["from"]].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:
+ logger.error(e)
+ logger.warning("Ignoring message with untrusted identity")
def handlePreKeyWhisperMessage(self, node):
pkMessageProtocolEntity = EncryptedMessageProtocolEntity.fromProtocolTreeNode(node)
diff --git a/yowsup/layers/interface/interface.py b/yowsup/layers/interface/interface.py
index 4d41e5c00..c957a5b7d 100644
--- a/yowsup/layers/interface/interface.py
+++ b/yowsup/layers/interface/interface.py
@@ -2,6 +2,8 @@
from yowsup.layers.protocol_iq.protocolentities import IqProtocolEntity
from yowsup.layers.network import YowNetworkLayer
from yowsup.layers.auth import YowAuthenticationProtocolLayer
+from yowsup.layers.protocol_receipts.protocolentities import OutgoingReceiptProtocolEntity
+from yowsup.layers.protocol_acks.protocolentities import IncomingAckProtocolEntity
import inspect
class ProtocolEntityCallback(object):
@@ -15,8 +17,10 @@ def __call__(self, fn):
class YowInterfaceLayer(YowLayer):
def __init__(self):
+ super(YowInterfaceLayer, self).__init__()
self.callbacks = {}
self.iqRegistry = {}
+ # self.receiptsRegistry = {}
members = inspect.getmembers(self, predicate=inspect.ismethod)
for m in members:
if hasattr(m[1], "callback"):
@@ -29,6 +33,33 @@ def _sendIq(self, iqEntity, onSuccess = None, onError = None):
self.iqRegistry[iqEntity.getId()] = (iqEntity, onSuccess, onError)
self.toLower(iqEntity)
+ # def _sendReceipt(self, outgoingReceiptProtocolEntity, onAck = None):
+ # assert outgoingReceiptProtocolEntity.__class__ == OutgoingReceiptProtocolEntity,\
+ # "Excepted OutgoingReceiptProtocolEntity in _sendReceipt, got %s" % outgoingReceiptProtocolEntity.__class__
+ # self.receiptsRegistry[outgoingReceiptProtocolEntity.getId()] = (outgoingReceiptProtocolEntity, onAck)
+ # self.toLower(outgoingReceiptProtocolEntity)
+
+ # def processReceiptsRegistry(self, incomingAckProtocolEntity):
+ # '''
+ # entity: IncomingAckProtocolEntity
+ # '''
+ #
+ # if incomingAckProtocolEntity.__class__ != IncomingAckProtocolEntity:
+ # return False
+ #
+ # receipt_id = incomingAckProtocolEntity.getId()
+ # if receipt_id in self.receiptsRegistry:
+ # originalReceiptEntity, ackClbk = self.receiptsRegistry[receipt_id]
+ # del self.receiptsRegistry[receipt_id]
+ #
+ # if ackClbk:
+ # ackClbk(incomingAckProtocolEntity, originalReceiptEntity)
+ #
+ # return True
+ #
+ # return False
+
+
def processIqRegistry(self, entity):
"""
:type entity: IqProtocolEntity
@@ -48,14 +79,10 @@ def processIqRegistry(self, entity):
return False
def getOwnJid(self, full = True):
- jid = self.getProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS)[0]
- if jid:
- return jid + "@s.whatsapp.net" if full else jid
- return None
+ return self.getLayerInterface(YowAuthenticationProtocolLayer).getUsername(full)
def connect(self):
- loginEvent = YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT)
- self.broadcastEvent(loginEvent)
+ self.getLayerInterface(YowNetworkLayer).connect()
def disconnect(self):
disconnectEvent = YowLayerEvent(YowNetworkLayer.EVENT_STATE_DISCONNECT)
@@ -69,8 +96,9 @@ def receive(self, entity):
entityType = entity.getTag()
if entityType in self.callbacks:
self.callbacks[entityType](entity)
-
+ else:
+ self.toUpper(entity)
+
def __str__(self):
return "Interface Layer"
-
diff --git a/yowsup/layers/network/layer.py b/yowsup/layers/network/layer.py
index db5d468ca..168a6067a 100644
--- a/yowsup/layers/network/layer.py
+++ b/yowsup/layers/network/layer.py
@@ -1,5 +1,6 @@
from yowsup.layers import YowLayer, YowLayerEvent
from yowsup.common.http.httpproxy import HttpProxy
+from yowsup.layers.network.layer_interface import YowNetworkLayerInterface
import asyncore, socket, logging
logger = logging.getLogger(__name__)
@@ -19,6 +20,7 @@ class YowNetworkLayer(YowLayer, asyncore.dispatcher_with_send):
def __init__(self):
YowLayer.__init__(self)
+ self.interface = YowNetworkLayerInterface(self)
asyncore.dispatcher.__init__(self)
httpProxy = HttpProxy.getFromEnviron()
proxyHandler = None
@@ -31,24 +33,34 @@ def onConnect():
proxyHandler = httpProxy.handler()
proxyHandler.onConnect = onConnect
self.proxyHandler = proxyHandler
-
+
def onEvent(self, ev):
if ev.getName() == YowNetworkLayer.EVENT_STATE_CONNECT:
- self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
- self.out_buffer = bytearray()
- endpoint = self.getProp(self.__class__.PROP_ENDPOINT)
- logger.debug("Connecting to %s:%s" % endpoint)
- if self.proxyHandler != None:
- logger.debug("HttpProxy connect: %s:%d" % endpoint)
- self.proxyHandler.connect(self, endpoint)
- else:
- self.connect(endpoint)
+ self.createConnection()
return True
elif ev.getName() == YowNetworkLayer.EVENT_STATE_DISCONNECT:
- self.handle_close(ev.getArg("reason") or "Requested")
+ self.destroyConnection(ev.getArg("reason"))
return True
+ def createConnection(self):
+ self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.out_buffer = bytearray()
+ endpoint = self.getProp(self.__class__.PROP_ENDPOINT)
+ logger.debug("Connecting to %s:%s" % endpoint)
+ if self.proxyHandler != None:
+ logger.debug("HttpProxy connect: %s:%d" % endpoint)
+ self.proxyHandler.connect(self, endpoint)
+ else:
+ self.connect(endpoint)
+
+ def destroyConnection(self, reason = None):
+ self.handle_close(reason or "Requested")
+
+ def getStatus(self):
+ return self.connected
+
def handle_connect(self):
+ self.connected = True
if self.proxyHandler != None:
logger.debug("HttpProxy handle connect")
self.proxyHandler.send(self)
@@ -56,10 +68,11 @@ def handle_connect(self):
self.emitEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECTED))
def handle_close(self, reason = "Connection Closed"):
+ self.connected = False
logger.debug("Disconnected, reason: %s" % reason)
self.emitEvent(YowLayerEvent(self.__class__.EVENT_STATE_DISCONNECTED, reason = reason, detached=True))
self.close()
-
+
def handle_error(self):
raise
@@ -73,8 +86,9 @@ def handle_read(self):
self.receive(data)
def send(self, data):
- self.out_buffer = self.out_buffer + data
- self.initiate_send()
+ if self.connected:
+ self.out_buffer = self.out_buffer + data
+ self.initiate_send()
def receive(self, data):
self.toUpper(data)
diff --git a/yowsup/layers/network/layer_interface.py b/yowsup/layers/network/layer_interface.py
new file mode 100644
index 000000000..a75594ebf
--- /dev/null
+++ b/yowsup/layers/network/layer_interface.py
@@ -0,0 +1,10 @@
+from yowsup.layers import YowLayerInterface
+class YowNetworkLayerInterface(YowLayerInterface):
+ def connect(self):
+ self._layer.createConnection()
+
+ def disconnect(self):
+ self._layer.destroyConnection()
+
+ def getStatus(self):
+ return self._layer.getStatus()
\ No newline at end of file
diff --git a/yowsup/layers/protocol_messages/protocolentities/message.py b/yowsup/layers/protocol_messages/protocolentities/message.py
index 4d10b46ff..98e9f71c5 100644
--- a/yowsup/layers/protocol_messages/protocolentities/message.py
+++ b/yowsup/layers/protocol_messages/protocolentities/message.py
@@ -55,24 +55,21 @@ def toProtocolTreeNode(self):
"id" : self._id,
}
- if not self.isOutgoing():
- attribs["t"] = str(self.timestamp)
-
- if self.offline is not None:
- attribs["offline"] = "1" if self.offline else "0"
-
if self.isOutgoing():
attribs["to"] = self.to
else:
attribs["from"] = self._from
- if self.notify:
- attribs["notify"] = self.notify
+ attribs["t"] = str(self.timestamp)
- if self.retry:
- attribs["retry"] = str(self.retry)
- if self.participant:
- attribs["participant"] = self.participant
+ if self.offline is not None:
+ attribs["offline"] = "1" if self.offline else "0"
+ if self.notify:
+ attribs["notify"] = self.notify
+ if self.retry:
+ attribs["retry"] = str(self.retry)
+ if self.participant:
+ attribs["participant"] = self.participant
xNode = None
@@ -109,7 +106,6 @@ def forward(self, to, _id = None):
OutgoingMessage.to = to
OutgoingMessage._from = None
OutgoingMessage._id = self._generateId() if _id is None else _id
- OutgoingMessage.participant = None # very strange issue with group messages otherwise
return OutgoingMessage
@staticmethod
diff --git a/yowsup/layers/protocol_receipts/protocolentities/receipt_outgoing.py b/yowsup/layers/protocol_receipts/protocolentities/receipt_outgoing.py
index ffdf43536..33a11d246 100644
--- a/yowsup/layers/protocol_receipts/protocolentities/receipt_outgoing.py
+++ b/yowsup/layers/protocol_receipts/protocolentities/receipt_outgoing.py
@@ -13,20 +13,39 @@ class OutgoingReceiptProtocolEntity(ReceiptProtocolEntity):
read
- INCOMING
-
+ multiple items:
+
+
+
+
+
+
'''
- def __init__(self, _id, to, read = False, participant = None, callId = None):
- super(OutgoingReceiptProtocolEntity, self).__init__(_id)
- self.setOutgoingData(to, read, participant, callId)
- def setOutgoingData(self, to, read, participant, callId):
+ def __init__(self, messageIds, to, read = False, participant = None, callId = None):
+ if type(messageIds) in (list, tuple):
+ if len(messageIds) > 1:
+ receiptId = self._generateId()
+ else:
+ receiptId = messageIds[0]
+ else:
+ receiptId = messageIds
+ messageIds = [messageIds]
+
+ super(OutgoingReceiptProtocolEntity, self).__init__(receiptId)
+ self.setOutgoingData(messageIds, to, read, participant, callId)
+
+ def setOutgoingData(self, messageIds, to, read, participant, callId):
+ self.messageIds = messageIds
self.to = to
self.read = read
self.participant = participant
self.callId = callId
-
+
+ def getMessageIds(self):
+ return self.messageIds
+
def toProtocolTreeNode(self):
node = super(OutgoingReceiptProtocolEntity, self).toProtocolTreeNode()
if self.read:
@@ -38,7 +57,11 @@ def toProtocolTreeNode(self):
node.addChild(offer)
node.setAttribute("to", self.to)
- node.setAttribute("t", str(int(time.time())))
+
+ if len(self.messageIds) > 1:
+ listNode = ProtocolTreeNode("list")
+ listNode.addChildren([ProtocolTreeNode("item", {"id": mId}) for mId in self.messageIds])
+ node.addChild(listNode)
return node
@@ -47,13 +70,21 @@ def __str__(self):
out += "To: \n%s" % self.to
if self.read:
out += "Type: \n%s" % "read"
+ out += "For: \n%s" % self.messageIds
return out
@staticmethod
def fromProtocolTreeNode(node):
+ listNode = node.getChild("list")
+ messageIds = []
+ if listNode:
+ messageIds = [child["id"] for child in listNode.getChildren()]
+ else:
+ messageIds = [node["id"]]
+
return OutgoingReceiptProtocolEntity(
- node.getAttributeValue("id"),
- node.getAttributeValue("to"),
- node.getAttributeValue("type"),
- node.getAttributeValue("participant")
+ messageIds,
+ node["to"],
+ node["type"] == "read",
+ node["participant"]
)
diff --git a/yowsup/stacks/yowstack.py b/yowsup/stacks/yowstack.py
index 324dbd508..c17a0850c 100644
--- a/yowsup/stacks/yowstack.py
+++ b/yowsup/stacks/yowstack.py
@@ -42,6 +42,10 @@ class YowStackBuilder(object):
def __init__(self):
self.layers = ()
+ self._props = {}
+
+ def setProp(self, key, value):
+ self._props[key] = value
def pushDefaultLayers(self, axolotl = False):
defaultLayers = YowStackBuilder.getDefaultLayers(axolotl)
@@ -57,7 +61,7 @@ def pop(self):
return self
def build(self):
- return YowStack(self.layers, reversed = False)
+ return YowStack(self.layers, reversed = False, props = self._props)
@staticmethod
def getDefaultLayers(axolotl = False, groups = True, media = True, privacy = True, profiles = True):
@@ -119,20 +123,36 @@ class YowStack(object):
__stack = []
__stackInstances = []
__detachedQueue = Queue.Queue()
- def __init__(self, stackClassesArr = None, reversed = True):
+ def __init__(self, stackClassesArr = None, reversed = True, props = None):
stackClassesArr = stackClassesArr or ()
self.__stack = stackClassesArr[::-1] if reversed else stackClassesArr
self.__stackInstances = []
- self._construct()
- self._props = {}
+ self._props = props or {}
self.setProp(YowNetworkLayer.PROP_ENDPOINT, YowConstants.ENDPOINTS[random.randint(0,len(YowConstants.ENDPOINTS)-1)])
self.setProp(YowCoderLayer.PROP_DOMAIN, YowConstants.DOMAIN)
self.setProp(YowCoderLayer.PROP_RESOURCE, env.CURRENT_ENV.getResource())
+ self._construct()
+
+
+ def getLayerInterface(self, YowLayerClass):
+ for inst in self.__stackInstances:
+ if inst.__class__ == YowLayerClass:
+ return inst.getLayerInterface()
+ elif inst.__class__ == YowParallelLayer:
+ res = inst.getLayerInterface(YowLayerClass)
+ if res:
+ return res
+ def send(self, data):
+ self.__stackInstances[-1].send(data)
+
+ def receive(self, data):
+ self.__stackInstances[0].receive(data)
+
def setCredentials(self, credentials):
- self.setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS, credentials)
+ self.getLayerInterface(YowAuthenticationProtocolLayer).setCredentials(credentials)
def addLayer(self, layerClass):
self.__stack.push(layerClass)
@@ -202,4 +222,3 @@ def _construct(self):
def getLayer(self, layerIndex):
return self.__stackInstances[layerIndex]
-