Source: yowsup
Version: 0.0~git20140314.938cf1-1
Severity: grave
Tags: upstream patch jessie
Justification: renders package unusable

I am the maintainer of this package.

Recently (November 2014), the WhatsApp changed its protocol to implement the
famous "double blue check", photo transfer when contacting a person and other
features. To support it, the authentication method was changed and this new
method is mandatory now. So, the old versions of the WhatsApp and the current
version of the youwsup in testing (revision -1) don't work anymore. In other
words, the version in testing is RC, because no longer register a new user
number nor authenticate a registered number.

The upstream provided new versions and a "legacy" version[1]. So, based in
legacy version and with support from the upstream, I added patches to fix each
file (as needed) in current yowsup package in Debian.

[1] https://github.com/tgalal/yowsup/tree/legacy

The list of patches:
         - add-missing-variable
         - add-PictureClient
         - add-the-tokenmap
         - add-whatsapp-auth-v2
         - update-bintreenode
         - update-connection-manager
         - update-protocoltreenode
         - update-the-interface-messages
         - update-user-agent
         - yowsup-cli (updated only; already in Debian)

Regards,

Eriberto

-- System Information:
Debian Release: 8.0
  APT prefers testing-updates
  APT policy: (500, 'testing-updates'), (500, 'testing')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 3.16.0-4-amd64 (SMP w/4 CPU cores)
Locale: LANG=pt_BR.UTF-8, LC_CTYPE=pt_BR.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Description: add the new and necessary file src/Examples/PictureClient.py.
Author: Tarek Galal <tare2.ga...@gmail.com>
Reviewed-by: Joao Eriberto Mota Filho <eribe...@debian.org>
Origin: https://github.com/tgalal/yowsup/tree/legacy
Last-Update: 2014-12-02
Index: yowsup-0.0~git20140314.938cf1/src/Examples/PictureClient.py
===================================================================
--- /dev/null
+++ yowsup-0.0~git20140314.938cf1/src/Examples/PictureClient.py
@@ -0,0 +1,80 @@
+'''
+Copyright (c) <2012> Tarek Galal <tare2.ga...@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this 
+software and associated documentation files (the "Software"), to deal in the Software 
+without restriction, including without limitation the rights to use, copy, modify, 
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 
+permit persons to whom the Software is furnished to do so, subject to the following 
+conditions:
+
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR 
+A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+'''
+
+import os
+parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+os.sys.path.insert(0,parentdir)
+import time
+
+from Yowsup.connectionmanager import YowsupConnectionManager
+
+class WhatsappPictureClient:
+	
+	def __init__(self, target, waitForReceipt=True):
+		
+		self.jid = target
+		jid = self.jid
+		print(jid)
+
+		
+		connectionManager = YowsupConnectionManager()
+		self.signalsInterface = connectionManager.getSignalsInterface()
+		self.methodsInterface = connectionManager.getMethodsInterface()
+		
+		self.signalsInterface.registerListener("auth_success", self.onAuthSuccess)
+		self.signalsInterface.registerListener("auth_fail", self.onAuthFailed)
+		self.signalsInterface.registerListener("contact_gotProfilePicture", self.onProfilePicture)
+		self.signalsInterface.registerListener("disconnected", self.onDisconnected)
+
+
+	
+	def login(self, username, password):
+		self.username = username
+		self.methodsInterface.call("auth_login", (username, password))
+
+
+	def onAuthSuccess(self, username):
+		print("Authed %s" % username)
+
+
+
+		self.methodsInterface.call("ready")
+		
+
+		self.getPictureByID(self.jid) 
+		print("getting picture..please press strg+c in case you got the picture")
+		
+		while True:
+			input()
+
+	def onAuthFailed(self, username, err):
+		print("Auth Failed!")
+
+	def onDisconnected(self, reason):
+		print("Disconnected because %s" %reason)
+
+	def onProfilePicture(self, jid, path, rest):
+		print('jid: ' + str(jid) + ' path: ' + str(path) + ' rest: ' + str(rest))
+		
+	def getPictureByID(self, jid):
+		print('jid:'+jid)
+		self.methodsInterface.call("contact_getProfilePicture", ( [jid+'@s.whatsapp.net'] ))
+
Description: change the authentication method to version 2.
Author: Tarek Galal <tare2.ga...@gmail.com>
Reviewed-by: Joao Eriberto Mota Filho <eribe...@debian.org>
Origin: https://github.com/tgalal/yowsup/tree/legacy
Last-Update: 2014-12-02
Index: yowsup-0.0~git20140314.938cf1/src/Yowsup/Auth/auth.py
===================================================================
--- yowsup-0.0~git20140314.938cf1.orig/src/Yowsup/Auth/auth.py
+++ yowsup-0.0~git20140314.938cf1/src/Yowsup/Auth/auth.py
@@ -19,7 +19,7 @@ CONTRACT, TORT OR OTHERWISE, ARISING FRO
 OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 '''
 
-from .mechanisms.wauth import WAuth as AuthMechanism
+from .mechanisms.wauth2 import WAuth2 as AuthMechanism
 
 from Yowsup.Common.constants import Constants
 from Yowsup.Common.debugger import Debugger
Index: yowsup-0.0~git20140314.938cf1/src/Yowsup/Auth/mechanisms/wauth2.py
===================================================================
--- /dev/null
+++ yowsup-0.0~git20140314.938cf1/src/Yowsup/Auth/mechanisms/wauth2.py
@@ -0,0 +1,363 @@
+'''
+Copyright (c) <2012> Tarek Galal <tare2.ga...@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
+A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+'''
+import sys
+sys.path.append("/home/tarek/Projects/Yowsupgit/yowsup/src")
+import socket, hashlib, hmac, sys
+from Yowsup.Common.debugger import Debugger
+from Yowsup.Common.watime import WATime
+from Yowsup.ConnectionIO.protocoltreenode import ProtocolTreeNode
+
+from struct import pack
+from operator import xor
+from itertools import starmap
+from hashlib import sha1
+import uuid
+
+def _bytearray(data):
+
+  if type(data) == str:
+		  return data
+  elif type(data) == list:
+		  tmp = [chr(x) if type(x) == int else x for x in data]
+		  return "".join(tmp)
+  elif type(data) == int:
+		  tmp = ""
+    #for i in range(0,data):
+    #	tmp = tmp + chr(0)
+    #	return tmp
+		  return [0] * data
+
+  return ""
+
+class WAuth2():
+
+  def __init__(self,conn):
+    Debugger.attach(self);
+
+    self.conn = conn
+    self._d("Yowsup WAUTH-2 INIT");
+
+  def setAuthObject(self, authObject):
+    self.authObject = authObject
+
+  def login(self, username, password, domain, resource):
+
+    self.username = username
+
+    try:
+      self._d("Starting stream")
+      self.conn.writer.streamStart(domain,resource);
+
+      self._d("Sending Features")
+      self.sendFeatures();
+
+      self._d("Sending Auth");
+      self.sendAuth();
+
+      self._d("Read stream start");
+      self.conn.reader.streamStart();
+
+      self._d("Read features and challenge");
+      challengeData = self.readFeaturesAndChallenge();
+
+      self._d("Sending Response")
+      self.sendResponse(challengeData);
+
+      self._d("Read success")
+
+      if not self.readSuccess(): return 0
+
+      self.conn.jid = "%s@%s" % (username, domain)
+      return self.conn
+
+    except socket.error:
+      return self.connectionError.emit()
+
+
+  def sendFeatures(self):
+    toWrite = ProtocolTreeNode("stream:features",None)
+
+
+    self.conn.writer.write(toWrite);
+
+  def sendAuth(self):
+    # "user":self.connection.user,
+    blob = []
+    node = ProtocolTreeNode("auth", {"passive": "false", "mechanism": "WAUTH-2", "user": self.username})
+    #node = ProtocolTreeNode("auth",{"user":self.username,"xmlns":"urn:ietf:params:xml:ns:xmpp-sasl","mechanism":"WAUTH-1"}, None, ''.join(map(chr, blob)));
+    self.conn.writer.write(node);
+
+  def readFeaturesAndChallenge(self):
+    root = self.conn.reader.nextTree();
+
+    while root is not None:
+      if ProtocolTreeNode.tagEquals(root,"stream:features"):
+        self._d("GOT FEATURES !!!!");
+        self.authObject.supportsReceiptAcks  = root.getChild("receipt_acks") is not None;
+        root = self.conn.reader.nextTree();
+
+        continue;
+
+      if ProtocolTreeNode.tagEquals(root,"challenge"):
+        self._d("GOT CHALLENGE !!!!");
+        #data = base64.b64decode(root.data);
+        return root.data;
+    raise Exception("fell out of loop in readFeaturesAndChallenge");
+
+
+  def sendResponse(self,challengeData):
+
+    authBlob = self.getAuthBlob(challengeData);
+    node = ProtocolTreeNode("response",{"xmlns":"urn:ietf:params:xml:ns:xmpp-sasl"}, None, authBlob);
+    self.conn.writer.write(node);
+    self.conn.reader.inn.buf = [];
+
+  def getAuthBlob(self, nonce):
+    #numArray = _bytearray(KeyStream.keyFromPasswordAndNonce(self.authObject.password, nonce))
+    keys = KeyStream.generateKeys(self.authObject.password, nonce)
+
+    self.conn.reader.inputKey = self.inputKey = KeyStream(keys[2], keys[3])
+    self.outputKey = KeyStream(keys[0], keys[1])
+
+    nums = [0] * 4
+
+    nums.extend(self.username)
+    nums.extend(nonce)
+
+    wt = WATime()
+    utcNow = int(wt.utcTimestamp())
+    nums.extend(str(utcNow))
+
+    encoded = self.outputKey.encodeMessage(nums, 0, 4, len(nums) - 4)
+    encoded = "".join(map(chr, encoded))
+
+    return encoded
+
+  def readSuccess(self):
+    node = self.conn.reader.nextTree();
+    self._d("Login Status: %s"%(node.tag));
+
+    if ProtocolTreeNode.tagEquals(node,"failure"):
+      self.authObject.authenticationFailed()
+      return 0
+      #raise Exception("Login Failure");
+
+    ProtocolTreeNode.require(node,"success");
+
+    expiration = node.getAttributeValue("expiration");
+
+    if expiration is not None:
+      self._d("Expires: "+str(expiration));
+      self.authObject.expireDate = expiration;
+
+      kind = node.getAttributeValue("kind");
+      self._d("Account type: %s"%(kind))
+
+    if kind == "paid":
+      self.authObject.accountKind = 1;
+    elif kind == "free":
+      self.authObject.accountKind = 0;
+    else:
+      self.authObject.accountKind = -1;
+
+    status = node.getAttributeValue("status");
+    self._d("Account status: %s"%(status));
+
+    if status == "expired":
+      self.loginFailed.emit()
+      raise Exception("Account expired on "+str(self.authObject.expireDate));
+
+    if status == "active":
+      if expiration is None:
+        #raise Exception ("active account with no expiration");
+        '''@@TODO expiration changed to creation'''
+    else:
+      self.authObject.accountKind = 1;
+
+    self.conn.reader.inn.buf = [];
+
+    self.conn.writer.outputKey = self.outputKey
+    self.authObject.authenticationComplete()
+    return 1
+
+
+class RC4:
+  def __init__(self, key, drop):
+    self.s = []
+    self.i = 0;
+    self.j = 0;
+
+    self.s = [0] * 256
+
+    for i in range(0, len(self.s)):
+      self.s[i] = i
+
+    for i in range(0, len(self.s)):
+      self.j = (self.j + self.s[i] + ord(key[i % len(key)])) % 256
+      RC4.swap(self.s, i, self.j)
+
+    self.j = 0;
+
+    self.cipher(_bytearray(drop), 0, drop)
+
+
+  def cipher(self, data, offset, length):
+    while True:
+      num = length
+      length = num - 1
+
+      if num == 0: break
+
+      self.i = (self.i+1) % 256
+      self.j = (self.j + self.s[self.i]) % 256
+
+      RC4.swap(self.s, self.i, self.j)
+
+      num2 = offset
+      offset = num2 + 1
+
+      data[num2] = ord(data[num2]) if type(data[num2]) == str else data[num2]
+      data[num2] = (data[num2] ^ self.s[(self.s[self.i] + self.s[self.j]) % 256])
+
+  @staticmethod
+  def swap(arr, i, j):
+    tmp = arr[i]
+    arr[i] = arr[j]
+    arr[j] = tmp
+
+
+if sys.version_info >= (3, 0):
+  buffer = lambda x: bytes(x, 'iso-8859-1') if type(x) is str else bytes(x)
+  _bytearray = lambda x: [0]*x if type(x) is int else x
+
+
+class KeyStream:
+
+  def __init__(self, key, macKey):
+    self.key = key if sys.version_info < (3, 0) else bytes(key, 'iso-8859-1')
+    self.rc4 = RC4(self.key, 0x300)
+    self.macKey = macKey if sys.version_info < (3, 0) else bytes(macKey, 'iso-8859-1')
+    self.seq = 0
+
+  def computeMac(self, bytes_buffer, int_offset, int_length):
+    mac = hmac.new(self.macKey, None, sha1)
+    mac.update(buffer(_bytearray(bytes_buffer[int_offset:])))
+
+    numArray = "%s%s%s%s" % (chr(self.seq >> 24), chr(self.seq >> 16), chr(self.seq >> 8), chr(self.seq))
+
+    mac.update(buffer(_bytearray(numArray)))
+
+    self.seq += 1
+    return mac.digest()
+
+
+  def decodeMessage(self, bufdata, macOffset, offset, length):
+    buf = bufdata[:-4]
+    hashed = bufdata[-4:]
+    numArray = self.computeMac(buf, 0, len(buf))
+
+    numArray = [ord(x) for x in numArray.decode('iso-8859-1')];
+
+    num = 0
+    while num < 4:
+      if numArray[macOffset + num] == hashed[num]:
+        num += 1
+      else:
+        raise Exception("INVALID MAC")
+
+    self.rc4.cipher(buf, 0, len(buf))
+
+    return [x for x in buf]
+
+  def encodeMessage(self, buf, macOffset, offset, length):
+    self.rc4.cipher(buf, offset, length)
+
+    hashed = self.computeMac(buf, offset, length)
+
+    numArray = [ord(x) for x in hashed.decode('iso-8859-1')]
+
+    output = buf[0:macOffset] + numArray[0:4] + buf[macOffset+4:]
+
+    return [x for x in output]
+
+  @staticmethod
+  def generateKeys(password, nonce):
+    _bytes = [0] * 4
+    numArray = [1,2,3,4]
+
+    if sys.version >= (3, 0):
+      nonce = nonce.encode('iso-8859-1')
+
+    for j in range(0, len(numArray)):
+      noncex = nonce + chr(numArray[j])
+      _bytes[j] = KeyStream.pbkdf2(password, noncex, 2, 20)
+
+    return _bytes
+
+  @staticmethod
+  def pbkdf2( password, salt, itercount, keylen, hashfn = hashlib.sha1 ):
+
+    def pbkdf2_F( h, salt, itercount, blocknum ):
+
+      def prf( h, data ):
+        hm = h.copy()
+        hm.update( buffer(_bytearray(data)) )
+        #hm.update(bytes(data))
+        d = hm.digest()
+
+        #return map(ord, d)
+        #print (hm.digest())
+
+        #if sys.version_info < (3, 0):
+        return [ord(i) for i in d.decode('iso-8859-1')]
+
+
+      U = prf( h, salt + pack('>i',blocknum ) )
+      T = U
+
+      for i in range(2, itercount+1):
+        U = prf( h, U )
+        T = starmap(xor, zip(T, U))
+
+      return T
+
+    digest_size = hashfn().digest_size
+    l = int(keylen / digest_size)
+    if keylen % digest_size != 0:
+      l += 1
+
+    h = hmac.new( password, None, hashfn )
+
+    T = []
+    for i in range(1, l+1):
+      tmp = pbkdf2_F( h, salt, itercount, i )
+      #tmp = map(chr, tmp)
+      #print(tmp)
+      #for item in tmp:
+      # print(item)
+      #sys.exit(1)
+      T.extend(tmp)
+
+    #print(T)
+    #sys.exit()
+    T = [chr(i) for i in T]
+    return "".join(T[0: keylen])
+
Description: update the connection manager to support the new WhatsApp version.
Author: Tarek Galal <tare2.ga...@gmail.com>
Reviewed-by: Joao Eriberto Mota Filho <eribe...@debian.org>
Origin: https://github.com/tgalal/yowsup/tree/legacy
Last-Update: 2014-12-02
Index: yowsup-0.0~git20140314.938cf1/src/Yowsup/connectionmanager.py
===================================================================
--- yowsup-0.0~git20140314.938cf1.orig/src/Yowsup/connectionmanager.py
+++ yowsup-0.0~git20140314.938cf1/src/Yowsup/connectionmanager.py
@@ -27,7 +27,7 @@ from Yowsup.Common.utilities import Util
 from Yowsup.Common.debugger import Debugger
 import threading, select, time
 from Yowsup.Common.watime import WATime
-from .Auth.auth import YowsupAuth
+from Yowsup.Auth.auth import YowsupAuth
 from Yowsup.Common.constants import Constants
 from Yowsup.Interfaces.Lib.LibInterface import LibMethodInterface, LibSignalInterface
 import tempfile
@@ -38,13 +38,12 @@ import base64
 import sys
 
 
-
 import traceback
 class YowsupConnectionManager:
 	
 	def __init__(self):
 		Debugger.attach(self)
-		self.currKeyId = 1
+		self.currKeyId = 0
 		self.iqId = 0
 		self.verbose = True
 		self.state = 0
@@ -124,20 +123,16 @@ class YowsupConnectionManager:
 
 		self.methodInterface.registerCallback("visible_ack",self.sendVisibleReceiptAck)
 
-		self.methodInterface.registerCallback("ping",self.sendPing)
-		self.methodInterface.registerCallback("pong",self.sendPong)
-
 		self.methodInterface.registerCallback("typing_send",self.sendTyping)
 		self.methodInterface.registerCallback("typing_paused",self.sendPaused)
 
-		self.methodInterface.registerCallback("subject_ack",self.sendSubjectReceived)
-
 		self.methodInterface.registerCallback("group_getGroups", self.sendGetGroups)
 		self.methodInterface.registerCallback("group_getInfo",self.sendGetGroupInfo)
 		self.methodInterface.registerCallback("group_create",self.sendCreateGroupChat)
 		self.methodInterface.registerCallback("group_addParticipants",self.sendAddParticipants)
 		self.methodInterface.registerCallback("group_removeParticipants",self.sendRemoveParticipants)
-		self.methodInterface.registerCallback("group_end",self.sendEndGroupChat)
+		self.methodInterface.registerCallback("group_leave",self.sendLeaveGroupChat)
+		self.methodInterface.registerCallback("group_delete",self.sendDeleteGroupChat)
 		self.methodInterface.registerCallback("group_setSubject",self.sendSetGroupSubject)
 		self.methodInterface.registerCallback("group_setPicture", self.sendSetPicture)
 		self.methodInterface.registerCallback("group_getPicture", self.sendGetPicture)
@@ -152,7 +147,7 @@ class YowsupConnectionManager:
 		self.methodInterface.registerCallback("status_update",self.sendChangeStatus)
 
 		self.methodInterface.registerCallback("presence_request",self.getLastOnline)
-		#self.methodInterface.registerCallback("presence_unsubscribe",self.sendUnsubscribe)#@@TODO implement method
+		self.methodInterface.registerCallback("presence_unsubscribe",self.sendUnsubscribe)
 		self.methodInterface.registerCallback("presence_subscribe",self.sendSubscribe)
 		self.methodInterface.registerCallback("presence_sendAvailableForChat",self.sendAvailableForChat)
 		self.methodInterface.registerCallback("presence_sendAvailable",self.sendAvailable)
@@ -164,13 +159,27 @@ class YowsupConnectionManager:
 		
 		self.methodInterface.registerCallback("profile_setStatus", self.sendChangeStatus)
 
+		self.methodInterface.registerCallback("sync_sendSync", self.sendSync)
+		
 		self.methodInterface.registerCallback("disconnect", self.disconnect)
 		self.methodInterface.registerCallback("ready", self.startReader)
 		
-		self.methodInterface.registerCallback("auth_login", self.auth )
-		#self.methodInterface.registerCallback("auth_login", self.auth)
+		self.methodInterface.registerCallback("auth_login", self.auth)
 		
 		self.methodInterface.registerCallback("media_requestUpload", self.sendRequestUpload)
+		
+		self.methodInterface.registerCallback("sync_sendContacts", self.sendSyncContacts)
+		self.methodInterface.registerCallback("sync_getStatuses", self.sendGetStatuses)
+
+		self.methodInterface.registerCallback("privacy_setList", self.sendSetPrivacyList)
+		self.methodInterface.registerCallback("privacy_getList", self.sendGetPrivacyList)
+
+		self.methodInterface.registerCallback("privacy_setSettings", self.sendSetPrivacySettings)
+		self.methodInterface.registerCallback("privacy_getSettings", self.sendGetPrivacySettings)
+
+		self.methodInterface.registerCallback("account_delete", self.sendAccountDelete)
+
+		self.methodInterface.registerCallback("subscription_generateLink", self.generateSubscriptionLink)
 
 
 	def disconnect(self, reason=""):
@@ -284,8 +293,10 @@ class YowsupConnectionManager:
 			
 			self.readerThread.setSocket(self.socket)
 			self.readerThread.disconnectedCallback = self.onDisconnected
+			self.readerThread.sendReceiptAck = self.sendReceiptAck
 			self.readerThread.onPing = self.sendPong
 			self.readerThread.ping = self.sendPing
+			self.readerThread.sendNotificationReceived = self.sendNotificationReceived
 			
 	
 			self.signalInterface.send("auth_success", (username,))
@@ -296,33 +307,19 @@ class YowsupConnectionManager:
 		
 	def sendTyping(self,jid):
 		self._d("SEND TYPING TO JID")
-		composing = ProtocolTreeNode("composing",{"xmlns":"http://jabber.org/protocol/chatstates"})
-		message = ProtocolTreeNode("message",{"to":jid,"type":"chat"},[composing]);
+		composing = ProtocolTreeNode("composing")
+		message = ProtocolTreeNode("chatstate",{"to":jid},[composing]);
 		self._writeNode(message);
 
 
 
 	def sendPaused(self,jid):
 		self._d("SEND PAUSED TO JID")
-		composing = ProtocolTreeNode("paused",{"xmlns":"http://jabber.org/protocol/chatstates"})
-		message = ProtocolTreeNode("message",{"to":jid,"type":"chat"},[composing]);
+		composing = ProtocolTreeNode("paused")
+		message = ProtocolTreeNode("chatstate",{"to":jid},[composing]);
 		self._writeNode(message);
 
 
-
-	def getSubjectMessage(self,to,msg_id,child):
-		messageNode = ProtocolTreeNode("message",{"to":to,"type":"subject","id":msg_id},[child]);
-
-		return messageNode
-
-	def sendSubjectReceived(self,to,msg_id):
-		self._d("Sending subject recv receipt")
-		receivedNode = ProtocolTreeNode("received",{"xmlns": "urn:xmpp:receipts"});
-		messageNode = self.getSubjectMessage(to,msg_id,receivedNode);
-		self._writeNode(messageNode);
-
-
-
 	def sendMessageReceipt(self, jid, msgId):
 		self.sendReceipt(jid, "chat", msgId)
 
@@ -331,9 +328,11 @@ class YowsupConnectionManager:
 
 	def sendReceipt(self,jid,mtype,mid):
 		self._d("sending message received to "+jid+" - type:"+mtype+" - id:"+mid)
-		receivedNode = ProtocolTreeNode("received",{"xmlns": "urn:xmpp:receipts"})
-		messageNode = ProtocolTreeNode("message",{"to":jid,"type":mtype,"id":mid},[receivedNode]);
-		self._writeNode(messageNode);
+		attr = {"to": jid, "id": mid}
+		if mtype == "read":
+		  attr["type"] = "read"
+		receiptNode = ProtocolTreeNode("receipt", attr)
+		self._writeNode(receiptNode)
 
 
 	def sendDeliveredReceiptAck(self,to,msg_id):
@@ -347,6 +346,29 @@ class YowsupConnectionManager:
 		messageNode = ProtocolTreeNode("message",{"to":to,"type":"chat","id":msg_id},[ackNode]);
 		return messageNode;
 
+	def sendReceiptAck(self, msg_id, receiptType):
+		ackNode = ProtocolTreeNode("ack",{"class": "receipt", "type": "delivery" if receiptType is None else receiptType, "id": msg_id})
+		self._writeNode(ackNode);
+
+	def sendMessageReceived(self, jid, msg_id):
+		receiptNode = ProtocolTreeNode("receipt",{"to": jid, "id": msg_id})
+		self._writeNode(receiptNode)
+
+	def sendNotificationReceived(self, to, msg_id, from_jid, participant, notificationType, childNode):
+		attrs = {"to": to, "class": "notification", "id": msg_id, "type": notificationType}
+		if participant is not None:
+			attrs["participant"] = participant
+		if from_jid is not None:
+			attrs["from"] = from_jid
+		ackNode = ProtocolTreeNode("ack", attrs, [childNode] if childNode is not None else None)
+		self._writeNode(ackNode)
+
+	def sendCleanDirty(self, dirtyType):
+		idx = self.makeId("clean_dirty_")
+		cleanNode = ProtocolTreeNode("clean", {"type": dirtyType})
+		iqNode = ProtocolTreeNode("iq", {"id": idx, "type": "set", "to": self.domain, "xmlns": "urn:xmpp:whatsapp:dirty"}, [cleanNode])
+		self._writeNode(iqNode);
+
 	def makeId(self,prefix):
 		self.iqId += 1
 		idx = ""
@@ -363,8 +385,8 @@ class YowsupConnectionManager:
 
 		self.readerThread.requests[idx] = self.readerThread.parsePingResponse;
 
-		pingNode = ProtocolTreeNode("ping",{"xmlns":"w:p"});
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":self.domain},[pingNode]);
+		pingNode = ProtocolTreeNode("ping");
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":self.domain,"xmlns":"w:p"},[pingNode]);
 		self._writeNode(iqNode);
 		return idx
 
@@ -384,8 +406,8 @@ class YowsupConnectionManager:
 		idx = self.makeId("last_")
 		self.readerThread.requests[idx] = self.readerThread.parseLastOnline;
 
-		query = ProtocolTreeNode("query",{"xmlns":"jabber:iq:last"});
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":jid},[query]);
+		query = ProtocolTreeNode("query");
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":jid,"xmlns":"jabber:iq:last"},[query]);
 		self._writeNode(iqNode)
 
 
@@ -416,6 +438,12 @@ class YowsupConnectionManager:
 		self._writeNode(presenceNode);
 
 
+	def sendUnsubscribe(self,to):
+		presenceNode = ProtocolTreeNode("presence",{"type":"unsubscribe","to":to});
+
+		self._writeNode(presenceNode);
+
+
 	def mediaNode(fn):
 		def wrapped(self, *args):
 				mediaType = fn(self, *args)
@@ -425,7 +453,7 @@ class YowsupConnectionManager:
 				name = args[2]
 				size = args[3]
 				
-				mmNode = ProtocolTreeNode("media", {"xmlns":"urn:xmpp:whatsapp:mms","type":mediaType,"file":name,"size":size,"url":url},None, args[4:][0] if args[4:] else None);
+				mmNode = ProtocolTreeNode("media", {"type":mediaType,"file":name,"size":size,"url":url},None, args[4:][0] if args[4:] else None);
 				return mmNode
 			
 		return wrapped
@@ -444,10 +472,12 @@ class YowsupConnectionManager:
 		
 	def sendChangeStatus(self,status):
 		self._d("updating status to: %s"%(status))
+
+		idx = self.makeId("send_status_")
+		statusNode = ProtocolTreeNode("status", None, None, status)
+		iqNode = ProtocolTreeNode("iq", {"to": self.domain, "type": "set", "id": idx, "xmlns": "status"}, [statusNode]);
 		
-		bodyNode = ProtocolTreeNode("body",None,None,status);
-		messageNode = self.getMessageNode("s.us",bodyNode)
-		self._writeNode(messageNode);
+		self._writeNode(iqNode);
 		
 		return messageNode.getAttributeValue("id")
 		
@@ -476,13 +506,13 @@ class YowsupConnectionManager:
 	def sendLocation(self, jid, latitude, longitude, preview):
 		self._d("sending location (" + latitude + ":" + longitude + ")")
 
-		return ProtocolTreeNode("media", {"xmlns":"urn:xmpp:whatsapp:mms","type":"location","latitude":latitude,"longitude":longitude},None,preview)
+		return ProtocolTreeNode("media", {"type":"location","latitude":latitude,"longitude":longitude},None,preview)
 		
 	@sendMessage
 	def sendVCard(self, jid, data, name):
 		
 		cardNode = ProtocolTreeNode("vcard",{"name":name},None,data);
-		return ProtocolTreeNode("media", {"xmlns":"urn:xmpp:whatsapp:mms","type":"vcard"},[cardNode])
+		return ProtocolTreeNode("media", {"type":"vcard"},[cardNode])
 	
 	@sendMessage
 	def sendBroadcast(self, jids, content):
@@ -493,10 +523,11 @@ class YowsupConnectionManager:
 		
 		return [broadcastNode, messageNode]
 
-	def sendClientConfig(self,sound,pushID,preview,platform):
+	def sendClientConfig(self):
 		idx = self.makeId("config_");
-		configNode = ProtocolTreeNode("config",{"xmlns":"urn:xmpp:whatsapp:push","sound":sound,"id":pushID,"preview":"1" if preview else "0","platform":platform})
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":self.domain},[configNode]);
+		configNode = ProtocolTreeNode("config",{"platform":"none"})
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":self.domain,"xmlns":"urn:xmpp:whatsapp:push"},[configNode]);
+		self.readerThread.requests[idx] = self.readerThread.parseResultNode;
 
 		self._writeNode(iqNode);
 
@@ -507,8 +538,8 @@ class YowsupConnectionManager:
 		idx = self.makeId("get_groups_")
 		self.readerThread.requests[idx] = self.readerThread.parseGroups;
 
-		queryNode = ProtocolTreeNode("list",{"xmlns":"w:g","type":gtype})
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":"g.us"},[queryNode])
+		queryNode = ProtocolTreeNode("list",{"type":gtype})
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":"g.us","xmlns":"w:g"},[queryNode])
 
 		self._writeNode(iqNode)
 
@@ -518,8 +549,8 @@ class YowsupConnectionManager:
 		idx = self.makeId("get_g_info_")
 		self.readerThread.requests[idx] = self.readerThread.parseGroupInfo;
 
-		queryNode = ProtocolTreeNode("query",{"xmlns":"w:g"})
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":jid},[queryNode])
+		queryNode = ProtocolTreeNode("query")
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":jid,"xmlns":"w:g"},[queryNode])
 
 		self._writeNode(iqNode)
 
@@ -529,8 +560,19 @@ class YowsupConnectionManager:
 		idx = self.makeId("create_group_")
 		self.readerThread.requests[idx] = self.readerThread.parseGroupCreated;
 
-		queryNode = ProtocolTreeNode("group",{"xmlns":"w:g","action":"create","subject":subject})
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":"g.us"},[queryNode])
+		queryNode = ProtocolTreeNode("group",{"action":"create","subject":subject})
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":"g.us","xmlns":"w:g"},[queryNode])
+
+		self._writeNode(iqNode)
+
+
+	def sendDeleteGroupChat(self,gjid):
+		self._d("creating group: %s"%(subject))
+		idx = self.makeId("create_group_")
+		self.readerThread.requests[idx] = self.readerThread.parseGroupCreated;
+
+		queryNode = ProtocolTreeNode("group",{"action":"delete"})
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":gjid,"xmlns":"w:g"},[queryNode])
 
 		self._writeNode(iqNode)
 
@@ -546,8 +588,8 @@ class YowsupConnectionManager:
 		for part in participants:
 			innerNodeChildren.append( ProtocolTreeNode("participant",{"jid":part}) )
 
-		queryNode = ProtocolTreeNode("add",{"xmlns":"w:g"},innerNodeChildren)
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":gjid},[queryNode])
+		queryNode = ProtocolTreeNode("add",None,innerNodeChildren)
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":gjid,"xmlns":"w:g"},[queryNode])
 
 		self._writeNode(iqNode)
 
@@ -562,13 +604,13 @@ class YowsupConnectionManager:
 		for part in participants:
 			innerNodeChildren.append( ProtocolTreeNode("participant",{"jid":part}) )
 
-		queryNode = ProtocolTreeNode("remove",{"xmlns":"w:g"},innerNodeChildren)
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":gjid},[queryNode])
+		queryNode = ProtocolTreeNode("remove",None,innerNodeChildren)
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":gjid,"xmlns":"w:g"},[queryNode])
 
 		self._writeNode(iqNode)
 
 
-	def sendEndGroupChat(self,gjid):
+	def sendLeaveGroupChat(self,gjid):
 		self._d("removing group: %s"%(gjid))
 		idx = self.makeId("leave_group_")
 		self.readerThread.requests[idx] = self.readerThread.parseGroupEnded;
@@ -576,8 +618,8 @@ class YowsupConnectionManager:
 		innerNodeChildren = []
 		innerNodeChildren.append( ProtocolTreeNode("group",{"id":gjid}) )
 
-		queryNode = ProtocolTreeNode("leave",{"xmlns":"w:g"},innerNodeChildren)
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":"g.us"},[queryNode])
+		queryNode = ProtocolTreeNode("leave",None,innerNodeChildren)
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":"g.us","xmlns":"w:g"},[queryNode])
 
 		self._writeNode(iqNode)
 
@@ -587,8 +629,8 @@ class YowsupConnectionManager:
 		idx = self.makeId("set_group_subject_")
 		self.readerThread.requests[idx] = self.readerThread.parseGroupSubject
 
-		queryNode = ProtocolTreeNode("subject",{"xmlns":"w:g","value":subject})
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":gjid},[queryNode]);
+		queryNode = ProtocolTreeNode("subject",{"value":subject})
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"set","to":gjid,"xmlns":"w:g"},[queryNode]);
 
 		self._writeNode(iqNode)
 
@@ -597,8 +639,8 @@ class YowsupConnectionManager:
 		idx = self.makeId("get_participants_")
 		self.readerThread.requests[idx] = self.readerThread.parseParticipants
 
-		listNode = ProtocolTreeNode("list",{"xmlns":"w:g"})
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":jid},[listNode]);
+		listNode = ProtocolTreeNode("list")
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":jid,"xmlns":"w:g"},[listNode]);
 
 		self._writeNode(iqNode)
 
@@ -610,8 +652,8 @@ class YowsupConnectionManager:
 		#@@TODO, ?!
 		self.readerThread.requests[idx] =  self.readerThread.parseGetPicture
 
-		listNode = ProtocolTreeNode("picture",{"xmlns":"w:profile:picture","type":"image"})
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"to":jid,"type":"get"},[listNode]);
+		listNode = ProtocolTreeNode("picture",{"type":"image"})
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"to":jid,"type":"get","xmlns":"w:profile:picture"},[listNode]);
 
 		self._writeNode(iqNode)
 
@@ -625,8 +667,8 @@ class YowsupConnectionManager:
 		for jid in jids:
 			innerNodeChildren.append( ProtocolTreeNode("user",{"jid": jid}) )
 
-		queryNode = ProtocolTreeNode("list",{"xmlns":"w:profile:picture"},innerNodeChildren)
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get"},[queryNode])
+		queryNode = ProtocolTreeNode("list",None,innerNodeChildren)
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"type":"get","to":self.jid,"xmlns":"w:profile:picture"},[queryNode])
 
 		self._writeNode(iqNode)
 
@@ -647,9 +689,9 @@ class YowsupConnectionManager:
 		idx = self.makeId("set_picture_")
 		self.readerThread.requests[idx] = self.readerThread.parseSetPicture
 
-		listNode = ProtocolTreeNode("picture",{"xmlns":"w:profile:picture","type":"image"}, None, imageData)
+		listNode = ProtocolTreeNode("picture",{"type":"image"}, None, imageData)
 
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"to":jid,"type":"set"},[listNode])
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"to":jid,"type":"set","xmlns":"w:profile:picture"},[listNode])
 
 		self._writeNode(iqNode)
 
@@ -662,42 +704,149 @@ class YowsupConnectionManager:
 		if type(size) is not str:
 			size = str(size)
 
-		attribs = {"xmlns":"w:m","hash":b64Hash, "type":t, "size":size}
+		attribs = {"hash":b64Hash, "type":t, "size":size}
 
 		if b64OrigHash:
 			attribs["orighash"] = b64OrigHash
 
 		mediaNode = ProtocolTreeNode("media", attribs)
-		iqNode = ProtocolTreeNode("iq",{"id":idx,"to":"s.whatsapp.net","type":"set"},[mediaNode])
+		iqNode = ProtocolTreeNode("iq",{"id":idx,"to":"s.whatsapp.net","type":"set","xmlns":"w:m"},[mediaNode])
 		
 		
 		self._writeNode(iqNode)
 
+
+	def sendSync(self, contacts):
+		idx = self.makeId("sendsync_")
+		self.readerThread.requests[idx] = self.readerThread.parseSync
+		
+		users = []
+		
+		for c in contacts:
+			users.append(ProtocolTreeNode("user",None,None,'+' + c.replace('+', '')))
+
+		node = ProtocolTreeNode(
+			"iq", 
+			{
+				"type" : "get",
+				"id" : idx,
+				"xmlns" : "urn:xmpp:whatsapp:sync"
+			}, 
+			[
+				(
+				ProtocolTreeNode(
+				"sync", 
+				{
+					"mode" : 'full',
+					"context" : 'registration',
+					"sid" : str((time.time() + 11644477200) * 10000000),
+					"index" : '0',
+					"last" : "true",
+				}, 
+				users, 
+				None)
+			  ),
+		]
+		, None)
+		self._writeNode(node)
+
+
 	def getMessageNode(self, jid, child):
-			requestNode = None;
 			serverNode = ProtocolTreeNode("server",None);
 			xNode = ProtocolTreeNode("x",{"xmlns":"jabber:x:event"},[serverNode]);
-			childCount = (0 if requestNode is None else 1) +2;
-			messageChildren = []#[None]*childCount;
-			if requestNode is not None:
-				messageChildren.append(requestNode);
-			#System.currentTimeMillis() / 1000L + "-"+1
-			messageChildren.append(xNode)
+			messageChildren = []
 			
 			if type(child) == list:
-				messageChildren.extend(child)
+				messageChildren = child
 			else:
 				messageChildren.append(child)
+			messageChildren.append(xNode)
 				
 			msgId = str(int(time.time()))+"-"+ str(self.currKeyId)
 			
-			messageNode = ProtocolTreeNode("message",{"to":jid,"type":"chat","id":msgId},messageChildren)
+			messageNode = ProtocolTreeNode("message",{"to":jid,"type":"text","id":msgId},messageChildren)
 			
 			self.currKeyId += 1
 
 
 			return messageNode;
 
+	def sendSyncContacts(self, numbers):
+		print("sendSyncContacts")
+		syncNodes = []
+		for number in numbers:
+			print(number)
+			if number.find("@") >= 0:
+				number = "+" + number.split("@")[0]
+			userNode = ProtocolTreeNode("user", None, None, number)
+			syncNodes.append(userNode)
+		if len(syncNodes) > 0:
+			idx = self.makeId("sync_")
+			self.readerThread.requests[idx] = self.readerThread.parseSyncContacts
+			syncNode = ProtocolTreeNode("sync", {"context": "background", "index": 0, "mode": "delta", "last": "true", "sid": str(int(time.time()))}, syncNodes)
+			iqNode = ProtocolTreeNode("iq", {"id": idx, "type": "get", "to": self.jid, "xmlns": "urn:xmpp:whatsapp:sync"}, syncNode)
+			self._writeNode(iqNode)
+
+	def sendGetStatuses(self, jids):
+		syncNodes = []
+		for cjid in cjids:
+			userNode = ProtocolTreeNode("user", {"jid": cjid})
+			syncNodes.append(userNode)
+		if len(syncNodes) > 0:
+			idx = self.makeId("sync_statuses_")
+			self.readerThread.requests[idx] = self.readerThread.parseSyncStatuses
+			statusNode = ProtocolTreeNode("status", None, syncNodes)
+			iqNode = ProtocolTreeNode("iq", {"id": idx, "type": "get", "to": self.domain, "xmlns": "status"}, statusNode)
+			self._writeNode(iqNode)
+
+	def sendSetPrivacyList(self, cjids):
+		privacyNodes = []
+		order = 0
+		for cjid in cjids:
+			itemNode = ProtocolTreeNode("item", {"type": "jid", "value": cjid, "action": "deny", "order": order})
+			order += 1
+		if len(privacyNodes) > 0:
+			idx = self.makeId("privacy_setlist_")
+			listNode = ProtocolTreeNode("list", {"name": "default"}, privacyNodes)
+			queryNode = ProtocolTreeNode("query", None, [listNode])
+			iqNode = ProtocolTreeNode("iq", {"id": idx, "type": "set", "xmlns": "jabber:iq:privacy"}, [queryNode])
+			self._writeNode(iqNode)
+
+	def sendGetPrivacyList(self):
+		idx = self.makeId("privacy_getlist_")
+		self.readerThread.requests[idx] = self.readerThread.parsePrivacyList
+		listNode = ProtocolTreeNode("list", {"name": "default"})
+		queryNode = ProtocolTreeNode("query", {}, [listNode])
+		iqNode = ProtocolTreeNode("iq", {"id": idx, "type": "get", "xmlns": "jabber:iq:privacy"}, [queryNode])
+		self._writeNode(iqNode)
+
+	def sendSetPrivacySettings(self, key, value):
+		idx = self.makeId("privacy_setvalue_")
+		categoryNode = ProtocolTreeNode("category", {"name": key, "value": value})
+		privacyNode = ProtocolTreeNode("privacy", None, [categoryNode])
+		iqNode = ProtocolTreeNode("iq", {"id": idx, "to": self.domain, "type": "set", "xmlns": "privacy"}, [privacyNode])
+		self._writeNode(iqNode)
+
+	def sendGetPrivacySettings(self):
+		idx = self.makeId("privacy_getvalues_")
+		self.readerThread.requests[idx] = self.readerThread.parsePrivacySettings
+		privacyNode = ProtocolTreeNode("privacy")
+		iqNode = ProtocolTreeNode("iq", {"id": idx, "type": "get", "to": self.domain, "xmlns": "privacy"}, [privacyNode])
+		self._writeNode(iqNode)
+
+	def sendAccountDelete(self):
+		idx = self.makeId("account_delete_")
+		self.readerThread.requests[idx] = self.readerThread.parseAccountDelete
+		removeNode = ProtocolTreeNode("remove")
+		iqNode = ProtocolTreeNode("iq", {"id": idx, "type": "get", "to": self.domain, "xmlns": "urn:xmpp:whatsapp:account"}, [removeNode])
+		self._writeNode(iqNode)
+
+	def generateSubscriptionLink(self, mode, years):
+		phone = self.jid.split("@")[0]
+		chksum = hashlib.md5(phone + "abc").hexdigest()
+		link = "https://www.whatsapp.com/payments/"; + mode + ".php?phone=" + phone + "&cksum=" + chksum + "&sku=" + years
+		self.signalInterface.send("subscription_link", (link, ))
+
 
 class ReaderThread(threading.Thread):
 	def __init__(self):
@@ -715,7 +864,6 @@ class ReaderThread(threading.Thread):
 		self.autoPong = True
 		self.onPing = self.ping = None
 
-		self.lastPongTime = int(time.time())
 		super(ReaderThread,self).__init__();
 
 		self.daemon = True
@@ -737,33 +885,12 @@ class ReaderThread(threading.Thread):
 			self.disconnectedSent = True
 			if self.disconnectedCallback:
 				self.disconnectedCallback()
-			self.lock.release()
-			self.signalInterface.send("disconnected", (reason,))
+		self.lock.release()
+		self.signalInterface.send("disconnected", (reason,))
 
 	def run(self):
 		self._d("Read thread startedX");
 		while True:
-
-			
-			countdown = self.timeout - ((int(time.time()) - self.lastPongTime))
-			
-			remainder = countdown % self.selectTimeout
-			countdown = countdown - remainder
-					
-			if countdown <= 0:
-				self._d("No hope, dying!")
-				self.sendDisconnected("closed")
-				return
-			else:
-				if countdown % (self.selectTimeout*10) == 0 or countdown < 11:
-					self._d("Waiting, time to die: T-%i seconds" % countdown )
-					
-				if self.timeout-countdown == 150 and self.ping and self.autoPong:
-					self.ping()
-
-				self.selectTimeout = 1 if countdown < 11 else 3
-
-
 			try:
 				ready = select.select([self.socket.reader.rawIn], [], [], self.selectTimeout)
 			except:
@@ -784,55 +911,38 @@ class ReaderThread(threading.Thread):
 					self.sendDisconnected("closed")
 					return
 
-				self.lastPongTime = int(time.time());
-
 				if node is not None:
-					if ProtocolTreeNode.tagEquals(node,"iq"):
+					if ProtocolTreeNode.tagEquals(node, "stream:error"):
+						childNode = node.getChild(0)
+						reason = childNode.getAttributeValue("text")
+						self._d("Stream error!")
+						self.sendDisconnected("stream:error" if reason is None else reason)
+
+					elif ProtocolTreeNode.tagEquals(node,"iq"):
 						iqType = node.getAttributeValue("type")
 						idx = node.getAttributeValue("id")
+						iqxmlns = node.getAttributeValue("xmlns")
 
 						if iqType is None:
 							raise Exception("iq doesn't have type")
 
-						if iqType == "result":
+						if iqxmlns == "urn:xmpp:ping":
+							self.onPing(idx)
+						elif iqType == "result":
 							if idx in self.requests:
 								self.requests[idx](node)
 								del self.requests[idx]
-							elif idx.startswith(self.connection.user):
-								accountNode = node.getChild(0)
-								ProtocolTreeNode.require(accountNode,"account")
-								kind = accountNode.getAttributeValue("kind")
-
-								if kind == "paid":
-									self.connection.account_kind = 1
-								elif kind == "free":
-									self.connection.account_kind = 0
-								else:
-									self.connection.account_kind = -1
-
-								expiration = accountNode.getAttributeValue("expiration")
-
-								if expiration is None:
-									raise Exception("no expiration")
-
-								try:
-									self.connection.expire_date = long(expiration)
-								except ValueError:
-									raise IOError("invalid expire date %s"%(expiration))
+							else:
+								self._d("unexpected result id: " + idx)
 
-								self.eventHandler.onAccountChanged(self.connection.account_kind,self.connection.expire_date)
 						elif iqType == "error":
 							if idx in self.requests:
 								self.requests[idx](node)
 								del self.requests[idx]
 						elif iqType == "get":
 							childNode = node.getChild(0)
-							if ProtocolTreeNode.tagEquals(childNode,"ping"):
-								if self.autoPong:
-									self.onPing(idx)
-									
-								self.signalInterface.send("ping", (idx,))	
-							elif ProtocolTreeNode.tagEquals(childNode,"query") and node.getAttributeValue("from") is not None and "http://jabber.org/protocol/disco#info"; == childNode.getAttributeValue("xmlns"):
+							
+							if ProtocolTreeNode.tagEquals(childNode,"query") and node.getAttributeValue("from") is not None and "http://jabber.org/protocol/disco#info"; == childNode.getAttributeValue("xmlns"):
 								pin = childNode.getAttributeValue("pin");
 								timeoutString = childNode.getAttributeValue("timeout");
 								try:
@@ -857,32 +967,166 @@ class ReaderThread(threading.Thread):
 						else:
 							raise Exception("Unkown iq type %s"%(iqType))
 
+					elif ProtocolTreeNode.tagEquals(node, "ib"):
+						dirtyNode = node.getChild("dirty")
+						if dirtyNode is not None:
+							dirtyType = dirtyNode.getAttributeValue("type")
+							self.signalInterface.send("ib_dirty", (dirtyType,))
+                            ##sendCleanDirty(dirtyType)
+
 					elif ProtocolTreeNode.tagEquals(node,"presence"):
-						xmlns = node.getAttributeValue("xmlns")
 						jid = node.getAttributeValue("from")
 
-						if (xmlns is None or xmlns == "urn:xmpp") and jid is not None:
+						if jid is not None:
 							presenceType = node.getAttributeValue("type")
 							if presenceType == "unavailable":
 								self.signalInterface.send("presence_unavailable", (jid,))
 							elif presenceType is None or presenceType == "available":
 								self.signalInterface.send("presence_available", (jid,))
 
-						elif xmlns == "w" and jid is not None:
-							status = node.getAttributeValue("status")
+					elif ProtocolTreeNode.tagEquals(node, "notification"):
+
+						receiptRequested = True;
+						notificationType = None
+
+						notificationType = node.getAttributeValue("type");
+						notificationId = node.getAttributeValue("id");
+						notificationTo = node.getAttributeValue("to");
+						fromJid = node.getAttributeValue("from");
+						timestamp =int(node.getAttributeValue("t"))
+						msgId = node.getAttributeValue("id")
+							
+						if notificationType == "picture":
+							bodyNode = node.getChild("set")
+							
+							if bodyNode:
+								pictureId = int(bodyNode.getAttributeValue("id"))
+								if "-" in bodyNode.getAttributeValue("jid"):
+									self.signalInterface.send("notification_groupPictureUpdated",(bodyNode.getAttributeValue("jid"), bodyNode.getAttributeValue("author"), timestamp, msgId, pictureId, receiptRequested))
+								else:
+									self.signalInterface.send("notification_contactProfilePictureUpdated",(bodyNode.getAttributeValue("jid"), timestamp, msgId, pictureId, receiptRequested))
+
+							else:
+								bodyNode = node.getChild("delete")
+
+								if bodyNode:
+									if "-" in bodyNode.getAttributeValue("jid"):
+										self.signalInterface.send("notification_groupPictureRemoved",(bodyNode.getAttributeValue("jid"), bodyNode.getAttributeValue("author"), timestamp, msgId, receiptRequested))
+									else:
+										self.signalInterface.send("notification_contactProfilePictureRemoved",(bodyNode.getAttributeValue("jid"), timestamp, msgId, receiptRequested))
+
+							#if isGroup:
+							#	
+							#	self.signalInterface.send("notification_groupPictureUpdated",(bodyNode.getAttributeValue("jid"), bodyNode.getAttributeValue("author"), timestamp, msgId, receiptRequested))
+							#else:
+							#	self.signalInterface.send("notification_contactProfilePictureUpdated",(bodyNode.getAttributeValue("jid"), timestamp, msgId, receiptRequested))
+
+							#self.sendNotificationReceived(notificationTo, notificationId, fromJid, participant, notificationType, None)
+
+						elif notificationType == "participant":
+							addSubject = None
+							removeSubject = None
+							author = None
+
+							bodyNode = node.getChild("add");
+							if bodyNode is not None:
+								addSubject = bodyNode.getAttributeValue("jid");
+								author = bodyNode.getAttributeValue("author") or addSubject
+
+							bodyNode = node.getChild("remove");
+							if bodyNode is not None:
+								removeSubject = bodyNode.getAttributeValue("jid");
+								author = bodyNode.getAttributeValue("author") or removeSubject
+
+							if addSubject is not None:
+								
+								self.signalInterface.send("notification_groupParticipantAdded", (fromAttribute, addSubject, author, timestamp, msgId, receiptRequested))
+								
+							if removeSubject is not None:
+								self.signalInterface.send("notification_groupParticipantRemoved", (fromAttribute, removeSubject, author, timestamp, msgId, receiptRequested))
+
+							#self.sendNotificationReceived(notificationTo, notificationId, fromJid, participant, notificationType, None)
+
+						elif notificationType == "web":
+							self._d("web notification not implemented")
+
+							#self.sendNotificationReceived(fromJid, notificationId, notificationTo, participant, notificationType, None)
+
+						elif notificationType == "status":
+							setNode = node.getChild("set")
+							status = None if setNode is None else (setNode.data if sys.version_info < (3, 0) else setNode.data.encode('latin-1').decode());
+							
+							if status is not None:
+								self.signalInterface.send("contact_statusReceived",(fromJid, status))
+
+							#self.sendNotificationReceived(notificationTo, notificationId, fromJid, participant, notificationType, None)
+
+						elif notificationType == "subject":
+							receiptRequested = True;
+
+							bodyNode = node.getChild("body");
+							newSubject = None if bodyNode is None else (bodyNode.data if sys.version_info < (3, 0) else bodyNode.data.encode('latin-1').decode());
+							
+							if newSubject is not None:
+								self.signalInterface.send("group_subjectReceived",(msgId, fromAttribute, author, newSubject, int(attribute_t),  receiptRequested))
+
+							#self.sendNotificationReceived(notificationTo, notificationId, fromJid, participant, notificationType, None)
+
+						elif notificationType == "contacts":
+							contactNode = node.getChild("add")
+							contactJid = contactNode.getAttributeValue("jid")
+							self.signalInterface.send("notification_contactAdded", (contactJid, ))
+								
+							contactsNode = ProtocolTreeNode("sync", {"contacts": "out"})
+							#self.sendNotificationReceived(fromJid, notificationId, notificationTo, participant, notificationType, contactsNode)
+
+					elif ProtocolTreeNode.tagEquals(node, "receipt"):
+						receiptType = node.getAttributeValue("type");
+						fromJid = node.getAttributeValue("from");
+						msg_id = node.getAttributeValue("id")
+						if fromJid[-9:] == "broadcast":
+							fromJid = node.getAttributeValue("participant")
+						self.sendReceiptAck(msg_id, receiptType)
+						if receiptType != "delivered" and receiptType != "played":
+							self.signalInterface.send("receipt_messageDelivered", (fromJid, msg_id))
+							groupNode = node.getChild("list")
+							if groupNode:
+								items = groupNode.getAllChildren("item");
+								for i in items:
+									msg_id = i.getAttributeValue("id")
+									self.signalInterface.send("receipt_messageDelivered", (fromJid, msg_id))
+
+					elif ProtocolTreeNode.tagEquals(node, "ack"):
+						ackClass = node.getAttributeValue("class")
+						if ackClass == "message":
+							fromAttribute = node.getAttributeValue("from")
+							msgId = node.getAttributeValue("id")
+							self.signalInterface.send("receipt_messageSent", (fromAttribute, msgId))
+						elif ackClass == "receipt":
+							self._d("ack receipt not implemented")
+
+					elif ProtocolTreeNode.tagEquals(node, "chatstate"):
+						fromAttribute = node.getAttributeValue("from")
+						composingNode = node.getChild("composing");
+						if composingNode is not None:
+							self.signalInterface.send("contact_typing", (fromAttribute, ))
+						pausedNode = node.getChild("paused");
+						if pausedNode is not None:
+							self.signalInterface.send("contact_paused", (fromAttribute, ))
 
-							if status == "dirty":
-								#categories = self.parseCategories(node); #@@TODO, send along with signal
-								self._d("WILL SEND DIRTY")
-								self.signalInterface.send("status_dirty")
-								self._d("SENT DIRTY")
 
 					elif ProtocolTreeNode.tagEquals(node,"message"):
 						self.parseMessage(node)
-					
+
 
 		self._d("Reader thread terminating now!")
-					
+
+	def parseResultNode(self,node):
+		typeval = node.getAttributeValue("type");
+		if typeval != "result":
+			idx = node.getAttributeValue("id")
+			self._d("error response: " + idx)
+
 	def parseOfflineMessageStamp(self,stamp):
 
 		watime = WATime();
@@ -895,9 +1139,7 @@ class ReaderThread(threading.Thread):
 
 	def parsePingResponse(self, node):
 		idx = node.getAttributeValue("id")
-		self.lastPongTime = int(time.time())
-		
-		
+
 
 	def parseLastOnline(self,node):
 		jid = node.getAttributeValue("from");
@@ -920,6 +1162,7 @@ class ReaderThread(threading.Thread):
 
 	def parseGroups(self,node):
 		children = node.getAllChildren("group");
+		groups = []
 		for groupNode in children:
 			jid = groupNode.getAttributeValue("id") + "@g.us"
 			owner = groupNode.getAttributeValue("owner")
@@ -929,6 +1172,9 @@ class ReaderThread(threading.Thread):
 			creation = groupNode.getAttributeValue("creation")
 
 			self.signalInterface.send("group_gotInfo",(jid, owner, subject, subjectOwner, int(subjectT),int(creation)))
+			groups.append((jid, owner, subject, subjectOwner, int(subjectT),int(creation)))
+
+		self.signalInterface.send("group_gotGroups", (groups,))
 
 
 	def parseGroupInfo(self,node):
@@ -1028,6 +1274,21 @@ class ReaderThread(threading.Thread):
 
 		return tmp
 	
+	def parseSync(self, node):
+		node_in = node.getChild("sync").getChild("in").getAllChildren("user")
+		node_out = node.getChild("sync").getChild("out").getAllChildren("user")
+		
+		_in = [item.data for item in node_in]
+		_out = [item.data for item in node_out]
+		
+		# Uncomment the following lines if you would rather return the jid
+		# instead of the (international) phone number.
+		# _in = [item.getAttributeValue("jid") for item in node_in]
+		# _out = [item.getAttributeValue("jid") for item in node_out]
+		
+		self.signalInterface.send("sync_gotSyncResult", (_in, _out))
+		
+	
 	def parseGetPicture(self,node):
 		jid = node.getAttributeValue("from");
 		if "error code" in node.toString():
@@ -1048,14 +1309,14 @@ class ReaderThread(threading.Thread):
 	def parseGetPictureIds(self,node):
 		jid = node.getAttributeValue("from");
 		groupNode = node.getChild("list")
-		#self._d(groupNode.toString())
+		self._d(groupNode.toString())
 		children = groupNode.getAllChildren("user");
-		#pids = []
+		pids = []
 		for c in children:
 			if c.getAttributeValue("id") is not None:
-				#pids.append({"jid":c.getAttributeValue("jid"),"id":c.getAttributeValue("id")})
+				pids.append({"jid":c.getAttributeValue("jid"),"id":c.getAttributeValue("id")})
 				self.signalInterface.send("contact_gotProfilePictureId", (c.getAttributeValue("jid"), c.getAttributeValue("id")))
-		#self.signalInterface.send("contact_gotProfilePictureIds", (pids,))
+		self.signalInterface.send("contact_gotProfilePictureIds", (pids,))
 
 
 	def parseSetPicture(self,node):
@@ -1109,56 +1370,107 @@ class ReaderThread(threading.Thread):
 			else:
 				self.signalInterface.send("media_uploadRequestFailed", (_hash,))
 				
+	def parseSyncContacts(self, node):
+		syncNode = node.getChild("sync")
+		childs = []
+		syncList = []
+		if syncNode is not None:
+			fullNode = syncNode.getChild("full")
+			if fullNode is not None:
+				childs += fullNode.getAllChildren()
+			inNode = syncNode.getChild("in")
+			if inNode is not None:
+				childs += inNode.getAllChildren()
+		for child in childs:
+			if ProtocolTreeNode.tagEquals(child, "user"):
+				cjid = child.getAttributeValue("jid")
+				cphone = child.data
+				syncList.append({"jid": cjid, "phone": cphone})
+		if len(syncList) > 0:
+			self.signalInterface.send("sync_contactsReceived", (syncList,))
+
+
+	def parseSyncStatuses(self, node):
+		syncNode = node.getChild("sync")
+		childs = node.getAllChildren()
+		syncList = []
+		for child in childs:
+			if ProtocolTreeNode.tagEquals(child, "user"):
+				cjid = child.getAttributeValue("jid")
+				timestamp = child.getAttributeValue("t")
+				message = child.data
+				if len(message) == 0:
+					code = child.getAttributeValue("code")
+					if code == "401":
+						message = "hidden"
+				syncList.append({"jid": cjid, "lastseen": timestamp, "message": message})
+		if len(syncList) > 0:
+			self.signalInterface.send("sync_statusesReceived", (syncList,))
+
+	def parsePrivacyList(self, node):
+		listNode = node.getChild("list")
+		privacyList = []
+		if listNode is not None:
+			childs = listNode.getAllChildren()
+			for child in childs:
+				if ProtocolTreeNode.tagEquals(child, "item"):
+					cjid = child.getAttributeValue("jid")
+					if cjid is not None:
+						privacyList.append(cjid)
+		if len(privacyList) > 0:
+			self.signalInterface.send("privacy_listReceived", (privacyList,))
+
+	def parsePrivacySettings(self, node):
+		settingsList = []
+		childs = node.getAllChildren()
+		for child in childs:
+			if ProtocolTreeNode.tagEquals(node, "category"):
+				key = child.getAttributeValue("name")
+				value = child.getAttributeValue("value")
+				settingsList.append({"key": key, "value": value})
+		if len(settingsList) > 0:
+			self.signalInterface.send("privacy_settingsReceived", (settingsList,))
+
+	def parseAccountDelete(self, node):
+		#TODO
+		self._d("parseAccountDelete not implemented")
 
 	def parseMessage(self,messageNode):
-
-
-		bodyNode = messageNode.getChild("body");
-#		offlineNode = messageNode.getChild("offline")
-
+		id = messageNode.getAttributeValue("id");
+		timestamp = int(messageNode.getAttributeValue("t"))
+		fromAttribute = messageNode.getAttributeValue("from");
+		author = messageNode.getAttributeValue("participant");
+		isBroadcast = False;
+		if fromAttribute.find("@broadcast") >= 0:
+			fromAttribute = author;
+			isBroadcast = True;
 		
-		newSubject = "" if bodyNode is None else bodyNode.data;
+		offline = messageNode.getAttributeValue("offline") is not None
+		retry = messageNode.getAttributeValue("retry");
+		typeAttribute = messageNode.getAttributeValue("type");
+
 		msgData = None
-#		timestamp =long(time.time()*1000) if not offlineNode else int(messageNode.getAttributeValue("t"))*1000;
-		timestamp =int(messageNode.getAttributeValue("t"))
 		isGroup = False
-		isBroadcast = False
 		
+		bodyNode = messageNode.getChild("body");
+		newSubject = "" if bodyNode is None else bodyNode.data;
 		if newSubject.find("New version of WhatsApp Messenger is now available")>-1:
 			self._d("Rejecting whatsapp server message")
 			return #REJECT THIS FUCKING MESSAGE!
 
-
-		fromAttribute = messageNode.getAttributeValue("from");
-
 		try:
 			fromAttribute.index('-')
 			isGroup = True
 		except:
 			pass
 
-		author = messageNode.getAttributeValue("author");
-		#@@TODO reactivate blocked contacts check from client
-		'''if fromAttribute is not None and fromAttribute in self.eventHandler.blockedContacts:
-			self._d("CONTACT BLOCKED!")
-			return
-
-		if author is not None and author in self.eventHandler.blockedContacts:
-			self._d("CONTACT BLOCKED!")
-			return
-		'''
-
 		pushName = None
 		notifNode = messageNode.getChild("notify")
 		if notifNode is not None:
 			pushName = notifNode.getAttributeValue("name");
-			#pushName = pushName.decode("utf8")
 
 
 		msgId = messageNode.getAttributeValue("id");
-		attribute_t = messageNode.getAttributeValue("t");
-
-		typeAttribute = messageNode.getAttributeValue("type");
 
 		if typeAttribute == "error":
 			errorCode = 0;
@@ -1171,96 +1483,13 @@ class ReaderThread(threading.Thread):
 					'''catch value error'''
 				self.signalInterface.send("message_error", (msgId, fromAttribute, errorCode))
 
-		elif typeAttribute == "notification":
-
-			receiptRequested = False;
-			pictureUpdated = None
-
-			pictureUpdated = messageNode.getChild("notification").getAttributeValue("type");
-
-			wr = None
-			wr = messageNode.getChild("request").getAttributeValue("xmlns");
-			if wr == "urn:xmpp:receipts":
-				receiptRequested = True
-				
-			if pictureUpdated == "picture":
-				notifNode = messageNode.getChild("notification");
-				#bodyNode = messageNode.getChild("notification").getChild("set") or messageNode.getChild("notification").getChild("delete")
-
-				bodyNode = notifNode.getChild("set")
-				
-				if bodyNode:
-					pictureId = int(bodyNode.getAttributeValue("id"))
-					if isGroup:
-						self.signalInterface.send("notification_groupPictureUpdated",(bodyNode.getAttributeValue("jid"), bodyNode.getAttributeValue("author"), timestamp, msgId, pictureId, receiptRequested))
-					else:
-						self.signalInterface.send("notification_contactProfilePictureUpdated",(bodyNode.getAttributeValue("jid"), timestamp, msgId, pictureId, receiptRequested))
-
-				else:
-					bodyNode = notifNode.getChild("delete")
-
-					if bodyNode:
-						if isGroup:
-							self.signalInterface.send("notification_groupPictureRemoved",(bodyNode.getAttributeValue("jid"), bodyNode.getAttributeValue("author"), timestamp, msgId, receiptRequested))
-						else:
-							self.signalInterface.send("notification_contactProfilePictureRemoved",(bodyNode.getAttributeValue("jid"), timestamp, msgId, receiptRequested))
-
-				#if isGroup:
-				#	
-				#	self.signalInterface.send("notification_groupPictureUpdated",(bodyNode.getAttributeValue("jid"), bodyNode.getAttributeValue("author"), timestamp, msgId, receiptRequested))
-				#else:
-				#	self.signalInterface.send("notification_contactProfilePictureUpdated",(bodyNode.getAttributeValue("jid"), timestamp, msgId, receiptRequested))
-
-			else:
-				addSubject = None
-				removeSubject = None
-				author = None
-
-				bodyNode = messageNode.getChild("notification").getChild("add");
-				if bodyNode is not None:
-					addSubject = bodyNode.getAttributeValue("jid");
-					author = bodyNode.getAttributeValue("author") or addSubject
-
-				bodyNode = messageNode.getChild("notification").getChild("remove");
-				if bodyNode is not None:
-					removeSubject = bodyNode.getAttributeValue("jid");
-					author = bodyNode.getAttributeValue("author") or removeSubject
-
-				if addSubject is not None:
-					
-					self.signalInterface.send("notification_groupParticipantAdded", (fromAttribute, addSubject, author, timestamp, msgId, receiptRequested))
-					
-				if removeSubject is not None:
-					self.signalInterface.send("notification_groupParticipantRemoved", (fromAttribute, removeSubject, author, timestamp, msgId, receiptRequested))
-
-
-		elif typeAttribute == "subject":
-			receiptRequested = False;
-			requestNodes = messageNode.getAllChildren("request");
-			for requestNode in requestNodes:
-				if requestNode.getAttributeValue("xmlns") == "urn:xmpp:receipts":
-					receiptRequested = True;
-
-			bodyNode = messageNode.getChild("body");
-			newSubject = None if bodyNode is None else (bodyNode.data if sys.version_info < (3, 0) else bodyNode.data.encode('latin-1').decode());
-			
-			if newSubject is not None:
-				self.signalInterface.send("group_subjectReceived",(msgId, fromAttribute, author, newSubject, int(attribute_t),  receiptRequested))
-
-		elif typeAttribute == "chat":
+		elif typeAttribute == "text" or typeAttribute == "media":
 			wantsReceipt = False;
 			messageChildren = [] if messageNode.children is None else messageNode.children
 
 			for childNode in messageChildren:
 				if ProtocolTreeNode.tagEquals(childNode,"request"):
 					wantsReceipt = True;
-				
-				if ProtocolTreeNode.tagEquals(childNode,"broadcast"):
-					isBroadcast = True
-				elif ProtocolTreeNode.tagEquals(childNode,"composing"):
-						self.signalInterface.send("contact_typing", (fromAttribute,))
-				elif ProtocolTreeNode.tagEquals(childNode,"paused"):
-						self.signalInterface.send("contact_paused",(fromAttribute,))
 
 				elif ProtocolTreeNode.tagEquals(childNode,"media") and msgId is not None:
 	
@@ -1324,7 +1553,7 @@ class ReaderThread(threading.Thread):
 					elif mediaType =="vcard":
 						#return
 						#mediaItem.preview = messageNode.getChild("media").data
-						vcardData = messageNode.getChild("media").getChild("vcard").toString()
+						vcardData = messageNode.getChild("media").getChild("vcard").data
 						vcardName = messageNode.getChild("media").getChild("vcard").getAttributeValue("name")
 						
 						if vcardName and not sys.version_info < (3, 0):
@@ -1404,10 +1633,6 @@ class ReaderThread(threading.Thread):
 								self.signalInterface.send("receipt_messageDelivered", (fromAttribute, msgId))
 							elif receipt_type == "visible":
 								self.signalInterface.send("receipt_visible", (fromAttribute, msgId))
-							
-
-
-
 
 			if msgData:
 				msgData = msgData if sys.version_info < (3, 0) else msgData.encode('latin-1').decode()
Description: update the messages used by client/server interface.
Author: Tarek Galal <tare2.ga...@gmail.com>
Reviewed-by: Joao Eriberto Mota Filho <eribe...@debian.org>
Origin: https://github.com/tgalal/yowsup/tree/legacy
Last-Update: 2014-12-02
Index: yowsup-0.0~git20140314.938cf1/src/Yowsup/Interfaces/Interface.py
===================================================================
--- yowsup-0.0~git20140314.938cf1.orig/src/Yowsup/Interfaces/Interface.py
+++ yowsup-0.0~git20140314.938cf1/src/Yowsup/Interfaces/Interface.py
@@ -74,19 +74,33 @@ class SignalInterfaceBase(object):
 			"notification_groupPictureRemoved",
 			"notification_groupParticipantAdded",
 			"notification_groupParticipantRemoved",
+			"notification_contactAdded",
 
 			"contact_gotProfilePictureId",
+			"contact_gotProfilePictureIds",
 			"contact_gotProfilePicture",
 			"contact_typing",
 			"contact_paused",
+			"contact_statusReceived",
 			
 			"profile_setPictureSuccess",
 			"profile_setPictureError",
 			"profile_setStatusSuccess",
+			
+			"sync_gotSyncResult",
 
 			"ping",
 			"pong",
 			"disconnected",
+
+			"subscription_link",
+
+			"privacy_listReceived",
+
+			"privacy_settingsReceived",
+
+			"sync_contactsReceived",
+			"sync_statusesReceived",
 			
 			"media_uploadRequestSuccess",
 			"media_uploadRequestFailed",
@@ -200,6 +214,7 @@ class MethodInterfaceBase(object):
 			"profile_setPicture",
 			"profile_setStatus",
 
+			"sync_sendSync",
 			
 			"ready",
 			"disconnect",
Description: fix the Examples path and countries.csv place.
Author: Joao Eriberto Mota Filho <eribe...@debian.org>

Description: fix the Examples path and countries.csv place.
             Make the command compliant with new WhatsApp version.
Author: Tarek Galal <tare2.ga...@gmail.com>
Reviewed-by: Joao Eriberto Mota Filho <eribe...@debian.org>
Origin: https://github.com/tgalal/yowsup/tree/legacy
Last-Update: 2014-12-02
Index: yowsup-0.0~git20140314.938cf1/src/yowsup-cli
===================================================================
--- yowsup-0.0~git20140314.938cf1.orig/src/yowsup-cli
+++ yowsup-0.0~git20140314.938cf1/src/yowsup-cli
@@ -30,18 +30,18 @@ import argparse, sys, os, csv
 from Yowsup.Common.utilities import Utilities
 from Yowsup.Common.debugger import Debugger
 from Yowsup.Common.constants import Constants
-from Examples.CmdClient import WhatsappCmdClient
-from Examples.EchoClient import WhatsappEchoClient
-from Examples.ListenerClient import WhatsappListenerClient
+from Yowsup.Examples.CmdClient import WhatsappCmdClient
+from Yowsup.Examples.EchoClient import WhatsappEchoClient
+from Yowsup.Examples.ListenerClient import WhatsappListenerClient
 from Yowsup.Registration.v2.existsrequest import WAExistsRequest as WAExistsRequestV2
 from Yowsup.Registration.v2.coderequest import WACodeRequest as WACodeRequestV2
 from Yowsup.Registration.v2.regrequest import WARegRequest as WARegRequestV2
 from Yowsup.Contacts.contacts import WAContactsSyncRequest
-
+from Yowsup.Examples.PictureClient import WhatsappPictureClient
 import threading,time, base64
 
 DEFAULT_CONFIG = os.path.expanduser("~")+"/.yowsup/auth"
-COUNTRIES_CSV = "countries.csv"
+COUNTRIES_CSV = "/usr/share/yowsup-cli/countries.csv"
 
 CONFIG_HELP = """\
 Yowsup Configuration
@@ -170,6 +170,7 @@ regGroup = parser.add_argument_group("Re
 modes = clientsGroup.add_mutually_exclusive_group()
 modes.add_argument('-l','--listen', help='Listen to messages', action="store_true", required=False, default=False)
 modes.add_argument('-s','--send', help="Send message to phone number and close connection. Phone is full number including country code, without '+' or '00'", action="store",  nargs=2, metavar=('<phone>','<message>'), required=False)
+modes.add_argument('-p','--picture', help='Get Profile Picture by ID', action="store", metavar=('<phone>'), required=False)
 modes.add_argument('-i','--interactive', help="Start an interactive conversation with a contact. Phone is full number including country code, without '+' or '00'", action="store", metavar='<phone>', required=False)
 modes.add_argument('-b','--broadcast', help="Broadcast message to multiple recepients, comma seperated", action="store", nargs=2, metavar=('<jids>', '<message>'), required=False)
 
@@ -250,6 +251,10 @@ else:
 			message = args["send"][1]
 			wa = WhatsappEchoClient(phone, message, args['wait'])
 			wa.login(login, password)
+		elif args['picture']:
+			phone = args["picture"]
+			wa = WhatsappPictureClient(phone, args['wait'])
+			wa.login(login, password)
 		elif args['listen']:
 			wa = WhatsappListenerClient(args['keepalive'], args['autoack'])
 
Description: add the token map file, needed by new version.
Author: Tarek Galal <tare2.ga...@gmail.com>
Reviewed-by: Joao Eriberto Mota Filho <eribe...@debian.org>
Origin: https://github.com/tgalal/yowsup/tree/legacy
Last-Update: 2014-12-02
Index: yowsup-0.0~git20140314.938cf1/src/Yowsup/ConnectionIO/tokenmap.py
===================================================================
--- /dev/null
+++ yowsup-0.0~git20140314.938cf1/src/Yowsup/ConnectionIO/tokenmap.py
@@ -0,0 +1,70 @@
+class TokenDictionary:
+
+  def __init__(self):
+    self.dictionary = ["", "", "", "account", "ack", "action", "active", "add", "after", "all", "allow", "apple",
+        "auth", "author", "available", "bad-protocol", "bad-request", "before", "body", "broadcast",
+        "cancel", "category", "challenge", "chat", "clean", "code", "composing", "config", "contacts",
+        "count", "create", "creation", "debug", "default", "delete", "delivery", "delta", "deny",
+        "digest", "dirty", "duplicate", "elapsed", "enable", "encoding", "error", "event",
+        "expiration", "expired", "fail", "failure", "false", "favorites", "feature", "features",
+        "feature-not-implemented", "field", "first", "free", "from", "g.us", "get", "google", "group",
+        "groups", "http://etherx.jabber.org/streams";, "http://jabber.org/protocol/chatstates";, "ib",
+        "id", "image", "img", "index", "internal-server-error", "ip", "iq", "item-not-found", "item",
+        "jabber:iq:last", "jabber:iq:privacy", "jabber:x:event", "jid", "kind", "last", "leave",
+        "list", "max", "mechanism", "media", "message_acks", "message", "method", "microsoft",
+        "missing", "modify", "mute", "name", "nokia", "none", "not-acceptable", "not-allowed",
+        "not-authorized", "notification", "notify", "off", "offline", "order", "owner", "owning",
+        "p_o", "p_t", "paid", "participant", "participants", "participating", "paused", "picture",
+        "pin", "ping", "platform", "port", "presence", "preview", "probe", "prop", "props", "query",
+        "raw", "read", "reason", "receipt", "received", "relay", "remote-server-timeout", "remove",
+        "request", "required", "resource-constraint", "resource", "response", "result", "retry",
+        "rim", "s_o", "s_t", "s.us", "s.whatsapp.net", "seconds", "server-error", "server",
+        "service-unavailable", "set", "show", "silent", "stat", "status", "stream:error",
+        "stream:features", "subject", "subscribe", "success", "sync", "t", "text", "timeout",
+        "timestamp", "to", "true", "type", "unavailable", "unsubscribe", "uri", "url",
+        "urn:ietf:params:xml:ns:xmpp-sasl", "urn:ietf:params:xml:ns:xmpp-stanzas",
+        "urn:ietf:params:xml:ns:xmpp-streams", "urn:xmpp:ping", "urn:xmpp:receipts",
+        "urn:xmpp:whatsapp:account", "urn:xmpp:whatsapp:dirty", "urn:xmpp:whatsapp:mms",
+        "urn:xmpp:whatsapp:push", "urn:xmpp:whatsapp", "user", "user-not-found", "value",
+        "version", "w:g", "w:p:r", "w:p", "w:profile:picture", "w", "wait", "WAUTH-2",
+        "x", "xmlns:stream", "xmlns", "1", "chatstate", "crypto", "enc", "class", "off_cnt",
+        "w:g2", "promote", "demote", "creator", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+        "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 
+		"Bell.caf", "Boing.caf", "Glass.caf", "Harp.caf", "TimePassing.caf", "Tri-tone.caf",
+        "Xylophone.caf", "background", "backoff", "chunked", "context", "full", "in", "interactive",
+        "out", "registration", "sid", "urn:xmpp:whatsapp:sync", "flt", "s16", "u8", "adpcm",
+        "amrnb", "amrwb", "mp3", "pcm", "qcelp", "wma", "h263", "h264", "jpeg", "mpeg4", "wmv",
+        "audio/3gpp", "audio/aac", "audio/amr", "audio/mp4", "audio/mpeg", "audio/ogg", "audio/qcelp",
+        "audio/wav", "audio/webm", "audio/x-caf", "audio/x-ms-wma", "image/gif", "image/jpeg",
+        "image/png", "video/3gpp", "video/avi", "video/mp4", "video/mpeg", "video/quicktime",
+        "video/x-flv", "video/x-ms-asf", "302", "400", "401", "402", "403", "404", "405", "406",
+        "407", "409", "500", "501", "503", "504", "abitrate", "acodec", "app_uptime", "asampfmt",
+        "asampfreq", "audio", "bb_db", "clear", "conflict", "conn_no_nna", "cost", "currency",
+        "duration", "extend", "file", "fps", "g_notify", "g_sound", "gcm", "google_play", "hash",
+        "height", "invalid", "jid-malformed", "latitude", "lc", "lg", "live", "location", "log",
+        "longitude", "max_groups", "max_participants", "max_subject", "mimetype", "mode",
+        "napi_version", "normalize", "orighash", "origin", "passive", "password", "played",
+        "policy-violation", "pop_mean_time", "pop_plus_minus", "price", "pricing", "redeem",
+        "Replaced by new connection", "resume", "signature", "size", "sound", "source",
+        "system-shutdown", "username", "vbitrate", "vcard", "vcodec", "video", "width",
+        "xml-not-well-formed", "checkmarks", "image_max_edge", "image_max_kbytes", "image_quality",
+        "ka", "ka_grow", "ka_shrink", "newmedia", "library", "caption", "forward", "c0", "c1", "c2",
+        "c3", "clock_skew", "cts", "k0", "k1", "login_rtt", "m_id", "nna_msg_rtt", "nna_no_off_count",
+        "nna_offline_ratio", "nna_push_rtt", "no_nna_con_count", "off_msg_rtt", "on_msg_rtt",
+        "stat_name", "sts", "suspect_conn", "lists", "self", "qr", "web", "w:b", "recipient",
+        "w:stats", "forbidden", "aurora.m4r", "bamboo.m4r", "chord.m4r", "circles.m4r", "complete.m4r",
+        "hello.m4r", "input.m4r", "keys.m4r", "note.m4r", "popcorn.m4r", "pulse.m4r", "synth.m4r",
+        "filehash"]
+
+  def getToken(self, index):
+    if index >= 0 and index < len(self.dictionary):
+        return self.dictionary[index]
+
+    raise Exception("Token index does not exist: %s" % index)
+
+  def getIndex(self, token):
+    for i in range(0, len(self.dictionary)):
+      if token == self.dictionary[i]:
+        return i
+
+    raise KeyError("No index for given token %s" % token)
Description: update the protocoltreenode file.
Author: Tarek Galal <tare2.ga...@gmail.com>
Reviewed-by: Joao Eriberto Mota Filho <eribe...@debian.org>
Origin: https://github.com/tgalal/yowsup/tree/legacy
Last-Update: 2014-12-02
Index: yowsup-0.0~git20140314.938cf1/src/Yowsup/ConnectionIO/protocoltreenode.py
===================================================================
--- yowsup-0.0~git20140314.938cf1.orig/src/Yowsup/ConnectionIO/protocoltreenode.py
+++ yowsup-0.0~git20140314.938cf1/src/Yowsup/ConnectionIO/protocoltreenode.py
@@ -33,21 +33,21 @@ class ProtocolTreeNode():
 		self.children = children;
 		self.data = data
 		
-	def toString(self):
+	def toString(self, depth = 0):
 		try:
-			out = "<"+self.tag;
+			out = (" " * depth) + "<"+self.tag;
 			if self.attributes is not None:
 				for key,val in self.attributes.items():
 					out+= " "+key+'="'+val+'"'
 			out+= ">\n";
 			if self.data is not None:
-				out += self.data;
+				out += (" " * (depth + 4)) + self.data.encode("hex") + "\n"
 			
 			if self.children is not None:
 				for c in self.children:
-					out+=c.toString();
+					out+=c.toString(depth + 4);
 			#print sel
-			out+= "</"+self.tag+">\n"
+			out+= (" " * depth) + "</"+self.tag+">\n"
 			return out
 		except TypeError:
 			print("ignored toString call, probably encountered byte")
Description: add a missing variable.
Author: Tarek Galal <tare2.ga...@gmail.com>
Reviewed-by: Joao Eriberto Mota Filho <eribe...@debian.org>
Origin: https://github.com/tgalal/yowsup/tree/legacy
Last-Update: 2014-12-02
Index: yowsup-0.0~git20140314.938cf1/src/Examples/CmdClient.py
===================================================================
--- yowsup-0.0~git20140314.938cf1.orig/src/Examples/CmdClient.py
+++ yowsup-0.0~git20140314.938cf1/src/Examples/CmdClient.py
@@ -24,6 +24,7 @@ import time, datetime, sys
 
 if sys.version_info >= (3, 0):
 	raw_input = input
+	long = int
 
 class WhatsappCmdClient:
 	
Description: update the constants used to simulate the local mobile phone.
             This data is used in SSL actions.
Author: Tarek Galal <tare2.ga...@gmail.com>
Reviewed-by: Joao Eriberto Mota Filho <eribe...@debian.org>
Origin: https://github.com/tgalal/yowsup/tree/legacy
Last-Update: 2014-12-02
Index: yowsup-0.0~git20140314.938cf1/src/Yowsup/Common/constants.py
===================================================================
--- yowsup-0.0~git20140314.938cf1.orig/src/Yowsup/Common/constants.py
+++ yowsup-0.0~git20140314.938cf1/src/Yowsup/Common/constants.py
@@ -53,10 +53,10 @@ class Constants:
 	v="0.82"
 
 	tokenData = {
-		"v": "2.12.15",
-		"r": "S40-2.12.15",
-		"u": "WhatsApp/2.12.15 S40Version/14.26 Device/Nokia302",
-		"t": "PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk1391039105258{phone}",
+		"v": "2.12.49",
+		"r": "S40-2.12.49",
+		"u": "WhatsApp/2.12.49 S40Version/14.26 Device/Nokia302",
+		"t": "PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk1413401214298{phone}",
 		"d": "Nokia302"
 	}
 
Description: update the bintreenode file.
Author: Tarek Galal <tare2.ga...@gmail.com>
Reviewed-by: Joao Eriberto Mota Filho <eribe...@debian.org>
Origin: https://github.com/tgalal/yowsup/tree/legacy
Last-Update: 2014-12-02
Index: yowsup-0.0~git20140314.938cf1/src/Yowsup/ConnectionIO/bintreenode.py
===================================================================
--- yowsup-0.0~git20140314.938cf1.orig/src/Yowsup/ConnectionIO/bintreenode.py
+++ yowsup-0.0~git20140314.938cf1/src/Yowsup/ConnectionIO/bintreenode.py
@@ -22,7 +22,7 @@ OR THE USE OR OTHER DEALINGS IN THE SOFT
 from Yowsup.Common.debugger import Debugger
 from Yowsup.Common.datastructures import ByteArray
 from Yowsup.Common.constants import Constants
-
+from .tokenmap import TokenDictionary
 
 from .protocoltreenode import ProtocolTreeNode
 from .ioexceptions import InvalidReadException
@@ -35,30 +35,26 @@ class BinTreeNodeReader():
         self.inputKey = None
         
         self._d('Reader init');
-        self.tokenMap = Constants.dictionary;
         self.rawIn = inputstream;
         self.inn = ByteArray();
         self.buf = []#bytearray(1024);
         self.bufSize = 0;
         self.readSize = 1;
-        
+        self.tokenDictionary = TokenDictionary()
 
     def readStanza(self):
 
-        num = self.readInt8(self.rawIn)
-        stanzaSize = self.readInt16(self.rawIn,1);
+        firstByte = self.readInt8(self.rawIn)
+        stanzaFlag = (firstByte & 0xF0) >> 4
+        stanzaSize = self.readInt16(self.rawIn) | ((firstByte & 0x0F) << 16)
+
+        isEncrypted = ((stanzaFlag & 8) != 0)
 
-        header = (num << 16) + stanzaSize#self.readInt24(self.rawIn)
-        
-        flags = (header >> 20);
-        #stanzaSize =  ((header & 0xF0000) >> 16) | ((header & 0xFF00) >> 8) | (header & 0xFF);
-        isEncrypted = ((flags & 8) != 0)
-        
         self.fillBuffer(stanzaSize);
         
         if self.inputKey is not None and isEncrypted:
             #self.inn.buf = bytearray(self.inn.buf)
-            self.inn.buf = self.inputKey.decodeMessage(self.inn.buf, 0, 4, len(self.inn.buf)-4)[4:]
+            self.inn.buf = self.inputKey.decodeMessage(self.inn.buf, 0, 4, len(self.inn.buf)-4)
 
     def streamStart(self):
 
@@ -68,7 +64,12 @@ class BinTreeNodeReader():
         size = self.readListSize(tag);
         tag = self.inn.read();
         if tag != 1:
-            raise Exception("expecting STREAM_START in streamStart");
+            if tag == 236:
+                tag = self.inn.read + 237
+
+            token = self.tokenDictionary.getToken(tag)
+
+            raise Exception("expecting STREAM_START in streamStart, instead got token: %s" % token);
         attribCount = (size - 2 + size % 2) / 2;
         self.readAttributes(attribCount);
     
@@ -119,22 +120,15 @@ class BinTreeNodeReader():
             attribs[key]=value;
         return attribs;
     
-    def getToken(self,token):
-        if (token >= 0 and token < len(self.tokenMap)):
-            ret = self.tokenMap[token];
-        else:
-            raise Exception("invalid token/length in getToken %i "%token);
-        
-        return ret;
-        
-    
     def readString(self,token):
         
         if token == -1:
             raise Exception("-1 token in readString");
         
-        if token > 4 and token < 245:
-            return self.getToken(token);
+        if token > 2 and token < 245:
+            if token == 236:
+              token = 237 + self.inn.read()
+            return self.tokenDictionary.getToken(token)
         
         if token == 0:
             return None;
@@ -158,7 +152,7 @@ class BinTreeNodeReader():
             
         if token == 254:
             token = self.inn.read();
-            return self.getToken(245+token);
+            return self.tokenMapper.getToken(245+token);
         if token == 250:
             user = self.readString(self.inn.read());
             server = self.readString(self.inn.read());
@@ -254,36 +248,25 @@ class BinTreeNodeWriter():
     TOKEN_8 = 254;
     #socket out; #FunXMPP.WAByteArrayOutputStream
     #socket realOut;
-    tokenMap={}
     
     def __init__(self,o):
         Debugger.attach(self)
 
         self.outputKey = None
 
-        dictionary = Constants.dictionary
         self.realOut = o;
         #self.out = o;
         self.tokenMap = {}
         self.out = ByteArray();
-        #this.tokenMap = new Hashtable(dictionary.length);
-        for i in range(0,len(dictionary)):
-            if dictionary[i] is not None:
-                self.tokenMap[dictionary[i]]=i
-        
-        #Utilities.debug(self.tokenMap);
-        '''
-        for (int i = 0; i < dictionary.length; i++)
-            if (dictionary[i] != null)
-                this.tokenMap.put(dictionary[i], new Integer(i));
-        '''
+
+        self.tokenDictionary = TokenDictionary()
 
     def streamStart(self,domain,resource):
         
         self.realOut.write(87);
         self.realOut.write(65);
         self.realOut.write(1);
-        self.realOut.write(2);
+        self.realOut.write(4);
 
         streamOpenAttributes  = {"to":domain,"resource":resource};
         self.writeListStart(len(streamOpenAttributes )*2+1);
@@ -299,7 +282,7 @@ class BinTreeNodeWriter():
             self.out.write(0);
         else:
             self._d("Outgoing");
-            self._d("\n %s" % node.toString());
+            self._d("\n%s"%node.toString());
             self.writeInternal(node);
 
         self.flushBuffer(needsFlush);
@@ -309,21 +292,17 @@ class BinTreeNodeWriter():
     def processBuffer(self):
         buf = self.out.getBuffer()
 
-        prep = [0,0,0]
-        prep.extend(buf)
-
         length1 = len(self.out.buf)
-        num = 0
 
         if self.outputKey is not None:
-            num = 1
-            prep.extend([0,0,0,0])
             length1 += 4
 
             #prep = bytearray(prep)
-            res = self.outputKey.encodeMessage(prep, len(prep) - 4 , 3, len(prep)-4-3)
+            buf = self.outputKey.encodeMessage(buf, len(buf), 0, len(buf))
 
-            res[0] = ((num << 4) | (length1 & 16711680) >> 16) % 256
+            res = [0,0,0]
+            res.extend(buf)
+            res[0] = ((8 << 4) | (length1 & 16711680) >> 16) % 256
             res[1] = ((length1 & 65280) >> 8) % 256
             res[2] = (length1 & 255) % 256
 
@@ -331,7 +310,9 @@ class BinTreeNodeWriter():
 
             return
         else:
-            prep[0] = ((num << 4) | (length1 & 16711680) >> 16) % 256
+            prep = [0,0,0]
+            prep.extend(buf)
+            prep[0] = ((0 << 4) | (length1 & 16711680) >> 16) % 256
             prep[1] = ((length1 & 65280) >> 8) % 256
             prep[2] = (length1 & 255) % 256
             self.out.buf = prep
@@ -437,8 +418,12 @@ class BinTreeNodeWriter():
     
     def writeString(self,tag):
         try:
-            key = self.tokenMap[tag];
-            self.writeToken(key);
+            key = self.tokenDictionary.getIndex(tag);
+            if key > 235:
+                self.writeToken(236);
+                self.writeToken(key - 237);
+            else:
+                self.writeToken(key);
         except KeyError:
             try:
 

Reply via email to