--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock
Please unblock package yowsup.
Yowsup is a cross platform Python library that provides communication across
WhatsApp network. In Debian, the source yowsup generates a library and a
command line client. The library is the base for any new application and the
client is useful for scripts and applications as Zabbix and Pidgin. Currently,
this is the only way to use WhatsApp in Debian.
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 debian/changelog since the current version in testing:
yowsup (0.0~git20140314.938cf1-3) unstable; urgency=medium
* Upload to unstable.
* Added some patches, provided by the upstream (thanks to Tarek Galal),
to make the program compliant with the WhatsApp version 2.11.432
(or later), released at November 2014. This version changed several
things in WhatsApp, including the authentication process. The
patches added are necessary to provide the connection and basic
activities only. 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)
* debian/watch: updated. Now, the upstream is using tags in GitHub.
-- Joao Eriberto Mota Filho <eribe...@debian.org> Tue, 02 Dec 2014 22:51:22
-0200
yowsup (0.0~git20140314.938cf1-2) experimental; urgency=medium
* debian/control:
- Added dh-python to Build-Depends field.
- Updated Standards-Version to 3.9.6.
* debian/watch: added a fake site to explain about the current
status of the original upstream homepage.
-- Joao Eriberto Mota Filho <eribe...@debian.org> Tue, 18 Nov 2014 09:00:10
-0200
Note that there are changes from experimental but these changes are in
"documentation level" or are basic procedures to satisfy the Lintian and
its don't offer any impact in unstable/testing.
I am available for any clarification.
Thanks a lot in advance.
Regards,
Eriberto
unblock yowsup/0.0~git20140314.938cf1-3
-- System Information:
Debian Release: jessie/sid
APT prefers testing
APT policy: (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
diff -Nru yowsup-0.0~git20140314.938cf1/debian/changelog yowsup-0.0~git20140314.938cf1/debian/changelog
--- yowsup-0.0~git20140314.938cf1/debian/changelog 2014-07-30 00:09:25.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/changelog 2014-12-02 23:11:47.000000000 -0200
@@ -1,3 +1,36 @@
+yowsup (0.0~git20140314.938cf1-3) unstable; urgency=medium
+
+ * Upload to unstable.
+ * Added some patches, provided by the upstream (thanks to Tarek Galal),
+ to make the program compliant with the WhatsApp version 2.11.432
+ (or later), released at November 2014. This version changed several
+ things in WhatsApp, including the authentication process. The
+ patches added are necessary to provide the connection and basic
+ activities only. 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)
+ * debian/watch: updated. Now, the upstream is using tags in GitHub.
+
+ -- Joao Eriberto Mota Filho <eribe...@debian.org> Tue, 02 Dec 2014 22:51:22 -0200
+
+yowsup (0.0~git20140314.938cf1-2) experimental; urgency=medium
+
+ * debian/control:
+ - Added dh-python to Build-Depends field.
+ - Updated Standards-Version to 3.9.6.
+ * debian/watch: added a fake site to explain about the current
+ status of the original upstream homepage.
+
+ -- Joao Eriberto Mota Filho <eribe...@debian.org> Tue, 18 Nov 2014 09:00:10 -0200
+
yowsup (0.0~git20140314.938cf1-1) unstable; urgency=low
* Initial release (Closes: #752201)
diff -Nru yowsup-0.0~git20140314.938cf1/debian/control yowsup-0.0~git20140314.938cf1/debian/control
--- yowsup-0.0~git20140314.938cf1/debian/control 2014-07-30 00:10:17.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/control 2014-12-02 22:51:00.000000000 -0200
@@ -2,8 +2,8 @@
Section: python
Priority: optional
Maintainer: Joao Eriberto Mota Filho <eribe...@debian.org>
-Build-Depends: debhelper (>= 9), python-all
-Standards-Version: 3.9.5
+Build-Depends: debhelper (>= 9), python-all, dh-python
+Standards-Version: 3.9.6
Homepage: https://github.com/tgalal/yowsup
Vcs-Git: git://anonscm.debian.org/collab-maint/yowsup.git
Vcs-Browser: http://anonscm.debian.org/?p=collab-maint/yowsup.git;a=summary
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/add-missing-variable yowsup-0.0~git20140314.938cf1/debian/patches/add-missing-variable
--- yowsup-0.0~git20140314.938cf1/debian/patches/add-missing-variable 1969-12-31 21:00:00.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/add-missing-variable 2014-12-02 22:45:10.000000000 -0200
@@ -0,0 +1,17 @@
+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:
+
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/add-PictureClient yowsup-0.0~git20140314.938cf1/debian/patches/add-PictureClient
--- yowsup-0.0~git20140314.938cf1/debian/patches/add-PictureClient 1969-12-31 21:00:00.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/add-PictureClient 2014-12-02 21:47:12.000000000 -0200
@@ -0,0 +1,90 @@
+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'] ))
++
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/add-the-tokenmap yowsup-0.0~git20140314.938cf1/debian/patches/add-the-tokenmap
--- yowsup-0.0~git20140314.938cf1/debian/patches/add-the-tokenmap 1969-12-31 21:00:00.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/add-the-tokenmap 2014-12-02 20:11:51.000000000 -0200
@@ -0,0 +1,80 @@
+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)
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/add-whatsapp-auth-v2 yowsup-0.0~git20140314.938cf1/debian/patches/add-whatsapp-auth-v2
--- yowsup-0.0~git20140314.938cf1/debian/patches/add-whatsapp-auth-v2 1969-12-31 21:00:00.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/add-whatsapp-auth-v2 2014-12-02 19:40:57.000000000 -0200
@@ -0,0 +1,386 @@
+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])
++
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/series yowsup-0.0~git20140314.938cf1/debian/patches/series
--- yowsup-0.0~git20140314.938cf1/debian/patches/series 2014-06-20 21:53:45.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/series 2014-12-02 22:42:41.000000000 -0200
@@ -1 +1,10 @@
+add-missing-variable
+add-PictureClient
+update-protocoltreenode
+update-bintreenode
+add-the-tokenmap
+update-the-interface-messages
+update-connection-manager
+update-user-agent
yowsup-cli
+add-whatsapp-auth-v2
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/update-bintreenode yowsup-0.0~git20140314.938cf1/debian/patches/update-bintreenode
--- yowsup-0.0~git20140314.938cf1/debian/patches/update-bintreenode 1969-12-31 21:00:00.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/update-bintreenode 2014-12-02 20:44:50.000000000 -0200
@@ -0,0 +1,207 @@
+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:
+
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/update-connection-manager yowsup-0.0~git20140314.938cf1/debian/patches/update-connection-manager
--- yowsup-0.0~git20140314.938cf1/debian/patches/update-connection-manager 1969-12-31 21:00:00.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/update-connection-manager 2014-12-02 19:49:38.000000000 -0200
@@ -0,0 +1,1196 @@
+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()
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/update-protocoltreenode yowsup-0.0~git20140314.938cf1/debian/patches/update-protocoltreenode
--- yowsup-0.0~git20140314.938cf1/debian/patches/update-protocoltreenode 1969-12-31 21:00:00.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/update-protocoltreenode 2014-12-02 20:56:41.000000000 -0200
@@ -0,0 +1,36 @@
+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")
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/update-the-interface-messages yowsup-0.0~git20140314.938cf1/debian/patches/update-the-interface-messages
--- yowsup-0.0~git20140314.938cf1/debian/patches/update-the-interface-messages 1969-12-31 21:00:00.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/update-the-interface-messages 2014-12-02 19:56:31.000000000 -0200
@@ -0,0 +1,51 @@
+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",
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/update-user-agent yowsup-0.0~git20140314.938cf1/debian/patches/update-user-agent
--- yowsup-0.0~git20140314.938cf1/debian/patches/update-user-agent 1969-12-31 21:00:00.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/update-user-agent 2014-12-02 22:28:18.000000000 -0200
@@ -0,0 +1,25 @@
+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"
+ }
+
diff -Nru yowsup-0.0~git20140314.938cf1/debian/patches/yowsup-cli yowsup-0.0~git20140314.938cf1/debian/patches/yowsup-cli
--- yowsup-0.0~git20140314.938cf1/debian/patches/yowsup-cli 2014-07-30 00:12:20.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/patches/yowsup-cli 2014-12-02 22:47:55.000000000 -0200
@@ -1,11 +1,17 @@
Description: fix the Examples path and countries.csv place.
Author: Joao Eriberto Mota Filho <eribe...@debian.org>
-Last-Update: 2014-07-01
+
+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,9 +30,9 @@ import argparse, sys, os, csv
+@@ -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
@@ -18,7 +24,9 @@
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
-@@ -41,7 +41,7 @@ from Yowsup.Contacts.contacts import WAC
+ from Yowsup.Contacts.contacts import WAContactsSyncRequest
+-
++from Yowsup.Examples.PictureClient import WhatsappPictureClient
import threading,time, base64
DEFAULT_CONFIG = os.path.expanduser("~")+"/.yowsup/auth"
@@ -27,3 +35,22 @@
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'])
+
diff -Nru yowsup-0.0~git20140314.938cf1/debian/watch yowsup-0.0~git20140314.938cf1/debian/watch
--- yowsup-0.0~git20140314.938cf1/debian/watch 2014-06-20 19:22:19.000000000 -0300
+++ yowsup-0.0~git20140314.938cf1/debian/watch 2014-12-02 23:13:16.000000000 -0200
@@ -1,2 +1,2 @@
version=3
-# The upstream didn't released a final version yet.
+https://github.com/tgalal/yowsup/releases .*/archive/v?(\d\S+)\.tar\.(?:bz2|gz|xz)
--- End Message ---