unexist.dev

Changeset 22:9a31bba19735

Updated yowsup to v2.4
author unexist
date Fri, 25 Sep 2015 13:20:33 +0200
parents 7ec835b12de7
children c17f85b25f52
files yowsup/README.md yowsup/yowsup-cli yowsup/yowsup/__init__.py yowsup/yowsup/common/constants.py yowsup/yowsup/demos/cli/cli.py yowsup/yowsup/demos/cli/layer.py yowsup/yowsup/demos/cli/stack.py yowsup/yowsup/demos/contacts/stack.py yowsup/yowsup/demos/echoclient/stack.py yowsup/yowsup/demos/sendclient/stack.py yowsup/yowsup/env/env_s40.py yowsup/yowsup/layers/__init__.py yowsup/yowsup/layers/auth/layer_authentication.py yowsup/yowsup/layers/auth/layer_crypt.py yowsup/yowsup/layers/axolotl/layer.py yowsup/yowsup/layers/axolotl/protocolentities/message_encrypted.py yowsup/yowsup/layers/axolotl/store/sqlite/litesignedprekeystore.py yowsup/yowsup/layers/interface/interface.py yowsup/yowsup/layers/network/layer.py yowsup/yowsup/layers/protocol_ib/layer.py yowsup/yowsup/layers/protocol_ib/protocolentities/__init__.py yowsup/yowsup/layers/protocol_media/protocolentities/message_media_downloadable_audio.py yowsup/yowsup/layers/protocol_messages/protocolentities/message.py yowsup/yowsup/layers/protocol_receipts/protocolentities/receipt_outgoing.py yowsup/yowsup/stacks/yowstack.py
diffstat 25 files changed, 428 insertions(+), 169 deletions(-) [+]
line wrap: on
line diff
--- a/yowsup/README.md	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/README.md	Fri Sep 25 13:20:33 2015 +0200
@@ -2,7 +2,19 @@
 
 <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z9KKEUVYEY6BN" target="_blank"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" /></a>
 
-## Updates (June 23, 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)
+Yowsup v2.3.183, see [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.3.183)
+
+### Updates (July 21, 2015)
+Yowsup v2.3.167, see [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.3.167)
+
+### Updates (June 23, 2015)
 Yowsup v2.3.123, see [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.3.123)
 
 ### Updates (May 29, 2015)
@@ -17,7 +29,7 @@
 
 Yowsup 2.2.15 is out.
 
-Image upload and send is now implemented. [Read here](https://github.com/tgalal/yowsup/wiki/Yowsup2:-Upload-and-send-Media) how to integrate in your code, or try it out with yowsup-cli
+Image upload and send is now implemented. [Read here](https://github.com/tgalal/yowsup/wiki/Upload-and-send-Media) how to integrate in your code, or try it out with yowsup-cli
 
 [Read full release notes](https://github.com/tgalal/yowsup/releases/tag/v2.2.15)
 
@@ -44,7 +56,7 @@
 
 For platforms which do not support encryption, they will get plaintext messages as usual.
 
-More technical details about axolotl in yowsup [here](https://github.com/tgalal/yowsup/wiki/Yowsup-2:-End-to-End-encryption)
+More technical details about axolotl in yowsup [here](https://github.com/tgalal/yowsup/wiki/End-to-End-encryption)
 
 ### New Send client demo
 
@@ -66,10 +78,9 @@
 
 Here is what you need to know about Yowsup 2.0 to get started: (Or quickly [jump to installation](#installation)):
 
- * **[The new architecture](https://github.com/tgalal/yowsup/wiki/Yowsup-2.0-Architecture)**
- * **[Create a sample app](https://github.com/tgalal/yowsup/wiki/Yowsup-2.0-Sample-app)**
+ * **[The new architecture](https://github.com/tgalal/yowsup/wiki/Architecture)**
+ * **[Create a sample app](https://github.com/tgalal/yowsup/wiki/Sample-Application)**
  * **[yowsup-cli 2.0](https://github.com/tgalal/yowsup/wiki/yowsup-cli-2.0)**
- * **[The new Command line client](https://github.com/tgalal/yowsup/wiki/Yowsup-2.0-Command-line-client)**
  * **[Yowsup development, debugging, maintainance and sanity](https://github.com/tgalal/yowsup/wiki/Yowsup-development,-debugging,-maintainance-and-sanity)**
 
 
--- a/yowsup/yowsup-cli	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup-cli	Fri Sep 25 13:20:33 2015 +0200
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-__version__ = "2.0.9"
+__version__ = "2.0.13"
 __author__ = "Tarek Galal"
 
 import sys, argparse, yowsup, logging
@@ -205,8 +205,7 @@
         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 @@
     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 @@
         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 @@
             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 @@
 
         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 @@
             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 @@
         parser = modeDict[mode]()
         if not parser.process():
             parser.print_help()
-
--- a/yowsup/yowsup/__init__.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/__init__.py	Fri Sep 25 13:20:33 2015 +0200
@@ -1,2 +1,2 @@
-__version__ = "2.3.124"
+__version__ = "2.4"
 __author__ = "Tarek Galal"
--- a/yowsup/yowsup/common/constants.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/common/constants.py	Fri Sep 25 13:20:33 2015 +0200
@@ -1,10 +1,25 @@
 class YowConstants:
     DOMAIN       = "s.whatsapp.net"
     ENDPOINTS     = (
-        ("c2.whatsapp.net", 443),
+        ("e1.whatsapp.net", 443),
+        ("e2.whatsapp.net", 443),
+        ("e3.whatsapp.net", 443),
+        ("e4.whatsapp.net", 443),
+        ("e5.whatsapp.net", 443),
+        ("e6.whatsapp.net", 443),
+        ("e7.whatsapp.net", 443),
+        ("e8.whatsapp.net", 443),
+        ("e9.whatsapp.net", 443),
+        ("e10.whatsapp.net", 443),
+        ("e11.whatsapp.net", 443),
+        ("e12.whatsapp.net", 443),
+        ("e13.whatsapp.net", 443),
+        ("e14.whatsapp.net", 443),
+        ("e15.whatsapp.net", 443),
+        ("e16.whatsapp.net", 443),
         )
 
     PATH_STORAGE = "~/.yowsup"
 
     PREVIEW_WIDTH = 64
-    PREVIEW_HEIGHT = 64
\ No newline at end of file
+    PREVIEW_HEIGHT = 64
--- a/yowsup/yowsup/demos/cli/cli.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/demos/cli/cli.py	Fri Sep 25 13:20:33 2015 +0200
@@ -90,7 +90,9 @@
                 out = ""
                 out += ("/%s " % cmd).ljust(15)
                 out += ("%s " % subcmd if subcmd != "_" else "").ljust(15)
-                out += ("%s " % " ".join(["[%s]" % c for c in subcmdDetails["args"]])).ljust(30)
+                args = ("%s " % " ".join(["<%s>" % c for c in subcmdDetails["args"][0:len(subcmdDetails["args"])-subcmdDetails["optional"]]]))
+                args += ("%s " % " ".join(["[%s]" % c for c in subcmdDetails["args"][len(subcmdDetails["args"])-subcmdDetails["optional"]:]]))
+                out += args.ljust(30)
                 out += subcmdDetails["desc"].ljust(20)
                 addToOut(subcmdDetails["order"], out)
 
@@ -190,4 +192,4 @@
 
 if __name__ == "__main__":
     c = Cli()
-    c.print_usage()
\ No newline at end of file
+    c.print_usage()
--- a/yowsup/yowsup/demos/cli/layer.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/demos/cli/layer.py	Fri Sep 25 13:20:33 2015 +0200
@@ -48,6 +48,7 @@
         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 @@
 
         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()
@@ -366,16 +370,25 @@
     def message_delivered(self, message_id):
         pass
 
-    @clicmd("Send and image")
-    def image_send(self, number, path):
+    @clicmd("Send an image with optional caption")
+    def image_send(self, number, path, caption = None):
         if self.assertConnected():
             jid = self.aliasToJid(number)
             entity = RequestUploadIqProtocolEntity(RequestUploadIqProtocolEntity.MEDIA_TYPE_IMAGE, filePath=path)
+            successFn = lambda successEntity, originalEntity: self.onRequestUploadResult(jid, path, successEntity, originalEntity, caption)
+            errorFn = lambda errorEntity, originalEntity: self.onRequestUploadError(jid, path, errorEntity, originalEntity)
+
+            self._sendIq(entity, successFn, errorFn)
+
+    @clicmd("Send audio file")
+    def audio_send(self, number, path):
+        if self.assertConnected():
+            jid = self.aliasToJid(number)
+            entity = RequestUploadIqProtocolEntity(RequestUploadIqProtocolEntity.MEDIA_TYPE_AUDIO, filePath=path)
             successFn = lambda successEntity, originalEntity: self.onRequestUploadResult(jid, path, successEntity, originalEntity)
             errorFn = lambda errorEntity, originalEntity: self.onRequestUploadError(jid, path, errorEntity, originalEntity)
 
             self._sendIq(entity, successFn, errorFn)
-
     @clicmd("Send typing state")
     def state_typing(self, jid):
         if self.assertConnected():
@@ -397,23 +410,20 @@
     @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))
+        if self.connected:
+            return self.output("Already connected, disconnect first")
+        self.getLayerInterface(YowNetworkLayer).connect()
+        return True
 
     @clicmd("Login to WhatsApp", 0)
     def login(self, username, b64password):
-
-        if self.connected:
-            return self.output("Already connected, disconnect first")
-
-        self.getStack().setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS, (username, b64password))
-        connectEvent = YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT)
-        self.broadcastEvent(connectEvent)
-        return True #prompt will wait until notified
-
+        self.setCredentials(username, b64password)
+        return self.L()
 
     ######## receive #########
 
@@ -504,32 +514,40 @@
             )
 
 
-    def doSendImage(self, filePath, url, to, ip = None):
-        entity = ImageDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to)
+    def doSendImage(self, filePath, url, to, ip = None, caption = None):
+        entity = ImageDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to, caption = caption)
         self.toLower(entity)
+
+    def doSendAudio(self, filePath, url, to, ip = None, caption = None):
+        entity = AudioDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to)
+        self.toLower(entity)
+
     def __str__(self):
         return "CLI Interface Layer"
 
     ########### callbacks ############
 
-    def onRequestUploadResult(self, jid, filePath, resultRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity):
+    def onRequestUploadResult(self, jid, filePath, resultRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity, caption = None):
+
+        if requestUploadIqProtocolEntity.mediaType == RequestUploadIqProtocolEntity.MEDIA_TYPE_AUDIO:
+            doSendFn = self.doSendAudio
+        else:
+            doSendFn = self.doSendImage
+
         if resultRequestUploadIqProtocolEntity.isDuplicate():
-            self.doSendImage(filePath, resultRequestUploadIqProtocolEntity.getUrl(), jid,
-                             resultRequestUploadIqProtocolEntity.getIp())
+            doSendFn(filePath, resultRequestUploadIqProtocolEntity.getUrl(), jid,
+                             resultRequestUploadIqProtocolEntity.getIp(), caption)
         else:
-            # successFn = lambda filePath, jid, url: self.onUploadSuccess(filePath, jid, url, resultRequestUploadIqProtocolEntity.getIp())
+            successFn = lambda filePath, jid, url: doSendFn(filePath, url, jid, resultRequestUploadIqProtocolEntity.getIp(), caption)
             mediaUploader = MediaUploader(jid, self.getOwnJid(), filePath,
                                       resultRequestUploadIqProtocolEntity.getUrl(),
                                       resultRequestUploadIqProtocolEntity.getResumeOffset(),
-                                      self.onUploadSuccess, self.onUploadError, self.onUploadProgress, async=False)
+                                      successFn, self.onUploadError, self.onUploadProgress, async=False)
             mediaUploader.start()
 
     def onRequestUploadError(self, jid, path, errorRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity):
         logger.error("Request upload for file %s for %s failed" % (path, jid))
 
-    def onUploadSuccess(self, filePath, jid, url):
-        self.doSendImage(filePath, url, jid)
-
     def onUploadError(self, filePath, jid, url):
         logger.error("Upload file %s to %s for %s failed!" % (filePath, url, jid))
 
--- a/yowsup/yowsup/demos/cli/stack.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/demos/cli/stack.py	Fri Sep 25 13:20:33 2015 +0200
@@ -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 @@
             .push(YowsupCliLayer)\
             .build()
 
+        # self.stack.setCredentials(credentials)
         self.stack.setCredentials(credentials)
 
     def start(self):
@@ -27,4 +27,4 @@
             print("Auth Error, reason %s" % e)
         except KeyboardInterrupt:
             print("\nYowsdown")
-            sys.exit(0)
\ No newline at end of file
+            sys.exit(0)
--- a/yowsup/yowsup/demos/contacts/stack.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/demos/contacts/stack.py	Fri Sep 25 13:20:33 2015 +0200
@@ -46,10 +46,7 @@
         self.stack = YowStack(layers)
         self.stack.setProp(SyncLayer.PROP_CONTACTS, contacts)
         self.stack.setProp(YowAuthenticationProtocolLayer.PROP_PASSIVE, True)
-        self.stack.setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS, credentials)
-        self.stack.setProp(YowNetworkLayer.PROP_ENDPOINT, YowConstants.ENDPOINTS[0])
-        self.stack.setProp(YowCoderLayer.PROP_DOMAIN, YowConstants.DOMAIN)
-        self.stack.setProp(YowCoderLayer.PROP_RESOURCE, env.CURRENT_ENV.getResource())
+        self.stack.setCredentials(credentials)
 
     def start(self):
         self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
--- a/yowsup/yowsup/demos/echoclient/stack.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/demos/echoclient/stack.py	Fri Sep 25 13:20:33 2015 +0200
@@ -41,10 +41,7 @@
             )
 
         self.stack = YowStack(layers)
-        self.stack.setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS, credentials)
-        self.stack.setProp(YowNetworkLayer.PROP_ENDPOINT, YowConstants.ENDPOINTS[0])
-        self.stack.setProp(YowCoderLayer.PROP_DOMAIN, YowConstants.DOMAIN)
-        self.stack.setProp(YowCoderLayer.PROP_RESOURCE, env.CURRENT_ENV.getResource())
+        self.stack.setCredentials(credentials)
 
     def start(self):
         self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
--- a/yowsup/yowsup/demos/sendclient/stack.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/demos/sendclient/stack.py	Fri Sep 25 13:20:33 2015 +0200
@@ -46,10 +46,7 @@
         self.stack = YowStack(layers)
         self.stack.setProp(SendLayer.PROP_MESSAGES, messages)
         self.stack.setProp(YowAuthenticationProtocolLayer.PROP_PASSIVE, True)
-        self.stack.setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS, credentials)
-        self.stack.setProp(YowNetworkLayer.PROP_ENDPOINT, YowConstants.ENDPOINTS[0])
-        self.stack.setProp(YowCoderLayer.PROP_DOMAIN, YowConstants.DOMAIN)
-        self.stack.setProp(YowCoderLayer.PROP_RESOURCE, env.CURRENT_ENV.getResource())
+        self.stack.setCredentials(credentials)
 
     def start(self):
         self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
--- a/yowsup/yowsup/env/env_s40.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/env/env_s40.py	Fri Sep 25 13:20:33 2015 +0200
@@ -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 @@
             OS_VERSION = self.getOSVersion(),
             DEVICE_NAME = self.getDeviceName()
         )
-
--- a/yowsup/yowsup/layers/__init__.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/__init__.py	Fri Sep 25 13:20:33 2015 +0200
@@ -33,6 +33,10 @@
 
     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 @@
             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 __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):
--- a/yowsup/yowsup/layers/auth/layer_authentication.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/auth/layer_authentication.py	Fri Sep 25 13:20:33 2015 +0200
@@ -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 @@
             "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 @@
         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)
--- a/yowsup/yowsup/layers/auth/layer_crypt.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/auth/layer_crypt.py	Fri Sep 25 13:20:33 2015 +0200
@@ -13,7 +13,7 @@
         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")
--- a/yowsup/yowsup/layers/axolotl/layer.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/axolotl/layer.py	Fri Sep 25 13:20:33 2015 +0200
@@ -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
@@ -15,10 +16,14 @@
 from yowsup.structs import ProtocolTreeNode
 from .protocolentities import GetKeysIqProtocolEntity, ResultGetKeysIqProtocolEntity
 from axolotl.util.hexutil import HexUtil
-from yowsup.env import CURRENT_ENV
 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
 
@@ -40,25 +45,37 @@
 
         self.sessionCiphers = {}
         self.pendingMessages = {}
+        self.pendingIncomingMessages = {}
         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
@@ -68,8 +85,7 @@
     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:
@@ -82,7 +98,9 @@
                 #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:
@@ -101,13 +119,17 @@
             elif protocolTreeNode.tag == "notification" and protocolTreeNode["type"] == "encrypt":
                 self.onEncryptNotification(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)
     ######
 
     ##### handling received data #####
     def onEncryptNotification(self, protocolTreeNode):
         entity = EncryptNotification.fromProtocolTreeNode(protocolTreeNode)
-        ack = OutgoingAckProtocolEntity(protocolTreeNode["id"], "notification", protocolTreeNode["type"])
+        ack = OutgoingAckProtocolEntity(protocolTreeNode["id"], "notification", protocolTreeNode["type"], protocolTreeNode["from"])
         self.toLower(ack.toProtocolTreeNode())
         self.sendKeys(fresh=False, countPreKeys = self.__class__._COUNT_PREKEYS - entity.getCount())
 
@@ -130,6 +152,13 @@
 
             del self.pendingMessages[jid]
 
+    def processPendingIncomingMessages(self, jid):
+        if jid in self.pendingIncomingMessages:
+            for messageNode in self.pendingIncomingMessages[jid]:
+                self.onMessage(messageNode)
+
+            del self.pendingIncomingMessages[jid]
+
     #### handling message types
 
     def handlePlaintextNode(self, node):
@@ -143,17 +172,25 @@
                 self.pendingMessages[node["to"]] = []
             self.pendingMessages[node["to"]].append(node)
 
-            self._sendIq(entity, self.onGetKeysResult, self.onGetKeysError)
+            self._sendIq(entity, lambda a, b: self.onGetKeysResult(a, b, self.processPendingMessages), self.onGetKeysError)
         else:
 
             sessionCipher = self.getSessionCipher(recipient_id)
 
-
-
+            if node["to"] in self.v2Jids:
+                version = 2
+                padded = bytearray()
+                padded.append(ord("\n"))
+                padded.extend(self.encodeInt7bit(len(plaintext)))
+                padded.extend(plaintext)
+                padded.append(ord("\x01"))
+                plaintext = padded
+            else:
+                version = 1
             ciphertext = sessionCipher.encrypt(plaintext)
             encEntity = EncryptedMessageProtocolEntity(
                 EncryptedMessageProtocolEntity.TYPE_MSG if ciphertext.__class__ == WhisperMessage else EncryptedMessageProtocolEntity.TYPE_PKMSG ,
-                                                   1,
+                                                   version,
                                                    ciphertext.serialize(),
                                                    MessageProtocolEntity.MESSAGE_TYPE_TEXT,
                                                    _id= node["id"],
@@ -166,15 +203,54 @@
                                                    )
             self.toLower(encEntity.toProtocolTreeNode())
 
+    def encodeInt7bit(self, value):
+        v = value
+        out = bytearray()
+        while v >= 0x80:
+          out.append((v | 0x80) % 256)
+          v >>= 7
+        out.append(v % 256)
+
+        return out
+
     def handleEncMessage(self, node):
         try:
+            if node.getChild("enc")["v"] == "2" and node["from"] not in self.v2Jids:
+                self.v2Jids.append(node["from"])
+
             if node.getChild("enc")["type"] == "pkmsg":
                 self.handlePreKeyWhisperMessage(node)
             else:
                 self.handleWhisperMessage(node)
-        except InvalidMessageException:
-            logger.error("Invalid message from %s!! Your axololtl database data might be inconsistent with WhatsApp, or with what that contact has" % node["from"])
-            sys.exit(1)
+        except InvalidMessageException as e:
+            # logger.error("Invalid message from %s!! Your axololtl database data might be inconsistent with WhatsApp, or with what that contact has" % node["from"])
+            # sys.exit(1)
+            logger.error(e)
+            retry = RetryOutgoingReceiptProtocolEntity.fromMesageNode(node)
+            retry.setRegData(self.store.getLocalRegistrationId())
+            self.toLower(retry.toProtocolTreeNode())
+        except InvalidKeyIdException as e:
+            logger.error(e)
+            retry = RetryOutgoingReceiptProtocolEntity.fromMesageNode(node)
+            retry.setRegData(self.store.getLocalRegistrationId())
+            self.toLower(retry.toProtocolTreeNode())
+        except NoSessionException as e:
+            logger.error(e)
+            entity = GetKeysIqProtocolEntity([node["from"]])
+            if node["from"] not in self.pendingIncomingMessages:
+                self.pendingIncomingMessages[node["from"]] = []
+            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)
 
@@ -182,6 +258,10 @@
         sessionCipher = self.getSessionCipher(pkMessageProtocolEntity.getFrom(False))
         plaintext = sessionCipher.decryptPkmsg(preKeyWhisperMessage)
 
+        if pkMessageProtocolEntity.getVersion() == 2:
+            plaintext = self.unpadV2Plaintext(plaintext)
+
+
         bodyNode = ProtocolTreeNode("body", data = plaintext)
         node.addChild(bodyNode)
         self.toUpper(node)
@@ -193,9 +273,19 @@
         sessionCipher = self.getSessionCipher(encMessageProtocolEntity.getFrom(False))
         plaintext = sessionCipher.decryptMsg(whisperMessage)
 
+        if encMessageProtocolEntity.getVersion() == 2:
+            plaintext = self.unpadV2Plaintext(plaintext)
+
         bodyNode = ProtocolTreeNode("body", data = plaintext)
         node.addChild(bodyNode)
         self.toUpper(node)
+
+    def unpadV2Plaintext(self, v2plaintext):
+        if len(v2plaintext) < 128:
+            return v2plaintext[2:-1]
+        else: # < 128 * 128
+            return v2plaintext[3: -1]
+
     ####
 
     ### keys set and get
@@ -234,7 +324,9 @@
             if currPercentage == prevPercentage:
                 continue
             prevPercentage = currPercentage
-            logger.debug("%s" % currPercentage + "%")
+            #logger.debug("%s" % currPercentage + "%")
+            sys.stdout.write("Storing prekeys %d%% \r" % (currPercentage))
+            sys.stdout.flush()
 
         if fresh:
             self.state = self.__class__._STATE_GENKEYS
@@ -243,7 +335,7 @@
     def onSentKeysError(self, errorNode, keysEntity):
         raise Exception("Sent keys were not accepted")
 
-    def onGetKeysResult(self, resultNode, getKeysEntity):
+    def onGetKeysResult(self, resultNode, getKeysEntity, processPendingFn):
         entity = ResultGetKeysIqProtocolEntity.fromProtocolTreeNode(resultNode)
 
         resultJids = entity.getJids()
@@ -261,7 +353,7 @@
                                                self.store, recipient_id, 1)
             sessionBuilder.processPreKeyBundle(preKeyBundle)
 
-            self.processPendingMessages(jid)
+            processPendingFn(jid)
 
     def onGetKeysError(self, errorNode, getKeysEntity):
         pass
@@ -284,4 +376,4 @@
             return self.sessionCiphers[recipientId]
         else:
             self.sessionCiphers[recipientId] = SessionCipher(self.store, self.store, self.store, self.store, recipientId, 1)
-            return self.sessionCiphers[recipientId]
\ No newline at end of file
+            return self.sessionCiphers[recipientId]
--- a/yowsup/yowsup/layers/axolotl/protocolentities/message_encrypted.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/axolotl/protocolentities/message_encrypted.py	Fri Sep 25 13:20:33 2015 +0200
@@ -3,8 +3,8 @@
 import sys
 class EncryptedMessageProtocolEntity(MessageProtocolEntity):
     '''
-    <message retry="1" from="4917675341470@s.whatsapp.net" t="1418906418" offline="1" type="text" id="1418906377-1" notify="Tarek Galal">
-<enc av="Android/2.11.456" type="{{type}}" v="1">
+    <message retry="1" from="49xxxxxxxx@s.whatsapp.net" t="1418906418" offline="1" type="text" id="1418906377-1" notify="Tarek Galal">
+<enc type="{{type}}" v="{{1 || 2}}">
 HEX:33089eb3c90312210510e0196be72fe65913c6a84e75a54f40a3ee290574d6a23f408df990e718da761a210521f1a3f3d5cb87fde19fadf618d3001b64941715efd3e0f36bba48c23b08c82f2242330a21059b0ce2c4720ec79719ba862ee3cda6d6332746d05689af13aabf43ea1c8d747f100018002210d31cd6ebea79e441c4935f72398c772e2ee21447eb675cfa28b99de8d2013000</enc>
 </message>
     '''
@@ -32,6 +32,9 @@
     def getEncData(self):
         return self.encData
 
+    def getVersion(self):
+        return self.encVersion
+
     def toProtocolTreeNode(self):
         node = super(EncryptedMessageProtocolEntity, self).toProtocolTreeNode()
         encNode = ProtocolTreeNode("enc", data = self.encData)
--- a/yowsup/yowsup/layers/axolotl/store/sqlite/litesignedprekeystore.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/axolotl/store/sqlite/litesignedprekeystore.py	Fri Sep 25 13:20:33 2015 +0200
@@ -1,5 +1,6 @@
 from axolotl.state.signedprekeystore import SignedPreKeyStore
 from axolotl.state.signedprekeyrecord import SignedPreKeyRecord
+from axolotl.invalidkeyidexception import InvalidKeyIdException
 class LiteSignedPreKeyStore(SignedPreKeyStore):
     def __init__(self, dbConn):
         """
@@ -18,7 +19,7 @@
 
         result = cursor.fetchone()
         if not result:
-            raise Exception("No such signedprekeyrecord! %s " % signedPreKeyId)
+            raise InvalidKeyIdException("No such signedprekeyrecord! %s " % signedPreKeyId)
 
         return SignedPreKeyRecord(serialized=result[0])
 
--- a/yowsup/yowsup/layers/interface/interface.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/interface/interface.py	Fri Sep 25 13:20:33 2015 +0200
@@ -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 @@
 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 @@
         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 @@
         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 @@
             entityType = entity.getTag()
             if entityType in self.callbacks:
                 self.callbacks[entityType](entity)
-        
+            else:
+                self.toUpper(entity)
+
 
     def __str__(self):
         return "Interface Layer"
-
--- a/yowsup/yowsup/layers/network/layer.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/network/layer.py	Fri Sep 25 13:20:33 2015 +0200
@@ -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 @@
 
     def __init__(self):
         YowLayer.__init__(self)
+        self.interface = YowNetworkLayerInterface(self)
         asyncore.dispatcher.__init__(self)
         httpProxy = HttpProxy.getFromEnviron()
         proxyHandler = None
@@ -31,23 +33,34 @@
             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)
-            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)
@@ -55,10 +68,11 @@
             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
 
@@ -72,8 +86,9 @@
             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)
--- a/yowsup/yowsup/layers/protocol_ib/layer.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/protocol_ib/layer.py	Fri Sep 25 13:20:33 2015 +0200
@@ -21,6 +21,8 @@
             self.toUpper(DirtyIbProtocolEntity.fromProtocolTreeNode(node))
         elif node.getChild("offline"):
             self.toUpper(OfflineIbProtocolEntity.fromProtocolTreeNode(node))
+        elif node.getChild("account"):
+            self.toUpper(AccountIbProtocolEntity.fromProtocolTreeNode(node))
         else:
             raise ValueError("Unkown ib node %s" % node)
 
--- a/yowsup/yowsup/layers/protocol_ib/protocolentities/__init__.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/protocol_ib/protocolentities/__init__.py	Fri Sep 25 13:20:33 2015 +0200
@@ -1,3 +1,4 @@
 from .clean_iq import CleanIqProtocolEntity
 from .dirty_ib import DirtyIbProtocolEntity
-from .offline_ib import OfflineIbProtocolEntity
\ No newline at end of file
+from .offline_ib import OfflineIbProtocolEntity
+from .account_ib import AccountIbProtocolEntity
\ No newline at end of file
--- a/yowsup/yowsup/layers/protocol_media/protocolentities/message_media_downloadable_audio.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/protocol_media/protocolentities/message_media_downloadable_audio.py	Fri Sep 25 13:20:33 2015 +0200
@@ -23,7 +23,7 @@
     '''
     def __init__(self,
             mimeType, fileHash, url, ip, size, fileName, 
-            abitrate, acodec, asampfreq, duration, encoding, origin, seconds, 
+            abitrate, acodec, asampfreq, duration, encoding, origin, seconds,
             _id = None, _from = None, to = None, notify = None, timestamp = None, 
             participant = None, preview = None, offline = None, retry = None):
 
@@ -42,7 +42,8 @@
         out += "Sampling freq.: %s\n" % self.asampfreq
         return out
 
-    def setAudioProps(self, abitrate, acodec, asampfreq, duration, encoding, origin, seconds):
+    def setAudioProps(self, abitrate = None, acodec = None, asampfreq = None,
+                      duration = None, encoding = None, origin = None, seconds = None):
         self.abitrate  = abitrate
         self.acodec    = acodec
         self.asampfreq = asampfreq
@@ -55,13 +56,20 @@
         node = super(AudioDownloadableMediaMessageProtocolEntity, self).toProtocolTreeNode()
         mediaNode = node.getChild("media")
 
-        mediaNode.setAttribute("abitrate",  self.abitrate)
-        mediaNode.setAttribute("acodec",    self.acodec)
-        mediaNode.setAttribute("asampfreq", self.asampfreq)
-        mediaNode.setAttribute("duration",  self.duration)
-        mediaNode.setAttribute("encoding",  self.encoding)
-        mediaNode.setAttribute("origin",    self.origin)
-        mediaNode.setAttribute("seconds",   self.seconds)
+        if self.abitrate:
+            mediaNode.setAttribute("abitrate",  self.abitrate)
+        if self.acodec:
+            mediaNode.setAttribute("acodec",    self.acodec)
+        if self.asampfreq:
+            mediaNode.setAttribute("asampfreq", self.asampfreq)
+        if self.duration:
+            mediaNode.setAttribute("duration",  self.duration)
+        if self.encoding:
+            mediaNode.setAttribute("encoding",  self.encoding)
+        if self.origin:
+            mediaNode.setAttribute("origin",    self.origin)
+        if self.seconds:
+            mediaNode.setAttribute("seconds",   self.seconds)
 
         return node
 
@@ -80,3 +88,12 @@
             mediaNode.getAttributeValue("seconds"),
         )
         return entity
+
+
+
+    @staticmethod
+    def fromFilePath(fpath, url, ip, to, mimeType = None, preview = None, filehash = None, filesize = None):
+        entity = DownloadableMediaMessageProtocolEntity.fromFilePath(fpath, url, DownloadableMediaMessageProtocolEntity.MEDIA_TYPE_AUDIO, ip, to, mimeType, preview)
+        entity.__class__ = AudioDownloadableMediaMessageProtocolEntity
+        entity.setAudioProps()
+        return entity
--- a/yowsup/yowsup/layers/protocol_messages/protocolentities/message.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/protocol_messages/protocolentities/message.py	Fri Sep 25 13:20:33 2015 +0200
@@ -55,24 +55,21 @@
             "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 @@
         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
--- a/yowsup/yowsup/layers/protocol_receipts/protocolentities/receipt_outgoing.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/layers/protocol_receipts/protocolentities/receipt_outgoing.py	Fri Sep 25 13:20:33 2015 +0200
@@ -13,20 +13,39 @@
     read
     <receipt to="xxxxxxxxxxx@s.whatsapp.net" id="1415389947-15" type="read"></receipt>
 
-    INCOMING
-    <receipt offline="0" from="4915225256022@s.whatsapp.net" id="1415577964-1" t="1415578027"></receipt>
+    multiple items:
+    <receipt type="read" to="xxxxxxxxxxxx@s.whatsapp.net" id="1431364583-191">
+        <list>
+            <item id="1431364572-189"></item>
+            <item id="1431364575-190"></item>
+        </list>
+    </receipt>
     '''
 
-    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 @@
             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 @@
         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"]
             )
--- a/yowsup/yowsup/stacks/yowstack.py	Fri Sep 04 14:43:23 2015 +0200
+++ b/yowsup/yowsup/stacks/yowstack.py	Fri Sep 25 13:20:33 2015 +0200
@@ -1,5 +1,5 @@
 from yowsup.layers import YowParallelLayer
-import asyncore, time, logging
+import asyncore, time, logging, random
 from yowsup.layers import YowLayer
 from yowsup.layers.auth                        import YowCryptLayer, YowAuthenticationProtocolLayer
 from yowsup.layers.coder                       import YowCoderLayer
@@ -42,6 +42,10 @@
 
     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 @@
         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 @@
     __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[0])
+        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 getLayer(self, layerIndex):
         return self.__stackInstances[layerIndex]
-