From a59e78c2f1aeec14ad6c3e7c2b4aeb9cfe80ce16 Mon Sep 17 00:00:00 2001 From: hoppler Date: Thu, 25 Oct 2012 12:29:00 +0200 Subject: [PATCH 001/271] Fix for Nullable object must have a value It fixes the "Nullable object must have a value" error by generating the required fields for the key. Also theres a workaround so the error wont occur again --- src/WhatsAppApi/Parser/FMessage.cs | 2 +- src/WhatsAppApi/Response/MessageRecvResponse.cs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/WhatsAppApi/Parser/FMessage.cs b/src/WhatsAppApi/Parser/FMessage.cs index 190f98b..1bebaf9 100644 --- a/src/WhatsAppApi/Parser/FMessage.cs +++ b/src/WhatsAppApi/Parser/FMessage.cs @@ -296,7 +296,7 @@ public bool Instantiated() public FMessage.Key Key() { - return new FMessage.Key(this.remote_jid, this.from_me.Value, this.id); + return new FMessage.Key(this.remote_jid, (!this.from_me.HasValue && this.from_me.Value), this.id); } public FMessage.Builder Key(FMessage.Key key) diff --git a/src/WhatsAppApi/Response/MessageRecvResponse.cs b/src/WhatsAppApi/Response/MessageRecvResponse.cs index 665657c..4252429 100644 --- a/src/WhatsAppApi/Response/MessageRecvResponse.cs +++ b/src/WhatsAppApi/Response/MessageRecvResponse.cs @@ -247,7 +247,12 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t { var tmpAttrName = itemNode.GetAttribute("name"); if (tmpAttrName != null) + { + builder.from_me = false; + builder.id = tmpAttrbId; + builder.remote_jid = tmpAttrFromJid; builder.Key().serverNickname = tmpAttrName; + } } } if (!builder.Timestamp().HasValue) From f8875c111d0f3c7a12fd2a9f7399bbecaea351b8 Mon Sep 17 00:00:00 2001 From: bader almutiri Date: Fri, 26 Oct 2012 03:45:56 +0300 Subject: [PATCH 002/271] ADD IOS PASSWORD --- src/WhatsAppApi/Register/WhatsRegister.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/WhatsAppApi/Register/WhatsRegister.cs b/src/WhatsAppApi/Register/WhatsRegister.cs index 77e3015..c18997f 100644 --- a/src/WhatsAppApi/Register/WhatsRegister.cs +++ b/src/WhatsAppApi/Register/WhatsRegister.cs @@ -110,7 +110,17 @@ private static string GetRegString(string countryCode, string phonenum, string c private static string ToPassword(this string bs) { - return (new string(bs.Reverse().ToArray())).MD5String(); + //OK LET'S CHECK IF IT'S IOS OR OTHER PALFROM ! ? + if (bs.Contains(":")) + { + string ps = bs.ToUpper(); + string ls = ps + ps; + return ls.MD5String(); + } + else + { + return (new string(bs.Reverse().ToArray())).MD5String(); + } } private static void GetLangAndLocale(CultureInfo that, out string lang, out string locale) From dd989978bec2e0e18ee3ba3cfc82c02ad15496cd Mon Sep 17 00:00:00 2001 From: hermanho Date: Wed, 7 Nov 2012 17:43:04 +0800 Subject: [PATCH 003/271] Update to protocol 1.2 --- src/WhatsAppApi/Helper/BinTreeNodeReader.cs | 280 +++++-- src/WhatsAppApi/Helper/BinTreeNodeWriter.cs | 222 +++++- src/WhatsAppApi/Helper/DecodeHelper.cs | 730 ++++++++++++------ src/WhatsAppApi/Helper/Func.cs | 50 +- .../Helper/IncompleteMessageException.cs | 8 +- src/WhatsAppApi/Helper/ProtocolTreeNode.cs | 59 +- .../Response/MessageRecvResponse.cs | 11 +- src/WhatsAppApi/Response/WhatsParser.cs | 6 +- src/WhatsAppApi/WhatsApp.cs | 266 +++++-- src/WhatsAppApi/WhatsAppApi.csproj | 4 + src/WhatsAppApi/WhatsNetwork.cs | 118 ++- src/WhatsAppApi/WhatsSendHandler.cs | 191 +++-- 12 files changed, 1402 insertions(+), 543 deletions(-) diff --git a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs index e3d50eb..370e582 100644 --- a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -8,33 +8,106 @@ namespace WhatsAppApi.Helper internal class BinTreeNodeReader { private string[] dictionary; - private string input; + //private string input; + + //change to protocol 1.2 + public byte[] Encryptionkey { get; set; } + private List buffer; public BinTreeNodeReader(string[] dict) { this.dictionary = dict; + this.Encryptionkey = null; } - public ProtocolTreeNode nextTree(string pInput = null) + //public ProtocolTreeNode nextTree(string pInput = null) + //{ + // if (pInput != null) + // { + // this.input = pInput; + // } + + // //int stanzaSize = this.peekInt16(); + // //Change to protocol 1.2 + // int stanzaSize = this.peekInt24(); + // int flags = (stanzaSize >> 20); + // int size = ((stanzaSize & 0xF0000) >> 16) | ((stanzaSize & 0xFF00) >> 8) | (stanzaSize & 0xFF); + + // bool isEncrypted = ((flags & 8) != 0); // 8 = (1 << 4) // Read node and decrypt + + // if (stanzaSize > this.input.Length) + // { + // //Es sind noch nicht alle Daten eingelesen, daher abbrechen und warten bis alles da ist + // var exception = new IncompleteMessageException("Incomplete message"); + // exception.setInput(this.input); + // throw exception; + // } + + // this.readInt24(); + // if (stanzaSize > 0) + // { + // if (isEncrypted && Encryptionkey != null) + // { + // RC4 encryption = new RC4(this.Encryptionkey, 256); + // byte[] dataB = this.sysEncoding.GetBytes(this.input); + // byte[] enData = new byte[dataB.Length - 3]; + // Buffer.BlockCopy(dataB, 3, enData, 0, dataB.Length - 3); + // //encryption.Cipher(enData, 0, dataB.Length - 3); + // enData = encryption.Encrypt(enData); + // Buffer.BlockCopy(enData, 0, dataB, 3, enData.Length); + // } + // return this.nextTreeInternal(); + // } + // return null; + //} + public ProtocolTreeNode nextTree(byte[] pInput = null) { if (pInput != null) { - this.input = pInput; + if (pInput.Length == 0) + return null; + this.buffer = new List(); + this.buffer.AddRange(pInput); } - int stanzaSize = this.peekInt16(); + //int stanzaSize = this.peekInt16(); + //Change to protocol 1.2 + int stanzaSize = this.peekInt24(); + int flags = (stanzaSize >> 20); + int size = ((stanzaSize & 0xF0000) >> 16) | ((stanzaSize & 0xFF00) >> 8) | (stanzaSize & 0xFF); - if (stanzaSize > this.input.Length) + bool isEncrypted = ((flags & 8) != 0); // 8 = (1 << 4) // Read node and decrypt + + //if (stanzaSize > this.buffer.Count) + if (size > this.buffer.Count) { //Es sind noch nicht alle Daten eingelesen, daher abbrechen und warten bis alles da ist var exception = new IncompleteMessageException("Incomplete message"); - exception.setInput(this.input); + exception.setInput(this.buffer.ToArray()); throw exception; } - this.readInt16(); - if (stanzaSize > 0) + this.readInt24(); + //if (stanzaSize > 0) + if (size > 0) { + if (isEncrypted && Encryptionkey != null) + { + byte[] data = this.buffer.ToArray(); + byte[] hashServerByte = new byte[4]; + byte[] packet = new byte[data.Length - 4]; + Buffer.BlockCopy(data, 0, hashServerByte, 0, 4); + Buffer.BlockCopy(data, 4, packet, 0, data.Length - 4); + + System.Security.Cryptography.HMACSHA1 h = new System.Security.Cryptography.HMACSHA1(this.Encryptionkey); + byte[] hashByte = new byte[4]; + Buffer.BlockCopy(h.ComputeHash(packet, 0, packet.Length), 0, hashByte, 0, 4); + + byte[] dataReal = Encryption.WhatsappDecrypt(this.Encryptionkey, packet); + + this.buffer.Clear(); + this.buffer.AddRange(dataReal); + } return this.nextTreeInternal(); } return null; @@ -54,20 +127,66 @@ protected string getToken(int token) return ret; } - protected string readString(int token) + //protected string readString(int token) + //{ + // string ret = ""; + // if (token == -1) + // { + // throw new Exception("BinTreeNodeReader->readString: Invalid token $token"); + // } + // if ((token > 4) && (token < 0xf5)) + // { + // ret = this.getToken(token); + // } + // else if (token == 0) + // { + // ret = ""; + // } + // else if (token == 0xfc) + // { + // int size = this.readInt8(); + // ret = WhatsApp.SYSEncoding.GetString(this.fillArray(size)); + // } + // else if (token == 0xfd) + // { + // int size = this.readInt24(); + // ret = WhatsApp.SYSEncoding.GetString(this.fillArray(size)); + // } + // else if (token == 0xfe) + // { + // int tmpToken = this.readInt8(); + // ret = this.getToken(tmpToken + 0xf5); + // } + // else if (token == 0xfa) + // { + // string user = this.readString(this.readInt8()); + // string server = this.readString(this.readInt8()); + // if ((user.Length > 0) && (server.Length > 0)) + // { + // ret = user + "@" + server; + // } + // else if (server.Length > 0) + // { + // ret = server; + // } + // } + // return ret; + //} + + protected byte[] readBytes(int token) { - string ret = ""; + byte[] ret = new byte[0]; if (token == -1) { throw new Exception("BinTreeNodeReader->readString: Invalid token $token"); } if ((token > 4) && (token < 0xf5)) { - ret = this.getToken(token); + ret = WhatsApp.SYSEncoding.GetBytes(this.getToken(token)); } else if (token == 0) { - ret = ""; + //ret = ""; } else if (token == 0xfc) { @@ -82,19 +201,19 @@ protected string readString(int token) else if (token == 0xfe) { int tmpToken = this.readInt8(); - ret = this.getToken(tmpToken + 0xf5); + ret = WhatsApp.SYSEncoding.GetBytes(this.getToken(tmpToken + 0xf5)); } else if (token == 0xfa) { - string user = this.readString(this.readInt8()); - string server = this.readString(this.readInt8()); + string user = WhatsApp.SYSEncoding.GetString(this.readBytes(this.readInt8())); + string server = WhatsApp.SYSEncoding.GetString(this.readBytes(this.readInt8())); if ((user.Length > 0) && (server.Length > 0)) { - ret = user + "@" + server; + ret = WhatsApp.SYSEncoding.GetBytes(user + "@" + server); } else if (server.Length > 0) { - ret = server; + ret = WhatsApp.SYSEncoding.GetBytes(server); } } return ret; @@ -103,11 +222,13 @@ protected string readString(int token) protected IEnumerable readAttributes(int size) { var attributes = new List(); - int attribCount = (size - 2 + size%2)/2; + int attribCount = (size - 2 + size % 2) / 2; for (int i = 0; i < attribCount; i++) { - string key = this.readString(this.readInt8()); - string value = this.readString(this.readInt8()); + byte[] keyB = this.readBytes(this.readInt8()); + byte[] valueB = this.readBytes(this.readInt8()); + string key = WhatsApp.SYSEncoding.GetString(keyB); + string value = WhatsApp.SYSEncoding.GetString(valueB); attributes.Add(new KeyValue(key, value)); } return attributes; @@ -128,19 +249,23 @@ protected ProtocolTreeNode nextTreeInternal() { return null; } - string tag = this.readString(token); + //string tag = this.readString(token); + byte[] tagB = this.readBytes(token); + string tag = WhatsApp.SYSEncoding.GetString(tagB); var tmpAttributes = this.readAttributes(size); - if ((size%2) == 1) + if ((size % 2) == 1) { return new ProtocolTreeNode(tag, tmpAttributes); } token = this.readInt8(); if (this.isListTag(token)) { - return new ProtocolTreeNode(tag, tmpAttributes, this.readList(token), ""); + //return new ProtocolTreeNode(tag, tmpAttributes, this.readList(token), ""); + return new ProtocolTreeNode(tag, tmpAttributes, this.readList(token), null); } - return new ProtocolTreeNode(tag, tmpAttributes, this.readString(token)); + //return new ProtocolTreeNode(tag, tmpAttributes, WhatsApp.SYSEncoding.GetBytes(this.readString(token))); + return new ProtocolTreeNode(tag, tmpAttributes, this.readBytes(token)); } protected bool isListTag(int token) @@ -177,60 +302,127 @@ protected int readListSize(int token) return size; } + protected int peekInt24() + { + int ret = 0; + //if (this.input.Length >= 3) + //{ + // ret = (int)this.input[0] << 16; + // ret |= (int)this.input[1] << 8; + // ret |= (int)this.input[2] << 0; + //} + if (this.buffer.Count >= 3) + { + ret = this.buffer[0] << 16; + ret |= this.buffer[1] << 8; + ret |= this.buffer[2] << 0; + } + return ret; + } + protected int readInt24() { int ret = 0; - if (this.input.Length >= 3) + //if (this.input.Length >= 3) + //{ + // ret = (int)this.input[0] << 16; + // ret |= (int)this.input[1] << 8; + // ret |= (int)this.input[2] << 0; + // this.input = this.input.Remove(0, 3); + //} + if (this.buffer.Count >= 3) { - ret = (int) this.input[0] << 16; - ret |= (int) this.input[1] << 8; - ret |= (int) this.input[2] << 0; - this.input = this.input.Remove(0, 3); + ret = this.buffer[0] << 16; + ret |=this.buffer[1] << 8; + ret |=this.buffer[2] << 0; + this.buffer.RemoveRange(0, 3); } return ret; } + //protected int peekInt16() + //{ + // int ret = 0; + // if (this.input.Length >= 2) + // { + // ret = (int)this.input[0] << 8; + // ret |= (int)this.input[1] << 0; + // } + // return ret; + //} protected int peekInt16() { int ret = 0; - if (this.input.Length >= 2) + if (this.buffer.Count >= 2) { - ret = (int) this.input[0] << 8; - ret |= (int) this.input[1] << 0; + ret = (int)this.buffer[0] << 8; + ret |= (int)this.buffer[1] << 0; } return ret; } + //protected int readInt16() + //{ + // int ret = 0; + // if (this.input.Length >= 2) + // { + // ret = (int)this.input[0] << 8; + // ret |= (int)this.input[1] << 0; + // this.input = this.input.Remove(0, 2); + // } + // return ret; + //} protected int readInt16() { int ret = 0; - if (this.input.Length >= 2) + if (this.buffer.Count >= 2) { - ret = (int)this.input[0] << 8; - ret |= (int)this.input[1] << 0; - this.input = this.input.Remove(0, 2); + ret = (int)this.buffer[0] << 8; + ret |= (int)this.buffer[1] << 0; + this.buffer.RemoveRange(0, 2); } return ret; } + //protected int readInt8() + //{ + // int ret = 0; + // if (this.input.Length >= 1) + // { + // ret = (int)this.input[0]; + // this.input = this.input.Remove(0, 1); + // } + // return ret; + //} protected int readInt8() { int ret = 0; - if (this.input.Length >= 1) + if (this.buffer.Count >= 1) { - ret = (int) this.input[0]; - this.input = this.input.Remove(0, 1); + ret = (int)this.buffer[0]; + this.buffer.RemoveAt(0); } return ret; } - protected string fillArray(int len) + //protected string fillArray(int len) + //{ + // string ret = ""; + // if (this.input.Length >= len) + // { + // ret = this.input.Substring(0, len); + // this.input = this.input.Remove(0, len); + // } + // return ret; + //} + protected byte[] fillArray(int len) { - string ret = ""; - if (this.input.Length >= len) + byte[] ret = new byte[len]; + if (this.buffer.Count >= len) { - ret = this.input.Substring(0, len); - this.input = this.input.Remove(0, len); + //this.buffer.CopyTo(0, ret, 0, len); + Buffer.BlockCopy(this.buffer.ToArray(), 0, ret, 0, len); + this.buffer.RemoveRange(0, len); } return ret; } diff --git a/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs b/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs index a75b5b2..d090b02 100644 --- a/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs +++ b/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs @@ -7,9 +7,13 @@ namespace WhatsAppApi.Helper { internal class BinTreeNodeWriter { - private string output; + //private string output; + private List buffer; private Dictionary tokenMap; + //change to protocol 1.2 + public byte[] Encryptionkey { get; set; } + public BinTreeNodeWriter(string[] dict) { this.tokenMap = new Dictionary(); @@ -23,47 +27,115 @@ public BinTreeNodeWriter(string[] dict) this.tokenMap[dict[i]] = i; } } + + buffer = new List(); } - public string StartStream(string domain, string resource) + //public string StartStream(string domain, string resource) + //{ + // var attributes = new List(); + // this.output = "WA"; + // //this.output += "\x01" + "\x01" + "\x00" + "\x19"; + + // attributes.Add(new KeyValue("to", domain)); + // attributes.Add(new KeyValue("resource", resource)); + // this.writeListStart(attributes.Count*2 + 1); + + // this.output += "\x01"; + // this.writeAttributes(attributes.ToArray()); + // string ret = this.output; + // this.output = ""; + // return ret; + //} + + public byte[] StartStream(string domain, string resource) { var attributes = new List(); - this.output = "WA"; - this.output += "\x01" + "\x01" +"\x00" + "\x19"; - + // protocol 1.2 + this.buffer = new List(); + attributes.Add(new KeyValue("to", domain)); attributes.Add(new KeyValue("resource", resource)); - this.writeListStart(attributes.Count*2 + 1); + this.writeListStart(attributes.Count * 2 + 1); - this.output += "\x01"; + this.buffer.Add(1); this.writeAttributes(attributes.ToArray()); - string ret = this.output; - this.output = ""; + + byte[] ret = this.flushBuffer(); + this.buffer.Add((byte)'W'); + this.buffer.Add((byte)'A'); + this.buffer.Add(0x1); + this.buffer.Add(0x2); + this.buffer.AddRange(ret); + ret = buffer.ToArray(); + this.buffer = new List(); return ret; } - public string Write(ProtocolTreeNode node) + //public string Write(ProtocolTreeNode node) + //{ + // if (node == null) + // { + // this.output += "\x00"; + // } + // else + // { + // this.writeInternal(node); + // } + // return this.flushBuffer(); + //} + public byte[] Write(ProtocolTreeNode node, bool encrypt = true) { if (node == null) { - this.output += "\x00"; + this.buffer.Add(0); } else { this.writeInternal(node); } - return this.flushBuffer(); + return this.flushBuffer(encrypt); } - protected string flushBuffer() + //protected string flushBuffer() + //{ + // int size = this.output.Length; + // this.output = this.GetInt16(size) + this.output; + // string ret = this.output; + // this.output = ""; + // return ret; + //} + + //change to protocol 1.2 + protected byte[] flushBuffer(bool encrypt = true) { - int size = this.output.Length; - this.output = this.GetInt16(size) + this.output; - string ret = this.output; - this.output = ""; + byte[] data = this.buffer.ToArray(); + byte[] ret = new byte[data.Length + 3]; + //byte[] ret = new byte[256]; + byte[] size = this.GetInt24(data.Length); + Buffer.BlockCopy(size, 0, ret, 0, 3); + + if (encrypt && this.Encryptionkey != null) + { + data = Encryption.WhatsappEncrypt(Encryptionkey, data, true); + size[0] |= 0x8; + } + Buffer.BlockCopy(data, 0, ret,3, data.Length); + this.buffer = new List(); return ret; } + //protected void writeAttributes(IEnumerable attributes) + //{ + // if (attributes != null) + // { + // foreach (var item in attributes) + // { + // this.writeString(item.Key); + // this.writeString(item.Value); + // } + // } + //} protected void writeAttributes(IEnumerable attributes) { if (attributes != null) @@ -76,47 +148,104 @@ protected void writeAttributes(IEnumerable attributes) } } - private string GetInt16(int len) + //private string GetInt16(int len) + //{ + // string ret = chr((len & 0xff00) >> 8); + // ret += chr(len & 0x00ff); + // return ret; + //} + private byte[] GetInt16(int len) + { + byte[] ret = new byte[2]; + ret[0] = (byte)((len & 0xff00) >> 8); + ret[1] = (byte)(len & 0x00ff); + return ret; + } + + //private string GetInt24(int len) + //{ + // string ret = chr((len & 0xf0000) >> 16); + // ret += chr((len & 0xff00) >> 8); + // ret += chr(len & 0x00ff); + // return ret; + //} + private byte[] GetInt24(int len) { - string ret = chr((len & 0xff00) >> 8); - ret += chr((len & 0x00ff) >> 0); + byte[] ret = new byte[3]; + ret[0] = (byte)((len & 0xf0000) >> 16); + ret[1] = (byte)((len & 0xff00) >> 8); + ret[2] = (byte)(len & 0xff); return ret; } + //protected void writeBytes(string bytes) + //{ + // int len = bytes.Length; + // if (len >= 0x100) + // { + // this.output += "\xfd"; + // this.writeInt24(len); + // } + // else + // { + // this.output += "\xfc"; + // this.writeInt8(len); + // } + // this.output += bytes; + //} protected void writeBytes(string bytes) + { + writeBytes(WhatsApp.SYSEncoding.GetBytes(bytes)); + } + protected void writeBytes(byte[] bytes) { int len = bytes.Length; if (len >= 0x100) { - this.output += "\xfd"; + this.buffer.Add(0xfd); this.writeInt24(len); } else { - this.output += "\xfc"; + this.buffer.Add(0xfc); this.writeInt8(len); } - this.output += bytes; + this.buffer.AddRange(bytes); } + //protected void writeInt16(int v) + //{ + // //string ret = ""; + // this.output += chr((v & 0xff00) >> 8); + // this.output += chr((v & 0x00ff) >> 0); + // //this.output = ret + this.output; + //} protected void writeInt16(int v) { - //string ret = ""; - this.output += chr((v & 0xff00) >> 8); - this.output += chr((v & 0x00ff) >> 0); - //this.output = ret + this.output; + this.buffer.Add((byte)((v & 0xff00) >> 8)); + this.buffer.Add((byte)(v & 0x00ff)); } + //protected void writeInt24(int v) + //{ + // this.output += chr((v & 0xff0000) >> 16); + // this.output += chr((v & 0x00ff00) >> 8); + // this.output += chr((v & 0x0000ff) >> 0); + //} protected void writeInt24(int v) { - this.output += chr((v & 0xff0000) >> 16); - this.output += chr((v & 0x00ff00) >> 8); - this.output += chr((v & 0x0000ff) >> 0); + this.buffer.Add((byte)((v & 0xff0000) >> 16)); + this.buffer.Add((byte)((v & 0x00ff00) >> 8)); + this.buffer.Add((byte)(v & 0x0000ff)); } + //protected void writeInt8(int v) + //{ + // this.output += chr(v & 0xff); + //} protected void writeInt8(int v) { - this.output += chr(v & 0xff); + this.buffer.Add((byte)(v & 0xff)); } protected void writeInternal(ProtocolTreeNode node) @@ -124,7 +253,7 @@ protected void writeInternal(ProtocolTreeNode node) int len = 1; if (node.attributeHash != null) { - len += node.attributeHash.Count()*2; + len += node.attributeHash.Count() * 2; } if (node.children.Any()) { @@ -152,7 +281,8 @@ protected void writeInternal(ProtocolTreeNode node) } protected void writeJid(string user, string server) { - this.output += "\xfa"; + //this.output += "\xfa"; + this.buffer.Add(0xfa); if (user.Length > 0) { this.writeString(user); @@ -168,16 +298,19 @@ protected void writeListStart(int len) { if (len == 0) { - this.output += "\x00"; + //this.output += "\x00"; + this.buffer.Add(0x00); } else if (len < 256) { - this.output += "\xf8"; + //this.output += "\xf8"; + this.buffer.Add(0xf8); this.writeInt8(len); } else { - this.output += "\xf9"; + //this.output += "\xf9"; + this.buffer.Add(0xf9); this.writeInt16(len); } } @@ -200,20 +333,33 @@ protected void writeString(string tag) } else { + //this.writeBytes(tag); this.writeBytes(tag); } } } + //protected void writeToken(int token) + //{ + // if (token < 0xf5) + // { + // this.output += chr(token); + // } + // else if (token <= 0x1f4) + // { + // this.output += "\xfe" + chr(token - 0xf5); + // } + //} protected void writeToken(int token) { if (token < 0xf5) { - this.output += chr(token); + this.buffer.Add((byte)token); } else if (token <= 0x1f4) { - this.output += "\xfe" + chr(token - 0xf5); + this.buffer.Add(0xfe); + this.buffer.Add((byte)(token - 0xf5)); } } diff --git a/src/WhatsAppApi/Helper/DecodeHelper.cs b/src/WhatsAppApi/Helper/DecodeHelper.cs index 0de7e98..849e607 100644 --- a/src/WhatsAppApi/Helper/DecodeHelper.cs +++ b/src/WhatsAppApi/Helper/DecodeHelper.cs @@ -39,256 +39,496 @@ public static string[] getDictionary() if (dictList != null) return dictList; - dictList = new string[249]; + //dictList = new string[249]; + //dictList[0] = null; + //dictList[1] = null; + //dictList[2] = null; + //dictList[3] = null; + //dictList[4] = null; + //dictList[5] = "1"; + //dictList[6] = "1.0"; + //dictList[7] = "ack"; + //dictList[8] = "action"; + //dictList[9] = "active"; + //dictList[10] = "add"; + //dictList[11] = "all"; + //dictList[12] = "allow"; + //dictList[13] = "apple"; + //dictList[14] = "audio"; + //dictList[15] = "auth"; + //dictList[16] = "author"; + //dictList[17] = "available"; + //dictList[18] = "bad-request"; + //dictList[19] = "basee64"; + //dictList[20] = "Bell.caf"; + //dictList[21] = "bind"; + //dictList[22] = "body"; + //dictList[23] = "Boing.caf"; + //dictList[24] = "cancel"; + //dictList[25] = "category"; + //dictList[26] = "challenge"; + //dictList[27] = "chat"; + //dictList[28] = "clean"; + //dictList[29] = "code"; + //dictList[30] = "composing"; + //dictList[31] = "config"; + //dictList[32] = "conflict"; + //dictList[33] = "contacts"; + //dictList[34] = "create"; + //dictList[35] = "creation"; + //dictList[36] = "default"; + //dictList[37] = "delay"; + //dictList[38] = "delete"; + //dictList[39] = "delivered"; + //dictList[40] = "deny"; + //dictList[41] = "DIGEST-MD5"; + //dictList[42] = "DIGEST-MD5-1"; + //dictList[43] = "dirty"; + //dictList[44] = "en"; + //dictList[45] = "enable"; + //dictList[46] = "encoding"; + //dictList[47] = "error"; + //dictList[48] = "expiration"; + //dictList[49] = "expired"; + //dictList[50] = "failure"; + //dictList[51] = "false"; + //dictList[52] = "favorites"; + //dictList[53] = "feature"; + //dictList[54] = "field"; + //dictList[55] = "free"; + //dictList[56] = "from"; + //dictList[57] = "g.us"; + //dictList[58] = "get"; + //dictList[59] = "Glas.caf"; + //dictList[60] = "google"; + //dictList[61] = "group"; + //dictList[62] = "groups"; + //dictList[63] = "g_sound"; + //dictList[64] = "Harp.caf"; + //dictList[65] = "http://etherx.jabber.org/streams"; + //dictList[66] = "http://jabber.org/protocol/chatstates"; + //dictList[67] = "id"; + //dictList[68] = "image"; + //dictList[69] = "img"; + //dictList[70] = "inactive"; + //dictList[71] = "internal-server-error"; + //dictList[72] = "iq"; + //dictList[73] = "item"; + //dictList[74] = "item-not-found"; + //dictList[75] = "jabber:client"; + //dictList[76] = "jabber:iq:last"; + //dictList[77] = "jabber:iq:privacy"; + //dictList[78] = "jabber:x:delay"; + //dictList[79] = "jabber:x:event"; + //dictList[80] = "jid"; + //dictList[81] = "jid-malformed"; + //dictList[82] = "kind"; + //dictList[83] = "leave"; + //dictList[84] = "leave-all"; + //dictList[85] = "list"; + //dictList[86] = "location"; + //dictList[87] = "max_groups"; + //dictList[88] = "max_participants"; + //dictList[89] = "max_subject"; + //dictList[90] = "mechanism"; + //dictList[91] = "mechanisms"; + //dictList[92] = "media"; + //dictList[93] = "message"; + //dictList[94] = "message_acks"; + //dictList[95] = "missing"; + //dictList[96] = "modify"; + //dictList[97] = "name"; + //dictList[98] = "not-acceptable"; + //dictList[99] = "not-allowed"; + //dictList[100] = "not-authorized"; + //dictList[101] = "notify"; + //dictList[102] = "Offline Storage"; + //dictList[103] = "order"; + //dictList[104] = "owner"; + //dictList[105] = "owning"; + //dictList[106] = "paid"; + //dictList[107] = "participant"; + //dictList[108] = "participants"; + //dictList[109] = "participating"; + //dictList[110] = "fail"; + //dictList[111] = "paused"; + //dictList[112] = "picture"; + //dictList[113] = "ping"; + //dictList[114] = "PLAIN"; + //dictList[115] = "platform"; + //dictList[116] = "presence"; + //dictList[117] = "preview"; + //dictList[118] = "probe"; + //dictList[119] = "prop"; + //dictList[120] = "props"; + //dictList[121] = "p_o"; + //dictList[122] = "p_t"; + //dictList[123] = "query"; + //dictList[124] = "raw"; + //dictList[125] = "receipt"; + //dictList[126] = "receipt_acks"; + //dictList[127] = "received"; + //dictList[128] = "relay"; + //dictList[129] = "remove"; + //dictList[130] = "Replaced by new connection"; + //dictList[131] = "request"; + //dictList[132] = "resource"; + //dictList[133] = "resource-constraint"; + //dictList[134] = "response"; + //dictList[135] = "result"; + //dictList[136] = "retry"; + //dictList[137] = "rim"; + //dictList[138] = "s.whatsapp.net"; + //dictList[139] = "seconds"; + //dictList[140] = "server"; + //dictList[141] = "session"; + //dictList[142] = "set"; + //dictList[143] = "show"; + //dictList[144] = "sid"; + //dictList[145] = "sound"; + //dictList[146] = "stamp"; + //dictList[147] = "starttls"; + //dictList[148] = "status"; + //dictList[149] = "stream:error"; + //dictList[150] = "stream:features"; + //dictList[151] = "subject"; + //dictList[152] = "subscribe"; + //dictList[153] = "success"; + //dictList[154] = "system-shutdown"; + //dictList[155] = "s_o"; + //dictList[156] = "s_t"; + //dictList[157] = "t"; + //dictList[158] = "TimePassing.caf"; + //dictList[159] = "timestamp"; + //dictList[160] = "to"; + //dictList[161] = "Tri-tone.caf"; + //dictList[162] = "type"; + //dictList[163] = "unavailable"; + //dictList[164] = "uri"; + //dictList[165] = "url"; + //dictList[166] = "urn:ietf:params:xml:ns:xmpp-bind"; + //dictList[167] = "urn:ietf:params:xml:ns:xmpp-sasl"; + //dictList[168] = "urn:ietf:params:xml:ns:xmpp-session"; + //dictList[169] = "urn:ietf:params:xml:ns:xmpp-stanzas"; + //dictList[170] = "urn:ietf:params:xml:ns:xmpp-streams"; + //dictList[171] = "urn:xmpp:delay"; + //dictList[172] = "urn:xmpp:ping"; + //dictList[173] = "urn:xmpp:receipts"; + //dictList[174] = "urn:xmpp:whatsapp"; + //dictList[175] = "urn:xmpp:whatsapp:dirty"; + //dictList[176] = "urn:xmpp:whatsapp:mms"; + //dictList[177] = "urn:xmpp:whatsapp:push"; + //dictList[178] = "value"; + //dictList[179] = "vcard"; + //dictList[180] = "version"; + //dictList[181] = "video"; + //dictList[182] = "w"; + //dictList[183] = "w:g"; + //dictList[184] = "w:p:r"; + //dictList[185] = "wait"; + //dictList[186] = "x"; + //dictList[187] = "xml-not-well-formed"; + //dictList[188] = "xml:lang"; + //dictList[189] = "xmlns"; + //dictList[190] = "xmlns:stream"; + //dictList[191] = "Xylophone.caf"; + //dictList[192] = "account"; + //dictList[193] = "digest"; + //dictList[194] = "g_notify"; + //dictList[195] = "method"; + //dictList[196] = "password"; + //dictList[197] = "registration"; + //dictList[198] = "stat"; + //dictList[199] = "text"; + //dictList[200] = "user"; + //dictList[201] = "username"; + //dictList[202] = "event"; + //dictList[203] = "latitude"; + //dictList[204] = "longitude"; + //dictList[205] = "true"; + //dictList[206] = "after"; + //dictList[207] = "before"; + //dictList[208] = "broadcast"; + //dictList[209] = "count"; + //dictList[210] = "features"; + //dictList[211] = "first"; + //dictList[212] = "index"; + //dictList[213] = "invalid-mechanism"; + //dictList[214] = "ldictListt"; + //dictList[215] = "max"; + //dictList[216] = "offline"; + //dictList[217] = "proceed"; + //dictList[218] = "required"; + //dictList[219] = "sync"; + //dictList[220] = "elapsed"; + //dictList[221] = "ip"; + //dictList[222] = "microsoft"; + //dictList[223] = "mute"; + //dictList[224] = "nokia"; + //dictList[225] = "off"; + //dictList[226] = "pin"; + //dictList[227] = "pop_mean_time"; + //dictList[228] = "pop_plus_minus"; + //dictList[229] = "port"; + //dictList[230] = "reason"; + //dictList[231] = "server-error"; + //dictList[232] = "silent"; + //dictList[233] = "timeout"; + //dictList[234] = "lc"; + //dictList[235] = "lg"; + //dictList[236] = "bad-protocol"; + //dictList[237] = "none"; + //dictList[238] = "remote-server-timeout"; + //dictList[239] = "service-unavailable"; + //dictList[240] = "w:p"; + //dictList[241] = "w:profile:picture"; + //dictList[242] = "notification"; + //dictList[243] = null; + //dictList[244] = null; + //dictList[245] = null; + //dictList[246] = null; + //dictList[247] = null; + //dictList[248] = "XXX"; + + // change to protocol 1.2 + dictList = new string[237]; dictList[0] = null; dictList[1] = null; dictList[2] = null; dictList[3] = null; dictList[4] = null; - dictList[5] = "1"; - dictList[6] = "1.0"; - dictList[7] = "ack"; - dictList[8] = "action"; - dictList[9] = "active"; - dictList[10] = "add"; - dictList[11] = "all"; - dictList[12] = "allow"; - dictList[13] = "apple"; - dictList[14] = "audio"; - dictList[15] = "auth"; - dictList[16] = "author"; - dictList[17] = "available"; - dictList[18] = "bad-request"; - dictList[19] = "basee64"; - dictList[20] = "Bell.caf"; - dictList[21] = "bind"; - dictList[22] = "body"; - dictList[23] = "Boing.caf"; - dictList[24] = "cancel"; - dictList[25] = "category"; - dictList[26] = "challenge"; - dictList[27] = "chat"; - dictList[28] = "clean"; - dictList[29] = "code"; - dictList[30] = "composing"; - dictList[31] = "config"; - dictList[32] = "conflict"; - dictList[33] = "contacts"; - dictList[34] = "create"; - dictList[35] = "creation"; - dictList[36] = "default"; - dictList[37] = "delay"; - dictList[38] = "delete"; - dictList[39] = "delivered"; - dictList[40] = "deny"; - dictList[41] = "DIGEST-MD5"; - dictList[42] = "DIGEST-MD5-1"; - dictList[43] = "dirty"; - dictList[44] = "en"; - dictList[45] = "enable"; - dictList[46] = "encoding"; - dictList[47] = "error"; - dictList[48] = "expiration"; - dictList[49] = "expired"; - dictList[50] = "failure"; - dictList[51] = "false"; - dictList[52] = "favorites"; - dictList[53] = "feature"; - dictList[54] = "field"; - dictList[55] = "free"; - dictList[56] = "from"; - dictList[57] = "g.us"; - dictList[58] = "get"; - dictList[59] = "Glas.caf"; - dictList[60] = "google"; - dictList[61] = "group"; - dictList[62] = "groups"; - dictList[63] = "g_sound"; - dictList[64] = "Harp.caf"; - dictList[65] = "http://etherx.jabber.org/streams"; - dictList[66] = "http://jabber.org/protocol/chatstates"; - dictList[67] = "id"; - dictList[68] = "image"; - dictList[69] = "img"; - dictList[70] = "inactive"; - dictList[71] = "internal-server-error"; - dictList[72] = "iq"; - dictList[73] = "item"; - dictList[74] = "item-not-found"; - dictList[75] = "jabber:client"; - dictList[76] = "jabber:iq:last"; - dictList[77] = "jabber:iq:privacy"; - dictList[78] = "jabber:x:delay"; - dictList[79] = "jabber:x:event"; - dictList[80] = "jid"; - dictList[81] = "jid-malformed"; - dictList[82] = "kind"; - dictList[83] = "leave"; - dictList[84] = "leave-all"; - dictList[85] = "list"; - dictList[86] = "location"; - dictList[87] = "max_groups"; - dictList[88] = "max_participants"; - dictList[89] = "max_subject"; - dictList[90] = "mechanism"; - dictList[91] = "mechanisms"; - dictList[92] = "media"; - dictList[93] = "message"; - dictList[94] = "message_acks"; - dictList[95] = "missing"; - dictList[96] = "modify"; - dictList[97] = "name"; - dictList[98] = "not-acceptable"; - dictList[99] = "not-allowed"; - dictList[100] = "not-authorized"; - dictList[101] = "notify"; - dictList[102] = "Offline Storage"; - dictList[103] = "order"; - dictList[104] = "owner"; - dictList[105] = "owning"; - dictList[106] = "paid"; - dictList[107] = "participant"; - dictList[108] = "participants"; - dictList[109] = "participating"; - dictList[110] = "fail"; - dictList[111] = "paused"; - dictList[112] = "picture"; - dictList[113] = "ping"; - dictList[114] = "PLAIN"; - dictList[115] = "platform"; - dictList[116] = "presence"; - dictList[117] = "preview"; - dictList[118] = "probe"; - dictList[119] = "prop"; - dictList[120] = "props"; - dictList[121] = "p_o"; - dictList[122] = "p_t"; - dictList[123] = "query"; - dictList[124] = "raw"; - dictList[125] = "receipt"; - dictList[126] = "receipt_acks"; - dictList[127] = "received"; - dictList[128] = "relay"; - dictList[129] = "remove"; - dictList[130] = "Replaced by new connection"; - dictList[131] = "request"; - dictList[132] = "resource"; - dictList[133] = "resource-constraint"; - dictList[134] = "response"; - dictList[135] = "result"; - dictList[136] = "retry"; - dictList[137] = "rim"; - dictList[138] = "s.whatsapp.net"; - dictList[139] = "seconds"; - dictList[140] = "server"; - dictList[141] = "session"; - dictList[142] = "set"; - dictList[143] = "show"; - dictList[144] = "sid"; - dictList[145] = "sound"; - dictList[146] = "stamp"; - dictList[147] = "starttls"; - dictList[148] = "status"; - dictList[149] = "stream:error"; - dictList[150] = "stream:features"; - dictList[151] = "subject"; - dictList[152] = "subscribe"; - dictList[153] = "success"; - dictList[154] = "system-shutdown"; - dictList[155] = "s_o"; - dictList[156] = "s_t"; - dictList[157] = "t"; - dictList[158] = "TimePassing.caf"; - dictList[159] = "timestamp"; - dictList[160] = "to"; - dictList[161] = "Tri-tone.caf"; - dictList[162] = "type"; - dictList[163] = "unavailable"; - dictList[164] = "uri"; - dictList[165] = "url"; - dictList[166] = "urn:ietf:params:xml:ns:xmpp-bind"; - dictList[167] = "urn:ietf:params:xml:ns:xmpp-sasl"; - dictList[168] = "urn:ietf:params:xml:ns:xmpp-session"; - dictList[169] = "urn:ietf:params:xml:ns:xmpp-stanzas"; - dictList[170] = "urn:ietf:params:xml:ns:xmpp-streams"; - dictList[171] = "urn:xmpp:delay"; - dictList[172] = "urn:xmpp:ping"; - dictList[173] = "urn:xmpp:receipts"; - dictList[174] = "urn:xmpp:whatsapp"; - dictList[175] = "urn:xmpp:whatsapp:dirty"; - dictList[176] = "urn:xmpp:whatsapp:mms"; - dictList[177] = "urn:xmpp:whatsapp:push"; - dictList[178] = "value"; - dictList[179] = "vcard"; - dictList[180] = "version"; - dictList[181] = "video"; - dictList[182] = "w"; - dictList[183] = "w:g"; - dictList[184] = "w:p:r"; - dictList[185] = "wait"; - dictList[186] = "x"; - dictList[187] = "xml-not-well-formed"; - dictList[188] = "xml:lang"; - dictList[189] = "xmlns"; - dictList[190] = "xmlns:stream"; - dictList[191] = "Xylophone.caf"; - dictList[192] = "account"; - dictList[193] = "digest"; - dictList[194] = "g_notify"; - dictList[195] = "method"; - dictList[196] = "password"; - dictList[197] = "registration"; - dictList[198] = "stat"; - dictList[199] = "text"; - dictList[200] = "user"; - dictList[201] = "username"; - dictList[202] = "event"; - dictList[203] = "latitude"; - dictList[204] = "longitude"; - dictList[205] = "true"; - dictList[206] = "after"; - dictList[207] = "before"; - dictList[208] = "broadcast"; - dictList[209] = "count"; - dictList[210] = "features"; - dictList[211] = "first"; - dictList[212] = "index"; - dictList[213] = "invalid-mechanism"; - dictList[214] = "ldictListt"; - dictList[215] = "max"; - dictList[216] = "offline"; - dictList[217] = "proceed"; - dictList[218] = "required"; - dictList[219] = "sync"; - dictList[220] = "elapsed"; - dictList[221] = "ip"; - dictList[222] = "microsoft"; - dictList[223] = "mute"; - dictList[224] = "nokia"; - dictList[225] = "off"; - dictList[226] = "pin"; - dictList[227] = "pop_mean_time"; - dictList[228] = "pop_plus_minus"; - dictList[229] = "port"; - dictList[230] = "reason"; - dictList[231] = "server-error"; - dictList[232] = "silent"; - dictList[233] = "timeout"; - dictList[234] = "lc"; - dictList[235] = "lg"; - dictList[236] = "bad-protocol"; - dictList[237] = "none"; - dictList[238] = "remote-server-timeout"; - dictList[239] = "service-unavailable"; - dictList[240] = "w:p"; - dictList[241] = "w:profile:picture"; - dictList[242] = "notification"; - dictList[243] = null; - dictList[244] = null; - dictList[245] = null; - dictList[246] = null; - dictList[247] = null; - dictList[248] = "XXX"; + dictList[5] = "account"; + dictList[6] = "ack"; + dictList[7] = "action"; + dictList[8] = "active"; + dictList[9] = "add"; + dictList[10] = "after"; + dictList[11] = "ib"; + dictList[12] = "all"; + dictList[13] = "allow"; + dictList[14] = "apple"; + dictList[15] = "audio"; + dictList[16] = "auth"; + dictList[17] = "author"; + dictList[18] = "available"; + dictList[19] = "bad-protocol"; + dictList[20] = "bad-request"; + dictList[21] = "before"; + dictList[22] = "Bell.caf"; + dictList[23] = "body"; + dictList[24] = "Boing.caf"; + dictList[25] = "cancel"; + dictList[26] = "category"; + dictList[27] = "challenge"; + dictList[28] = "chat"; + dictList[29] = "clean"; + dictList[30] = "code"; + dictList[31] = "composing"; + dictList[32] = "config"; + dictList[33] = "conflict"; + dictList[34] = "contacts"; + dictList[35] = "count"; + dictList[36] = "create"; + dictList[37] = "creation"; + dictList[38] = "default"; + dictList[39] = "delay"; + dictList[40] = "delete"; + dictList[41] = "delivered"; + dictList[42] = "deny"; + dictList[43] = "digest"; + dictList[44] = "DIGEST-MD5-1"; + dictList[45] = "DIGEST-MD5-2"; + dictList[46] = "dirty"; + dictList[47] = "elapsed"; + dictList[48] = "broadcast"; + dictList[49] = "enable"; + dictList[50] = "encoding"; + dictList[51] = "duplicate"; + dictList[52] = "error"; + dictList[53] = "event"; + dictList[54] = "expiration"; + dictList[55] = "expired"; + dictList[56] = "fail"; + dictList[57] = "failure"; + dictList[58] = "false"; + dictList[59] = "favorites"; + dictList[60] = "feature"; + dictList[61] = "features"; + dictList[62] = "field"; + dictList[63] = "first"; + dictList[64] = "free"; + dictList[65] = "from"; + dictList[66] = "g.us"; + dictList[67] = "get"; + dictList[68] = "Glass.caf"; + dictList[69] = "google"; + dictList[70] = "group"; + dictList[71] = "groups"; + dictList[72] = "g_notify"; + dictList[73] = "g_sound"; + dictList[74] = "Harp.caf"; + dictList[75] = "http://etherx.jabber.org/streams"; + dictList[76] = "http://jabber.org/protocol/chatstates"; + dictList[77] = "id"; + dictList[78] = "image"; + dictList[79] = "img"; + dictList[80] = "inactive"; + dictList[81] = "index"; + dictList[82] = "internal-server-error"; + dictList[83] = "invalid-mechanism"; + dictList[84] = "ip"; + dictList[85] = "iq"; + dictList[86] = "item"; + dictList[87] = "item-not-found"; + dictList[88] = "user-not-found"; + dictList[89] = "jabber:iq:last"; + dictList[90] = "jabber:iq:privacy"; + dictList[91] = "jabber:x:delay"; + dictList[92] = "jabber:x:event"; + dictList[93] = "jid"; + dictList[94] = "jid-malformed"; + dictList[95] = "kind"; + dictList[96] = "last"; + dictList[97] = "latitude"; + dictList[98] = "lc"; + dictList[99] = "leave"; + dictList[100] = "leave-all"; + dictList[101] = "lg"; + dictList[102] = "list"; + dictList[103] = "location"; + dictList[104] = "longitude"; + dictList[105] = "max"; + dictList[106] = "max_groups"; + dictList[107] = "max_participants"; + dictList[108] = "max_subject"; + dictList[109] = "mechanism"; + dictList[110] = "media"; + dictList[111] = "message"; + dictList[112] = "message_acks"; + dictList[113] = "method"; + dictList[114] = "microsoft"; + dictList[115] = "missing"; + dictList[116] = "modify"; + dictList[117] = "mute"; + dictList[118] = "name"; + dictList[119] = "nokia"; + dictList[120] = "none"; + dictList[121] = "not-acceptable"; + dictList[122] = "not-allowed"; + dictList[123] = "not-authorized"; + dictList[124] = "notification"; + dictList[125] = "notify"; + dictList[126] = "off"; + dictList[127] = "offline"; + dictList[128] = "order"; + dictList[129] = "owner"; + dictList[130] = "owning"; + dictList[131] = "paid"; + dictList[132] = "participant"; + dictList[133] = "participants"; + dictList[134] = "participating"; + dictList[135] = "password"; + dictList[136] = "paused"; + dictList[137] = "picture"; + dictList[138] = "pin"; + dictList[139] = "ping"; + dictList[140] = "platform"; + dictList[141] = "pop_mean_time"; + dictList[142] = "pop_plus_minus"; + dictList[143] = "port"; + dictList[144] = "presence"; + dictList[145] = "preview"; + dictList[146] = "probe"; + dictList[147] = "proceed"; + dictList[148] = "prop"; + dictList[149] = "props"; + dictList[150] = "p_o"; + dictList[151] = "p_t"; + dictList[152] = "query"; + dictList[153] = "raw"; + dictList[154] = "reason"; + dictList[155] = "receipt"; + dictList[156] = "receipt_acks"; + dictList[157] = "received"; + dictList[158] = "registration"; + dictList[159] = "relay"; + dictList[160] = "remote-server-timeout"; + dictList[161] = "remove"; + dictList[162] = "Replaced by new connection"; + dictList[163] = "request"; + dictList[164] = "required"; + dictList[165] = "resource"; + dictList[166] = "resource-constraint"; + dictList[167] = "response"; + dictList[168] = "result"; + dictList[169] = "retry"; + dictList[170] = "rim"; + dictList[171] = "s.whatsapp.net"; + dictList[172] = "s.us"; + dictList[173] = "seconds"; + dictList[174] = "server"; + dictList[175] = "server-error"; + dictList[176] = "service-unavailable"; + dictList[177] = "set"; + dictList[178] = "show"; + dictList[179] = "sid"; + dictList[180] = "silent"; + dictList[181] = "sound"; + dictList[182] = "stamp"; + dictList[183] = "unsubscribe"; + dictList[184] = "stat"; + dictList[185] = "status"; + dictList[186] = "stream:error"; + dictList[187] = "stream:features"; + dictList[188] = "subject"; + dictList[189] = "subscribe"; + dictList[190] = "success"; + dictList[191] = "sync"; + dictList[192] = "system-shutdown"; + dictList[193] = "s_o"; + dictList[194] = "s_t"; + dictList[195] = "t"; + dictList[196] = "text"; + dictList[197] = "timeout"; + dictList[198] = "TimePassing.caf"; + dictList[199] = "timestamp"; + dictList[200] = "to"; + dictList[201] = "Tri-tone.caf"; + dictList[202] = "true"; + dictList[203] = "type"; + dictList[204] = "unavailable"; + dictList[205] = "uri"; + dictList[206] = "url"; + dictList[207] = "urn:ietf:params:xml:ns:xmpp-sasl"; + dictList[208] = "urn:ietf:params:xml:ns:xmpp-stanzas"; + dictList[209] = "urn:ietf:params:xml:ns:xmpp-streams"; + dictList[210] = "urn:xmpp:delay"; + dictList[211] = "urn:xmpp:ping"; + dictList[212] = "urn:xmpp:receipts"; + dictList[213] = "urn:xmpp:whatsapp"; + dictList[214] = "urn:xmpp:whatsapp:account"; + dictList[215] = "urn:xmpp:whatsapp:dirty"; + dictList[216] = "urn:xmpp:whatsapp:mms"; + dictList[217] = "urn:xmpp:whatsapp:push"; + dictList[218] = "user"; + dictList[219] = "username"; + dictList[220] = "value"; + dictList[221] = "vcard"; + dictList[222] = "version"; + dictList[223] = "video"; + dictList[224] = "w"; + dictList[225] = "w:g"; + dictList[226] = "w:p"; + dictList[227] = "w:p:r"; + dictList[228] = "w:profile:picture"; + dictList[229] = "wait"; + dictList[230] = "x"; + dictList[231] = "xml-not-well-formed"; + dictList[232] = "xmlns"; + dictList[233] = "xmlns:stream"; + dictList[234] = "Xylophone.caf"; + dictList[235] = "1"; + dictList[236] = "WAUTH-1"; return dictList; } diff --git a/src/WhatsAppApi/Helper/Func.cs b/src/WhatsAppApi/Helper/Func.cs index 91a7e77..25e24c2 100644 --- a/src/WhatsAppApi/Helper/Func.cs +++ b/src/WhatsAppApi/Helper/Func.cs @@ -70,19 +70,18 @@ public static string HexString2Ascii(string hexString) // fclose($fp); //} - public static string EncodeTo64(string toEncode, Encoding enc) - { - byte[] toEncodeAsBytes = enc.GetBytes(toEncode); - string returnValue = System.Convert.ToBase64String(toEncodeAsBytes); - return returnValue; - } - - public static string DecodeTo64(string toDecode, Encoding enc) - { - byte[] toDecodeAsBytes = System.Convert.FromBase64String(toDecode); - string returnValue = enc.GetString(toDecodeAsBytes); - return returnValue; - } + //public static string EncodeTo64(string toEncode, Encoding enc) + //{ + // byte[] toEncodeAsBytes = enc.GetBytes(toEncode); + // string returnValue = System.Convert.ToBase64String(toEncodeAsBytes); + // return returnValue; + //} + //public static string DecodeTo64(string toDecode, Encoding enc) + //{ + // byte[] toDecodeAsBytes = System.Convert.FromBase64String(toDecode); + // string returnValue = enc.GetString(toDecodeAsBytes); + // return returnValue; + //} // function startsWith($haystack, $needle , $pos=0){ // $length = strlen($needle); @@ -94,5 +93,30 @@ public static string DecodeTo64(string toDecode, Encoding enc) // $start = $length * -1; // return (substr($haystack, $start) === $needle); //} + + public static long GetUnixTimestamp(DateTime value) + { + //create Timespan by subtracting the value provided from + //the Unix Epoch + TimeSpan span = (value - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)); + return (long)span.TotalSeconds; + } + public static long GetNowUnixTimestamp() + { + return GetUnixTimestamp(DateTime.UtcNow); + } + + public static bool ArrayEqual(byte[] b1, byte[] b2) + { + int len = b1.Length; + if (b1.Length != b2.Length) + return false; + for (int i = 0; i < len; i++) + { + if (b1[i] != b2[i]) + return false; + } + return true; + } } } diff --git a/src/WhatsAppApi/Helper/IncompleteMessageException.cs b/src/WhatsAppApi/Helper/IncompleteMessageException.cs index 838416a..0ba45b3 100644 --- a/src/WhatsAppApi/Helper/IncompleteMessageException.cs +++ b/src/WhatsAppApi/Helper/IncompleteMessageException.cs @@ -10,7 +10,7 @@ class IncompleteMessageException : Exception private int code; private string message; private string input; - + private byte[] buffer; public IncompleteMessageException(string message, int code = 0) { @@ -22,11 +22,13 @@ public void setInput(string input) { this.input = input; } - + public void setInput(byte[] input) + { + this.buffer = input; + } public string getInput() { return this.input; } - } } diff --git a/src/WhatsAppApi/Helper/ProtocolTreeNode.cs b/src/WhatsAppApi/Helper/ProtocolTreeNode.cs index f54f17a..af017f9 100644 --- a/src/WhatsAppApi/Helper/ProtocolTreeNode.cs +++ b/src/WhatsAppApi/Helper/ProtocolTreeNode.cs @@ -10,37 +10,57 @@ public class ProtocolTreeNode public string tag; public IEnumerable attributeHash; public IEnumerable children; - public string data; + public byte[] data; + //public ProtocolTreeNode(string tag, IEnumerable attributeHash, IEnumerable children = null, + // string data = "") public ProtocolTreeNode(string tag, IEnumerable attributeHash, IEnumerable children = null, - string data = "") + byte[] data = null) { this.tag = tag ?? ""; this.attributeHash = attributeHash ?? new KeyValue[0]; this.children = children ?? new ProtocolTreeNode[0]; - this.data = data ?? ""; + //this.data = data ?? ""; + this.data = new byte[0]; + if (data != null) + this.data = data; } - public ProtocolTreeNode(string tag, IEnumerable attributeHash, ProtocolTreeNode children = null, - string data = "") + //public ProtocolTreeNode(string tag, IEnumerable attributeHash, ProtocolTreeNode children = null, + // string data = "") + //{ + // this.tag = tag ?? ""; + // this.attributeHash = attributeHash ?? new KeyValue[0]; + // this.children = children != null ? new ProtocolTreeNode[] { children } : new ProtocolTreeNode[0]; + // this.data = data ?? ""; + //} + public ProtocolTreeNode(string tag, IEnumerable attributeHash, ProtocolTreeNode children = null) { this.tag = tag ?? ""; this.attributeHash = attributeHash ?? new KeyValue[0]; this.children = children != null ? new ProtocolTreeNode[] { children } : new ProtocolTreeNode[0]; - this.data = data ?? ""; + //this.data = data ?? ""; + this.data = new byte[0]; } - public ProtocolTreeNode(string tag, IEnumerable attributeHash, IEnumerable children, - byte[] data) : this(tag, attributeHash, children, Encoding.Default.GetString(data)) - {} + //public ProtocolTreeNode(string tag, IEnumerable attributeHash, IEnumerable children, + // byte[] data) + // : this(tag, attributeHash, children, Encoding.Default.GetString(data)) + //{ } - public ProtocolTreeNode(string tag, IEnumerable attributeHash, string data = "") + //public ProtocolTreeNode(string tag, IEnumerable attributeHash, string data = "") + // : this(tag, attributeHash, new ProtocolTreeNode[0], data) + //{ } + public ProtocolTreeNode(string tag, IEnumerable attributeHash, byte[] data = null) : this(tag, attributeHash, new ProtocolTreeNode[0], data) { } - - - public ProtocolTreeNode(string tag, IEnumerable attributeHash) : this(tag, attributeHash, new ProtocolTreeNode[0], "") + //public ProtocolTreeNode(string tag, IEnumerable attributeHash) + // : this(tag, attributeHash, new ProtocolTreeNode[0], "") + //{ + //} + public ProtocolTreeNode(string tag, IEnumerable attributeHash) + : this(tag, attributeHash, new ProtocolTreeNode[0], null) { } @@ -51,13 +71,14 @@ public string NodeString(string indent = "") { foreach (var item in this.attributeHash) { - ret += string.Format(" {0}=\"{1}\"" ,item.Key, item.Value); + ret += string.Format(" {0}=\"{1}\"", item.Key, item.Value); } } ret += ">"; if (this.data.Length > 0) { - ret += this.data; + //ret += this.data; + ret += WhatsApp.SYSEncoding.GetString(this.data); } if (this.children != null && this.children.Count() > 0) { @@ -111,7 +132,7 @@ public IEnumerable GetAllChildren(string tag) { if (tag.Equals(item.tag, StringComparison.InvariantCultureIgnoreCase)) { - tmpReturn.Add(item); + tmpReturn.Add(item); } tmpReturn.AddRange(item.GetAllChildren(tag)); } @@ -124,7 +145,11 @@ public IEnumerable GetAllChildren() return this.children.ToArray(); } - public string GetDataString() + //public string GetDataString() + //{ + // return this.data; + //} + public byte[] GetData() { return this.data; } diff --git a/src/WhatsAppApi/Response/MessageRecvResponse.cs b/src/WhatsAppApi/Response/MessageRecvResponse.cs index 4252429..496ea4d 100644 --- a/src/WhatsAppApi/Response/MessageRecvResponse.cs +++ b/src/WhatsAppApi/Response/MessageRecvResponse.cs @@ -110,7 +110,8 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t } else if (ProtocolTreeNode.TagEquals(itemNode, "body") && (tmpAttrbId != null)) { - string dataString = itemNode.GetDataString(); + //string dataString = itemNode.GetDataString(); + string dataString = WhatsApp.SYSEncoding.GetString(itemNode.GetData()); var tmpMessKey = new FMessage.Key(tmpAttrFrom, false, tmpAttrbId); builder.Key(tmpMessKey).Remote_resource(tmpAttrFromJid).NewIncomingInstance().Data(dataString); } @@ -170,7 +171,7 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t ProtocolTreeNode tmpChildMedia = itemNode.GetChild("media"); if (tmpChildMedia != null) { - builder.Media_name(tmpChildMedia.GetAttribute("name")).Data(tmpChildMedia.GetDataString()); + builder.Media_name(tmpChildMedia.GetAttribute("name")).Data(WhatsApp.SYSEncoding.GetString(tmpChildMedia.GetData())); } } else @@ -178,7 +179,8 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t string tmpAttrEncoding = itemNode.GetAttribute("encoding") ?? "text"; if (tmpAttrEncoding == "text") { - builder.Data(itemNode.GetDataString()); + //builder.Data(itemNode.GetDataString()); + builder.Data(WhatsApp.SYSEncoding.GetString(itemNode.GetData())); } //else //{ @@ -277,7 +279,8 @@ private void TypeSubject(ProtocolTreeNode messageNode, string tmpFrom, string uJ } } ProtocolTreeNode child = messageNode.GetChild("body"); - string subject = (child == null) ? null : child.GetDataString(); + //string subject = (child == null) ? null : child.GetDataString(); + string subject = (child == null) ? null : WhatsApp.SYSEncoding.GetString(child.GetData()); if (subject != null) { WhatsEventHandler.OnGroupNewSubjectEventHandler(tmpFrom, uJid, subject, int.Parse(tmpT, CultureInfo.InvariantCulture)); diff --git a/src/WhatsAppApi/Response/WhatsParser.cs b/src/WhatsAppApi/Response/WhatsParser.cs index 8acba85..fe76501 100644 --- a/src/WhatsAppApi/Response/WhatsParser.cs +++ b/src/WhatsAppApi/Response/WhatsParser.cs @@ -12,12 +12,14 @@ public class WhatsParser public WhatsSendHandler WhatsSendHandler { get; private set; } private WhatsNetwork whatsNetwork; private MessageRecvResponse messResponseHandler; + private BinTreeNodeWriter _binWriter; - public WhatsParser(WhatsNetwork whatsNet) + internal WhatsParser(WhatsNetwork whatsNet, BinTreeNodeWriter writer) { - this.WhatsSendHandler = new WhatsSendHandler(whatsNet); + this.WhatsSendHandler = new WhatsSendHandler(whatsNet, writer); this.whatsNetwork = whatsNet; this.messResponseHandler = new MessageRecvResponse(this.WhatsSendHandler); + this._binWriter = writer; } public void ParseProtocolNode(ProtocolTreeNode protNode) diff --git a/src/WhatsAppApi/WhatsApp.cs b/src/WhatsAppApi/WhatsApp.cs index c58b0ed..0579309 100644 --- a/src/WhatsAppApi/WhatsApp.cs +++ b/src/WhatsAppApi/WhatsApp.cs @@ -14,9 +14,9 @@ namespace WhatsAppApi { public class WhatsApp { - private readonly Encoding sysEncoding; + //private readonly Encoding sysEncoding; private AccountInfo accountinfo; - private Dictionary challengeArray; + //private Dictionary challengeArray; private string connectedStatus = "connected"; private bool debug; private string disconnectedStatus = "disconnected"; @@ -28,18 +28,24 @@ public class WhatsApp private string phoneNumber; private BinTreeNodeReader reader; private BinTreeNodeWriter writer; - private int timeout = 2000; - + //private int timeout = 2000; + private int timeout = 100000; + private WhatsNetwork whatsNetwork; public WhatsSendHandler WhatsSendHandler { get; private set; } public WhatsParser WhatsParser { get; private set; } + //protocol 1.2 + public static readonly Encoding SYSEncoding = Encoding.GetEncoding("ISO-8859-1"); + private byte[] _encryptionKey; + private byte[] _challengeBytes; + //array("sec" => 2, "usec" => 0); public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) { this.messageQueue = new List(); - this.sysEncoding = Encoding.GetEncoding("ISO-8859-1"); - this.challengeArray = new Dictionary(); + //this.sysEncoding = Encoding.GetEncoding("ISO-8859-1"); + //this.challengeArray = new Dictionary(); this.phoneNumber = phoneNum; this.imei = imei; @@ -51,8 +57,10 @@ public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) this.loginStatus = disconnectedStatus; - this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.sysEncoding, this.timeout); - this.WhatsParser = new WhatsParser(this.whatsNetwork); + //this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.sysEncoding, this.timeout); + //this.WhatsParser = new WhatsParser(this.whatsNetwork); + this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.timeout); + this.WhatsParser = new WhatsParser(this.whatsNetwork, this.writer); this.WhatsSendHandler = this.WhatsParser.WhatsSendHandler; } @@ -116,17 +124,23 @@ public void Login() var data = this.writer.StartStream(WhatsConstants.WhatsAppServer, resource); var feat = this.addFeatures(); var auth = this.addAuth(); - this.whatsNetwork.SendData(data); - this.whatsNetwork.SendNode(feat); - this.whatsNetwork.SendNode(auth); + //this.whatsNetwork.SendData(data); + //this.whatsNetwork.SendNode(feat); + //this.whatsNetwork.SendNode(auth); + processOutboundData(data); + processOutboundData(feat, false); + processOutboundData(auth, false); this.PollMessages(); - ProtocolTreeNode authResp = this.addAuthResponse(); - this.whatsNetwork.SendNode(authResp); + //ProtocolTreeNode authResp = this.addAuthResponse(); + //this.whatsNetwork.SendNode(authResp); + ProtocolTreeNode authResp = this.addAuthResponse_v1_2(); + processOutboundData(authResp, false); int cnt = 0; do { this.PollMessages(); + System.Threading.Thread.Sleep(500); } while ((cnt++ < 100) && (this.loginStatus.Equals(this.disconnectedStatus, StringComparison.OrdinalIgnoreCase))); } @@ -134,8 +148,7 @@ public void Login() public void Message(string to, string txt) { //var bodyNode = new ProtocolTreeNode("body", null, txt); - var tmpMessage = new FMessage(to, true) - {key = {id = TicketCounter.MakeId("mSend_")}, data = txt}; + var tmpMessage = new FMessage(to, true) { key = { id = TicketCounter.MakeId("mSend_") }, data = txt }; this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage); } @@ -188,22 +201,86 @@ public void sendNickname(string nickname) protected ProtocolTreeNode addAuth() { + //var node = new ProtocolTreeNode("auth", + // new KeyValue[] { new KeyValue("xmlns", @"urn:ietf:params:xml:ns:xmpp-sasl"), new KeyValue("mechanism", "DIGEST-MD5-1") }); + // change to protocol 1.2 var node = new ProtocolTreeNode("auth", - new KeyValue[] { new KeyValue("xmlns", @"urn:ietf:params:xml:ns:xmpp-sasl"), new KeyValue("mechanism", "DIGEST-MD5-1") }); + new KeyValue[] { new KeyValue("xmlns", @"urn:ietf:params:xml:ns:xmpp-sasl"), + new KeyValue("mechanism", "WAUTH-1"), + new KeyValue("user", this.phoneNumber) }); return node; } - protected ProtocolTreeNode addAuthResponse() + //protected ProtocolTreeNode addAuthResponse() + //{ + // if (!this.challengeArray.ContainsKey("nonce")) + // this.challengeArray.Add("nonce", ""); + + // string resp = this.authenticate(this.challengeArray["nonce"]); + + // var node = new ProtocolTreeNode("response", + // new KeyValue[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") }, + // Func.EncodeTo64(resp, this.sysEncoding)); + // return node; + //} + + //change to protocol 1.2 + protected ProtocolTreeNode addAuthResponse_v1_2() { - if (!this.challengeArray.ContainsKey("nonce")) - this.challengeArray.Add("nonce", ""); + //while (this._encryptionKey == null) + while (this._challengeBytes == null) + { + this.PollMessages(); + System.Threading.Thread.Sleep(500); + } + long totalSeconds = Func.GetNowUnixTimestamp(); + + Rfc2898DeriveBytes r = new Rfc2898DeriveBytes(this.encryptPassword(), _challengeBytes, 16); + this._encryptionKey = r.GetBytes(20); + this.reader.Encryptionkey = _encryptionKey; + this.writer.Encryptionkey = _encryptionKey; + + List b = new List(); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(this.phoneNumber)); + b.AddRange(this._challengeBytes); + //b.AddRange(WhatsApp.SYSEncoding.GetBytes(Func.GetNowUnixTimestamp().ToString())); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(totalSeconds.ToString())); - string resp = this.authenticate(this.challengeArray["nonce"]); + byte[] data = b.ToArray(); + byte[] response = Encryption.WhatsappEncrypt(_encryptionKey, data, false); var node = new ProtocolTreeNode("response", new KeyValue[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") }, - Func.EncodeTo64(resp, this.sysEncoding)); + response); + return node; + /* + + //TimeSpan span = (TimeSpan)(DateTime.UtcNow - UnixEpoch); + //long totalSeconds = (long)span.TotalSeconds; + byte[] bytes = new Rfc2898DeriveBytes(this.encryptPassword(), this._challengeBytes, 0x10).GetBytes(20); + + System.Diagnostics.Debug.Assert(this._encryptionKey.SequenceEqual(bytes)); + + this._encryptionKey = bytes; + this.reader.Encryptionkey = bytes; + KeyStream OutputKey = new KeyStream(bytes); + List list = new List(0x400); + list.AddRange(Enumerable.Range(0, 4).Select(i => (byte)0)); + list.AddRange(WhatsApp.SYSEncoding.GetBytes(this.phoneNumber)); + list.AddRange(this._challengeBytes); + list.AddRange(WhatsApp.SYSEncoding.GetBytes(totalSeconds.ToString())); + byte[] buffer = list.ToArray(); + OutputKey.EncodeMessage(buffer, 0, 4, buffer.Length - 4); + + + System.Diagnostics.Debug.Assert(response.SequenceEqual(buffer)); + + KeyValue[] attributes = new KeyValue[1]; + attributes[0] = new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); + //this.writer.Write(new ProtocolTreeNode("response", attributes, null, buffer)); + return new ProtocolTreeNode("response", attributes, null, buffer); + * */ } protected ProtocolTreeNode addFeatures() @@ -211,55 +288,57 @@ protected ProtocolTreeNode addFeatures() var child = new ProtocolTreeNode("receipt_acks", null); var childList = new List(); childList.Add(child); - var parent = new ProtocolTreeNode("stream:features", null, childList, ""); + //var parent = new ProtocolTreeNode("stream:features", null, childList, ""); + var parent = new ProtocolTreeNode("stream:features", null, childList, null); return parent; } - protected string authenticate(string nonce) - { - string NC = "00000001"; - string qop = "auth"; - string cnonce = Func.random_uuid(); - string data1 = this.phoneNumber; - data1 += ":"; - data1 += WhatsConstants.WhatsAppServer; - data1 += ":"; - data1 += this.encryptPassword(); //this.EncryptPassword(); - - string data2 = Func.HexString2Ascii(md5(data1)); - data2 += ":"; - data2 += nonce; - data2 += ":"; - data2 += cnonce; - - string data3 = "AUTHENTICATE:"; - data3 += WhatsConstants.WhatsAppDigest; - - string data4 = md5(data2); - data4 += ":"; - data4 += nonce; - data4 += ":"; - data4 += NC; - data4 += ":"; - data4 += cnonce; - data4 += ":"; - data4 += qop; - data4 += ":"; - data4 += md5(data3); - - string data5 = md5(data4); - string response = - string.Format( - "username=\"{0}\",realm=\"{1}\",nonce=\"{2}\",cnonce=\"{3}\",nc={4},qop={5},digest-uri=\"{6}\",response={7},charset=ISO-8859-1", - this.phoneNumber, - WhatsConstants.WhatsAppRealm, - nonce, - cnonce, - NC, - qop, - WhatsConstants.WhatsAppDigest, - data5); - return response; - } + + //protected string authenticate(string nonce) + //{ + // string NC = "00000001"; + // string qop = "auth"; + // string cnonce = Func.random_uuid(); + // string data1 = this.phoneNumber; + // data1 += ":"; + // data1 += WhatsConstants.WhatsAppServer; + // data1 += ":"; + // data1 += this.encryptPassword(); //this.EncryptPassword(); + + // string data2 = Func.HexString2Ascii(md5(data1)); + // data2 += ":"; + // data2 += nonce; + // data2 += ":"; + // data2 += cnonce; + + // string data3 = "AUTHENTICATE:"; + // data3 += WhatsConstants.WhatsAppDigest; + + // string data4 = md5(data2); + // data4 += ":"; + // data4 += nonce; + // data4 += ":"; + // data4 += NC; + // data4 += ":"; + // data4 += cnonce; + // data4 += ":"; + // data4 += qop; + // data4 += ":"; + // data4 += md5(data3); + + // string data5 = md5(data4); + // string response = + // string.Format( + // "username=\"{0}\",realm=\"{1}\",nonce=\"{2}\",cnonce=\"{3}\",nc={4},qop={5},digest-uri=\"{6}\",response={7},charset=ISO-8859-1", + // this.phoneNumber, + // WhatsConstants.WhatsAppRealm, + // nonce, + // cnonce, + // NC, + // qop, + // WhatsConstants.WhatsAppDigest, + // data5); + // return response; + //} protected void DebugPrint(string debugMsg) { @@ -271,20 +350,41 @@ protected void DebugPrint(string debugMsg) protected void processChallenge(ProtocolTreeNode node) { - string challenge = Func.DecodeTo64(node.data, this.sysEncoding); - string[] challengeStrs = challenge.Split(','); - this.challengeArray = new Dictionary(); - foreach (var item in challengeStrs) - { - string[] d = item.Split('='); - if (this.challengeArray.ContainsKey(d[0])) - this.challengeArray[d[0]] = d[1].Replace("\"", ""); - else - this.challengeArray.Add(d[0], d[1].Replace("\"", "")); - } + //string challenge = Func.DecodeTo64(node.data, this.sysEncoding); + //string[] challengeStrs = challenge.Split(','); + //this.challengeArray = new Dictionary(); + //foreach (var item in challengeStrs) + //{ + // string[] d = item.Split('='); + // if (this.challengeArray.ContainsKey(d[0])) + // this.challengeArray[d[0]] = d[1].Replace("\"", ""); + // else + // this.challengeArray.Add(d[0], d[1].Replace("\"", "")); + //} + + //change to protocol 1.2 + _challengeBytes = node.data; + } + + protected void processOutboundData(ProtocolTreeNode node, bool encrypt = true) + { + this.DebugPrint(node.NodeString("SENT: ")); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this.writer.Write(node, encrypt)); + } + protected void processOutboundData(string data) + { + this.DebugPrint("SENT: " + data); + this.whatsNetwork.SendData(data); + } + protected void processOutboundData(byte[] data) + { + this.DebugPrint("SENT: " + WhatsApp.SYSEncoding.GetString(data)); + this.whatsNetwork.SendData(data); } - protected void processInboundData(string data) + //protected void processInboundData(string data) + protected void processInboundData(byte[] data) { try { @@ -305,6 +405,10 @@ protected void processInboundData(string data) node.GetAttribute("creation"), node.GetAttribute("expiration")); } + else if (node.tag.Equals("failure", StringComparison.OrdinalIgnoreCase)) + { + this.loginStatus = this.disconnectedStatus; + } if (node.tag.Equals("message", StringComparison.OrdinalIgnoreCase)) { this.AddMessage(node); @@ -351,7 +455,7 @@ protected void sendMessageReceived(ProtocolTreeNode msg) private string md5(string pass) { MD5 md5 = MD5.Create(); - byte[] dataMd5 = md5.ComputeHash(this.sysEncoding.GetBytes(pass)); + byte[] dataMd5 = md5.ComputeHash(WhatsApp.SYSEncoding.GetBytes(pass)); var sb = new StringBuilder(); for (int i = 0; i < dataMd5.Length; i++) sb.AppendFormat("{0:x2}", dataMd5[i]); diff --git a/src/WhatsAppApi/WhatsAppApi.csproj b/src/WhatsAppApi/WhatsAppApi.csproj index 5ec2a6d..e1bd7f9 100644 --- a/src/WhatsAppApi/WhatsAppApi.csproj +++ b/src/WhatsAppApi/WhatsAppApi.csproj @@ -46,10 +46,14 @@ + + + Code + diff --git a/src/WhatsAppApi/WhatsNetwork.cs b/src/WhatsAppApi/WhatsNetwork.cs index 34b8222..5a36320 100644 --- a/src/WhatsAppApi/WhatsNetwork.cs +++ b/src/WhatsAppApi/WhatsNetwork.cs @@ -9,22 +9,23 @@ namespace WhatsAppApi { public class WhatsNetwork { - private readonly Encoding sysEncoding; private readonly int recvTimeout; private readonly string whatsHost; private readonly int whatsPort; - private string incomplete_message = ""; + //private string incomplete_message = ""; + private List incomplete_message = new List(); private Socket socket; - private BinTreeNodeWriter binWriter; + //private BinTreeNodeWriter binWriter; - public WhatsNetwork(string whatsHost, int port, Encoding encoding, int timeout = 2000) + public WhatsNetwork(string whatsHost, int port, int timeout = 2000) + //public WhatsNetwork(string whatsHost, int port, BinTreeNodeWriter writer, int timeout = 2000) { - this.sysEncoding = encoding; this.recvTimeout = timeout; this.whatsHost = whatsHost; this.whatsPort = port; - this.binWriter = new BinTreeNodeWriter(DecodeHelper.getDictionary()); + //this.binWriter = writer; + this.incomplete_message = new List(); } public void Connect() @@ -35,54 +36,121 @@ public void Connect() //var tmpNetStream = new NetworkStream(this.socket); //this.streamReader = new StreamReader(tmpNetStream); //this.streamWriter = new StreamWriter(tmpNetStream); + + if (!this.socket.Connected) + throw new System.IO.IOException("Cannot connect"); } - public string ReadData() + //public string ReadData() + //{ + // string buff = ""; + // string ret = Socket_read(1024); + // if (ret != null) + // { + // buff = this.incomplete_message + ret; + // this.incomplete_message = ""; + // } + // return buff; + //} + public byte[] ReadData() { - string buff = ""; - string ret = Socket_read(1024); + List buff = new List(); + byte[] ret = Socket_read(1024); if (ret != null) { - buff = this.incomplete_message + ret; - this.incomplete_message = ""; + buff.AddRange(this.incomplete_message); + buff.AddRange(ret); + this.incomplete_message = new List(); } - return buff; + return buff.ToArray(); } public void SendData(string data) { Socket_send(data, data.Length, 0); } - - public void SendNode(ProtocolTreeNode node) + public void SendData(byte[] data) { - //this.DebugPrint(node.NodeString("SENT: ")); - this.SendData(this.binWriter.Write(node)); + Socket_send(data); } - private string Socket_read(int length) + //public void SendNode(ProtocolTreeNode node) + //{ + // //this.DebugPrint(node.NodeString("SENT: ")); + // this.SendData(this.binWriter.Write(node)); + //} + + //private string Socket_read(int length) + //{ + // if (!socket.Connected) + // { + // throw new System.IO.IOException("Disconnected"); + // } + + // var buff = new byte[length]; + // int receiveLength = 0; + // try + // { + // receiveLength = socket.Receive(buff, 0, length, 0); + // } + // catch (SocketException excpt) + // { + // if (excpt.SocketErrorCode == SocketError.TimedOut) + // { + // Console.WriteLine("Socket-Timout"); + // return null; + // } + // else + // { + // Console.WriteLine("Unbehandelter Fehler bei Sockerread: {0}", excpt); + // throw excpt; + // } + // } + + // string tmpRet = this.sysEncoding.GetString(buff); + // return tmpRet; + //} + private byte[] Socket_read(int length) { + if (!socket.Connected) + { + throw new System.IO.IOException("Disconnected"); + } + var buff = new byte[length]; + int receiveLength = 0; try { - socket.Receive(buff, 0, length, 0); + receiveLength = socket.Receive(buff, 0, length, 0); } catch (SocketException excpt) { - //if (excpt.SocketErrorCode == SocketError.TimedOut) - // Console.WriteLine("Socket-Timout"); - return null; - //else - // Console.WriteLine("Unbehandelter Fehler bei Sockerread: {0}", excpt); + if (excpt.SocketErrorCode == SocketError.TimedOut) + { + Console.WriteLine("Socket-Timout"); + return null; + } + else + { + Console.WriteLine("Unbehandelter Fehler bei Sockerread: {0}", excpt); + throw excpt; + } } - string tmpRet = this.sysEncoding.GetString(buff); + + byte[] tmpRet = new byte[receiveLength]; + if (receiveLength > 0) + Buffer.BlockCopy(buff, 0, tmpRet, 0, receiveLength); return tmpRet; } private void Socket_send(string data, int length, int flags) { - var tmpBytes = this.sysEncoding.GetBytes(data); + var tmpBytes = WhatsApp.SYSEncoding.GetBytes(data); this.socket.Send(tmpBytes); } + private void Socket_send(byte[] data) + { + this.socket.Send(data); + } } } diff --git a/src/WhatsAppApi/WhatsSendHandler.cs b/src/WhatsAppApi/WhatsSendHandler.cs index 847c1f4..58b1209 100644 --- a/src/WhatsAppApi/WhatsSendHandler.cs +++ b/src/WhatsAppApi/WhatsSendHandler.cs @@ -12,25 +12,26 @@ namespace WhatsAppApi public class WhatsSendHandler { private string MyJID = ""; - private string PushName = "Nickname?"; - private string whatsAppRealm = "s.whatsapp.net"; + private BinTreeNodeWriter _binWriter; //private BinTreeNodeWriter TreeNodeWriter; private WhatsNetwork whatsNetwork; //this.Login.domain ?? - public WhatsSendHandler(WhatsNetwork net) + //public WhatsSendHandler(WhatsNetwork net) + internal WhatsSendHandler(WhatsNetwork net, BinTreeNodeWriter writer) { //this.TreeNodeWriter = new BinTreeNodeWriter(DecodeHelper.getDictionary()); this.whatsNetwork = net; + this._binWriter = writer; } - public void SendActive() { var node = new ProtocolTreeNode("presence", new[] {new KeyValue("type", "active")}); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendAddParticipants(string gjid, IEnumerable participants) @@ -58,7 +59,8 @@ public void SendAddParticipants(string gjid, IEnumerable participants, A public void SendAvailableForChat(string nickName) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName) }); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendClearDirty(IEnumerable categoryNames) @@ -73,7 +75,8 @@ public void SendClearDirty(IEnumerable categoryNames) new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "s.whatsapp.net") }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendClearDirty(string category) @@ -86,7 +89,8 @@ public void SendClientConfig(string platform, string lg, string lc) string v = TicketCounter.MakeId("config_"); var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("lg", lg), new KeyValue("lc", lc) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", whatsAppRealm) }, new ProtocolTreeNode[] { child }); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, bool preview, bool defaultSetting, bool groupsSetting, IEnumerable groups, Action onCompleted, Action onError) @@ -114,42 +118,45 @@ public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, // this.AddIqHandler(id, new FunXMPP.IqResultHandler(parse, onError)); //} var node = new ProtocolTreeNode("iq", + new[] + { + new KeyValue("id", id), new KeyValue("type", "set"), + new KeyValue("to", "") //this.Login.Domain) + }, + new ProtocolTreeNode[] + { + new ProtocolTreeNode("config", new[] { - new KeyValue("id", id), new KeyValue("type", "set"), - new KeyValue("to", "") //this.Login.Domain) + new KeyValue("xmlns","urn:xmpp:whatsapp:push"), + new KeyValue("platform", platform), + new KeyValue("lg", lg), + new KeyValue("lc", lc), + new KeyValue("clear", "0"), + new KeyValue("id", pushUri.ToString()), + new KeyValue("preview",preview ? "1" : "0"), + new KeyValue("default",defaultSetting ? "1" : "0"), + new KeyValue("groups",groupsSetting ? "1" : "0") }, - new ProtocolTreeNode[] - { - new ProtocolTreeNode("config", - new[] - { - new KeyValue("xmlns","urn:xmpp:whatsapp:push"), - new KeyValue("platform", platform), - new KeyValue("lg", lg), - new KeyValue("lc", lc), - new KeyValue("clear", "0"), - new KeyValue("id", pushUri.ToString()), - new KeyValue("preview",preview ? "1" : "0"), - new KeyValue("default",defaultSetting ? "1" : "0"), - new KeyValue("groups",groupsSetting ? "1" : "0") - }, - this.ProcessGroupSettings(groups)) - }); - this.whatsNetwork.SendNode(node); + this.ProcessGroupSettings(groups)) + }); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendClose() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendComposing(string to) { var child = new ProtocolTreeNode("composing", new[] { new KeyValue("xmlns", "http://jabber.org/protocol/chatstates") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "chat") }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendCreateGroupChat(string subject) @@ -179,7 +186,8 @@ public void SendCreateGroupChat(string subject, Action onSuccess, Action //})); var child = new ProtocolTreeNode("group", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("action", "create"), new KeyValue("subject", subject) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "g.us") }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendDeleteAccount(Action onSuccess, Action onError) @@ -200,7 +208,8 @@ public void SendDeleteAccount(Action onSuccess, Action onError) new KeyValue("xmlns", "urn:xmpp:whatsapp:account") }) }); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendDeleteFromRoster(string jid) @@ -209,7 +218,8 @@ public void SendDeleteFromRoster(string jid) var innerChild = new ProtocolTreeNode("item", new[] { new KeyValue("jid", jid), new KeyValue("subscription", "remove") }); var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "jabber:iq:roster") }, new ProtocolTreeNode[] {innerChild}); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "set"), new KeyValue("id", v) }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendDeliveredReceiptAck(string to, string id) @@ -235,7 +245,8 @@ public void SendEndGroupChat(string gjid, Action onSuccess, Action onError) //}, onError)); var child = new ProtocolTreeNode("group", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("action", "delete") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetClientConfig() @@ -258,7 +269,8 @@ public void SendGetClientConfig() //})); var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", this.whatsAppRealm) }, new ProtocolTreeNode[] { child }); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetDirty() @@ -273,7 +285,8 @@ public void SendGetDirty() //})); var child = new ProtocolTreeNode("status", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetGroupInfo(string gjid) @@ -293,7 +306,8 @@ public void SendGetGroupInfo(string gjid) //})); var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "w:g") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } //public void SendGetGroups() @@ -340,7 +354,8 @@ public void SendGetParticipants(string gjid) //})); var child = new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:g") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", gjid) }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetPhoto(string jid, bool largeFormat) @@ -382,7 +397,8 @@ public void SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, A } var child = new ProtocolTreeNode("picture", attrList.ToArray()); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid) }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetPhotoIds(IEnumerable jids) @@ -416,7 +432,8 @@ public void SendGetPhotoIds(IEnumerable jids) var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", this.MyJID) }, new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:profile:picture") }, (from jid in jids select new ProtocolTreeNode("user", new[] { new KeyValue("jid", jid) })).ToArray())); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetPrivacyList() @@ -443,7 +460,8 @@ public void SendGetPrivacyList() var innerChild = new ProtocolTreeNode("list", new[] { new KeyValue("name", "default") }); var child = new ProtocolTreeNode("query", new KeyValue[] { new KeyValue("xmlns", "jabber:iq:privacy") }, innerChild); var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "get") }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetServerProperties() @@ -466,7 +484,8 @@ public void SendGetServerProperties() //})); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, new ProtocolTreeNode("props", new[] { new KeyValue("xmlns", "w") })); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetStatus(string jid) @@ -478,14 +497,16 @@ public void SendGetStatus(string jid) string v = TicketManager.GenerateId(); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "action"), new KeyValue("id", v) }, new ProtocolTreeNode("action", new[] { new KeyValue("type", "get") })); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } } public void SendInactive() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "inactive") }); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendLeaveGroup(string gjid) @@ -518,7 +539,8 @@ public void SendLeaveGroups(IEnumerable gjids, Action onSuccess, Action< IEnumerable innerChilds = from gjid in gjids select new ProtocolTreeNode("group", new[] { new KeyValue("id", gjid) }); var child = new ProtocolTreeNode("leave", new KeyValue[] { new KeyValue("xmlns", "w:g") }, innerChilds); var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "g.us") }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendMessage(FMessage message) @@ -537,26 +559,30 @@ public void SendMessageReceived(FMessage message) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendNop() { - this.whatsNetwork.SendNode(null); + //this.whatsNetwork.SendNode(null); + this.whatsNetwork.SendData(this._binWriter.Write(null)); } public void SendNotificationReceived(string jid, string id) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "notification"), new KeyValue("id", id) }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendPaused(string to) { var child = new ProtocolTreeNode("paused", new[] { new KeyValue("xmlns", "http://jabber.org/protocol/chatstates") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "chat") }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendPing() @@ -565,19 +591,22 @@ public void SendPing() //this.AddIqHandler(id, new IqResultHandler((node, from) => this.EventHandler.OnPingResponseReceived(), node => this.EventHandler.OnPingResponseReceived())); var child = new ProtocolTreeNode("ping", new[] { new KeyValue("xmlns", "w:p") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get") }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendPong(string id) { var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "result"), new KeyValue("to", this.whatsAppRealm), new KeyValue("id", id) }); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendPresenceSubscriptionRequest(string to) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "subscribe"), new KeyValue("to", to) }); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendQueryLastOnline(string jid) @@ -598,7 +627,8 @@ public void SendQueryLastOnline(string jid) //})); var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "jabber:iq:last") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid) }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendRelayCapable(string platform, bool value) @@ -606,14 +636,16 @@ public void SendRelayCapable(string platform, bool value) string v = TicketCounter.MakeId("relay_"); var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("relay", value ? "1" : "0") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", this.whatsAppRealm) }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendRelayComplete(string id, int millis) { var child = new ProtocolTreeNode("relay", new[] { new KeyValue("elapsed", millis.ToString(CultureInfo.InvariantCulture)) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "result"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendRelayTimeout(string id) @@ -621,7 +653,8 @@ public void SendRelayTimeout(string id) var innerChild = new ProtocolTreeNode("remote-server-timeout", new[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas") }); var child = new ProtocolTreeNode("error", new[] { new KeyValue("code", "504"), new KeyValue("type", "wait") }, innerChild); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "error"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendRemoveParticipants(string gjid, List participants) @@ -664,7 +697,8 @@ public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, A //}, onError)); var child = new ProtocolTreeNode("subject", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("value", subject) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes, Action onSuccess, Action onError) @@ -690,7 +724,8 @@ public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes, Action list.Add(new ProtocolTreeNode("picture", new[] { new KeyValue("type", "preview") }, null, thumbnailBytes)); } var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", jid) }, list.ToArray()); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendSetPrivacyBlockedList(IEnumerable list) @@ -706,7 +741,8 @@ public void SendSetPrivacyBlockedList(IEnumerable jidSet, Action onSucce var child = new ProtocolTreeNode("list", new KeyValue[] { new KeyValue("name", "default") }, (nodeArray.Length == 0) ? null : nodeArray); var node2 = new ProtocolTreeNode("query", new KeyValue[] { new KeyValue("xmlns", "jabber:iq:privacy") }, child); var node3 = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set") }, node2); - this.whatsNetwork.SendNode(node3); + //this.whatsNetwork.SendNode(node3); + this.whatsNetwork.SendData(this._binWriter.Write(node3)); } public void SendStatusUpdate(string status, Action onComplete, Action onError) @@ -719,27 +755,33 @@ public void SendStatusUpdate(string status, Action onComplete, Action onErr // OnError = onError //}; //trackedMessages.Add(message.key, handler); - var messageNode = GetMessageNode(message, new ProtocolTreeNode("body", null, status)); - this.whatsNetwork.SendNode(messageNode); + + //var messageNode = GetMessageNode(message, new ProtocolTreeNode("body", null, status)); + var messageNode = GetMessageNode(message, new ProtocolTreeNode("body", null, WhatsApp.SYSEncoding.GetBytes(status))); + //this.whatsNetwork.SendNode(messageNode); + this.whatsNetwork.SendData(this._binWriter.Write(messageNode)); } public void SendSubjectReceived(string to, string id) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = GetSubjectMessage(to, id, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendUnsubscribeHim(string jid) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribed"), new KeyValue("to", jid) }); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendUnsubscribeMe(string jid) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribe"), new KeyValue("to", jid) }); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendVisibleReceiptAck(string to, string id) @@ -751,12 +793,15 @@ internal void SendGetGroups(string id, string type) { var child = new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("type", type) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "g.us") }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } internal void SendMessageWithBody(FMessage message) { - var child = new ProtocolTreeNode("body", null, message.data); - this.whatsNetwork.SendNode(GetMessageNode(message, child)); + //var child = new ProtocolTreeNode("body", null, message.data); + var child = new ProtocolTreeNode("body", null, WhatsApp.SYSEncoding.GetBytes(message.data)); + //this.whatsNetwork.SendNode(GetMessageNode(message, child)); + this.whatsNetwork.SendData(this._binWriter.Write(GetMessageNode(message, child))); } internal void SendMessageWithMedia(FMessage message) @@ -790,7 +835,8 @@ internal void SendMessageWithMedia(FMessage message) } if ((FMessage.Type.Contact == message.media_wa_type) && (message.media_name != null)) { - node = new ProtocolTreeNode("media", list.ToArray(), new ProtocolTreeNode("vcard", new KeyValue[] { new KeyValue("name", message.media_name) }, message.data)); + //node = new ProtocolTreeNode("media", list.ToArray(), new ProtocolTreeNode("vcard", new KeyValue[] { new KeyValue("name", message.media_name) }, message.data)); + node = new ProtocolTreeNode("media", list.ToArray(), new ProtocolTreeNode("vcard", new KeyValue[] { new KeyValue("name", message.media_name) }, WhatsApp.SYSEncoding.GetBytes(message.data))); } else { @@ -811,14 +857,16 @@ internal void SendMessageWithMedia(FMessage message) } node = new ProtocolTreeNode("media", list.ToArray(), null, data); } - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } internal void SendVerbParticipants(string gjid, IEnumerable participants, string id, string inner_tag) { IEnumerable source = from jid in participants select new ProtocolTreeNode("participant", new[] { new KeyValue("jid", jid) }); var child = new ProtocolTreeNode(inner_tag, new[] { new KeyValue("xmlns", "w:g") }, source); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, child); - this.whatsNetwork.SendNode(node); + //this.whatsNetwork.SendNode(node); + this.whatsNetwork.SendData(this._binWriter.Write(node)); } private IEnumerable ProcessGroupSettings(IEnumerable groups) { @@ -844,7 +892,8 @@ private void SendReceiptAck(string to, string id, string receiptType) new KeyValue("type", "chat"), new KeyValue("id", id) }, tmpChild); - this.whatsNetwork.SendNode(resultNode); + //this.whatsNetwork.SendNode(resultNode); + this.whatsNetwork.SendData(this._binWriter.Write(resultNode)); } internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode) From b2fd89b3af4c2a116fdc82b831d60a3353268374 Mon Sep 17 00:00:00 2001 From: hermanho Date: Wed, 7 Nov 2012 17:45:50 +0800 Subject: [PATCH 004/271] Update to protocol 1.2 --- src/WhatsAppApi/Helper/Encryption.cs | 46 +++++++++++++++++++++++ src/WhatsAppApi/Helper/RC4.cs | 55 ++++++++++++++++++++++++++++ src/WhatsAppPort/app.config | 3 ++ src/WhatsTest/app.config | 3 ++ 4 files changed, 107 insertions(+) create mode 100644 src/WhatsAppApi/Helper/Encryption.cs create mode 100644 src/WhatsAppApi/Helper/RC4.cs create mode 100644 src/WhatsAppPort/app.config create mode 100644 src/WhatsTest/app.config diff --git a/src/WhatsAppApi/Helper/Encryption.cs b/src/WhatsAppApi/Helper/Encryption.cs new file mode 100644 index 0000000..4958b94 --- /dev/null +++ b/src/WhatsAppApi/Helper/Encryption.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace WhatsAppApi.Helper +{ + static class Encryption + { + public static byte[] WhatsappEncrypt(byte[] key, byte[] data, bool appendHash) + { + RC4 encryption = new RC4(key, 256); + HMACSHA1 h = new HMACSHA1(key); + byte[] buff = new byte[data.Length]; + Buffer.BlockCopy(data, 0, buff, 0, data.Length); + byte[] buff2 = new byte[data.Length + 4]; + Buffer.BlockCopy(data, 0, buff2, 4, data.Length); + + encryption.Cipher(buff); + byte[] hashByte = h.ComputeHash(buff); + byte[] response = new byte[4 + buff.Length]; + if (appendHash) + { + Buffer.BlockCopy(buff, 0, response, 0, buff.Length); + Buffer.BlockCopy(hashByte, 0, response, buff.Length, 4); + } + else + { + Buffer.BlockCopy(hashByte, 0, response, 0, 4); + Buffer.BlockCopy(buff, 0, response, 4, buff.Length); + } + + return response; + } + + public static byte[] WhatsappDecrypt(byte[] key, byte[] data) + { + RC4 encryption = new RC4(key, 256); + byte[] buff = new byte[data.Length]; + Buffer.BlockCopy(data, 0, buff, 0, data.Length); + encryption.Cipher(buff); + return buff; + } + } +} diff --git a/src/WhatsAppApi/Helper/RC4.cs b/src/WhatsAppApi/Helper/RC4.cs new file mode 100644 index 0000000..456a342 --- /dev/null +++ b/src/WhatsAppApi/Helper/RC4.cs @@ -0,0 +1,55 @@ +using System; + +namespace WhatsAppApi.Helper +{ + internal class RC4 + { + private int i = 0; + private int j = 0; + private int[] s; + + public RC4(byte[] key, int drop) + { + s = new int[256]; + while (this.i < this.s.Length) + { + this.s[this.i] = this.i; + this.i++; + } + this.j = 0; + this.i = 0; + while (this.i < 0x100) + { + this.j = ((this.j + key[this.i % key.Length]) + this.s[this.i]) & 0xff; + Swap(this.s, this.i, this.j); + this.i++; + } + this.i = this.j = 0; + this.Cipher(new byte[drop]); + } + + public void Cipher(byte[] data) + { + this.Cipher(data, 0, data.Length); + } + + public void Cipher(byte[] data, int offset, int length) + { + for (int i = length; i > 0; i--) + { + this.i = (this.i + 1) & 0xff; + this.j = (this.j + this.s[this.i]) & 0xff; + Swap(this.s, this.i, this.j); + int index = offset++; + data[index] = (byte)(data[index] ^ this.s[(this.s[this.i] + this.s[this.j]) & 0xff]); + } + } + + private static void Swap(T[] s, int i, int j) + { + T num = s[i]; + s[i] = s[j]; + s[j] = num; + } + } +} diff --git a/src/WhatsAppPort/app.config b/src/WhatsAppPort/app.config new file mode 100644 index 0000000..c5e1dae --- /dev/null +++ b/src/WhatsAppPort/app.config @@ -0,0 +1,3 @@ + + + diff --git a/src/WhatsTest/app.config b/src/WhatsTest/app.config new file mode 100644 index 0000000..c5e1dae --- /dev/null +++ b/src/WhatsTest/app.config @@ -0,0 +1,3 @@ + + + From fe378c2f5a334152d2487e0d11b6c22d85f0cb63 Mon Sep 17 00:00:00 2001 From: hermanho Date: Wed, 7 Nov 2012 21:29:58 +0800 Subject: [PATCH 005/271] Fixed the length issue --- src/WhatsAppApi/Account/WhatsUserManager.cs | 6 +- src/WhatsAppApi/Helper/BinTreeNodeReader.cs | 50 +++++++-- src/WhatsAppApi/Helper/BinTreeNodeWriter.cs | 19 ++-- .../Helper/DisconnectedException.cs | 26 +++++ src/WhatsAppApi/Helper/Encryption.cs | 16 +++ src/WhatsAppApi/Helper/TicketManager.cs | 16 ++- src/WhatsAppApi/Parser/FMessage.cs | 16 +++ src/WhatsAppApi/WhatsApp.cs | 103 ++++++++++-------- src/WhatsAppApi/WhatsAppApi.csproj | 6 +- src/WhatsAppApi/WhatsNetwork.cs | 13 ++- src/WhatsAppApi/WhatsSendHandler.cs | 12 +- .../Properties/Resources.Designer.cs | 66 +++++------ .../Properties/Settings.Designer.cs | 24 ++-- src/WhatsAppPort/WhatsAppPort.csproj | 7 +- src/WhatsAppPort/frmForm.Designer.cs | 1 + src/WhatsAppPort/frmUserChat.Designer.cs | 1 + src/WhatsTest/WhatsTest.csproj | 8 +- 17 files changed, 261 insertions(+), 129 deletions(-) create mode 100644 src/WhatsAppApi/Helper/DisconnectedException.cs diff --git a/src/WhatsAppApi/Account/WhatsUserManager.cs b/src/WhatsAppApi/Account/WhatsUserManager.cs index fd5497d..7ada04a 100644 --- a/src/WhatsAppApi/Account/WhatsUserManager.cs +++ b/src/WhatsAppApi/Account/WhatsUserManager.cs @@ -26,7 +26,11 @@ public WhatsUser CreateUser(string jid, string nickname = "") if (this.userList.ContainsKey(jid)) return this.userList[jid]; - var tmpUser = new WhatsUser(jid, WhatsConstants.WhatsAppServer, nickname); + string server = WhatsConstants.WhatsAppServer; + if (jid.Contains("-")) + server = WhatsConstants.WhatsGroupChat; + + var tmpUser = new WhatsUser(jid, server, nickname); this.userList.Add(jid, tmpUser); return tmpUser; } diff --git a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs index 370e582..4818c94 100644 --- a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -91,24 +91,47 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) //if (stanzaSize > 0) if (size > 0) { + byte[] dataReal = null; + byte[] data = new byte[size]; + Buffer.BlockCopy(this.buffer.ToArray(), 0, data, 0, data.Length); + + byte[] data2 = new byte[data.Length]; + Buffer.BlockCopy(data, 0, data2, 0, data.Length); + if (isEncrypted && Encryptionkey != null) { - byte[] data = this.buffer.ToArray(); byte[] hashServerByte = new byte[4]; - byte[] packet = new byte[data.Length - 4]; + byte[] packet = new byte[size - 4]; Buffer.BlockCopy(data, 0, hashServerByte, 0, 4); - Buffer.BlockCopy(data, 4, packet, 0, data.Length - 4); + Buffer.BlockCopy(data, 4, packet, 0, size - 4); System.Security.Cryptography.HMACSHA1 h = new System.Security.Cryptography.HMACSHA1(this.Encryptionkey); byte[] hashByte = new byte[4]; Buffer.BlockCopy(h.ComputeHash(packet, 0, packet.Length), 0, hashByte, 0, 4); - byte[] dataReal = Encryption.WhatsappDecrypt(this.Encryptionkey, packet); + // 20121107 not sure why the packet is indicated an ecrypted but the hmcash1 is incorrect + if (hashServerByte.SequenceEqual(hashByte)) + { + this.buffer.RemoveRange(0, 4); + dataReal = Encryption.WhatsappDecrypt(this.Encryptionkey, packet); - this.buffer.Clear(); - this.buffer.AddRange(dataReal); + for (int i = 0; i < size - 4; i++) + { + this.buffer[i] = dataReal[i]; + } + } + else + { + //dataReal = data; + } + + //this.buffer.Clear(); + //this.buffer.AddRange(dataReal); } - return this.nextTreeInternal(); + ProtocolTreeNode node = this.nextTreeInternal(); + if (node != null) + this.DebugPrint(node.NodeString("RECVD: ")); + return node; } return null; } @@ -122,7 +145,7 @@ protected string getToken(int token) } else { - throw new Exception("BinTreeNodeReader->getToken: Invalid token $token"); + //throw new Exception("BinTreeNodeReader->getToken: Invalid token $token"); } return ret; } @@ -297,7 +320,8 @@ protected int readListSize(int token) } else { - throw new Exception("BinTreeNodeReader->readListSize: Invalid token $token"); + //throw new Exception("BinTreeNodeReader->readListSize: Invalid token $token"); + size = 0; } return size; } @@ -426,6 +450,12 @@ protected byte[] fillArray(int len) } return ret; } + protected void DebugPrint(string debugMsg) + { + if (WhatsApp.DEBUG && debugMsg.Length > 0) + { + Console.WriteLine(debugMsg); + } + } } - } diff --git a/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs b/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs index d090b02..3a71a7a 100644 --- a/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs +++ b/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs @@ -92,6 +92,7 @@ public byte[] Write(ProtocolTreeNode node, bool encrypt = true) } else { + this.DebugPrint(node.NodeString("SENT: ")); this.writeInternal(node); } return this.flushBuffer(encrypt); @@ -110,16 +111,15 @@ public byte[] Write(ProtocolTreeNode node, bool encrypt = true) protected byte[] flushBuffer(bool encrypt = true) { byte[] data = this.buffer.ToArray(); - byte[] ret = new byte[data.Length + 3]; - //byte[] ret = new byte[256]; byte[] size = this.GetInt24(data.Length); - Buffer.BlockCopy(size, 0, ret, 0, 3); if (encrypt && this.Encryptionkey != null) { data = Encryption.WhatsappEncrypt(Encryptionkey, data, true); size[0] |= 0x8; } + byte[] ret = new byte[data.Length + 3]; + Buffer.BlockCopy(size, 0, ret, 0, 3); Buffer.BlockCopy(data, 0, ret,3, data.Length); this.buffer = new List(); return ret; @@ -363,15 +363,12 @@ protected void writeToken(int token) } } - /// - /// Check if chr ist correct - /// - /// - /// - private string chr(int value) + protected void DebugPrint(string debugMsg) { - char tmpRealValue = (char)(value); - return Convert.ToString(tmpRealValue); + if (WhatsApp.DEBUG && debugMsg.Length > 0) + { + Console.WriteLine(debugMsg); + } } } } diff --git a/src/WhatsAppApi/Helper/DisconnectedException.cs b/src/WhatsAppApi/Helper/DisconnectedException.cs new file mode 100644 index 0000000..4447349 --- /dev/null +++ b/src/WhatsAppApi/Helper/DisconnectedException.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WhatsAppApi.Helper +{ + class ConnectionException : Exception + { + public ConnectionException() + : base() + { + + } + public ConnectionException(string message) + : base(message) + { + + } + public ConnectionException(string message, Exception innerException) + : base(message, innerException) + { + + } + } +} diff --git a/src/WhatsAppApi/Helper/Encryption.cs b/src/WhatsAppApi/Helper/Encryption.cs index 4958b94..63cd5d3 100644 --- a/src/WhatsAppApi/Helper/Encryption.cs +++ b/src/WhatsAppApi/Helper/Encryption.cs @@ -34,6 +34,22 @@ public static byte[] WhatsappEncrypt(byte[] key, byte[] data, bool appendHash) return response; } + public static void DecodeMessage(byte[] key,byte[] buffer, int macOffset, int offset, int length) + { + RC4 rc4 = new RC4(key, 256); + HMACSHA1 h = new HMACSHA1(key); + byte[] buffer2 = h.ComputeHash(buffer, offset, length); + for (int i = 0; i < 4; i++) + { + if (buffer2[i] != buffer[macOffset + i]) + { + return; + } + } + rc4.Cipher(buffer, offset, length); + Array.Copy(buffer, offset, buffer, macOffset, length); + } + public static byte[] WhatsappDecrypt(byte[] key, byte[] data) { RC4 encryption = new RC4(key, 256); diff --git a/src/WhatsAppApi/Helper/TicketManager.cs b/src/WhatsAppApi/Helper/TicketManager.cs index adf9bac..c2aea58 100644 --- a/src/WhatsAppApi/Helper/TicketManager.cs +++ b/src/WhatsAppApi/Helper/TicketManager.cs @@ -8,15 +8,27 @@ namespace WhatsAppApi.Helper { class TicketManager { - public static string IdBase { get; private set; } + private static TicketManager _instance; + private string idBase; + public static string IdBase + { + get { + if (_instance == null) + _instance = new TicketManager(); + return _instance.idBase; + } + } public TicketManager() { - IdBase = DateTime.Now.Ticks.ToString(); + idBase = DateTime.Now.Ticks.ToString(); } public static string GenerateId() { + if (_instance == null) + _instance = new TicketManager(); + return (IdBase + "-" + TicketCounter.NextTicket()); } } diff --git a/src/WhatsAppApi/Parser/FMessage.cs b/src/WhatsAppApi/Parser/FMessage.cs index 1bebaf9..3c2d6e5 100644 --- a/src/WhatsAppApi/Parser/FMessage.cs +++ b/src/WhatsAppApi/Parser/FMessage.cs @@ -24,6 +24,8 @@ public class FMessage public DateTime? timestamp; public bool wants_receipt; + public WhatsAppApi.Account.WhatsUser User { get; private set; } + public FMessage(Key key) { this.status = Status.Undefined; @@ -31,6 +33,13 @@ public FMessage(Key key) this.key = key; } + internal FMessage(WhatsAppApi.Account.WhatsUser remote_user, bool from_me) + { + this.status = Status.Undefined; + this.gap_behind = true; + this.User = remote_user; + this.key = new Key(remote_user.GetFullJid(), from_me, TicketManager.GenerateId()); + } internal FMessage(string remote_jid, bool from_me) { this.status = Status.Undefined; @@ -45,6 +54,13 @@ public FMessage(string remote_jid, string data, object image) this.thumb_image = image; this.timestamp = new DateTime?(DateTime.Now); } + public FMessage(WhatsAppApi.Account.WhatsUser remote_user, string data, object image) + : this(remote_user, true) + { + this.data = data; + this.thumb_image = image; + this.timestamp = new DateTime?(DateTime.Now); + } public void AcceptVisitor(FMessageVisitor visitor) { diff --git a/src/WhatsAppApi/WhatsApp.cs b/src/WhatsAppApi/WhatsApp.cs index 0579309..447eacc 100644 --- a/src/WhatsAppApi/WhatsApp.cs +++ b/src/WhatsAppApi/WhatsApp.cs @@ -14,14 +14,21 @@ namespace WhatsAppApi { public class WhatsApp { + public enum CONNECTION_STATUS + { + DISCONNECTED, + CONNECTED + } + //private readonly Encoding sysEncoding; private AccountInfo accountinfo; //private Dictionary challengeArray; - private string connectedStatus = "connected"; - private bool debug; - private string disconnectedStatus = "disconnected"; + //private string connectedStatus = "connected"; + public static bool DEBUG; + //private string disconnectedStatus = "disconnected"; private string imei; - private string loginStatus; + //private string loginStatus; + private CONNECTION_STATUS loginStatus; private object messageLock = new object(); private List messageQueue; private string name; @@ -29,14 +36,15 @@ public class WhatsApp private BinTreeNodeReader reader; private BinTreeNodeWriter writer; //private int timeout = 2000; - private int timeout = 100000; + private int timeout = 5000; private WhatsNetwork whatsNetwork; public WhatsSendHandler WhatsSendHandler { get; private set; } public WhatsParser WhatsParser { get; private set; } //protocol 1.2 - public static readonly Encoding SYSEncoding = Encoding.GetEncoding("ISO-8859-1"); + //public static readonly Encoding SYSEncoding = Encoding.GetEncoding("ISO-8859-1"); + public static readonly Encoding SYSEncoding = Encoding.UTF8; private byte[] _encryptionKey; private byte[] _challengeBytes; @@ -50,12 +58,13 @@ public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) this.phoneNumber = phoneNum; this.imei = imei; this.name = nick; - this.debug = debug; + WhatsApp.DEBUG = debug; string[] dict = DecodeHelper.getDictionary(); this.writer = new BinTreeNodeWriter(dict); this.reader = new BinTreeNodeReader(dict); - this.loginStatus = disconnectedStatus; + //this.loginStatus = disconnectedStatus; + this.loginStatus = CONNECTION_STATUS.DISCONNECTED; //this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.sysEncoding, this.timeout); //this.WhatsParser = new WhatsParser(this.whatsNetwork); @@ -127,28 +136,32 @@ public void Login() //this.whatsNetwork.SendData(data); //this.whatsNetwork.SendNode(feat); //this.whatsNetwork.SendNode(auth); - processOutboundData(data); - processOutboundData(feat, false); - processOutboundData(auth, false); + this.whatsNetwork.SendData(data); + this.whatsNetwork.SendData(this.writer.Write(feat, false)); + this.whatsNetwork.SendData(this.writer.Write(auth, false)); this.PollMessages(); //ProtocolTreeNode authResp = this.addAuthResponse(); //this.whatsNetwork.SendNode(authResp); ProtocolTreeNode authResp = this.addAuthResponse_v1_2(); - processOutboundData(authResp, false); + this.whatsNetwork.SendData(this.writer.Write(authResp, false)); int cnt = 0; do { this.PollMessages(); System.Threading.Thread.Sleep(500); + //} while ((cnt++ < 100) && + // (this.loginStatus.Equals(this.disconnectedStatus, StringComparison.OrdinalIgnoreCase))); } while ((cnt++ < 100) && - (this.loginStatus.Equals(this.disconnectedStatus, StringComparison.OrdinalIgnoreCase))); + (this.loginStatus == CONNECTION_STATUS.DISCONNECTED)); } public void Message(string to, string txt) { //var bodyNode = new ProtocolTreeNode("body", null, txt); - var tmpMessage = new FMessage(to, true) { key = { id = TicketCounter.MakeId("mSend_") }, data = txt }; + + //var tmpMessage = new FMessage(to, true) { key = { id = TicketCounter.MakeId("mSend_") }, data = txt }; + var tmpMessage = new FMessage(to, true) { key = { id = TicketManager.GenerateId() }, data = txt }; this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage); } @@ -169,7 +182,25 @@ public void MessageImage(string msgid, string to, string url, string file, strin public void PollMessages() { - this.processInboundData(this.whatsNetwork.ReadData()); + ////somehow disconnected + //if (this.loginStatus == CONNECTION_STATUS.CONNECTED && !this.whatsNetwork.SocketStatus) + //{ + // this.loginStatus = CONNECTION_STATUS.DISCONNECTED; + // this.Connect(); + // this.Login(); + // return; + //} + //try + //{ + this.processInboundData(this.whatsNetwork.ReadData()); + //} + //catch (ConnectionException cex) + //{ + // this.loginStatus = CONNECTION_STATUS.DISCONNECTED; + // this.Connect(); + // this.Login(); + // return; + //} } public void Pong(string msgid) @@ -342,7 +373,7 @@ protected ProtocolTreeNode addFeatures() protected void DebugPrint(string debugMsg) { - if (this.debug && debugMsg.Length > 0) + if (WhatsApp.DEBUG && debugMsg.Length > 0) { Console.WriteLine(debugMsg); } @@ -365,24 +396,7 @@ protected void processChallenge(ProtocolTreeNode node) //change to protocol 1.2 _challengeBytes = node.data; } - - protected void processOutboundData(ProtocolTreeNode node, bool encrypt = true) - { - this.DebugPrint(node.NodeString("SENT: ")); - //this.whatsNetwork.SendNode(node); - this.whatsNetwork.SendData(this.writer.Write(node, encrypt)); - } - protected void processOutboundData(string data) - { - this.DebugPrint("SENT: " + data); - this.whatsNetwork.SendData(data); - } - protected void processOutboundData(byte[] data) - { - this.DebugPrint("SENT: " + WhatsApp.SYSEncoding.GetString(data)); - this.whatsNetwork.SendData(data); - } - + //protected void processInboundData(string data) protected void processInboundData(byte[] data) { @@ -392,31 +406,34 @@ protected void processInboundData(byte[] data) while (node != null) { this.WhatsParser.ParseProtocolNode(node); - this.DebugPrint(node.NodeString("RECVD: ")); - if (node.tag.Equals("challenge", StringComparison.OrdinalIgnoreCase)) + if (ProtocolTreeNode.TagEquals(node, "challenge")) { this.processChallenge(node); } - else if (node.tag.Equals("success", StringComparison.OrdinalIgnoreCase)) + else if (ProtocolTreeNode.TagEquals(node,"success")) { - this.loginStatus = this.connectedStatus; + this.loginStatus = CONNECTION_STATUS.CONNECTED; this.accountinfo = new AccountInfo(node.GetAttribute("status"), node.GetAttribute("kind"), node.GetAttribute("creation"), node.GetAttribute("expiration")); } - else if (node.tag.Equals("failure", StringComparison.OrdinalIgnoreCase)) + else if (ProtocolTreeNode.TagEquals(node,"failure")) { - this.loginStatus = this.disconnectedStatus; + this.loginStatus = CONNECTION_STATUS.DISCONNECTED; } - if (node.tag.Equals("message", StringComparison.OrdinalIgnoreCase)) + if (ProtocolTreeNode.TagEquals(node,"message")) { this.AddMessage(node); this.sendMessageReceived(node); } - if (node.tag.Equals("iq", StringComparison.OrdinalIgnoreCase) + if (ProtocolTreeNode.TagEquals(node,"stream:error")) + { + Console.Write(node.NodeString()); + } + if (ProtocolTreeNode.TagEquals(node,"iq") && node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) - && node.children.First().tag.Equals("ping", StringComparison.OrdinalIgnoreCase)) + && ProtocolTreeNode.TagEquals(node.children.First(), "ping")) { this.Pong(node.GetAttribute("id")); } diff --git a/src/WhatsAppApi/WhatsAppApi.csproj b/src/WhatsAppApi/WhatsAppApi.csproj index e1bd7f9..54aad8d 100644 --- a/src/WhatsAppApi/WhatsAppApi.csproj +++ b/src/WhatsAppApi/WhatsAppApi.csproj @@ -10,8 +10,9 @@ Properties WhatsAppApi WhatsAppApi - v3.5 + v4.5 512 + true @@ -21,6 +22,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -29,6 +31,7 @@ TRACE prompt 4 + false @@ -46,6 +49,7 @@ + diff --git a/src/WhatsAppApi/WhatsNetwork.cs b/src/WhatsAppApi/WhatsNetwork.cs index 5a36320..daef18f 100644 --- a/src/WhatsAppApi/WhatsNetwork.cs +++ b/src/WhatsAppApi/WhatsNetwork.cs @@ -33,12 +33,13 @@ public void Connect() this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.socket.Connect(this.whatsHost, this.whatsPort); this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.recvTimeout); + //var tmpNetStream = new NetworkStream(this.socket); //this.streamReader = new StreamReader(tmpNetStream); //this.streamWriter = new StreamWriter(tmpNetStream); if (!this.socket.Connected) - throw new System.IO.IOException("Cannot connect"); + throw new ConnectionException("Cannot connect"); } //public string ReadData() @@ -114,7 +115,7 @@ private byte[] Socket_read(int length) { if (!socket.Connected) { - throw new System.IO.IOException("Disconnected"); + throw new ConnectionException(); } var buff = new byte[length]; @@ -128,12 +129,13 @@ private byte[] Socket_read(int length) if (excpt.SocketErrorCode == SocketError.TimedOut) { Console.WriteLine("Socket-Timout"); + //throw new ConnectionException("Timeout", excpt); return null; } else { Console.WriteLine("Unbehandelter Fehler bei Sockerread: {0}", excpt); - throw excpt; + throw new ConnectionException("error",excpt); } } @@ -152,5 +154,10 @@ private void Socket_send(byte[] data) { this.socket.Send(data); } + + public bool SocketStatus + { + get { return socket.Connected; } + } } } diff --git a/src/WhatsAppApi/WhatsSendHandler.cs b/src/WhatsAppApi/WhatsSendHandler.cs index 58b1209..0a440f4 100644 --- a/src/WhatsAppApi/WhatsSendHandler.cs +++ b/src/WhatsAppApi/WhatsSendHandler.cs @@ -899,11 +899,13 @@ private void SendReceiptAck(string to, string id, string receiptType) internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode) { //ProtocolTreeNode node = null; - var serverNode = new ProtocolTreeNode("server", null); - var xNode = new ProtocolTreeNode("x", new[] { new KeyValue("xmlns", "jabber:x:event") }, serverNode); - IEnumerable node = (from n in new ProtocolTreeNode[] { xNode, pNode } - where n != null - select n).ToArray(); + //var serverNode = new ProtocolTreeNode("server", null); + //var xNode = new ProtocolTreeNode("x", new[] { new KeyValue("xmlns", "jabber:x:event") }, serverNode); + + //IEnumerable node = (from n in new ProtocolTreeNode[] { xNode, pNode } + // where n != null + // select n).ToArray(); + ProtocolTreeNode[] node = new ProtocolTreeNode[] { pNode }; return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, node); } diff --git a/src/WhatsAppPort/Properties/Resources.Designer.cs b/src/WhatsAppPort/Properties/Resources.Designer.cs index c57ffd3..3850a60 100644 --- a/src/WhatsAppPort/Properties/Resources.Designer.cs +++ b/src/WhatsAppPort/Properties/Resources.Designer.cs @@ -1,69 +1,61 @@ //------------------------------------------------------------------------------ // -// Dieser Code wurde von einem Tool generiert. -// Laufzeitversion:4.0.30319.269 +// This code was generated by a tool. +// Runtime Version:4.0.30319.17929 // -// Änderungen an dieser Datei können fehlerhaftes Verhalten verursachen und gehen verloren, wenn -// der Code neu generiert wird. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ -namespace WhatsAppPort.Properties -{ - - +namespace WhatsAppPort.Properties { + using System; + + /// - /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse - // über ein Tool wie ResGen oder Visual Studio automatisch generiert. - // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen - // mit der Option /str erneut aus, oder erstellen Sie Ihr VS-Projekt neu. + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// - /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WhatsAppPort.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// - /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle - /// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden. + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } diff --git a/src/WhatsAppPort/Properties/Settings.Designer.cs b/src/WhatsAppPort/Properties/Settings.Designer.cs index 012edfd..5fb37ca 100644 --- a/src/WhatsAppPort/Properties/Settings.Designer.cs +++ b/src/WhatsAppPort/Properties/Settings.Designer.cs @@ -1,28 +1,24 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.269 +// Runtime Version:4.0.30319.17929 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace WhatsAppPort.Properties -{ - - +namespace WhatsAppPort.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/src/WhatsAppPort/WhatsAppPort.csproj b/src/WhatsAppPort/WhatsAppPort.csproj index bb6b137..37cd41c 100644 --- a/src/WhatsAppPort/WhatsAppPort.csproj +++ b/src/WhatsAppPort/WhatsAppPort.csproj @@ -10,8 +10,9 @@ Properties WhatsAppPort WhatsAppPort - v3.5 + v4.5 512 + x86 @@ -22,6 +23,7 @@ DEBUG;TRACE prompt 4 + false x86 @@ -31,6 +33,7 @@ TRACE prompt 4 + false @@ -95,7 +98,9 @@ True Resources.resx + True + SettingsSingleFileGenerator Settings.Designer.cs diff --git a/src/WhatsAppPort/frmForm.Designer.cs b/src/WhatsAppPort/frmForm.Designer.cs index 7aa56ec..6e2099b 100644 --- a/src/WhatsAppPort/frmForm.Designer.cs +++ b/src/WhatsAppPort/frmForm.Designer.cs @@ -67,6 +67,7 @@ private void InitializeComponent() // // frmForm // + this.AcceptButton = this.btnAddContact; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(228, 380); diff --git a/src/WhatsAppPort/frmUserChat.Designer.cs b/src/WhatsAppPort/frmUserChat.Designer.cs index 1aa2cd5..1283f35 100644 --- a/src/WhatsAppPort/frmUserChat.Designer.cs +++ b/src/WhatsAppPort/frmUserChat.Designer.cs @@ -90,6 +90,7 @@ private void InitializeComponent() // // frmUserChat // + this.AcceptButton = this.btnSend; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(540, 355); diff --git a/src/WhatsTest/WhatsTest.csproj b/src/WhatsTest/WhatsTest.csproj index d00e841..0c42fb1 100644 --- a/src/WhatsTest/WhatsTest.csproj +++ b/src/WhatsTest/WhatsTest.csproj @@ -10,8 +10,9 @@ Properties WhatsTest WhatsTest - v3.5 + v4.5 512 + x86 @@ -22,6 +23,7 @@ DEBUG;TRACE prompt 4 + false x86 @@ -31,6 +33,7 @@ TRACE prompt 4 + false @@ -50,6 +53,9 @@ WhatsAppApi + + + + From 4df09edc3be3503943511d11e6229300132917e1 Mon Sep 17 00:00:00 2001 From: hermanho Date: Thu, 8 Nov 2012 04:42:01 +0800 Subject: [PATCH 011/271] Fixed up some issue but still error... --- src/WhatsAppApi/Helper/BinTreeNodeReader.cs | 71 ++++++++++--------- src/WhatsAppApi/Helper/BinTreeNodeWriter.cs | 11 ++- src/WhatsAppApi/Helper/DecodeHelper.cs | 18 ++++- src/WhatsAppApi/Helper/Encryption.cs | 1 - .../Helper/IncompleteMessageException.cs | 10 +-- src/WhatsAppApi/Helper/TicketManager.cs | 3 +- src/WhatsAppApi/WhatsApp.cs | 26 ++++++- src/WhatsAppApi/WhatsNetwork.cs | 50 +++++++------ src/WhatsAppApi/WhatsSendHandler.cs | 12 ++-- 9 files changed, 128 insertions(+), 74 deletions(-) diff --git a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs index 4818c94..7e8b5d6 100644 --- a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -74,7 +74,16 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) //Change to protocol 1.2 int stanzaSize = this.peekInt24(); int flags = (stanzaSize >> 20); - int size = ((stanzaSize & 0xF0000) >> 16) | ((stanzaSize & 0xFF00) >> 8) | (stanzaSize & 0xFF); + //int size = ((stanzaSize & 0xF0000) >> 16) | ((stanzaSize & 0xFF00) >> 8) | (stanzaSize & 0xFF); + int size = (stanzaSize & 0xFFFFF); + + if (this.buffer.Count >= 3) + { + int stanzaSize2 = (this.buffer[0] << 0x10) + (this.buffer[1] << 8) + this.buffer[2]; + int flags2 = (stanzaSize2 & 0xf00000) >> 20; + int size2 = (stanzaSize2 & 0xfffff); + System.Diagnostics.Debug.Assert(size == size2); + } bool isEncrypted = ((flags & 8) != 0); // 8 = (1 << 4) // Read node and decrypt @@ -86,7 +95,6 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) exception.setInput(this.buffer.ToArray()); throw exception; } - this.readInt24(); //if (stanzaSize > 0) if (size > 0) @@ -95,13 +103,11 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) byte[] data = new byte[size]; Buffer.BlockCopy(this.buffer.ToArray(), 0, data, 0, data.Length); - byte[] data2 = new byte[data.Length]; - Buffer.BlockCopy(data, 0, data2, 0, data.Length); + byte[] packet = new byte[size - 4]; if (isEncrypted && Encryptionkey != null) { byte[] hashServerByte = new byte[4]; - byte[] packet = new byte[size - 4]; Buffer.BlockCopy(data, 0, hashServerByte, 0, 4); Buffer.BlockCopy(data, 4, packet, 0, size - 4); @@ -120,13 +126,6 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) this.buffer[i] = dataReal[i]; } } - else - { - //dataReal = data; - } - - //this.buffer.Clear(); - //this.buffer.AddRange(dataReal); } ProtocolTreeNode node = this.nextTreeInternal(); if (node != null) @@ -209,7 +208,7 @@ protected byte[] readBytes(int token) } else if (token == 0) { - //ret = ""; + ret = new byte[0]; } else if (token == 0xfc) { @@ -219,9 +218,8 @@ protected byte[] readBytes(int token) else if (token == 0xfd) { int size = this.readInt24(); - ret = this.fillArray(size); } - else if (token == 0xfe) + else if (token == 0xfe) { int tmpToken = this.readInt8(); ret = WhatsApp.SYSEncoding.GetBytes(this.getToken(tmpToken + 0xf5)); @@ -261,34 +259,35 @@ protected ProtocolTreeNode nextTreeInternal() { int token = this.readInt8(); int size = this.readListSize(token); - token = this.readInt8(); - - if (token == 1) + int token2 = this.readInt8(); + if (token2 == 1) { var attributes = this.readAttributes(size); return new ProtocolTreeNode("start", attributes); } - else if (token == 2) + if (token2 == 2) { return null; } //string tag = this.readString(token); - byte[] tagB = this.readBytes(token); - string tag = WhatsApp.SYSEncoding.GetString(tagB); + string tag = WhatsApp.SYSEncoding.GetString(this.readBytes(token2)); var tmpAttributes = this.readAttributes(size); - + //if (size == 0 || string.IsNullOrWhiteSpace(tag)) + //{ + // return null; + //} if ((size % 2) == 1) { return new ProtocolTreeNode(tag, tmpAttributes); } - token = this.readInt8(); - if (this.isListTag(token)) + int token3 = this.readInt8(); + if (this.isListTag(token3)) { //return new ProtocolTreeNode(tag, tmpAttributes, this.readList(token), ""); - return new ProtocolTreeNode(tag, tmpAttributes, this.readList(token), null); + return new ProtocolTreeNode(tag, tmpAttributes, this.readList(token3)); } //return new ProtocolTreeNode(tag, tmpAttributes, WhatsApp.SYSEncoding.GetBytes(this.readString(token))); - return new ProtocolTreeNode(tag, tmpAttributes, this.readBytes(token)); + return new ProtocolTreeNode(tag, tmpAttributes, null, this.readBytes(token3)); } protected bool isListTag(int token) @@ -310,7 +309,11 @@ protected List readList(int token) protected int readListSize(int token) { int size = 0; - if (token == 0xf8) + if (token == 0) + { + size = 0; + } + else if (token == 0xf8) { size = this.readInt8(); } @@ -320,8 +323,7 @@ protected int readListSize(int token) } else { - //throw new Exception("BinTreeNodeReader->readListSize: Invalid token $token"); - size = 0; + throw new Exception("BinTreeNodeReader->readListSize: Invalid token $token"); } return size; } @@ -337,9 +339,10 @@ protected int peekInt24() //} if (this.buffer.Count >= 3) { - ret = this.buffer[0] << 16; - ret |= this.buffer[1] << 8; - ret |= this.buffer[2] << 0; + // ret = this.buffer[0] << 16; + // ret |= this.buffer[1] << 8; + // ret |= this.buffer[2] << 0; + ret = (this.buffer[0] << 16) + (this.buffer[1] << 8) + this.buffer[2]; } return ret; } @@ -448,6 +451,10 @@ protected byte[] fillArray(int len) Buffer.BlockCopy(this.buffer.ToArray(), 0, ret, 0, len); this.buffer.RemoveRange(0, len); } + else + { + throw new Exception(); + } return ret; } protected void DebugPrint(string debugMsg) diff --git a/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs b/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs index caf7be4..f000c96 100644 --- a/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs +++ b/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs @@ -111,16 +111,21 @@ public byte[] Write(ProtocolTreeNode node, bool encrypt = true) protected byte[] flushBuffer(bool encrypt = true) { byte[] data = this.buffer.ToArray(); - byte[] size = this.GetInt24(data.Length); + byte[] data2 = new byte[data.Length + 4]; + Buffer.BlockCopy(data, 0, data2, 0, data.Length); + int len = data.Length; + byte[] size = this.GetInt24(len); if (encrypt && this.Encryptionkey != null) { data = Encryption.WhatsappEncrypt(Encryptionkey, data, true); - size[0] |= 0x8; + len += 4; + size = this.GetInt24(len); + size[0] |= (1 << 4); } byte[] ret = new byte[data.Length + 3]; Buffer.BlockCopy(size, 0, ret, 0, 3); - Buffer.BlockCopy(data, 0, ret,3, data.Length); + Buffer.BlockCopy(data, 0, ret, 3, len); this.buffer = new List(); return ret; } diff --git a/src/WhatsAppApi/Helper/DecodeHelper.cs b/src/WhatsAppApi/Helper/DecodeHelper.cs index 849e607..51a8f4b 100644 --- a/src/WhatsAppApi/Helper/DecodeHelper.cs +++ b/src/WhatsAppApi/Helper/DecodeHelper.cs @@ -26,9 +26,9 @@ public static string[] SplitStringN(this string value, int count) { returnList.Add(value.Substring(i, count)); } - if (value.Length%count != 0) + if (value.Length % count != 0) { - int tmpRest = value.Length%count; + int tmpRest = value.Length % count; returnList.Add(value.Substring(value.Length - 1 - tmpRest, tmpRest)); } return returnList.ToArray(); @@ -291,7 +291,7 @@ public static string[] getDictionary() //dictList[248] = "XXX"; // change to protocol 1.2 - dictList = new string[237]; + dictList = new string[249]; dictList[0] = null; dictList[1] = null; dictList[2] = null; @@ -529,6 +529,18 @@ public static string[] getDictionary() dictList[234] = "Xylophone.caf"; dictList[235] = "1"; dictList[236] = "WAUTH-1"; + dictList[237] = null; + dictList[238] = null; + dictList[239] = null; + dictList[240] = null; + dictList[241] = null; + dictList[242] = null; + dictList[243] = null; + dictList[244] = null; + dictList[245] = null; + dictList[246] = null; + dictList[247] = null; + dictList[248] = "XXX"; return dictList; } diff --git a/src/WhatsAppApi/Helper/Encryption.cs b/src/WhatsAppApi/Helper/Encryption.cs index bc88592..84269be 100644 --- a/src/WhatsAppApi/Helper/Encryption.cs +++ b/src/WhatsAppApi/Helper/Encryption.cs @@ -31,7 +31,6 @@ public static byte[] WhatsappEncrypt(byte[] key, byte[] data, bool appendHash) return response; } - public static byte[] WhatsappDecrypt(byte[] key, byte[] data) { RC4 encryption = new RC4(key, 256); diff --git a/src/WhatsAppApi/Helper/IncompleteMessageException.cs b/src/WhatsAppApi/Helper/IncompleteMessageException.cs index 0ba45b3..0eeb08d 100644 --- a/src/WhatsAppApi/Helper/IncompleteMessageException.cs +++ b/src/WhatsAppApi/Helper/IncompleteMessageException.cs @@ -9,7 +9,6 @@ class IncompleteMessageException : Exception { private int code; private string message; - private string input; private byte[] buffer; public IncompleteMessageException(string message, int code = 0) @@ -17,18 +16,13 @@ public IncompleteMessageException(string message, int code = 0) this.message = message; this.code = code; } - - public void setInput(string input) - { - this.input = input; - } public void setInput(byte[] input) { this.buffer = input; } - public string getInput() + public byte[] getInput() { - return this.input; + return this.buffer; } } } diff --git a/src/WhatsAppApi/Helper/TicketManager.cs b/src/WhatsAppApi/Helper/TicketManager.cs index c2aea58..831a4cb 100644 --- a/src/WhatsAppApi/Helper/TicketManager.cs +++ b/src/WhatsAppApi/Helper/TicketManager.cs @@ -21,7 +21,8 @@ public static string IdBase public TicketManager() { - idBase = DateTime.Now.Ticks.ToString(); + //idBase = DateTime.Now.Ticks.ToString(); + idBase = Func.GetNowUnixTimestamp().ToString(); } public static string GenerateId() diff --git a/src/WhatsAppApi/WhatsApp.cs b/src/WhatsAppApi/WhatsApp.cs index 3fe3649..73b4878 100644 --- a/src/WhatsAppApi/WhatsApp.cs +++ b/src/WhatsAppApi/WhatsApp.cs @@ -47,6 +47,7 @@ public enum CONNECTION_STATUS public static readonly Encoding SYSEncoding = Encoding.UTF8; private byte[] _encryptionKey; private byte[] _challengeBytes; + private List _incompleteBytes; //array("sec" => 2, "usec" => 0); public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) @@ -71,6 +72,8 @@ public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.timeout); this.WhatsParser = new WhatsParser(this.whatsNetwork, this.writer); this.WhatsSendHandler = this.WhatsParser.WhatsSendHandler; + + _incompleteBytes = new List(); } public void AddMessage(ProtocolTreeNode node) @@ -86,6 +89,11 @@ public void Connect() { this.whatsNetwork.Connect(); } + public void Disconnect() + { + this.whatsNetwork.Connect(); + this.loginStatus = CONNECTION_STATUS.DISCONNECTED; + } public string encryptPassword() { @@ -149,7 +157,7 @@ public void Login() do { this.PollMessages(); - System.Threading.Thread.Sleep(500); + System.Threading.Thread.Sleep(50); //} while ((cnt++ < 100) && // (this.loginStatus.Equals(this.disconnectedStatus, StringComparison.OrdinalIgnoreCase))); } while ((cnt++ < 100) && @@ -354,6 +362,15 @@ protected void processInboundData(byte[] data) { try { + //List combined = new List(); + //foreach (IncompleteMessageException e in _incompleteBytes) + //{ + // combined.AddRange(e.getInput()); + //} + //_incompleteBytes.Clear(); + //if (data !=null) + //combined.AddRange(data); + //var node = this.reader.nextTree(combined.ToArray()); var node = this.reader.nextTree(data); while (node != null) { @@ -389,11 +406,18 @@ protected void processInboundData(byte[] data) { this.Pong(node.GetAttribute("id")); } + if (ProtocolTreeNode.TagEquals(node, "Replaced by new connection")) + { + this.Connect(); + this.Login(); + } node = this.reader.nextTree(); } } catch (IncompleteMessageException e) { + //_incompleteBytes.Add(e); + //this.PollMessages(); } } diff --git a/src/WhatsAppApi/WhatsNetwork.cs b/src/WhatsAppApi/WhatsNetwork.cs index daef18f..7151c24 100644 --- a/src/WhatsAppApi/WhatsNetwork.cs +++ b/src/WhatsAppApi/WhatsNetwork.cs @@ -42,6 +42,12 @@ public void Connect() throw new ConnectionException("Cannot connect"); } + public void Disconenct() + { + if (this.socket.Connected) + this.socket.Disconnect(true); + } + //public string ReadData() //{ // string buff = ""; @@ -57,13 +63,14 @@ public byte[] ReadData() { List buff = new List(); byte[] ret = Socket_read(1024); - if (ret != null) - { - buff.AddRange(this.incomplete_message); - buff.AddRange(ret); - this.incomplete_message = new List(); - } - return buff.ToArray(); + //if (ret != null) + //{ + // buff.AddRange(this.incomplete_message); + // buff.AddRange(ret); + // this.incomplete_message = new List(); + //} + //return buff.ToArray(); + return ret; } public void SendData(string data) @@ -120,24 +127,27 @@ private byte[] Socket_read(int length) var buff = new byte[length]; int receiveLength = 0; - try - { - receiveLength = socket.Receive(buff, 0, length, 0); - } - catch (SocketException excpt) + do { - if (excpt.SocketErrorCode == SocketError.TimedOut) + try { - Console.WriteLine("Socket-Timout"); - //throw new ConnectionException("Timeout", excpt); - return null; + receiveLength = socket.Receive(buff, 0, length, 0); } - else + catch (SocketException excpt) { - Console.WriteLine("Unbehandelter Fehler bei Sockerread: {0}", excpt); - throw new ConnectionException("error",excpt); + if (excpt.SocketErrorCode == SocketError.TimedOut) + { + Console.WriteLine("Socket-Timout"); + //throw new ConnectionException("Timeout", excpt); + return null; + } + else + { + Console.WriteLine("Unbehandelter Fehler bei Sockerread: {0}", excpt); + throw new ConnectionException("error", excpt); + } } - } + } while (receiveLength <= 0); byte[] tmpRet = new byte[receiveLength]; if (receiveLength > 0) diff --git a/src/WhatsAppApi/WhatsSendHandler.cs b/src/WhatsAppApi/WhatsSendHandler.cs index 0a440f4..55b685e 100644 --- a/src/WhatsAppApi/WhatsSendHandler.cs +++ b/src/WhatsAppApi/WhatsSendHandler.cs @@ -59,7 +59,6 @@ public void SendAddParticipants(string gjid, IEnumerable participants, A public void SendAvailableForChat(string nickName) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName) }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -799,7 +798,7 @@ internal void SendGetGroups(string id, string type) internal void SendMessageWithBody(FMessage message) { //var child = new ProtocolTreeNode("body", null, message.data); - var child = new ProtocolTreeNode("body", null, WhatsApp.SYSEncoding.GetBytes(message.data)); + var child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); //this.whatsNetwork.SendNode(GetMessageNode(message, child)); this.whatsNetwork.SendData(this._binWriter.Write(GetMessageNode(message, child))); } @@ -900,13 +899,16 @@ internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNo { //ProtocolTreeNode node = null; //var serverNode = new ProtocolTreeNode("server", null); + ////if (message.key.remote_jid.Contains('@')) + //// serverNode.data = WhatsApp.SYSEncoding.GetBytes(message.key.remote_jid.Split('@')[1]); //var xNode = new ProtocolTreeNode("x", new[] { new KeyValue("xmlns", "jabber:x:event") }, serverNode); - //IEnumerable node = (from n in new ProtocolTreeNode[] { xNode, pNode } // where n != null // select n).ToArray(); - ProtocolTreeNode[] node = new ProtocolTreeNode[] { pNode }; - return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, node); + + //ProtocolTreeNode[] node = new ProtocolTreeNode[] { pNode }; + //return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, node); + return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, pNode); } public static ProtocolTreeNode GetSubjectMessage(string to, string id, ProtocolTreeNode child) From 933105976eb4284a48ac569242752a519b1c9be9 Mon Sep 17 00:00:00 2001 From: Luis Rodriguez Date: Thu, 8 Nov 2012 01:14:43 +0100 Subject: [PATCH 012/271] Extended peekXInt methods by adding an optional offset parameter --- src/WhatsAppApi/Helper/BinTreeNodeReader.cs | 43 ++++++++++++++------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs index 7e8b5d6..9ac6c6e 100644 --- a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -70,12 +70,19 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) this.buffer.AddRange(pInput); } - //int stanzaSize = this.peekInt16(); - //Change to protocol 1.2 - int stanzaSize = this.peekInt24(); - int flags = (stanzaSize >> 20); - //int size = ((stanzaSize & 0xF0000) >> 16) | ((stanzaSize & 0xFF00) >> 8) | (stanzaSize & 0xFF); - int size = (stanzaSize & 0xFFFFF); + ////int stanzaSize = this.peekInt16(); + ////Change to protocol 1.2 + //int stanzaSize = this.peekInt24(); + //int flags = (stanzaSize >> 20); + ////int size = ((stanzaSize & 0xF0000) >> 16) | ((stanzaSize & 0xFF00) >> 8) | (stanzaSize & 0xFF); + //int size = (stanzaSize & 0xFFFFF); + + // Ported from the allegedly working PHP version ~lrg + int stanzaFlag = (this.peekInt8() & 0xF0) >> 4; + int stanzaSize = this.peekInt16(1); + + int flags = stanzaFlag; + int size = stanzaSize; if (this.buffer.Count >= 3) { @@ -328,7 +335,17 @@ protected int readListSize(int token) return size; } - protected int peekInt24() + protected int peekInt8(int offset = 0) + { + int ret = 0; + + if (this.buffer.Count >= offset + 1) + ret = this.buffer[offset]; + + return ret; + } + + protected int peekInt24(int offset = 0) { int ret = 0; //if (this.input.Length >= 3) @@ -337,12 +354,12 @@ protected int peekInt24() // ret |= (int)this.input[1] << 8; // ret |= (int)this.input[2] << 0; //} - if (this.buffer.Count >= 3) + if (this.buffer.Count >= 3 + offset) { // ret = this.buffer[0] << 16; // ret |= this.buffer[1] << 8; // ret |= this.buffer[2] << 0; - ret = (this.buffer[0] << 16) + (this.buffer[1] << 8) + this.buffer[2]; + ret = (this.buffer[0 + offset] << 16) + (this.buffer[1 + offset] << 8) + this.buffer[2 + offset]; } return ret; } @@ -377,13 +394,13 @@ protected int readInt24() // } // return ret; //} - protected int peekInt16() + protected int peekInt16(int offset = 0) { int ret = 0; - if (this.buffer.Count >= 2) + if (this.buffer.Count >= offset + 2) { - ret = (int)this.buffer[0] << 8; - ret |= (int)this.buffer[1] << 0; + ret = (int)this.buffer[0+offset] << 8; + ret |= (int)this.buffer[1+offset] << 0; } return ret; } From 2830e9639bbef76788a0be1c283d414157f04709 Mon Sep 17 00:00:00 2001 From: Luis Rodriguez Date: Thu, 8 Nov 2012 01:37:14 +0100 Subject: [PATCH 013/271] Refactored out a 'decode' method. Cleaned up nextTree method. Errors not yet fixed --- src/WhatsAppApi/Helper/BinTreeNodeReader.cs | 89 +++++++++------------ 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs index 9ac6c6e..70a7fcb 100644 --- a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -70,13 +70,6 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) this.buffer.AddRange(pInput); } - ////int stanzaSize = this.peekInt16(); - ////Change to protocol 1.2 - //int stanzaSize = this.peekInt24(); - //int flags = (stanzaSize >> 20); - ////int size = ((stanzaSize & 0xF0000) >> 16) | ((stanzaSize & 0xFF00) >> 8) | (stanzaSize & 0xFF); - //int size = (stanzaSize & 0xFFFFF); - // Ported from the allegedly working PHP version ~lrg int stanzaFlag = (this.peekInt8() & 0xF0) >> 4; int stanzaSize = this.peekInt16(1); @@ -84,56 +77,24 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) int flags = stanzaFlag; int size = stanzaSize; - if (this.buffer.Count >= 3) + if (stanzaSize > this.buffer.Count) { - int stanzaSize2 = (this.buffer[0] << 0x10) + (this.buffer[1] << 8) + this.buffer[2]; - int flags2 = (stanzaSize2 & 0xf00000) >> 20; - int size2 = (stanzaSize2 & 0xfffff); - System.Diagnostics.Debug.Assert(size == size2); - } - - bool isEncrypted = ((flags & 8) != 0); // 8 = (1 << 4) // Read node and decrypt - - //if (stanzaSize > this.buffer.Count) - if (size > this.buffer.Count) - { - //Es sind noch nicht alle Daten eingelesen, daher abbrechen und warten bis alles da ist var exception = new IncompleteMessageException("Incomplete message"); exception.setInput(this.buffer.ToArray()); throw exception; } + this.readInt24(); - //if (stanzaSize > 0) - if (size > 0) - { - byte[] dataReal = null; - byte[] data = new byte[size]; - Buffer.BlockCopy(this.buffer.ToArray(), 0, data, 0, data.Length); - byte[] packet = new byte[size - 4]; + bool isEncrypted = (stanzaFlag & 8) != 0; - if (isEncrypted && Encryptionkey != null) - { - byte[] hashServerByte = new byte[4]; - Buffer.BlockCopy(data, 0, hashServerByte, 0, 4); - Buffer.BlockCopy(data, 4, packet, 0, size - 4); - - System.Security.Cryptography.HMACSHA1 h = new System.Security.Cryptography.HMACSHA1(this.Encryptionkey); - byte[] hashByte = new byte[4]; - Buffer.BlockCopy(h.ComputeHash(packet, 0, packet.Length), 0, hashByte, 0, 4); - - // 20121107 not sure why the packet is indicated an ecrypted but the hmcash1 is incorrect - if (hashServerByte.SequenceEqual(hashByte)) - { - this.buffer.RemoveRange(0, 4); - dataReal = Encryption.WhatsappDecrypt(this.Encryptionkey, packet); - - for (int i = 0; i < size - 4; i++) - { - this.buffer[i] = dataReal[i]; - } - } - } + if (isEncrypted && Encryptionkey != null) + { + decode(ref this.buffer, size); + } + + if(stanzaSize > 0) + { ProtocolTreeNode node = this.nextTreeInternal(); if (node != null) this.DebugPrint(node.NodeString("RECVD: ")); @@ -142,6 +103,36 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) return null; } + protected void decode(ref List buffer, int stanzaSize) + { + int size = stanzaSize; + byte[] data = new byte[size]; + byte[] dataReal = null; + Buffer.BlockCopy(buffer.ToArray(), 0, data, 0, size); + + byte[] packet = new byte[size - 4]; + + byte[] hashServerByte = new byte[4]; + Buffer.BlockCopy(data, 0, hashServerByte, 0, 4); + Buffer.BlockCopy(data, 4, packet, 0, size - 4); + + System.Security.Cryptography.HMACSHA1 h = new System.Security.Cryptography.HMACSHA1(this.Encryptionkey); + byte[] hashByte = new byte[4]; + Buffer.BlockCopy(h.ComputeHash(packet, 0, packet.Length), 0, hashByte, 0, 4); + + // 20121107 not sure why the packet is indicated an ecrypted but the hmcash1 is incorrect + if (hashServerByte.SequenceEqual(hashByte)) + { + this.buffer.RemoveRange(0, 4); + dataReal = Encryption.WhatsappDecrypt(this.Encryptionkey, packet); + + for (int i = 0; i < size - 4; i++) + { + this.buffer[i] = dataReal[i]; + } + } + } + protected string getToken(int token) { string ret = ""; From 8719a13afba22ba814e7076165e2588f7fdd1257 Mon Sep 17 00:00:00 2001 From: Luis Rodriguez Date: Thu, 8 Nov 2012 02:29:41 +0100 Subject: [PATCH 014/271] Found & partially fixed bug. Encryption streams were being initialized after every usage. I think there's an advancement, but there are still errors (connection is still closed after a short while). Maybe something related to outgoing packets. --- src/WhatsAppApi/Helper/BinTreeNodeReader.cs | 15 ++++++++------- src/WhatsAppApi/Helper/Encryption.cs | 13 +++++++++---- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs index 70a7fcb..6006a39 100644 --- a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -131,6 +131,10 @@ protected void decode(ref List buffer, int stanzaSize) this.buffer[i] = dataReal[i]; } } + else + { + throw new Exception("Hash doesnt match"); + } } protected string getToken(int token) @@ -142,7 +146,7 @@ protected string getToken(int token) } else { - //throw new Exception("BinTreeNodeReader->getToken: Invalid token $token"); + throw new Exception("BinTreeNodeReader->getToken: Invalid token $token"); } return ret; } @@ -267,13 +271,11 @@ protected ProtocolTreeNode nextTreeInternal() { return null; } + //string tag = this.readString(token); string tag = WhatsApp.SYSEncoding.GetString(this.readBytes(token2)); var tmpAttributes = this.readAttributes(size); - //if (size == 0 || string.IsNullOrWhiteSpace(tag)) - //{ - // return null; - //} + if ((size % 2) == 1) { return new ProtocolTreeNode(tag, tmpAttributes); @@ -281,10 +283,9 @@ protected ProtocolTreeNode nextTreeInternal() int token3 = this.readInt8(); if (this.isListTag(token3)) { - //return new ProtocolTreeNode(tag, tmpAttributes, this.readList(token), ""); return new ProtocolTreeNode(tag, tmpAttributes, this.readList(token3)); } - //return new ProtocolTreeNode(tag, tmpAttributes, WhatsApp.SYSEncoding.GetBytes(this.readString(token))); + return new ProtocolTreeNode(tag, tmpAttributes, null, this.readBytes(token3)); } diff --git a/src/WhatsAppApi/Helper/Encryption.cs b/src/WhatsAppApi/Helper/Encryption.cs index 84269be..18bb7cc 100644 --- a/src/WhatsAppApi/Helper/Encryption.cs +++ b/src/WhatsAppApi/Helper/Encryption.cs @@ -8,14 +8,18 @@ namespace WhatsAppApi.Helper { static class Encryption { + public static RC4 encryptionOutgoing = null; + public static RC4 encryptionIncoming = null; + public static byte[] WhatsappEncrypt(byte[] key, byte[] data, bool appendHash) { - RC4 encryption = new RC4(key, 256); + if(encryptionOutgoing == null) + encryptionOutgoing = new RC4(key, 256); HMACSHA1 h = new HMACSHA1(key); byte[] buff = new byte[data.Length]; Buffer.BlockCopy(data, 0, buff, 0, data.Length); - encryption.Cipher(buff); + encryptionOutgoing.Cipher(buff); byte[] hashByte = h.ComputeHash(buff); byte[] response = new byte[4 + buff.Length]; if (appendHash) @@ -33,10 +37,11 @@ public static byte[] WhatsappEncrypt(byte[] key, byte[] data, bool appendHash) } public static byte[] WhatsappDecrypt(byte[] key, byte[] data) { - RC4 encryption = new RC4(key, 256); + if (encryptionIncoming == null) + encryptionIncoming = new RC4(key, 256); byte[] buff = new byte[data.Length]; Buffer.BlockCopy(data, 0, buff, 0, data.Length); - encryption.Cipher(buff); + encryptionIncoming.Cipher(buff); return buff; } } From e7f2b097d5a044c0448b0e4970b1016a805f4a48 Mon Sep 17 00:00:00 2001 From: hermanho Date: Fri, 9 Nov 2012 16:20:52 +0800 Subject: [PATCH 015/271] Fixed WhatsAppApi cannot receive the body > 255 bytes --- src/WhatsAppApi/Helper/BinTreeNodeReader.cs | 13 +++++++------ src/WhatsAppApi/Helper/ProtocolTreeNode.cs | 2 +- src/WhatsAppApi/WhatsApp.cs | 16 +++++++++++++--- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs index 6006a39..69da279 100644 --- a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -90,7 +90,7 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) if (isEncrypted && Encryptionkey != null) { - decode(ref this.buffer, size); + decode(size); } if(stanzaSize > 0) @@ -103,12 +103,12 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) return null; } - protected void decode(ref List buffer, int stanzaSize) + protected void decode(int stanzaSize) { int size = stanzaSize; byte[] data = new byte[size]; byte[] dataReal = null; - Buffer.BlockCopy(buffer.ToArray(), 0, data, 0, size); + Buffer.BlockCopy(this.buffer.ToArray(), 0, data, 0, size); byte[] packet = new byte[size - 4]; @@ -220,8 +220,9 @@ protected byte[] readBytes(int token) else if (token == 0xfd) { int size = this.readInt24(); + ret = this.fillArray(size); } - else if (token == 0xfe) + else if (token == 0xfe) { int tmpToken = this.readInt8(); ret = WhatsApp.SYSEncoding.GetBytes(this.getToken(tmpToken + 0xf5)); @@ -259,8 +260,8 @@ protected IEnumerable readAttributes(int size) protected ProtocolTreeNode nextTreeInternal() { - int token = this.readInt8(); - int size = this.readListSize(token); + int token1 = this.readInt8(); + int size = this.readListSize(token1); int token2 = this.readInt8(); if (token2 == 1) { diff --git a/src/WhatsAppApi/Helper/ProtocolTreeNode.cs b/src/WhatsAppApi/Helper/ProtocolTreeNode.cs index af017f9..9210bdd 100644 --- a/src/WhatsAppApi/Helper/ProtocolTreeNode.cs +++ b/src/WhatsAppApi/Helper/ProtocolTreeNode.cs @@ -109,7 +109,7 @@ public ProtocolTreeNode GetChild(string tag) { foreach (var item in this.children) { - if (tag.Equals(item.tag, StringComparison.InvariantCultureIgnoreCase)) + if (ProtocolTreeNode.TagEquals(item, tag)) { return item; } diff --git a/src/WhatsAppApi/WhatsApp.cs b/src/WhatsAppApi/WhatsApp.cs index 73b4878..1d5f4ec 100644 --- a/src/WhatsAppApi/WhatsApp.cs +++ b/src/WhatsAppApi/WhatsApp.cs @@ -406,10 +406,20 @@ protected void processInboundData(byte[] data) { this.Pong(node.GetAttribute("id")); } - if (ProtocolTreeNode.TagEquals(node, "Replaced by new connection")) + if (ProtocolTreeNode.TagEquals(node ,"stream:error")) { - this.Connect(); - this.Login(); + var textNode = node.GetChild("text"); + if (textNode != null) + { + string content = WhatsApp.SYSEncoding.GetString(textNode.GetData()); + Console.WriteLine("Error : " + content); + if (content.Equals("Replaced by new connection", StringComparison.OrdinalIgnoreCase)) + { + this.Disconnect(); + this.Connect(); + this.Login(); + } + } } node = this.reader.nextTree(); } From 6f89ffeac01b55b56e0342695fb106503ffce643 Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 10:51:51 +0100 Subject: [PATCH 016/271] Changed directory structure, updated constants --- .gitattributes | 49 ------ .gitignore | 165 ------------------ CHANGELOG.md | 0 CONTRIBUTING.md | 0 README.md | 0 .../Account/WhatsUser.cs | 0 .../Account/WhatsUserManager.cs | 0 .../Helper/AccountInfo.cs | 0 .../Helper/BinTreeNodeReader.cs | 0 .../Helper/BinTreeNodeWriter.cs | 0 .../Helper/DecodeHelper.cs | 0 .../Helper/DisconnectedException.cs | 0 .../Helper/Encryption.cs | 0 .../Helper/Func.cs | 0 .../Helper/IncompleteMessageException.cs | 0 .../Helper/KeyValue.cs | 0 .../Helper/ProtocolTreeNode.cs | 0 .../WhatsAppApi => WhatsAppApi}/Helper/RC4.cs | 0 .../Helper/TicketManager.cs | 0 .../Parser/FMessage.cs | 0 .../Parser/FMessageVisitor.cs | 0 .../Parser/GroupSetting.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Register/WhatsRegister.cs | 0 .../Response/CorruptStreamException.cs | 0 .../Response/MessageRecvResponse.cs | 0 .../Response/WhatsEventHandler.cs | 0 .../Response/WhatsParser.cs | 0 .../Settings/WhatsConstants.cs | 8 +- {src/WhatsAppApi => WhatsAppApi}/WhatsApp.cs | 0 .../WhatsAppApi.csproj | 0 .../WhatsNetwork.cs | 0 .../WhatsSendHandler.cs | 0 ...gnTimeResolveAssemblyReferencesInput.cache | Bin 0 -> 5901 bytes ...ppApi_vs2010.sln => WhatsAppApi_vs2010.sln | 108 ++++++------ {src/WhatsAppPort => WhatsAppPort}/Program.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Properties/Resources.Designer.cs | 0 .../Properties/Resources.resx | 0 .../Properties/Settings.Designer.cs | 0 .../Properties/Settings.settings | 0 {src/WhatsAppPort => WhatsAppPort}/User.cs | 0 .../WhatsAppPort.csproj | 0 .../WhatsMessageHandler.cs | 0 {src/WhatsAppPort => WhatsAppPort}/app.config | 0 .../frmAddUser.Designer.cs | 0 .../frmAddUser.cs | 0 .../frmAddUser.resx | 0 .../frmForm.Designer.cs | 0 {src/WhatsAppPort => WhatsAppPort}/frmForm.cs | 0 .../frmForm.resx | 0 .../frmLogin.Designer.cs | 0 .../WhatsAppPort => WhatsAppPort}/frmLogin.cs | 0 .../frmLogin.resx | 0 .../frmUserChat.Designer.cs | 0 .../frmUserChat.cs | 0 .../frmUserChat.resx | 0 ...gnTimeResolveAssemblyReferencesInput.cache | Bin 0 -> 6289 bytes {src/WhatsTest => WhatsTest}/Program.cs | 0 .../Properties/AssemblyInfo.cs | 0 {src/WhatsTest => WhatsTest}/WhatsTest.csproj | 0 {src/WhatsTest => WhatsTest}/app.config | 0 ...gnTimeResolveAssemblyReferencesInput.cache | Bin 0 -> 5971 bytes env/WhatsAppApi_vs2012.sln | 54 ------ 64 files changed, 57 insertions(+), 327 deletions(-) delete mode 100644 .gitattributes delete mode 100644 .gitignore delete mode 100644 CHANGELOG.md delete mode 100644 CONTRIBUTING.md delete mode 100644 README.md rename {src/WhatsAppApi => WhatsAppApi}/Account/WhatsUser.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Account/WhatsUserManager.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/AccountInfo.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/BinTreeNodeReader.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/BinTreeNodeWriter.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/DecodeHelper.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/DisconnectedException.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/Encryption.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/Func.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/IncompleteMessageException.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/KeyValue.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/ProtocolTreeNode.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/RC4.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Helper/TicketManager.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Parser/FMessage.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Parser/FMessageVisitor.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Parser/GroupSetting.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Properties/AssemblyInfo.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Register/WhatsRegister.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Response/CorruptStreamException.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Response/MessageRecvResponse.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Response/WhatsEventHandler.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Response/WhatsParser.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/Settings/WhatsConstants.cs (66%) rename {src/WhatsAppApi => WhatsAppApi}/WhatsApp.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/WhatsAppApi.csproj (100%) rename {src/WhatsAppApi => WhatsAppApi}/WhatsNetwork.cs (100%) rename {src/WhatsAppApi => WhatsAppApi}/WhatsSendHandler.cs (100%) create mode 100644 WhatsAppApi/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache rename env/WhatsAppApi_vs2010.sln => WhatsAppApi_vs2010.sln (91%) rename {src/WhatsAppPort => WhatsAppPort}/Program.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/Properties/AssemblyInfo.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/Properties/Resources.Designer.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/Properties/Resources.resx (100%) rename {src/WhatsAppPort => WhatsAppPort}/Properties/Settings.Designer.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/Properties/Settings.settings (100%) rename {src/WhatsAppPort => WhatsAppPort}/User.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/WhatsAppPort.csproj (100%) rename {src/WhatsAppPort => WhatsAppPort}/WhatsMessageHandler.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/app.config (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmAddUser.Designer.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmAddUser.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmAddUser.resx (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmForm.Designer.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmForm.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmForm.resx (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmLogin.Designer.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmLogin.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmLogin.resx (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmUserChat.Designer.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmUserChat.cs (100%) rename {src/WhatsAppPort => WhatsAppPort}/frmUserChat.resx (100%) create mode 100644 WhatsAppPort/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache rename {src/WhatsTest => WhatsTest}/Program.cs (100%) rename {src/WhatsTest => WhatsTest}/Properties/AssemblyInfo.cs (100%) rename {src/WhatsTest => WhatsTest}/WhatsTest.csproj (100%) rename {src/WhatsTest => WhatsTest}/app.config (100%) create mode 100644 WhatsTest/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache delete mode 100644 env/WhatsAppApi_vs2012.sln diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index a664be3..0000000 --- a/.gitattributes +++ /dev/null @@ -1,49 +0,0 @@ -*.doc diff=astextplain -*.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain -*.dot diff=astextplain -*.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain -*.rtf diff=astextplain -*.RTF diff=astextplain - -*.jpg binary -*.png binary -*.gif binary - -*.cs text=auto diff=csharp -*.vb text=auto -*.c text=auto -*.cpp text=auto -*.cxx text=auto -*.h text=auto -*.hxx text=auto -*.py text=auto -*.rb text=auto -*.java text=auto -*.html text=auto -*.htm text=auto -*.css text=auto -*.scss text=auto -*.sass text=auto -*.less text=auto -*.js text=auto -*.lisp text=auto -*.clj text=auto -*.sql text=auto -*.php text=auto -*.lua text=auto -*.m text=auto -*.asm text=auto -*.erl text=auto -*.fs text=auto -*.fsx text=auto -*.hs text=auto - -*.csproj text=auto merge=union -*.vbproj text=auto merge=union -*.fsproj text=auto merge=union -*.dbproj text=auto merge=union -*.sln text=auto eol=crlf merge=union diff --git a/.gitignore b/.gitignore deleted file mode 100644 index aa8811c..0000000 --- a/.gitignore +++ /dev/null @@ -1,165 +0,0 @@ - -################# -## Eclipse -################# - -*.pydevproject -.project -.metadata -bin/** -tmp/** -tmp/**/* -*.tmp -*.bak -*.swp -*~.nib -local.properties -.classpath -.settings/ -.loadpath - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# CDT-specific -.cproject - -# PDT-specific -.buildpath - - -################# -## Visual Studio -################# - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.sln.docstates - -# Build results -**/[Dd]ebug/ -**/[Rr]elease/ -*_i.c -*_p.c -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.vspscc -.builds -**/*.dotCover - -## TODO: If you have NuGet Package Restore enabled, uncomment this -#**/packages/ - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opensdf -*.sdf - -# Visual Studio profiler -*.psess -*.vsp - -# ReSharper is a .NET coding add-in -_ReSharper* - -# Installshield output folder -[Ee]xpress - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish - -# Others -[Bb]in -[Oo]bj -sql -TestResults -*.Cache -ClientBin -stylecop.* -~$* -*.dbmdl -Generated_Code #added for RIA/Silverlight projects - -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML - - - -############ -## Windows -############ - -# Windows image file caches -Thumbs.db - -# Folder config file -Desktop.ini - - -############# -## Python -############# - -*.py[co] - -# Packages -*.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox - -#Translations -*.mo - -#Mr Developer -.mr.developer.cfg - -# Mac crap -.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index e69de29..0000000 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/src/WhatsAppApi/Account/WhatsUser.cs b/WhatsAppApi/Account/WhatsUser.cs similarity index 100% rename from src/WhatsAppApi/Account/WhatsUser.cs rename to WhatsAppApi/Account/WhatsUser.cs diff --git a/src/WhatsAppApi/Account/WhatsUserManager.cs b/WhatsAppApi/Account/WhatsUserManager.cs similarity index 100% rename from src/WhatsAppApi/Account/WhatsUserManager.cs rename to WhatsAppApi/Account/WhatsUserManager.cs diff --git a/src/WhatsAppApi/Helper/AccountInfo.cs b/WhatsAppApi/Helper/AccountInfo.cs similarity index 100% rename from src/WhatsAppApi/Helper/AccountInfo.cs rename to WhatsAppApi/Helper/AccountInfo.cs diff --git a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs b/WhatsAppApi/Helper/BinTreeNodeReader.cs similarity index 100% rename from src/WhatsAppApi/Helper/BinTreeNodeReader.cs rename to WhatsAppApi/Helper/BinTreeNodeReader.cs diff --git a/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs b/WhatsAppApi/Helper/BinTreeNodeWriter.cs similarity index 100% rename from src/WhatsAppApi/Helper/BinTreeNodeWriter.cs rename to WhatsAppApi/Helper/BinTreeNodeWriter.cs diff --git a/src/WhatsAppApi/Helper/DecodeHelper.cs b/WhatsAppApi/Helper/DecodeHelper.cs similarity index 100% rename from src/WhatsAppApi/Helper/DecodeHelper.cs rename to WhatsAppApi/Helper/DecodeHelper.cs diff --git a/src/WhatsAppApi/Helper/DisconnectedException.cs b/WhatsAppApi/Helper/DisconnectedException.cs similarity index 100% rename from src/WhatsAppApi/Helper/DisconnectedException.cs rename to WhatsAppApi/Helper/DisconnectedException.cs diff --git a/src/WhatsAppApi/Helper/Encryption.cs b/WhatsAppApi/Helper/Encryption.cs similarity index 100% rename from src/WhatsAppApi/Helper/Encryption.cs rename to WhatsAppApi/Helper/Encryption.cs diff --git a/src/WhatsAppApi/Helper/Func.cs b/WhatsAppApi/Helper/Func.cs similarity index 100% rename from src/WhatsAppApi/Helper/Func.cs rename to WhatsAppApi/Helper/Func.cs diff --git a/src/WhatsAppApi/Helper/IncompleteMessageException.cs b/WhatsAppApi/Helper/IncompleteMessageException.cs similarity index 100% rename from src/WhatsAppApi/Helper/IncompleteMessageException.cs rename to WhatsAppApi/Helper/IncompleteMessageException.cs diff --git a/src/WhatsAppApi/Helper/KeyValue.cs b/WhatsAppApi/Helper/KeyValue.cs similarity index 100% rename from src/WhatsAppApi/Helper/KeyValue.cs rename to WhatsAppApi/Helper/KeyValue.cs diff --git a/src/WhatsAppApi/Helper/ProtocolTreeNode.cs b/WhatsAppApi/Helper/ProtocolTreeNode.cs similarity index 100% rename from src/WhatsAppApi/Helper/ProtocolTreeNode.cs rename to WhatsAppApi/Helper/ProtocolTreeNode.cs diff --git a/src/WhatsAppApi/Helper/RC4.cs b/WhatsAppApi/Helper/RC4.cs similarity index 100% rename from src/WhatsAppApi/Helper/RC4.cs rename to WhatsAppApi/Helper/RC4.cs diff --git a/src/WhatsAppApi/Helper/TicketManager.cs b/WhatsAppApi/Helper/TicketManager.cs similarity index 100% rename from src/WhatsAppApi/Helper/TicketManager.cs rename to WhatsAppApi/Helper/TicketManager.cs diff --git a/src/WhatsAppApi/Parser/FMessage.cs b/WhatsAppApi/Parser/FMessage.cs similarity index 100% rename from src/WhatsAppApi/Parser/FMessage.cs rename to WhatsAppApi/Parser/FMessage.cs diff --git a/src/WhatsAppApi/Parser/FMessageVisitor.cs b/WhatsAppApi/Parser/FMessageVisitor.cs similarity index 100% rename from src/WhatsAppApi/Parser/FMessageVisitor.cs rename to WhatsAppApi/Parser/FMessageVisitor.cs diff --git a/src/WhatsAppApi/Parser/GroupSetting.cs b/WhatsAppApi/Parser/GroupSetting.cs similarity index 100% rename from src/WhatsAppApi/Parser/GroupSetting.cs rename to WhatsAppApi/Parser/GroupSetting.cs diff --git a/src/WhatsAppApi/Properties/AssemblyInfo.cs b/WhatsAppApi/Properties/AssemblyInfo.cs similarity index 100% rename from src/WhatsAppApi/Properties/AssemblyInfo.cs rename to WhatsAppApi/Properties/AssemblyInfo.cs diff --git a/src/WhatsAppApi/Register/WhatsRegister.cs b/WhatsAppApi/Register/WhatsRegister.cs similarity index 100% rename from src/WhatsAppApi/Register/WhatsRegister.cs rename to WhatsAppApi/Register/WhatsRegister.cs diff --git a/src/WhatsAppApi/Response/CorruptStreamException.cs b/WhatsAppApi/Response/CorruptStreamException.cs similarity index 100% rename from src/WhatsAppApi/Response/CorruptStreamException.cs rename to WhatsAppApi/Response/CorruptStreamException.cs diff --git a/src/WhatsAppApi/Response/MessageRecvResponse.cs b/WhatsAppApi/Response/MessageRecvResponse.cs similarity index 100% rename from src/WhatsAppApi/Response/MessageRecvResponse.cs rename to WhatsAppApi/Response/MessageRecvResponse.cs diff --git a/src/WhatsAppApi/Response/WhatsEventHandler.cs b/WhatsAppApi/Response/WhatsEventHandler.cs similarity index 100% rename from src/WhatsAppApi/Response/WhatsEventHandler.cs rename to WhatsAppApi/Response/WhatsEventHandler.cs diff --git a/src/WhatsAppApi/Response/WhatsParser.cs b/WhatsAppApi/Response/WhatsParser.cs similarity index 100% rename from src/WhatsAppApi/Response/WhatsParser.cs rename to WhatsAppApi/Response/WhatsParser.cs diff --git a/src/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs similarity index 66% rename from src/WhatsAppApi/Settings/WhatsConstants.cs rename to WhatsAppApi/Settings/WhatsConstants.cs index e9c2585..f3730f1 100644 --- a/src/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -14,14 +14,12 @@ class WhatsConstants public const string WhatsAppRealm = "s.whatsapp.net"; public const string WhatsAppServer = "s.whatsapp.net"; public const string WhatsGroupChat = "g.us"; - //public const string WhatsAppVer = "2.8.2"; - public const string WhatsAppVer = "2.8.4"; + public const string WhatsAppVer = "2.8.7"; public const int WhatsPort = 5222; public const string IphoneDevice = "iPhone"; - //public const string UserAgend = "WhatsApp/2.8.2 WP7/7.10.8773.98 Device/NOKIA-Lumia_800-H112.1402.2.3"; //"WhatsApp/2.8.2 WP7/2.3.7 Device/HTC-HERO-H1.0"; - public const string UserAgend = "WhatsApp/2.8.4 iPhone_OS/6.0.1 Device/iPhone_4S"; - public const string WhatsBuildHash = "889d4f44e479e6c38b4a834c6d8417815f999abe";//v2.8.0"c0d4db538579a3016902bf699c16d490acf91ff4"; //v2.0.0 "13944fe0a89d1e6cce0c405178f7c0d00313d558"; + public const string UserAgend = "WhatsApp/2.8.7 iPhone_OS/6.1.0 Device/iPhone_4S"; + public const string WhatsBuildHash = "889d4f44e479e6c38b4a834c6d8417815f999abe"; #endregion #region ParserConstants diff --git a/src/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs similarity index 100% rename from src/WhatsAppApi/WhatsApp.cs rename to WhatsAppApi/WhatsApp.cs diff --git a/src/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj similarity index 100% rename from src/WhatsAppApi/WhatsAppApi.csproj rename to WhatsAppApi/WhatsAppApi.csproj diff --git a/src/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs similarity index 100% rename from src/WhatsAppApi/WhatsNetwork.cs rename to WhatsAppApi/WhatsNetwork.cs diff --git a/src/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs similarity index 100% rename from src/WhatsAppApi/WhatsSendHandler.cs rename to WhatsAppApi/WhatsSendHandler.cs diff --git a/WhatsAppApi/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/WhatsAppApi/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 0000000000000000000000000000000000000000..9bca4d5c8d8b284793c545f123add329f0aa9a01 GIT binary patch literal 5901 zcmeHL;Zhqn5VpZwFoZUyDM_G7oHm`3)V^cV`li_V_3_Q_S@BZrTtEkXe1K(7a#hh=jH$k=PkFTP@=5O zO|E?A7PRkhVJ?emRd{Q;l~i&Qq~gL`%%siWFO^*OUF}O)^uX7WxvAuaUvjzqD>RB? zAH2mG{R)y=}dLK|{k;w|^P_5vX6zFvXQIC1W6B2SzW*CY!Pige8<6RT@>BW+AWur45 z$8~6+h)ts2$+e#Voj5b}R(O0L@!-3+eJwXP-RldYjD6v8)fk2er8h4>(fk$8-nLnQE|jl^%D zpzdM%5+^Z&!WcynF>a!Gd6aL}nS}zfj5?EGg-pOQ*H?;RflVFEn%PvlI=3qE%Hgpw zhBzOV7p&hEmS(F^2hYk0+pmJBmGz42*M7;`+GAQRZLF<>ZnbN*r5d+H>4TMnl3zio zup$?+jO0NH_aLMQc_@Pf56h2fGkXu>Uy$=(qL@7GUz-V$pT;d5A3rso4Eht;9}8OJ z*cv>Xe{Ot*jgf;HtP9!n+{|43Hhxb~I4Q2i*C=A9K9QT%WDPsPcooG$!YZJi50Fp*@`(*hK_-g;J98Z zERhldx*?j>@LX=airfbQ_Sz}6(%TKv2ywA>9qDYjDxF@Grt(*6duf4h|S7L-if z0lv%R%3)0+#R&)qf!;^ZUoHjuuIdJHd4LG-5TR$kE&;#c-XDY>qg~!FO;=Gp`lYE? zlzE4aQDYZh7^-S$SMfh#c=w?B!ippR#KKeSWA5K4_VlX$$#;;e$op%~nzna_hvN@1 zq1sP^I1U|yMiRwJPfl9n5kgIObL>;qHNNeU;k0FZ*S%q!{TOGP-MAU@8Y@9;knVbn z?@`27@eUs?qIb#GmLgKGQ%-MzLSm0`E-?Z z7?W@Zn$l?hb*l-*HO{B`Vt3 z;>xE^QTsL*Mn=?X!rjQOC*oU7DlXjBRMH5367h`hXkW5bm-$*!ClSy4Wrth8vPMZ9 zFn6__oUf!SOO;g0PABO+9lwSWiB`T{-cZL=HvSNA@K-4o{b%kVER-s^u4OeNOg%+<<#&Gj3aQeOAt#6ObO?(!xIXu!X$Gc1{oH$_F42ZT*2ocrq3$brbDPKxd-i&XWHXI zeGKX&L1_|7!!SSj0t#b?a}XTT$)&la=zaJfqy#eo0~v`ho7FpErU2!6@{J`@zlBS2mOZ?*$xu;}TQW-v(=Xj(jAZSh&K+jFzCGx3&dpbA|VIt30pVs;|AWC8LFAXh-M zfgxw9rUnYILYIaHbA1JKtzOLvEp-D>-2KM@k?9YDd5&n{nFw@w0zk`s0ihaS!{}f7 z8l9E24!#lS5C@c8UnngwV1$5WWF~_kb`kPe93iJ|v%=xy|(W`|XpF(l-eZX+*1L~@YE3L`fU82cH!B3Z{T z77%tQkOYW_$C(Q=XeZ=HXq`NM!(EN3q@09=9h~?RVnF@$ap~fGQR9UC1nq-(1SA1x z;FghXe literal 0 HcmV?d00001 diff --git a/src/WhatsTest/Program.cs b/WhatsTest/Program.cs similarity index 100% rename from src/WhatsTest/Program.cs rename to WhatsTest/Program.cs diff --git a/src/WhatsTest/Properties/AssemblyInfo.cs b/WhatsTest/Properties/AssemblyInfo.cs similarity index 100% rename from src/WhatsTest/Properties/AssemblyInfo.cs rename to WhatsTest/Properties/AssemblyInfo.cs diff --git a/src/WhatsTest/WhatsTest.csproj b/WhatsTest/WhatsTest.csproj similarity index 100% rename from src/WhatsTest/WhatsTest.csproj rename to WhatsTest/WhatsTest.csproj diff --git a/src/WhatsTest/app.config b/WhatsTest/app.config similarity index 100% rename from src/WhatsTest/app.config rename to WhatsTest/app.config diff --git a/WhatsTest/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/WhatsTest/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 0000000000000000000000000000000000000000..e24cff6f12b82e831369ab5054e4067e21baeb19 GIT binary patch literal 5971 zcmeHLaZejJ6i*$BB`Vt7 zD&s_)STlX}RqP`_*z0RarpmN=-`&vzb@&oF+ zqQRUMrC6=(HrEa{^Ux|a0i#!iSK-whciEIpJ%>9~v(89tRWlXeb#s!|SVPGDPFIPR zq3`agzMOC!SnnfjEKylyI#etAWySOcfT$IuoRtU|#R>z>kJU)Q=8@Ux2twcuJq z@A2~?9)8q*Qp<2*ol2iEGs`8jwUC^tJ9S4>S!7h&dwHt&)OcNr3OEE8F)3Mm(@nMRwd!Q?J7hJNj1yuG1cb{giIkm3}P%E!|DhMIB6sC zTOhD`6#t2%7z07ZK}5)H5U-93?L4zo1eVdDl35`Wu*{9MQkYB3f{2-p>k{NTF_Rs#MBr!aya(c! zD=i+Fk!c6ih};K|B>>sto+BEn`vuT^EBpdh*F>w6*aEvAfPM+|Axl?*Mi~LCGG<|E zPp!~-M*GtN^XN`+5*(ang_%~e0C^gaEB!>yQcX=1MW(wnF_>!x=32g-7ux0qp!k8l z0*J*cL7?_S(Ej;K+V5*_u!Ki|@V=k!Lg2UD`y)XA8qo8AeyNSNQm?PnxmD({D}!y; z+KNFdqQ>yF7^JxC$U=h@*Dvb5gNHHMg^Nc`jqED^CmY>8XkC1v=bu=3Y<+x(7+etW z%Kyo`%yq>54QF55JENm<0|Kg363CVC7$lEDto7ymCf@+mY%j?K(=Pe8PZOss@}&2P zVf1$}+U&*5kR4eGWR>`4M7{?R%fVYr6bWWPI}NCGN&*=tmPEihp+aUs+&@}9_ZjGz zJcZuLqc_~s_#Tpz@ahCR{+K+2_UWV2`HvpWWAX#^_k+nM0cYZBh0M&bZrTR26L`=H(iDC2{jJ|AqA^pa3%Rt$A5FP$|aLoVT O%kJQq4`1tc#{UC9kF!qz literal 0 HcmV?d00001 diff --git a/env/WhatsAppApi_vs2012.sln b/env/WhatsAppApi_vs2012.sln deleted file mode 100644 index 85b5a69..0000000 --- a/env/WhatsAppApi_vs2012.sln +++ /dev/null @@ -1,54 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsAppApi", "..\src\WhatsAppApi\WhatsAppApi.csproj", "{3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsAppPort", "..\src\WhatsAppPort\WhatsAppPort.csproj", "{91D9D263-BFB7-4C31-890F-4A42F734670E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsTest", "..\src\WhatsTest\WhatsTest.csproj", "{E003A3A5-4ECF-475A-8926-154EF85CF4B7}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|x86.ActiveCfg = Debug|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Any CPU.Build.0 = Release|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|x86.ActiveCfg = Release|Any CPU - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|Any CPU.ActiveCfg = Debug|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|x86.ActiveCfg = Debug|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|x86.Build.0 = Debug|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|Any CPU.ActiveCfg = Release|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|Mixed Platforms.Build.0 = Release|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|x86.ActiveCfg = Release|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|x86.Build.0 = Release|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|Any CPU.ActiveCfg = Debug|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|x86.ActiveCfg = Debug|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|x86.Build.0 = Debug|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|Any CPU.ActiveCfg = Release|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|Mixed Platforms.Build.0 = Release|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|x86.ActiveCfg = Release|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal From 4f6c1b749ced4e613938c01de94bf9fe126bfe46 Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 11:34:48 +0100 Subject: [PATCH 017/271] Removed old code comments,removed empty statemens - Removed all annoying code commments - Changed addAuthv_1_2 to addAuth (Whatsapp.cs) - Removed empty statements --- WhatsAppApi/Helper/AccountInfo.cs | 1 - WhatsAppApi/Helper/BinTreeNodeReader.cs | 152 +------- WhatsAppApi/Helper/BinTreeNodeWriter.cs | 104 ------ WhatsAppApi/Helper/DecodeHelper.cs | 263 +------------ WhatsAppApi/Helper/Func.cs | 42 +-- .../Helper/IncompleteMessageException.cs | 2 + WhatsAppApi/Helper/ProtocolTreeNode.cs | 34 -- WhatsAppApi/Helper/TicketManager.cs | 7 +- WhatsAppApi/Register/WhatsRegister.cs | 16 +- .../Response/CorruptStreamException.cs | 9 +- WhatsAppApi/Response/MessageRecvResponse.cs | 43 +-- WhatsAppApi/Response/WhatsEventHandler.cs | 14 - WhatsAppApi/Response/WhatsParser.cs | 351 +----------------- WhatsAppApi/WhatsApp.cs | 161 +------- WhatsAppApi/WhatsNetwork.cs | 72 +--- WhatsAppApi/WhatsSendHandler.cs | 339 +---------------- WhatsAppApi/bin/Debug/WhatsAppApi.dll | Bin 0 -> 70656 bytes WhatsAppApi/bin/Debug/WhatsAppApi.pdb | Bin 0 -> 185856 bytes .../WhatsAppApi.csproj.FileListAbsolute.txt | 5 + WhatsAppApi/obj/Debug/WhatsAppApi.dll | Bin 0 -> 70656 bytes WhatsAppApi/obj/Debug/WhatsAppApi.pdb | Bin 0 -> 185856 bytes WhatsAppApi_vs2010.suo | Bin 0 -> 45568 bytes 22 files changed, 46 insertions(+), 1569 deletions(-) create mode 100644 WhatsAppApi/bin/Debug/WhatsAppApi.dll create mode 100644 WhatsAppApi/bin/Debug/WhatsAppApi.pdb create mode 100644 WhatsAppApi/obj/Debug/WhatsAppApi.csproj.FileListAbsolute.txt create mode 100644 WhatsAppApi/obj/Debug/WhatsAppApi.dll create mode 100644 WhatsAppApi/obj/Debug/WhatsAppApi.pdb create mode 100644 WhatsAppApi_vs2010.suo diff --git a/WhatsAppApi/Helper/AccountInfo.cs b/WhatsAppApi/Helper/AccountInfo.cs index 2e4ea33..963d82f 100644 --- a/WhatsAppApi/Helper/AccountInfo.cs +++ b/WhatsAppApi/Helper/AccountInfo.cs @@ -7,7 +7,6 @@ namespace WhatsAppApi.Helper { public class AccountInfo { - //status'=>$node->getAttribute('status'),'kind'=>$node->getAttribute('kind'),'creation'=>$node->getAttribute('creation'),'expiration'=>$node->getAttribute('expiration') public string Status { get; private set; } public string Kind { get; private set; } public string Creation { get; private set; } diff --git a/WhatsAppApi/Helper/BinTreeNodeReader.cs b/WhatsAppApi/Helper/BinTreeNodeReader.cs index 69da279..49a217d 100644 --- a/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -8,9 +8,6 @@ namespace WhatsAppApi.Helper internal class BinTreeNodeReader { private string[] dictionary; - //private string input; - - //change to protocol 1.2 public byte[] Encryptionkey { get; set; } private List buffer; @@ -20,46 +17,6 @@ public BinTreeNodeReader(string[] dict) this.Encryptionkey = null; } - //public ProtocolTreeNode nextTree(string pInput = null) - //{ - // if (pInput != null) - // { - // this.input = pInput; - // } - - // //int stanzaSize = this.peekInt16(); - // //Change to protocol 1.2 - // int stanzaSize = this.peekInt24(); - // int flags = (stanzaSize >> 20); - // int size = ((stanzaSize & 0xF0000) >> 16) | ((stanzaSize & 0xFF00) >> 8) | (stanzaSize & 0xFF); - - // bool isEncrypted = ((flags & 8) != 0); // 8 = (1 << 4) // Read node and decrypt - - // if (stanzaSize > this.input.Length) - // { - // //Es sind noch nicht alle Daten eingelesen, daher abbrechen und warten bis alles da ist - // var exception = new IncompleteMessageException("Incomplete message"); - // exception.setInput(this.input); - // throw exception; - // } - - // this.readInt24(); - // if (stanzaSize > 0) - // { - // if (isEncrypted && Encryptionkey != null) - // { - // RC4 encryption = new RC4(this.Encryptionkey, 256); - // byte[] dataB = this.sysEncoding.GetBytes(this.input); - // byte[] enData = new byte[dataB.Length - 3]; - // Buffer.BlockCopy(dataB, 3, enData, 0, dataB.Length - 3); - // //encryption.Cipher(enData, 0, dataB.Length - 3); - // enData = encryption.Encrypt(enData); - // Buffer.BlockCopy(enData, 0, dataB, 3, enData.Length); - // } - // return this.nextTreeInternal(); - // } - // return null; - //} public ProtocolTreeNode nextTree(byte[] pInput = null) { if (pInput != null) @@ -70,7 +27,6 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) this.buffer.AddRange(pInput); } - // Ported from the allegedly working PHP version ~lrg int stanzaFlag = (this.peekInt8() & 0xF0) >> 4; int stanzaSize = this.peekInt16(1); @@ -120,7 +76,6 @@ protected void decode(int stanzaSize) byte[] hashByte = new byte[4]; Buffer.BlockCopy(h.ComputeHash(packet, 0, packet.Length), 0, hashByte, 0, 4); - // 20121107 not sure why the packet is indicated an ecrypted but the hmcash1 is incorrect if (hashServerByte.SequenceEqual(hashByte)) { this.buffer.RemoveRange(0, 4); @@ -151,52 +106,6 @@ protected string getToken(int token) return ret; } - //protected string readString(int token) - //{ - // string ret = ""; - // if (token == -1) - // { - // throw new Exception("BinTreeNodeReader->readString: Invalid token $token"); - // } - // if ((token > 4) && (token < 0xf5)) - // { - // ret = this.getToken(token); - // } - // else if (token == 0) - // { - // ret = ""; - // } - // else if (token == 0xfc) - // { - // int size = this.readInt8(); - // ret = WhatsApp.SYSEncoding.GetString(this.fillArray(size)); - // } - // else if (token == 0xfd) - // { - // int size = this.readInt24(); - // ret = WhatsApp.SYSEncoding.GetString(this.fillArray(size)); - // } - // else if (token == 0xfe) - // { - // int tmpToken = this.readInt8(); - // ret = this.getToken(tmpToken + 0xf5); - // } - // else if (token == 0xfa) - // { - // string user = this.readString(this.readInt8()); - // string server = this.readString(this.readInt8()); - // if ((user.Length > 0) && (server.Length > 0)) - // { - // ret = user + "@" + server; - // } - // else if (server.Length > 0) - // { - // ret = server; - // } - // } - // return ret; - //} - protected byte[] readBytes(int token) { byte[] ret = new byte[0]; @@ -272,8 +181,6 @@ protected ProtocolTreeNode nextTreeInternal() { return null; } - - //string tag = this.readString(token); string tag = WhatsApp.SYSEncoding.GetString(this.readBytes(token2)); var tmpAttributes = this.readAttributes(size); @@ -341,17 +248,8 @@ protected int peekInt8(int offset = 0) protected int peekInt24(int offset = 0) { int ret = 0; - //if (this.input.Length >= 3) - //{ - // ret = (int)this.input[0] << 16; - // ret |= (int)this.input[1] << 8; - // ret |= (int)this.input[2] << 0; - //} if (this.buffer.Count >= 3 + offset) { - // ret = this.buffer[0] << 16; - // ret |= this.buffer[1] << 8; - // ret |= this.buffer[2] << 0; ret = (this.buffer[0 + offset] << 16) + (this.buffer[1 + offset] << 8) + this.buffer[2 + offset]; } return ret; @@ -360,13 +258,6 @@ protected int peekInt24(int offset = 0) protected int readInt24() { int ret = 0; - //if (this.input.Length >= 3) - //{ - // ret = (int)this.input[0] << 16; - // ret |= (int)this.input[1] << 8; - // ret |= (int)this.input[2] << 0; - // this.input = this.input.Remove(0, 3); - //} if (this.buffer.Count >= 3) { ret = this.buffer[0] << 16; @@ -377,16 +268,6 @@ protected int readInt24() return ret; } - //protected int peekInt16() - //{ - // int ret = 0; - // if (this.input.Length >= 2) - // { - // ret = (int)this.input[0] << 8; - // ret |= (int)this.input[1] << 0; - // } - // return ret; - //} protected int peekInt16(int offset = 0) { int ret = 0; @@ -398,17 +279,6 @@ protected int peekInt16(int offset = 0) return ret; } - //protected int readInt16() - //{ - // int ret = 0; - // if (this.input.Length >= 2) - // { - // ret = (int)this.input[0] << 8; - // ret |= (int)this.input[1] << 0; - // this.input = this.input.Remove(0, 2); - // } - // return ret; - //} protected int readInt16() { int ret = 0; @@ -421,16 +291,6 @@ protected int readInt16() return ret; } - //protected int readInt8() - //{ - // int ret = 0; - // if (this.input.Length >= 1) - // { - // ret = (int)this.input[0]; - // this.input = this.input.Remove(0, 1); - // } - // return ret; - //} protected int readInt8() { int ret = 0; @@ -442,22 +302,11 @@ protected int readInt8() return ret; } - //protected string fillArray(int len) - //{ - // string ret = ""; - // if (this.input.Length >= len) - // { - // ret = this.input.Substring(0, len); - // this.input = this.input.Remove(0, len); - // } - // return ret; - //} protected byte[] fillArray(int len) { byte[] ret = new byte[len]; if (this.buffer.Count >= len) { - //this.buffer.CopyTo(0, ret, 0, len); Buffer.BlockCopy(this.buffer.ToArray(), 0, ret, 0, len); this.buffer.RemoveRange(0, len); } @@ -467,6 +316,7 @@ protected byte[] fillArray(int len) } return ret; } + protected void DebugPrint(string debugMsg) { if (WhatsApp.DEBUG && debugMsg.Length > 0) diff --git a/WhatsAppApi/Helper/BinTreeNodeWriter.cs b/WhatsAppApi/Helper/BinTreeNodeWriter.cs index f000c96..8320c21 100644 --- a/WhatsAppApi/Helper/BinTreeNodeWriter.cs +++ b/WhatsAppApi/Helper/BinTreeNodeWriter.cs @@ -7,11 +7,8 @@ namespace WhatsAppApi.Helper { internal class BinTreeNodeWriter { - //private string output; private List buffer; private Dictionary tokenMap; - - //change to protocol 1.2 public byte[] Encryptionkey { get; set; } public BinTreeNodeWriter(string[] dict) @@ -31,27 +28,9 @@ public BinTreeNodeWriter(string[] dict) buffer = new List(); } - //public string StartStream(string domain, string resource) - //{ - // var attributes = new List(); - // this.output = "WA"; - // //this.output += "\x01" + "\x01" + "\x00" + "\x19"; - - // attributes.Add(new KeyValue("to", domain)); - // attributes.Add(new KeyValue("resource", resource)); - // this.writeListStart(attributes.Count*2 + 1); - - // this.output += "\x01"; - // this.writeAttributes(attributes.ToArray()); - // string ret = this.output; - // this.output = ""; - // return ret; - //} - public byte[] StartStream(string domain, string resource) { var attributes = new List(); - // protocol 1.2 this.buffer = new List(); attributes.Add(new KeyValue("to", domain)); @@ -72,18 +51,6 @@ public byte[] StartStream(string domain, string resource) return ret; } - //public string Write(ProtocolTreeNode node) - //{ - // if (node == null) - // { - // this.output += "\x00"; - // } - // else - // { - // this.writeInternal(node); - // } - // return this.flushBuffer(); - //} public byte[] Write(ProtocolTreeNode node, bool encrypt = true) { if (node == null) @@ -98,16 +65,6 @@ public byte[] Write(ProtocolTreeNode node, bool encrypt = true) return this.flushBuffer(encrypt); } - //protected string flushBuffer() - //{ - // int size = this.output.Length; - // this.output = this.GetInt16(size) + this.output; - // string ret = this.output; - // this.output = ""; - // return ret; - //} - - //change to protocol 1.2 protected byte[] flushBuffer(bool encrypt = true) { byte[] data = this.buffer.ToArray(); @@ -142,12 +99,6 @@ protected void writeAttributes(IEnumerable attributes) } } - //private string GetInt16(int len) - //{ - // string ret = chr((len & 0xff00) >> 8); - // ret += chr(len & 0x00ff); - // return ret; - //} private byte[] GetInt16(int len) { byte[] ret = new byte[2]; @@ -156,13 +107,6 @@ private byte[] GetInt16(int len) return ret; } - //private string GetInt24(int len) - //{ - // string ret = chr((len & 0xf0000) >> 16); - // ret += chr((len & 0xff00) >> 8); - // ret += chr(len & 0x00ff); - // return ret; - //} private byte[] GetInt24(int len) { byte[] ret = new byte[3]; @@ -172,21 +116,6 @@ private byte[] GetInt24(int len) return ret; } - //protected void writeBytes(string bytes) - //{ - // int len = bytes.Length; - // if (len >= 0x100) - // { - // this.output += "\xfd"; - // this.writeInt24(len); - // } - // else - // { - // this.output += "\xfc"; - // this.writeInt8(len); - // } - // this.output += bytes; - //} protected void writeBytes(string bytes) { writeBytes(WhatsApp.SYSEncoding.GetBytes(bytes)); @@ -207,25 +136,12 @@ protected void writeBytes(byte[] bytes) this.buffer.AddRange(bytes); } - //protected void writeInt16(int v) - //{ - // //string ret = ""; - // this.output += chr((v & 0xff00) >> 8); - // this.output += chr((v & 0x00ff) >> 0); - // //this.output = ret + this.output; - //} protected void writeInt16(int v) { this.buffer.Add((byte)((v & 0xff00) >> 8)); this.buffer.Add((byte)(v & 0x00ff)); } - //protected void writeInt24(int v) - //{ - // this.output += chr((v & 0xff0000) >> 16); - // this.output += chr((v & 0x00ff00) >> 8); - // this.output += chr((v & 0x0000ff) >> 0); - //} protected void writeInt24(int v) { this.buffer.Add((byte)((v & 0xff0000) >> 16)); @@ -233,10 +149,6 @@ protected void writeInt24(int v) this.buffer.Add((byte)(v & 0x0000ff)); } - //protected void writeInt8(int v) - //{ - // this.output += chr(v & 0xff); - //} protected void writeInt8(int v) { this.buffer.Add((byte)(v & 0xff)); @@ -275,7 +187,6 @@ protected void writeInternal(ProtocolTreeNode node) } protected void writeJid(string user, string server) { - //this.output += "\xfa"; this.buffer.Add(0xfa); if (user.Length > 0) { @@ -292,18 +203,15 @@ protected void writeListStart(int len) { if (len == 0) { - //this.output += "\x00"; this.buffer.Add(0x00); } else if (len < 256) { - //this.output += "\xf8"; this.buffer.Add(0xf8); this.writeInt8(len); } else { - //this.output += "\xf9"; this.buffer.Add(0xf9); this.writeInt16(len); } @@ -327,23 +235,11 @@ protected void writeString(string tag) } else { - //this.writeBytes(tag); this.writeBytes(tag); } } } - //protected void writeToken(int token) - //{ - // if (token < 0xf5) - // { - // this.output += chr(token); - // } - // else if (token <= 0x1f4) - // { - // this.output += "\xfe" + chr(token - 0xf5); - // } - //} protected void writeToken(int token) { if (token < 0xf5) diff --git a/WhatsAppApi/Helper/DecodeHelper.cs b/WhatsAppApi/Helper/DecodeHelper.cs index 51a8f4b..6b36c90 100644 --- a/WhatsAppApi/Helper/DecodeHelper.cs +++ b/WhatsAppApi/Helper/DecodeHelper.cs @@ -19,6 +19,7 @@ public static string decode(string hex) } return strBuilder.ToString(); } + public static string[] SplitStringN(this string value, int count) { var returnList = new List(); @@ -35,262 +36,11 @@ public static string[] SplitStringN(this string value, int count) } public static string[] getDictionary() - { - if (dictList != null) - return dictList; - - //dictList = new string[249]; - //dictList[0] = null; - //dictList[1] = null; - //dictList[2] = null; - //dictList[3] = null; - //dictList[4] = null; - //dictList[5] = "1"; - //dictList[6] = "1.0"; - //dictList[7] = "ack"; - //dictList[8] = "action"; - //dictList[9] = "active"; - //dictList[10] = "add"; - //dictList[11] = "all"; - //dictList[12] = "allow"; - //dictList[13] = "apple"; - //dictList[14] = "audio"; - //dictList[15] = "auth"; - //dictList[16] = "author"; - //dictList[17] = "available"; - //dictList[18] = "bad-request"; - //dictList[19] = "basee64"; - //dictList[20] = "Bell.caf"; - //dictList[21] = "bind"; - //dictList[22] = "body"; - //dictList[23] = "Boing.caf"; - //dictList[24] = "cancel"; - //dictList[25] = "category"; - //dictList[26] = "challenge"; - //dictList[27] = "chat"; - //dictList[28] = "clean"; - //dictList[29] = "code"; - //dictList[30] = "composing"; - //dictList[31] = "config"; - //dictList[32] = "conflict"; - //dictList[33] = "contacts"; - //dictList[34] = "create"; - //dictList[35] = "creation"; - //dictList[36] = "default"; - //dictList[37] = "delay"; - //dictList[38] = "delete"; - //dictList[39] = "delivered"; - //dictList[40] = "deny"; - //dictList[41] = "DIGEST-MD5"; - //dictList[42] = "DIGEST-MD5-1"; - //dictList[43] = "dirty"; - //dictList[44] = "en"; - //dictList[45] = "enable"; - //dictList[46] = "encoding"; - //dictList[47] = "error"; - //dictList[48] = "expiration"; - //dictList[49] = "expired"; - //dictList[50] = "failure"; - //dictList[51] = "false"; - //dictList[52] = "favorites"; - //dictList[53] = "feature"; - //dictList[54] = "field"; - //dictList[55] = "free"; - //dictList[56] = "from"; - //dictList[57] = "g.us"; - //dictList[58] = "get"; - //dictList[59] = "Glas.caf"; - //dictList[60] = "google"; - //dictList[61] = "group"; - //dictList[62] = "groups"; - //dictList[63] = "g_sound"; - //dictList[64] = "Harp.caf"; - //dictList[65] = "http://etherx.jabber.org/streams"; - //dictList[66] = "http://jabber.org/protocol/chatstates"; - //dictList[67] = "id"; - //dictList[68] = "image"; - //dictList[69] = "img"; - //dictList[70] = "inactive"; - //dictList[71] = "internal-server-error"; - //dictList[72] = "iq"; - //dictList[73] = "item"; - //dictList[74] = "item-not-found"; - //dictList[75] = "jabber:client"; - //dictList[76] = "jabber:iq:last"; - //dictList[77] = "jabber:iq:privacy"; - //dictList[78] = "jabber:x:delay"; - //dictList[79] = "jabber:x:event"; - //dictList[80] = "jid"; - //dictList[81] = "jid-malformed"; - //dictList[82] = "kind"; - //dictList[83] = "leave"; - //dictList[84] = "leave-all"; - //dictList[85] = "list"; - //dictList[86] = "location"; - //dictList[87] = "max_groups"; - //dictList[88] = "max_participants"; - //dictList[89] = "max_subject"; - //dictList[90] = "mechanism"; - //dictList[91] = "mechanisms"; - //dictList[92] = "media"; - //dictList[93] = "message"; - //dictList[94] = "message_acks"; - //dictList[95] = "missing"; - //dictList[96] = "modify"; - //dictList[97] = "name"; - //dictList[98] = "not-acceptable"; - //dictList[99] = "not-allowed"; - //dictList[100] = "not-authorized"; - //dictList[101] = "notify"; - //dictList[102] = "Offline Storage"; - //dictList[103] = "order"; - //dictList[104] = "owner"; - //dictList[105] = "owning"; - //dictList[106] = "paid"; - //dictList[107] = "participant"; - //dictList[108] = "participants"; - //dictList[109] = "participating"; - //dictList[110] = "fail"; - //dictList[111] = "paused"; - //dictList[112] = "picture"; - //dictList[113] = "ping"; - //dictList[114] = "PLAIN"; - //dictList[115] = "platform"; - //dictList[116] = "presence"; - //dictList[117] = "preview"; - //dictList[118] = "probe"; - //dictList[119] = "prop"; - //dictList[120] = "props"; - //dictList[121] = "p_o"; - //dictList[122] = "p_t"; - //dictList[123] = "query"; - //dictList[124] = "raw"; - //dictList[125] = "receipt"; - //dictList[126] = "receipt_acks"; - //dictList[127] = "received"; - //dictList[128] = "relay"; - //dictList[129] = "remove"; - //dictList[130] = "Replaced by new connection"; - //dictList[131] = "request"; - //dictList[132] = "resource"; - //dictList[133] = "resource-constraint"; - //dictList[134] = "response"; - //dictList[135] = "result"; - //dictList[136] = "retry"; - //dictList[137] = "rim"; - //dictList[138] = "s.whatsapp.net"; - //dictList[139] = "seconds"; - //dictList[140] = "server"; - //dictList[141] = "session"; - //dictList[142] = "set"; - //dictList[143] = "show"; - //dictList[144] = "sid"; - //dictList[145] = "sound"; - //dictList[146] = "stamp"; - //dictList[147] = "starttls"; - //dictList[148] = "status"; - //dictList[149] = "stream:error"; - //dictList[150] = "stream:features"; - //dictList[151] = "subject"; - //dictList[152] = "subscribe"; - //dictList[153] = "success"; - //dictList[154] = "system-shutdown"; - //dictList[155] = "s_o"; - //dictList[156] = "s_t"; - //dictList[157] = "t"; - //dictList[158] = "TimePassing.caf"; - //dictList[159] = "timestamp"; - //dictList[160] = "to"; - //dictList[161] = "Tri-tone.caf"; - //dictList[162] = "type"; - //dictList[163] = "unavailable"; - //dictList[164] = "uri"; - //dictList[165] = "url"; - //dictList[166] = "urn:ietf:params:xml:ns:xmpp-bind"; - //dictList[167] = "urn:ietf:params:xml:ns:xmpp-sasl"; - //dictList[168] = "urn:ietf:params:xml:ns:xmpp-session"; - //dictList[169] = "urn:ietf:params:xml:ns:xmpp-stanzas"; - //dictList[170] = "urn:ietf:params:xml:ns:xmpp-streams"; - //dictList[171] = "urn:xmpp:delay"; - //dictList[172] = "urn:xmpp:ping"; - //dictList[173] = "urn:xmpp:receipts"; - //dictList[174] = "urn:xmpp:whatsapp"; - //dictList[175] = "urn:xmpp:whatsapp:dirty"; - //dictList[176] = "urn:xmpp:whatsapp:mms"; - //dictList[177] = "urn:xmpp:whatsapp:push"; - //dictList[178] = "value"; - //dictList[179] = "vcard"; - //dictList[180] = "version"; - //dictList[181] = "video"; - //dictList[182] = "w"; - //dictList[183] = "w:g"; - //dictList[184] = "w:p:r"; - //dictList[185] = "wait"; - //dictList[186] = "x"; - //dictList[187] = "xml-not-well-formed"; - //dictList[188] = "xml:lang"; - //dictList[189] = "xmlns"; - //dictList[190] = "xmlns:stream"; - //dictList[191] = "Xylophone.caf"; - //dictList[192] = "account"; - //dictList[193] = "digest"; - //dictList[194] = "g_notify"; - //dictList[195] = "method"; - //dictList[196] = "password"; - //dictList[197] = "registration"; - //dictList[198] = "stat"; - //dictList[199] = "text"; - //dictList[200] = "user"; - //dictList[201] = "username"; - //dictList[202] = "event"; - //dictList[203] = "latitude"; - //dictList[204] = "longitude"; - //dictList[205] = "true"; - //dictList[206] = "after"; - //dictList[207] = "before"; - //dictList[208] = "broadcast"; - //dictList[209] = "count"; - //dictList[210] = "features"; - //dictList[211] = "first"; - //dictList[212] = "index"; - //dictList[213] = "invalid-mechanism"; - //dictList[214] = "ldictListt"; - //dictList[215] = "max"; - //dictList[216] = "offline"; - //dictList[217] = "proceed"; - //dictList[218] = "required"; - //dictList[219] = "sync"; - //dictList[220] = "elapsed"; - //dictList[221] = "ip"; - //dictList[222] = "microsoft"; - //dictList[223] = "mute"; - //dictList[224] = "nokia"; - //dictList[225] = "off"; - //dictList[226] = "pin"; - //dictList[227] = "pop_mean_time"; - //dictList[228] = "pop_plus_minus"; - //dictList[229] = "port"; - //dictList[230] = "reason"; - //dictList[231] = "server-error"; - //dictList[232] = "silent"; - //dictList[233] = "timeout"; - //dictList[234] = "lc"; - //dictList[235] = "lg"; - //dictList[236] = "bad-protocol"; - //dictList[237] = "none"; - //dictList[238] = "remote-server-timeout"; - //dictList[239] = "service-unavailable"; - //dictList[240] = "w:p"; - //dictList[241] = "w:profile:picture"; - //dictList[242] = "notification"; - //dictList[243] = null; - //dictList[244] = null; - //dictList[245] = null; - //dictList[246] = null; - //dictList[247] = null; - //dictList[248] = "XXX"; - - // change to protocol 1.2 + { + if (dictList != null) + { + return dictList; + } dictList = new string[249]; dictList[0] = null; dictList[1] = null; @@ -541,7 +291,6 @@ public static string[] getDictionary() dictList[246] = null; dictList[247] = null; dictList[248] = "XXX"; - return dictList; } diff --git a/WhatsAppApi/Helper/Func.cs b/WhatsAppApi/Helper/Func.cs index 25e24c2..e150096 100644 --- a/WhatsAppApi/Helper/Func.cs +++ b/WhatsAppApi/Helper/Func.cs @@ -55,52 +55,12 @@ public static string HexString2Ascii(string hexString) return sb.ToString(); } - //function createIcon($file) - //{ - // $outfile = "thumb.jpg"; - // $cmd = "convert $file -resize 100x100 $outfile"; - // system($cmd); - // $fp = fopen($outfile, "r"); - // $contents = fread($fp, filesize($outfile)); - // fclose($fp); - // $b64 = base64_encode($contents); - // $outfile .= "b64"; - // $fp = fopen($outfile, "w"); - // fwrite($fp, $b64); - // fclose($fp); - //} - - //public static string EncodeTo64(string toEncode, Encoding enc) - //{ - // byte[] toEncodeAsBytes = enc.GetBytes(toEncode); - // string returnValue = System.Convert.ToBase64String(toEncodeAsBytes); - // return returnValue; - //} - //public static string DecodeTo64(string toDecode, Encoding enc) - //{ - // byte[] toDecodeAsBytes = System.Convert.FromBase64String(toDecode); - // string returnValue = enc.GetString(toDecodeAsBytes); - // return returnValue; - //} - -// function startsWith($haystack, $needle , $pos=0){ -// $length = strlen($needle); -// return (substr($haystack, $pos, $length) === $needle); -//} - -//function endsWith($haystack, $needle){ -// $length = strlen($needle); -// $start = $length * -1; -// return (substr($haystack, $start) === $needle); -//} - public static long GetUnixTimestamp(DateTime value) { - //create Timespan by subtracting the value provided from - //the Unix Epoch TimeSpan span = (value - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)); return (long)span.TotalSeconds; } + public static long GetNowUnixTimestamp() { return GetUnixTimestamp(DateTime.UtcNow); diff --git a/WhatsAppApi/Helper/IncompleteMessageException.cs b/WhatsAppApi/Helper/IncompleteMessageException.cs index 0eeb08d..58e6cfd 100644 --- a/WhatsAppApi/Helper/IncompleteMessageException.cs +++ b/WhatsAppApi/Helper/IncompleteMessageException.cs @@ -16,10 +16,12 @@ public IncompleteMessageException(string message, int code = 0) this.message = message; this.code = code; } + public void setInput(byte[] input) { this.buffer = input; } + public byte[] getInput() { return this.buffer; diff --git a/WhatsAppApi/Helper/ProtocolTreeNode.cs b/WhatsAppApi/Helper/ProtocolTreeNode.cs index 9210bdd..6aa70d1 100644 --- a/WhatsAppApi/Helper/ProtocolTreeNode.cs +++ b/WhatsAppApi/Helper/ProtocolTreeNode.cs @@ -12,53 +12,29 @@ public class ProtocolTreeNode public IEnumerable children; public byte[] data; - //public ProtocolTreeNode(string tag, IEnumerable attributeHash, IEnumerable children = null, - // string data = "") public ProtocolTreeNode(string tag, IEnumerable attributeHash, IEnumerable children = null, byte[] data = null) { this.tag = tag ?? ""; this.attributeHash = attributeHash ?? new KeyValue[0]; this.children = children ?? new ProtocolTreeNode[0]; - //this.data = data ?? ""; this.data = new byte[0]; if (data != null) this.data = data; } - //public ProtocolTreeNode(string tag, IEnumerable attributeHash, ProtocolTreeNode children = null, - // string data = "") - //{ - // this.tag = tag ?? ""; - // this.attributeHash = attributeHash ?? new KeyValue[0]; - // this.children = children != null ? new ProtocolTreeNode[] { children } : new ProtocolTreeNode[0]; - // this.data = data ?? ""; - //} public ProtocolTreeNode(string tag, IEnumerable attributeHash, ProtocolTreeNode children = null) { this.tag = tag ?? ""; this.attributeHash = attributeHash ?? new KeyValue[0]; this.children = children != null ? new ProtocolTreeNode[] { children } : new ProtocolTreeNode[0]; - //this.data = data ?? ""; this.data = new byte[0]; } - //public ProtocolTreeNode(string tag, IEnumerable attributeHash, IEnumerable children, - // byte[] data) - // : this(tag, attributeHash, children, Encoding.Default.GetString(data)) - //{ } - - //public ProtocolTreeNode(string tag, IEnumerable attributeHash, string data = "") - // : this(tag, attributeHash, new ProtocolTreeNode[0], data) - //{ } public ProtocolTreeNode(string tag, IEnumerable attributeHash, byte[] data = null) : this(tag, attributeHash, new ProtocolTreeNode[0], data) { } - //public ProtocolTreeNode(string tag, IEnumerable attributeHash) - // : this(tag, attributeHash, new ProtocolTreeNode[0], "") - //{ - //} public ProtocolTreeNode(string tag, IEnumerable attributeHash) : this(tag, attributeHash, new ProtocolTreeNode[0], null) { @@ -77,7 +53,6 @@ public string NodeString(string indent = "") ret += ">"; if (this.data.Length > 0) { - //ret += this.data; ret += WhatsApp.SYSEncoding.GetString(this.data); } if (this.children != null && this.children.Count() > 0) @@ -94,11 +69,6 @@ public string NodeString(string indent = "") public string GetAttribute(string attribute) { - //string ret = null; - //if (this.attributeHash.ContainsKey(attribute)) - //{ - // ret = this.attributeHash[attribute]; - //} var ret = this.attributeHash.FirstOrDefault(x => x.Key.Equals(attribute)); return (ret == null) ? null : ret.Value; } @@ -145,10 +115,6 @@ public IEnumerable GetAllChildren() return this.children.ToArray(); } - //public string GetDataString() - //{ - // return this.data; - //} public byte[] GetData() { return this.data; diff --git a/WhatsAppApi/Helper/TicketManager.cs b/WhatsAppApi/Helper/TicketManager.cs index 831a4cb..43cf4d6 100644 --- a/WhatsAppApi/Helper/TicketManager.cs +++ b/WhatsAppApi/Helper/TicketManager.cs @@ -21,7 +21,6 @@ public static string IdBase public TicketManager() { - //idBase = DateTime.Now.Ticks.ToString(); idBase = Func.GetNowUnixTimestamp().ToString(); } @@ -46,11 +45,7 @@ public static int NextTicket() public static string MakeId(string prefix) { int num = NextTicket(); - if (true)//this.IsVerboseId) - { - return (prefix + num); - } - //return num.ToString("X"); + return (prefix + num); } } } diff --git a/WhatsAppApi/Register/WhatsRegister.cs b/WhatsAppApi/Register/WhatsRegister.cs index c18997f..3e5112d 100644 --- a/WhatsAppApi/Register/WhatsRegister.cs +++ b/WhatsAppApi/Register/WhatsRegister.cs @@ -21,11 +21,6 @@ public static bool RegisterUser(string countryCode, string phoneNumber) var result = StartWebRequest("", "", WhatsConstants.UserAgend, both); Console.WriteLine(result); return result.Contains("status=\"success-sent\""); - /* - * - * - * - */ } public static bool VerifyRegistration(string countryCode, string phoneNumber, string password, string code) @@ -36,13 +31,6 @@ public static bool VerifyRegistration(string countryCode, string phoneNumber, st var result = StartWebRequest("", "", WhatsConstants.UserAgend, verifyString); Console.WriteLine(result); return true; - - /* - * - * - * - * - */ } public static bool ExistsAndDelete(string countrycode, string phone, string pass) @@ -70,7 +58,7 @@ private static string StartWebRequest(string website, string postData, string us return html; } } - catch (WebException ex) + catch (WebException) { return "error"; } @@ -100,7 +88,6 @@ private static string GetRegString(string countryCode, string phonenum, string c { tmpLocalCode = "US"; } - //string countryCode = "49"; string phoneNumber = phonenum; const string buildHash = WhatsConstants.WhatsBuildHash; string tmpToken = ("k7Iy3bWARdNeSL8gYgY6WveX12A1g4uTNXrRzt1H" + buildHash + phoneNumber).MD5String().ToLower(); @@ -110,7 +97,6 @@ private static string GetRegString(string countryCode, string phonenum, string c private static string ToPassword(this string bs) { - //OK LET'S CHECK IF IT'S IOS OR OTHER PALFROM ! ? if (bs.Contains(":")) { string ps = bs.ToUpper(); diff --git a/WhatsAppApi/Response/CorruptStreamException.cs b/WhatsAppApi/Response/CorruptStreamException.cs index 07615ff..49daad0 100644 --- a/WhatsAppApi/Response/CorruptStreamException.cs +++ b/WhatsAppApi/Response/CorruptStreamException.cs @@ -6,13 +6,12 @@ namespace WhatsAppApi.Response { class CorruptStreamException : Exception - { - public string Message { get; private set; } - + { + public string EMessage { get; private set; } public CorruptStreamException(string pMessage) { - // TODO: Complete member initialization - this.Message = pMessage; + // TODO: Complete member initialization + this.EMessage = pMessage; } } } diff --git a/WhatsAppApi/Response/MessageRecvResponse.cs b/WhatsAppApi/Response/MessageRecvResponse.cs index 496ea4d..add4ef7 100644 --- a/WhatsAppApi/Response/MessageRecvResponse.cs +++ b/WhatsAppApi/Response/MessageRecvResponse.cs @@ -55,7 +55,6 @@ public void ParseMessageRecv(ProtocolTreeNode messageNode) private void TypeNotification(ProtocolTreeNode messageNode, string tmpAttrFrom, string tmpAttrbId) { - //bool tmpIsRequest = false; foreach (ProtocolTreeNode tmpChild in (messageNode.GetAllChildren() ?? new ProtocolTreeNode[0])) { if (ProtocolTreeNode.TagEquals(tmpChild, "notification")) @@ -82,22 +81,18 @@ private static void TypeNotificationPicture(ProtocolTreeNode tmpChild, string tm string photoId = item.GetAttribute("id"); if (photoId != null) { - //this.EventHandler.OnPhotoChanged(tmpFrom, item.GetAttribute("author"), photoId); WhatsEventHandler.OnPhotoChangedEventHandler(tmpFrom, item.GetAttribute("author"), photoId); } } else if (ProtocolTreeNode.TagEquals(item, "delete")) { - //this.EventHandler.OnPhotoChanged(tmpFrom, item.GetAttribute("author"), null); WhatsEventHandler.OnPhotoChangedEventHandler(tmpFrom, item.GetAttribute("author"), null); } } } - private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string tmpAttrbId, FMessage.Builder builder, - string tmpAttrFromJid) + private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string tmpAttrbId, FMessage.Builder builder, string tmpAttrFromJid) { - //bool duplicate = false; foreach (ProtocolTreeNode itemNode in (messageNode.GetAllChildren() ?? new ProtocolTreeNode[0])) { if (ProtocolTreeNode.TagEquals(itemNode, "composing")) @@ -110,7 +105,6 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t } else if (ProtocolTreeNode.TagEquals(itemNode, "body") && (tmpAttrbId != null)) { - //string dataString = itemNode.GetDataString(); string dataString = WhatsApp.SYSEncoding.GetString(itemNode.GetData()); var tmpMessKey = new FMessage.Key(tmpAttrFrom, false, tmpAttrbId); builder.Key(tmpMessKey).Remote_resource(tmpAttrFromJid).NewIncomingInstance().Data(dataString); @@ -179,13 +173,8 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t string tmpAttrEncoding = itemNode.GetAttribute("encoding") ?? "text"; if (tmpAttrEncoding == "text") { - //builder.Data(itemNode.GetDataString()); builder.Data(WhatsApp.SYSEncoding.GetString(itemNode.GetData())); } - //else - //{ - // builder.BinaryData(messageNode.data); - //} } var tmpMessageKey = new FMessage.Key(tmpAttrFrom, false, tmpAttrbId); builder.Key(tmpMessageKey).Remote_resource(tmpAttrFromJid).NewIncomingInstance(); @@ -200,19 +189,6 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t if ("jabber:x:event".Equals(str16) && (tmpAttrbId != null)) { var tmpMessageKey = new FMessage.Key(tmpAttrFrom, true, tmpAttrbId); - //if (this.EventHandler != null) - //{ - // ErrorHandler handler2 = null; - // if (trackedMessages.TryGetValue(key4, out handler2)) - // { - // trackedMessages.Remove(key4); - // handler2.OnCompleted(); - // } - // else - // { - // this.EventHandler.OnMessageStatusUpdate(key4, FMessage.Status.ReceivedByServer); - // } - //} } } else if (ProtocolTreeNode.TagEquals(itemNode, "received")) @@ -220,11 +196,7 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t if (tmpAttrbId != null) { var tmpMessageKey = new FMessage.Key(tmpAttrFrom, true, tmpAttrbId); - //if (this.EventHandler != null) - //{ - // this.EventHandler.OnMessageStatusUpdate(key5, FMessage.Status.ReceivedByTarget); - //} - if (true) //this.SupportsReceiptAcks) + if (true) { string tmpAttrType = itemNode.GetAttribute("type"); if ((tmpAttrType != null) && !tmpAttrType.Equals("delivered")) @@ -279,7 +251,6 @@ private void TypeSubject(ProtocolTreeNode messageNode, string tmpFrom, string uJ } } ProtocolTreeNode child = messageNode.GetChild("body"); - //string subject = (child == null) ? null : child.GetDataString(); string subject = (child == null) ? null : WhatsApp.SYSEncoding.GetString(child.GetData()); if (subject != null) { @@ -308,16 +279,6 @@ private void TypeError(ProtocolTreeNode messageNode, string tmpAttrbId, string t if ((tmpAttrFrom != null) && (tmpAttrbId != null)) { FMessage.Key key = new FMessage.Key(tmpAttrFrom, true, tmpAttrbId); - //ErrorHandler handler = null; - //if (trackedMessages.TryGetValue(key, out handler)) - //{ - // trackedMessages.Remove(key); - // handler.OnError(num2); - //} - //else - //{ - // this.EventHandler.OnMessageError(key, num2); - //} } } } diff --git a/WhatsAppApi/Response/WhatsEventHandler.cs b/WhatsAppApi/Response/WhatsEventHandler.cs index e1b1b19..132bf46 100644 --- a/WhatsAppApi/Response/WhatsEventHandler.cs +++ b/WhatsAppApi/Response/WhatsEventHandler.cs @@ -114,19 +114,5 @@ internal static void OnPhotoChangedEventHandler(string from, string uJid, string #endregion - - //#region Unregister Events - //internal void UnregisterStringArrayHandler(StringArrayHandler _event) - //{ - // if (_event != null) - // { - // Delegate[] tmpDelegates = _event.GetInvocationList(); - // for (int i = 0; i < tmpDelegates.Length; i++) - // { - // _event -= (StringArrayHandler)tmpDelegates[i]; - // } - // } - //} - //#endregion } } diff --git a/WhatsAppApi/Response/WhatsParser.cs b/WhatsAppApi/Response/WhatsParser.cs index fe76501..48ad270 100644 --- a/WhatsAppApi/Response/WhatsParser.cs +++ b/WhatsAppApi/Response/WhatsParser.cs @@ -35,15 +35,7 @@ public void ParseProtocolNode(ProtocolTreeNode protNode) } if (!attributeValue.Equals("result")) { - if (attributeValue.Equals("error")) - { - //IqResultHandler handler2 = this.PopIqHandler(id); - //if (handler2 != null) - //{ - // handler2.ErrorNode(node); - //} - } - else if (!attributeValue.Equals("get")) + if (!attributeValue.Equals("get")) { if (!attributeValue.Equals("set")) { @@ -67,72 +59,18 @@ public void ParseProtocolNode(ProtocolTreeNode protNode) else { ProtocolTreeNode node3 = protNode.GetChild("ping"); - if (node3 != null) - { - //this.EventHandler.OnPing(id); - } - else if ((!ProtocolTreeNode.TagEquals(node3, "query") - || (str3 == null)) - && (ProtocolTreeNode.TagEquals(node3, "relay") - && (str3 != null))) + if ((!ProtocolTreeNode.TagEquals(node3, "query") || (str3 == null)) && (ProtocolTreeNode.TagEquals(node3, "relay") && (str3 != null))) { int num; string pin = node3.GetAttribute("pin"); string tmpTimeout = node3.GetAttribute("timeout"); - if ( - !int.TryParse(tmpTimeout ?? "0", WhatsConstants.WhatsAppNumberStyle, CultureInfo.InvariantCulture, - out num)) + if ( !int.TryParse(tmpTimeout ?? "0", WhatsConstants.WhatsAppNumberStyle, CultureInfo.InvariantCulture, out num)) { - //throw new CorruptStreamException( - // "relay-iq exception parsing timeout attribute: " + tmpTimeout); - } - if (pin != null) - { - //this.EventHandler.OnRelayRequest(pin, num, id); + throw new CorruptStreamException("relay-iq exception parsing timeout attribute: " + tmpTimeout); } } } } - else - { - //IqResultHandler handler = this.PopIqHandler(id); - //if (handler != null) - //{ - // handler.Parse(node, str3); - //} - //else if (id.StartsWith(this.Login.User)) - //{ - // ProtocolNode node2 = node.GetChild(0); - // ProtocolNode.Require(node2, "account"); - // string str4 = node2.GetAttribute("kind"); - // if ("paid".Equals(str4)) - // { - // this.account_kind = AccountKind.Paid; - // } - // else if ("free".Equals(str4)) - // { - // this.account_kind = AccountKind.Free; - // } - // else - // { - // this.account_kind = AccountKind.Unknown; - // } - // string s = node2.GetAttribute("expiration"); - // if (s == null) - // { - // throw new IOException("no expiration"); - // } - // try - // { - // this.expire_date = long.Parse(s, CultureInfo.InvariantCulture); - // } - // catch (FormatException) - // { - // throw new IOException("invalid expire date: " + s); - // } - // this.EventHandler.OnAccountChange(this.account_kind, this.expire_date); - //} - } } else if (ProtocolTreeNode.TagEquals(protNode, "presence")) { @@ -141,14 +79,6 @@ public void ParseProtocolNode(ProtocolTreeNode protNode) if (((str9 == null) || "urn:xmpp".Equals(str9)) && (jid != null)) { string str11 = protNode.GetAttribute("type"); - if ("unavailable".Equals(str11)) - { - //this.EventHandler.OnAvailable(jid, false); - } - else if ((str11 == null) || "available".Equals(str11)) - { - //this.EventHandler.OnAvailable(jid, true); - } } else if ("w".Equals(str9) && (jid != null)) { @@ -162,290 +92,17 @@ public void ParseProtocolNode(ProtocolTreeNode protNode) if ("dirty".Equals(str14)) { Dictionary categories = ParseCategories(protNode); - //this.EventHandler.OnDirty(categories); } } - //else if (this.GroupEventHandler != null) - //{ - // this.GroupEventHandler.OnGroupRemoveUser(jid, str13); - //} } - //else if (this.GroupEventHandler != null) - //{ - // this.GroupEventHandler.OnGroupAddUser(jid, str12); - //} } } else if (ProtocolTreeNode.TagEquals(protNode, "message")) { this.messResponseHandler.ParseMessageRecv(protNode); } - else if ((ProtocolTreeNode.TagEquals(protNode, "ib") /*&& (this.EventHandler != null)*/) && - (protNode.GetChild("offline") != null)) - { - //this.EventHandler.OnOfflineMessagesCompleted(); - } } - - //internal void ParseMessageInitialTagAlreadyChecked(ProtocolTreeNode messageNode) - //{ - // FMessage.Builder builder = new FMessage.Builder(); - // string tmpAttrbId = messageNode.GetAttribute("id"); - // string tmpNodeFrom = messageNode.GetAttribute("from"); - // string ujid = messageNode.GetAttribute("author") ?? ""; - // string tmpNodeType = messageNode.GetAttribute("type"); - // string tmpNodeAttrib = messageNode.GetAttribute("t"); - // long result = 0L; - // if (!string.IsNullOrEmpty(tmpNodeAttrib) && long.TryParse(tmpNodeAttrib, out result)) - // { - // builder.Timestamp(new DateTime?(WhatsConstants.UnixEpoch.AddSeconds((double)result))); - // } - // if ("error".Equals(tmpNodeType)) - // { - // int num2 = 0; - // foreach (ProtocolTreeNode node in messageNode.GetAllChildren("error")) - // { - // string tmpCode = node.GetAttribute("code"); - // try - // { - // num2 = int.Parse(tmpCode, CultureInfo.InvariantCulture); - // } - // catch (Exception) - // { - // } - // } - // if ((tmpNodeFrom != null) && (tmpAttrbId != null)) - // { - // FMessage.Key key = new FMessage.Key(tmpNodeFrom, true, tmpAttrbId); - // //ErrorHandler handler = null; - // //if (trackedMessages.TryGetValue(key, out handler)) - // //{ - // // trackedMessages.Remove(key); - // // handler.OnError(num2); - // //} - // //else - // //{ - // // this.EventHandler.OnMessageError(key, num2); - // //} - // } - // } - // else if ("subject".Equals(tmpNodeType)) - // { - // bool flag = false; - // foreach (ProtocolTreeNode node2 in messageNode.GetAllChildren("request")) - // { - // if ("urn:xmpp:receipts".Equals(node2.GetAttribute("xmlns"))) - // { - // flag = true; - // } - // } - // ProtocolTreeNode child = messageNode.GetChild("body"); - // string subject = (child == null) ? null : child.GetDataString(); - // //if ((subject != null) && (this.GroupEventHandler != null)) - // //{ - // // this.GroupEventHandler.OnGroupNewSubject(str3, ujid, subject, int.Parse(s, CultureInfo.InvariantCulture)); - // //} - // //if (flag) - // //{ - // // this.SendSubjectReceived(str3, attributeValue); - // //} - // } - // else if ("chat".Equals(tmpNodeType)) - // { - // bool duplicate = false; - // foreach (ProtocolTreeNode itemNode in (messageNode.GetAllChildren() ?? new ProtocolTreeNode[0])) - // { - // if (ProtocolTreeNode.TagEquals(itemNode, "composing")) - // { - // //if (this.EventHandler != null) - // //{ - // // this.EventHandler.OnIsTyping(str3, true); - // //} - // } - // else if (ProtocolTreeNode.TagEquals(itemNode, "paused")) - // { - // //if (this.EventHandler != null) - // //{ - // // this.EventHandler.OnIsTyping(str3, false); - // //} - // } - // else if (ProtocolTreeNode.TagEquals(itemNode, "body") && (tmpAttrbId != null)) - // { - // string dataString = itemNode.GetDataString(); - // FMessage.Key key2 = new FMessage.Key(tmpNodeFrom, false, tmpAttrbId); - // builder.Key(key2).Remote_resource(ujid).NewIncomingInstance().Data(dataString); - // } - // else if (ProtocolTreeNode.TagEquals(itemNode, "media") && (tmpAttrbId != null)) - // { - // long num3; - // int num4; - // builder.Media_wa_type(FMessage.GetMessage_WA_Type(itemNode.GetAttribute("type"))).Media_url(itemNode.GetAttribute("url")).Media_name(itemNode.GetAttribute("file")); - // if (long.TryParse(itemNode.GetAttribute("size"), WhatsConstants.WhatsAppNumberStyle, CultureInfo.InvariantCulture, out num3)) - // { - // builder.Media_size(num3); - // } - // string str10 = itemNode.GetAttribute("seconds"); - // if ((str10 != null) && int.TryParse(str10, WhatsConstants.WhatsAppNumberStyle, CultureInfo.InvariantCulture, out num4)) - // { - // builder.Media_duration_seconds(num4); - // } - // if (((FMessage.Type)builder.Media_wa_type().Value) == FMessage.Type.Location) - // { - // double num5 = 0; - // double num6 = 0; - // string str11 = itemNode.GetAttribute("latitude"); - // string str12 = itemNode.GetAttribute("longitude"); - // if ((str11 == null) || (str12 == null)) - // { - // str11 = "0"; - // str12 = "0"; - // } - // if (!double.TryParse(str11, WhatsConstants.WhatsAppNumberStyle, CultureInfo.InvariantCulture, out num5) || !double.TryParse(str12, WhatsConstants.WhatsAppNumberStyle, CultureInfo.InvariantCulture, out num6)) - // { - // //throw new CorruptStreamException("location message exception parsing lat or long attribute: " + str11 + " " + str12); - // } - // builder.Latitude(num5).Longitude(num6); - // string details = itemNode.GetAttribute("name"); - // string url = itemNode.GetAttribute("url"); - // if (details != null) - // { - // builder.Location_details(details); - // } - // if (url != null) - // { - // builder.Location_url(url); - // } - // } - // if (((FMessage.Type)builder.Media_wa_type().Value) == FMessage.Type.Contact) - // { - // ProtocolTreeNode node5 = itemNode.GetChild("media"); - // if (node5 != null) - // { - // builder.Media_name(node5.GetAttribute("name")).Data(node5.GetDataString()); - // } - // } - // else - // { - // string str15 = itemNode.GetAttribute("encoding") ?? "text"; - // if (str15 == "text") - // { - // builder.Data(itemNode.GetDataString()); - // } - // else - // { - // //builder.BinaryData(messageNode.data); - // } - // } - // FMessage.Key key3 = new FMessage.Key(tmpNodeFrom, false, tmpAttrbId); - // builder.Key(key3).Remote_resource(ujid).NewIncomingInstance(); - // } - // else if (ProtocolTreeNode.TagEquals(itemNode, "request")) - // { - // builder.Wants_receipt(true); - // } - // else if (ProtocolTreeNode.TagEquals(itemNode, "x")) - // { - // string str16 = itemNode.GetAttribute("xmlns"); - // if ("jabber:x:event".Equals(str16) && (tmpAttrbId != null)) - // { - // FMessage.Key key4 = new FMessage.Key(tmpNodeFrom, true, tmpAttrbId); - // //if (this.EventHandler != null) - // //{ - // // ErrorHandler handler2 = null; - // // if (trackedMessages.TryGetValue(key4, out handler2)) - // // { - // // trackedMessages.Remove(key4); - // // handler2.OnCompleted(); - // // } - // // else - // // { - // // this.EventHandler.OnMessageStatusUpdate(key4, FMessage.Status.ReceivedByServer); - // // } - // //} - // } - // } - // else if (ProtocolTreeNode.TagEquals(itemNode, "received")) - // { - // if (tmpAttrbId != null) - // { - // FMessage.Key key5 = new FMessage.Key(tmpNodeFrom, true, tmpAttrbId); - // //if (this.EventHandler != null) - // //{ - // // this.EventHandler.OnMessageStatusUpdate(key5, FMessage.Status.ReceivedByTarget); - // //} - // if (true)//this.SupportsReceiptAcks) - // { - // string str17 = itemNode.GetAttribute("type"); - // if ((str17 != null) && !str17.Equals("delivered")) - // { - // if (str17.Equals("visible")) - // { - // this.WhatsSendHandler.SendVisibleReceiptAck(tmpNodeFrom, tmpAttrbId); - // } - // } - // else - // { - // this.WhatsSendHandler.SendDeliveredReceiptAck(tmpNodeFrom, tmpAttrbId); - // } - // } - // } - // } - // else if (ProtocolTreeNode.TagEquals(itemNode, "offline")) - // { - // builder.Offline(true); - // } - // } - // if (!builder.Timestamp().HasValue) - // { - // builder.Timestamp(new DateTime?(DateTime.Now)); - // } - // FMessage message = builder.Build(); - // if ((message != null) && (this.MessageRecievedEvent != null)) - // { - // this.MessageRecievedEvent(message); - // //this.EventHandler.OnMessageForMe(message, duplicate); - // } - // } - // else if ("notification".Equals(tmpNodeType)) - // { - // bool flag3 = false; - // foreach (ProtocolTreeNode node6 in (messageNode.GetAllChildren() ?? new ProtocolTreeNode[0])) - // { - // if (ProtocolTreeNode.TagEquals(node6, "notification")) - // { - // string x = node6.GetAttribute("type"); - // if (StringComparer.Ordinal.Equals(x, "picture") )//&& (this.EventHandler != null)) - // { - // foreach (ProtocolTreeNode node7 in (node6.GetAllChildren() ?? new ProtocolTreeNode[0])) - // { - // if (ProtocolTreeNode.TagEquals(node7, "set")) - // { - // string photoId = node7.GetAttribute("id"); - // if (photoId != null) - // { - // //this.EventHandler.OnPhotoChanged(str3, node7.GetAttribute("author"), photoId); - // } - // } - // else if (ProtocolTreeNode.TagEquals(node7, "delete")) - // { - // //this.EventHandler.OnPhotoChanged(str3, node7.GetAttribute("author"), null); - // } - // } - // } - // } - // else if (ProtocolTreeNode.TagEquals(node6, "request")) - // { - // flag3 = true; - // } - // } - // if (flag3) - // { - // //this.whatsSendHandler.SendNotificationReceived(str3, attributeValue); - // } - // } - //} - internal static Dictionary ParseCategories(ProtocolTreeNode dirtyNode) { var dictionary = new Dictionary(); diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 1d5f4ec..7a3597a 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -20,14 +20,9 @@ public enum CONNECTION_STATUS CONNECTED } - //private readonly Encoding sysEncoding; private AccountInfo accountinfo; - //private Dictionary challengeArray; - //private string connectedStatus = "connected"; public static bool DEBUG; - //private string disconnectedStatus = "disconnected"; private string imei; - //private string loginStatus; private CONNECTION_STATUS loginStatus; private object messageLock = new object(); private List messageQueue; @@ -35,26 +30,20 @@ public enum CONNECTION_STATUS private string phoneNumber; private BinTreeNodeReader reader; private BinTreeNodeWriter writer; - //private int timeout = 2000; private int timeout = 5000; private WhatsNetwork whatsNetwork; public WhatsSendHandler WhatsSendHandler { get; private set; } public WhatsParser WhatsParser { get; private set; } - //protocol 1.2 - //public static readonly Encoding SYSEncoding = Encoding.GetEncoding("ISO-8859-1"); public static readonly Encoding SYSEncoding = Encoding.UTF8; private byte[] _encryptionKey; private byte[] _challengeBytes; private List _incompleteBytes; - //array("sec" => 2, "usec" => 0); public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) { this.messageQueue = new List(); - //this.sysEncoding = Encoding.GetEncoding("ISO-8859-1"); - //this.challengeArray = new Dictionary(); this.phoneNumber = phoneNum; this.imei = imei; @@ -63,12 +52,7 @@ public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) string[] dict = DecodeHelper.getDictionary(); this.writer = new BinTreeNodeWriter(dict); this.reader = new BinTreeNodeReader(dict); - - //this.loginStatus = disconnectedStatus; this.loginStatus = CONNECTION_STATUS.DISCONNECTED; - - //this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.sysEncoding, this.timeout); - //this.WhatsParser = new WhatsParser(this.whatsNetwork); this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.timeout); this.WhatsParser = new WhatsParser(this.whatsNetwork, this.writer); this.WhatsSendHandler = this.WhatsParser.WhatsSendHandler; @@ -133,7 +117,6 @@ public bool HasMessages() public void Login() { - //"$this->device-$this->whatsAppVer-$this->port"; string resource = string.Format(@"{0}-{1}-{2}", WhatsConstants.IphoneDevice, WhatsConstants.WhatsAppVer, @@ -141,34 +124,23 @@ public void Login() var data = this.writer.StartStream(WhatsConstants.WhatsAppServer, resource); var feat = this.addFeatures(); var auth = this.addAuth(); - //this.whatsNetwork.SendData(data); - //this.whatsNetwork.SendNode(feat); - //this.whatsNetwork.SendNode(auth); this.whatsNetwork.SendData(data); this.whatsNetwork.SendData(this.writer.Write(feat, false)); this.whatsNetwork.SendData(this.writer.Write(auth, false)); - this.PollMessages(); - //ProtocolTreeNode authResp = this.addAuthResponse(); - //this.whatsNetwork.SendNode(authResp); - ProtocolTreeNode authResp = this.addAuthResponse_v1_2(); + ProtocolTreeNode authResp = this.addAuthResponse(); this.whatsNetwork.SendData(this.writer.Write(authResp, false)); int cnt = 0; do { this.PollMessages(); System.Threading.Thread.Sleep(50); - //} while ((cnt++ < 100) && - // (this.loginStatus.Equals(this.disconnectedStatus, StringComparison.OrdinalIgnoreCase))); - } while ((cnt++ < 100) && - (this.loginStatus == CONNECTION_STATUS.DISCONNECTED)); + } + while ((cnt++ < 100) && (this.loginStatus == CONNECTION_STATUS.DISCONNECTED)); } public void Message(string to, string txt) { - //var bodyNode = new ProtocolTreeNode("body", null, txt); - - //var tmpMessage = new FMessage(to, true) { key = { id = TicketCounter.MakeId("mSend_") }, data = txt }; var tmpMessage = new FMessage(to, true) { key = { id = TicketManager.GenerateId() }, data = txt }; this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage); } @@ -196,18 +168,6 @@ public void PollMessages() public void Pong(string msgid) { this.WhatsParser.WhatsSendHandler.SendPong(msgid); - - //string whatsAppServer = this.whatsAppServer; - - //var messageHash = new KeyValue[] - // { - // new KeyValue("to", whatsAppServer), - // new KeyValue("id", msgid), - // new KeyValue("type", "result"), - // }; - - //var messsageNode = new ProtocolTreeNode("iq", messageHash, null); - //this.whatsNetwork.SendNode(messsageNode); } public void RequestLastSeen(string jid) @@ -217,14 +177,11 @@ public void RequestLastSeen(string jid) public void sendNickname(string nickname) { - this.WhatsParser.WhatsSendHandler.SendAvailableForChat(nickname); //es muss der nickname festgelegt werden + this.WhatsParser.WhatsSendHandler.SendAvailableForChat(nickname); } protected ProtocolTreeNode addAuth() { - //var node = new ProtocolTreeNode("auth", - // new KeyValue[] { new KeyValue("xmlns", @"urn:ietf:params:xml:ns:xmpp-sasl"), new KeyValue("mechanism", "DIGEST-MD5-1") }); - // change to protocol 1.2 var node = new ProtocolTreeNode("auth", new KeyValue[] { new KeyValue("xmlns", @"urn:ietf:params:xml:ns:xmpp-sasl"), new KeyValue("mechanism", "WAUTH-1"), @@ -232,21 +189,7 @@ protected ProtocolTreeNode addAuth() return node; } - //protected ProtocolTreeNode addAuthResponse() - //{ - // if (!this.challengeArray.ContainsKey("nonce")) - // this.challengeArray.Add("nonce", ""); - - // string resp = this.authenticate(this.challengeArray["nonce"]); - - // var node = new ProtocolTreeNode("response", - // new KeyValue[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") }, - // Func.EncodeTo64(resp, this.sysEncoding)); - // return node; - //} - - //change to protocol 1.2 - protected ProtocolTreeNode addAuthResponse_v1_2() + protected ProtocolTreeNode addAuthResponse() { while (this._challengeBytes == null) { @@ -279,58 +222,10 @@ protected ProtocolTreeNode addFeatures() var child = new ProtocolTreeNode("receipt_acks", null); var childList = new List(); childList.Add(child); - //var parent = new ProtocolTreeNode("stream:features", null, childList, ""); var parent = new ProtocolTreeNode("stream:features", null, childList, null); return parent; } - //protected string authenticate(string nonce) - //{ - // string NC = "00000001"; - // string qop = "auth"; - // string cnonce = Func.random_uuid(); - // string data1 = this.phoneNumber; - // data1 += ":"; - // data1 += WhatsConstants.WhatsAppServer; - // data1 += ":"; - // data1 += this.encryptPassword(); //this.EncryptPassword(); - - // string data2 = Func.HexString2Ascii(md5(data1)); - // data2 += ":"; - // data2 += nonce; - // data2 += ":"; - // data2 += cnonce; - - // string data3 = "AUTHENTICATE:"; - // data3 += WhatsConstants.WhatsAppDigest; - - // string data4 = md5(data2); - // data4 += ":"; - // data4 += nonce; - // data4 += ":"; - // data4 += NC; - // data4 += ":"; - // data4 += cnonce; - // data4 += ":"; - // data4 += qop; - // data4 += ":"; - // data4 += md5(data3); - - // string data5 = md5(data4); - // string response = - // string.Format( - // "username=\"{0}\",realm=\"{1}\",nonce=\"{2}\",cnonce=\"{3}\",nc={4},qop={5},digest-uri=\"{6}\",response={7},charset=ISO-8859-1", - // this.phoneNumber, - // WhatsConstants.WhatsAppRealm, - // nonce, - // cnonce, - // NC, - // qop, - // WhatsConstants.WhatsAppDigest, - // data5); - // return response; - //} - protected void DebugPrint(string debugMsg) { if (WhatsApp.DEBUG && debugMsg.Length > 0) @@ -341,36 +236,13 @@ protected void DebugPrint(string debugMsg) protected void processChallenge(ProtocolTreeNode node) { - //string challenge = Func.DecodeTo64(node.data, this.sysEncoding); - //string[] challengeStrs = challenge.Split(','); - //this.challengeArray = new Dictionary(); - //foreach (var item in challengeStrs) - //{ - // string[] d = item.Split('='); - // if (this.challengeArray.ContainsKey(d[0])) - // this.challengeArray[d[0]] = d[1].Replace("\"", ""); - // else - // this.challengeArray.Add(d[0], d[1].Replace("\"", "")); - //} - - //change to protocol 1.2 _challengeBytes = node.data; } - //protected void processInboundData(string data) protected void processInboundData(byte[] data) { try { - //List combined = new List(); - //foreach (IncompleteMessageException e in _incompleteBytes) - //{ - // combined.AddRange(e.getInput()); - //} - //_incompleteBytes.Clear(); - //if (data !=null) - //combined.AddRange(data); - //var node = this.reader.nextTree(combined.ToArray()); var node = this.reader.nextTree(data); while (node != null) { @@ -424,16 +296,14 @@ protected void processInboundData(byte[] data) node = this.reader.nextTree(); } } - catch (IncompleteMessageException e) + catch (IncompleteMessageException) { - //_incompleteBytes.Add(e); - //this.PollMessages(); + } } protected void sendMessageReceived(ProtocolTreeNode msg) { - //this.WhatsParser.WhatsSendHandler.SendMessageReceived(); ProtocolTreeNode requestNode = msg.GetChild("request"); if (requestNode == null || !requestNode.GetAttribute("xmlns").Equals("urn:xmpp:receipts", StringComparison.OrdinalIgnoreCase)) @@ -441,18 +311,6 @@ protected void sendMessageReceived(ProtocolTreeNode msg) FMessage tmpMessage = new FMessage(new FMessage.Key(msg.GetAttribute("from"), true, msg.GetAttribute("id"))); this.WhatsParser.WhatsSendHandler.SendMessageReceived(tmpMessage); - //var receivedNode = new ProtocolTreeNode("received", - // new[] {new KeyValue("xmlns", "urn:xmpp:receipts")}); - - //var messageNode = new ProtocolTreeNode("message", - // new[] - // { - // new KeyValue("to", msg.GetAttribute("from")), - // new KeyValue("type", "chat"), - // new KeyValue("id", msg.GetAttribute("id")) - // }, - // new[] {receivedNode}); - //this.whatsNetwork.SendNode(messageNode); } private string md5(string pass) @@ -464,13 +322,10 @@ private string md5(string pass) sb.AppendFormat("{0:x2}", dataMd5[i]); return sb.ToString(); } + private void PrintInfo(string p) { this.DebugPrint(p); } - - /** - * TODO - */ } } diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index 7151c24..4afbf0d 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -12,31 +12,22 @@ public class WhatsNetwork private readonly int recvTimeout; private readonly string whatsHost; private readonly int whatsPort; - - //private string incomplete_message = ""; private List incomplete_message = new List(); private Socket socket; - //private BinTreeNodeWriter binWriter; public WhatsNetwork(string whatsHost, int port, int timeout = 2000) - //public WhatsNetwork(string whatsHost, int port, BinTreeNodeWriter writer, int timeout = 2000) { this.recvTimeout = timeout; this.whatsHost = whatsHost; this.whatsPort = port; - //this.binWriter = writer; this.incomplete_message = new List(); } public void Connect() { this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - this.socket.Connect(this.whatsHost, this.whatsPort); + this.socket.Connect(this.whatsHost, this.whatsPort); this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.recvTimeout); - - //var tmpNetStream = new NetworkStream(this.socket); - //this.streamReader = new StreamReader(tmpNetStream); - //this.streamWriter = new StreamWriter(tmpNetStream); if (!this.socket.Connected) throw new ConnectionException("Cannot connect"); @@ -48,28 +39,10 @@ public void Disconenct() this.socket.Disconnect(true); } - //public string ReadData() - //{ - // string buff = ""; - // string ret = Socket_read(1024); - // if (ret != null) - // { - // buff = this.incomplete_message + ret; - // this.incomplete_message = ""; - // } - // return buff; - //} public byte[] ReadData() { List buff = new List(); byte[] ret = Socket_read(1024); - //if (ret != null) - //{ - // buff.AddRange(this.incomplete_message); - // buff.AddRange(ret); - // this.incomplete_message = new List(); - //} - //return buff.ToArray(); return ret; } @@ -81,43 +54,7 @@ public void SendData(byte[] data) { Socket_send(data); } - - //public void SendNode(ProtocolTreeNode node) - //{ - // //this.DebugPrint(node.NodeString("SENT: ")); - // this.SendData(this.binWriter.Write(node)); - //} - - //private string Socket_read(int length) - //{ - // if (!socket.Connected) - // { - // throw new System.IO.IOException("Disconnected"); - // } - - // var buff = new byte[length]; - // int receiveLength = 0; - // try - // { - // receiveLength = socket.Receive(buff, 0, length, 0); - // } - // catch (SocketException excpt) - // { - // if (excpt.SocketErrorCode == SocketError.TimedOut) - // { - // Console.WriteLine("Socket-Timout"); - // return null; - // } - // else - // { - // Console.WriteLine("Unbehandelter Fehler bei Sockerread: {0}", excpt); - // throw excpt; - // } - // } - - // string tmpRet = this.sysEncoding.GetString(buff); - // return tmpRet; - //} + private byte[] Socket_read(int length) { if (!socket.Connected) @@ -138,7 +75,6 @@ private byte[] Socket_read(int length) if (excpt.SocketErrorCode == SocketError.TimedOut) { Console.WriteLine("Socket-Timout"); - //throw new ConnectionException("Timeout", excpt); return null; } else @@ -147,7 +83,8 @@ private byte[] Socket_read(int length) throw new ConnectionException("error", excpt); } } - } while (receiveLength <= 0); + } + while (receiveLength <= 0); byte[] tmpRet = new byte[receiveLength]; if (receiveLength > 0) @@ -160,6 +97,7 @@ private void Socket_send(string data, int length, int flags) var tmpBytes = WhatsApp.SYSEncoding.GetBytes(data); this.socket.Send(tmpBytes); } + private void Socket_send(byte[] data) { this.socket.Send(data); diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 55b685e..3d959e3 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -12,17 +12,12 @@ namespace WhatsAppApi public class WhatsSendHandler { private string MyJID = ""; - private string PushName = "Nickname?"; private string whatsAppRealm = "s.whatsapp.net"; private BinTreeNodeWriter _binWriter; - - //private BinTreeNodeWriter TreeNodeWriter; - private WhatsNetwork whatsNetwork; - //this.Login.domain ?? - //public WhatsSendHandler(WhatsNetwork net) + private WhatsNetwork whatsNetwork; + internal WhatsSendHandler(WhatsNetwork net, BinTreeNodeWriter writer) { - //this.TreeNodeWriter = new BinTreeNodeWriter(DecodeHelper.getDictionary()); this.whatsNetwork = net; this._binWriter = writer; } @@ -30,7 +25,6 @@ internal WhatsSendHandler(WhatsNetwork net, BinTreeNodeWriter writer) public void SendActive() { var node = new ProtocolTreeNode("presence", new[] {new KeyValue("type", "active")}); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -42,17 +36,6 @@ public void SendAddParticipants(string gjid, IEnumerable participants) public void SendAddParticipants(string gjid, IEnumerable participants, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("add_group_participants_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // List successList = new List(); - // Dictionary failTable = new Dictionary(); - // this.ReadSuccessAndFailure(node, successList, failTable, "add"); - // this.GroupEventHandler.OnAddGroupParticipants(from, successList, failTable); - // if (onSuccess != null) - // { - // onSuccess(); - // } - //}, onError)); this.SendVerbParticipants(gjid, participants, id, "add"); } @@ -65,7 +48,6 @@ public void SendAvailableForChat(string nickName) public void SendClearDirty(IEnumerable categoryNames) { string id = TicketCounter.MakeId("clean_dirty_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(null)); IEnumerable source = from category in categoryNames select new ProtocolTreeNode("category", new[] { new KeyValue("name", category) }); var child = new ProtocolTreeNode("clean", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") }, source); var node = new ProtocolTreeNode("iq", @@ -74,7 +56,6 @@ public void SendClearDirty(IEnumerable categoryNames) new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "s.whatsapp.net") }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -88,34 +69,12 @@ public void SendClientConfig(string platform, string lg, string lc) string v = TicketCounter.MakeId("config_"); var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("lg", lg), new KeyValue("lc", lc) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", whatsAppRealm) }, new ProtocolTreeNode[] { child }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, bool preview, bool defaultSetting, bool groupsSetting, IEnumerable groups, Action onCompleted, Action onError) { - //Action parse = null; string id = TicketCounter.MakeId("config_"); - //if ((onCompleted != null) || (onError != null)) - //{ - // if (onCompleted == null) - // { - // onCompleted = delegate - // { - // }; - // } - // if (onError == null) - // { - // onError = delegate(int ign) - // { - // }; - // } - // if (parse == null) - // { - // parse = (node, from) => onCompleted(); - // } - // this.AddIqHandler(id, new FunXMPP.IqResultHandler(parse, onError)); - //} var node = new ProtocolTreeNode("iq", new[] { @@ -139,14 +98,12 @@ public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, }, this.ProcessGroupSettings(groups)) }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendClose() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -154,7 +111,6 @@ public void SendComposing(string to) { var child = new ProtocolTreeNode("composing", new[] { new KeyValue("xmlns", "http://jabber.org/protocol/chatstates") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "chat") }, new ProtocolTreeNode[] {child}); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -166,33 +122,14 @@ public void SendCreateGroupChat(string subject) public void SendCreateGroupChat(string subject, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("create_group_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // FunXMPP.ProtocolTreeNode child = node.GetChild(0); - // FunXMPP.ProtocolTreeNode.Require(child, "group"); - // string gid = child.GetAttributeValue("id"); - // this.GroupEventHandler.OnGroupCreated(GidToGjid(gid), subject); - // if (onSuccess != null) - // { - // onSuccess(GidToGjid(gid)); - // } - //}, delegate(int code) - //{ - // if (onError != null) - // { - // onError(code); - // } - //})); var child = new ProtocolTreeNode("group", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("action", "create"), new KeyValue("subject", subject) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "g.us") }, new ProtocolTreeNode[] {child}); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendDeleteAccount(Action onSuccess, Action onError) { string id = TicketCounter.MakeId("del_acct_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler((node, from) => onSuccess(), onError)); var node = new ProtocolTreeNode("iq", new[] { @@ -207,7 +144,6 @@ public void SendDeleteAccount(Action onSuccess, Action onError) new KeyValue("xmlns", "urn:xmpp:whatsapp:account") }) }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -217,13 +153,11 @@ public void SendDeleteFromRoster(string jid) var innerChild = new ProtocolTreeNode("item", new[] { new KeyValue("jid", jid), new KeyValue("subscription", "remove") }); var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "jabber:iq:roster") }, new ProtocolTreeNode[] {innerChild}); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "set"), new KeyValue("id", v) }, new ProtocolTreeNode[] {child}); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendDeliveredReceiptAck(string to, string id) { - //this.Login.TreeNodeWriter.Write(this.GetReceiptAck(to, id, "delivered"), this.AsyncHandler); this.SendReceiptAck(to, id, "delivered"); } @@ -235,125 +169,52 @@ public void SendEndGroupChat(string gjid) public void SendEndGroupChat(string gjid, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("remove_group_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // if (onSuccess != null) - // { - // onSuccess(); - // } - //}, onError)); var child = new ProtocolTreeNode("group", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("action", "delete") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] {child}); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetClientConfig() { string id = TicketCounter.MakeId("get_config_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // FunXMPP.ProtocolTreeNode child = node.GetChild(0); - // FunXMPP.ProtocolTreeNode.Require(child, "config"); - // string attributeValue = child.GetAttributeValue("id"); - // this.EventHandler.OnClientConfigReceived(attributeValue); - //}, delegate(FunXMPP.ProtocolTreeNode node) - //{ - // foreach (string str in from configNode in node.GetAllChildren("config") - // where configNode != null - // select configNode.GetAttributeValue("id")) - // { - // this.EventHandler.OnClientConfigReceived(str); - // } - //})); var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", this.whatsAppRealm) }, new ProtocolTreeNode[] { child }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetDirty() { string id = TicketCounter.MakeId("get_dirty_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // FunXMPP.ProtocolTreeNode child = node.GetChild(0); - // FunXMPP.ProtocolTreeNode.Require(child, "dirty"); - // Dictionary categories = ParseCategories(child); - // this.EventHandler.OnDirtyResponse(categories); - //})); var child = new ProtocolTreeNode("status", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, new ProtocolTreeNode[] {child}); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetGroupInfo(string gjid) { string id = TicketCounter.MakeId("get_g_info_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // FunXMPP.ProtocolTreeNode child = node.GetChild(0); - // FunXMPP.ProtocolTreeNode.Require(child, "group"); - // child.GetAttributeValue("id"); - // string owner = child.GetAttributeValue("owner"); - // string attributeValue = child.GetAttributeValue("subject"); - // string s = child.GetAttributeValue("s_t"); - // string str4 = child.GetAttributeValue("s_o"); - // string str5 = child.GetAttributeValue("creation"); - // this.GroupEventHandler.OnGroupInfo(from, owner, attributeValue, str4, int.Parse(s, CultureInfo.InvariantCulture), int.Parse(str5, CultureInfo.InvariantCulture)); - //})); var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "w:g") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] {child}); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } - //public void SendGetGroups() - //{ - // this.SendGetGroups((Action)null, (Action)null); - //} - public void SendGetGroups(Action onSuccess, Action onError) { string id = TicketCounter.MakeId("get_groups_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // List list = new List(); - // this.ReadGroupList(node, list); - // this.GroupEventHandler.OnParticipatingGroups(list); - // if (onSuccess != null) - // { - // onSuccess(); - // } - //}, onError)); this.SendGetGroups(id, "participating"); } public void SendGetOwningGroups() { string id = TicketCounter.MakeId("get_owning_groups_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // List list = new List(); - // this.ReadGroupList(node, list); - // this.GroupEventHandler.OnOwningGroups(list); - //})); this.SendGetGroups(id, "owning"); } public void SendGetParticipants(string gjid) { string id = TicketCounter.MakeId("get_participants_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // List list = new List(); - // this.ReadAttributeList(node, list, "participant", "jid"); - // this.GroupEventHandler.OnGetParticipants(from, list); - //})); var child = new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:g") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", gjid) }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -365,26 +226,6 @@ public void SendGetPhoto(string jid, bool largeFormat) public void SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, Action onComplete) { string id = TicketCounter.MakeId("get_photo_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // string x = node.GetAttributeValue("type"); - // if (StringComparer.Ordinal.Equals(x, "result") && (this.EventHandler != null)) - // { - // foreach (FunXMPP.ProtocolTreeNode node2 in node.children ?? new FunXMPP.ProtocolTreeNode[0]) - // { - // if (FunXMPP.ProtocolTreeNode.TagEquals(node2, "picture")) - // { - // string attributeValue = node2.GetAttributeValue("id"); - // if (((attributeValue != null) && (node2.data != null)) && (node2.data.Length != 0)) - // { - // this.EventHandler.OnPhotoData(attributeValue, node2.data, jid, largeFormat); - // this.EventHandler.OnPhotoChanged(jid, null, attributeValue); - // } - // } - // } - // } - // onComplete(); - //}, error => onComplete())); var attrList = new List { new KeyValue("xmlns", "w:profile:picture") }; if (!largeFormat) { @@ -396,94 +237,32 @@ public void SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, A } var child = new ProtocolTreeNode("picture", attrList.ToArray()); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid) }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetPhotoIds(IEnumerable jids) { string id = TicketCounter.MakeId("get_photo_id_"); - //this.AddIqHandler(id, new FunXMPP.IqResultHandler(delegate(FunXMPP.ProtocolTreeNode node, string from) - //{ - // string x = node.GetAttributeValue("type"); - // if (StringComparer.Ordinal.Equals(x, "result") && (this.EventHandler != null)) - // { - // foreach (FunXMPP.ProtocolTreeNode node2 in node.children ?? new FunXMPP.ProtocolTreeNode[0]) - // { - // if (FunXMPP.ProtocolTreeNode.TagEquals(node2, "list")) - // { - // foreach (FunXMPP.ProtocolTreeNode node3 in node2.children ?? new FunXMPP.ProtocolTreeNode[0]) - // { - // if (FunXMPP.ProtocolTreeNode.TagEquals(node3, "user")) - // { - // string attributeValue = node3.GetAttributeValue("jid"); - // string photoId = node3.GetAttributeValue("id"); - // if (attributeValue != null) - // { - // this.EventHandler.OnPhotoChanged(attributeValue, null, photoId); - // } - // } - // } - // } - // } - // } - //})); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", this.MyJID) }, new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:profile:picture") }, (from jid in jids select new ProtocolTreeNode("user", new[] { new KeyValue("jid", jid) })).ToArray())); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetPrivacyList() { string id = TicketCounter.MakeId("privacylist_"); - //this.AddIqHandler(id, new IqResultHandler(delegate(ProtocolTreeNode node, string from) - //{ - // ProtocolTreeNode child = node.GetChild(0); - // ProtocolTreeNode.Require(child, "query"); - // ProtocolTreeNode node3 = child.GetChild(0); - // ProtocolTreeNode.Require(node3, "list"); - // this.EventHandler.OnPrivacyBlockList(this.GetJidsFromPrivacyList(node3)); - //}, delegate(int errorCode) - //{ - // if (errorCode == 0x194) - // { - // this.EventHandler.OnPrivacyBlockList(new string[0]); - // } - // else - // { - // (Application.Current as App).WriteLineDebug("privacy list - unexpected error " + errorCode); - // } - //})); var innerChild = new ProtocolTreeNode("list", new[] { new KeyValue("name", "default") }); var child = new ProtocolTreeNode("query", new KeyValue[] { new KeyValue("xmlns", "jabber:iq:privacy") }, innerChild); var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "get") }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendGetServerProperties() { string id = TicketCounter.MakeId("get_server_properties_"); - //this.AddIqHandler(id, new IqResultHandler(delegate(ProtocolTreeNode node, string from) - //{ - // var enumerable = from n in node.GetAllChildren("props") - // select n.GetAllChildren("prop") into propNode - // select new { Name = propNode.GetAttributeValue("name"), Value = propNode.GetAttributeValue("value") }; - // Dictionary nameValueMap = new Dictionary(); - // foreach (var typeb in enumerable) - // { - // if ((typeb.Name != null) && (typeb.Value != null)) - // { - // nameValueMap[typeb.Name] = typeb.Value; - // } - // } - // this.GroupEventHandler.OnServerProperties(nameValueMap); - //})); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, new ProtocolTreeNode("props", new[] { new KeyValue("xmlns", "w") })); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -496,7 +275,6 @@ public void SendGetStatus(string jid) string v = TicketManager.GenerateId(); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "action"), new KeyValue("id", v) }, new ProtocolTreeNode("action", new[] { new KeyValue("type", "get") })); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } } @@ -504,7 +282,6 @@ public void SendGetStatus(string jid) public void SendInactive() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "inactive") }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -521,24 +298,9 @@ public void SendLeaveGroup(string gjid, Action onSuccess, Action onError) public void SendLeaveGroups(IEnumerable gjids, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("leave_group_"); - //this.AddIqHandler(id, new IqResultHandler(delegate (ProtocolTreeNode node, string from) { - // ProtocolTreeNode child = node.GetChild("leave"); - // if (child != null) - // { - // foreach (string str in from group in child.GetAllChildren("group") select group.GetAttributeValue("id")) - // { - // this.GroupEventHandler.OnLeaveGroup(str); - // } - // } - // if (onSuccess != null) - // { - // onSuccess(); - // } - //}, onError)); IEnumerable innerChilds = from gjid in gjids select new ProtocolTreeNode("group", new[] { new KeyValue("id", gjid) }); var child = new ProtocolTreeNode("leave", new KeyValue[] { new KeyValue("xmlns", "w:g") }, innerChilds); var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "g.us") }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -558,13 +320,11 @@ public void SendMessageReceived(FMessage message) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendNop() { - //this.whatsNetwork.SendNode(null); this.whatsNetwork.SendData(this._binWriter.Write(null)); } @@ -572,7 +332,6 @@ public void SendNotificationReceived(string jid, string id) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "notification"), new KeyValue("id", id) }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -580,53 +339,34 @@ public void SendPaused(string to) { var child = new ProtocolTreeNode("paused", new[] { new KeyValue("xmlns", "http://jabber.org/protocol/chatstates") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "chat") }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendPing() { string id = TicketCounter.MakeId("ping_"); - //this.AddIqHandler(id, new IqResultHandler((node, from) => this.EventHandler.OnPingResponseReceived(), node => this.EventHandler.OnPingResponseReceived())); var child = new ProtocolTreeNode("ping", new[] { new KeyValue("xmlns", "w:p") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get") }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendPong(string id) { var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "result"), new KeyValue("to", this.whatsAppRealm), new KeyValue("id", id) }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendPresenceSubscriptionRequest(string to) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "subscribe"), new KeyValue("to", to) }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendQueryLastOnline(string jid) { string id = TicketCounter.MakeId("last_"); - //this.AddIqHandler(id, new IqResultHandler(delegate(ProtocolTreeNode node, string from) - //{ - // int num; - // ProtocolTreeNode child = node.GetChild(0); - // ProtocolTreeNode.Require(child, "query"); - // string s = child.GetAttributeValue("seconds"); - // string dataString = null; - // dataString = child.GetDataString(); - // if (((s != null) && (from != null)) && int.TryParse(s, WhatsAppNumberStyle, CultureInfo.InvariantCulture, out num)) - // { - // this.EventHandler.OnLastSeen(from, num, dataString); - // } - //})); var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "jabber:iq:last") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid) }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -635,7 +375,6 @@ public void SendRelayCapable(string platform, bool value) string v = TicketCounter.MakeId("relay_"); var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("relay", value ? "1" : "0") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", this.whatsAppRealm) }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -643,7 +382,6 @@ public void SendRelayComplete(string id, int millis) { var child = new ProtocolTreeNode("relay", new[] { new KeyValue("elapsed", millis.ToString(CultureInfo.InvariantCulture)) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "result"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -652,7 +390,6 @@ public void SendRelayTimeout(string id) var innerChild = new ProtocolTreeNode("remote-server-timeout", new[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas") }); var child = new ProtocolTreeNode("error", new[] { new KeyValue("code", "504"), new KeyValue("type", "wait") }, innerChild); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "error"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -664,17 +401,6 @@ public void SendRemoveParticipants(string gjid, List participants) public void SendRemoveParticipants(string gjid, List participants, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("remove_group_participants_"); - //this.AddIqHandler(id, new IqResultHandler(delegate(ProtocolTreeNode node, string from) - //{ - // List successList = new List(); - // Dictionary failTable = new Dictionary(); - // this.ReadSuccessAndFailure(node, successList, failTable, "remove"); - // this.GroupEventHandler.OnRemoveGroupParticipants(from, successList, failTable); - // if (onSuccess != null) - // { - // onSuccess(); - // } - //}, onError)); this.SendVerbParticipants(gjid, participants, id, "remove"); } @@ -686,44 +412,20 @@ public void SendSetGroupSubject(string gjid, string subject) public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("set_group_subject_"); - //this.AddIqHandler(id, new IqResultHandler(delegate(ProtocolTreeNode node, string from) - //{ - // this.GroupEventHandler.OnSetSubject(from); - // if (onSuccess != null) - // { - // onSuccess(); - // } - //}, onError)); var child = new ProtocolTreeNode("subject", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("value", subject) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("set_photo_"); - //this.AddIqHandler(id, new IqResultHandler(delegate(ProtocolTreeNode node, string from) - //{ - // if (this.EventHandler != null) - // { - // string attributeValue = null; - // ProtocolTreeNode child = node.GetChild("picture"); - // if (child != null) - // { - // attributeValue = child.GetAttributeValue("id"); - // } - // this.EventHandler.OnPhotoChanged(jid, null, attributeValue); - // } - // onSuccess(); - //}, onError)); var list = new List { new ProtocolTreeNode("picture", new[] { new KeyValue("xmlns", "w:profile:picture") }, null, bytes) }; if (thumbnailBytes != null) { list.Add(new ProtocolTreeNode("picture", new[] { new KeyValue("type", "preview") }, null, thumbnailBytes)); } var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", jid) }, list.ToArray()); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -735,12 +437,10 @@ public void SendSetPrivacyBlockedList(IEnumerable list) public void SendSetPrivacyBlockedList(IEnumerable jidSet, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("privacy_"); - //this.AddIqHandler(id, new IqResultHandler((node, from) => onSuccess(), onError)); ProtocolTreeNode[] nodeArray = Enumerable.Select(jidSet, (Func)((jid, index) => new ProtocolTreeNode("item", new KeyValue[] { new KeyValue("type", "jid"), new KeyValue("value", jid), new KeyValue("action", "deny"), new KeyValue("order", index.ToString(CultureInfo.InvariantCulture)) }))).ToArray(); var child = new ProtocolTreeNode("list", new KeyValue[] { new KeyValue("name", "default") }, (nodeArray.Length == 0) ? null : nodeArray); var node2 = new ProtocolTreeNode("query", new KeyValue[] { new KeyValue("xmlns", "jabber:iq:privacy") }, child); var node3 = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set") }, node2); - //this.whatsNetwork.SendNode(node3); this.whatsNetwork.SendData(this._binWriter.Write(node3)); } @@ -748,16 +448,7 @@ public void SendStatusUpdate(string status, Action onComplete, Action onErr { string id = TicketManager.GenerateId(); FMessage message = new FMessage(new FMessage.Key("s.us", true, id)); - //ErrorHandler handler = new ErrorHandler - //{ - // OnCompleted = onComplete, - // OnError = onError - //}; - //trackedMessages.Add(message.key, handler); - - //var messageNode = GetMessageNode(message, new ProtocolTreeNode("body", null, status)); var messageNode = GetMessageNode(message, new ProtocolTreeNode("body", null, WhatsApp.SYSEncoding.GetBytes(status))); - //this.whatsNetwork.SendNode(messageNode); this.whatsNetwork.SendData(this._binWriter.Write(messageNode)); } @@ -765,21 +456,18 @@ public void SendSubjectReceived(string to, string id) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = GetSubjectMessage(to, id, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendUnsubscribeHim(string jid) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribed"), new KeyValue("to", jid) }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } public void SendUnsubscribeMe(string jid) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribe"), new KeyValue("to", jid) }); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -792,14 +480,12 @@ internal void SendGetGroups(string id, string type) { var child = new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("type", type) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "g.us") }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } + internal void SendMessageWithBody(FMessage message) { - //var child = new ProtocolTreeNode("body", null, message.data); var child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); - //this.whatsNetwork.SendNode(GetMessageNode(message, child)); this.whatsNetwork.SendData(this._binWriter.Write(GetMessageNode(message, child))); } @@ -834,7 +520,6 @@ internal void SendMessageWithMedia(FMessage message) } if ((FMessage.Type.Contact == message.media_wa_type) && (message.media_name != null)) { - //node = new ProtocolTreeNode("media", list.ToArray(), new ProtocolTreeNode("vcard", new KeyValue[] { new KeyValue("name", message.media_name) }, message.data)); node = new ProtocolTreeNode("media", list.ToArray(), new ProtocolTreeNode("vcard", new KeyValue[] { new KeyValue("name", message.media_name) }, WhatsApp.SYSEncoding.GetBytes(message.data))); } else @@ -856,17 +541,17 @@ internal void SendMessageWithMedia(FMessage message) } node = new ProtocolTreeNode("media", list.ToArray(), null, data); } - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } + internal void SendVerbParticipants(string gjid, IEnumerable participants, string id, string inner_tag) { IEnumerable source = from jid in participants select new ProtocolTreeNode("participant", new[] { new KeyValue("jid", jid) }); var child = new ProtocolTreeNode(inner_tag, new[] { new KeyValue("xmlns", "w:g") }, source); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, child); - //this.whatsNetwork.SendNode(node); this.whatsNetwork.SendData(this._binWriter.Write(node)); } + private IEnumerable ProcessGroupSettings(IEnumerable groups) { ProtocolTreeNode[] nodeArray = null; @@ -891,23 +576,11 @@ private void SendReceiptAck(string to, string id, string receiptType) new KeyValue("type", "chat"), new KeyValue("id", id) }, tmpChild); - //this.whatsNetwork.SendNode(resultNode); this.whatsNetwork.SendData(this._binWriter.Write(resultNode)); } internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode) { - //ProtocolTreeNode node = null; - //var serverNode = new ProtocolTreeNode("server", null); - ////if (message.key.remote_jid.Contains('@')) - //// serverNode.data = WhatsApp.SYSEncoding.GetBytes(message.key.remote_jid.Split('@')[1]); - //var xNode = new ProtocolTreeNode("x", new[] { new KeyValue("xmlns", "jabber:x:event") }, serverNode); - //IEnumerable node = (from n in new ProtocolTreeNode[] { xNode, pNode } - // where n != null - // select n).ToArray(); - - //ProtocolTreeNode[] node = new ProtocolTreeNode[] { pNode }; - //return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, node); return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, pNode); } @@ -916,4 +589,4 @@ public static ProtocolTreeNode GetSubjectMessage(string to, string id, ProtocolT return new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "subject"), new KeyValue("id", id) }, child); } } -} +} \ No newline at end of file diff --git a/WhatsAppApi/bin/Debug/WhatsAppApi.dll b/WhatsAppApi/bin/Debug/WhatsAppApi.dll new file mode 100644 index 0000000000000000000000000000000000000000..0f2d7e58df906475366f2ea2c866be6f6423a2cb GIT binary patch literal 70656 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ǰh20&7 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ǰh20&7 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 F{2#I!@wET| literal 0 HcmV?d00001 From 69a389485fbdd52cfe5b6bb5d8f39c57090e1d0d Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 02:53:30 -0800 Subject: [PATCH 018/271] Added simple readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..4525c68 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +WhatsAPINet +=========== + +This is a API written in C# but it can be used in any .NET language. It's a fork from WhatsAPINet, which is based on WhatsAPI. +The goal of this fork is to make it all work again and refacter the code, add documentation and write documentation +on how Whatsapp works. From 381582bca72bd096cc652caa97a2b79d49714cb4 Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 12:01:28 +0100 Subject: [PATCH 019/271] Added protocol docs --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 4525c68..cbcf41a 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,14 @@ WhatsAPINet This is a API written in C# but it can be used in any .NET language. It's a fork from WhatsAPINet, which is based on WhatsAPI. The goal of this fork is to make it all work again and refacter the code, add documentation and write documentation on how Whatsapp works. + +## Protocol +The WhatsApp protocol is sadly not officially documented. While working on this project, I will try to document most of my progress in the docs folder. + +The protocol is based on the XML-based XMPP protocol, a heavily documented internet messaging protocol. To reduce the XML overhead, the XML structure and a lot of common keywords are converted into bytes. See docs/funxmpp.txt for a more detailed explanation. Sadly, the XMPP standard is not followed exactly everywhere. + +Protocol wrapper: FunXMPP (see Wiki) +Main protocol: XMPP (http://xmpp.org/rfcs/rfc6120.html) +Authentification: SASL, digest auth (http://tools.ietf.org/html/rfc2831) +Blocking: XMPP privacy lists? (http://xmpp.org/extensions/xep-0016.html) +Ping (http://xmpp.org/extensions/xep-0199.html) From 17afa743ae9e62b9f88ae42d7925ebf683564789 Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 12:01:54 +0100 Subject: [PATCH 020/271] Fixed markup --- README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cbcf41a..b7d2def 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,17 @@ The goal of this fork is to make it all work again and refacter the code, add do on how Whatsapp works. ## Protocol -The WhatsApp protocol is sadly not officially documented. While working on this project, I will try to document most of my progress in the docs folder. +The WhatsApp protocol is sadly not officially documented. While working on this +project, I will try to document most of my progress in the docs folder. -The protocol is based on the XML-based XMPP protocol, a heavily documented internet messaging protocol. To reduce the XML overhead, the XML structure and a lot of common keywords are converted into bytes. See docs/funxmpp.txt for a more detailed explanation. Sadly, the XMPP standard is not followed exactly everywhere. +The protocol is based on the XML-based XMPP protocol, a heavily documented +internet messaging protocol. To reduce the XML overhead, the XML structure and a +lot of common keywords are converted into bytes. See docs/funxmpp.txt for a +more detailed explanation. Sadly, the XMPP standard is not followed exactly +everywhere. -Protocol wrapper: FunXMPP (see Wiki) -Main protocol: XMPP (http://xmpp.org/rfcs/rfc6120.html) -Authentification: SASL, digest auth (http://tools.ietf.org/html/rfc2831) -Blocking: XMPP privacy lists? (http://xmpp.org/extensions/xep-0016.html) -Ping (http://xmpp.org/extensions/xep-0199.html) +* Protocol wrapper: FunXMPP (see doc/funxmpp.txt) +* Main protocol: XMPP (http://xmpp.org/rfcs/rfc6120.html) +* Authentification: SASL, digest auth (http://tools.ietf.org/html/rfc2831) +* Blocking: XMPP privacy lists? (http://xmpp.org/extensions/xep-0016.html) +* Ping (http://xmpp.org/extensions/xep-0199.html) From a8114307e51522c7fe1002917af96d1ea0684dbb Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 12:02:15 +0100 Subject: [PATCH 021/271] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7d2def..2a667d4 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ lot of common keywords are converted into bytes. See docs/funxmpp.txt for a more detailed explanation. Sadly, the XMPP standard is not followed exactly everywhere. -* Protocol wrapper: FunXMPP (see doc/funxmpp.txt) +* Protocol wrapper: FunXMPP (see Wiki) * Main protocol: XMPP (http://xmpp.org/rfcs/rfc6120.html) * Authentification: SASL, digest auth (http://tools.ietf.org/html/rfc2831) * Blocking: XMPP privacy lists? (http://xmpp.org/extensions/xep-0016.html) From ac472467ef99f5eb2b1ea222e79260b2c29e4350 Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 12:02:32 +0100 Subject: [PATCH 022/271] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 2a667d4..ae5e3eb 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,12 @@ everywhere. * Authentification: SASL, digest auth (http://tools.ietf.org/html/rfc2831) * Blocking: XMPP privacy lists? (http://xmpp.org/extensions/xep-0016.html) * Ping (http://xmpp.org/extensions/xep-0199.html) + +### Encryption + +Following the news that WhatsApp messages were sent in plaintext (and thus +readable for everyone when on a WiFi hotspot), encryption was added. At least +the Android client uses this encryption, which seems to be TLS as specified by +the XMPP RFC. However, I did not really look into this. Also, the mapping of +keywords to bytes seems to have also changed in the latest version of the +Android app. From 490ace544a18b19fc092771fa2f72e5ac7189caf Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 12:04:34 +0100 Subject: [PATCH 023/271] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ae5e3eb..687baf5 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ This is a API written in C# but it can be used in any .NET language. It's a fork The goal of this fork is to make it all work again and refacter the code, add documentation and write documentation on how Whatsapp works. +## Documentation +* FunXMPP Protocol - https://github.com/TheKirk/WhatsAPINet/wiki/FunXMPP + ## Protocol The WhatsApp protocol is sadly not officially documented. While working on this project, I will try to document most of my progress in the docs folder. From bd4d9c0ec67c8dec978372370aec6f581b124f30 Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 14:04:53 +0100 Subject: [PATCH 024/271] Added documentation for WhatsSendHandler.cs --- WhatsAppApi/WhatsSendHandler.cs | 355 ++++++++++++++++++++++++++++++-- WhatsAppApi_vs2010.suo | Bin 45568 -> 53760 bytes 2 files changed, 339 insertions(+), 16 deletions(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 3d959e3..36992d7 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -9,30 +9,68 @@ namespace WhatsAppApi { + /// + /// Handles sending messages to the Whatsapp servers + /// public class WhatsSendHandler { + /// + /// Holds the jabber id that is used to authenticate + /// private string MyJID = ""; - private string whatsAppRealm = "s.whatsapp.net"; + + /// + /// The whatsapp realm, defined in WhatsConstants. + /// + private string whatsAppRealm = WhatsAppApi.Settings.WhatsConstants.WhatsAppRealm; + + /// + /// Holds an instance of the BinTreeNodeWriter + /// private BinTreeNodeWriter _binWriter; + + /// + /// Holds an instance of the WhatsNetwork class + /// private WhatsNetwork whatsNetwork; + /// + /// Default class constructor + /// + /// An instance of the WhatsNetwork class + /// An instance of the BinTreeNodeWriter internal WhatsSendHandler(WhatsNetwork net, BinTreeNodeWriter writer) { this.whatsNetwork = net; this._binWriter = writer; } + /// + /// Sends a message to the Whatsapp server to tell that the user is 'online' / 'active' + /// public void SendActive() { var node = new ProtocolTreeNode("presence", new[] {new KeyValue("type", "active")}); this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Sends a request to the server to add participants to a group chat + /// + /// Group jabber id + /// A list of participants (List of jids) public void SendAddParticipants(string gjid, IEnumerable participants) { this.SendAddParticipants(gjid, participants, null, null); - } - + } + + /// + /// Sends a request to the server to add participants to a group chat + /// + /// Group jabber id + /// A list of participants (List of jids) + /// The action to be executed when the request is successfull. + /// The action to be executed when the request fails public void SendAddParticipants(string gjid, IEnumerable participants, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("add_group_participants_"); @@ -64,6 +102,12 @@ public void SendClearDirty(string category) this.SendClearDirty(new string[] { category }); } + /// + /// Sends the client configuration to the Whatsapp server. + /// + /// The string identifying the client. + /// ? + /// ? public void SendClientConfig(string platform, string lg, string lc) { string v = TicketCounter.MakeId("config_"); @@ -72,6 +116,19 @@ public void SendClientConfig(string platform, string lg, string lc) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Sends the client configuration to the Whatsapp server. + /// + /// The string identifying the client. + /// ? + /// ? + /// ? + /// ? + /// Default settings. + /// Settings regarding groups. + /// A list of groups + /// Action to be executed when the request was successfull. + /// Action to be executed when the request failed. public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, bool preview, bool defaultSetting, bool groupsSetting, IEnumerable groups, Action onCompleted, Action onError) { string id = TicketCounter.MakeId("config_"); @@ -101,12 +158,19 @@ public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Change status to 'Offline' + /// public void SendClose() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Send the status of 'writing'/'typing'/'composing' to the Whatsapp server + /// + /// The recipient, the one the client is talking to. public void SendComposing(string to) { var child = new ProtocolTreeNode("composing", new[] { new KeyValue("xmlns", "http://jabber.org/protocol/chatstates") }); @@ -114,11 +178,21 @@ public void SendComposing(string to) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Requests to create a new and empty group chat. + /// + /// The subject of the group chat. public void SendCreateGroupChat(string subject) { this.SendCreateGroupChat(subject, null, null); } + /// + /// Requests to create a new and empty group chat. + /// + /// The subjecct of the group chat. + /// Acction to be executed when the request was successful. + /// Action to be executed when the request failed. public void SendCreateGroupChat(string subject, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("create_group_"); @@ -127,6 +201,11 @@ public void SendCreateGroupChat(string subject, Action onSuccess, Action this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Make a reques to the Whatsapp servers to delete a specific account. + /// + /// The action to be executed when the request was successful. + /// The action to be executed when the request failed. public void SendDeleteAccount(Action onSuccess, Action onError) { string id = TicketCounter.MakeId("del_acct_"); @@ -147,6 +226,10 @@ public void SendDeleteAccount(Action onSuccess, Action onError) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Request to be delete from a group chat. + /// + /// public void SendDeleteFromRoster(string jid) { string v = TicketCounter.MakeId("roster_"); @@ -156,16 +239,31 @@ public void SendDeleteFromRoster(string jid) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Sends the 'Delivered' status to a specific client + /// + /// The JID of the person the chat is with. + /// The message id. public void SendDeliveredReceiptAck(string to, string id) { this.SendReceiptAck(to, id, "delivered"); } + /// + /// Sends a request to end and remove the group chat. + /// + /// The group jabber id. public void SendEndGroupChat(string gjid) { this.SendEndGroupChat(gjid, null, null); - } - + } + + /// + /// Sends a request to end and remove the group chat. + /// + /// The group jabber id. + /// The action to be executed when the request was successful. + /// The action to be executed when the request failed. public void SendEndGroupChat(string gjid, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("remove_group_"); @@ -174,6 +272,9 @@ public void SendEndGroupChat(string gjid, Action onSuccess, Action onError) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Request the client confguration + /// public void SendGetClientConfig() { string id = TicketCounter.MakeId("get_config_"); @@ -182,6 +283,9 @@ public void SendGetClientConfig() this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Request dirty + /// public void SendGetDirty() { string id = TicketCounter.MakeId("get_dirty_"); @@ -190,6 +294,10 @@ public void SendGetDirty() this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Send a request to retrieve group information + /// + /// The group jabber id public void SendGetGroupInfo(string gjid) { string id = TicketCounter.MakeId("get_g_info_"); @@ -198,18 +306,30 @@ public void SendGetGroupInfo(string gjid) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Make a request to retrieve all groups where the client is participating in + /// + /// The action to be executed when the request was successful. + /// The action to be executed when the request failed. public void SendGetGroups(Action onSuccess, Action onError) { string id = TicketCounter.MakeId("get_groups_"); this.SendGetGroups(id, "participating"); } + /// + /// Make a request to retrieve all groups where the client is the owner of. + /// public void SendGetOwningGroups() { string id = TicketCounter.MakeId("get_owning_groups_"); this.SendGetGroups(id, "owning"); } + /// + /// Make a request to retrieve all group participents + /// + /// The group jabber id public void SendGetParticipants(string gjid) { string id = TicketCounter.MakeId("get_participants_"); @@ -218,11 +338,23 @@ public void SendGetParticipants(string gjid) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Make a request to retrieve a photo + /// + /// The group jabber id. + /// If set to true, the photo will be retrieved in the highest resolution. public void SendGetPhoto(string jid, bool largeFormat) { this.SendGetPhoto(jid, null, largeFormat, delegate { }); } + /// + /// Make a request to retrieve a photo for a specific photo id. + /// + /// The group jabber id. + /// The specific photo that needs to be retrieved. + /// If set to true, the photo will be retrieved in the highest resolution. + /// The action to be executed when the request was successful. public void SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, Action onComplete) { string id = TicketCounter.MakeId("get_photo_"); @@ -240,6 +372,10 @@ public void SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, A this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Make a request to retrieve a list of photo id's + /// + /// The list of jabber id's the photos need to be retrieved of. public void SendGetPhotoIds(IEnumerable jids) { string id = TicketCounter.MakeId("get_photo_id_"); @@ -249,6 +385,9 @@ public void SendGetPhotoIds(IEnumerable jids) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Make a request to retrieve a list of privacy's + /// public void SendGetPrivacyList() { string id = TicketCounter.MakeId("privacylist_"); @@ -258,6 +397,9 @@ public void SendGetPrivacyList() this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Make a request to retrieve information (properties) about the server. + /// public void SendGetServerProperties() { string id = TicketCounter.MakeId("get_server_properties_"); @@ -266,6 +408,10 @@ public void SendGetServerProperties() this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Make a request to retrieve the status for specific jabber id. + /// + /// The jabber id the the status should be retrieved from. public void SendGetStatus(string jid) { int index = jid.IndexOf('@'); @@ -279,22 +425,41 @@ public void SendGetStatus(string jid) } } + /// + /// Make a request to change our status to inactive. + /// public void SendInactive() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "inactive") }); this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Make a request to leave a group chat + /// + /// The group jabber id public void SendLeaveGroup(string gjid) { this.SendLeaveGroup(gjid, null, null); } + /// + /// Make a rquest to leave a group chat + /// + /// The group jabber id. + /// The action to be executed when the request was successful. + /// The action to be executed when the request failed. public void SendLeaveGroup(string gjid, Action onSuccess, Action onError) { this.SendLeaveGroups(new string[] { gjid }, onSuccess, onError); } + /// + /// Make a request to leave multiple groups at the same time. + /// + /// The group jabber id. + /// The action to be executed when the request was successful. + /// The action to be executed when the request failed. public void SendLeaveGroups(IEnumerable gjids, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("leave_group_"); @@ -304,8 +469,12 @@ public void SendLeaveGroups(IEnumerable gjids, Action onSuccess, Action< this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Sends a message, message properties are defined in the instance of FMessage. + /// + /// An instance of the FMessage class. public void SendMessage(FMessage message) - { + { if (message.media_wa_type != FMessage.Type.Undefined) { this.SendMessageWithMedia(message); @@ -316,6 +485,10 @@ public void SendMessage(FMessage message) } } + /// + /// Tell the server the message has been recieved. + /// + /// An instance of the FMessage class. public void SendMessageReceived(FMessage message) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); @@ -323,11 +496,19 @@ public void SendMessageReceived(FMessage message) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Send a null byte to the server + /// public void SendNop() { this.whatsNetwork.SendData(this._binWriter.Write(null)); } + /// + /// Send a notification to a specific user that the message has been recieved + /// + /// The jabber id + /// The id of the message public void SendNotificationReceived(string jid, string id) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); @@ -335,6 +516,10 @@ public void SendNotificationReceived(string jid, string id) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Send pause + /// + /// The jabber id of the reciever public void SendPaused(string to) { var child = new ProtocolTreeNode("paused", new[] { new KeyValue("xmlns", "http://jabber.org/protocol/chatstates") }); @@ -342,6 +527,9 @@ public void SendPaused(string to) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Send a ping to the server + /// public void SendPing() { string id = TicketCounter.MakeId("ping_"); @@ -350,18 +538,30 @@ public void SendPing() this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Send a pong to a specific user + /// + /// public void SendPong(string id) { var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "result"), new KeyValue("to", this.whatsAppRealm), new KeyValue("id", id) }); this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Send a subscription request + /// + /// public void SendPresenceSubscriptionRequest(string to) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "subscribe"), new KeyValue("to", to) }); this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Request to retrieve the LastOnline string + /// + /// The jabber id public void SendQueryLastOnline(string jid) { string id = TicketCounter.MakeId("last_"); @@ -370,6 +570,11 @@ public void SendQueryLastOnline(string jid) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Tell the server wether the platform is replay capable + /// + /// The platform + /// Capable or not public void SendRelayCapable(string platform, bool value) { string v = TicketCounter.MakeId("relay_"); @@ -378,6 +583,11 @@ public void SendRelayCapable(string platform, bool value) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Tell the server the relay was complete + /// + /// The id + /// Miliseconds it took to relay the message public void SendRelayComplete(string id, int millis) { var child = new ProtocolTreeNode("relay", new[] { new KeyValue("elapsed", millis.ToString(CultureInfo.InvariantCulture)) }); @@ -385,30 +595,58 @@ public void SendRelayComplete(string id, int millis) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Send a relay timeout + /// + /// The id public void SendRelayTimeout(string id) { var innerChild = new ProtocolTreeNode("remote-server-timeout", new[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas") }); var child = new ProtocolTreeNode("error", new[] { new KeyValue("code", "504"), new KeyValue("type", "wait") }, innerChild); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "error"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); this.whatsNetwork.SendData(this._binWriter.Write(node)); - } - + } + + /// + /// Request to remove participants from a group + /// + /// The group jabber id + /// A list of participants public void SendRemoveParticipants(string gjid, List participants) { this.SendRemoveParticipants(gjid, participants, null, null); - } - + } + + /// + /// Request to remove participants from a group + /// + /// The group jabber id + /// A list of participants + /// Action to execute when the request was successful + /// Action to execute when the request failed public void SendRemoveParticipants(string gjid, List participants, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("remove_group_participants_"); this.SendVerbParticipants(gjid, participants, id, "remove"); } + /// + /// Request to set the group subject + /// + /// The group jabber id + /// The new group subject public void SendSetGroupSubject(string gjid, string subject) { this.SendSetGroupSubject(gjid, subject, null, null); - } - + } + + /// + /// Request to set the group subject + /// + /// The group jabber id + /// The new group subject + /// Action to execute when the request was successful + /// Action to execute when the request failed public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("set_group_subject_"); @@ -417,6 +655,14 @@ public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, A this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Change the profile picture + /// + /// The user jabber id + /// The ammount of bytes needed for the photo + /// The amount of bytes needed for the thumbanil + /// Action to execute when the request was successful + /// Action to execute when the request failed public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("set_photo_"); @@ -429,11 +675,21 @@ public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes, Action this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Set the list of people that have been blocked + /// + /// The list of people that have been blocked. public void SendSetPrivacyBlockedList(IEnumerable list) { this.SendSetPrivacyBlockedList(list, null, null); - } - + } + + /// + /// Set the list of people that have been blocked + /// + /// List of jabber id's + /// Action to execute when the request was successful + /// Action to execute when the request failed public void SendSetPrivacyBlockedList(IEnumerable jidSet, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("privacy_"); @@ -444,6 +700,12 @@ public void SendSetPrivacyBlockedList(IEnumerable jidSet, Action onSucce this.whatsNetwork.SendData(this._binWriter.Write(node3)); } + /// + /// Send a status update + /// + /// The status + /// Action to execute when the request was successful + /// Action to execute when the request failed public void SendStatusUpdate(string status, Action onComplete, Action onError) { string id = TicketManager.GenerateId(); @@ -452,6 +714,11 @@ public void SendStatusUpdate(string status, Action onComplete, Action onErr this.whatsNetwork.SendData(this._binWriter.Write(messageNode)); } + /// + /// Tell the server the new subject has been recieved + /// + /// The recipient + /// The id public void SendSubjectReceived(string to, string id) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); @@ -459,23 +726,41 @@ public void SendSubjectReceived(string to, string id) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Unsubscibe him + /// + /// The jabber id public void SendUnsubscribeHim(string jid) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribed"), new KeyValue("to", jid) }); this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Unsubscribe me + /// + /// The jabber id public void SendUnsubscribeMe(string jid) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribe"), new KeyValue("to", jid) }); this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Tell the server the 'visible' status has been acknowledged + /// + /// Recipient + /// The id public void SendVisibleReceiptAck(string to, string id) { this.SendReceiptAck(to, id, "visible"); } + /// + /// Request to retrieve all groups + /// + /// The id + /// The type internal void SendGetGroups(string id, string type) { var child = new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("type", type) }); @@ -483,12 +768,20 @@ internal void SendGetGroups(string id, string type) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Send a message with a body (Plain text); + /// + /// An instance of the FMessage class. internal void SendMessageWithBody(FMessage message) { var child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); this.whatsNetwork.SendData(this._binWriter.Write(GetMessageNode(message, child))); } + /// + /// Send a message with media (photo/sound/movie) + /// + /// An instance of the FMessage class. internal void SendMessageWithMedia(FMessage message) { ProtocolTreeNode node; @@ -544,6 +837,13 @@ internal void SendMessageWithMedia(FMessage message) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Send a verb of group participants + /// + /// The group jabber id + /// List of participants + /// The id + /// Inner tag internal void SendVerbParticipants(string gjid, IEnumerable participants, string id, string inner_tag) { IEnumerable source = from jid in participants select new ProtocolTreeNode("participant", new[] { new KeyValue("jid", jid) }); @@ -552,6 +852,11 @@ internal void SendVerbParticipants(string gjid, IEnumerable participants this.whatsNetwork.SendData(this._binWriter.Write(node)); } + /// + /// Processes group settings + /// + /// A list of instances of the GroupSetting class. + /// A list of ProtocolTreeNodes private IEnumerable ProcessGroupSettings(IEnumerable groups) { ProtocolTreeNode[] nodeArray = null; @@ -567,6 +872,12 @@ private IEnumerable ProcessGroupSettings(IEnumerable + /// Tell the server the reciepient has been acknowledged + /// + /// The reciepient + /// The id + /// The receipt type private void SendReceiptAck(string to, string id, string receiptType) { var tmpChild = new ProtocolTreeNode("ack", new[] { new KeyValue("xmlns", "urn:xmpp:receipts"), new KeyValue("type", receiptType) }); @@ -579,11 +890,23 @@ private void SendReceiptAck(string to, string id, string receiptType) this.whatsNetwork.SendData(this._binWriter.Write(resultNode)); } + /// + /// Get the message node + /// + /// the message + /// The protocol tree node + /// An instance of the ProtocolTreeNode class. internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode) { return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, pNode); - } - + } + + /// + /// Get the message node + /// + /// the message + /// The protocol tree node + /// An instance of the ProtocolTreeNode class. public static ProtocolTreeNode GetSubjectMessage(string to, string id, ProtocolTreeNode child) { return new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "subject"), new KeyValue("id", id) }, child); diff --git a/WhatsAppApi_vs2010.suo b/WhatsAppApi_vs2010.suo index 8e52edab73d925dae515fba245163ab46b3161e4..9b46c04acd4afd8a74fd3b8dfcc7d7e227fa60ee 100644 GIT binary patch delta 2429 zcmZuy2~1R16#XCm;K{s9>cA#u}(4#v0p1 zo(r{D*+h$?Ebc3A6mc7<)mj%&K~c~UgDEjldj9_p{90dfa_>2F-*WH2?@d;-RaUWe zdAk)ionaW#VYO^b5}X;pOkfs}49o`R0P}#kz^Ov)Po=@WIvEJ zTZ>u?!S(}DHAy`Lf<`7*b*d7rlzN#$((DqI0WzVg)6Sw$dqNrTf$DHWqY+Q)B$G*} zd%T7TgkmLAtI~8gmC6vALedQJ8kVI>PeN6l21~43U`AQ2A_b(BWTbnuM&jnLQ}f_K z@_=7KPKW_R#Q+!ZU|E3bH;}&p3{nvhL0;-@NTk`u-oM;q*-x&<7Y=cEUY8keepxlu zJis|K4`Dk2;nVDb%?Ao7FM=%wc2m9wb}#iyVfO+1DL(*vkopy{l|U8chhdLU{}}9X z>YrqoOy(5GX)2tBt)~8Y*bBf#%4=Z%0BR|(gS|riYp~a;Z-K1`8YsUB`xo_Z!wRLj zL-}21Jj2`rxle^g*e2j1<&R;TffmZ2!nRWXIcyuyF7V9>@s+vNPV5$PE^stS^$8+D zW?Ql$+}Y31wb0joor9l;lFl7VCWadMj2T%YJgzm^4t$>Q ztg`URQ;tkC>q!&WkIfU~wE9t`HB8F~^l49AuyHtBvFOR!*)d+jY{j^_N*{JZPaHd$ zWTv}W8yORE<6KC!ok>xM{mu(r1T{obRh(J})dmpGCR{p23CIB&hJK`ze}`I2d-P#| zE2+j*4`}YPsorHE!p9ay6k}on@EWXQwyI8^^>4{|JFF?rv~Pt9tvSu?$rkpUlq@uQ z@vC5Z-4oSdCR|dzIJFY08=Ws^eF&tShK2P`J__{|RfkC`^7eu1C7Y}{0V4FC zFpl)*<$G{AKQ%n$aZ6(GcV6MygQ{D#wLiYW=c;KkPblrQUXD zs^#Jtm%H1N8>F3^I45=ojfo^#>00YxCbT!*GgzUTri+(G`1GiiA5?F;pIEDNAaykC zeS2M!sqfp%AF8ixvg$I3unz_qS?1yy_CrMa&XVz^5qrBHtWBDh-UmN?oLMog#bVyl zm2PhF>t8pkz)jaSF7TkESWm4|%l5=1lDaYdc>YDPDeR~vr1ktaZT#x_o8HIeifg3? zOd{q{w%)I+_xmOncwAU_+@{qvDK_fUb(Jb+F&fDTlbWalhIppl`hM!bqLv?Ddwrb3IU))Km@%xK(6_H|%*(5f332)I} zsnR}6`L`8$V^?-;>dJ|BiV4@J?NZUN=FCd{n(8C=B8w}g(5YRqem_9iTdc-EAlJpb z(W-_iiU;5gB1iZ6PfY!XTQcKtA^0$RnFdzFzJ(SXWT0 zbG1khrf?agXDDmrLVkypp(G(?drDH@++7>8i}omJvE12BG!f~6Z;x=`n>IXMQ+qqU zBc*I;b7QEE)gac5l|Hz|Jf9C-vz&dSNpG&7SH~|sIp>&grS+tvyji$%ES@2p`WX&a SLlTs+Bs$|1nX8P39q}I$t)0yP delta 315 zcmZoz!rbtLX~P>PmVf{M|DXJUDTndj#K6UqSaSF#Z12jS_x~&-GXn#|0kz3hES{W< zKsEm*f&9sKn{_7tVDaG)0&+wl0veNeSvFU&o?+o&1PZW11t*tqUEtsZa=73E+yNZ2 zKn^cdVDlI55GEEjxHMnLWRER6o4@c~VCCQdii*M2iJa%zQZPj& z%2zl*`X`s@_)k8}Vz~K>&INA{L7+4fR0(T7kg;lVAcyzlBNz5e-ehex`JE=)WQQs? z!PLV?t1rKa`Ox=n!hw5RC;XF{tiM@vb3@e|>5U;%7&py0ATh~6VzTi0x04e%tO4n< BZgv0w From 562a3111cc05978a4277d6f879eb58308d5cfc0c Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 14:11:55 +0100 Subject: [PATCH 025/271] Added documentation for WhatsNetwork.cs Also translated the german exception message --- WhatsAppApi/WhatsNetwork.cs | 296 ++++++++++++++++++++++-------------- 1 file changed, 186 insertions(+), 110 deletions(-) diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index 4afbf0d..8a96d84 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -1,111 +1,187 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Sockets; -using System.Text; -using WhatsAppApi.Helper; - -namespace WhatsAppApi -{ - public class WhatsNetwork - { - private readonly int recvTimeout; - private readonly string whatsHost; - private readonly int whatsPort; - private List incomplete_message = new List(); - private Socket socket; - - public WhatsNetwork(string whatsHost, int port, int timeout = 2000) - { - this.recvTimeout = timeout; - this.whatsHost = whatsHost; - this.whatsPort = port; - this.incomplete_message = new List(); - } - - public void Connect() - { - this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using WhatsAppApi.Helper; + +namespace WhatsAppApi +{ + /// + /// Contains methods to connect to the Whatsapp network + /// + public class WhatsNetwork + { + /// + /// The time between sending and recieving + /// + private readonly int recvTimeout; + + /// + /// The hostname of the whatsapp server + /// + private readonly string whatsHost; + + /// + /// The port of the whatsapp server + /// + private readonly int whatsPort; + + /// + /// A list of bytes for incomplete messages + /// + private List incomplete_message = new List(); + + /// + /// A socket to connect to the whatsapp network + /// + private Socket socket; + + /// + /// Default class constructor + /// + /// The hostname of the whatsapp server + /// The port of the whatsapp server + /// Timeout for the connection + public WhatsNetwork(string whatsHost, int port, int timeout = 2000) + { + this.recvTimeout = timeout; + this.whatsHost = whatsHost; + this.whatsPort = port; + this.incomplete_message = new List(); + } + + /// + /// Default class constructor + /// + /// Timeout for the connection + public WhatsNetwork(int timeout = 2000) + { + this.recvTimeout = timeout; + this.whatsHost = Settings.WhatsConstants.WhatsAppHost; + this.whatsPort = Settings.WhatsConstants.WhatsPort; + this.incomplete_message = new List(); + } + + /// + /// Connect to the whatsapp server + /// + public void Connect() + { + this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.socket.Connect(this.whatsHost, this.whatsPort); - this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.recvTimeout); - - if (!this.socket.Connected) - throw new ConnectionException("Cannot connect"); - } - - public void Disconenct() - { - if (this.socket.Connected) - this.socket.Disconnect(true); - } - - public byte[] ReadData() - { - List buff = new List(); - byte[] ret = Socket_read(1024); - return ret; - } - - public void SendData(string data) - { - Socket_send(data, data.Length, 0); - } - public void SendData(byte[] data) - { - Socket_send(data); - } - - private byte[] Socket_read(int length) - { - if (!socket.Connected) - { - throw new ConnectionException(); - } - - var buff = new byte[length]; - int receiveLength = 0; - do - { - try - { - receiveLength = socket.Receive(buff, 0, length, 0); - } - catch (SocketException excpt) - { - if (excpt.SocketErrorCode == SocketError.TimedOut) - { - Console.WriteLine("Socket-Timout"); - return null; - } - else - { - Console.WriteLine("Unbehandelter Fehler bei Sockerread: {0}", excpt); - throw new ConnectionException("error", excpt); - } - } - } - while (receiveLength <= 0); - - byte[] tmpRet = new byte[receiveLength]; - if (receiveLength > 0) - Buffer.BlockCopy(buff, 0, tmpRet, 0, receiveLength); - return tmpRet; - } - - private void Socket_send(string data, int length, int flags) - { - var tmpBytes = WhatsApp.SYSEncoding.GetBytes(data); - this.socket.Send(tmpBytes); - } - - private void Socket_send(byte[] data) - { - this.socket.Send(data); - } - - public bool SocketStatus - { - get { return socket.Connected; } - } - } -} + this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.recvTimeout); + + if (!this.socket.Connected) + throw new ConnectionException("Cannot connect"); + } + + /// + /// Disconnect from the whatsapp server + /// + public void Disconenct() + { + if (this.socket.Connected) + this.socket.Disconnect(true); + } + + /// + /// Read 1024 bytes + /// + /// + public byte[] ReadData() + { + List buff = new List(); + byte[] ret = Socket_read(1024); + return ret; + } + + /// + /// Send data to the whatsapp server + /// + /// Data to be send as a string + public void SendData(string data) + { + Socket_send(data, data.Length, 0); + } + + /// + /// Send data to the whatsapp server + /// + /// Data to be send as a byte array + public void SendData(byte[] data) + { + Socket_send(data); + } + + /// + /// Read in a message with a specific length + /// + /// The lengh of the message + /// The recieved data as a byte array + private byte[] Socket_read(int length) + { + if (!socket.Connected) + { + throw new ConnectionException(); + } + + var buff = new byte[length]; + int receiveLength = 0; + do + { + try + { + receiveLength = socket.Receive(buff, 0, length, 0); + } + catch (SocketException excpt) + { + if (excpt.SocketErrorCode == SocketError.TimedOut) + { + Console.WriteLine("Socket-Timout"); + return null; + } + else + { + throw new ConnectionException("Unknown error occured", excpt); + } + } + } + while (receiveLength <= 0); + + byte[] tmpRet = new byte[receiveLength]; + if (receiveLength > 0) + Buffer.BlockCopy(buff, 0, tmpRet, 0, receiveLength); + return tmpRet; + } + + /// + /// Sends data of a specific length to the server + /// + /// The data that needs to be send + /// The lenght of the data + /// Optional flags + private void Socket_send(string data, int length, int flags) + { + var tmpBytes = WhatsApp.SYSEncoding.GetBytes(data); + this.socket.Send(tmpBytes); + } + + /// + /// Send data to the server + /// + /// The data that needs to be send as a byte array + private void Socket_send(byte[] data) + { + this.socket.Send(data); + } + + /// + /// Returns the socket status. + /// + public bool SocketStatus + { + get { return socket.Connected; } + } + } +} From 7d523d8da90bec8c95a4b46c5da126b26248d73e Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 14:28:50 +0100 Subject: [PATCH 026/271] Added documentation to WhatsApp.cs --- WhatsAppApi/WhatsApp.cs | 182 ++++++++++++++++++++++++++ WhatsAppApi/bin/Debug/WhatsAppApi.dll | Bin 70656 -> 70656 bytes WhatsAppApi/bin/Debug/WhatsAppApi.pdb | Bin 185856 -> 185856 bytes WhatsAppApi/obj/Debug/WhatsAppApi.dll | Bin 70656 -> 70656 bytes WhatsAppApi/obj/Debug/WhatsAppApi.pdb | Bin 185856 -> 185856 bytes 5 files changed, 182 insertions(+) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 7a3597a..ad754c9 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -12,35 +12,118 @@ namespace WhatsAppApi { + /// + /// Main api interface + /// public class WhatsApp { + + /// + /// Describes the connection status with the whatsapp server + /// public enum CONNECTION_STATUS { DISCONNECTED, CONNECTED } + /// + /// An instance of the AccountInfo class + /// private AccountInfo accountinfo; + + /// + /// Determines wether debug mode is on or offf + /// public static bool DEBUG; + + /// + /// The imei/mac adress + /// private string imei; + + /// + /// Holds the login status + /// private CONNECTION_STATUS loginStatus; + + /// + /// A lock for a message + /// private object messageLock = new object(); + + /// + /// Que for recieved messages + /// private List messageQueue; + + /// + /// The name of the user + /// private string name; + + /// + /// The phonenumber + /// private string phoneNumber; + + /// + /// An instance of the BinaryTreeNodeReader class + /// private BinTreeNodeReader reader; + + /// + /// An instance of the BinaryTreeNodeWriter class + /// private BinTreeNodeWriter writer; + + /// + /// The timeout for the connection with the Whatsapp servers + /// private int timeout = 5000; + /// + /// An instance of the WhatsNetwork class + /// private WhatsNetwork whatsNetwork; + + /// + /// An instance of the WhatsSendHandler class + /// public WhatsSendHandler WhatsSendHandler { get; private set; } + + /// + /// An instance of the WhatsParser class + /// public WhatsParser WhatsParser { get; private set; } + /// + /// Holds the encoding we use, default is UTF8 + /// public static readonly Encoding SYSEncoding = Encoding.UTF8; + + /// + /// Empty bytes to hold the encryption key + /// private byte[] _encryptionKey; + + /// + /// Empty bytes to hold the challenge + /// private byte[] _challengeBytes; + + /// + /// A list of exceptions for incomplete bytes + /// private List _incompleteBytes; + /// + /// Default class constructor + /// + /// The phone number + /// The imei / mac + /// User nickname + /// Debug on or off, false by default public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) { this.messageQueue = new List(); @@ -60,6 +143,10 @@ public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) _incompleteBytes = new List(); } + /// + /// Add a message to the message que + /// + /// An instance of the ProtocolTreeNode class public void AddMessage(ProtocolTreeNode node) { lock (messageLock) @@ -69,34 +156,56 @@ public void AddMessage(ProtocolTreeNode node) } + /// + /// Connect to the whatsapp network + /// public void Connect() { this.whatsNetwork.Connect(); } + + /// + /// Disconnect from the whatsapp network + /// public void Disconnect() { this.whatsNetwork.Connect(); this.loginStatus = CONNECTION_STATUS.DISCONNECTED; } + /// + /// Encrypt the password (hash) + /// + /// public string encryptPassword() { + // iPhone if (this.imei.Contains(":")) { this.imei = this.imei.ToUpper(); return md5(this.imei + this.imei); } + + // Other else { return md5(new string(this.imei.Reverse().ToArray())); } } + /// + /// Get the account information + /// + /// An instance of the AccountInfo class public AccountInfo GetAccountInfo() { return this.accountinfo; } + /// + /// Retrieve all messages + /// + /// An array of instances of the ProtocolTreeNode class. public ProtocolTreeNode[] GetAllMessages() { ProtocolTreeNode[] tmpReturn = null; @@ -108,6 +217,10 @@ public ProtocolTreeNode[] GetAllMessages() return tmpReturn; } + /// + /// Checks wether we have messages to retrieve + /// + /// true or false public bool HasMessages() { if (this.messageQueue == null) @@ -115,6 +228,9 @@ public bool HasMessages() return this.messageQueue.Count > 0; } + /// + /// Logs us in to the server + /// public void Login() { string resource = string.Format(@"{0}-{1}-{2}", @@ -139,12 +255,26 @@ public void Login() while ((cnt++ < 100) && (this.loginStatus == CONNECTION_STATUS.DISCONNECTED)); } + /// + /// Send a message to a person + /// + /// The phone number to send + /// The text that needs to be send public void Message(string to, string txt) { var tmpMessage = new FMessage(to, true) { key = { id = TicketManager.GenerateId() }, data = txt }; this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage); } + /// + /// Send an image to a person + /// + /// The id of the message + /// the reciepient + /// The url to the image + /// Filename + /// The size of the image in string format + /// Icon public void MessageImage(string msgid, string to, string url, string file, string size, string icon) { //var mediaAttribs = new KeyValue[] @@ -160,26 +290,45 @@ public void MessageImage(string msgid, string to, string url, string file, strin //this.SendMessageNode(msgid, to, mediaNode); } + /// + /// Retrieve messages from the server + /// public void PollMessages() { this.processInboundData(this.whatsNetwork.ReadData()); } + /// + /// Send a pong to the whatsapp server + /// + /// Message id public void Pong(string msgid) { this.WhatsParser.WhatsSendHandler.SendPong(msgid); } + /// + /// Request last seen + /// + /// Jabber id public void RequestLastSeen(string jid) { this.WhatsParser.WhatsSendHandler.SendQueryLastOnline(jid); } + /// + /// Send nickname + /// + /// The nickname public void sendNickname(string nickname) { this.WhatsParser.WhatsSendHandler.SendAvailableForChat(nickname); } + /// + /// Add the authenication nodes + /// + /// An instance of the ProtocolTreeNode class protected ProtocolTreeNode addAuth() { var node = new ProtocolTreeNode("auth", @@ -189,6 +338,10 @@ protected ProtocolTreeNode addAuth() return node; } + /// + /// Add the auth response to protocoltreenode + /// + /// An instance of the ProtocolTreeNode protected ProtocolTreeNode addAuthResponse() { while (this._challengeBytes == null) @@ -217,6 +370,10 @@ protected ProtocolTreeNode addAuthResponse() return node; } + /// + /// Add stream features + /// + /// protected ProtocolTreeNode addFeatures() { var child = new ProtocolTreeNode("receipt_acks", null); @@ -226,6 +383,10 @@ protected ProtocolTreeNode addFeatures() return parent; } + /// + /// Print a message to the debug console + /// + /// The message protected void DebugPrint(string debugMsg) { if (WhatsApp.DEBUG && debugMsg.Length > 0) @@ -234,11 +395,19 @@ protected void DebugPrint(string debugMsg) } } + /// + /// Process the challenge + /// + /// The node that contains the challenge protected void processChallenge(ProtocolTreeNode node) { _challengeBytes = node.data; } + /// + /// Process inbound data + /// + /// Data to process protected void processInboundData(byte[] data) { try @@ -302,6 +471,10 @@ protected void processInboundData(byte[] data) } } + /// + /// Tell the server we recieved the message + /// + /// The ProtocolTreeNode that contains the message protected void sendMessageReceived(ProtocolTreeNode msg) { ProtocolTreeNode requestNode = msg.GetChild("request"); @@ -313,6 +486,11 @@ protected void sendMessageReceived(ProtocolTreeNode msg) this.WhatsParser.WhatsSendHandler.SendMessageReceived(tmpMessage); } + /// + /// MD5 hashes the password + /// + /// String the needs to be hashed + /// A md5 hash private string md5(string pass) { MD5 md5 = MD5.Create(); @@ -323,6 +501,10 @@ private string md5(string pass) return sb.ToString(); } + /// + /// Prints debug + /// + /// message private void PrintInfo(string p) { this.DebugPrint(p); diff --git a/WhatsAppApi/bin/Debug/WhatsAppApi.dll b/WhatsAppApi/bin/Debug/WhatsAppApi.dll index 0f2d7e58df906475366f2ea2c866be6f6423a2cb..16cd000b3c97fbbdca02d78e381206537c90174c 100644 GIT binary patch delta 12570 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 delta 12543 zcmai*33wF6*0Aez_w-CAvt(v6lRcTNbT$$QkOT-OtO3EWlduI@1Z3&Z2@{=(IJuUz-<;v+5>{!=wQ)9B;#|2_|=&wI|P zI#tzG)zv+*W53j~U)s7}*igOgzEnuv_Vg6T`8*&s1pli95-)B`_t<6uu^|sdM>0(p z3TPelfGpYxfdrBdERX7>X2$Lg ztACFDG^_@_?}gRPzNf?Lz_^I|MckgSmBxpQ1=|u_k<2vl`*6hlNiT%eQOOU5)qf

*$3s~|U);I%!U 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 diff --git a/WhatsAppApi/bin/Debug/WhatsAppApi.pdb b/WhatsAppApi/bin/Debug/WhatsAppApi.pdb index d0c727c9455c8acb91aabc50f8af12c24dea5f65..ff2d3338347b5f15d155e43bef3cfd481ecf1a3d 100644 GIT binary patch delta 22632 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 delta 21615 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 diff --git a/WhatsAppApi/obj/Debug/WhatsAppApi.dll b/WhatsAppApi/obj/Debug/WhatsAppApi.dll index 0f2d7e58df906475366f2ea2c866be6f6423a2cb..16cd000b3c97fbbdca02d78e381206537c90174c 100644 GIT binary patch delta 12570 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 delta 12543 zcmai*33wF6*0Aez_w-CAvt(v6lRcTNbT$$QkOT-OtO3EWlduI@1Z3&Z2@{=(IJuUz-<;v+5>{!=wQ)9B;#|2_|=&wI|P zI#tzG)zv+*W53j~U)s7}*igOgzEnuv_Vg6T`8*&s1pli95-)B`_t<6uu^|sdM>0(p z3TPelfGpYxfdrBdERX7>X2$Lg ztACFDG^_@_?}gRPzNf?Lz_^I|MckgSmBxpQ1=|u_k<2vl`*6hlNiT%eQOOU5)qf

*$3s~|U);I%!U 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 diff --git a/WhatsAppApi/obj/Debug/WhatsAppApi.pdb b/WhatsAppApi/obj/Debug/WhatsAppApi.pdb index d0c727c9455c8acb91aabc50f8af12c24dea5f65..ff2d3338347b5f15d155e43bef3cfd481ecf1a3d 100644 GIT binary patch delta 22632 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 delta 21615 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 From 158801631ebb2f822fc5f1ee71490eebabc9c59f Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 14:31:28 +0100 Subject: [PATCH 027/271] Added documentation to WhatsConstants.cs --- WhatsAppApi/Settings/WhatsConstants.cs | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index f3730f1..4cbe9da 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -6,24 +6,74 @@ namespace WhatsAppApi.Settings { + ///

+ /// Holds constant information used to connect to whatsapp server + /// class WhatsConstants { #region ServerConstants + + /// + /// The whatsapp digest + /// public const string WhatsAppDigest = "xmpp/s.whatsapp.net"; + + /// + /// The whatsapp host + /// public const string WhatsAppHost = "bin-short.whatsapp.net"; + + /// + /// The whatsapp XMPP realm + /// public const string WhatsAppRealm = "s.whatsapp.net"; + + /// + /// The whatsapp server + /// public const string WhatsAppServer = "s.whatsapp.net"; + + /// + /// The whatsapp group chat server + /// public const string WhatsGroupChat = "g.us"; + + + /// + /// The whatsapp version the client complies to + /// public const string WhatsAppVer = "2.8.7"; + + /// + /// The port that needs to be connected to + /// public const int WhatsPort = 5222; + /// + /// iPhone device + /// public const string IphoneDevice = "iPhone"; + + /// + /// The useragent used for http requests + /// public const string UserAgend = "WhatsApp/2.8.7 iPhone_OS/6.1.0 Device/iPhone_4S"; + + /// + /// The whatsapp build hash + /// public const string WhatsBuildHash = "889d4f44e479e6c38b4a834c6d8417815f999abe"; #endregion #region ParserConstants + /// + /// The number style used + /// public static NumberStyles WhatsAppNumberStyle = (NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign); + + /// + /// Unix epoch DateTime + /// public static DateTime UnixEpoch = new DateTime(0x7b2, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); #endregion } From d39b2c4166939a667bb751083fadcfd485255752 Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 14:35:00 +0100 Subject: [PATCH 028/271] Added documentation to WhatsParser.cs --- WhatsAppApi/Response/WhatsParser.cs | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/WhatsAppApi/Response/WhatsParser.cs b/WhatsAppApi/Response/WhatsParser.cs index 48ad270..03f29d7 100644 --- a/WhatsAppApi/Response/WhatsParser.cs +++ b/WhatsAppApi/Response/WhatsParser.cs @@ -7,13 +7,37 @@ namespace WhatsAppApi.Response { + /// + /// Parses whatsapp messages + /// public class WhatsParser { + /// + /// An instance of the WhatsSendHandler class + /// public WhatsSendHandler WhatsSendHandler { get; private set; } + + /// + /// An instnce of the WhatsNetwork class + /// private WhatsNetwork whatsNetwork; + + /// + /// An instance of the MessageRecvResponse class + /// private MessageRecvResponse messResponseHandler; + + + /// + /// An instance of the Binary Tree node writer class + /// private BinTreeNodeWriter _binWriter; + /// + /// Default constructor + /// + /// An instance of the WhatsNetwork class + /// An instance of the BinTreeNodeWriter class internal WhatsParser(WhatsNetwork whatsNet, BinTreeNodeWriter writer) { this.WhatsSendHandler = new WhatsSendHandler(whatsNet, writer); @@ -22,6 +46,10 @@ internal WhatsParser(WhatsNetwork whatsNet, BinTreeNodeWriter writer) this._binWriter = writer; } + /// + /// Parse a tree node + /// + /// An instance of the ProtocolTreeNode class that needs to be parsed. public void ParseProtocolNode(ProtocolTreeNode protNode) { if (ProtocolTreeNode.TagEquals(protNode, "iq")) @@ -103,6 +131,11 @@ public void ParseProtocolNode(ProtocolTreeNode protNode) } } + /// + /// Parse categories + /// + /// An instance of the ProtocolTreeNode class + /// A dictionary with the categories used internal static Dictionary ParseCategories(ProtocolTreeNode dirtyNode) { var dictionary = new Dictionary(); From 152b76649faf9f0f55638a07f6f96e8782a567fb Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 14:39:49 +0100 Subject: [PATCH 029/271] Added documentation to WhatsEventHandler.cs --- WhatsAppApi/Response/WhatsEventHandler.cs | 62 +++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/Response/WhatsEventHandler.cs b/WhatsAppApi/Response/WhatsEventHandler.cs index 132bf46..de43e16 100644 --- a/WhatsAppApi/Response/WhatsEventHandler.cs +++ b/WhatsAppApi/Response/WhatsEventHandler.cs @@ -7,26 +7,80 @@ namespace WhatsAppApi.Response { + /// + /// Handles events + /// public static class WhatsEventHandler { #region Delegates + /// + /// Event occures when the message has been recieved + /// + /// The message that has been recieved public delegate void MessageRecievedHandler(FMessage mess); + + /// + /// Handles string arrays + /// + /// A string array public delegate void StringArrayHandler(string[] value); + + /// + /// Handles boolean valies + /// + /// Sender + /// Boolean value public delegate void BoolHandler(string from, bool value); + + /// + /// Event occures when somebody changes his profile picture + /// + /// The sender + /// The user id that changed his profile picture + /// The id of the new photo public delegate void PhotoChangedHandler(string from, string uJid, string photoId); + + /// + /// Event occurs when the group subject has changed + /// + /// The changer + /// The uid of the changer + /// The new subject + /// ? public delegate void GroupNewSubjectHandler(string from, string uJid, string subject, int t); #endregion - #region Events - public static event MessageRecievedHandler MessageRecievedEvent; - public static event BoolHandler IsTypingEvent; - public static event GroupNewSubjectHandler GroupNewSubjectEvent; + #region Events + /// + /// Event occures when the message has been recieved + /// + public static event MessageRecievedHandler MessageRecievedEvent; + + /// + /// Handles boolean valies + /// + public static event BoolHandler IsTypingEvent; + + /// + /// Event occurs when the group subject has changed + /// + public static event GroupNewSubjectHandler GroupNewSubjectEvent; + + /// + /// Event occures when somebody changes his profile picture + /// public static event PhotoChangedHandler PhotoChangedEvent; #endregion #region OnMethods + + /* + * No need to add documentation here + * User will only handle the delagates and events + * */ + internal static void OnMessageRecievedEventHandler(FMessage mess) { var h = MessageRecievedEvent; From 6efcdd277666f5e23ec8703cee9dc6ce2e44c2cb Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Thu, 31 Jan 2013 14:45:33 +0100 Subject: [PATCH 030/271] Added documentation for MessageRecvResponse.cs --- WhatsAppApi/Response/MessageRecvResponse.cs | 48 ++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/Response/MessageRecvResponse.cs b/WhatsAppApi/Response/MessageRecvResponse.cs index add4ef7..090a146 100644 --- a/WhatsAppApi/Response/MessageRecvResponse.cs +++ b/WhatsAppApi/Response/MessageRecvResponse.cs @@ -9,15 +9,29 @@ namespace WhatsAppApi.Response { + /// + /// Respond to a recieved message + /// class MessageRecvResponse { + /// + /// An instance of the WhatsSendHandler class + /// private WhatsSendHandler sendHandler; + /// + /// Default class constructor + /// + /// An instance of the WhatsSendHandler class public MessageRecvResponse(WhatsSendHandler sendHandler) { this.sendHandler = sendHandler; } + /// + /// Parse recieved message + /// + /// TreeNode that contains the recieved message public void ParseMessageRecv(ProtocolTreeNode messageNode) { FMessage.Builder builder = new FMessage.Builder(); @@ -52,7 +66,12 @@ public void ParseMessageRecv(ProtocolTreeNode messageNode) } } - + /// + /// Notify typing + /// + /// The protocoltreenode + /// From? + /// Message id private void TypeNotification(ProtocolTreeNode messageNode, string tmpAttrFrom, string tmpAttrbId) { foreach (ProtocolTreeNode tmpChild in (messageNode.GetAllChildren() ?? new ProtocolTreeNode[0])) @@ -72,6 +91,11 @@ private void TypeNotification(ProtocolTreeNode messageNode, string tmpAttrFrom, } } + /// + /// Notify typing picture + /// + /// Child + /// From? private static void TypeNotificationPicture(ProtocolTreeNode tmpChild, string tmpFrom) { foreach (ProtocolTreeNode item in (tmpChild.GetAllChildren() ?? new ProtocolTreeNode[0])) @@ -91,6 +115,14 @@ private static void TypeNotificationPicture(ProtocolTreeNode tmpChild, string tm } } + /// + /// Notify typing chat + /// + /// + /// + /// + /// + /// private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string tmpAttrbId, FMessage.Builder builder, string tmpAttrFromJid) { foreach (ProtocolTreeNode itemNode in (messageNode.GetAllChildren() ?? new ProtocolTreeNode[0])) @@ -240,6 +272,14 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t } } + /// + /// Type subject + /// + /// + /// + /// + /// + /// private void TypeSubject(ProtocolTreeNode messageNode, string tmpFrom, string uJid, string tmpId, string tmpT) { bool flag = false; @@ -262,6 +302,12 @@ private void TypeSubject(ProtocolTreeNode messageNode, string tmpFrom, string uJ } } + /// + /// Type error + /// + /// + /// + /// private void TypeError(ProtocolTreeNode messageNode, string tmpAttrbId, string tmpAttrFrom) { int num2 = 0; From 21628719f60ae8614dae938c9f504b82b9121d24 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 6 Jun 2013 15:33:21 +0200 Subject: [PATCH 031/271] Added gitignore --- .gitattributes | 22 +++++++ .gitignore | 166 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..db3352a --- /dev/null +++ b/.gitignore @@ -0,0 +1,166 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +obj/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +*.pdb +*.cache +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store From 004fbe0597b014b50740cac036d4afbd20fd9d32 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 6 Jun 2013 15:34:43 +0200 Subject: [PATCH 032/271] Adde MediaUploader placeholder (TODO) --- WhatsAppApi/Helper/MediaUploader.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 WhatsAppApi/Helper/MediaUploader.cs diff --git a/WhatsAppApi/Helper/MediaUploader.cs b/WhatsAppApi/Helper/MediaUploader.cs new file mode 100644 index 0000000..a72d1bf --- /dev/null +++ b/WhatsAppApi/Helper/MediaUploader.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WhatsAppApi.Helper +{ + class MediaUploader + { + + } +} From 4033fbb25f3477aae41c27838014b6332f55027e Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 6 Jun 2013 15:35:04 +0200 Subject: [PATCH 033/271] Added ContactSync --- WhatsAppApi/Helper/ContactSync.cs | 156 ++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 WhatsAppApi/Helper/ContactSync.cs diff --git a/WhatsAppApi/Helper/ContactSync.cs b/WhatsAppApi/Helper/ContactSync.cs new file mode 100644 index 0000000..76fbc00 --- /dev/null +++ b/WhatsAppApi/Helper/ContactSync.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Web.Script.Serialization; + +namespace WhatsAppApi.Helper +{ + public class ContactSync + { + const string RequestURL = "https://sro.whatsapp.net/v2/sync/a"; + const string ExecureURL = "https://sro.whatsapp.net/v2/sync/q"; + protected string username; + protected string password; + protected HttpWebRequest request; + + public ContactSync(string username, string password) + { + this.username = username; + this.password = password; + } + + public ContactSyncResult[] Sync(string[] contacts) + { + string nonce = this._getSyncNonce(); + string res = this._executeSync(nonce, contacts); + + JavaScriptSerializer jss = new JavaScriptSerializer(); + ContactSyncResultContainer c = jss.Deserialize(res); + + return c.c; + } + + protected string _getPostfields(string[] contacts) + { + string fields = "ut=all&t=c"; + foreach (string contact in contacts) + { + string con = contact; + if(!con.Contains('+')) + { + con = "%2B" + con; + } + fields += "&u[]=" + con; + } + return fields; + } + + protected string _executeSync(string cnonce, string[] contacts) + { + this.request = WebRequest.Create(ExecureURL) as HttpWebRequest; + string postfields = this._getPostfields(contacts); + request.Method = "POST"; + this._setHeaders(cnonce, postfields.Length); + using (var writer = new StreamWriter(request.GetRequestStream())) + { + writer.Write(postfields); + } + HttpWebResponse response = this.request.GetResponse() as HttpWebResponse; + StreamReader reader = new StreamReader(response.GetResponseStream()); + return reader.ReadToEnd(); + } + + protected string _getSyncNonce() + { + this.request = WebRequest.Create(RequestURL) as HttpWebRequest; + this._setHeaders("0", 0); + HttpWebResponse response = this.request.GetResponse() as HttpWebResponse; + string cnonce = this._getCnonce(response.Headers.Get("WWW-Authenticate")); + return cnonce; + } + + protected string _getCnonce(string header) + { + string[] parts = header.Split(','); + parts = parts.Last().Replace('\\', '\0').Split('"'); + return parts[1]; + } + + protected static string _getCnonce() + { + string foo = _hash(DateTime.Now.Ticks.ToString()).Substring(0, 10);//random + return _hash(foo); + } + + protected static string _hash(string data) + { + return _hash(data, false); + } + + protected static string _hash(string data, bool raw) + { + byte[] bytes = System.Text.Encoding.Default.GetBytes(data); + MD5 md5 = MD5.Create(); + md5.ComputeHash(bytes); + if (!raw) + { + return _hexEncode(md5.Hash); + } + else + { + return System.Text.Encoding.Default.GetString(md5.Hash); + } + } + + protected static string _hexEncode(byte[] data) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < data.Length; i++) + { + sb.Append(data[i].ToString("X2")); + } + return sb.ToString().ToLower(); + } + + protected void _setHeaders(string nonce, long contentlength) + { + this.request.Headers.Clear(); + this.request.UserAgent = "WhatsApp/2.4.7 S40Version/14.26 Device/Nokia302"; + this.request.Accept = "text/json"; + this.request.ContentType = "application/x-www-form-urlencoded"; + string foo = this._generateAuth(nonce); + this.request.Headers.Add("Authorization", foo); + this.request.Headers.Add("Accept-Encoding", "identity"); + this.request.ContentLength = contentlength; + } + + protected string _generateAuth(string nonce) + { + string cnonce = _getCnonce(); + string nc = "00000001"; + string digestUri = "WAWA/s.whatsapp.net"; + string credentials = this.username + ":s.whatsapp.net:"; + credentials += System.Text.Encoding.Default.GetString(Convert.FromBase64String(this.password)); + string response = _hash(_hash(_hash(credentials, true) + ":" + nonce + ":" + cnonce) + ":" + nonce + ":" + nc + ":" + cnonce + ":auth:" + _hash("AUTHENTICATE:" + digestUri)); + return "X-WAWA:username=\"" + this.username + "\",realm=\"s.whatsapp.net\",nonce=\"" + nonce + "\",cnonce=\"" + cnonce + "\",nc=\"" + nc + "\",qop=\"auth\",digest-uri=\"" + digestUri + "\",response=\"" + response + "\",charset=\"utf-8\""; + } + } + + public class ContactSyncResult + { + public string p { get; set; } + public string n { get; set; } + public string s { get; set; } + public long t { get; set; } + public int w { get; set; } + } + + public class ContactSyncResultContainer + { + public ContactSyncResult[] c { get; set; } + } +} From 6730dcf141e6d8a14679fc67f309bed0ada8f0d4 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 6 Jun 2013 15:35:23 +0200 Subject: [PATCH 034/271] Updated Program.cs --- WhatsTest/Program.cs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/WhatsTest/Program.cs b/WhatsTest/Program.cs index 728e049..2397440 100644 --- a/WhatsTest/Program.cs +++ b/WhatsTest/Program.cs @@ -10,6 +10,7 @@ using System.Threading; using WhatsAppApi; using WhatsAppApi.Account; +using WhatsAppApi.Helper; using WhatsAppApi.Register; namespace WhatsTest @@ -22,26 +23,21 @@ private static void Main(string[] args) System.Console.OutputEncoding = Encoding.Default; System.Console.InputEncoding = Encoding.Default; string nickname = "WhatsAPI Test"; - //string sender = ""; // Mobile number with country code (but without + or 00) - //string imei = ""; // MAC Address for iOS IMEI for other platform (Android/etc) + string sender = "3526********"; // Mobile number with country code (but without + or 00) + string password = "JJ9gQWk******************Y=";//v2 password + string target = "316********";// Mobile number to send the message to - //WhatsApp wa = new WhatsApp(sender, imei, nickname, true); + WhatsApp wa = new WhatsApp(sender, password, nickname, true); + + wa.Connect(); + wa.Login(); + wa.sendNickname(nickname); + wa.Disconnect(); - //string countrycode = sender.Substring(0, 2); - //string phonenumber = sender.Remove(0, 2); + wa.PresenceSubscription(target); + wa.GetStatus(target); - //if (!WhatsRegister.ExistsAndDelete(countrycode, phonenumber, imei)) - //{ - // PrintToConsole("Wrong Password"); - // return; - //} - - //wa.Connect(); - //wa.Login(); - //wa.sendNickname("test"); - - //ProcessChat(wa, ""); - RegisterAccount(); + ProcessChat(wa, ""); Console.ReadKey(); } From d66cc661a450e92bb706499f802fc6d2546e22e4 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 6 Jun 2013 15:36:57 +0200 Subject: [PATCH 035/271] Changed accessors for MissVenom --- WhatsAppApi/Helper/BinTreeNodeReader.cs | 34 ++++++++++++++----- WhatsAppApi/Helper/BinTreeNodeWriter.cs | 2 +- WhatsAppApi/Helper/DecodeHelper.cs | 10 +++--- WhatsAppApi/Helper/Encryption.cs | 2 +- .../Helper/IncompleteMessageException.cs | 2 +- WhatsAppApi/Helper/RC4.cs | 2 +- 6 files changed, 34 insertions(+), 18 deletions(-) diff --git a/WhatsAppApi/Helper/BinTreeNodeReader.cs b/WhatsAppApi/Helper/BinTreeNodeReader.cs index 49a217d..cdf1542 100644 --- a/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -5,7 +5,7 @@ namespace WhatsAppApi.Helper { - internal class BinTreeNodeReader + public class BinTreeNodeReader { private string[] dictionary; public byte[] Encryptionkey { get; set; } @@ -17,7 +17,7 @@ public BinTreeNodeReader(string[] dict) this.Encryptionkey = null; } - public ProtocolTreeNode nextTree(byte[] pInput = null) + public ProtocolTreeNode nextTree(byte[] pInput = null, bool useDecrypt = true) { if (pInput != null) { @@ -44,9 +44,16 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) bool isEncrypted = (stanzaFlag & 8) != 0; - if (isEncrypted && Encryptionkey != null) + if (isEncrypted) { - decode(size); + if (Encryptionkey != null) + { + decode(size, useDecrypt); + } + else + { + throw new Exception("Received encrypted message, encryption key not set"); + } } if(stanzaSize > 0) @@ -59,7 +66,7 @@ public ProtocolTreeNode nextTree(byte[] pInput = null) return null; } - protected void decode(int stanzaSize) + protected void decode(int stanzaSize, bool useDecrypt) { int size = stanzaSize; byte[] data = new byte[size]; @@ -79,7 +86,16 @@ protected void decode(int stanzaSize) if (hashServerByte.SequenceEqual(hashByte)) { this.buffer.RemoveRange(0, 4); - dataReal = Encryption.WhatsappDecrypt(this.Encryptionkey, packet); + if (useDecrypt) + { + dataReal = Encryption.WhatsappDecrypt(this.Encryptionkey, packet); + } + else + { + dataReal = Encryption.WhatsappEncrypt(this.Encryptionkey, packet, true); + //dataReal = new byte[foo.Length - 4]; + //Buffer.BlockCopy(foo, 0, dataReal, 0, dataReal.Length); + } for (int i = 0; i < size - 4; i++) { @@ -101,7 +117,7 @@ protected string getToken(int token) } else { - throw new Exception("BinTreeNodeReader->getToken: Invalid token $token"); + throw new Exception("BinTreeNodeReader->getToken: Invalid token " + token); } return ret; } @@ -111,7 +127,7 @@ protected byte[] readBytes(int token) byte[] ret = new byte[0]; if (token == -1) { - throw new Exception("BinTreeNodeReader->readString: Invalid token $token"); + throw new Exception("BinTreeNodeReader->readString: Invalid token " + token); } if ((token > 4) && (token < 0xf5)) { @@ -230,7 +246,7 @@ protected int readListSize(int token) } else { - throw new Exception("BinTreeNodeReader->readListSize: Invalid token $token"); + throw new Exception("BinTreeNodeReader->readListSize: Invalid token " + token); } return size; } diff --git a/WhatsAppApi/Helper/BinTreeNodeWriter.cs b/WhatsAppApi/Helper/BinTreeNodeWriter.cs index 8320c21..e065517 100644 --- a/WhatsAppApi/Helper/BinTreeNodeWriter.cs +++ b/WhatsAppApi/Helper/BinTreeNodeWriter.cs @@ -5,7 +5,7 @@ namespace WhatsAppApi.Helper { - internal class BinTreeNodeWriter + public class BinTreeNodeWriter { private List buffer; private Dictionary tokenMap; diff --git a/WhatsAppApi/Helper/DecodeHelper.cs b/WhatsAppApi/Helper/DecodeHelper.cs index 6b36c90..79d33a0 100644 --- a/WhatsAppApi/Helper/DecodeHelper.cs +++ b/WhatsAppApi/Helper/DecodeHelper.cs @@ -6,7 +6,7 @@ namespace WhatsAppApi.Helper { - internal static class DecodeHelper + public static class DecodeHelper { private static string[] dictList = null; public static string decode(string hex) @@ -36,10 +36,10 @@ public static string[] SplitStringN(this string value, int count) } public static string[] getDictionary() - { - if (dictList != null) - { - return dictList; + { + if (dictList != null) + { + return dictList; } dictList = new string[249]; dictList[0] = null; diff --git a/WhatsAppApi/Helper/Encryption.cs b/WhatsAppApi/Helper/Encryption.cs index 18bb7cc..035ccc6 100644 --- a/WhatsAppApi/Helper/Encryption.cs +++ b/WhatsAppApi/Helper/Encryption.cs @@ -6,7 +6,7 @@ namespace WhatsAppApi.Helper { - static class Encryption + public static class Encryption { public static RC4 encryptionOutgoing = null; public static RC4 encryptionIncoming = null; diff --git a/WhatsAppApi/Helper/IncompleteMessageException.cs b/WhatsAppApi/Helper/IncompleteMessageException.cs index 58e6cfd..d8a3b06 100644 --- a/WhatsAppApi/Helper/IncompleteMessageException.cs +++ b/WhatsAppApi/Helper/IncompleteMessageException.cs @@ -5,7 +5,7 @@ namespace WhatsAppApi.Helper { - class IncompleteMessageException : Exception + public class IncompleteMessageException : Exception { private int code; private string message; diff --git a/WhatsAppApi/Helper/RC4.cs b/WhatsAppApi/Helper/RC4.cs index 456a342..70b4d55 100644 --- a/WhatsAppApi/Helper/RC4.cs +++ b/WhatsAppApi/Helper/RC4.cs @@ -2,7 +2,7 @@ namespace WhatsAppApi.Helper { - internal class RC4 + public class RC4 { private int i = 0; private int j = 0; From e05e1979e124e58fbbaff0d73b18ced612b2ec04 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 6 Jun 2013 15:37:39 +0200 Subject: [PATCH 036/271] The "other" changes...? --- WhatsAppApi/WhatsApp.cs | 358 ++++++++++++--------- WhatsAppApi/WhatsAppApi.csproj | 4 + WhatsAppApi/WhatsNetwork.cs | 376 +++++++++++----------- WhatsAppApi/WhatsSendHandler.cs | 539 ++++++++++++++++---------------- WhatsAppApi_vs2010.sln | 108 +++---- 5 files changed, 732 insertions(+), 653 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index ad754c9..ce3b85e 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -12,117 +12,127 @@ namespace WhatsAppApi { - ///
- /// Main api interface + /// + /// Main api interface /// public class WhatsApp { - /// - /// Describes the connection status with the whatsapp server + /// + /// Describes the connection status with the whatsapp server /// public enum CONNECTION_STATUS { + UNAUTHORIZED, DISCONNECTED, - CONNECTED + CONNECTED, + LOGGEDIN } - /// - /// An instance of the AccountInfo class + /// + /// An instance of the AccountInfo class /// private AccountInfo accountinfo; - /// - /// Determines wether debug mode is on or offf + /// + /// Determines wether debug mode is on or offf /// public static bool DEBUG; - /// - /// The imei/mac adress + /// + /// The imei/mac adress /// private string imei; - /// - /// Holds the login status + /// + /// Holds the login status /// private CONNECTION_STATUS loginStatus; - /// - /// A lock for a message + public CONNECTION_STATUS ConnectionStatus + { + get + { + return this.loginStatus; + } + } + + /// + /// A lock for a message /// private object messageLock = new object(); - /// - /// Que for recieved messages + /// + /// Que for recieved messages /// private List messageQueue; - /// - /// The name of the user + /// + /// The name of the user /// private string name; - /// - /// The phonenumber + /// + /// The phonenumber /// private string phoneNumber; - /// - /// An instance of the BinaryTreeNodeReader class + /// + /// An instance of the BinaryTreeNodeReader class /// private BinTreeNodeReader reader; - /// - /// An instance of the BinaryTreeNodeWriter class + /// + /// An instance of the BinaryTreeNodeWriter class /// private BinTreeNodeWriter writer; - /// - /// The timeout for the connection with the Whatsapp servers + /// + /// The timeout for the connection with the Whatsapp servers /// - private int timeout = 5000; + private int timeout = 300000; - /// - /// An instance of the WhatsNetwork class + /// + /// An instance of the WhatsNetwork class /// private WhatsNetwork whatsNetwork; - /// - /// An instance of the WhatsSendHandler class + /// + /// An instance of the WhatsSendHandler class /// public WhatsSendHandler WhatsSendHandler { get; private set; } - /// - /// An instance of the WhatsParser class + /// + /// An instance of the WhatsParser class /// public WhatsParser WhatsParser { get; private set; } - /// - /// Holds the encoding we use, default is UTF8 + /// + /// Holds the encoding we use, default is UTF8 /// public static readonly Encoding SYSEncoding = Encoding.UTF8; - /// - /// Empty bytes to hold the encryption key + /// + /// Empty bytes to hold the encryption key /// private byte[] _encryptionKey; - /// - /// Empty bytes to hold the challenge + /// + /// Empty bytes to hold the challenge /// private byte[] _challengeBytes; - /// - /// A list of exceptions for incomplete bytes + /// + /// A list of exceptions for incomplete bytes /// private List _incompleteBytes; - /// - /// Default class constructor - /// - /// The phone number - /// The imei / mac - /// User nickname + /// + /// Default class constructor + /// + /// The phone number + /// The imei / mac + /// User nickname /// Debug on or off, false by default public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) { @@ -143,9 +153,9 @@ public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) _incompleteBytes = new List(); } - /// - /// Add a message to the message que - /// + /// + /// Add a message to the message que + /// /// An instance of the ProtocolTreeNode class public void AddMessage(ProtocolTreeNode node) { @@ -156,55 +166,54 @@ public void AddMessage(ProtocolTreeNode node) } - /// - /// Connect to the whatsapp network + /// + /// Connect to the whatsapp network /// public void Connect() { this.whatsNetwork.Connect(); } - /// - /// Disconnect from the whatsapp network + /// + /// Disconnect from the whatsapp network /// public void Disconnect() { - this.whatsNetwork.Connect(); + this.whatsNetwork.Disconenct(); this.loginStatus = CONNECTION_STATUS.DISCONNECTED; } - /// - /// Encrypt the password (hash) - /// + /// + /// Encrypt the password (hash) + /// /// - public string encryptPassword() + public byte[] encryptPassword() { - // iPhone - if (this.imei.Contains(":")) - { - this.imei = this.imei.ToUpper(); - return md5(this.imei + this.imei); - } - - // Other - else - { - return md5(new string(this.imei.Reverse().ToArray())); - } + return Convert.FromBase64String(this.imei); } - /// - /// Get the account information - /// + /// + /// Get the account information + /// /// An instance of the AccountInfo class public AccountInfo GetAccountInfo() { return this.accountinfo; } - /// - /// Retrieve all messages - /// + public void GetStatus(string jid) + { + this.WhatsSendHandler.SendGetStatus(this.GetJID(jid)); + } + + public void PresenceSubscription(string target) + { + this.WhatsSendHandler.SendPresenceSubscriptionRequest(this.GetJID(target)); + } + + /// + /// Retrieve all messages + /// /// An array of instances of the ProtocolTreeNode class. public ProtocolTreeNode[] GetAllMessages() { @@ -217,9 +226,9 @@ public ProtocolTreeNode[] GetAllMessages() return tmpReturn; } - /// - /// Checks wether we have messages to retrieve - /// + /// + /// Checks wether we have messages to retrieve + /// /// true or false public bool HasMessages() { @@ -228,11 +237,18 @@ public bool HasMessages() return this.messageQueue.Count > 0; } - /// - /// Logs us in to the server + /// + /// Logs us in to the server /// public void Login() { + //reset stuff + this.reader.Encryptionkey = null; + this.writer.Encryptionkey = null; + this._challengeBytes = null; + Encryption.encryptionIncoming = null; + Encryption.encryptionOutgoing = null; + string resource = string.Format(@"{0}-{1}-{2}", WhatsConstants.IphoneDevice, WhatsConstants.WhatsAppVer, @@ -255,25 +271,48 @@ public void Login() while ((cnt++ < 100) && (this.loginStatus == CONNECTION_STATUS.DISCONNECTED)); } - /// - /// Send a message to a person - /// - /// The phone number to send + /// + /// Send a message to a person + /// + /// The phone number to send /// The text that needs to be send public void Message(string to, string txt) { - var tmpMessage = new FMessage(to, true) { key = { id = TicketManager.GenerateId() }, data = txt }; + var tmpMessage = new FMessage(this.GetJID(to), true) { key = { id = TicketManager.GenerateId() }, data = txt }; this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage); } - /// - /// Send an image to a person - /// - /// The id of the message - /// the reciepient - /// The url to the image - /// Filename - /// The size of the image in string format + /// + /// Convert the input string to a JID if necessary + /// + /// Phonenumber or JID + public string GetJID(string target) + { + if (!target.Contains('@')) + { + //check if group message + if (target.Contains('-')) + { + //to group + target += "@g.us"; + } + else + { + //to normal user + target += "@s.whatsapp.net"; + } + } + return target; + } + + /// + /// Send an image to a person + /// + /// The id of the message + /// the reciepient + /// The url to the image + /// Filename + /// The size of the image in string format /// Icon public void MessageImage(string msgid, string to, string url, string file, string size, string icon) { @@ -290,44 +329,44 @@ public void MessageImage(string msgid, string to, string url, string file, strin //this.SendMessageNode(msgid, to, mediaNode); } - /// - /// Retrieve messages from the server + /// + /// Retrieve messages from the server /// public void PollMessages() { this.processInboundData(this.whatsNetwork.ReadData()); } - /// - /// Send a pong to the whatsapp server - /// + /// + /// Send a pong to the whatsapp server + /// /// Message id public void Pong(string msgid) { this.WhatsParser.WhatsSendHandler.SendPong(msgid); } - /// - /// Request last seen - /// + /// + /// Request last seen + /// /// Jabber id public void RequestLastSeen(string jid) { - this.WhatsParser.WhatsSendHandler.SendQueryLastOnline(jid); + this.WhatsParser.WhatsSendHandler.SendQueryLastOnline(this.GetJID(jid)); } - /// - /// Send nickname - /// + /// + /// Send nickname + /// /// The nickname public void sendNickname(string nickname) { this.WhatsParser.WhatsSendHandler.SendAvailableForChat(nickname); } - /// - /// Add the authenication nodes - /// + /// + /// Add the authenication nodes + /// /// An instance of the ProtocolTreeNode class protected ProtocolTreeNode addAuth() { @@ -338,9 +377,9 @@ protected ProtocolTreeNode addAuth() return node; } - /// - /// Add the auth response to protocoltreenode - /// + /// + /// Add the auth response to protocoltreenode + /// /// An instance of the ProtocolTreeNode protected ProtocolTreeNode addAuthResponse() { @@ -370,9 +409,9 @@ protected ProtocolTreeNode addAuthResponse() return node; } - /// - /// Add stream features - /// + /// + /// Add stream features + /// /// protected ProtocolTreeNode addFeatures() { @@ -383,9 +422,9 @@ protected ProtocolTreeNode addFeatures() return parent; } - /// - /// Print a message to the debug console - /// + /// + /// Print a message to the debug console + /// /// The message protected void DebugPrint(string debugMsg) { @@ -395,34 +434,46 @@ protected void DebugPrint(string debugMsg) } } - /// - /// Process the challenge - /// + /// + /// Process the challenge + /// /// The node that contains the challenge protected void processChallenge(ProtocolTreeNode node) { _challengeBytes = node.data; } - /// - /// Process inbound data - /// + /// + /// Process inbound data + /// /// Data to process protected void processInboundData(byte[] data) { try { - var node = this.reader.nextTree(data); + List foo = new List(); + foreach (IncompleteMessageException e in this._incompleteBytes) + { + foo.AddRange(e.getInput()); + } + this._incompleteBytes = new List(); + foo.AddRange(data); + ProtocolTreeNode node = this.reader.nextTree(foo.ToArray()); while (node != null) { - this.WhatsParser.ParseProtocolNode(node); + //this.WhatsParser.ParseProtocolNode(node); + if (node.tag == "iq" + && node.GetAttribute("type") == "error") + { + this.AddMessage(node); + } if (ProtocolTreeNode.TagEquals(node, "challenge")) { this.processChallenge(node); - } + } else if (ProtocolTreeNode.TagEquals(node,"success")) { - this.loginStatus = CONNECTION_STATUS.CONNECTED; + this.loginStatus = CONNECTION_STATUS.LOGGEDIN; this.accountinfo = new AccountInfo(node.GetAttribute("status"), node.GetAttribute("kind"), node.GetAttribute("creation"), @@ -430,7 +481,7 @@ protected void processInboundData(byte[] data) } else if (ProtocolTreeNode.TagEquals(node,"failure")) { - this.loginStatus = CONNECTION_STATUS.DISCONNECTED; + this.loginStatus = CONNECTION_STATUS.UNAUTHORIZED; } if (ProtocolTreeNode.TagEquals(node,"message")) { @@ -441,6 +492,22 @@ protected void processInboundData(byte[] data) { Console.Write(node.NodeString()); } + if (ProtocolTreeNode.TagEquals(node, "iq") + && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.First(), "query") + ) + { + //last seen + this.AddMessage(node); + } + if (ProtocolTreeNode.TagEquals(node, "iq") + && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.First(), "picture") + ) + { + //profile picture + this.AddMessage(node); + } if (ProtocolTreeNode.TagEquals(node,"iq") && node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) && ProtocolTreeNode.TagEquals(node.children.First(), "ping")) @@ -462,18 +529,23 @@ protected void processInboundData(byte[] data) } } } + if (ProtocolTreeNode.TagEquals(node, "presence")) + { + //presence node + this.AddMessage(node); + } node = this.reader.nextTree(); } } - catch (IncompleteMessageException) + catch (IncompleteMessageException ex) { - + this._incompleteBytes.Add(ex); } } - /// - /// Tell the server we recieved the message - /// + /// + /// Tell the server we recieved the message + /// /// The ProtocolTreeNode that contains the message protected void sendMessageReceived(ProtocolTreeNode msg) { @@ -486,10 +558,10 @@ protected void sendMessageReceived(ProtocolTreeNode msg) this.WhatsParser.WhatsSendHandler.SendMessageReceived(tmpMessage); } - /// - /// MD5 hashes the password - /// - /// String the needs to be hashed + /// + /// MD5 hashes the password + /// + /// String the needs to be hashed /// A md5 hash private string md5(string pass) { @@ -501,9 +573,9 @@ private string md5(string pass) return sb.ToString(); } - /// - /// Prints debug - /// + /// + /// Prints debug + /// /// message private void PrintInfo(string p) { diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index a691153..3841ee8 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -23,6 +23,7 @@ prompt 4 false + false pdbonly @@ -36,6 +37,7 @@ + @@ -48,12 +50,14 @@ + + Code diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index 8a96d84..abc8434 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -1,187 +1,189 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Sockets; -using System.Text; -using WhatsAppApi.Helper; - -namespace WhatsAppApi -{ - /// - /// Contains methods to connect to the Whatsapp network - /// - public class WhatsNetwork - { - /// - /// The time between sending and recieving - /// - private readonly int recvTimeout; - - /// - /// The hostname of the whatsapp server - /// - private readonly string whatsHost; - - /// - /// The port of the whatsapp server - /// - private readonly int whatsPort; - - /// - /// A list of bytes for incomplete messages - /// - private List incomplete_message = new List(); - - /// - /// A socket to connect to the whatsapp network - /// - private Socket socket; - - /// - /// Default class constructor - /// - /// The hostname of the whatsapp server - /// The port of the whatsapp server - /// Timeout for the connection - public WhatsNetwork(string whatsHost, int port, int timeout = 2000) - { - this.recvTimeout = timeout; - this.whatsHost = whatsHost; - this.whatsPort = port; - this.incomplete_message = new List(); - } - - /// - /// Default class constructor - /// - /// Timeout for the connection - public WhatsNetwork(int timeout = 2000) - { - this.recvTimeout = timeout; - this.whatsHost = Settings.WhatsConstants.WhatsAppHost; - this.whatsPort = Settings.WhatsConstants.WhatsPort; - this.incomplete_message = new List(); - } - - /// - /// Connect to the whatsapp server - /// - public void Connect() - { - this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - this.socket.Connect(this.whatsHost, this.whatsPort); - this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.recvTimeout); - - if (!this.socket.Connected) - throw new ConnectionException("Cannot connect"); - } - - /// - /// Disconnect from the whatsapp server - /// - public void Disconenct() - { - if (this.socket.Connected) - this.socket.Disconnect(true); - } - - /// - /// Read 1024 bytes - /// - /// - public byte[] ReadData() - { - List buff = new List(); - byte[] ret = Socket_read(1024); - return ret; - } - - /// - /// Send data to the whatsapp server - /// - /// Data to be send as a string - public void SendData(string data) - { - Socket_send(data, data.Length, 0); - } - - /// - /// Send data to the whatsapp server - /// - /// Data to be send as a byte array - public void SendData(byte[] data) - { - Socket_send(data); - } - - /// - /// Read in a message with a specific length - /// - /// The lengh of the message - /// The recieved data as a byte array - private byte[] Socket_read(int length) - { - if (!socket.Connected) - { - throw new ConnectionException(); - } - - var buff = new byte[length]; - int receiveLength = 0; - do - { - try - { - receiveLength = socket.Receive(buff, 0, length, 0); - } - catch (SocketException excpt) - { - if (excpt.SocketErrorCode == SocketError.TimedOut) - { - Console.WriteLine("Socket-Timout"); - return null; - } - else - { - throw new ConnectionException("Unknown error occured", excpt); - } - } - } - while (receiveLength <= 0); - - byte[] tmpRet = new byte[receiveLength]; - if (receiveLength > 0) - Buffer.BlockCopy(buff, 0, tmpRet, 0, receiveLength); - return tmpRet; - } - - /// - /// Sends data of a specific length to the server - /// - /// The data that needs to be send - /// The lenght of the data - /// Optional flags - private void Socket_send(string data, int length, int flags) - { - var tmpBytes = WhatsApp.SYSEncoding.GetBytes(data); - this.socket.Send(tmpBytes); - } - - /// - /// Send data to the server - /// - /// The data that needs to be send as a byte array - private void Socket_send(byte[] data) - { - this.socket.Send(data); - } - - /// - /// Returns the socket status. - /// - public bool SocketStatus - { - get { return socket.Connected; } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using WhatsAppApi.Helper; + +namespace WhatsAppApi +{ + /// + /// Contains methods to connect to the Whatsapp network + /// + public class WhatsNetwork + { + /// + /// The time between sending and recieving + /// + private readonly int recvTimeout; + + /// + /// The hostname of the whatsapp server + /// + private readonly string whatsHost; + + /// + /// The port of the whatsapp server + /// + private readonly int whatsPort; + + /// + /// A list of bytes for incomplete messages + /// + private List incomplete_message = new List(); + + /// + /// A socket to connect to the whatsapp network + /// + private Socket socket; + + /// + /// Default class constructor + /// + /// The hostname of the whatsapp server + /// The port of the whatsapp server + /// Timeout for the connection + public WhatsNetwork(string whatsHost, int port, int timeout = 2000) + { + this.recvTimeout = timeout; + this.whatsHost = whatsHost; + this.whatsPort = port; + this.incomplete_message = new List(); + } + + /// + /// Default class constructor + /// + /// Timeout for the connection + public WhatsNetwork(int timeout = 2000) + { + this.recvTimeout = timeout; + this.whatsHost = Settings.WhatsConstants.WhatsAppHost; + this.whatsPort = Settings.WhatsConstants.WhatsPort; + this.incomplete_message = new List(); + } + + /// + /// Connect to the whatsapp server + /// + public void Connect() + { + this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + this.socket.Connect(this.whatsHost, this.whatsPort); + this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.recvTimeout); + + if (!this.socket.Connected) + throw new ConnectionException("Cannot connect"); + } + + /// + /// Disconnect from the whatsapp server + /// + public void Disconenct() + { + if (this.socket != null) + { + this.socket.Close(); + } + } + + /// + /// Read 1024 bytes + /// + /// + public byte[] ReadData() + { + List buff = new List(); + byte[] ret = Socket_read(1024); + return ret; + } + + /// + /// Send data to the whatsapp server + /// + /// Data to be send as a string + public void SendData(string data) + { + Socket_send(data, data.Length, 0); + } + + /// + /// Send data to the whatsapp server + /// + /// Data to be send as a byte array + public void SendData(byte[] data) + { + Socket_send(data); + } + + /// + /// Read in a message with a specific length + /// + /// The lengh of the message + /// The recieved data as a byte array + private byte[] Socket_read(int length) + { + if (!socket.Connected) + { + throw new ConnectionException(); + } + + var buff = new byte[length]; + int receiveLength = 0; + do + { + try + { + receiveLength = socket.Receive(buff, 0, length, 0); + } + catch (SocketException excpt) + { + if (excpt.SocketErrorCode == SocketError.TimedOut) + { + Console.WriteLine("Socket-Timout"); + return null; + } + else + { + throw new ConnectionException("Unknown error occured", excpt); + } + } + } + while (receiveLength <= 0); + + byte[] tmpRet = new byte[receiveLength]; + if (receiveLength > 0) + Buffer.BlockCopy(buff, 0, tmpRet, 0, receiveLength); + return tmpRet; + } + + /// + /// Sends data of a specific length to the server + /// + /// The data that needs to be send + /// The lenght of the data + /// Optional flags + private void Socket_send(string data, int length, int flags) + { + var tmpBytes = WhatsApp.SYSEncoding.GetBytes(data); + this.socket.Send(tmpBytes); + } + + /// + /// Send data to the server + /// + /// The data that needs to be send as a byte array + private void Socket_send(byte[] data) + { + this.socket.Send(data); + } + + /// + /// Returns the socket status. + /// + public bool SocketStatus + { + get { return socket.Connected; } + } + } +} diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 36992d7..4e16868 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -9,35 +9,35 @@ namespace WhatsAppApi { - /// - /// Handles sending messages to the Whatsapp servers + /// + /// Handles sending messages to the Whatsapp servers /// public class WhatsSendHandler { - /// - /// Holds the jabber id that is used to authenticate + /// + /// Holds the jabber id that is used to authenticate /// private string MyJID = ""; - /// - /// The whatsapp realm, defined in WhatsConstants. + /// + /// The whatsapp realm, defined in WhatsConstants. /// private string whatsAppRealm = WhatsAppApi.Settings.WhatsConstants.WhatsAppRealm; - /// - /// Holds an instance of the BinTreeNodeWriter + /// + /// Holds an instance of the BinTreeNodeWriter /// private BinTreeNodeWriter _binWriter; - /// - /// Holds an instance of the WhatsNetwork class + /// + /// Holds an instance of the WhatsNetwork class /// private WhatsNetwork whatsNetwork; - /// - /// Default class constructor - /// - /// An instance of the WhatsNetwork class + /// + /// Default class constructor + /// + /// An instance of the WhatsNetwork class /// An instance of the BinTreeNodeWriter internal WhatsSendHandler(WhatsNetwork net, BinTreeNodeWriter writer) { @@ -45,8 +45,8 @@ internal WhatsSendHandler(WhatsNetwork net, BinTreeNodeWriter writer) this._binWriter = writer; } - /// - /// Sends a message to the Whatsapp server to tell that the user is 'online' / 'active' + /// + /// Sends a message to the Whatsapp server to tell that the user is 'online' / 'active' /// public void SendActive() { @@ -54,22 +54,22 @@ public void SendActive() this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Sends a request to the server to add participants to a group chat - /// - /// Group jabber id + /// + /// Sends a request to the server to add participants to a group chat + /// + /// Group jabber id /// A list of participants (List of jids) public void SendAddParticipants(string gjid, IEnumerable participants) { this.SendAddParticipants(gjid, participants, null, null); - } - - /// - /// Sends a request to the server to add participants to a group chat - /// - /// Group jabber id - /// A list of participants (List of jids) - /// The action to be executed when the request is successfull. + } + + /// + /// Sends a request to the server to add participants to a group chat + /// + /// Group jabber id + /// A list of participants (List of jids) + /// The action to be executed when the request is successfull. /// The action to be executed when the request fails public void SendAddParticipants(string gjid, IEnumerable participants, Action onSuccess, Action onError) { @@ -102,11 +102,11 @@ public void SendClearDirty(string category) this.SendClearDirty(new string[] { category }); } - /// - /// Sends the client configuration to the Whatsapp server. - /// - /// The string identifying the client. - /// ? + /// + /// Sends the client configuration to the Whatsapp server. + /// + /// The string identifying the client. + /// ? /// ? public void SendClientConfig(string platform, string lg, string lc) { @@ -116,18 +116,18 @@ public void SendClientConfig(string platform, string lg, string lc) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Sends the client configuration to the Whatsapp server. - /// - /// The string identifying the client. - /// ? - /// ? - /// ? - /// ? - /// Default settings. - /// Settings regarding groups. - /// A list of groups - /// Action to be executed when the request was successfull. + /// + /// Sends the client configuration to the Whatsapp server. + /// + /// The string identifying the client. + /// ? + /// ? + /// ? + /// ? + /// Default settings. + /// Settings regarding groups. + /// A list of groups + /// Action to be executed when the request was successfull. /// Action to be executed when the request failed. public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, bool preview, bool defaultSetting, bool groupsSetting, IEnumerable groups, Action onCompleted, Action onError) { @@ -158,8 +158,8 @@ public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Change status to 'Offline' + /// + /// Change status to 'Offline' /// public void SendClose() { @@ -167,9 +167,9 @@ public void SendClose() this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Send the status of 'writing'/'typing'/'composing' to the Whatsapp server - /// + /// + /// Send the status of 'writing'/'typing'/'composing' to the Whatsapp server + /// /// The recipient, the one the client is talking to. public void SendComposing(string to) { @@ -178,20 +178,20 @@ public void SendComposing(string to) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Requests to create a new and empty group chat. - /// + /// + /// Requests to create a new and empty group chat. + /// /// The subject of the group chat. public void SendCreateGroupChat(string subject) { this.SendCreateGroupChat(subject, null, null); } - /// - /// Requests to create a new and empty group chat. - /// - /// The subjecct of the group chat. - /// Acction to be executed when the request was successful. + /// + /// Requests to create a new and empty group chat. + /// + /// The subjecct of the group chat. + /// Acction to be executed when the request was successful. /// Action to be executed when the request failed. public void SendCreateGroupChat(string subject, Action onSuccess, Action onError) { @@ -201,10 +201,10 @@ public void SendCreateGroupChat(string subject, Action onSuccess, Action this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Make a reques to the Whatsapp servers to delete a specific account. - /// - /// The action to be executed when the request was successful. + /// + /// Make a reques to the Whatsapp servers to delete a specific account. + /// + /// The action to be executed when the request was successful. /// The action to be executed when the request failed. public void SendDeleteAccount(Action onSuccess, Action onError) { @@ -226,9 +226,9 @@ public void SendDeleteAccount(Action onSuccess, Action onError) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Request to be delete from a group chat. - /// + /// + /// Request to be delete from a group chat. + /// /// public void SendDeleteFromRoster(string jid) { @@ -239,30 +239,30 @@ public void SendDeleteFromRoster(string jid) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Sends the 'Delivered' status to a specific client - /// - /// The JID of the person the chat is with. + /// + /// Sends the 'Delivered' status to a specific client + /// + /// The JID of the person the chat is with. /// The message id. public void SendDeliveredReceiptAck(string to, string id) { this.SendReceiptAck(to, id, "delivered"); } - /// - /// Sends a request to end and remove the group chat. - /// + /// + /// Sends a request to end and remove the group chat. + /// /// The group jabber id. public void SendEndGroupChat(string gjid) { this.SendEndGroupChat(gjid, null, null); - } - - /// - /// Sends a request to end and remove the group chat. - /// - /// The group jabber id. - /// The action to be executed when the request was successful. + } + + /// + /// Sends a request to end and remove the group chat. + /// + /// The group jabber id. + /// The action to be executed when the request was successful. /// The action to be executed when the request failed. public void SendEndGroupChat(string gjid, Action onSuccess, Action onError) { @@ -272,8 +272,8 @@ public void SendEndGroupChat(string gjid, Action onSuccess, Action onError) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Request the client confguration + /// + /// Request the client confguration /// public void SendGetClientConfig() { @@ -283,8 +283,8 @@ public void SendGetClientConfig() this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Request dirty + /// + /// Request dirty /// public void SendGetDirty() { @@ -294,9 +294,9 @@ public void SendGetDirty() this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Send a request to retrieve group information - /// + /// + /// Send a request to retrieve group information + /// /// The group jabber id public void SendGetGroupInfo(string gjid) { @@ -306,10 +306,10 @@ public void SendGetGroupInfo(string gjid) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Make a request to retrieve all groups where the client is participating in - /// - /// The action to be executed when the request was successful. + /// + /// Make a request to retrieve all groups where the client is participating in + /// + /// The action to be executed when the request was successful. /// The action to be executed when the request failed. public void SendGetGroups(Action onSuccess, Action onError) { @@ -317,8 +317,8 @@ public void SendGetGroups(Action onSuccess, Action onError) this.SendGetGroups(id, "participating"); } - /// - /// Make a request to retrieve all groups where the client is the owner of. + /// + /// Make a request to retrieve all groups where the client is the owner of. /// public void SendGetOwningGroups() { @@ -326,9 +326,9 @@ public void SendGetOwningGroups() this.SendGetGroups(id, "owning"); } - /// - /// Make a request to retrieve all group participents - /// + /// + /// Make a request to retrieve all group participents + /// /// The group jabber id public void SendGetParticipants(string gjid) { @@ -338,24 +338,24 @@ public void SendGetParticipants(string gjid) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Make a request to retrieve a photo - /// - /// The group jabber id. + /// + /// Make a request to retrieve a photo + /// + /// The group jabber id. /// If set to true, the photo will be retrieved in the highest resolution. - public void SendGetPhoto(string jid, bool largeFormat) + public string SendGetPhoto(string jid, bool largeFormat) { - this.SendGetPhoto(jid, null, largeFormat, delegate { }); + return this.SendGetPhoto(jid, null, largeFormat, delegate { }); } - /// - /// Make a request to retrieve a photo for a specific photo id. - /// - /// The group jabber id. - /// The specific photo that needs to be retrieved. - /// If set to true, the photo will be retrieved in the highest resolution. + /// + /// Make a request to retrieve a photo for a specific photo id. + /// + /// The group jabber id. + /// The specific photo that needs to be retrieved. + /// If set to true, the photo will be retrieved in the highest resolution. /// The action to be executed when the request was successful. - public void SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, Action onComplete) + public string SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, Action onComplete) { string id = TicketCounter.MakeId("get_photo_"); var attrList = new List { new KeyValue("xmlns", "w:profile:picture") }; @@ -370,11 +370,12 @@ public void SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, A var child = new ProtocolTreeNode("picture", attrList.ToArray()); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid) }, child); this.whatsNetwork.SendData(this._binWriter.Write(node)); + return id; } - /// - /// Make a request to retrieve a list of photo id's - /// + /// + /// Make a request to retrieve a list of photo id's + /// /// The list of jabber id's the photos need to be retrieved of. public void SendGetPhotoIds(IEnumerable jids) { @@ -385,8 +386,8 @@ public void SendGetPhotoIds(IEnumerable jids) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Make a request to retrieve a list of privacy's + /// + /// Make a request to retrieve a list of privacy's /// public void SendGetPrivacyList() { @@ -397,8 +398,8 @@ public void SendGetPrivacyList() this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Make a request to retrieve information (properties) about the server. + /// + /// Make a request to retrieve information (properties) about the server. /// public void SendGetServerProperties() { @@ -408,9 +409,9 @@ public void SendGetServerProperties() this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Make a request to retrieve the status for specific jabber id. - /// + /// + /// Make a request to retrieve the status for specific jabber id. + /// /// The jabber id the the status should be retrieved from. public void SendGetStatus(string jid) { @@ -425,8 +426,8 @@ public void SendGetStatus(string jid) } } - /// - /// Make a request to change our status to inactive. + /// + /// Make a request to change our status to inactive. /// public void SendInactive() { @@ -434,31 +435,31 @@ public void SendInactive() this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Make a request to leave a group chat - /// + /// + /// Make a request to leave a group chat + /// /// The group jabber id public void SendLeaveGroup(string gjid) { this.SendLeaveGroup(gjid, null, null); } - /// - /// Make a rquest to leave a group chat - /// - /// The group jabber id. - /// The action to be executed when the request was successful. + /// + /// Make a rquest to leave a group chat + /// + /// The group jabber id. + /// The action to be executed when the request was successful. /// The action to be executed when the request failed. public void SendLeaveGroup(string gjid, Action onSuccess, Action onError) { this.SendLeaveGroups(new string[] { gjid }, onSuccess, onError); } - /// - /// Make a request to leave multiple groups at the same time. - /// - /// The group jabber id. - /// The action to be executed when the request was successful. + /// + /// Make a request to leave multiple groups at the same time. + /// + /// The group jabber id. + /// The action to be executed when the request was successful. /// The action to be executed when the request failed. public void SendLeaveGroups(IEnumerable gjids, Action onSuccess, Action onError) { @@ -469,9 +470,9 @@ public void SendLeaveGroups(IEnumerable gjids, Action onSuccess, Action< this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Sends a message, message properties are defined in the instance of FMessage. - /// + /// + /// Sends a message, message properties are defined in the instance of FMessage. + /// /// An instance of the FMessage class. public void SendMessage(FMessage message) { @@ -485,9 +486,9 @@ public void SendMessage(FMessage message) } } - /// - /// Tell the server the message has been recieved. - /// + /// + /// Tell the server the message has been recieved. + /// /// An instance of the FMessage class. public void SendMessageReceived(FMessage message) { @@ -496,18 +497,18 @@ public void SendMessageReceived(FMessage message) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Send a null byte to the server + /// + /// Send a null byte to the server /// public void SendNop() { this.whatsNetwork.SendData(this._binWriter.Write(null)); } - /// - /// Send a notification to a specific user that the message has been recieved - /// - /// The jabber id + /// + /// Send a notification to a specific user that the message has been recieved + /// + /// The jabber id /// The id of the message public void SendNotificationReceived(string jid, string id) { @@ -516,9 +517,9 @@ public void SendNotificationReceived(string jid, string id) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Send pause - /// + /// + /// Send pause + /// /// The jabber id of the reciever public void SendPaused(string to) { @@ -527,8 +528,8 @@ public void SendPaused(string to) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Send a ping to the server + /// + /// Send a ping to the server /// public void SendPing() { @@ -538,9 +539,9 @@ public void SendPing() this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Send a pong to a specific user - /// + /// + /// Send a pong to a specific user + /// /// public void SendPong(string id) { @@ -548,9 +549,9 @@ public void SendPong(string id) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Send a subscription request - /// + /// + /// Send a subscription request + /// /// public void SendPresenceSubscriptionRequest(string to) { @@ -558,9 +559,9 @@ public void SendPresenceSubscriptionRequest(string to) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Request to retrieve the LastOnline string - /// + /// + /// Request to retrieve the LastOnline string + /// /// The jabber id public void SendQueryLastOnline(string jid) { @@ -570,10 +571,10 @@ public void SendQueryLastOnline(string jid) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Tell the server wether the platform is replay capable - /// - /// The platform + /// + /// Tell the server wether the platform is replay capable + /// + /// The platform /// Capable or not public void SendRelayCapable(string platform, bool value) { @@ -583,10 +584,10 @@ public void SendRelayCapable(string platform, bool value) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Tell the server the relay was complete - /// - /// The id + /// + /// Tell the server the relay was complete + /// + /// The id /// Miliseconds it took to relay the message public void SendRelayComplete(string id, int millis) { @@ -595,9 +596,9 @@ public void SendRelayComplete(string id, int millis) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Send a relay timeout - /// + /// + /// Send a relay timeout + /// /// The id public void SendRelayTimeout(string id) { @@ -605,24 +606,24 @@ public void SendRelayTimeout(string id) var child = new ProtocolTreeNode("error", new[] { new KeyValue("code", "504"), new KeyValue("type", "wait") }, innerChild); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "error"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); this.whatsNetwork.SendData(this._binWriter.Write(node)); - } - - /// - /// Request to remove participants from a group - /// - /// The group jabber id + } + + /// + /// Request to remove participants from a group + /// + /// The group jabber id /// A list of participants public void SendRemoveParticipants(string gjid, List participants) { this.SendRemoveParticipants(gjid, participants, null, null); - } - - /// - /// Request to remove participants from a group - /// - /// The group jabber id - /// A list of participants - /// Action to execute when the request was successful + } + + /// + /// Request to remove participants from a group + /// + /// The group jabber id + /// A list of participants + /// Action to execute when the request was successful /// Action to execute when the request failed public void SendRemoveParticipants(string gjid, List participants, Action onSuccess, Action onError) { @@ -630,22 +631,22 @@ public void SendRemoveParticipants(string gjid, List participants, Actio this.SendVerbParticipants(gjid, participants, id, "remove"); } - /// - /// Request to set the group subject - /// - /// The group jabber id + /// + /// Request to set the group subject + /// + /// The group jabber id /// The new group subject public void SendSetGroupSubject(string gjid, string subject) { this.SendSetGroupSubject(gjid, subject, null, null); - } - - /// - /// Request to set the group subject - /// - /// The group jabber id - /// The new group subject - /// Action to execute when the request was successful + } + + /// + /// Request to set the group subject + /// + /// The group jabber id + /// The new group subject + /// Action to execute when the request was successful /// Action to execute when the request failed public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, Action onError) { @@ -655,13 +656,13 @@ public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, A this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Change the profile picture - /// - /// The user jabber id - /// The ammount of bytes needed for the photo - /// The amount of bytes needed for the thumbanil - /// Action to execute when the request was successful + /// + /// Change the profile picture + /// + /// The user jabber id + /// The ammount of bytes needed for the photo + /// The amount of bytes needed for the thumbanil + /// Action to execute when the request was successful /// Action to execute when the request failed public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes, Action onSuccess, Action onError) { @@ -675,20 +676,20 @@ public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes, Action this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Set the list of people that have been blocked - /// + /// + /// Set the list of people that have been blocked + /// /// The list of people that have been blocked. public void SendSetPrivacyBlockedList(IEnumerable list) { this.SendSetPrivacyBlockedList(list, null, null); - } - - /// - /// Set the list of people that have been blocked - /// - /// List of jabber id's - /// Action to execute when the request was successful + } + + /// + /// Set the list of people that have been blocked + /// + /// List of jabber id's + /// Action to execute when the request was successful /// Action to execute when the request failed public void SendSetPrivacyBlockedList(IEnumerable jidSet, Action onSuccess, Action onError) { @@ -700,11 +701,11 @@ public void SendSetPrivacyBlockedList(IEnumerable jidSet, Action onSucce this.whatsNetwork.SendData(this._binWriter.Write(node3)); } - /// - /// Send a status update - /// - /// The status - /// Action to execute when the request was successful + /// + /// Send a status update + /// + /// The status + /// Action to execute when the request was successful /// Action to execute when the request failed public void SendStatusUpdate(string status, Action onComplete, Action onError) { @@ -714,10 +715,10 @@ public void SendStatusUpdate(string status, Action onComplete, Action onErr this.whatsNetwork.SendData(this._binWriter.Write(messageNode)); } - /// - /// Tell the server the new subject has been recieved - /// - /// The recipient + /// + /// Tell the server the new subject has been recieved + /// + /// The recipient /// The id public void SendSubjectReceived(string to, string id) { @@ -726,9 +727,9 @@ public void SendSubjectReceived(string to, string id) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Unsubscibe him - /// + /// + /// Unsubscibe him + /// /// The jabber id public void SendUnsubscribeHim(string jid) { @@ -736,9 +737,9 @@ public void SendUnsubscribeHim(string jid) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Unsubscribe me - /// + /// + /// Unsubscribe me + /// /// The jabber id public void SendUnsubscribeMe(string jid) { @@ -746,20 +747,20 @@ public void SendUnsubscribeMe(string jid) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Tell the server the 'visible' status has been acknowledged - /// - /// Recipient + /// + /// Tell the server the 'visible' status has been acknowledged + /// + /// Recipient /// The id public void SendVisibleReceiptAck(string to, string id) { this.SendReceiptAck(to, id, "visible"); } - /// - /// Request to retrieve all groups - /// - /// The id + /// + /// Request to retrieve all groups + /// + /// The id /// The type internal void SendGetGroups(string id, string type) { @@ -768,9 +769,9 @@ internal void SendGetGroups(string id, string type) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Send a message with a body (Plain text); - /// + /// + /// Send a message with a body (Plain text); + /// /// An instance of the FMessage class. internal void SendMessageWithBody(FMessage message) { @@ -778,9 +779,9 @@ internal void SendMessageWithBody(FMessage message) this.whatsNetwork.SendData(this._binWriter.Write(GetMessageNode(message, child))); } - /// - /// Send a message with media (photo/sound/movie) - /// + /// + /// Send a message with media (photo/sound/movie) + /// /// An instance of the FMessage class. internal void SendMessageWithMedia(FMessage message) { @@ -837,12 +838,12 @@ internal void SendMessageWithMedia(FMessage message) this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Send a verb of group participants - /// - /// The group jabber id - /// List of participants - /// The id + /// + /// Send a verb of group participants + /// + /// The group jabber id + /// List of participants + /// The id /// Inner tag internal void SendVerbParticipants(string gjid, IEnumerable participants, string id, string inner_tag) { @@ -852,10 +853,10 @@ internal void SendVerbParticipants(string gjid, IEnumerable participants this.whatsNetwork.SendData(this._binWriter.Write(node)); } - /// - /// Processes group settings - /// - /// A list of instances of the GroupSetting class. + /// + /// Processes group settings + /// + /// A list of instances of the GroupSetting class. /// A list of ProtocolTreeNodes private IEnumerable ProcessGroupSettings(IEnumerable groups) { @@ -872,11 +873,11 @@ private IEnumerable ProcessGroupSettings(IEnumerable - /// Tell the server the reciepient has been acknowledged - /// - /// The reciepient - /// The id + /// + /// Tell the server the reciepient has been acknowledged + /// + /// The reciepient + /// The id /// The receipt type private void SendReceiptAck(string to, string id, string receiptType) { @@ -890,22 +891,22 @@ private void SendReceiptAck(string to, string id, string receiptType) this.whatsNetwork.SendData(this._binWriter.Write(resultNode)); } - /// - /// Get the message node - /// - /// the message - /// The protocol tree node + /// + /// Get the message node + /// + /// the message + /// The protocol tree node /// An instance of the ProtocolTreeNode class. internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode) { return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, pNode); - } - - /// - /// Get the message node - /// - /// the message - /// The protocol tree node + } + + /// + /// Get the message node + /// + /// the message + /// The protocol tree node /// An instance of the ProtocolTreeNode class. public static ProtocolTreeNode GetSubjectMessage(string to, string id, ProtocolTreeNode child) { diff --git a/WhatsAppApi_vs2010.sln b/WhatsAppApi_vs2010.sln index a937448..756f0fd 100644 --- a/WhatsAppApi_vs2010.sln +++ b/WhatsAppApi_vs2010.sln @@ -1,54 +1,54 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsAppApi", "WhatsAppApi\WhatsAppApi.csproj", "{3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsAppPort", "WhatsAppPort\WhatsAppPort.csproj", "{91D9D263-BFB7-4C31-890F-4A42F734670E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsTest", "WhatsTest\WhatsTest.csproj", "{E003A3A5-4ECF-475A-8926-154EF85CF4B7}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|x86.ActiveCfg = Debug|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Any CPU.Build.0 = Release|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|x86.ActiveCfg = Release|Any CPU - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|Any CPU.ActiveCfg = Debug|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|x86.ActiveCfg = Debug|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|x86.Build.0 = Debug|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|Any CPU.ActiveCfg = Release|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|Mixed Platforms.Build.0 = Release|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|x86.ActiveCfg = Release|x86 - {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|x86.Build.0 = Release|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|Any CPU.ActiveCfg = Debug|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|x86.ActiveCfg = Debug|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|x86.Build.0 = Debug|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|Any CPU.ActiveCfg = Release|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|Mixed Platforms.Build.0 = Release|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|x86.ActiveCfg = Release|x86 - {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsAppApi", "WhatsAppApi\WhatsAppApi.csproj", "{3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsAppPort", "WhatsAppPort\WhatsAppPort.csproj", "{91D9D263-BFB7-4C31-890F-4A42F734670E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsTest", "WhatsTest\WhatsTest.csproj", "{E003A3A5-4ECF-475A-8926-154EF85CF4B7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Debug|x86.ActiveCfg = Debug|Any CPU + {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Any CPU.Build.0 = Release|Any CPU + {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}.Release|x86.ActiveCfg = Release|Any CPU + {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|Any CPU.ActiveCfg = Debug|x86 + {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|x86.ActiveCfg = Debug|x86 + {91D9D263-BFB7-4C31-890F-4A42F734670E}.Debug|x86.Build.0 = Debug|x86 + {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|Any CPU.ActiveCfg = Release|x86 + {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|Mixed Platforms.Build.0 = Release|x86 + {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|x86.ActiveCfg = Release|x86 + {91D9D263-BFB7-4C31-890F-4A42F734670E}.Release|x86.Build.0 = Release|x86 + {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|Any CPU.ActiveCfg = Debug|x86 + {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|x86.ActiveCfg = Debug|x86 + {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Debug|x86.Build.0 = Debug|x86 + {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|Any CPU.ActiveCfg = Release|x86 + {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|Mixed Platforms.Build.0 = Release|x86 + {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|x86.ActiveCfg = Release|x86 + {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal From 33ce658c76170e4f1182a6a9533025df897cbaa7 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 6 Jun 2013 15:37:48 +0200 Subject: [PATCH 037/271] Argh! --- .../Response/CorruptStreamException.cs | 4 +- WhatsAppApi/Response/MessageRecvResponse.cs | 76 +++++++++---------- WhatsAppApi/Response/WhatsEventHandler.cs | 76 +++++++++---------- 3 files changed, 78 insertions(+), 78 deletions(-) diff --git a/WhatsAppApi/Response/CorruptStreamException.cs b/WhatsAppApi/Response/CorruptStreamException.cs index 49daad0..faa24c5 100644 --- a/WhatsAppApi/Response/CorruptStreamException.cs +++ b/WhatsAppApi/Response/CorruptStreamException.cs @@ -6,11 +6,11 @@ namespace WhatsAppApi.Response { class CorruptStreamException : Exception - { + { public string EMessage { get; private set; } public CorruptStreamException(string pMessage) { - // TODO: Complete member initialization + // TODO: Complete member initialization this.EMessage = pMessage; } } diff --git a/WhatsAppApi/Response/MessageRecvResponse.cs b/WhatsAppApi/Response/MessageRecvResponse.cs index 090a146..15f32b7 100644 --- a/WhatsAppApi/Response/MessageRecvResponse.cs +++ b/WhatsAppApi/Response/MessageRecvResponse.cs @@ -9,28 +9,28 @@ namespace WhatsAppApi.Response { - /// - /// Respond to a recieved message + /// + /// Respond to a recieved message /// class MessageRecvResponse { - /// - /// An instance of the WhatsSendHandler class + /// + /// An instance of the WhatsSendHandler class /// private WhatsSendHandler sendHandler; - /// - /// Default class constructor - /// + /// + /// Default class constructor + /// /// An instance of the WhatsSendHandler class public MessageRecvResponse(WhatsSendHandler sendHandler) { this.sendHandler = sendHandler; } - /// - /// Parse recieved message - /// + /// + /// Parse recieved message + /// /// TreeNode that contains the recieved message public void ParseMessageRecv(ProtocolTreeNode messageNode) { @@ -66,11 +66,11 @@ public void ParseMessageRecv(ProtocolTreeNode messageNode) } } - /// - /// Notify typing - /// - /// The protocoltreenode - /// From? + /// + /// Notify typing + /// + /// The protocoltreenode + /// From? /// Message id private void TypeNotification(ProtocolTreeNode messageNode, string tmpAttrFrom, string tmpAttrbId) { @@ -91,10 +91,10 @@ private void TypeNotification(ProtocolTreeNode messageNode, string tmpAttrFrom, } } - /// - /// Notify typing picture - /// - /// Child + /// + /// Notify typing picture + /// + /// Child /// From? private static void TypeNotificationPicture(ProtocolTreeNode tmpChild, string tmpFrom) { @@ -115,13 +115,13 @@ private static void TypeNotificationPicture(ProtocolTreeNode tmpChild, string tm } } - /// - /// Notify typing chat - /// - /// - /// - /// - /// + /// + /// Notify typing chat + /// + /// + /// + /// + /// /// private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string tmpAttrbId, FMessage.Builder builder, string tmpAttrFromJid) { @@ -272,13 +272,13 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t } } - /// - /// Type subject - /// - /// - /// - /// - /// + /// + /// Type subject + /// + /// + /// + /// + /// /// private void TypeSubject(ProtocolTreeNode messageNode, string tmpFrom, string uJid, string tmpId, string tmpT) { @@ -302,11 +302,11 @@ private void TypeSubject(ProtocolTreeNode messageNode, string tmpFrom, string uJ } } - /// - /// Type error - /// - /// - /// + /// + /// Type error + /// + /// + /// /// private void TypeError(ProtocolTreeNode messageNode, string tmpAttrbId, string tmpAttrFrom) { diff --git a/WhatsAppApi/Response/WhatsEventHandler.cs b/WhatsAppApi/Response/WhatsEventHandler.cs index de43e16..3a7447f 100644 --- a/WhatsAppApi/Response/WhatsEventHandler.cs +++ b/WhatsAppApi/Response/WhatsEventHandler.cs @@ -7,68 +7,68 @@ namespace WhatsAppApi.Response { - /// - /// Handles events + /// + /// Handles events /// public static class WhatsEventHandler { #region Delegates - /// - /// Event occures when the message has been recieved - /// + /// + /// Event occures when the message has been recieved + /// /// The message that has been recieved public delegate void MessageRecievedHandler(FMessage mess); - /// - /// Handles string arrays - /// + /// + /// Handles string arrays + /// /// A string array public delegate void StringArrayHandler(string[] value); - /// - /// Handles boolean valies - /// - /// Sender + /// + /// Handles boolean valies + /// + /// Sender /// Boolean value public delegate void BoolHandler(string from, bool value); - /// - /// Event occures when somebody changes his profile picture - /// - /// The sender - /// The user id that changed his profile picture + /// + /// Event occures when somebody changes his profile picture + /// + /// The sender + /// The user id that changed his profile picture /// The id of the new photo public delegate void PhotoChangedHandler(string from, string uJid, string photoId); - /// - /// Event occurs when the group subject has changed - /// - /// The changer - /// The uid of the changer - /// The new subject + /// + /// Event occurs when the group subject has changed + /// + /// The changer + /// The uid of the changer + /// The new subject /// ? public delegate void GroupNewSubjectHandler(string from, string uJid, string subject, int t); #endregion - #region Events - /// - /// Event occures when the message has been recieved + #region Events + /// + /// Event occures when the message has been recieved /// - public static event MessageRecievedHandler MessageRecievedEvent; - - /// - /// Handles boolean valies + public static event MessageRecievedHandler MessageRecievedEvent; + + /// + /// Handles boolean valies /// - public static event BoolHandler IsTypingEvent; - - /// - /// Event occurs when the group subject has changed + public static event BoolHandler IsTypingEvent; + + /// + /// Event occurs when the group subject has changed /// - public static event GroupNewSubjectHandler GroupNewSubjectEvent; - - /// - /// Event occures when somebody changes his profile picture + public static event GroupNewSubjectHandler GroupNewSubjectEvent; + + /// + /// Event occures when somebody changes his profile picture /// public static event PhotoChangedHandler PhotoChangedEvent; From 857d420bd121bd3d2ab990231639ab7e9ca5041a Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 7 Jun 2013 12:45:21 +0200 Subject: [PATCH 038/271] Added media upload request --- WhatsAppApi/WhatsApp.cs | 61 ++++++++++++++++++++++++++------- WhatsAppApi/WhatsSendHandler.cs | 5 +++ 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index ce3b85e..24f107c 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -314,19 +314,56 @@ public string GetJID(string target) /// Filename /// The size of the image in string format /// Icon - public void MessageImage(string msgid, string to, string url, string file, string size, string icon) + public void MessageImage(string to, string filepath) { - //var mediaAttribs = new KeyValue[] - // { - // new KeyValue("xmlns", "urn:xmpp:whatsapp:mms"), - // new KeyValue("type", "image"), - // new KeyValue("url", url), - // new KeyValue("file", file), - // new KeyValue("size", size) - // }; - - //var mediaNode = new ProtocolTreeNode("media", mediaAttribs, icon); - //this.SendMessageNode(msgid, to, mediaNode); + FileInfo finfo = new FileInfo(filepath); + string type = string.Empty; + switch (finfo.Extension) + { + case ".png": + type = "image/png"; + break; + case ".gif": + type = "image/gif"; + break; + default: + type = "image/jpeg"; + break; + } + + //create hash + string filehash = string.Empty; + using(FileStream fs = File.OpenRead(filepath)) + { + using(BufferedStream bs = new BufferedStream(fs)) + { + using(HashAlgorithm sha = HashAlgorithm.Create("sha256")) + { + byte[] raw = sha.ComputeHash(bs); + filehash = Convert.ToBase64String(raw); + } + } + } + + //request upload + this.requestFileUpload(filehash, "image", finfo.Length, filepath, to); + } + + private void requestFileUpload(string b64hash, string type, long size, string path, string to) + { + ProtocolTreeNode media = new ProtocolTreeNode("media", new KeyValue[] { + new KeyValue("xmlns", "w:m"), + new KeyValue("hash", b64hash), + new KeyValue("type", type), + new KeyValue("size", size.ToString()) + }); + string id = TicketManager.GenerateId(); + ProtocolTreeNode node = new ProtocolTreeNode("iq", new KeyValue[] { + new KeyValue("id", id), + new KeyValue("to", WhatsConstants.WhatsAppServer), + new KeyValue("type", "set") + }, media); + this.WhatsSendHandler.SendNode(node); } /// diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 4e16868..6734506 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -838,6 +838,11 @@ internal void SendMessageWithMedia(FMessage message) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + public void SendNode(ProtocolTreeNode node) + { + this.whatsNetwork.SendData(this._binWriter.Write(node)); + } + /// /// Send a verb of group participants /// From f7cc05d87e040ee6e3893cefd8688dcf0a904585 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 7 Jun 2013 23:28:11 +0200 Subject: [PATCH 039/271] Fixed continuous receipt replay And some broken code for image upload. Scumbag Max strikes again :') --- WhatsAppApi/WhatsApp.cs | 100 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 24f107c..cda0857 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; +using System.Net.Security; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; @@ -28,6 +30,8 @@ public enum CONNECTION_STATUS CONNECTED, LOGGEDIN } + + private ProtocolTreeNode uploadResponse; /// /// An instance of the AccountInfo class @@ -346,10 +350,10 @@ public void MessageImage(string to, string filepath) } //request upload - this.requestFileUpload(filehash, "image", finfo.Length, filepath, to); + string url = this.UploadFile(filehash, "image", finfo.Length, filepath, to, type); } - private void requestFileUpload(string b64hash, string type, long size, string path, string to) + private string UploadFile(string b64hash, string type, long size, string path, string to, string contenttype) { ProtocolTreeNode media = new ProtocolTreeNode("media", new KeyValue[] { new KeyValue("xmlns", "w:m"), @@ -363,7 +367,86 @@ private void requestFileUpload(string b64hash, string type, long size, string pa new KeyValue("to", WhatsConstants.WhatsAppServer), new KeyValue("type", "set") }, media); + this.uploadResponse = null; this.WhatsSendHandler.SendNode(node); + int i = 0; + while (this.uploadResponse == null && i < 5) + { + i++; + this.PollMessages(); + } + try + { + string uploadUrl = this.uploadResponse.GetChild("media").GetAttribute("url"); + this.uploadResponse = null; + + Uri uri = new Uri(uploadUrl); + + string hashname = string.Empty; + byte[] buff = MD5.Create().ComputeHash(System.Text.Encoding.Default.GetBytes(path)); + StringBuilder sb = new StringBuilder(); + foreach (byte b in buff) + { + sb.Append(b.ToString("X2")); + } + hashname = String.Format("{0}.{1}", sb.ToString(), path.Split('.').Last()); + + string boundary = "zzXXzzYYzzXXzzQQ"; + + sb = new StringBuilder(); + + sb.AppendFormat("--{0}\r\n", boundary); + sb.Append("Content-Disposition: form-data; name=\"to\"\r\n\r\n"); + sb.AppendFormat("{0}\r\n", to); + sb.AppendFormat("--{0}\r\n", boundary); + sb.Append("Content-Disposition: form-data; name=\"from\"\r\n\r\n"); + sb.AppendFormat("{0}\r\n", this.phoneNumber); + sb.AppendFormat("{0}\r\n", boundary); + sb.AppendFormat("Content-Disposition: form-data; name=\"file\"; filename=\"{0}\"\r\n", hashname); + sb.AppendFormat("Content-Type: {0}\r\n\r\n", contenttype); + string header = sb.ToString(); + + sb = new StringBuilder(); + sb.AppendFormat("\r\n--{0}--\r\n", boundary); + string footer = sb.ToString(); + + long clength = size + header.Length + footer.Length; + + sb = new StringBuilder(); + sb.AppendFormat("POST {0}\r\n", uploadUrl); + sb.AppendFormat("Content-Type: multipart/form-data; boundary={0}\r\n", boundary); + sb.AppendFormat("Host: {0}\r\n", uri.Host); + sb.AppendFormat("User-Agent: {0}\r\n", WhatsConstants.UserAgent); + sb.AppendFormat("Content-Length: {0}\r\n\r\n", clength); + string post = sb.ToString(); + + TcpClient tc = new TcpClient(uri.Host, 443); + SslStream ssl = new SslStream(tc.GetStream()); + try + { + ssl.AuthenticateAsClient(uri.Host); + } + catch (Exception e) + { + throw e; + } + byte[] msgdata = Encoding.ASCII.GetBytes(post); + ssl.Write(msgdata); + msgdata = Encoding.ASCII.GetBytes(header); + ssl.Write(msgdata); + buff = File.ReadAllBytes(path); + ssl.Write(buff); + msgdata = Encoding.ASCII.GetBytes(footer); + ssl.Write(msgdata); + + //moment of truth... + ssl.Read(buff, 0, buff.Length); + string json = System.Text.Encoding.ASCII.GetString(buff); + return json; + } + catch (Exception e) + { } + return null; } /// @@ -523,7 +606,10 @@ protected void processInboundData(byte[] data) if (ProtocolTreeNode.TagEquals(node,"message")) { this.AddMessage(node); - this.sendMessageReceived(node); + if (node.GetChild("received") == null) + { + this.sendMessageReceived(node); + } } if (ProtocolTreeNode.TagEquals(node,"stream:error")) { @@ -537,6 +623,14 @@ protected void processInboundData(byte[] data) //last seen this.AddMessage(node); } + if (ProtocolTreeNode.TagEquals(node, "iq") + && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.First(), "media") + ) + { + //media upload + this.uploadResponse = node; + } if (ProtocolTreeNode.TagEquals(node, "iq") && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) && ProtocolTreeNode.TagEquals(node.children.First(), "picture") From 416bcd084f60459cb1b91934b17168812290b00e Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 7 Jun 2013 23:46:07 +0200 Subject: [PATCH 040/271] Updated useragent and token Again big thanks to Jannik Vogel for helping me find it :) --- WhatsAppApi/Settings/WhatsConstants.cs | 56 +++++++++++++------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index 4cbe9da..d489ef2 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -6,73 +6,73 @@ namespace WhatsAppApi.Settings { - /// - /// Holds constant information used to connect to whatsapp server + /// + /// Holds constant information used to connect to whatsapp server /// class WhatsConstants { #region ServerConstants - /// - /// The whatsapp digest + /// + /// The whatsapp digest /// public const string WhatsAppDigest = "xmpp/s.whatsapp.net"; - /// - /// The whatsapp host + /// + /// The whatsapp host /// public const string WhatsAppHost = "bin-short.whatsapp.net"; - /// - /// The whatsapp XMPP realm + /// + /// The whatsapp XMPP realm /// public const string WhatsAppRealm = "s.whatsapp.net"; - /// - /// The whatsapp server + /// + /// The whatsapp server /// public const string WhatsAppServer = "s.whatsapp.net"; - /// - /// The whatsapp group chat server + /// + /// The whatsapp group chat server /// public const string WhatsGroupChat = "g.us"; - /// - /// The whatsapp version the client complies to + /// + /// The whatsapp version the client complies to /// public const string WhatsAppVer = "2.8.7"; - /// - /// The port that needs to be connected to + /// + /// The port that needs to be connected to /// public const int WhatsPort = 5222; - /// - /// iPhone device + /// + /// iPhone device /// public const string IphoneDevice = "iPhone"; - /// - /// The useragent used for http requests + /// + /// The useragent used for http requests /// - public const string UserAgend = "WhatsApp/2.8.7 iPhone_OS/6.1.0 Device/iPhone_4S"; + public const string UserAgent = "WhatsApp/2.9.4 WP7/7.10.8858 Device/HTC-HTC-H0002"; - /// - /// The whatsapp build hash + /// + /// The whatsapp build hash /// - public const string WhatsBuildHash = "889d4f44e479e6c38b4a834c6d8417815f999abe"; + public const string WhatsBuildHash = "Od52pFozHNWF9XbTN5lrqDtnsiZGL2G3l9yw1GiQ21a31a2d9dbdc9a8ce324ef2df918064fd26e30a"; #endregion #region ParserConstants - /// - /// The number style used + /// + /// The number style used /// public static NumberStyles WhatsAppNumberStyle = (NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign); - /// - /// Unix epoch DateTime + /// + /// Unix epoch DateTime /// public static DateTime UnixEpoch = new DateTime(0x7b2, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); #endregion From 6d566ad2ac437f7681de3bc27477057ad1810956 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 7 Jun 2013 23:46:54 +0200 Subject: [PATCH 041/271] LINE BREAKS WAT R U DOIN? STAHP! --- WhatsAppApi/Register/WhatsRegister.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegister.cs b/WhatsAppApi/Register/WhatsRegister.cs index 3e5112d..7519b55 100644 --- a/WhatsAppApi/Register/WhatsRegister.cs +++ b/WhatsAppApi/Register/WhatsRegister.cs @@ -18,7 +18,7 @@ public static bool RegisterUser(string countryCode, string phoneNumber) string postData = GetRegString(countryCode, phoneNumber); string both = website + "?" + postData; - var result = StartWebRequest("", "", WhatsConstants.UserAgend, both); + var result = StartWebRequest("", "", WhatsConstants.UserAgent, both); Console.WriteLine(result); return result.Contains("status=\"success-sent\""); } @@ -28,7 +28,7 @@ public static bool VerifyRegistration(string countryCode, string phoneNumber, st string tmpPassword = password.ToPassword(); string verifyString = string.Format("https://r.whatsapp.net/v1/register.php?cc={0}&in={1}&udid={2}&code={3}", new object[] { countryCode, phoneNumber, tmpPassword, code }); - var result = StartWebRequest("", "", WhatsConstants.UserAgend, verifyString); + var result = StartWebRequest("", "", WhatsConstants.UserAgent, verifyString); Console.WriteLine(result); return true; } @@ -41,7 +41,7 @@ public static bool ExistsAndDelete(string countrycode, string phone, string pass webString = webString + string.Format("&udid={0}", pass.ToPassword()); } - var result = StartWebRequest("", "", WhatsConstants.UserAgend, webString); + var result = StartWebRequest("", "", WhatsConstants.UserAgent, webString); return result.Contains("status=\"ok\""); } From 0de42b31ffaf15fff79c7907d0d0981700572602 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 10 Jun 2013 18:06:14 +0200 Subject: [PATCH 042/271] Updated chat host --- WhatsAppApi/Settings/WhatsConstants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index d489ef2..dbf95e8 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -21,7 +21,7 @@ class WhatsConstants /// /// The whatsapp host /// - public const string WhatsAppHost = "bin-short.whatsapp.net"; + public const string WhatsAppHost = "c2.whatsapp.net"; /// /// The whatsapp XMPP realm From a4436ed5c7cd1263baddfb7e5e76a213aba85fdc Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 10 Jun 2013 18:06:31 +0200 Subject: [PATCH 043/271] Updated media sending --- WhatsAppApi/WhatsSendHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 6734506..3540d4e 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -835,7 +835,7 @@ internal void SendMessageWithMedia(FMessage message) } node = new ProtocolTreeNode("media", list.ToArray(), null, data); } - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this._binWriter.Write(GetMessageNode(message, node))); } public void SendNode(ProtocolTreeNode node) From e3abe97c3ad1e92e681035be902529e25f7bff34 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 10 Jun 2013 18:07:02 +0200 Subject: [PATCH 044/271] Partially fixed media upload --- WhatsAppApi/WhatsApp.cs | 200 +++++++++++++++++++++++++++------------- 1 file changed, 135 insertions(+), 65 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index cda0857..0ab5438 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -7,6 +7,7 @@ using System.Net.Sockets; using System.Security.Cryptography; using System.Text; +using System.Web.Script.Serialization; using WhatsAppApi.Helper; using WhatsAppApi.Parser; using WhatsAppApi.Response; @@ -320,6 +321,7 @@ public string GetJID(string target) /// Icon public void MessageImage(string to, string filepath) { + to = this.GetJID(to); FileInfo finfo = new FileInfo(filepath); string type = string.Empty; switch (finfo.Extension) @@ -350,10 +352,18 @@ public void MessageImage(string to, string filepath) } //request upload - string url = this.UploadFile(filehash, "image", finfo.Length, filepath, to, type); + UploadResponse response = this.UploadFile(filehash, "image", finfo.Length, filepath, to, type); + + if (response != null && !String.IsNullOrEmpty(response.url)) + { + //send message + FMessage msg = new FMessage(to, true) { key = { id = TicketManager.GenerateId() }, media_wa_type = FMessage.Type.Image, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url }; + this.WhatsSendHandler.SendMessage(msg); + } + } - private string UploadFile(string b64hash, string type, long size, string path, string to, string contenttype) + private UploadResponse UploadFile(string b64hash, string type, long size, string path, string to, string contenttype) { ProtocolTreeNode media = new ProtocolTreeNode("media", new KeyValue[] { new KeyValue("xmlns", "w:m"), @@ -375,80 +385,140 @@ private string UploadFile(string b64hash, string type, long size, string path, s i++; this.PollMessages(); } - try + if (this.uploadResponse != null && this.uploadResponse.GetChild("duplicate") != null) { - string uploadUrl = this.uploadResponse.GetChild("media").GetAttribute("url"); + UploadResponse res = new UploadResponse(this.uploadResponse); this.uploadResponse = null; - - Uri uri = new Uri(uploadUrl); - - string hashname = string.Empty; - byte[] buff = MD5.Create().ComputeHash(System.Text.Encoding.Default.GetBytes(path)); - StringBuilder sb = new StringBuilder(); - foreach (byte b in buff) - { - sb.Append(b.ToString("X2")); - } - hashname = String.Format("{0}.{1}", sb.ToString(), path.Split('.').Last()); - - string boundary = "zzXXzzYYzzXXzzQQ"; - - sb = new StringBuilder(); - - sb.AppendFormat("--{0}\r\n", boundary); - sb.Append("Content-Disposition: form-data; name=\"to\"\r\n\r\n"); - sb.AppendFormat("{0}\r\n", to); - sb.AppendFormat("--{0}\r\n", boundary); - sb.Append("Content-Disposition: form-data; name=\"from\"\r\n\r\n"); - sb.AppendFormat("{0}\r\n", this.phoneNumber); - sb.AppendFormat("{0}\r\n", boundary); - sb.AppendFormat("Content-Disposition: form-data; name=\"file\"; filename=\"{0}\"\r\n", hashname); - sb.AppendFormat("Content-Type: {0}\r\n\r\n", contenttype); - string header = sb.ToString(); - - sb = new StringBuilder(); - sb.AppendFormat("\r\n--{0}--\r\n", boundary); - string footer = sb.ToString(); - - long clength = size + header.Length + footer.Length; - - sb = new StringBuilder(); - sb.AppendFormat("POST {0}\r\n", uploadUrl); - sb.AppendFormat("Content-Type: multipart/form-data; boundary={0}\r\n", boundary); - sb.AppendFormat("Host: {0}\r\n", uri.Host); - sb.AppendFormat("User-Agent: {0}\r\n", WhatsConstants.UserAgent); - sb.AppendFormat("Content-Length: {0}\r\n\r\n", clength); - string post = sb.ToString(); - - TcpClient tc = new TcpClient(uri.Host, 443); - SslStream ssl = new SslStream(tc.GetStream()); + return res; + } + else + { try { - ssl.AuthenticateAsClient(uri.Host); + string uploadUrl = this.uploadResponse.GetChild("media").GetAttribute("url"); + this.uploadResponse = null; + + Uri uri = new Uri(uploadUrl); + + string hashname = string.Empty; + byte[] buff = MD5.Create().ComputeHash(System.Text.Encoding.Default.GetBytes(path)); + StringBuilder sb = new StringBuilder(); + foreach (byte b in buff) + { + sb.Append(b.ToString("X2")); + } + hashname = String.Format("{0}.{1}", sb.ToString(), path.Split('.').Last()); + + string boundary = "zzXXzzYYzzXXzzQQ"; + + sb = new StringBuilder(); + + sb.AppendFormat("--{0}\r\n", boundary); + sb.Append("Content-Disposition: form-data; name=\"to\"\r\n\r\n"); + sb.AppendFormat("{0}\r\n", to); + sb.AppendFormat("--{0}\r\n", boundary); + sb.Append("Content-Disposition: form-data; name=\"from\"\r\n\r\n"); + sb.AppendFormat("{0}\r\n", this.phoneNumber); + sb.AppendFormat("--{0}\r\n", boundary); + sb.AppendFormat("Content-Disposition: form-data; name=\"file\"; filename=\"{0}\"\r\n", hashname); + sb.AppendFormat("Content-Type: {0}\r\n\r\n", contenttype); + string header = sb.ToString(); + + sb = new StringBuilder(); + sb.AppendFormat("\r\n--{0}--\r\n", boundary); + string footer = sb.ToString(); + + long clength = size + header.Length + footer.Length; + + sb = new StringBuilder(); + sb.AppendFormat("POST {0}\r\n", uploadUrl); + sb.AppendFormat("Content-Type: multipart/form-data; boundary={0}\r\n", boundary); + sb.AppendFormat("Host: {0}\r\n", uri.Host); + sb.AppendFormat("User-Agent: {0}\r\n", WhatsConstants.UserAgent); + sb.AppendFormat("Content-Length: {0}\r\n\r\n", clength); + string post = sb.ToString(); + + TcpClient tc = new TcpClient(uri.Host, 443); + SslStream ssl = new SslStream(tc.GetStream()); + try + { + ssl.AuthenticateAsClient(uri.Host); + } + catch (Exception e) + { + throw e; + } + + //HttpWebRequest req = WebRequest.Create(uploadUrl) as HttpWebRequest; + //set post headers + //req.ContentType = String.Format("multipart/form-data; boundary={0}\r\n", boundary); + //req.UserAgent = WhatsConstants.UserAgent; + //Stream reqStream = req.GetRequestStream(); + + List buf = new List(); + buf.AddRange(Encoding.UTF8.GetBytes(post)); + buf.AddRange(Encoding.UTF8.GetBytes(header)); + buf.AddRange(File.ReadAllBytes(path)); + buf.AddRange(Encoding.UTF8.GetBytes(footer)); + + ssl.Write(buf.ToArray(), 0, buf.ToArray().Length); + + //moment of truth... + buff = new byte[1024]; + ssl.Read(buff, 0, 1024); + + string result = Encoding.UTF8.GetString(buff); + foreach (string line in result.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries)) + { + if (line.StartsWith("{")) + { + string fooo = line.TrimEnd(new char[] { (char)0 }); + JavaScriptSerializer jss = new JavaScriptSerializer(); + UploadResponse resp = jss.Deserialize(fooo); + if (!String.IsNullOrEmpty(resp.url)) + { + return resp; + } + } + } } catch (Exception e) { - throw e; + //Console.WriteLine(e.Message); } - byte[] msgdata = Encoding.ASCII.GetBytes(post); - ssl.Write(msgdata); - msgdata = Encoding.ASCII.GetBytes(header); - ssl.Write(msgdata); - buff = File.ReadAllBytes(path); - ssl.Write(buff); - msgdata = Encoding.ASCII.GetBytes(footer); - ssl.Write(msgdata); - - //moment of truth... - ssl.Read(buff, 0, buff.Length); - string json = System.Text.Encoding.ASCII.GetString(buff); - return json; } - catch (Exception e) - { } return null; } + public class UploadResponse + { + public string url { get; set; } + public string mimetype { get; set; } + public int size { get; set; } + public string filehash { get; set; } + public string type { get; set; } + public int width { get; set; } + public int height { get; set; } + + public UploadResponse() + { } + + public UploadResponse(ProtocolTreeNode node) + { + node = node.GetChild("duplicate"); + if (node != null) + { + this.url = node.GetAttribute("url"); + this.mimetype = node.GetAttribute("mimetype"); + this.size = Int32.Parse(node.GetAttribute("size")); + this.filehash = node.GetAttribute("filehash"); + this.type = node.GetAttribute("type"); + this.width = Int32.Parse(node.GetAttribute("width")); + this.height = Int32.Parse(node.GetAttribute("height")); + } + } + } + /// /// Retrieve messages from the server /// @@ -625,7 +695,7 @@ protected void processInboundData(byte[] data) } if (ProtocolTreeNode.TagEquals(node, "iq") && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "media") + && (ProtocolTreeNode.TagEquals(node.children.First(), "media") || ProtocolTreeNode.TagEquals(node.children.First(), "duplicate")) ) { //media upload From f17e0a7f7adf829c73ecc5e57fb66ac81ffcd1ec Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 10 Jun 2013 18:07:42 +0200 Subject: [PATCH 045/271] Deleted output --- WhatsAppApi/bin/Debug/WhatsAppApi.dll | Bin 70656 -> 0 bytes WhatsAppApi/bin/Debug/WhatsAppApi.pdb | Bin 185856 -> 0 bytes ...ignTimeResolveAssemblyReferencesInput.cache | Bin 5901 -> 0 bytes .../WhatsAppApi.csproj.FileListAbsolute.txt | 5 ----- WhatsAppApi/obj/Debug/WhatsAppApi.dll | Bin 70656 -> 0 bytes WhatsAppApi/obj/Debug/WhatsAppApi.pdb | Bin 185856 -> 0 bytes ...ignTimeResolveAssemblyReferencesInput.cache | Bin 6289 -> 0 bytes ...ignTimeResolveAssemblyReferencesInput.cache | Bin 5971 -> 0 bytes 8 files changed, 5 deletions(-) delete mode 100644 WhatsAppApi/bin/Debug/WhatsAppApi.dll delete mode 100644 WhatsAppApi/bin/Debug/WhatsAppApi.pdb delete mode 100644 WhatsAppApi/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache delete mode 100644 WhatsAppApi/obj/Debug/WhatsAppApi.csproj.FileListAbsolute.txt delete mode 100644 WhatsAppApi/obj/Debug/WhatsAppApi.dll delete mode 100644 WhatsAppApi/obj/Debug/WhatsAppApi.pdb delete mode 100644 WhatsAppPort/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache delete mode 100644 WhatsTest/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache diff --git a/WhatsAppApi/bin/Debug/WhatsAppApi.dll b/WhatsAppApi/bin/Debug/WhatsAppApi.dll deleted file mode 100644 index 16cd000b3c97fbbdca02d78e381206537c90174c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70656 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 XxcKrSVz<)qdaR~eubTuIG diff --git a/WhatsAppApi/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/WhatsAppApi/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache deleted file mode 100644 index 9bca4d5c8d8b284793c545f123add329f0aa9a01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5901 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 XxcKrSVz<)qdaR~eubTuIG diff --git a/WhatsAppPort/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/WhatsAppPort/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache deleted file mode 100644 index e1e316ac9009dc8793453fc424b208512ad24504..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6289 zcmeHLZBrXJ5VpZ67(yGKGQ%-MzLSm0`E-?Z z7?W@Zn$l?hb*l-*HO{B`Vt3 z;>xE^QTsL*Mn=?X!rjQOC*oU7DlXjBRMH5367h`hXkW5bm-$*!ClSy4Wrth8vPMZ9 zFn6__oUf!SOO;g0PABO+9lwSWiB`T{-cZL=HvSNA@K-4o{b%kVER-s^u4OeNOg%+<<#&Gj3aQeOAt#6ObO?(!xIXu!X$Gc1{oH$_F42ZT*2ocrq3$brbDPKxd-i&XWHXI zeGKX&L1_|7!!SSj0t#b?a}XTT$)&la=zaJfqy#eo0~v`ho7FpErU2!6@{J`@zlBS2mOZ?*$xu;}TQW-v(=Xj(jAZSh&K+jFzCGx3&dpbA|VIt30pVs;|AWC8LFAXh-M zfgxw9rUnYILYIaHbA1JKtzOLvEp-D>-2KM@k?9YDd5&n{nFw@w0zk`s0ihaS!{}f7 z8l9E24!#lS5C@c8UnngwV1$5WWF~_kb`kPe93iJ|v%=xy|(W`|XpF(l-eZX+*1L~@YE3L`fU82cH!B3Z{T z77%tQkOYW_$C(Q=XeZ=HXq`NM!(EN3q@09=9h~?RVnF@$ap~fGQR9UC1nq-(1SA1x z;FghXe diff --git a/WhatsTest/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/WhatsTest/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache deleted file mode 100644 index e24cff6f12b82e831369ab5054e4067e21baeb19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5971 zcmeHLaZejJ6i*$BB`Vt7 zD&s_)STlX}RqP`_*z0RarpmN=-`&vzb@&oF+ zqQRUMrC6=(HrEa{^Ux|a0i#!iSK-whciEIpJ%>9~v(89tRWlXeb#s!|SVPGDPFIPR zq3`agzMOC!SnnfjEKylyI#etAWySOcfT$IuoRtU|#R>z>kJU)Q=8@Ux2twcuJq z@A2~?9)8q*Qp<2*ol2iEGs`8jwUC^tJ9S4>S!7h&dwHt&)OcNr3OEE8F)3Mm(@nMRwd!Q?J7hJNj1yuG1cb{giIkm3}P%E!|DhMIB6sC zTOhD`6#t2%7z07ZK}5)H5U-93?L4zo1eVdDl35`Wu*{9MQkYB3f{2-p>k{NTF_Rs#MBr!aya(c! zD=i+Fk!c6ih};K|B>>sto+BEn`vuT^EBpdh*F>w6*aEvAfPM+|Axl?*Mi~LCGG<|E zPp!~-M*GtN^XN`+5*(ang_%~e0C^gaEB!>yQcX=1MW(wnF_>!x=32g-7ux0qp!k8l z0*J*cL7?_S(Ej;K+V5*_u!Ki|@V=k!Lg2UD`y)XA8qo8AeyNSNQm?PnxmD({D}!y; z+KNFdqQ>yF7^JxC$U=h@*Dvb5gNHHMg^Nc`jqED^CmY>8XkC1v=bu=3Y<+x(7+etW z%Kyo`%yq>54QF55JENm<0|Kg363CVC7$lEDto7ymCf@+mY%j?K(=Pe8PZOss@}&2P zVf1$}+U&*5kR4eGWR>`4M7{?R%fVYr6bWWPI}NCGN&*=tmPEihp+aUs+&@}9_ZjGz zJcZuLqc_~s_#Tpz@ahCR{+K+2_UWV2`HvpWWAX#^_k+nM0cYZBh0M&bZrTR26L`=H(iDC2{jJ|AqA^pa3%Rt$A5FP$|aLoVT O%kJQq4`1tc#{UC9kF!qz From 7c9b1349d7fa183eab1e75f16ac7724e8ec6f65d Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 10 Jun 2013 19:16:00 +0200 Subject: [PATCH 046/271] Added image thumbnails --- WhatsAppApi/WhatsApp.cs | 42 +++++++++++++++++++++++++++++++++- WhatsAppApi/WhatsAppApi.csproj | 1 + 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 0ab5438..1d40f6a 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Linq; using System.Net; @@ -357,12 +358,51 @@ public void MessageImage(string to, string filepath) if (response != null && !String.IsNullOrEmpty(response.url)) { //send message - FMessage msg = new FMessage(to, true) { key = { id = TicketManager.GenerateId() }, media_wa_type = FMessage.Type.Image, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url }; + FMessage msg = new FMessage(to, true) { key = { id = TicketManager.GenerateId() }, media_wa_type = FMessage.Type.Image, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url, binary_data = this.CreateThumbnail(filepath) }; this.WhatsSendHandler.SendMessage(msg); } } + private byte[] CreateThumbnail(string path) + { + if (File.Exists(path)) + { + Image orig = Image.FromFile(path); + if (orig != null) + { + int newHeight = 0; + int newWidth = 0; + float imgWidth = float.Parse(orig.Width.ToString()); + float imgHeight = float.Parse(orig.Height.ToString()); + if (orig.Width > orig.Height) + { + newHeight = (int)((imgHeight / imgWidth) * 100); + newWidth = 100; + } + else + { + newWidth = (int)((imgWidth / imgHeight) * 100); + newHeight = 100; + } + + Bitmap newImage = new Bitmap(newWidth, newHeight); + using(Graphics gr = Graphics.FromImage(newImage)) + { + gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; + gr.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; + gr.DrawImage(orig, new Rectangle(0, 0, newWidth, newHeight)); + } + MemoryStream ms = new MemoryStream(); + newImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); + ms.Close(); + return ms.ToArray(); + } + } + return null; + } + private UploadResponse UploadFile(string b64hash, string type, long size, string path, string to, string contenttype) { ProtocolTreeNode media = new ProtocolTreeNode("media", new KeyValue[] { diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index 3841ee8..1674157 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -37,6 +37,7 @@ + From 42081b7cae15e3f2b76d768ba29f47816a18adee Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 11 Jun 2013 09:45:00 +0200 Subject: [PATCH 047/271] Cleanup --- WhatsAppApi/WhatsApp.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 1d40f6a..febd8c1 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -489,12 +489,6 @@ private UploadResponse UploadFile(string b64hash, string type, long size, string throw e; } - //HttpWebRequest req = WebRequest.Create(uploadUrl) as HttpWebRequest; - //set post headers - //req.ContentType = String.Format("multipart/form-data; boundary={0}\r\n", boundary); - //req.UserAgent = WhatsConstants.UserAgent; - //Stream reqStream = req.GetRequestStream(); - List buf = new List(); buf.AddRange(Encoding.UTF8.GetBytes(post)); buf.AddRange(Encoding.UTF8.GetBytes(header)); @@ -523,9 +517,7 @@ private UploadResponse UploadFile(string b64hash, string type, long size, string } } catch (Exception e) - { - //Console.WriteLine(e.Message); - } + { } } return null; } From a0d2651fd60690ed8e4de55ab61e01115d359a5f Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 13 Jun 2013 15:07:15 +0200 Subject: [PATCH 048/271] Removed node data from debug output Caused unexpected crashes --- WhatsAppApi/Helper/ProtocolTreeNode.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/Helper/ProtocolTreeNode.cs b/WhatsAppApi/Helper/ProtocolTreeNode.cs index 6aa70d1..5ff2f8d 100644 --- a/WhatsAppApi/Helper/ProtocolTreeNode.cs +++ b/WhatsAppApi/Helper/ProtocolTreeNode.cs @@ -51,10 +51,10 @@ public string NodeString(string indent = "") } } ret += ">"; - if (this.data.Length > 0) - { - ret += WhatsApp.SYSEncoding.GetString(this.data); - } + //if (this.data.Length > 0) + //{ + // ret += WhatsApp.SYSEncoding.GetString(this.data); + //} if (this.children != null && this.children.Count() > 0) { foreach (var item in this.children) From 46099220a2e2f4cab2459b3b47236033071eb1c1 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 13 Jun 2013 15:08:55 +0200 Subject: [PATCH 049/271] Added debug check --- WhatsAppApi/Register/WhatsRegister.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/Register/WhatsRegister.cs b/WhatsAppApi/Register/WhatsRegister.cs index 7519b55..55ec373 100644 --- a/WhatsAppApi/Register/WhatsRegister.cs +++ b/WhatsAppApi/Register/WhatsRegister.cs @@ -19,7 +19,10 @@ public static bool RegisterUser(string countryCode, string phoneNumber) string both = website + "?" + postData; var result = StartWebRequest("", "", WhatsConstants.UserAgent, both); - Console.WriteLine(result); + if (WhatsApp.DEBUG && result.Length > 0) + { + Console.WriteLine(result); + } return result.Contains("status=\"success-sent\""); } From e6a4f013b8defac01a6879fddd2d8f81581747eb Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 13 Jun 2013 15:13:35 +0200 Subject: [PATCH 050/271] Fixes Fixed clearing incompleteBytes buffer on reconnect Fixed nullReference exception if data is null Added picture subscription Fixed some formatting --- WhatsAppApi/WhatsApp.cs | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index febd8c1..78da945 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -33,6 +33,11 @@ public enum CONNECTION_STATUS LOGGEDIN } + public void ClearIncomplete() + { + this._incompleteBytes.Clear(); + } + private ProtocolTreeNode uploadResponse; /// @@ -638,8 +643,10 @@ protected ProtocolTreeNode addAuthResponse() protected ProtocolTreeNode addFeatures() { var child = new ProtocolTreeNode("receipt_acks", null); + var child2 = new ProtocolTreeNode("w:profile:picture", new KeyValue[] { new KeyValue("type", "all") }); var childList = new List(); childList.Add(child); + childList.Add(child2); var parent = new ProtocolTreeNode("stream:features", null, childList, null); return parent; } @@ -674,12 +681,18 @@ protected void processInboundData(byte[] data) try { List foo = new List(); - foreach (IncompleteMessageException e in this._incompleteBytes) + if (this._incompleteBytes.Count > 0) { - foo.AddRange(e.getInput()); + foreach (IncompleteMessageException e in this._incompleteBytes) + { + foo.AddRange(e.getInput()); + } + this._incompleteBytes.Clear(); + } + if (data != null) + { + foo.AddRange(data); } - this._incompleteBytes = new List(); - foo.AddRange(data); ProtocolTreeNode node = this.reader.nextTree(foo.ToArray()); while (node != null) { @@ -692,8 +705,8 @@ protected void processInboundData(byte[] data) if (ProtocolTreeNode.TagEquals(node, "challenge")) { this.processChallenge(node); - } - else if (ProtocolTreeNode.TagEquals(node,"success")) + } + else if (ProtocolTreeNode.TagEquals(node, "success")) { this.loginStatus = CONNECTION_STATUS.LOGGEDIN; this.accountinfo = new AccountInfo(node.GetAttribute("status"), @@ -701,11 +714,11 @@ protected void processInboundData(byte[] data) node.GetAttribute("creation"), node.GetAttribute("expiration")); } - else if (ProtocolTreeNode.TagEquals(node,"failure")) + else if (ProtocolTreeNode.TagEquals(node, "failure")) { this.loginStatus = CONNECTION_STATUS.UNAUTHORIZED; } - if (ProtocolTreeNode.TagEquals(node,"message")) + if (ProtocolTreeNode.TagEquals(node, "message")) { this.AddMessage(node); if (node.GetChild("received") == null) @@ -713,7 +726,7 @@ protected void processInboundData(byte[] data) this.sendMessageReceived(node); } } - if (ProtocolTreeNode.TagEquals(node,"stream:error")) + if (ProtocolTreeNode.TagEquals(node, "stream:error")) { Console.Write(node.NodeString()); } @@ -741,13 +754,13 @@ protected void processInboundData(byte[] data) //profile picture this.AddMessage(node); } - if (ProtocolTreeNode.TagEquals(node,"iq") + if (ProtocolTreeNode.TagEquals(node, "iq") && node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) && ProtocolTreeNode.TagEquals(node.children.First(), "ping")) { this.Pong(node.GetAttribute("id")); } - if (ProtocolTreeNode.TagEquals(node ,"stream:error")) + if (ProtocolTreeNode.TagEquals(node, "stream:error")) { var textNode = node.GetChild("text"); if (textNode != null) From 1ca9a210e16be402ae722c20470f14720d7e4195 Mon Sep 17 00:00:00 2001 From: mainlyer Date: Fri, 14 Jun 2013 20:01:42 +0300 Subject: [PATCH 051/271] Methods to request and register a code for WhatsApp users. --- WhatsAppApi/Register/WhatsRegisterV2 | 131 +++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 WhatsAppApi/Register/WhatsRegisterV2 diff --git a/WhatsAppApi/Register/WhatsRegisterV2 b/WhatsAppApi/Register/WhatsRegisterV2 new file mode 100644 index 0000000..b6d9bde --- /dev/null +++ b/WhatsAppApi/Register/WhatsRegisterV2 @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using WhatsAppApi.Settings; + +namespace WhatsAppApi.Register +{ + public static class WhatsRegisterV2 + { + public static bool RequestCode(string countryCode, string phoneNumber, string method = "sms") + { + try + { + string language, locale; + CultureInfo.CurrentCulture.GetLanguageAndLocale(out language, out locale); + string id = phoneNumber.Reverse().ToMD5String(); + string token = string.Concat(WhatsConstants.WhatsBuildHash, phoneNumber).ToMD5String(); + string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc=000&mnc=000&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token); + return (GetResponse(uri).GetJsonValue("status") == "sent"); + } + catch + { + return false; + } + } + + public static string RegisterCode(string countryCode, string phoneNumber, string code) + { + try + { + string id = phoneNumber.Reverse().ToMD5String(); + string uri = string.Format("https://v.whatsapp.net/v2/register?cc={0}&in={1}&id={2}&code={3}", countryCode, phoneNumber, id, code); + if (GetResponse(uri).GetJsonValue("status") == "ok") + { + return GetResponse(uri).GetJsonValue("pw"); + } + return null; + } + catch + { + return null; + } + } + + private static string GetResponse(string uri) + { + var request = HttpWebRequest.CreateHttp(new Uri(uri)); + request.KeepAlive = false; + request.Date = DateTime.Now; + request.UserAgent = WhatsConstants.UserAgent; + request.Accept = "text/json"; + using (var reader = new StreamReader(request.GetResponse().GetResponseStream())) + { + return reader.ReadLine(); + } + } + + private static string ToMD5String(this IEnumerable s) + { + return new string(s.ToArray()).ToMD5String(); + } + + private static string ToMD5String(this string s) + { + return string.Join(string.Empty, MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(s)).Select(item => item.ToString("x2")).ToArray()); + } + + private static void GetLanguageAndLocale(this CultureInfo self, out string language, out string locale) + { + string name = self.Name; + int n1 = name.IndexOf('-'); + if (n1 > 0) + { + int n2 = name.LastIndexOf('-'); + language = name.Substring(0, n1); + locale = name.Substring(n2 + 1); + } + else + { + language = name; + switch (language) + { + case "cs": + locale = "CZ"; + return; + + case "da": + locale = "DK"; + return; + + case "el": + locale = "GR"; + return; + + case "ja": + locale = "JP"; + return; + + case "ko": + locale = "KR"; + return; + + case "sv": + locale = "SE"; + return; + + case "sr": + locale = "RS"; + return; + } + locale = language.ToUpper(); + } + } + + private static string GetJsonValue(this string s, string parameter) + { + Match match; + if ((match = Regex.Match(s, string.Format("\"?{0}\"?:\"(?.+?)\"", parameter), RegexOptions.Singleline | RegexOptions.IgnoreCase)).Success) + { + return match.Groups["Value"].Value; + } + return null; + } + } +} From 6ed39e14a97f592bc5c192203b0dd6318b3a2a5b Mon Sep 17 00:00:00 2001 From: mainlyer Date: Fri, 14 Jun 2013 20:03:41 +0300 Subject: [PATCH 052/271] Rename WhatsRegisterV2 to WhatsRegisterV2.cs --- WhatsAppApi/Register/{WhatsRegisterV2 => WhatsRegisterV2.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename WhatsAppApi/Register/{WhatsRegisterV2 => WhatsRegisterV2.cs} (100%) diff --git a/WhatsAppApi/Register/WhatsRegisterV2 b/WhatsAppApi/Register/WhatsRegisterV2.cs similarity index 100% rename from WhatsAppApi/Register/WhatsRegisterV2 rename to WhatsAppApi/Register/WhatsRegisterV2.cs From fb5ca513c3b5a4f867b18a333637a628afe9271e Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 10:50:44 +0200 Subject: [PATCH 053/271] Added exception handling for contact sync --- WhatsAppApi/Helper/ContactSync.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/WhatsAppApi/Helper/ContactSync.cs b/WhatsAppApi/Helper/ContactSync.cs index 76fbc00..5d968c7 100644 --- a/WhatsAppApi/Helper/ContactSync.cs +++ b/WhatsAppApi/Helper/ContactSync.cs @@ -59,9 +59,16 @@ protected string _executeSync(string cnonce, string[] contacts) { writer.Write(postfields); } - HttpWebResponse response = this.request.GetResponse() as HttpWebResponse; - StreamReader reader = new StreamReader(response.GetResponseStream()); - return reader.ReadToEnd(); + try + { + HttpWebResponse response = this.request.GetResponse() as HttpWebResponse; + StreamReader reader = new StreamReader(response.GetResponseStream()); + return reader.ReadToEnd(); + } + catch (Exception e) + { + return null; + } } protected string _getSyncNonce() From 650d6e8238a3bb1468fc9852359c6d49bf9b8318 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 11:04:19 +0200 Subject: [PATCH 054/271] Updated client details to WP7 --- WhatsAppApi/Settings/WhatsConstants.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index dbf95e8..8329620 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.8.7"; + public const string WhatsAppVer = "2.9.4"; /// /// The port that needs to be connected to @@ -52,7 +52,7 @@ class WhatsConstants /// /// iPhone device /// - public const string IphoneDevice = "iPhone"; + public const string IphoneDevice = "WP7"; /// /// The useragent used for http requests From 0f97481acc4081386f193276c6e948667349bdf9 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 11:05:19 +0200 Subject: [PATCH 055/271] Included WhatsRegisterV2.cs in project --- WhatsAppApi/WhatsAppApi.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index 1674157..4f92a36 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -68,6 +68,7 @@ + From 3a998f51009d481eb973c124aafc96c5c2a3d68c Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 11:06:53 +0200 Subject: [PATCH 056/271] Fixed WhatsRegisterV2 errors --- WhatsAppApi/Register/WhatsRegisterV2.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index b6d9bde..74434b6 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -50,9 +50,8 @@ public static string RegisterCode(string countryCode, string phoneNumber, string private static string GetResponse(string uri) { - var request = HttpWebRequest.CreateHttp(new Uri(uri)); + HttpWebRequest request = HttpWebRequest.Create(new Uri(uri)) as HttpWebRequest; request.KeepAlive = false; - request.Date = DateTime.Now; request.UserAgent = WhatsConstants.UserAgent; request.Accept = "text/json"; using (var reader = new StreamReader(request.GetResponse().GetResponseStream())) From c6af3f0ec15573a79a188cb7486a285ea0f0bd66 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 11:09:30 +0200 Subject: [PATCH 057/271] Updated code request URL --- WhatsAppApi/Register/WhatsRegisterV2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 74434b6..bbce700 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -21,7 +21,7 @@ public static bool RequestCode(string countryCode, string phoneNumber, string me CultureInfo.CurrentCulture.GetLanguageAndLocale(out language, out locale); string id = phoneNumber.Reverse().ToMD5String(); string token = string.Concat(WhatsConstants.WhatsBuildHash, phoneNumber).ToMD5String(); - string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc=000&mnc=000&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token); + string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc=204&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token); return (GetResponse(uri).GetJsonValue("status") == "sent"); } catch From 11269c368ee7ba4ce50da3d58d8377ac62c28cb8 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 11:17:05 +0200 Subject: [PATCH 058/271] Changed identity from MD5 to SHA --- WhatsAppApi/Register/WhatsRegisterV2.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index bbce700..3ef4197 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -19,8 +19,8 @@ public static bool RequestCode(string countryCode, string phoneNumber, string me { string language, locale; CultureInfo.CurrentCulture.GetLanguageAndLocale(out language, out locale); - string id = phoneNumber.Reverse().ToMD5String(); - string token = string.Concat(WhatsConstants.WhatsBuildHash, phoneNumber).ToMD5String(); + string id = phoneNumber.Reverse().ToSHAString(); + string token = string.Concat(WhatsConstants.WhatsBuildHash, phoneNumber).ToSHAString(); string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc=204&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token); return (GetResponse(uri).GetJsonValue("status") == "sent"); } @@ -34,7 +34,7 @@ public static string RegisterCode(string countryCode, string phoneNumber, string { try { - string id = phoneNumber.Reverse().ToMD5String(); + string id = phoneNumber.Reverse().ToSHAString(); string uri = string.Format("https://v.whatsapp.net/v2/register?cc={0}&in={1}&id={2}&code={3}", countryCode, phoneNumber, id, code); if (GetResponse(uri).GetJsonValue("status") == "ok") { @@ -60,14 +60,16 @@ private static string GetResponse(string uri) } } - private static string ToMD5String(this IEnumerable s) + private static string ToSHAString(this IEnumerable s) { - return new string(s.ToArray()).ToMD5String(); + return new string(s.ToArray()).ToSHAString(); } - private static string ToMD5String(this string s) + private static string ToSHAString(this string s) { - return string.Join(string.Empty, MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(s)).Select(item => item.ToString("x2")).ToArray()); + byte[] data = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(s)); + string str = WhatsApp.SYSEncoding.GetString(data); + return System.Uri.EscapeDataString(str); } private static void GetLanguageAndLocale(this CultureInfo self, out string language, out string locale) From 2c0dabea241318c7d46671c2690ee93497b15ffb Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 11:23:55 +0200 Subject: [PATCH 059/271] Returned ToMD5String method for token generation Whoops! --- WhatsAppApi/Register/WhatsRegisterV2.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 3ef4197..310d1a4 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -20,7 +20,7 @@ public static bool RequestCode(string countryCode, string phoneNumber, string me string language, locale; CultureInfo.CurrentCulture.GetLanguageAndLocale(out language, out locale); string id = phoneNumber.Reverse().ToSHAString(); - string token = string.Concat(WhatsConstants.WhatsBuildHash, phoneNumber).ToSHAString(); + string token = string.Concat(WhatsConstants.WhatsBuildHash, phoneNumber).ToMD5String(); string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc=204&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token); return (GetResponse(uri).GetJsonValue("status") == "sent"); } @@ -72,6 +72,17 @@ private static string ToSHAString(this string s) return System.Uri.EscapeDataString(str); } + private static string ToMD5String(this IEnumerable s) + { + return new string(s.ToArray()).ToMD5String(); + } + + private static string ToMD5String(this string s) + { + return string.Join(string.Empty, MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(s)).Select(item => item.ToString("x2")).ToArray()); + } + + private static void GetLanguageAndLocale(this CultureInfo self, out string language, out string locale) { string name = self.Name; From 73a81961dbb2b7b5bfdaab6755e4dcc6cd0a7cc2 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 11:31:25 +0200 Subject: [PATCH 060/271] Removed V1 registration --- WhatsAppApi/Register/WhatsRegister.cs | 163 -------------------------- WhatsAppApi/WhatsAppApi.csproj | 1 - 2 files changed, 164 deletions(-) delete mode 100644 WhatsAppApi/Register/WhatsRegister.cs diff --git a/WhatsAppApi/Register/WhatsRegister.cs b/WhatsAppApi/Register/WhatsRegister.cs deleted file mode 100644 index 55ec373..0000000 --- a/WhatsAppApi/Register/WhatsRegister.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Security.Cryptography; -using System.Text; -using WhatsAppApi.Settings; - -namespace WhatsAppApi.Register -{ - public static class WhatsRegister - { - public static bool RegisterUser(string countryCode, string phoneNumber) - { - string website = "https://r.whatsapp.net/v1/code.php"; - string postData = GetRegString(countryCode, phoneNumber); - string both = website + "?" + postData; - - var result = StartWebRequest("", "", WhatsConstants.UserAgent, both); - if (WhatsApp.DEBUG && result.Length > 0) - { - Console.WriteLine(result); - } - return result.Contains("status=\"success-sent\""); - } - - public static bool VerifyRegistration(string countryCode, string phoneNumber, string password, string code) - { - string tmpPassword = password.ToPassword(); - string verifyString = string.Format("https://r.whatsapp.net/v1/register.php?cc={0}&in={1}&udid={2}&code={3}", new object[] { countryCode, phoneNumber, tmpPassword, code }); - - var result = StartWebRequest("", "", WhatsConstants.UserAgent, verifyString); - Console.WriteLine(result); - return true; - } - - public static bool ExistsAndDelete(string countrycode, string phone, string pass) - { - string webString = string.Format("https://r.whatsapp.net/v1/exist.php?cc={0}&in={1}", System.Uri.EscapeDataString(countrycode), System.Uri.EscapeDataString(phone)); - if (pass != null) - { - webString = webString + string.Format("&udid={0}", pass.ToPassword()); - } - - var result = StartWebRequest("", "", WhatsConstants.UserAgent, webString); - return result.Contains("status=\"ok\""); - } - - private static string StartWebRequest(string website, string postData, string userAgent, string both) - { - var request = (HttpWebRequest)WebRequest.Create(both); - request.UserAgent = userAgent; - try - { - var response = (HttpWebResponse)request.GetResponse(); - using (var reader = new StreamReader(response.GetResponseStream())) - { - var html = reader.ReadToEnd(); - return html; - } - } - catch (WebException) - { - return "error"; - } - } - - private static string MD5String(this string pass) - { - MD5 md5 = MD5.Create(); - byte[] dataMd5 = md5.ComputeHash(Encoding.UTF8.GetBytes(pass)); - return ByteToString(dataMd5); - } - - private static string ByteToString(byte[] dataMd5) - { - var sb = new StringBuilder(); - for (int i = 0; i < dataMd5.Length; i++) - sb.AppendFormat("{0:x2}", dataMd5[i]); - return sb.ToString(); - } - - private static string GetRegString(string countryCode, string phonenum, string codeType = "sms") - { - string tmpLangCode; - string tmpLocalCode; - GetLangAndLocale(CultureInfo.CurrentCulture, out tmpLangCode, out tmpLocalCode); - if (tmpLocalCode == "029") - { - tmpLocalCode = "US"; - } - string phoneNumber = phonenum; - const string buildHash = WhatsConstants.WhatsBuildHash; - string tmpToken = ("k7Iy3bWARdNeSL8gYgY6WveX12A1g4uTNXrRzt1H" + buildHash + phoneNumber).MD5String().ToLower(); - string regString = string.Format("cc={0}&in={1}&lg={2}&lc={3}&method={4}&mcc=000&mnc=000&imsi=000&token={5}", new object[] { countryCode, phoneNumber, tmpLangCode, tmpLocalCode, codeType, tmpToken }); - return regString; - } - - private static string ToPassword(this string bs) - { - if (bs.Contains(":")) - { - string ps = bs.ToUpper(); - string ls = ps + ps; - return ls.MD5String(); - } - else - { - return (new string(bs.Reverse().ToArray())).MD5String(); - } - } - - private static void GetLangAndLocale(CultureInfo that, out string lang, out string locale) - { - string name = that.Name; - int index = name.IndexOf('-'); - if (index > 0) - { - int num2 = name.LastIndexOf('-'); - lang = name.Substring(0, index); - locale = name.Substring(num2 + 1); - } - else - { - lang = name; - switch (lang) - { - case "cs": - locale = "CZ"; - return; - - case "da": - locale = "DK"; - return; - - case "el": - locale = "GR"; - return; - - case "ja": - locale = "JP"; - return; - - case "ko": - locale = "KR"; - return; - - case "sv": - locale = "SE"; - return; - - case "sr": - locale = "RS"; - return; - } - locale = lang.ToUpper(); - } - } - - } -} diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index 4f92a36..9637c7b 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -67,7 +67,6 @@ - From e9393abb23498f21104cb2b6b31b83f23f4d1fbb Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 11:39:55 +0200 Subject: [PATCH 061/271] Added automatically sending nickname on login --- WhatsAppApi/WhatsApp.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 78da945..61375f3 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -280,6 +280,7 @@ public void Login() System.Threading.Thread.Sleep(50); } while ((cnt++ < 100) && (this.loginStatus == CONNECTION_STATUS.DISCONNECTED)); + this.sendNickname(this.name); } /// From af1569d75b2d251b0d9d956e63a0d25b0385b7de Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 11:40:30 +0200 Subject: [PATCH 062/271] Updated example project --- WhatsTest/Program.cs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/WhatsTest/Program.cs b/WhatsTest/Program.cs index 2397440..a5704e4 100644 --- a/WhatsTest/Program.cs +++ b/WhatsTest/Program.cs @@ -31,11 +31,10 @@ private static void Main(string[] args) wa.Connect(); wa.Login(); - wa.sendNickname(nickname); - wa.Disconnect(); + wa.PollMessages(); - wa.PresenceSubscription(target); - wa.GetStatus(target); + wa.Message(target, "Hi this is sent using WhatsApiNet"); + wa.PollMessages(); ProcessChat(wa, ""); @@ -113,19 +112,26 @@ private static void ProcessChat(WhatsApp wa, string dst) private static void RegisterAccount() { - Console.Write("CountryCode (ex. 49): "); + Console.Write("CountryCode (ex. 31): "); string countryCode = Console.ReadLine(); - Console.Write("Phonenumber: "); + Console.Write("Phonenumber (ex. 650568134): "); string phoneNumber = Console.ReadLine(); - if (!WhatsRegister.RegisterUser(countryCode, phoneNumber)) + if (!WhatsRegisterV2.RequestCode(countryCode, phoneNumber)) return; - Console.Write("Enter send Code: "); + Console.Write("Enter received code: "); string tmpCode = Console.ReadLine(); - Console.Write("Enter your new Password: "); - string tmpPassword = Console.ReadLine(); - WhatsRegister.VerifyRegistration(countryCode, phoneNumber, tmpPassword, tmpCode); + string password = WhatsRegisterV2.RegisterCode(countryCode, phoneNumber, tmpCode); + if (String.IsNullOrEmpty(password)) + { + Console.WriteLine("Error registering code"); + } + else + { + Console.WriteLine(String.Format("Registration succesful. Password = {0}", password)); + } + Console.ReadLine(); } From 7094b66d28364374583cec2138e01fb7fd5ccf58 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 12 Jul 2013 12:02:53 +0200 Subject: [PATCH 063/271] Added RequestExist to WhatsRegisterV2.cs Used to obtain new password for an existing account --- WhatsAppApi/Register/WhatsRegisterV2.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 310d1a4..260aa18 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -48,6 +48,27 @@ public static string RegisterCode(string countryCode, string phoneNumber, string } } + public static string RequestExist(string countryCode, string phoneNumber, string id = null) + { + try + { + if (String.IsNullOrEmpty(id)) + { + id = phoneNumber.Reverse().ToSHAString(); + } + string uri = string.Format("https://v.whatsapp.net/v2/exist?cc={0}&in={1}&id={2}", countryCode, phoneNumber, id); + if (GetResponse(uri).GetJsonValue("status") == "ok") + { + return GetResponse(uri).GetJsonValue("pw"); + } + return null; + } + catch + { + return null; + } + } + private static string GetResponse(string uri) { HttpWebRequest request = HttpWebRequest.Create(new Uri(uri)) as HttpWebRequest; @@ -69,7 +90,7 @@ private static string ToSHAString(this string s) { byte[] data = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(s)); string str = WhatsApp.SYSEncoding.GetString(data); - return System.Uri.EscapeDataString(str); + return System.Uri.EscapeDataString(str).ToLower(); } private static string ToMD5String(this IEnumerable s) From 3355171a4723e23749ce52671e02d1237d04eefc Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 15 Jul 2013 15:35:33 +0200 Subject: [PATCH 064/271] Fixed SHA string encoding See https://github.com/shirioko/WhatsAPINet/commit/2c0dabea241318c7d46671c2690ee93497b15ffb#commitcomment-3634354 --- WhatsAppApi/Register/WhatsRegisterV2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 260aa18..831b122 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -89,7 +89,7 @@ private static string ToSHAString(this IEnumerable s) private static string ToSHAString(this string s) { byte[] data = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(s)); - string str = WhatsApp.SYSEncoding.GetString(data); + string str = Encoding.ASCII.GetString(data); return System.Uri.EscapeDataString(str).ToLower(); } From fad27d1d81e11e01973566bf92ef31de5d378807 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 26 Jul 2013 07:36:35 +0200 Subject: [PATCH 065/271] Updated user agent to latest version --- WhatsAppApi/Settings/WhatsConstants.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index 8329620..7e223dd 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -21,7 +21,7 @@ class WhatsConstants /// /// The whatsapp host /// - public const string WhatsAppHost = "c2.whatsapp.net"; + public const string WhatsAppHost = "c.whatsapp.net"; /// /// The whatsapp XMPP realm @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.9.4"; + public const string WhatsAppVer = "2.10.523"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.9.4 WP7/7.10.8858 Device/HTC-HTC-H0002"; + public const string UserAgent = "WhatsApp/2.10.523 WP7/7.10.8858 Device/HTC-HTC-H0002"; /// /// The whatsapp build hash From a91c400aa6aae2052c023f11399fb41ccd94c19c Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 26 Jul 2013 07:36:57 +0200 Subject: [PATCH 066/271] Added WhatsConstant user agent to contact sync --- WhatsAppApi/Helper/ContactSync.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/Helper/ContactSync.cs b/WhatsAppApi/Helper/ContactSync.cs index 5d968c7..767c450 100644 --- a/WhatsAppApi/Helper/ContactSync.cs +++ b/WhatsAppApi/Helper/ContactSync.cs @@ -126,7 +126,7 @@ protected static string _hexEncode(byte[] data) protected void _setHeaders(string nonce, long contentlength) { this.request.Headers.Clear(); - this.request.UserAgent = "WhatsApp/2.4.7 S40Version/14.26 Device/Nokia302"; + this.request.UserAgent = WhatsAppApi.Settings.WhatsConstants.UserAgent; this.request.Accept = "text/json"; this.request.ContentType = "application/x-www-form-urlencoded"; string foo = this._generateAuth(nonce); From ddfa4d1ac2a21dd1a0f35904e605ebebaddb84d6 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 26 Jul 2013 09:11:19 +0200 Subject: [PATCH 067/271] Fixed encoding for all platforms By https://github.com/Dynogic --- WhatsAppApi/Helper/ContactSync.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/WhatsAppApi/Helper/ContactSync.cs b/WhatsAppApi/Helper/ContactSync.cs index 767c450..768fa26 100644 --- a/WhatsAppApi/Helper/ContactSync.cs +++ b/WhatsAppApi/Helper/ContactSync.cs @@ -40,7 +40,7 @@ protected string _getPostfields(string[] contacts) foreach (string contact in contacts) { string con = contact; - if(!con.Contains('+')) + if (!con.Contains('+')) { con = "%2B" + con; } @@ -100,7 +100,7 @@ protected static string _hash(string data) protected static string _hash(string data, bool raw) { - byte[] bytes = System.Text.Encoding.Default.GetBytes(data); + byte[] bytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(data); MD5 md5 = MD5.Create(); md5.ComputeHash(bytes); if (!raw) @@ -109,7 +109,7 @@ protected static string _hash(string data, bool raw) } else { - return System.Text.Encoding.Default.GetString(md5.Hash); + return Encoding.GetEncoding("ISO-8859-1").GetString(md5.Hash); } } @@ -141,7 +141,7 @@ protected string _generateAuth(string nonce) string nc = "00000001"; string digestUri = "WAWA/s.whatsapp.net"; string credentials = this.username + ":s.whatsapp.net:"; - credentials += System.Text.Encoding.Default.GetString(Convert.FromBase64String(this.password)); + credentials += Encoding.GetEncoding("ISO-8859-1").GetString(Convert.FromBase64String(this.password)); string response = _hash(_hash(_hash(credentials, true) + ":" + nonce + ":" + cnonce) + ":" + nonce + ":" + nc + ":" + cnonce + ":auth:" + _hash("AUTHENTICATE:" + digestUri)); return "X-WAWA:username=\"" + this.username + "\",realm=\"s.whatsapp.net\",nonce=\"" + nonce + "\",cnonce=\"" + cnonce + "\",nc=\"" + nc + "\",qop=\"auth\",digest-uri=\"" + digestUri + "\",response=\"" + response + "\",charset=\"utf-8\""; } @@ -160,4 +160,4 @@ public class ContactSyncResultContainer { public ContactSyncResult[] c { get; set; } } -} +} \ No newline at end of file From c9f624770d44bd8add3d180e4882f39be00f5bc5 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 29 Jul 2013 16:09:51 +0200 Subject: [PATCH 068/271] Updated to Android client details Should fix code request --- WhatsAppApi/Settings/WhatsConstants.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index 7e223dd..e4ac6d5 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.10.523"; + public const string WhatsAppVer = "2.10.222"; /// /// The port that needs to be connected to @@ -52,17 +52,17 @@ class WhatsConstants /// /// iPhone device /// - public const string IphoneDevice = "WP7"; + public const string IphoneDevice = "Android"; /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.10.523 WP7/7.10.8858 Device/HTC-HTC-H0002"; + public const string UserAgent = "WhatsApp/2.10.222 Android/4.2.2 Device/LGE-Nexus_4"; /// /// The whatsapp build hash /// - public const string WhatsBuildHash = "Od52pFozHNWF9XbTN5lrqDtnsiZGL2G3l9yw1GiQ21a31a2d9dbdc9a8ce324ef2df918064fd26e30a"; + public const string WhatsBuildHash = "30820332308202f0a00302010202044c2536a4300b06072a8648ce3804030500307c310b3009060355040613025553311330110603550408130a43616c69666f726e6961311430120603550407130b53616e746120436c61726131163014060355040a130d576861747341707020496e632e31143012060355040b130b456e67696e656572696e67311430120603550403130b427269616e204163746f6e301e170d3130303632353233303731365a170d3434303231353233303731365a307c310b3009060355040613025553311330110603550408130a43616c69666f726e6961311430120603550407130b53616e746120436c61726131163014060355040a130d576861747341707020496e632e31143012060355040b130b456e67696e656572696e67311430120603550403130b427269616e204163746f6e308201b83082012c06072a8648ce3804013082011f02818100fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c70215009760508f15230bccb292b982a2eb840bf0581cf502818100f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b3d0782675159578ebad4594fe67107108180b449167123e84c281613b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06928b665e807b552564014c3bfecf492a0381850002818100d1198b4b81687bcf246d41a8a725f0a989a51bce326e84c828e1f556648bd71da487054d6de70fff4b49432b6862aa48fc2a93161b2c15a2ff5e671672dfb576e9d12aaff7369b9a99d04fb29d2bbbb2a503ee41b1ff37887064f41fe2805609063500a8e547349282d15981cdb58a08bede51dd7e9867295b3dfb45ffc6b259300b06072a8648ce3804030500032f00302c021400a602a7477acf841077237be090df436582ca2f0214350ce0268d07e71e55774ab4eacd4d071cd1efad8ac295a1bc5c0df1147db1ad2172b776"; #endregion #region ParserConstants From f3766a813a59f8214efa161598d42ac1c59efd42 Mon Sep 17 00:00:00 2001 From: Rodrigo Fraga Date: Sat, 17 Aug 2013 10:49:27 -0400 Subject: [PATCH 069/271] removing whitespaces --- WhatsAppApi/Response/WhatsParser.cs | 42 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/WhatsAppApi/Response/WhatsParser.cs b/WhatsAppApi/Response/WhatsParser.cs index 03f29d7..8614c08 100644 --- a/WhatsAppApi/Response/WhatsParser.cs +++ b/WhatsAppApi/Response/WhatsParser.cs @@ -7,36 +7,36 @@ namespace WhatsAppApi.Response { - /// - /// Parses whatsapp messages + /// + /// Parses whatsapp messages /// public class WhatsParser { - /// - /// An instance of the WhatsSendHandler class + /// + /// An instance of the WhatsSendHandler class /// public WhatsSendHandler WhatsSendHandler { get; private set; } - /// - /// An instnce of the WhatsNetwork class + /// + /// An instnce of the WhatsNetwork class /// private WhatsNetwork whatsNetwork; - /// - /// An instance of the MessageRecvResponse class + /// + /// An instance of the MessageRecvResponse class /// private MessageRecvResponse messResponseHandler; - /// - /// An instance of the Binary Tree node writer class + /// + /// An instance of the Binary Tree node writer class /// private BinTreeNodeWriter _binWriter; - /// - /// Default constructor - /// - /// An instance of the WhatsNetwork class + /// + /// Default constructor + /// + /// An instance of the WhatsNetwork class /// An instance of the BinTreeNodeWriter class internal WhatsParser(WhatsNetwork whatsNet, BinTreeNodeWriter writer) { @@ -46,9 +46,9 @@ internal WhatsParser(WhatsNetwork whatsNet, BinTreeNodeWriter writer) this._binWriter = writer; } - /// - /// Parse a tree node - /// + /// + /// Parse a tree node + /// /// An instance of the ProtocolTreeNode class that needs to be parsed. public void ParseProtocolNode(ProtocolTreeNode protNode) { @@ -131,10 +131,10 @@ public void ParseProtocolNode(ProtocolTreeNode protNode) } } - /// - /// Parse categories - /// - /// An instance of the ProtocolTreeNode class + /// + /// Parse categories + /// + /// An instance of the ProtocolTreeNode class /// A dictionary with the categories used internal static Dictionary ParseCategories(ProtocolTreeNode dirtyNode) { From acceb9248f4fc4f95a2f6ce6176aa29d93fb34f6 Mon Sep 17 00:00:00 2001 From: Rodrigo Fraga Date: Sat, 17 Aug 2013 10:58:20 -0400 Subject: [PATCH 070/271] Revert "Removed V1 registration" This reverts commit 73a81961dbb2b7b5bfdaab6755e4dcc6cd0a7cc2. --- WhatsAppApi/Register/WhatsRegister.cs | 163 ++++++++++++++++++++++++++ WhatsAppApi/WhatsAppApi.csproj | 1 + 2 files changed, 164 insertions(+) create mode 100644 WhatsAppApi/Register/WhatsRegister.cs diff --git a/WhatsAppApi/Register/WhatsRegister.cs b/WhatsAppApi/Register/WhatsRegister.cs new file mode 100644 index 0000000..55ec373 --- /dev/null +++ b/WhatsAppApi/Register/WhatsRegister.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using WhatsAppApi.Settings; + +namespace WhatsAppApi.Register +{ + public static class WhatsRegister + { + public static bool RegisterUser(string countryCode, string phoneNumber) + { + string website = "https://r.whatsapp.net/v1/code.php"; + string postData = GetRegString(countryCode, phoneNumber); + string both = website + "?" + postData; + + var result = StartWebRequest("", "", WhatsConstants.UserAgent, both); + if (WhatsApp.DEBUG && result.Length > 0) + { + Console.WriteLine(result); + } + return result.Contains("status=\"success-sent\""); + } + + public static bool VerifyRegistration(string countryCode, string phoneNumber, string password, string code) + { + string tmpPassword = password.ToPassword(); + string verifyString = string.Format("https://r.whatsapp.net/v1/register.php?cc={0}&in={1}&udid={2}&code={3}", new object[] { countryCode, phoneNumber, tmpPassword, code }); + + var result = StartWebRequest("", "", WhatsConstants.UserAgent, verifyString); + Console.WriteLine(result); + return true; + } + + public static bool ExistsAndDelete(string countrycode, string phone, string pass) + { + string webString = string.Format("https://r.whatsapp.net/v1/exist.php?cc={0}&in={1}", System.Uri.EscapeDataString(countrycode), System.Uri.EscapeDataString(phone)); + if (pass != null) + { + webString = webString + string.Format("&udid={0}", pass.ToPassword()); + } + + var result = StartWebRequest("", "", WhatsConstants.UserAgent, webString); + return result.Contains("status=\"ok\""); + } + + private static string StartWebRequest(string website, string postData, string userAgent, string both) + { + var request = (HttpWebRequest)WebRequest.Create(both); + request.UserAgent = userAgent; + try + { + var response = (HttpWebResponse)request.GetResponse(); + using (var reader = new StreamReader(response.GetResponseStream())) + { + var html = reader.ReadToEnd(); + return html; + } + } + catch (WebException) + { + return "error"; + } + } + + private static string MD5String(this string pass) + { + MD5 md5 = MD5.Create(); + byte[] dataMd5 = md5.ComputeHash(Encoding.UTF8.GetBytes(pass)); + return ByteToString(dataMd5); + } + + private static string ByteToString(byte[] dataMd5) + { + var sb = new StringBuilder(); + for (int i = 0; i < dataMd5.Length; i++) + sb.AppendFormat("{0:x2}", dataMd5[i]); + return sb.ToString(); + } + + private static string GetRegString(string countryCode, string phonenum, string codeType = "sms") + { + string tmpLangCode; + string tmpLocalCode; + GetLangAndLocale(CultureInfo.CurrentCulture, out tmpLangCode, out tmpLocalCode); + if (tmpLocalCode == "029") + { + tmpLocalCode = "US"; + } + string phoneNumber = phonenum; + const string buildHash = WhatsConstants.WhatsBuildHash; + string tmpToken = ("k7Iy3bWARdNeSL8gYgY6WveX12A1g4uTNXrRzt1H" + buildHash + phoneNumber).MD5String().ToLower(); + string regString = string.Format("cc={0}&in={1}&lg={2}&lc={3}&method={4}&mcc=000&mnc=000&imsi=000&token={5}", new object[] { countryCode, phoneNumber, tmpLangCode, tmpLocalCode, codeType, tmpToken }); + return regString; + } + + private static string ToPassword(this string bs) + { + if (bs.Contains(":")) + { + string ps = bs.ToUpper(); + string ls = ps + ps; + return ls.MD5String(); + } + else + { + return (new string(bs.Reverse().ToArray())).MD5String(); + } + } + + private static void GetLangAndLocale(CultureInfo that, out string lang, out string locale) + { + string name = that.Name; + int index = name.IndexOf('-'); + if (index > 0) + { + int num2 = name.LastIndexOf('-'); + lang = name.Substring(0, index); + locale = name.Substring(num2 + 1); + } + else + { + lang = name; + switch (lang) + { + case "cs": + locale = "CZ"; + return; + + case "da": + locale = "DK"; + return; + + case "el": + locale = "GR"; + return; + + case "ja": + locale = "JP"; + return; + + case "ko": + locale = "KR"; + return; + + case "sv": + locale = "SE"; + return; + + case "sr": + locale = "RS"; + return; + } + locale = lang.ToUpper(); + } + } + + } +} diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index 9637c7b..4f92a36 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -67,6 +67,7 @@ + From b5728a012b5791bcefcbeeff8c9da2edc72cb26b Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 19 Aug 2013 17:20:34 +0200 Subject: [PATCH 071/271] Translation --- WhatsAppPort/frmLogin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppPort/frmLogin.cs b/WhatsAppPort/frmLogin.cs index 846fc46..c3d4d07 100644 --- a/WhatsAppPort/frmLogin.cs +++ b/WhatsAppPort/frmLogin.cs @@ -20,7 +20,7 @@ private void btnLogin_Click(object sender, EventArgs e) { if (!this.CheckLogin(this.textBoxPhone.Text, this.textBoxPass.Text)) { - MessageBox.Show(this, "Login fehlgeschlagen", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(this, "Login failed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } From 0e8549bdf39cff2242975a4c5cbddc6935426725 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 19 Aug 2013 17:33:17 +0200 Subject: [PATCH 072/271] Added WhatSocket class --- WhatsAppPort/WhatSocket.cs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 WhatsAppPort/WhatSocket.cs diff --git a/WhatsAppPort/WhatSocket.cs b/WhatsAppPort/WhatSocket.cs new file mode 100644 index 0000000..3d0a1a3 --- /dev/null +++ b/WhatsAppPort/WhatSocket.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WhatsAppPort +{ + public class WhatSocket + { + private static WhatsAppApi.WhatsApp _instance; + + public static WhatsAppApi.WhatsApp Instance + { + get + { + if (_instance != null) + { + return _instance; + } + else + { + throw new Exception("Instance is not set"); + } + } + } + + public static void Create(string username, string password, string nickname, bool debug = false) + { + _instance = new WhatsAppApi.WhatsApp(username, password, nickname, debug); + } + } +} From 41ea3bb5165ba669a1d6d6ac61171758d8f0c887 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 19 Aug 2013 17:36:45 +0200 Subject: [PATCH 073/271] Implemented WhatSocket class for Login screen --- WhatsAppPort/WhatsAppPort.csproj | 1 + WhatsAppPort/frmLogin.cs | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/WhatsAppPort/WhatsAppPort.csproj b/WhatsAppPort/WhatsAppPort.csproj index 6410781..41b9aab 100644 --- a/WhatsAppPort/WhatsAppPort.csproj +++ b/WhatsAppPort/WhatsAppPort.csproj @@ -75,6 +75,7 @@ frmUserChat.cs + frmForm.cs Designer diff --git a/WhatsAppPort/frmLogin.cs b/WhatsAppPort/frmLogin.cs index c3d4d07..0a98ce2 100644 --- a/WhatsAppPort/frmLogin.cs +++ b/WhatsAppPort/frmLogin.cs @@ -41,14 +41,18 @@ private bool CheckLogin(string user, string pass) if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(pass)) return false; - string cc = user.Substring(0, 2); - string phone = user.Remove(0, 2).TrimStart('0'); - return WhatsAppApi.Register.WhatsRegister.ExistsAndDelete(cc, phone, pass); + WhatSocket.Create(user, pass, this.textBoxNick.Text, true); + WhatSocket.Instance.Connect(); + WhatSocket.Instance.Login(); + //check login status + if (WhatSocket.Instance.ConnectionStatus == WhatsAppApi.WhatsApp.CONNECTION_STATUS.LOGGEDIN) + { + return true; + } } catch (Exception) - { - return false; - } + { } + return false; } } } From 9232e07d17351ead8ed057dc6c3b08ec7c8bb918 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 19 Aug 2013 18:51:10 +0200 Subject: [PATCH 074/271] Added WhatSocket.Instance to WhatsAppPort --- WhatsAppPort/frmForm.cs | 14 ++++++-------- WhatsAppPort/frmUserChat.cs | 10 ++++------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/WhatsAppPort/frmForm.cs b/WhatsAppPort/frmForm.cs index a62ce13..3c99dc4 100644 --- a/WhatsAppPort/frmForm.cs +++ b/WhatsAppPort/frmForm.cs @@ -18,7 +18,6 @@ namespace WhatsAppPort { public partial class frmForm : Form { - private WhatsApp wa; private WhatsMessageHandler messageHandler; private BackgroundWorker bgWorker; private volatile bool isRunning; @@ -42,7 +41,6 @@ public frmForm(string num, string pass, string nick) this.bgWorker.ProgressChanged += NewMessageArrived; this.bgWorker.WorkerSupportsCancellation = true; this.bgWorker.WorkerReportsProgress = true; - this.wa = new WhatsApp(this.phoneNum, this.phonePass, this.phoneNick); this.messageHandler = new WhatsMessageHandler(); } @@ -67,8 +65,8 @@ private void btnAddContact_Click(object sender, EventArgs e) private void Form1_Load(object sender, EventArgs e) { - this.wa.Connect(); - this.wa.Login(); + WhatSocket.Instance.Connect(); + WhatSocket.Instance.Login(); this.bgWorker.RunWorkerAsync(); } @@ -79,14 +77,14 @@ private void ProcessMessages(object sender, DoWorkEventArgs args) while (this.isRunning) { - if (!wa.HasMessages()) + if (!WhatSocket.Instance.HasMessages()) { - wa.PollMessages(); + WhatSocket.Instance.PollMessages(); Thread.Sleep(100); continue; } - var tmpMessages = wa.GetAllMessages(); + var tmpMessages = WhatSocket.Instance.GetAllMessages(); (sender as BackgroundWorker).ReportProgress(1, tmpMessages); } } @@ -144,7 +142,7 @@ private void listViewContacts_DoubleClick(object sender, EventArgs e) var selItem = tmpListView.SelectedItems[0]; var tmpUser = selItem.Tag as User; - var tmpDialog = new frmUserChat(this.wa, tmpUser); + var tmpDialog = new frmUserChat(tmpUser); //tmpDialog.MessageRecievedEvent += new frmUserChat.ProtocolDelegate(tmpDialog_MessageRecievedEvent); tmpDialog.Show(); } diff --git a/WhatsAppPort/frmUserChat.cs b/WhatsAppPort/frmUserChat.cs index ddf20eb..3f6489b 100644 --- a/WhatsAppPort/frmUserChat.cs +++ b/WhatsAppPort/frmUserChat.cs @@ -21,14 +21,12 @@ public partial class frmUserChat : Form //public event StringDelegate MessageSentEvent; //public event Action MessageAckEvent; //public event ProtocolDelegate MessageRecievedEvent; - private WhatsAppApi.WhatsApp whatsApp; private User user; private bool isTyping; - public frmUserChat(WhatsAppApi.WhatsApp whats, User user) + public frmUserChat(User user) { InitializeComponent(); - this.whatsApp = whats; this.user = user; this.isTyping = false; WhatsEventHandler.MessageRecievedEvent += WhatsEventHandlerOnMessageRecievedEvent; @@ -53,7 +51,7 @@ private void btnSend_Click(object sender, EventArgs e) if (this.txtBxSentText.Text.Length == 0) return; - this.whatsApp.Message(this.user.WhatsUser.GetFullJid(), txtBxSentText.Text); + WhatSocket.Instance.Message(this.user.WhatsUser.GetFullJid(), txtBxSentText.Text); this.AddNewText(this.user.UserName, txtBxSentText.Text); txtBxSentText.Clear(); } @@ -68,7 +66,7 @@ private void txtBxSentText_TextChanged(object sender, EventArgs e) if (!this.isTyping) { this.isTyping = true; - this.whatsApp.WhatsSendHandler.SendComposing(this.user.WhatsUser.GetFullJid()); + WhatSocket.Instance.WhatsSendHandler.SendComposing(this.user.WhatsUser.GetFullJid()); this.timerTyping.Start(); } } @@ -80,7 +78,7 @@ private void timerTyping_Tick(object sender, EventArgs e) this.isTyping = false; return; } - this.whatsApp.WhatsSendHandler.SendPaused(this.user.WhatsUser.GetFullJid()); + WhatSocket.Instance.WhatsSendHandler.SendPaused(this.user.WhatsUser.GetFullJid()); this.timerTyping.Stop(); } } From 4066be009a91b93c2a177e1fe4fc7f67393e4e4f Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 20 Aug 2013 11:54:33 +0200 Subject: [PATCH 075/271] Updated to latest working registration info --- WhatsAppApi/Register/WhatsRegister.cs | 2 +- WhatsAppApi/Register/WhatsRegisterV2.cs | 2 +- WhatsAppApi/Settings/WhatsConstants.cs | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegister.cs b/WhatsAppApi/Register/WhatsRegister.cs index 55ec373..5b3b220 100644 --- a/WhatsAppApi/Register/WhatsRegister.cs +++ b/WhatsAppApi/Register/WhatsRegister.cs @@ -92,7 +92,7 @@ private static string GetRegString(string countryCode, string phonenum, string c tmpLocalCode = "US"; } string phoneNumber = phonenum; - const string buildHash = WhatsConstants.WhatsBuildHash; + const string buildHash = WhatsConstants.WhatsRegToken + WhatsConstants.WhatsBuildHash; string tmpToken = ("k7Iy3bWARdNeSL8gYgY6WveX12A1g4uTNXrRzt1H" + buildHash + phoneNumber).MD5String().ToLower(); string regString = string.Format("cc={0}&in={1}&lg={2}&lc={3}&method={4}&mcc=000&mnc=000&imsi=000&token={5}", new object[] { countryCode, phoneNumber, tmpLangCode, tmpLocalCode, codeType, tmpToken }); return regString; diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 831b122..4e15074 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -20,7 +20,7 @@ public static bool RequestCode(string countryCode, string phoneNumber, string me string language, locale; CultureInfo.CurrentCulture.GetLanguageAndLocale(out language, out locale); string id = phoneNumber.Reverse().ToSHAString(); - string token = string.Concat(WhatsConstants.WhatsBuildHash, phoneNumber).ToMD5String(); + string token = string.Concat(WhatsConstants.WhatsRegToken + WhatsConstants.WhatsBuildHash, phoneNumber).ToMD5String(); string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc=204&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token); return (GetResponse(uri).GetJsonValue("status") == "sent"); } diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index e4ac6d5..e8f84ab 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.10.222"; + public const string WhatsAppVer = "2.10.750"; /// /// The port that needs to be connected to @@ -57,12 +57,15 @@ class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.10.222 Android/4.2.2 Device/LGE-Nexus_4"; + public const string UserAgent = "WhatsApp/2.10.750 Android/4.2.1 Device/GalaxyS3"; /// /// The whatsapp build hash /// - public const string WhatsBuildHash = "30820332308202f0a00302010202044c2536a4300b06072a8648ce3804030500307c310b3009060355040613025553311330110603550408130a43616c69666f726e6961311430120603550407130b53616e746120436c61726131163014060355040a130d576861747341707020496e632e31143012060355040b130b456e67696e656572696e67311430120603550403130b427269616e204163746f6e301e170d3130303632353233303731365a170d3434303231353233303731365a307c310b3009060355040613025553311330110603550408130a43616c69666f726e6961311430120603550407130b53616e746120436c61726131163014060355040a130d576861747341707020496e632e31143012060355040b130b456e67696e656572696e67311430120603550403130b427269616e204163746f6e308201b83082012c06072a8648ce3804013082011f02818100fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c70215009760508f15230bccb292b982a2eb840bf0581cf502818100f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b3d0782675159578ebad4594fe67107108180b449167123e84c281613b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06928b665e807b552564014c3bfecf492a0381850002818100d1198b4b81687bcf246d41a8a725f0a989a51bce326e84c828e1f556648bd71da487054d6de70fff4b49432b6862aa48fc2a93161b2c15a2ff5e671672dfb576e9d12aaff7369b9a99d04fb29d2bbbb2a503ee41b1ff37887064f41fe2805609063500a8e547349282d15981cdb58a08bede51dd7e9867295b3dfb45ffc6b259300b06072a8648ce3804030500032f00302c021400a602a7477acf841077237be090df436582ca2f0214350ce0268d07e71e55774ab4eacd4d071cd1efad8ac295a1bc5c0df1147db1ad2172b776"; + public const string WhatsRegToken = "30820332308202f0a00302010202044c2536a4300b06072a8648ce3804030500307c310b3009060355040613025553311330110603550408130a43616c69666f726e6961311430120603550407130b53616e746120436c61726131163014060355040a130d576861747341707020496e632e31143012060355040b130b456e67696e656572696e67311430120603550403130b427269616e204163746f6e301e170d3130303632353233303731365a170d3434303231353233303731365a307c310b3009060355040613025553311330110603550408130a43616c69666f726e6961311430120603550407130b53616e746120436c61726131163014060355040a130d576861747341707020496e632e31143012060355040b130b456e67696e656572696e67311430120603550403130b427269616e204163746f6e308201b83082012c06072a8648ce3804013082011f02818100fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c70215009760508f15230bccb292b982a2eb840bf0581cf502818100f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b3d0782675159578ebad4594fe67107108180b449167123e84c281613b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06928b665e807b552564014c3bfecf492a0381850002818100d1198b4b81687bcf246d41a8a725f0a989a51bce326e84c828e1f556648bd71da487054d6de70fff4b49432b6862aa48fc2a93161b2c15a2ff5e671672dfb576e9d12aaff7369b9a99d04fb29d2bbbb2a503ee41b1ff37887064f41fe2805609063500a8e547349282d15981cdb58a08bede51dd7e9867295b3dfb45ffc6b259300b06072a8648ce3804030500032f00302c021400a602a7477acf841077237be090df436582ca2f0214350ce0268d07e71e55774ab4eacd4d071cd1efad"; + + public const string WhatsBuildHash = "022e923a364bfacff3a80de3f950b1e0"; + #endregion #region ParserConstants From ca91624d4ff3edf5df5d564d9a532fa94015e740 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 20 Aug 2013 12:39:50 +0200 Subject: [PATCH 076/271] Added registration form placeholder --- WhatsAppPort/WhatsAppPort.csproj | 9 ++ WhatsAppPort/frmLogin.Designer.cs | 15 ++- WhatsAppPort/frmLogin.cs | 10 ++ WhatsAppPort/frmRegister.Designer.cs | 189 +++++++++++++++++++++++++++ WhatsAppPort/frmRegister.cs | 25 ++++ WhatsAppPort/frmRegister.resx | 120 +++++++++++++++++ 6 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 WhatsAppPort/frmRegister.Designer.cs create mode 100644 WhatsAppPort/frmRegister.cs create mode 100644 WhatsAppPort/frmRegister.resx diff --git a/WhatsAppPort/WhatsAppPort.csproj b/WhatsAppPort/WhatsAppPort.csproj index 41b9aab..3a6b7fa 100644 --- a/WhatsAppPort/WhatsAppPort.csproj +++ b/WhatsAppPort/WhatsAppPort.csproj @@ -65,6 +65,12 @@ frmLogin.cs + + Form + + + frmRegister.cs + @@ -88,6 +94,9 @@ frmLogin.cs Designer + + frmRegister.cs + frmUserChat.cs diff --git a/WhatsAppPort/frmLogin.Designer.cs b/WhatsAppPort/frmLogin.Designer.cs index d61bd7e..8c0a8ff 100644 --- a/WhatsAppPort/frmLogin.Designer.cs +++ b/WhatsAppPort/frmLogin.Designer.cs @@ -37,6 +37,7 @@ private void InitializeComponent() this.label2 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.progressBar1 = new System.Windows.Forms.ProgressBar(); + this.btnRegister = new System.Windows.Forms.Button(); this.groupBox1.SuspendLayout(); this.SuspendLayout(); // @@ -129,14 +130,25 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.progressBar1.Location = new System.Drawing.Point(22, 175); this.progressBar1.Name = "progressBar1"; - this.progressBar1.Size = new System.Drawing.Size(236, 23); + this.progressBar1.Size = new System.Drawing.Size(155, 23); this.progressBar1.TabIndex = 2; // + // btnRegister + // + this.btnRegister.Location = new System.Drawing.Point(183, 175); + this.btnRegister.Name = "btnRegister"; + this.btnRegister.Size = new System.Drawing.Size(75, 23); + this.btnRegister.TabIndex = 3; + this.btnRegister.Text = "Register"; + this.btnRegister.UseVisualStyleBackColor = true; + this.btnRegister.Click += new System.EventHandler(this.btnRegister_Click); + // // frmLogin // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(357, 208); + this.Controls.Add(this.btnRegister); this.Controls.Add(this.progressBar1); this.Controls.Add(this.groupBox1); this.Controls.Add(this.btnLogin); @@ -159,5 +171,6 @@ private void InitializeComponent() private System.Windows.Forms.ProgressBar progressBar1; private System.Windows.Forms.TextBox textBoxNick; private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button btnRegister; } } \ No newline at end of file diff --git a/WhatsAppPort/frmLogin.cs b/WhatsAppPort/frmLogin.cs index 0a98ce2..61adb06 100644 --- a/WhatsAppPort/frmLogin.cs +++ b/WhatsAppPort/frmLogin.cs @@ -54,5 +54,15 @@ private bool CheckLogin(string user, string pass) { } return false; } + + private void btnRegister_Click(object sender, EventArgs e) + { + frmRegister regForm = new frmRegister(this.textBoxPhone.Text); + DialogResult regResult = regForm.ShowDialog(this); + if (regResult == System.Windows.Forms.DialogResult.OK) + { + this.textBoxPass.Text = regForm.password; + } + } } } diff --git a/WhatsAppPort/frmRegister.Designer.cs b/WhatsAppPort/frmRegister.Designer.cs new file mode 100644 index 0000000..32dcea4 --- /dev/null +++ b/WhatsAppPort/frmRegister.Designer.cs @@ -0,0 +1,189 @@ +namespace WhatsAppPort +{ + partial class frmRegister + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.txtPhoneNumber = new System.Windows.Forms.TextBox(); + this.btnCodeRequest = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.grpStep1 = new System.Windows.Forms.GroupBox(); + this.grpStep2 = new System.Windows.Forms.GroupBox(); + this.radSMS = new System.Windows.Forms.RadioButton(); + this.radVoice = new System.Windows.Forms.RadioButton(); + this.label1 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.btnRegisterCode = new System.Windows.Forms.Button(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.grpStep1.SuspendLayout(); + this.grpStep2.SuspendLayout(); + this.SuspendLayout(); + // + // txtPhoneNumber + // + this.txtPhoneNumber.Location = new System.Drawing.Point(88, 19); + this.txtPhoneNumber.Name = "txtPhoneNumber"; + this.txtPhoneNumber.Size = new System.Drawing.Size(165, 20); + this.txtPhoneNumber.TabIndex = 1; + // + // btnCodeRequest + // + this.btnCodeRequest.Location = new System.Drawing.Point(159, 45); + this.btnCodeRequest.Name = "btnCodeRequest"; + this.btnCodeRequest.Size = new System.Drawing.Size(94, 23); + this.btnCodeRequest.TabIndex = 2; + this.btnCodeRequest.Text = "Request code"; + this.btnCodeRequest.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(6, 22); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(76, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Phone number"; + // + // grpStep1 + // + this.grpStep1.Controls.Add(this.radVoice); + this.grpStep1.Controls.Add(this.radSMS); + this.grpStep1.Controls.Add(this.label2); + this.grpStep1.Controls.Add(this.btnCodeRequest); + this.grpStep1.Controls.Add(this.txtPhoneNumber); + this.grpStep1.Location = new System.Drawing.Point(13, 13); + this.grpStep1.Name = "grpStep1"; + this.grpStep1.Size = new System.Drawing.Size(259, 76); + this.grpStep1.TabIndex = 4; + this.grpStep1.TabStop = false; + this.grpStep1.Text = "Step 1: Request code"; + // + // grpStep2 + // + this.grpStep2.Controls.Add(this.textBox2); + this.grpStep2.Controls.Add(this.btnRegisterCode); + this.grpStep2.Controls.Add(this.textBox1); + this.grpStep2.Controls.Add(this.label1); + this.grpStep2.Location = new System.Drawing.Point(13, 96); + this.grpStep2.Name = "grpStep2"; + this.grpStep2.Size = new System.Drawing.Size(259, 130); + this.grpStep2.TabIndex = 5; + this.grpStep2.TabStop = false; + this.grpStep2.Text = "Step 2: Confirm code"; + // + // radSMS + // + this.radSMS.AutoSize = true; + this.radSMS.Checked = true; + this.radSMS.Location = new System.Drawing.Point(9, 50); + this.radSMS.Name = "radSMS"; + this.radSMS.Size = new System.Drawing.Size(48, 17); + this.radSMS.TabIndex = 4; + this.radSMS.TabStop = true; + this.radSMS.Text = "SMS"; + this.radSMS.UseVisualStyleBackColor = true; + // + // radVoice + // + this.radVoice.AutoSize = true; + this.radVoice.Location = new System.Drawing.Point(64, 50); + this.radVoice.Name = "radVoice"; + this.radVoice.Size = new System.Drawing.Size(52, 17); + this.radVoice.TabIndex = 5; + this.radVoice.Text = "Voice"; + this.radVoice.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(6, 22); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(32, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Code"; + // + // textBox1 + // + this.textBox1.Enabled = false; + this.textBox1.Location = new System.Drawing.Point(88, 19); + this.textBox1.MaxLength = 6; + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.Size = new System.Drawing.Size(65, 20); + this.textBox1.TabIndex = 1; + // + // btnRegisterCode + // + this.btnRegisterCode.Enabled = false; + this.btnRegisterCode.Location = new System.Drawing.Point(159, 17); + this.btnRegisterCode.Name = "btnRegisterCode"; + this.btnRegisterCode.Size = new System.Drawing.Size(94, 23); + this.btnRegisterCode.TabIndex = 2; + this.btnRegisterCode.Text = "Confirm code"; + this.btnRegisterCode.UseVisualStyleBackColor = true; + // + // textBox2 + // + this.textBox2.Location = new System.Drawing.Point(9, 46); + this.textBox2.Multiline = true; + this.textBox2.Name = "textBox2"; + this.textBox2.ReadOnly = true; + this.textBox2.Size = new System.Drawing.Size(244, 78); + this.textBox2.TabIndex = 3; + // + // frmRegister + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(284, 238); + this.Controls.Add(this.grpStep2); + this.Controls.Add(this.grpStep1); + this.Name = "frmRegister"; + this.Text = "Register"; + this.grpStep1.ResumeLayout(false); + this.grpStep1.PerformLayout(); + this.grpStep2.ResumeLayout(false); + this.grpStep2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TextBox txtPhoneNumber; + private System.Windows.Forms.Button btnCodeRequest; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.GroupBox grpStep1; + private System.Windows.Forms.RadioButton radSMS; + private System.Windows.Forms.GroupBox grpStep2; + private System.Windows.Forms.RadioButton radVoice; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Button btnRegisterCode; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label label1; + } +} \ No newline at end of file diff --git a/WhatsAppPort/frmRegister.cs b/WhatsAppPort/frmRegister.cs new file mode 100644 index 0000000..c795429 --- /dev/null +++ b/WhatsAppPort/frmRegister.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace WhatsAppPort +{ + public partial class frmRegister : Form + { + public string password; + + public frmRegister(string number) + { + InitializeComponent(); + if (!string.IsNullOrEmpty(number)) + { + this.txtPhoneNumber.Text = number; + } + } + } +} diff --git a/WhatsAppPort/frmRegister.resx b/WhatsAppPort/frmRegister.resx new file mode 100644 index 0000000..7080a7d --- /dev/null +++ b/WhatsAppPort/frmRegister.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file From f3bea4e9173316b0cc23a1f023d586944456c4e4 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 20 Aug 2013 12:47:53 +0200 Subject: [PATCH 077/271] Fixed group panels and more --- WhatsAppPort/frmLogin.cs | 1 + WhatsAppPort/frmRegister.Designer.cs | 63 ++++++++++++++++++++-------- WhatsAppPort/frmRegister.cs | 20 +++++++++ 3 files changed, 67 insertions(+), 17 deletions(-) diff --git a/WhatsAppPort/frmLogin.cs b/WhatsAppPort/frmLogin.cs index 61adb06..db68e81 100644 --- a/WhatsAppPort/frmLogin.cs +++ b/WhatsAppPort/frmLogin.cs @@ -62,6 +62,7 @@ private void btnRegister_Click(object sender, EventArgs e) if (regResult == System.Windows.Forms.DialogResult.OK) { this.textBoxPass.Text = regForm.password; + this.textBoxPhone.Text = regForm.number; } } } diff --git a/WhatsAppPort/frmRegister.Designer.cs b/WhatsAppPort/frmRegister.Designer.cs index 32dcea4..88c855b 100644 --- a/WhatsAppPort/frmRegister.Designer.cs +++ b/WhatsAppPort/frmRegister.Designer.cs @@ -38,9 +38,12 @@ private void InitializeComponent() this.label1 = new System.Windows.Forms.Label(); this.textBox1 = new System.Windows.Forms.TextBox(); this.btnRegisterCode = new System.Windows.Forms.Button(); - this.textBox2 = new System.Windows.Forms.TextBox(); + this.txtOutput = new System.Windows.Forms.TextBox(); + this.btnDone = new System.Windows.Forms.Button(); + this.grpResult = new System.Windows.Forms.GroupBox(); this.grpStep1.SuspendLayout(); this.grpStep2.SuspendLayout(); + this.grpResult.SuspendLayout(); this.SuspendLayout(); // // txtPhoneNumber @@ -58,6 +61,7 @@ private void InitializeComponent() this.btnCodeRequest.TabIndex = 2; this.btnCodeRequest.Text = "Request code"; this.btnCodeRequest.UseVisualStyleBackColor = true; + this.btnCodeRequest.Click += new System.EventHandler(this.btnCodeRequest_Click); // // label2 // @@ -84,13 +88,13 @@ private void InitializeComponent() // // grpStep2 // - this.grpStep2.Controls.Add(this.textBox2); this.grpStep2.Controls.Add(this.btnRegisterCode); this.grpStep2.Controls.Add(this.textBox1); this.grpStep2.Controls.Add(this.label1); + this.grpStep2.Enabled = false; this.grpStep2.Location = new System.Drawing.Point(13, 96); this.grpStep2.Name = "grpStep2"; - this.grpStep2.Size = new System.Drawing.Size(259, 130); + this.grpStep2.Size = new System.Drawing.Size(259, 50); this.grpStep2.TabIndex = 5; this.grpStep2.TabStop = false; this.grpStep2.Text = "Step 2: Confirm code"; @@ -128,38 +132,59 @@ private void InitializeComponent() // // textBox1 // - this.textBox1.Enabled = false; this.textBox1.Location = new System.Drawing.Point(88, 19); this.textBox1.MaxLength = 6; this.textBox1.Name = "textBox1"; - this.textBox1.ReadOnly = true; this.textBox1.Size = new System.Drawing.Size(65, 20); this.textBox1.TabIndex = 1; // // btnRegisterCode // - this.btnRegisterCode.Enabled = false; this.btnRegisterCode.Location = new System.Drawing.Point(159, 17); this.btnRegisterCode.Name = "btnRegisterCode"; this.btnRegisterCode.Size = new System.Drawing.Size(94, 23); this.btnRegisterCode.TabIndex = 2; this.btnRegisterCode.Text = "Confirm code"; this.btnRegisterCode.UseVisualStyleBackColor = true; - // - // textBox2 - // - this.textBox2.Location = new System.Drawing.Point(9, 46); - this.textBox2.Multiline = true; - this.textBox2.Name = "textBox2"; - this.textBox2.ReadOnly = true; - this.textBox2.Size = new System.Drawing.Size(244, 78); - this.textBox2.TabIndex = 3; + this.btnRegisterCode.Click += new System.EventHandler(this.btnRegisterCode_Click); + // + // txtOutput + // + this.txtOutput.Location = new System.Drawing.Point(9, 19); + this.txtOutput.Multiline = true; + this.txtOutput.Name = "txtOutput"; + this.txtOutput.ReadOnly = true; + this.txtOutput.Size = new System.Drawing.Size(244, 102); + this.txtOutput.TabIndex = 3; + // + // btnDone + // + this.btnDone.Location = new System.Drawing.Point(178, 127); + this.btnDone.Name = "btnDone"; + this.btnDone.Size = new System.Drawing.Size(75, 23); + this.btnDone.TabIndex = 6; + this.btnDone.Text = "Done"; + this.btnDone.UseVisualStyleBackColor = true; + this.btnDone.Click += new System.EventHandler(this.btnDone_Click); + // + // grpResult + // + this.grpResult.Controls.Add(this.btnDone); + this.grpResult.Controls.Add(this.txtOutput); + this.grpResult.Enabled = false; + this.grpResult.Location = new System.Drawing.Point(13, 153); + this.grpResult.Name = "grpResult"; + this.grpResult.Size = new System.Drawing.Size(259, 156); + this.grpResult.TabIndex = 7; + this.grpResult.TabStop = false; + this.grpResult.Text = "groupBox1"; // // frmRegister // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(284, 238); + this.ClientSize = new System.Drawing.Size(284, 321); + this.Controls.Add(this.grpResult); this.Controls.Add(this.grpStep2); this.Controls.Add(this.grpStep1); this.Name = "frmRegister"; @@ -168,6 +193,8 @@ private void InitializeComponent() this.grpStep1.PerformLayout(); this.grpStep2.ResumeLayout(false); this.grpStep2.PerformLayout(); + this.grpResult.ResumeLayout(false); + this.grpResult.PerformLayout(); this.ResumeLayout(false); } @@ -181,9 +208,11 @@ private void InitializeComponent() private System.Windows.Forms.RadioButton radSMS; private System.Windows.Forms.GroupBox grpStep2; private System.Windows.Forms.RadioButton radVoice; - private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.TextBox txtOutput; private System.Windows.Forms.Button btnRegisterCode; private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button btnDone; + private System.Windows.Forms.GroupBox grpResult; } } \ No newline at end of file diff --git a/WhatsAppPort/frmRegister.cs b/WhatsAppPort/frmRegister.cs index c795429..6159eec 100644 --- a/WhatsAppPort/frmRegister.cs +++ b/WhatsAppPort/frmRegister.cs @@ -11,6 +11,7 @@ namespace WhatsAppPort { public partial class frmRegister : Form { + public string number; public string password; public frmRegister(string number) @@ -18,8 +19,27 @@ public frmRegister(string number) InitializeComponent(); if (!string.IsNullOrEmpty(number)) { + this.number = number; this.txtPhoneNumber.Text = number; } } + + private void btnCodeRequest_Click(object sender, EventArgs e) + { + this.grpStep1.Enabled = false; + this.grpStep2.Enabled = true; + } + + private void btnRegisterCode_Click(object sender, EventArgs e) + { + this.grpStep2.Enabled = false; + this.grpResult.Enabled = true; + } + + private void btnDone_Click(object sender, EventArgs e) + { + this.password = "Derp"; + this.DialogResult = System.Windows.Forms.DialogResult.OK; + } } } From 7fc9c4db47b9a52a2b98454d0ff3ba318a843e25 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 20 Aug 2013 12:52:20 +0200 Subject: [PATCH 078/271] Renamed code input textbox --- WhatsAppPort/frmRegister.Designer.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/WhatsAppPort/frmRegister.Designer.cs b/WhatsAppPort/frmRegister.Designer.cs index 88c855b..cec6e92 100644 --- a/WhatsAppPort/frmRegister.Designer.cs +++ b/WhatsAppPort/frmRegister.Designer.cs @@ -36,7 +36,7 @@ private void InitializeComponent() this.radSMS = new System.Windows.Forms.RadioButton(); this.radVoice = new System.Windows.Forms.RadioButton(); this.label1 = new System.Windows.Forms.Label(); - this.textBox1 = new System.Windows.Forms.TextBox(); + this.txtCode = new System.Windows.Forms.TextBox(); this.btnRegisterCode = new System.Windows.Forms.Button(); this.txtOutput = new System.Windows.Forms.TextBox(); this.btnDone = new System.Windows.Forms.Button(); @@ -89,7 +89,7 @@ private void InitializeComponent() // grpStep2 // this.grpStep2.Controls.Add(this.btnRegisterCode); - this.grpStep2.Controls.Add(this.textBox1); + this.grpStep2.Controls.Add(this.txtCode); this.grpStep2.Controls.Add(this.label1); this.grpStep2.Enabled = false; this.grpStep2.Location = new System.Drawing.Point(13, 96); @@ -130,13 +130,13 @@ private void InitializeComponent() this.label1.TabIndex = 0; this.label1.Text = "Code"; // - // textBox1 + // txtCode // - this.textBox1.Location = new System.Drawing.Point(88, 19); - this.textBox1.MaxLength = 6; - this.textBox1.Name = "textBox1"; - this.textBox1.Size = new System.Drawing.Size(65, 20); - this.textBox1.TabIndex = 1; + this.txtCode.Location = new System.Drawing.Point(88, 19); + this.txtCode.MaxLength = 6; + this.txtCode.Name = "txtCode"; + this.txtCode.Size = new System.Drawing.Size(65, 20); + this.txtCode.TabIndex = 1; // // btnRegisterCode // @@ -210,7 +210,7 @@ private void InitializeComponent() private System.Windows.Forms.RadioButton radVoice; private System.Windows.Forms.TextBox txtOutput; private System.Windows.Forms.Button btnRegisterCode; - private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.TextBox txtCode; private System.Windows.Forms.Label label1; private System.Windows.Forms.Button btnDone; private System.Windows.Forms.GroupBox grpResult; From 9ca9887ed1791edd3c99810cebea525c14d87e09 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 20 Aug 2013 12:59:50 +0200 Subject: [PATCH 079/271] Added code request functionality --- WhatsAppPort/frmRegister.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/WhatsAppPort/frmRegister.cs b/WhatsAppPort/frmRegister.cs index 6159eec..7949b31 100644 --- a/WhatsAppPort/frmRegister.cs +++ b/WhatsAppPort/frmRegister.cs @@ -26,8 +26,22 @@ public frmRegister(string number) private void btnCodeRequest_Click(object sender, EventArgs e) { - this.grpStep1.Enabled = false; - this.grpStep2.Enabled = true; + if (!String.IsNullOrEmpty(this.txtPhoneNumber.Text)) + { + string method = "sms"; + if (this.radVoice.Checked) + { + method = "voice"; + } + this.number = this.txtPhoneNumber.Text; + string cc = this.number.Substring(0, 2); + string phone = this.number.Substring(2); + if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(cc, phone, method)) + { + this.grpStep1.Enabled = false; + this.grpStep2.Enabled = true; + } + } } private void btnRegisterCode_Click(object sender, EventArgs e) From 97a04a3dae3306da7a9d365d0e01973ba4126371 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 20 Aug 2013 13:08:56 +0200 Subject: [PATCH 080/271] Added code register functionality and made cc and phone into properties to remove duplicate code --- WhatsAppPort/frmRegister.cs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/WhatsAppPort/frmRegister.cs b/WhatsAppPort/frmRegister.cs index 7949b31..5fbee91 100644 --- a/WhatsAppPort/frmRegister.cs +++ b/WhatsAppPort/frmRegister.cs @@ -12,6 +12,8 @@ namespace WhatsAppPort public partial class frmRegister : Form { public string number; + protected string cc; + protected string phone; public string password; public frmRegister(string number) @@ -20,6 +22,8 @@ public frmRegister(string number) if (!string.IsNullOrEmpty(number)) { this.number = number; + this.cc = this.number.Substring(0, 2); + this.phone = this.number.Substring(2); this.txtPhoneNumber.Text = number; } } @@ -34,9 +38,7 @@ private void btnCodeRequest_Click(object sender, EventArgs e) method = "voice"; } this.number = this.txtPhoneNumber.Text; - string cc = this.number.Substring(0, 2); - string phone = this.number.Substring(2); - if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(cc, phone, method)) + if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(this.cc, this.phone, method)) { this.grpStep1.Enabled = false; this.grpStep2.Enabled = true; @@ -46,13 +48,21 @@ private void btnCodeRequest_Click(object sender, EventArgs e) private void btnRegisterCode_Click(object sender, EventArgs e) { - this.grpStep2.Enabled = false; - this.grpResult.Enabled = true; + if (!String.IsNullOrEmpty(this.txtCode.Text) && this.txtCode.Text.Length == 6) + { + string code = this.txtCode.Text; + this.password = WhatsAppApi.Register.WhatsRegisterV2.RegisterCode(this.cc, this.phone, code); + if (!String.IsNullOrEmpty(this.password)) + { + this.txtOutput.Text = String.Format("Found password:\r\n{0}\r\n\r\nWrite it down and click Done to proceed", this.password); + this.grpStep2.Enabled = false; + this.grpResult.Enabled = true; + } + } } private void btnDone_Click(object sender, EventArgs e) { - this.password = "Derp"; this.DialogResult = System.Windows.Forms.DialogResult.OK; } } From 8ad4461c31bd7c801818b5be9d64917e9b082b7d Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 20 Aug 2013 17:13:35 +0200 Subject: [PATCH 081/271] Latest changes --- WhatsAppPort/frmRegister.Designer.cs | 78 ++++++++++++++-------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/WhatsAppPort/frmRegister.Designer.cs b/WhatsAppPort/frmRegister.Designer.cs index cec6e92..4784e40 100644 --- a/WhatsAppPort/frmRegister.Designer.cs +++ b/WhatsAppPort/frmRegister.Designer.cs @@ -32,12 +32,12 @@ private void InitializeComponent() this.btnCodeRequest = new System.Windows.Forms.Button(); this.label2 = new System.Windows.Forms.Label(); this.grpStep1 = new System.Windows.Forms.GroupBox(); - this.grpStep2 = new System.Windows.Forms.GroupBox(); - this.radSMS = new System.Windows.Forms.RadioButton(); this.radVoice = new System.Windows.Forms.RadioButton(); - this.label1 = new System.Windows.Forms.Label(); - this.txtCode = new System.Windows.Forms.TextBox(); + this.radSMS = new System.Windows.Forms.RadioButton(); + this.grpStep2 = new System.Windows.Forms.GroupBox(); this.btnRegisterCode = new System.Windows.Forms.Button(); + this.txtCode = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); this.txtOutput = new System.Windows.Forms.TextBox(); this.btnDone = new System.Windows.Forms.Button(); this.grpResult = new System.Windows.Forms.GroupBox(); @@ -86,18 +86,15 @@ private void InitializeComponent() this.grpStep1.TabStop = false; this.grpStep1.Text = "Step 1: Request code"; // - // grpStep2 + // radVoice // - this.grpStep2.Controls.Add(this.btnRegisterCode); - this.grpStep2.Controls.Add(this.txtCode); - this.grpStep2.Controls.Add(this.label1); - this.grpStep2.Enabled = false; - this.grpStep2.Location = new System.Drawing.Point(13, 96); - this.grpStep2.Name = "grpStep2"; - this.grpStep2.Size = new System.Drawing.Size(259, 50); - this.grpStep2.TabIndex = 5; - this.grpStep2.TabStop = false; - this.grpStep2.Text = "Step 2: Confirm code"; + this.radVoice.AutoSize = true; + this.radVoice.Location = new System.Drawing.Point(64, 50); + this.radVoice.Name = "radVoice"; + this.radVoice.Size = new System.Drawing.Size(52, 17); + this.radVoice.TabIndex = 5; + this.radVoice.Text = "Voice"; + this.radVoice.UseVisualStyleBackColor = true; // // radSMS // @@ -111,24 +108,28 @@ private void InitializeComponent() this.radSMS.Text = "SMS"; this.radSMS.UseVisualStyleBackColor = true; // - // radVoice + // grpStep2 // - this.radVoice.AutoSize = true; - this.radVoice.Location = new System.Drawing.Point(64, 50); - this.radVoice.Name = "radVoice"; - this.radVoice.Size = new System.Drawing.Size(52, 17); - this.radVoice.TabIndex = 5; - this.radVoice.Text = "Voice"; - this.radVoice.UseVisualStyleBackColor = true; + this.grpStep2.Controls.Add(this.btnRegisterCode); + this.grpStep2.Controls.Add(this.txtCode); + this.grpStep2.Controls.Add(this.label1); + this.grpStep2.Enabled = false; + this.grpStep2.Location = new System.Drawing.Point(13, 96); + this.grpStep2.Name = "grpStep2"; + this.grpStep2.Size = new System.Drawing.Size(259, 50); + this.grpStep2.TabIndex = 5; + this.grpStep2.TabStop = false; + this.grpStep2.Text = "Step 2: Confirm code"; // - // label1 + // btnRegisterCode // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(6, 22); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(32, 13); - this.label1.TabIndex = 0; - this.label1.Text = "Code"; + this.btnRegisterCode.Location = new System.Drawing.Point(159, 17); + this.btnRegisterCode.Name = "btnRegisterCode"; + this.btnRegisterCode.Size = new System.Drawing.Size(94, 23); + this.btnRegisterCode.TabIndex = 2; + this.btnRegisterCode.Text = "Confirm code"; + this.btnRegisterCode.UseVisualStyleBackColor = true; + this.btnRegisterCode.Click += new System.EventHandler(this.btnRegisterCode_Click); // // txtCode // @@ -138,15 +139,14 @@ private void InitializeComponent() this.txtCode.Size = new System.Drawing.Size(65, 20); this.txtCode.TabIndex = 1; // - // btnRegisterCode + // label1 // - this.btnRegisterCode.Location = new System.Drawing.Point(159, 17); - this.btnRegisterCode.Name = "btnRegisterCode"; - this.btnRegisterCode.Size = new System.Drawing.Size(94, 23); - this.btnRegisterCode.TabIndex = 2; - this.btnRegisterCode.Text = "Confirm code"; - this.btnRegisterCode.UseVisualStyleBackColor = true; - this.btnRegisterCode.Click += new System.EventHandler(this.btnRegisterCode_Click); + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(6, 22); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(32, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Code"; // // txtOutput // @@ -177,7 +177,7 @@ private void InitializeComponent() this.grpResult.Size = new System.Drawing.Size(259, 156); this.grpResult.TabIndex = 7; this.grpResult.TabStop = false; - this.grpResult.Text = "groupBox1"; + this.grpResult.Text = "Step 3: Retrieve password"; // // frmRegister // From d43f0657a2a2f2e4a06814468f21f6a3abacd237 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 10:44:07 +0200 Subject: [PATCH 082/271] Added cc and phone splitting in codeRequest Oops --- WhatsAppPort/frmRegister.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WhatsAppPort/frmRegister.cs b/WhatsAppPort/frmRegister.cs index 5fbee91..4d25978 100644 --- a/WhatsAppPort/frmRegister.cs +++ b/WhatsAppPort/frmRegister.cs @@ -38,6 +38,8 @@ private void btnCodeRequest_Click(object sender, EventArgs e) method = "voice"; } this.number = this.txtPhoneNumber.Text; + this.cc = this.number.Substring(0, 2); + this.phone = this.number.Substring(2); if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(this.cc, this.phone, method)) { this.grpStep1.Enabled = false; From 304d1d1b400ee9bb94cde428c7d0f0471f27648a Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 12:16:33 +0200 Subject: [PATCH 083/271] Added WhatsAppReg project Useful for registering new numbers, just the reg form from WhatsAppPort that I added yesterday. WhatsAppApi.dll is included as an embedded resource the same way as MissVenom uses it. --- WhatsAppApi_vs2010.sln | 16 +- WhatsAppReg/Context.cs | 36 ++++ WhatsAppReg/Program.cs | 23 +++ WhatsAppReg/Properties/AssemblyInfo.cs | 36 ++++ WhatsAppReg/Properties/Resources.Designer.cs | 73 +++++++ WhatsAppReg/Properties/Resources.resx | 124 +++++++++++ WhatsAppReg/Properties/Settings.Designer.cs | 26 +++ WhatsAppReg/Properties/Settings.settings | 7 + WhatsAppReg/Resources/WhatsAppApi.dll | Bin 0 -> 89088 bytes WhatsAppReg/WhatsAppReg.csproj | 99 +++++++++ WhatsAppReg/frmRegister.Designer.cs | 205 +++++++++++++++++++ WhatsAppReg/frmRegister.cs | 59 ++++++ WhatsAppReg/frmRegister.resx | 120 +++++++++++ 13 files changed, 822 insertions(+), 2 deletions(-) create mode 100644 WhatsAppReg/Context.cs create mode 100644 WhatsAppReg/Program.cs create mode 100644 WhatsAppReg/Properties/AssemblyInfo.cs create mode 100644 WhatsAppReg/Properties/Resources.Designer.cs create mode 100644 WhatsAppReg/Properties/Resources.resx create mode 100644 WhatsAppReg/Properties/Settings.Designer.cs create mode 100644 WhatsAppReg/Properties/Settings.settings create mode 100644 WhatsAppReg/Resources/WhatsAppApi.dll create mode 100644 WhatsAppReg/WhatsAppReg.csproj create mode 100644 WhatsAppReg/frmRegister.Designer.cs create mode 100644 WhatsAppReg/frmRegister.cs create mode 100644 WhatsAppReg/frmRegister.resx diff --git a/WhatsAppApi_vs2010.sln b/WhatsAppApi_vs2010.sln index 756f0fd..19b864c 100644 --- a/WhatsAppApi_vs2010.sln +++ b/WhatsAppApi_vs2010.sln @@ -1,12 +1,14 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsAppApi", "WhatsAppApi\WhatsAppApi.csproj", "{3FC9096A-C4D2-40C7-BE7B-D98ACAB3BD2B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsAppPort", "WhatsAppPort\WhatsAppPort.csproj", "{91D9D263-BFB7-4C31-890F-4A42F734670E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsTest", "WhatsTest\WhatsTest.csproj", "{E003A3A5-4ECF-475A-8926-154EF85CF4B7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsAppReg", "WhatsAppReg\WhatsAppReg.csproj", "{D2EEC76E-966B-4B59-922E-D76091D4D594}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -47,6 +49,16 @@ Global {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|Mixed Platforms.Build.0 = Release|x86 {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|x86.ActiveCfg = Release|x86 {E003A3A5-4ECF-475A-8926-154EF85CF4B7}.Release|x86.Build.0 = Release|x86 + {D2EEC76E-966B-4B59-922E-D76091D4D594}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2EEC76E-966B-4B59-922E-D76091D4D594}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2EEC76E-966B-4B59-922E-D76091D4D594}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D2EEC76E-966B-4B59-922E-D76091D4D594}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D2EEC76E-966B-4B59-922E-D76091D4D594}.Debug|x86.ActiveCfg = Debug|Any CPU + {D2EEC76E-966B-4B59-922E-D76091D4D594}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2EEC76E-966B-4B59-922E-D76091D4D594}.Release|Any CPU.Build.0 = Release|Any CPU + {D2EEC76E-966B-4B59-922E-D76091D4D594}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D2EEC76E-966B-4B59-922E-D76091D4D594}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D2EEC76E-966B-4B59-922E-D76091D4D594}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/WhatsAppReg/Context.cs b/WhatsAppReg/Context.cs new file mode 100644 index 0000000..ba16837 --- /dev/null +++ b/WhatsAppReg/Context.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace WhatsAppReg +{ + public class Context + { + public Context() + { + AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); + } + + public void Run() + { + Application.Run(new frmRegister()); + } + + System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll", ""); + + dllName = dllName.Replace(".", "_"); + + if (dllName.EndsWith("_resources")) return null; + + System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly()); + + byte[] bytes = (byte[])rm.GetObject(dllName); + + return System.Reflection.Assembly.Load(bytes); + } + } +} diff --git a/WhatsAppReg/Program.cs b/WhatsAppReg/Program.cs new file mode 100644 index 0000000..d8d97e6 --- /dev/null +++ b/WhatsAppReg/Program.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace WhatsAppReg +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Context c = new Context(); + c.Run(); + } + } +} diff --git a/WhatsAppReg/Properties/AssemblyInfo.cs b/WhatsAppReg/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..22d325d --- /dev/null +++ b/WhatsAppReg/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WhatsAppReg")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WhatsAppReg")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f1569335-e486-4473-9de1-75540dbec6f4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/WhatsAppReg/Properties/Resources.Designer.cs b/WhatsAppReg/Properties/Resources.Designer.cs new file mode 100644 index 0000000..3de3598 --- /dev/null +++ b/WhatsAppReg/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18051 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WhatsAppReg.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WhatsAppReg.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] WhatsAppApi { + get { + object obj = ResourceManager.GetObject("WhatsAppApi", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/WhatsAppReg/Properties/Resources.resx b/WhatsAppReg/Properties/Resources.resx new file mode 100644 index 0000000..badd567 --- /dev/null +++ b/WhatsAppReg/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\WhatsAppApi.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/WhatsAppReg/Properties/Settings.Designer.cs b/WhatsAppReg/Properties/Settings.Designer.cs new file mode 100644 index 0000000..2786eb7 --- /dev/null +++ b/WhatsAppReg/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18051 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WhatsAppReg.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/WhatsAppReg/Properties/Settings.settings b/WhatsAppReg/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/WhatsAppReg/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/WhatsAppReg/Resources/WhatsAppApi.dll b/WhatsAppReg/Resources/WhatsAppApi.dll new file mode 100644 index 0000000000000000000000000000000000000000..5f18fb7ccfa86de4be4c816bcef9833f13f9e2ce GIT binary patch literal 89088 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 literal 0 HcmV?d00001 diff --git a/WhatsAppReg/WhatsAppReg.csproj b/WhatsAppReg/WhatsAppReg.csproj new file mode 100644 index 0000000..1591ed9 --- /dev/null +++ b/WhatsAppReg/WhatsAppReg.csproj @@ -0,0 +1,99 @@ + + + + + Debug + AnyCPU + {D2EEC76E-966B-4B59-922E-D76091D4D594} + WinExe + Properties + WhatsAppReg + WhatsAppReg + v4.0 + 512 + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + Form + + + frmRegister.cs + + + + + frmRegister.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {3fc9096a-c4d2-40c7-be7b-d98acab3bd2b} + WhatsAppApi + False + + + + + + + + \ No newline at end of file diff --git a/WhatsAppReg/frmRegister.Designer.cs b/WhatsAppReg/frmRegister.Designer.cs new file mode 100644 index 0000000..ee12c0e --- /dev/null +++ b/WhatsAppReg/frmRegister.Designer.cs @@ -0,0 +1,205 @@ +namespace WhatsAppReg +{ + partial class frmRegister + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.txtPhoneNumber = new System.Windows.Forms.TextBox(); + this.btnCodeRequest = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.grpStep1 = new System.Windows.Forms.GroupBox(); + this.radVoice = new System.Windows.Forms.RadioButton(); + this.radSMS = new System.Windows.Forms.RadioButton(); + this.grpStep2 = new System.Windows.Forms.GroupBox(); + this.btnRegisterCode = new System.Windows.Forms.Button(); + this.txtCode = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.txtOutput = new System.Windows.Forms.TextBox(); + this.grpResult = new System.Windows.Forms.GroupBox(); + this.grpStep1.SuspendLayout(); + this.grpStep2.SuspendLayout(); + this.grpResult.SuspendLayout(); + this.SuspendLayout(); + // + // txtPhoneNumber + // + this.txtPhoneNumber.Location = new System.Drawing.Point(88, 19); + this.txtPhoneNumber.Name = "txtPhoneNumber"; + this.txtPhoneNumber.Size = new System.Drawing.Size(165, 20); + this.txtPhoneNumber.TabIndex = 1; + // + // btnCodeRequest + // + this.btnCodeRequest.Location = new System.Drawing.Point(159, 45); + this.btnCodeRequest.Name = "btnCodeRequest"; + this.btnCodeRequest.Size = new System.Drawing.Size(94, 23); + this.btnCodeRequest.TabIndex = 2; + this.btnCodeRequest.Text = "Request code"; + this.btnCodeRequest.UseVisualStyleBackColor = true; + this.btnCodeRequest.Click += new System.EventHandler(this.btnCodeRequest_Click); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(6, 22); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(76, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Phone number"; + // + // grpStep1 + // + this.grpStep1.Controls.Add(this.radVoice); + this.grpStep1.Controls.Add(this.radSMS); + this.grpStep1.Controls.Add(this.label2); + this.grpStep1.Controls.Add(this.btnCodeRequest); + this.grpStep1.Controls.Add(this.txtPhoneNumber); + this.grpStep1.Location = new System.Drawing.Point(13, 13); + this.grpStep1.Name = "grpStep1"; + this.grpStep1.Size = new System.Drawing.Size(259, 76); + this.grpStep1.TabIndex = 4; + this.grpStep1.TabStop = false; + this.grpStep1.Text = "Step 1: Request code"; + // + // radVoice + // + this.radVoice.AutoSize = true; + this.radVoice.Location = new System.Drawing.Point(64, 50); + this.radVoice.Name = "radVoice"; + this.radVoice.Size = new System.Drawing.Size(52, 17); + this.radVoice.TabIndex = 5; + this.radVoice.Text = "Voice"; + this.radVoice.UseVisualStyleBackColor = true; + // + // radSMS + // + this.radSMS.AutoSize = true; + this.radSMS.Checked = true; + this.radSMS.Location = new System.Drawing.Point(9, 50); + this.radSMS.Name = "radSMS"; + this.radSMS.Size = new System.Drawing.Size(48, 17); + this.radSMS.TabIndex = 4; + this.radSMS.TabStop = true; + this.radSMS.Text = "SMS"; + this.radSMS.UseVisualStyleBackColor = true; + // + // grpStep2 + // + this.grpStep2.Controls.Add(this.btnRegisterCode); + this.grpStep2.Controls.Add(this.txtCode); + this.grpStep2.Controls.Add(this.label1); + this.grpStep2.Enabled = false; + this.grpStep2.Location = new System.Drawing.Point(13, 96); + this.grpStep2.Name = "grpStep2"; + this.grpStep2.Size = new System.Drawing.Size(259, 50); + this.grpStep2.TabIndex = 5; + this.grpStep2.TabStop = false; + this.grpStep2.Text = "Step 2: Confirm code"; + // + // btnRegisterCode + // + this.btnRegisterCode.Location = new System.Drawing.Point(159, 17); + this.btnRegisterCode.Name = "btnRegisterCode"; + this.btnRegisterCode.Size = new System.Drawing.Size(94, 23); + this.btnRegisterCode.TabIndex = 2; + this.btnRegisterCode.Text = "Confirm code"; + this.btnRegisterCode.UseVisualStyleBackColor = true; + this.btnRegisterCode.Click += new System.EventHandler(this.btnRegisterCode_Click); + // + // txtCode + // + this.txtCode.Location = new System.Drawing.Point(88, 19); + this.txtCode.MaxLength = 6; + this.txtCode.Name = "txtCode"; + this.txtCode.Size = new System.Drawing.Size(65, 20); + this.txtCode.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(6, 22); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(32, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Code"; + // + // txtOutput + // + this.txtOutput.Location = new System.Drawing.Point(9, 19); + this.txtOutput.Multiline = true; + this.txtOutput.Name = "txtOutput"; + this.txtOutput.ReadOnly = true; + this.txtOutput.Size = new System.Drawing.Size(244, 102); + this.txtOutput.TabIndex = 3; + // + // grpResult + // + this.grpResult.Controls.Add(this.txtOutput); + this.grpResult.Enabled = false; + this.grpResult.Location = new System.Drawing.Point(13, 153); + this.grpResult.Name = "grpResult"; + this.grpResult.Size = new System.Drawing.Size(259, 134); + this.grpResult.TabIndex = 7; + this.grpResult.TabStop = false; + this.grpResult.Text = "Step 3: Retrieve password"; + // + // frmRegister + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(284, 300); + this.Controls.Add(this.grpResult); + this.Controls.Add(this.grpStep2); + this.Controls.Add(this.grpStep1); + this.Name = "frmRegister"; + this.Text = "WhatsApp Registration"; + this.grpStep1.ResumeLayout(false); + this.grpStep1.PerformLayout(); + this.grpStep2.ResumeLayout(false); + this.grpStep2.PerformLayout(); + this.grpResult.ResumeLayout(false); + this.grpResult.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TextBox txtPhoneNumber; + private System.Windows.Forms.Button btnCodeRequest; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.GroupBox grpStep1; + private System.Windows.Forms.RadioButton radSMS; + private System.Windows.Forms.GroupBox grpStep2; + private System.Windows.Forms.RadioButton radVoice; + private System.Windows.Forms.TextBox txtOutput; + private System.Windows.Forms.Button btnRegisterCode; + private System.Windows.Forms.TextBox txtCode; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.GroupBox grpResult; + } +} \ No newline at end of file diff --git a/WhatsAppReg/frmRegister.cs b/WhatsAppReg/frmRegister.cs new file mode 100644 index 0000000..4662f98 --- /dev/null +++ b/WhatsAppReg/frmRegister.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace WhatsAppReg +{ + public partial class frmRegister : Form + { + protected string number; + protected string cc; + protected string phone; + protected string password; + + public frmRegister() + { + InitializeComponent(); + } + + private void btnCodeRequest_Click(object sender, EventArgs e) + { + if (!String.IsNullOrEmpty(this.txtPhoneNumber.Text)) + { + string method = "sms"; + if (this.radVoice.Checked) + { + method = "voice"; + } + this.number = this.txtPhoneNumber.Text; + this.cc = this.number.Substring(0, 2); + this.phone = this.number.Substring(2); + if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(this.cc, this.phone, method)) + { + this.grpStep1.Enabled = false; + this.grpStep2.Enabled = true; + } + } + } + + private void btnRegisterCode_Click(object sender, EventArgs e) + { + if (!String.IsNullOrEmpty(this.txtCode.Text) && this.txtCode.Text.Length == 6) + { + string code = this.txtCode.Text; + this.password = WhatsAppApi.Register.WhatsRegisterV2.RegisterCode(this.cc, this.phone, code); + if (!String.IsNullOrEmpty(this.password)) + { + this.txtOutput.Text = String.Format("Found password:\r\n{0}\r\n\r\nWrite it down and exit the program", this.password); + this.grpStep2.Enabled = false; + this.grpResult.Enabled = true; + } + } + } + } +} diff --git a/WhatsAppReg/frmRegister.resx b/WhatsAppReg/frmRegister.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/WhatsAppReg/frmRegister.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file From 163f547d384622539c3dc2bbeecc5e2fc74ebd9c Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 12:17:09 +0200 Subject: [PATCH 084/271] Added precompiled exe of WhatsAppReg For your convenience --- WhatsAppReg.exe | Bin 0 -> 101888 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 WhatsAppReg.exe diff --git a/WhatsAppReg.exe b/WhatsAppReg.exe new file mode 100644 index 0000000000000000000000000000000000000000..99faa3ae9c88ebcc934a99ff8ef5a95246b4375f GIT binary patch literal 101888 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 Z!UGih7xCYDeeNg9gI$;Z{nAO`e*t{o3_1V+ literal 0 HcmV?d00001 From ac4adb8cb03754df7cc61730af1ba3ad361c604c Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 14:26:25 +0200 Subject: [PATCH 085/271] Added handling of password response on codeRequest calling codeRequest with an already registered number + identity combination will return your registration details instead, the same data as codeRegister --- WhatsAppApi/Register/WhatsRegisterV2.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 4e15074..e81711a 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -13,8 +13,9 @@ namespace WhatsAppApi.Register { public static class WhatsRegisterV2 { - public static bool RequestCode(string countryCode, string phoneNumber, string method = "sms") + public static bool RequestCode(string countryCode, string phoneNumber, out string password, string method = "sms") { + password = null; try { string language, locale; @@ -22,7 +23,13 @@ public static bool RequestCode(string countryCode, string phoneNumber, string me string id = phoneNumber.Reverse().ToSHAString(); string token = string.Concat(WhatsConstants.WhatsRegToken + WhatsConstants.WhatsBuildHash, phoneNumber).ToMD5String(); string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc=204&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token); - return (GetResponse(uri).GetJsonValue("status") == "sent"); + string response = GetResponse(uri); + password = response.GetJsonValue("pw"); + if (!string.IsNullOrEmpty(password)) + { + return true; + } + return (response.GetJsonValue("status") == "sent"); } catch { From 7eb1c4694a1283fbd6312e5b438ebc4f08cfa944 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 14:27:34 +0200 Subject: [PATCH 086/271] Updated WhatsAppReg to work with updated code --- WhatsAppReg/frmRegister.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppReg/frmRegister.cs b/WhatsAppReg/frmRegister.cs index 4662f98..1fbfb51 100644 --- a/WhatsAppReg/frmRegister.cs +++ b/WhatsAppReg/frmRegister.cs @@ -33,7 +33,7 @@ private void btnCodeRequest_Click(object sender, EventArgs e) this.number = this.txtPhoneNumber.Text; this.cc = this.number.Substring(0, 2); this.phone = this.number.Substring(2); - if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(this.cc, this.phone, method)) + if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(this.cc, this.phone, out this.password, method)) { this.grpStep1.Enabled = false; this.grpStep2.Enabled = true; From 5367c7f358768dc52d4ee63957d3540e5e0b1d12 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 14:31:55 +0200 Subject: [PATCH 087/271] Step3 to new method to prevent duplicate code --- WhatsAppReg/frmRegister.cs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/WhatsAppReg/frmRegister.cs b/WhatsAppReg/frmRegister.cs index 1fbfb51..5e44eda 100644 --- a/WhatsAppReg/frmRegister.cs +++ b/WhatsAppReg/frmRegister.cs @@ -35,8 +35,16 @@ private void btnCodeRequest_Click(object sender, EventArgs e) this.phone = this.number.Substring(2); if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(this.cc, this.phone, out this.password, method)) { - this.grpStep1.Enabled = false; - this.grpStep2.Enabled = true; + if (!string.IsNullOrEmpty(this.password)) + { + //password received + this.OnReceivePassword(); + } + else + { + this.grpStep1.Enabled = false; + this.grpStep2.Enabled = true; + } } } } @@ -49,11 +57,17 @@ private void btnRegisterCode_Click(object sender, EventArgs e) this.password = WhatsAppApi.Register.WhatsRegisterV2.RegisterCode(this.cc, this.phone, code); if (!String.IsNullOrEmpty(this.password)) { - this.txtOutput.Text = String.Format("Found password:\r\n{0}\r\n\r\nWrite it down and exit the program", this.password); - this.grpStep2.Enabled = false; - this.grpResult.Enabled = true; + this.OnReceivePassword(); } } } + + private void OnReceivePassword() + { + this.txtOutput.Text = String.Format("Found password:\r\n{0}\r\n\r\nWrite it down and exit the program", this.password); + this.grpStep1.Enabled = false; + this.grpStep2.Enabled = false; + this.grpResult.Enabled = true; + } } } From fc7f5f680380dcdb905c883c969508e6a36deda3 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 14:32:20 +0200 Subject: [PATCH 088/271] Updated code to build with new V2 Reg code --- WhatsAppPort/frmRegister.cs | 2 +- WhatsTest/Program.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WhatsAppPort/frmRegister.cs b/WhatsAppPort/frmRegister.cs index 4d25978..1312f84 100644 --- a/WhatsAppPort/frmRegister.cs +++ b/WhatsAppPort/frmRegister.cs @@ -40,7 +40,7 @@ private void btnCodeRequest_Click(object sender, EventArgs e) this.number = this.txtPhoneNumber.Text; this.cc = this.number.Substring(0, 2); this.phone = this.number.Substring(2); - if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(this.cc, this.phone, method)) + if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(this.cc, this.phone, out this.password, method)) { this.grpStep1.Enabled = false; this.grpStep2.Enabled = true; diff --git a/WhatsTest/Program.cs b/WhatsTest/Program.cs index a5704e4..4b8bfe1 100644 --- a/WhatsTest/Program.cs +++ b/WhatsTest/Program.cs @@ -116,13 +116,13 @@ private static void RegisterAccount() string countryCode = Console.ReadLine(); Console.Write("Phonenumber (ex. 650568134): "); string phoneNumber = Console.ReadLine(); - - if (!WhatsRegisterV2.RequestCode(countryCode, phoneNumber)) + string password = null; + if (!WhatsRegisterV2.RequestCode(countryCode, phoneNumber, out password)) return; Console.Write("Enter received code: "); string tmpCode = Console.ReadLine(); - string password = WhatsRegisterV2.RegisterCode(countryCode, phoneNumber, tmpCode); + password = WhatsRegisterV2.RegisterCode(countryCode, phoneNumber, tmpCode); if (String.IsNullOrEmpty(password)) { Console.WriteLine("Error registering code"); From c29a5aa68c3ded6c9502947bb41781e61f198e81 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 14:34:35 +0200 Subject: [PATCH 089/271] Added changes from WhatsAppReg to WhatsAppPort --- WhatsAppPort/frmRegister.cs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/WhatsAppPort/frmRegister.cs b/WhatsAppPort/frmRegister.cs index 1312f84..092b875 100644 --- a/WhatsAppPort/frmRegister.cs +++ b/WhatsAppPort/frmRegister.cs @@ -42,8 +42,16 @@ private void btnCodeRequest_Click(object sender, EventArgs e) this.phone = this.number.Substring(2); if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(this.cc, this.phone, out this.password, method)) { - this.grpStep1.Enabled = false; - this.grpStep2.Enabled = true; + if (!string.IsNullOrEmpty(this.password)) + { + //password received + this.OnReceivePassword(); + } + else + { + this.grpStep1.Enabled = false; + this.grpStep2.Enabled = true; + } } } } @@ -56,13 +64,19 @@ private void btnRegisterCode_Click(object sender, EventArgs e) this.password = WhatsAppApi.Register.WhatsRegisterV2.RegisterCode(this.cc, this.phone, code); if (!String.IsNullOrEmpty(this.password)) { - this.txtOutput.Text = String.Format("Found password:\r\n{0}\r\n\r\nWrite it down and click Done to proceed", this.password); - this.grpStep2.Enabled = false; - this.grpResult.Enabled = true; + this.OnReceivePassword(); } } } + private void OnReceivePassword() + { + this.txtOutput.Text = String.Format("Found password:\r\n{0}\r\n\r\nWrite it down and exit the program", this.password); + this.grpStep1.Enabled = false; + this.grpStep2.Enabled = false; + this.grpResult.Enabled = true; + } + private void btnDone_Click(object sender, EventArgs e) { this.DialogResult = System.Windows.Forms.DialogResult.OK; From 743463fb8b9123f82d3e3ca72adb504b75299b53 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 14:40:30 +0200 Subject: [PATCH 090/271] Updated WhatsApiReg with latest WhatsAppApi.dll --- WhatsAppReg/Resources/WhatsAppApi.dll | Bin 89088 -> 89088 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/WhatsAppReg/Resources/WhatsAppApi.dll b/WhatsAppReg/Resources/WhatsAppApi.dll index 5f18fb7ccfa86de4be4c816bcef9833f13f9e2ce..5be8b0c38e21ba0520e7eaf1f416b4f409964d1c 100644 GIT binary patch delta 5963 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 Do~w>} From b2ee3880161faecd250064746e241b28937c8ccd Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 14:40:46 +0200 Subject: [PATCH 091/271] Updated precompiled exe --- WhatsAppReg.exe | Bin 101888 -> 101888 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/WhatsAppReg.exe b/WhatsAppReg.exe index 99faa3ae9c88ebcc934a99ff8ef5a95246b4375f..db2c1a958885f19102164221276ed62ef6d7e63b 100644 GIT binary patch delta 7915 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 Itmzc|Z*)z{)c^nh From 4a59a72c65a778acfe4467cd71ad48ed0a1088f0 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 14:52:14 +0200 Subject: [PATCH 092/271] Added optional identity parameter to V2 reg --- WhatsAppApi/Register/WhatsRegisterV2.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index e81711a..d7888a1 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -13,14 +13,18 @@ namespace WhatsAppApi.Register { public static class WhatsRegisterV2 { - public static bool RequestCode(string countryCode, string phoneNumber, out string password, string method = "sms") + public static bool RequestCode(string countryCode, string phoneNumber, out string password, string method = "sms", string id = null) { password = null; try { string language, locale; CultureInfo.CurrentCulture.GetLanguageAndLocale(out language, out locale); - string id = phoneNumber.Reverse().ToSHAString(); + if (string.IsNullOrEmpty(id)) + { + //auto-generate (insecure) + id = phoneNumber.Reverse().ToSHAString(); + } string token = string.Concat(WhatsConstants.WhatsRegToken + WhatsConstants.WhatsBuildHash, phoneNumber).ToMD5String(); string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc=204&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token); string response = GetResponse(uri); @@ -37,11 +41,15 @@ public static bool RequestCode(string countryCode, string phoneNumber, out strin } } - public static string RegisterCode(string countryCode, string phoneNumber, string code) + public static string RegisterCode(string countryCode, string phoneNumber, string code, string id = null) { try { - string id = phoneNumber.Reverse().ToSHAString(); + if (string.IsNullOrEmpty(id)) + { + //auto generate (insecure) + id = phoneNumber.Reverse().ToSHAString(); + } string uri = string.Format("https://v.whatsapp.net/v2/register?cc={0}&in={1}&id={2}&code={3}", countryCode, phoneNumber, id, code); if (GetResponse(uri).GetJsonValue("status") == "ok") { From 1f6d6445fdde59034b8979d7befc121765b2f83f Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 21 Aug 2013 14:54:17 +0200 Subject: [PATCH 093/271] Fixed making multiple requests Calling GetResponse multiple times would create multiple requests --- WhatsAppApi/Register/WhatsRegisterV2.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index d7888a1..532befe 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -51,9 +51,10 @@ public static string RegisterCode(string countryCode, string phoneNumber, string id = phoneNumber.Reverse().ToSHAString(); } string uri = string.Format("https://v.whatsapp.net/v2/register?cc={0}&in={1}&id={2}&code={3}", countryCode, phoneNumber, id, code); - if (GetResponse(uri).GetJsonValue("status") == "ok") + string response = GetResponse(uri); + if (response.GetJsonValue("status") == "ok") { - return GetResponse(uri).GetJsonValue("pw"); + return response.GetJsonValue("pw"); } return null; } @@ -72,9 +73,10 @@ public static string RequestExist(string countryCode, string phoneNumber, string id = phoneNumber.Reverse().ToSHAString(); } string uri = string.Format("https://v.whatsapp.net/v2/exist?cc={0}&in={1}&id={2}", countryCode, phoneNumber, id); - if (GetResponse(uri).GetJsonValue("status") == "ok") + string response = GetResponse(uri); + if (response.GetJsonValue("status") == "ok") { - return GetResponse(uri).GetJsonValue("pw"); + return response.GetJsonValue("pw"); } return null; } From a990787a85e02ca5be64c9685ed8995fafdcbed5 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 22 Aug 2013 16:05:39 +0200 Subject: [PATCH 094/271] Added phone number parser --- WhatsAppApi/Parser/PhoneNumber.cs | 56 ++++ WhatsAppApi/Parser/countries.csv | 254 +++++++++++++++++++ WhatsAppApi/Properties/Resources.Designer.cs | 88 +++++++ WhatsAppApi/Properties/Resources.resx | 124 +++++++++ WhatsAppApi/WhatsAppApi.csproj | 13 + 5 files changed, 535 insertions(+) create mode 100644 WhatsAppApi/Parser/PhoneNumber.cs create mode 100644 WhatsAppApi/Parser/countries.csv create mode 100644 WhatsAppApi/Properties/Resources.Designer.cs create mode 100644 WhatsAppApi/Properties/Resources.resx diff --git a/WhatsAppApi/Parser/PhoneNumber.cs b/WhatsAppApi/Parser/PhoneNumber.cs new file mode 100644 index 0000000..5f19ab9 --- /dev/null +++ b/WhatsAppApi/Parser/PhoneNumber.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace WhatsAppApi.Parser +{ + public class PhoneNumber + { + public string Country; + public string CC; + public string Number; + public string FullNumber + { + get + { + return this.CC + this.Number; + } + } + public string ISO3166; + public string ISO639; + public string MCC; + + public PhoneNumber(string number) + { + using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("WhatsAppApi.Parser.countries.csv")) + { + using (var reader = new StreamReader(stream)) + { + string csv = reader.ReadToEnd(); + string[] lines = csv.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); + foreach (string line in lines) + { + string[] values = line.Split(new char[] { ',' }); + //try to match + if (number.StartsWith(values[1])) + { + //matched + this.Country = values[0].Trim(new char[] { '"' }); + this.CC = values[1]; + this.Number = number.Substring(this.CC.Length); + this.ISO3166 = values[3].Trim(new char[] { '"' }); + this.ISO639 = values[4].Trim(new char[] { '"' }); + this.MCC = values[2]; + return; + } + } + //could not match! + throw new Exception(String.Format("Could not dissect phone number {0}", number)); + } + } + } + } +} diff --git a/WhatsAppApi/Parser/countries.csv b/WhatsAppApi/Parser/countries.csv new file mode 100644 index 0000000..2d91880 --- /dev/null +++ b/WhatsAppApi/Parser/countries.csv @@ -0,0 +1,254 @@ +"Afghanistan",93,412,"AF","ps" +"Albania",355,276,"AL","sq" +"Alberta",1403,302,"CA","en" +"Alberta",1780,302,"CA","en" +"Algeria",213,603,"DZ","ar" +"Andorra",376,213,"AD","ca" +"Angola",244,631,"AO","pt" +"Anguilla",1264,"365","AI","en" +"Antarctica (Australian bases)",6721,232,"AQ","en" +"Antigua and Barbuda",1268,"344","AG","en" +"Argentina",54,722,"AR","es" +"Armenia",374,283,"AM","hy" +"Aruba",297,363,"AW","nl" +"Ascension",247,658,"AC","en" +"Australia",61,505,"AU","en" +"Austria",43,232,"AT","de" +"Azerbaijan",994,400,"AZ","az" +"Bahamas",1242,"364","BS","en" +"Bahrain",973,426,"BH","ar" +"Bangladesh",880,470,"BD","bn" +"Barbados",1246,"342","BB","en" +"Belarus",375,257,"BY","be" +"Belgium",32,206,"BE","nl" +"Belize",501,702,"BZ","es" +"Benin",229,616,"BJ","fr" +"Bermuda",1441,"350","BM","en" +"Bhutan",975,402,"BT","dz" +"Bolivia",591,736,"BO","es" +"Bosnia and Herzegovina",387,218,"BA","bs" +"Botswana",267,652,"BW","en" +"Brazil",55,724,"BR","pt" +"British Columbia", 1250,302,"CA","en" +"British Columbia", 1604,302,"CA","en" +"British Columbia", 1778,302,"CA","en" +"British Indian Ocean Territory",246,348,"IO","en" +"British Virgin Islands",1284,"348","GB","en" +"Brunei",673,528,"BN","ms" +"Bulgaria",359,284,"BG","bg" +"Burkina Faso",226,613,"BF","fr" +"Burundi",257,642,"BI","rn" +"Cambodia",855,456,"KH","km" +"Cameroon",237,624,"CM","fr" +"Cape Verde",238,625,"CV","pt" +"Cayman Islands",1345,"346","GB","en" +"Central African Republic",236,623,"CF","sg" +"Chad",235,622,"TD","fr" +"Chile",56,730,"CL","es" +"China",86,"460|461","CN","en" +"Colombia",57,732,"CO","es" +"Comoros",269,654,"KM","fr" +"Democratic Republic of the Congo",243,630,"CD","fr" +"Republic of the Congo",242,629,"CG","fr" +"Cook Islands",682,548,"CK","en" +"Costa Rica",506,658,"CR","es" +"Cote d'Ivoire",712,"612","CI","fr" +"Croatia",385,219,"HR","hr" +"Cuba",53,368,"CU","es" +"Cyprus",357,280,"CY","el" +"Czech Republic",420,230,"CZ","cs" +"Denmark",45,238,"DK","da" +"Djibouti",253,638,"DJ","fr" +"Dominica",1767,"366","DM","en" +"Dominican Republic",1809,"370","DO","es" +"Dominican Republic",1829,"370","DO","en" +"East Timor",670,514,"TL","pt" +"Ecuador",593,740,"EC","es" +"Egypt",20,602,"EG","ar" +"El Salvador",503,706,"SV","es" +"Equatorial Guinea",240,627,"GQ","es" +"Eritrea",291,657,"ER","ti" +"Estonia",372,248,"EE","et" +"Ethiopia",251,636,"ET","am" +"Falkland Islands",500,750,"FK","en" +"Faroe Islands",298,288,"FO","fo" +"Fiji",679,542,"FJ","en" +"Finland",358,244,"FI","fi" +"France",33,208,"FR","fr" +"French Guiana",594,742,"GF","fr" +"French Polynesia",689,547,"PF","fr" +"Gabon",241,628,"GA","fr" +"Gambia",220,607,"GM","en" +"Gaza Strip",970,0,"PS","ar" +"Georgia",995,282,"GE","ka" +"Germany",49,262,"DE","de" +"Ghana",233,620,"GH","ak" +"Gibraltar",350,266,"GI","en" +"Greece",30,202,"GR","el" +"Greenland",299,290,"GL","kl" +"Grenada",1473,"352","GD","en" +"Guadeloupe",590,340,"GP","fr" +"Guam",1671,"535","GU","en" +"Guatemala",502,704,"GT","es" +"Guinea",224,611,"GN","fr" +"Guinea-Bissau",245,632,"GW","pt" +"Guyana",592,738,"GY","pt" +"Haiti",509,372,"HT","fr" +"Honduras",504,708,"HN","es" +"Hong Kong",852,454,"HK","zh" +"Hungary",36,216,"HU","hu" +"Iceland",354,274,"IS","is" +"India",91,"404|405|406","IN","hi" +"Indonesia",62,510,"ID","id" +"Iraq",964,418,"IQ","ar" +"Iran",98,432,"IR","fa" +"Ireland (Eire)",353,272,"IE","en" +"Israel",972,425,"IL","he" +"Italy",39,222,"IT","it" +"Jamaica",1876,"338","JM","en" +"Japan",81,"440|441","JP","ja" +"Jordan",962,416,"JO","ar" +"Kazakhstan",7,401,"KZ","kk" +"Kenya",254,639,"KE","sw" +"Kiribati",686,545,"KI","en" +"Kuwait",965,419,"KW","ar" +"Kyrgyzstan",996,437,"KG","ky" +"Laos",856,457,"LA","lo" +"Latvia",371,247,"LV","lv" +"Lebanon",961,415,"LB","ar" +"Lesotho",266,651,"LS","st" +"Liberia",231,618,"LR","en" +"Libya",218,606,"LY","ar" +"Liechtenstein",423,295,"LI","de" +"Lithuania",370,246,"LT","lt" +"Luxembourg",352,270,"LU","fr" +"Macau",853,455,"MO","pt" +"Republic of Macedonia",389,294,"MK","mk" +"Madagascar",261,646,"MG","mg" +"Malawi",265,650,"MW","ny" +"Malaysia",60,502,"MY","en" +"Maldives",960,472,"MV","dv" +"Mali",223,610,"ML","fr" +"Malta",356,278,"MT","mt" +"Manitoba",1204,302,"CA","en" +"Marshall Islands",692,551,"MH","mh" +"Martinique",596,340,"MQ","fr" +"Mauritania",222,609,"MR","ar" +"Mauritius",230,617,"MU","en" +"Mayotte",262,654,"YT","fr" +"Mexico",52,334,"MX","es" +"Federated States of Micronesia",691,550,"FM","en" +"Moldova",373,259,"MD","ru" +"Monaco",377,212,"MC","fr" +"Mongolia",976,428,"MN","mn" +"Montenegro",382,297,"ME","sr" +"Montserrat",1664,"354",MS,"en" +"Morocco",212,"604","MA","ar" +"Mozambique",258,643,"MZ","pt" +"Myanmar",95,414,"MM","my" +"Namibia",264,649,"NA","en" +"Nauru",674,536,"NR","na" +"Netherlands",31,204,"NL","nl" +"Netherlands Antilles",599,362,"AN","nl" +"Nepal",977,429,"NP","ne" +"New Brunswick",1506,302,"CA","en" +"New Caledonia",687,546,"NC","fr" +"New Zealand",64,530,"NZ","en" +"Newfoundland",1709,302,"CA","en" +"Nicaragua",505,710,"NI","es" +"Niger",227,614,"NE","fr" +"Nigeria",234,621,"NG","ha" +"Niue",683,555,"NU","en" +"Norfolk Island",6723,505,"NF","en" +"North Korea",850,467,"KP","ko" +"Northern Mariana Islands",1670,"534","MP","en" +"Northwest Territories",1867,302,"CA","en" +"Norway",47,242,"NO","nb" +"Nova Scotia",1902,302,"CA","en" +"Oman",968,422,"OM","ar" +"Ontario",1416,302,"CA","en" +"Ontario",1519,302,"CA","en" +"Ontario",1613,302,"CA","en" +"Ontario",1647,302,"CA","en" +"Ontario",1705,302,"CA","en" +"Ontario",1807,302,"CA","en" +"Ontario",1905,302,"CA","en" +"Pakistan",92,410,"PK","en" +"Palau",680,552,"PW","en" +"Palestine",970,425,"PS","ar" +"Panama",507,714,"PA","es" +"Papua New Guinea",675,537,"PG","ho" +"Paraguay",595,744,"PY","es" +"Peru",51,716,"PE","es" +"Philippines",63,515,"PH","fil" +"Poland",48,260,"PL","pl" +"Portugal",351,268,"PT","pt" +"Qatar",974,427,"QA","ar" +"Quebec",1418,302,"CA","en" +"Quebec",1450,302,"CA","en" +"Quebec",1514,302,"CA","en" +"Quebec",1819,302,"CA","en" +"Reunion",262,647,"RE","fr" +"Romania",40,226,"RO","ro" +"Russia",7,250,"RU","ru" +"Rwanda",250,635,"RW","rw" +"Saint-Barthelemy",590,340,"BL","fr" +"Saint Helena",290,658,"SH","en" +"Saint Kitts and Nevis",1869,"356","KN","en" +"Saint Lucia",1758,"358","LC","en" +"Saint Martin (French side)",590,340, "MF","fr" +"Saint Pierre and Miquelon",508,308,"PM","fr" +"Saint Vincent and the Grenadines",1670,"360","VC","en" +"Samoa",685,549,"WS","sm" +"Sao Tome and Principe",239,626,"ST","pt" +"Saskatchewan",1306,302,"CA","en" +"Saudi Arabia",966,420,"SA","ar" +"Senegal",221,608,"SN","wo" +"Serbia",381,220,"RS","sr" +"Seychelles",248,633,"SC","fr" +"Sierra Leone",232,619,"SL","en" +"Singapore",65,525,"SG","en" +"Slovakia",421,231,"SK","sk" +"Slovenia",386,293,"SI","sl" +"Solomon Islands",677,540,"SB","en" +"Somalia",252,637,"SO","so" +"South Africa",27,655,"ZA","xh" +"South Korea",82,450,"KR","ko" +"South Sudan",211,659,"SS","en" +"Spain",34,214,"ES","es" +"Sri Lanka",94,413,"LK","si" +"Sudan",249,634,"SD","ar" +"Suriname",597,746,"SR","nl" +"Swaziland",268,653,"SZ","ss" +"Sweden",46,240,"SE","sv" +"Switzerland",41,228,"CH","de" +"Syria",963,417,"SY","ar" +"Taiwan",886,466,"TW","cmn" +"Tajikistan",992,436,"TJ","tg" +"Tanzania",255,640,"TZ","sw" +"Thailand",66,520,"TH","th" +"Togo",228,615,"TG","fr" +"Tokelau",690,690,"TK","tkl" +"Tonga",676,539,"TO","to" +"Trinidad and Tobago",1868,"374","TT","en" +"Tunisia",216,605,"TN","ar" +"Turkey",90,286,"TR","tr" +"Turkmenistan",993,438,"TM","tk" +"Turks and Caicos Islands",1649,"376","TC","en" +"Tuvalu",688,553,"TV","tvl" +"Uganda",256,641,"UG","sw" +"Ukraine",380,255,"UA","uk" +"United Arab Emirates",971,"424|430|431","AE","ar" +"United Kingdom",44,"234|235","GB","en" +"United States of America",1,"310|311|312|313|314|315|316","US","en" +"Uruguay",598,748,"UY","es" +"Uzbekistan",998,434,"UZ","uz" +"Vanuatu",678,541,"VU","bi" +"Venezuela",58,734,"VE","es" +"Vietnam",84,452,"VN","vi" +"U.S. Virgin Islands",1340,"332","VI","en" +"Wallis and Futuna",681,543,"WF","fr" +"West Bank",970,0,"PS","ar" +"Yemen",967,421,"YE","ar" +"Zambia",260,645,"ZM","en" +"Zimbabwe",263,648,"ZW","en" diff --git a/WhatsAppApi/Properties/Resources.Designer.cs b/WhatsAppApi/Properties/Resources.Designer.cs new file mode 100644 index 0000000..b71bd0e --- /dev/null +++ b/WhatsAppApi/Properties/Resources.Designer.cs @@ -0,0 +1,88 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18051 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WhatsAppApi.Properties { + using System; + + + ///

+ /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WhatsAppApi.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to "Afghanistan",93,412,"AF","ps" + ///"Albania",355,276,"AL","sq" + ///"Alberta",1403,302,"CA","en" + ///"Alberta",1780,302,"CA","en" + ///"Algeria",213,603,"DZ","ar" + ///"Andorra",376,213,"AD","ca" + ///"Angola",244,631,"AO","pt" + ///"Anguilla",1264,"365","AI","en" + ///"Antarctica (Australian bases)",6721,232,"AQ","en" + ///"Antigua and Barbuda",1268,"344","AG","en" + ///"Argentina",54,722,"AR","es" + ///"Armenia",374,283,"AM","hy" + ///"Aruba",297,363,"AW","nl" + ///"Ascension",247,658,"AC","en" + ///"Australia",61,505,"AU","en" + ///"Austria",43,232,"AT","de" + ///" [re.... + /// + internal static string countries { + get { + return ResourceManager.GetString("countries", resourceCulture); + } + } + } +} diff --git a/WhatsAppApi/Properties/Resources.resx b/WhatsAppApi/Properties/Resources.resx new file mode 100644 index 0000000..9f55b98 --- /dev/null +++ b/WhatsAppApi/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Parser\countries.csv;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + \ No newline at end of file diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index 4f92a36..bd10ff6 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -59,6 +59,7 @@ + Code @@ -67,6 +68,11 @@ + + True + True + Resources.resx + @@ -79,6 +85,13 @@ + + + + ResXFileCodeGenerator + Resources.Designer.cs + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - ..\Resources\WhatsAppApi.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/WhatsAppReg/Properties/Settings.Designer.cs b/WhatsAppReg/Properties/Settings.Designer.cs deleted file mode 100644 index 2786eb7..0000000 --- a/WhatsAppReg/Properties/Settings.Designer.cs +++ /dev/null @@ -1,26 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.18051 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace WhatsAppReg.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - } -} diff --git a/WhatsAppReg/Properties/Settings.settings b/WhatsAppReg/Properties/Settings.settings deleted file mode 100644 index 3964565..0000000 --- a/WhatsAppReg/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/WhatsAppReg/Resources/WhatsAppApi.dll b/WhatsAppReg/Resources/WhatsAppApi.dll deleted file mode 100644 index 5be8b0c38e21ba0520e7eaf1f416b4f409964d1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89088 zcmc${2Y^&X7B*Tp^zAs|PB+~>GYpfM;nLkhR+1>7fG7%xBEbNNt0>ZN2Mi2@poj@n zL^0w5x+Ne zFTb#E)}eLoqIq?5=GE;sep200`Pql|Dk+I{S3yr0CB!(*5U1aq*ryQN8$z#-YEdEf z)r7Dl^~qM)>tJ7ouMp*I*QMMX$1VhZeiGz`8>dytU0?%wMlZM3Jp=@n z4Ki{7q8kp%%+do6*eu;_M~r6XAY`+cuRGwh^fn?n z2{3bT%+O7Dpb=JP4;4yl7dW>gBr_x8eGD-UQi9Re+lXf5lW@+J6i|x7a&1HrG2Gtx z3y+I~$-ecvgV3#bzz*ap zbUWY<)KGTEf#f(a>_8^L>9NEs5Dz798xd=DPh?y)5%UR=3??n74uM)#Q`xzsia&8P zn6%Ag6}tti!b!NZT?*EUlt8R5!6tPJVd07wh< zwUe9&5u;20r2F9Z*o~^bwMN}nh_ZKT=~)OGl^3dJ<$EB+$e@40!Dxx8UcYJ4q=8;Q(~cD zznf55<9!0+e8h*w#FjxvbXH|#F6UJ<Oj~9n@8;#BD-S!mY=`o(ZMG)} zF~ylvG8Q7B&RC@GeUb3(s5`fHsKqKg)g(}hWu&I`L;oNfI#iQLq}UGc zwmtqzV@kh1e*W6>SrVp9s#^sAHHh6=0@RzX+<&aw^Lp8s^etY5ZnokS~2k}gOh z+a^?pZSy(RuhJh@ArbjL;8prP9R8vxZ$q|6lAXI1qhF&!)RiQyBx_M241=T3TCIKI zxY)`ZwDcroU#`qh72GnMesH0V5xuVL7}-@+2aUIt>m$FG>_H=;swqXL zaPmyBXiW>qtSz7M>~gXom`WLX+zeAsU+Hk@uvUiH>Pwc5;t|zoe}+R&=n2L~I~n;Q zl=y6IFh3L)%HupFaX__TUr?|I^GI`~<&~|GV9pBWQL7@aWi$-(bkvCK4pc2j=S)Fu z5$Sqb>N+&KPW+6KRkuT)z1T__b+C(rVTCBnkHTdBN*}){69?CpD-WLUw9oe*Iyd|V zcUuk=n*yY^bu7YzFg7$ZV%5=*nd&E%M;-OfQUxprDs9o|gdM0$FE~K8mD0)KVdiql zThc;P zjBI+^6womuGii2_&6!j)jW%=~7&9gNGDMfkwaI8}AU-Z#6$I5Lk2|JIJ7=j3HMh4v z^bug-xJ<;wfl8boj(o=$Fq9tw3k{5@c7&wbe7H3d$^l5V5mIk9iK@G#-aMqr;k!aP z6dJfNDjCU|4PulI{iNDP7-emstak@L{TVIj=%^%RWzC?hQ0~3cDp=~fWu_Yr6>mn- zsUt7Y(dNniVqXxU)`jMe8^c@|)%N8_B5gE2Az3m;?eILh@<=q;rB_!2g{5mFH$PXPsU12k2m`6y2cNjP)oM<_6x29s7-Q;~X@~cddWn`w z^jL|=;FLkSHV$EPat45ro6wDt)K|(pKjK?jhJmhD6gYt_s!fQPk%#ma?+tt#8+>N<6Q4BLc&d;PEQF)?4 z9Xhhr%FiO|Auu?MlMn{f67`YPAwQcO4}~GIdpqUlA3R)X0w3n#7Q%-UHXJk;PtSL_PIb=x7Iv-- zC1Edj^fs!94hCfr&LYWB8(xhmghnd>$!ck}F!c_nxLh{bR?4joi21Oj4H%ULRAm7< za}gMqq&Pk z`w61cq%>}_gK76DNvcGI!7BI95>+FLU|07+iE0piu$%jwM77}%U~R+=T9l?>ZPcca zYGZD_Byj9BphZ2C16sC#fC?fBYykn9E(vS_0jetrYyknfCkbr93L8|2N6RQY>A;qP z1CK}cfWB3czNM)aVLnH?8@0II>p|^aEa;Actf4vs&4P0j6#%N2m7fP&v%k4}Hpr?B zrJ*`L8R1q_UC4uv>OuwTg29zBKOdx^K{bF|xPI@-uW?|mu3(EJL5^{rIjD)p^)CsMI6Z&!C_(O%=u zDcZZa#}w_gVfQ2tMBLRLh`JYeAm;wj110Vi9w>Eh@<5q;j|a-#$2?%WFLR#l58uw2g=;q$w zfm-)Y57fC&d7$2X-2)Borygi@wNNn$-Q5xoG`ZCtXm(pX(8C?(0mmKdfu8RE9>};y zdLZjA@<7f#(E}~+dJpt+|Kfq(?)@I<<38tszV3S-=;warf&OkVTujdZx7q_cxIH|u zqubvD1KqJ6801d%z+m?f4-9czJuuWg-UB_%z(MX94;<_s;(;0NQ68A-9^-*o z?x`L)#68mkv)zk4aH#ud5B$Ns#RG@A_jurN_Zbh&ao_d85$-1*IMOvris_l_HhJJE zcbEs}xf4B*cMtTyeD^31EO1Zufa{*?fmV032Nt@wdtj0Kum_HIpYgyk?yDX+)_uY8-@$d%1Cwh1z;XiwL z6XCl(d@137d-yWKhFz5RPlRc;P&T}EmlN*dxnDten1`<!s)WdfXKFPy(6F%3&enhwmr+Zx25}_-hY8NLY&()Bg}*+rtkN?(5-42-9-2(w8lS zM|tj#5Q+#J{l0gDmt&rN3p1x1aRsQ0DB4MmxC8u0SAH- zs|ubmNA5$En7OK8GMof~nM27itnvUtHVITXjF1eIC*}zwAtcja$P^`+keE&~5v3pu zP6c3Fp&$&oSW*}ydx)GRb83E~{SL~XN`#9%vZY<Oee~%?Rw${j&_Jjcj#V!N_y) z^ntk)FX;JQc*Q2;r%+y6f|{mo2)}h9yhq$u5K=cj-GY@oD_KX!&+?1l#HSTD1ykFh zrME+?BwCsBTPKF&)RYRLid=Rnx-9cts<#tP*X_`1wnLMrY>*aO>l}bD^u~7MDXjZq zy`mQD75Sr)3M?pcz2nT{EQ!eFJGprAGOeSVyvLoUJUN*11pa%#f0`FAd@Co%z|lDt zhFS(p7Z*X&Q~N$C*8(Yz!@RFlM`c)dV-Ty9sU-eVt;do7#S}w@aN6)aBGb!RLd3(N zohW`zOEbRpd&75JhSgq{{b1_yU=R-iOb)!adW#uR#C#~a5 z$Cv)pzOB*JVm0%5nDzz)teZ_OvF7AzXokMf!m3hxBT)mAY7;d?)b2!;XII*rU@tXu zA)Lr!JLRcdlR}8t51`of-x*I!D7l(q$;MLr#SyN+CTpAMG9%I8NX~!*?YDe{gd|i$ zhJPcSkl%JIp=X9$15=lqR7)WfmTM_-xzX~=o0TQm?qS}nj9P9z*n_-d8I*gK33!q^ z8J1g5&u@){b5>YxJz4HRc#!ToZ)4?V$}gSS%O?9yk|;kM!C<4K4yS5Hz#6kchC8(2 zRqg?gCGp_C6TU4kZH?i$R}gR*;A@sl4WGD)Snh~oqGbG>GH9pVZ^(*1)8&4HF3u4x z_sOG<1ufje;NNC}n z%sf4plp2`B<{VWxqnabkBxQRN5HCRf2<4ruH{98*?v1pzJ7CY&>uvPa+mJ?OUZOWK zN=<3^h>{JP9#84X_8!EWWY_FIy52?@C*O!0?bU&6aP-Ct^qeWO>yo~F4$_|k;j-I{ zktOZ7QP-1A5%z0IaZK*yo#mt<{d9vnSL87@pz%IIaW2w}mdp-B2Cak@(veuHf)Jb{ z8S{TDq}i03Qm#jhAX?#qUtcl{B+86Jn$MX@2MMO4)fv|M^UpuaGBK&I5@5qQtV?6i zaI|K+$+pV~>ZZltjJvca!fJ`Mo`mErBRdn?5De{+>a_MxXSxby>&yWkbuQCTke~bTTn713$ZcLWf^gkChkmM zI%>?8!pY)p?L$DidNHVrs_pr|1jMbXH(9zVxRL-bf~o zRR*tP>#0bGI|glZo^0I4=JcA3&XCtr{SK*u1MN-t9nu8{ngsbBDhm!Yd-6L}6&z@} z(eF?#+ZvJ^-4Flixt;Wy{uaxb(NnO|LT^S-!$y*1^h($$>Wp3m8^x8;tH~9CquqyA z?LuWmTE!L+h8GBI0Rgd@G*f6Ni!Srd6ho+F@Ta%!4KkmlZkqrbL|U>dzRqdLw_I<% zZ8swJkZd}$C~Ye z5$`CN&N&?dWfI!(NP914*?n6UCNT@f6^M5XOfOzS?S0r`bO#4Q?Y-H76vp#GD39=P zPJ@b%l#hytRf{Y-QAl+#VKZ7$4I}M@m`qy2jAYR>JIWvue5yWkYX`NWQo`9Esf1KM zSq%kJRI|)=IIOfkLTM*$g?&GKe6bmAlgUxW1A!=`SRiOpq@558M5$zf$ifuL5T=lY zDU^YpS3*pt@?`S)6e1xeQ+cs}vM_~ogBdR%MnX)c5@$xTFopDt880M8LORYBnF4i* z|78B)#FDROhGd?!MBV++%C%kupNr9nbEbHC=gYX(RVcc4u6P-QO5x`_hF`8~AcCeU zhM>24L30VTRQbv!m-YcLAC~Sc2kVCntQFO$5Iag`d?CfGCWJU6NP5m1P6p`{H786V zi_SKg1h|oqOd$*Vr@GG+vM_~O9nM;gh!BzGWU$Z$8||b|m<*CCp*ti6;&H<=TY_~jnCM6Ybx+|lC29qy$++nj5hWwP9+IL0DJPBx zPV_B0$+-t|l{#y?h8H<75_8{0dD0CY4rr5=cE4f*PF_-KS3ym+O;(i~tt2)yzZB>u ztE=0C3C<;m8JS0O(kTc#i?H1+oG^-W7vSr>1dYIS?>N|lJgbWFd{`QE1}Nk_50(;j zKswCbd$O+;<^$4Uw;dk&N))e6pn8fN;xL_;;VI&7hO4}eR1!|=o5Cq$Q#fsH3Rea; zg?UjV+9|dm#}*55=#=NCpgeCOxd&CzJ+BQ^_H6P!FI<*MS?^SL$<82A= z_+>NixfhPS73ZL_fpSiSQV_jHhzM#GpLwHkb#KJWN5F7wY!iZ%r^0yUFB)`SL>|iZ z1n_k{@0GUPM<_djYgY9{c=C))@vPTd-ue#COTlj+isw>9P$6)5?G=O@bYFyj=Q7Z6 znQbfVQ6uKQgReTY^a7apuvm#Z-VyV=vPSY@DxHo*;j~czC5huubPB2pXN>~YVHjMh zbS{zW3kDrrb~>Mul2_-Fa`+dqEG{OblcGYcjzT}37>w4-A(f=UKj}cd#sgNYGhP9w zh*jI7&W9%Z))Kf9)R=)|;Jger(X?<6CXe1HGBIOV6O9Q?C4-@m;-~o1TTwq+X4bTB zhGUca4pNq+Bg?RV2zyG|KZ8B3?EitiQrUliy-L~jo??ErvWH;r%J!e)%P0yJQ@Fx( zu0rU!18wscN8;2G^uxdGAk!^HKoc{jn^4~>^{rFiX7$afZ$J4pCh#D4A_YZjKYzxz zKg!%pO!SKVEo8GHb9pPQF?SF=8(av^n;?|9lXevIWz$0!$xvZ&r;JXZQ9%c(po3Kb z7}a#ttSd@8SS)Slbp#L(;D(R8<3Q-u+kg|z3C`6>d`ln|x3<>7i8jgD$=!*VNcrGm zH78*To7Qd0AvkUqUV6E(P~;k=WezCRruamUxTE2JyA6w3XjpIVHAoXGGiAs2<5q6P zxTAjGUStsC2V>$M2qxef-O>qQTdM_FSftaw}#D`cqid{R?3 zUtYU~Htc#Z$NWcmL+Y-ftq4FH;@ROTC0e2BZCWuxSj0(H{RTy@|p;fQ(KNEX`BJwG%&C?;w!Y zo58Y-NKB{ety#Jsn{`n6cpdNmW}ESs|M%MriUMs0eg6MwGYb0>JCcsMyP$Lo?nrBG z2O>q9BKh0!$tA7)?fAMlIX^#t2Wkx*0hIX3LPm62J3LDs#X%I5GWQKy1(_ zOMHqFABgx`9|Q+3SHFi@cYZPKQG?Hfu{~yRFV6O6gU=?iy|{2+V(`{76XT}A``i)) zl1IdGJ5n}5-R(ey<=h1sa+TKcCqM|>n(N#R%oEi7-w?0tzp>JMJh4I8V95CwNza@0 z1$v2Ygu+3D`w2VI@x+1?u6Trk7#V%cE_AS`;DYNGp`i02(fOSI$w>Pkq}|B3gVt$( zAwL}P$Flp$wOQ0{3Q4XI_prRwMxDQcFUTiv^7oL)jaZNR2;pU`EI#ic{a~a?nQjL1 zxE)`(H*%?e8CSNuI!@8fTq4^5T1!4f$*BK|jFXEp()kM+U!&zK%3!X)fh7}K(4nFY zI^UCgwH68Nbh6FRHwdPG7gp}*u)nZnAK}J)YXbRff{FQu+#|M>x$&TkoAd8Th zbWl`A95YkSw-9I0zYLgsqN(7JlnyVj107+N4#NVuA$&W^$}fc%+1i=O05847O3hFn zV=SvQkcVPfc{D{>NXIP2Y94etheRjTIHbY*={S5pcs$Hf(^sanXkq1Vfno6p2+p)T z8b#nXs&tfz)Z*hlyn9lCuyi@f*SQzzt1z7VNE5=PW;l;_n%L-Mjj9p23twvoeC(zJ z#fPq!V!)QamiSA}Jla+(o~TIhI?Ukrp#}wFmx?aVneb3O{8>;N@sI!FmS+uGd|*&U z7_#_?p9En$lr!ycOF8fU#zXQG&hBc5d=gpC{UlKrJM1F{nb=b4o{1#eAvZ7>pRj{6 zh_t>3X%|C&EB`0h>|h>44oWti(vuOR(CIXZ1W{WiH$o#xtJ#in;-h(t2ff56E0S^% zYC<99sfrY*ysG~&iw_#|CQUqM$8v`w@|YJLmyW!n9rLtAszG)qi%;ybDBOh6F`Rgb zUBc&aR#j{{kJMooG@FfbS$fydxe;dr@Sp6g|o^Lc1F zeXauvqsV0V%iyutB%O&SDZ8Ku4<$Iu;?s^YrYNNWaaqmTJuE&SDd{K%uY_&e&gDwY zq_+Z9Y6f`qsnjeD@ER2gauY(a?e-~<8_mH^P@W<0e*g2&*kFufgE1~Q7{P}cWX01J zXaw`5$8KdY|$1wQx4=}W-c5^$u;X{xnD~=m!$&)UH;H?+@|Y; zxiX^Wv1bo_ha&Hmx+uH|Hb->_qlU=BdSrR*cp{>EEyMUPVg>uZA=gs2Bi_+5)#yz*($GFJW(WHw*!86__@=C^>b=m3;16tzA?drcUO zg@UP$D+~F@KyO9{>xo4EC+KO1t3NkW+5UeY-#qBwa8EBfrf@1Wvt2?ga8*;}=t`)g@h ztEmj|_9$#hiV{d+1|UaiXF1lh92T!rkMwk@qZ|{rBZn^!Dm&*vK@Jr=E@2raB9C;A z{L0MY+V0DiLs7RmowV!(P>ajRIB~ERn07MxBHGE)vF#lC(#{-PuwH#`fxs5zEv+wM zlEdfxbR0{?;y;GIre62PT2r*2K~_0=;X@VerxL+vKb<~i`%3x*+E2r$)LeEFKJuub zZqXAjR{Lslw^~zVUq$v*J4vRymFN%a{*XXDY@;YzM zh|+3$jt0w5GSd9+2spF10%Iu_DKE}0v{6#|jwS5~te|N3)|~|L7*2P5@^}}FJa?cS zPc-WPbUs(J8{|tsD3&nllaLRRC~5OgL&&YcpcIz8tA43WR60`LKge{+^?OQT{u%g5 z{zMzARGw%z#3;@S%Jc-i>4gn5%F#p~ENENW&nB=8eKyQ$nC?K7h&!H)g9~LNj}Tmg zpdlLh=40&q(hfbM(+i1<=}rzGOtPrdg86X({t^r&^DT&Q8+zDJckl|7r8_M!O1rC=V#b$odnz1gU3VH;jbaTg$$c=51Y%VHLe z4dwzgGSz6e7HNXkbqev8Yi8?nl#{&16SeZs12n}W7%N8H_2l_75L(KCt|+2Nw%ok| z2nPz?2l;Ecuh5qRiJ7!WjCJ_d7x9Uk?x*DOC9>nvFGf@^lNFbKX~i4MUNOvnFmsm! zy^g~u|M4n4#3ul({NLfrH1H4=l?xU`2mc9bg&D?Z!|{-;cXmjY8PypFjf*&1bU9s)<#cg`=zT%KK1+^M9ceS1SCQty z$UV=ilY8l}v&i zYf6#)doWOlIDv<`9|lH|*IM6)iGm7XVfq7N`49%O{v-MM7$2-v$uaK9B70+DjBANe zrl9vtY@XUxtu8~(#jEV9BoD^jAE8$Kkj^H%D&+=-6du|EO!In-d}ar*k|*V51|MoF zc;P3qB79OP$~XNgWdzlBb&{7#f{L>`#n&phktvX51bn@vK(bnoMh%Xk4LcF)B+EUN zUkhbOmSiL0)RYq0Oz3Uo-eQKU`+X?wxt{XNH${2Qki*U*akY+uceFh+Ozhq$Mq068 zM&A@VP~ZyiYINM<%7{gEjO`+{EQaL-e%*lgvEm^FV;(pp82zSy>aJBOPLSl+S90{7- z6)2?fOyVkCH5|EE;F6J-DZh1hqRDRk6rs>0KK~gkt)COZ{JIeQ7r{UaQ|?+39%Y)-HBEl(pWjx9gy+m+@pb zk+Cb$?TQftVBnA5V8wJ%(COa*zB=-r=osgU{-6y*+VEle9%|&5-vd?A=?jCNEG-WD zjQmm}&GL~71FvP<@=hZXjfDdIw#pvVw$Uwr+4SJ{glyR^!n&AjR)Q(MOw0Fj6eZ1< zn55<_l5EQ4n_Osw#hwT^TP~X$eqe_OCVCqUu5$0|PFIK_QEhoE)>`R_pV~wo%MX$E zHQ>?Pcu?F%cHbaPmjoohVkL_Pd0j(Q!lIht8BvOeRZem^bKoT&s0eTa>?v1}H4Bf3 zP(-05KciW~cN$Z&EvA=2I%kP(g&0Vj+vi;&>9M7czmb1tvoBfy9#VFTO*!BaT=#L3AFqLY8XmpY(&`>;+jk zhcG@}OIh(f0!{WEqtFjPaVwj^*2j;q;$#X{e2X&-G>noXEpKlvt+0Mdc0r)B7xhh~ z3>*blZ@F?oS)7e2+Q+wIgD!$Lhxi;f_iA$WvRla8q2L80Ne<#kwn`{Nh#z;5OL$?> z`hb?58RF}Xk~&X0@+C4rCKx23ZD%*R43P z5Z?!vwCbWLbmx{kw-yqwAB#u#HYHzqNNznHhoGIGkyRm?%;SM`JNlDJcVXp8_FT7+ zJnf%AtHF1K3vv{aPdEJ~ZLePlVh4W^#T|o|l%Gs5JrqOcVZeVaTq7^Mwq%&#byJCPdC6jCP20(38kN*AqW46)mcEu{TI+1-A{VfGZ$4jMH(^R*H=k65mc{ z%D2PG`$c>d5VOc)3m?oMhlKH&C(N7Z?a#v5AkHxPBA+}P!~=sEhI{D{p92bp{pWya z2uODW`@I$74xZLOoZb|6FYYmyorQ`*v-peXON%3H0pa2Tfh{1cFA&%Q!X*U)TR`|@ zfxs3JHWUbK0byf-z?Q1Uw#dQ$Mo4O4Sb>eHyMmf|R6X{H31DG0 zDA2x%fYE*_ea!aD=p!G(Ca%nfK2Y5KW-q+nTV-{yh*3Jwi-xS`{fix zn>0DrHi|TF!cCI-LS&tV5r^JJQRQ)XMLE&>Vg%Lqm$`i*CZ7SMC**p#djW=8527&ew)cHO z9uJnliUTmX5$n{U$p~k-n^tFcx{8Wkq9-aS&f3YlCsD}ct4JzeELwAnG~o5A-??D{a(5=^aO0Jm%kphWHiJ^VU!Mc5^nwU@~82JZi%C1+V^3BcED zTEQU=#2nz)FSxBAY=aOSOxu|t(<37)t^mJaz<`!&cypDW4fO3ITfEsll$Ub~vI@s~aNvU8QghDn_=aphe%fJ{Zz^vJ%EA6G#x3KkM>vxxKFU*> z4m!k5;pj3A7Ka1zcy4w#zWWk?(=G-1jHQ1@y7;8q1=NsWKPIw_NKB_6V!%%w&^u`U zDF@YEexjl<k1d##%wcLNl*d4NnWFuAd{8lkR9dgPxtK9L(e?ELuNOCnxhef+t)EsjBw%L`RR4NU$;6wu2 z(QJU9@F=*}@T+*G*7jYy@iT0t!R@>1r6K2BsD;ehO=0=Pn84GoV8<-xy7Fsn1m=H* zb&TItPnSa%_owor(_pe9Q6X{8(gSd+5M)@~>ZZ8>s64!f)^VRfRvwuQQ7(3DuGN3w30=S4X0dN9s|rj?iN-h4}cvNPfLgQPO-|u~2LNr<&pR zr=L_&3i01o45}BHM#y?`jF+C@tQR!hl#v$d1*TvHidQeF58b8>q-T2y0&~)TTfHdX z{|-5^wqv`edvem4N0z#*cDtZbsa&NhN8=ps$2&w9<>8AGiyK_lDH>2IzJSU{T5&=H zqiTU{s8w|QC@lAVzLpsabpGZH$0m>~I@Ux(NgT&7EFt+TQHMqvw*gH)lI5%;Ws`b$ zSV8Y@DCpfqUiwMzJgw(sY^!(l0;Wuu{Oynq8yx>d8|-DQOcrI`?`wX!ZMdu;|6e@$ zsoj(XQLHy!qxs)R>**u4tyKO0ryhg!4YN1d7e3|5OT)lU^3tPI|9$RmE3bb?3HvCl zIKMlGNnXtDrM&NY^5OxV&hq;7c-WY-U4Ga$c>LI2)v<7LrYrHhj^QtoWvY(>K29M69kha=>*nY=E@5U;= zfKoip6x9>)wurym!K3NiF?so7Tfb|55EQE+JLmt^p6vhISb!hx>{K1;T|Ai}*~I@= zu97YV1uBlfqL^Ly;Rz_x&eZ0)7}w9oqiB4s(4{vkd9S%#Q@i;zANR&)+Zh?sYu-wn zG}j!p*B|C#o4?pPXR9o>-)IX+Cw^^QOb-*w=znYeDhC8d%Vq<4V$$y>$TM;N`J5SW zjw$j4c{4$)gDKqe0BQu4{gI15QQfc#Xc`0;pZb7e3%JK+tV;SMO zMJEm7Q$&1`TWQX=vka+AhUCP=Q#D9oc{Jpg8xfqF^Cnr?C}}~1%}3KzU$N|emA#|H zba6>hq{P2hPRd8OAPYS;+$kUbw>jEx<&x}QXpYGJ|G$g6$}5#pl;W6@9@wDo=&yZ|2gGeN= z?$D14;HO3J#aPWL$AD4haiJ+9z3Q|WN3X`Jf>4c+r)fwqX_{|tfkio=qWxxL1Q{vc zU&a@@ZM*cOb*IWjul)KIA;L!r{FRGtp174LG08?VbM8q;6eYJ!Ro}VdsKb5U)Z9yA zaXplNIza`uz4g!zsk95jCV$J?VZ5$ZrB9+Bg3?=Dlddo78LvsxdWKw+Rz6Pl`jktM zN8A?eU-ZMPJTfPWZIN#-lHwPJ3+=71W%{jJw5|UA?pj3UY4KXh3@T4Nb*0wa`REIN zv4_qa$P$*dxZ~`Dmdi~3T~Iz76OIOQz2)^*<%bK6OSd>Jp%vPI=f~GJ(kapo%dYf; zKkV6;LcFKq8?@OtAF~2=LypI>!{YUo9ZFP=l}?kz!)Zoclk5JpXe3pP<{}&&O-Q6R zL4Yk7o)$(%INFKPA^%i}a>wye^q6H@=NekvG$8$X3vtb){9RkjUxQ9y%8AB$NY#o> zvdA(bG2KaFj=>X+sBgl;E8M&@;h!u7aBLuF1aWOlWrBa&;2o0kO&bcsfB&?B7qI+I zG4#eBRmLC$tU;Z35p=!fJ^wccKOtna~^Wy zm&;JLJ#^0E5XwtB3goqute4x2)v$5ooR2_%d3g1kednK0WU{PPKmA|yoxj#6)9dvA zC*ks{AYILrtAZ^5*~k;_J7{7l8<^tw)oWnXCPrZ0gw$>Qulgc}2FcX~=?SgZ?v&7%7WGj&1lCE7J|INv|& z-b1AmN8MT6sa>VV-evZx6@Tf)S&K*&y%jfMBGGE;Fr3CYxcKGUvq4@OOM0P7I`>lV z+ojn1QjbNygJLY{yIt2j&IjQ~Cws#&mke@+m53weB^aZv4_pU7M3M4w8A>U47cDzrJ^5&-?uKvm(qhk2Oc*G$RlnNWm?-@2rtVOy(j%V4roVF8n?48iQb%ksE0MmO^u|S!}<;V>SLL< zWF9=pM=FOF5LTqnP~1iiY3OfD!*(1NlS1J9Ax;uS=HLytRj7#CrXU@dR0?0{oN4WO z5X#|jq$LqCa(xXajgB|hvlVA(aN&w?k+)N92Huh0M8$*Sj1i}@&^zO76}4|?APjDQ z=~v-8wZFbTNj9jeObSEz-&~LK?Nt7EGjgeylOfwADl0D{Dl0kT#A1T!Tec_+x4bA( zKGY@XotG^vgDa<6gBbZMaTGOyM-%iLDvHu!iffsL@J3GBW27dJ-1B9hqx<#h(`$!5 z1N#j`B-kZ@c8OK!P#TvC@eRHg_kw-WLU+!*!&-^s9JB{@5U6p#N#f>fwrKVJ*Ck#lL|od2k9?_YCkI9v+&T~mMh(NL(MK=Aa|n?UIcl)U&5|4{h!{*5>Z z|DZF>XW~m<>+z4qCeRJ0;|vS^{x#c)24!SB+7!{+OyF-0L{pn&X z5^jlO>!;LP;(VB;;#RhAVe_4Oinn(InFH7y%I4t>dp1~N0nAcyUc>3IKgjfN*^D+) z+DA9`Z?wep#v1(4&wY&>8zbVaM#_h9_n$$pXR{Z~h#1^G9E^xjFfDNy(-$)RRHi@D zeRFq9ywROxh%}MewTWWy+0;93iIbUf2b(Xk`3{@u<{G0^><`ltN3nTk^U`KZ+`;x! zG@`%E_BWb2S7Am(MGunUUp@A0h=^s*OguimlFe0YUclznY(50j5^p&aPDM{L8`$g# zGa^>>oY&J5SM{VA?`HGSo|Nk6*!+ymuh4Q4PD%72)%2JMc9P;8APtK*yXNCTq=v5+|(g%&Xy%&1u` zM2%me0ws=ij$dhsy0gt;Jv)laK62q@8AZ+2ys56c4S%r55d{=3=A%?s{PbfquD9Xj_+Lsy@PsF>7!uprs zTp-?Oe>IF;y!%D5_eN9M1-DawU^H4Gy!-*TsS25l4pk_`=s1O9;42qr>3`G%2&G)3 z+`U=98|XwlaY>*v^D#ENnDwBXY7%`Tn}1>R z88$5(GuOmUfpIVm>w==akF`lrCN6u^iinBJK89(Euh|r6nM~0boQ)VChg}o@VDk$$ z1EI@7Plq(dW z{Hsxt?HlYp86qCL0Oo^`-w?I2xPjjkM+i8$7O8@rBr#YLL!8HyjgflLNd`ka7ja-G z84U4$q&MtC!UJLcCqm)$h!LISF(3(#ZN{)x?7*?gGIXV~1z=KC-UA$(eL8$$Ss%^%q`N{Lcqkhzs9F;Fb= z0kjUSe<{gRReC?lDB&D|Vd}UtD(igNc+(commLqYTlwiQCzYQEbA9=x=Hz87?1-4W zY%QA`*u0L-yV-o4&6jOT(OYc)l+AD13|2&-V~rJE<4G~Fq7L)ti51;p&ZuxenOi~S z<$^~O$5iA%KfmH$P%f(&2>T68zm?6uRqO)FmWo|rK3zfj_HxCT_>N+?_`#q|h*Mb~ z%w|4*2q;e^#^dJ0r-`%Tg}kcil8aAXmSJ-sn z+=Ak^qQ4B#OB5)RV&EHq(W}q68Mz2Bs?gr{64p->m zz`6Ksl?N1h25z;Oh)T{^;Z`RmDD*C)Qxy6a|H)4*JNDQ{F!YFBHI!uFKtQ%$BAe!-G7KB!aJ+0gEOD*ZFMEhI!Ac%#G zmW#uzxwt@gnL_h{vf>|#^Ek`K$m7c*8jbgI$6~BUVXm;Y;04)Mh0X-(EpAfiLZCk4 z5rrqi)hBG9p;JR(BMj-K1Fo8He4)I=*Zw2ZKQaB(F$>N@H}l-F$HU16vGJ! zbCkHdKcSVOvk>2C@t{KMf;P~T3SA7hG2%sqHohGfEF-X z9>RlPK#P^zY`D!9Cn&dhaGNbomu{@{hl*>9C?gINw-r%~ewe5mLvoU2!}TM@2}QJ* zK2NMIqG|d9ahXKo(Qv!gDqh>2{fQTV7K*N8C3-7-sJ=*?uh2)~BlV+2cN|!t8uw9n zk$#NmtI&5y-!Wn^qjmV@hNWZzDiyjkIvS`!p_`&5 zVy(z1bXU{{>MxP#7aM`rX=o7*HO>*cD>N)N(l}R~F_B{syT(fJdt~d1XbjvoD71TQ zFJqm!vWWHr`invn;O{(fPZ5nV&J$Y{niQLEoG)Hds9if4D6o%=uU(rB)K{Syu_KKO z#NZ-o1=?MqIf&sxF`<>$qmL8qPd9f0P3UAt|faJ zSBgPJv>(tIiJSsh%o~bmD>!c~q8K=D5;t>v>qJv&*t}V! zX;~XBd%w~$^Hwoep;4tN^A7Piqcvi`(r)J6VmDgw5o^Sur7N}j#C(PFrE9dmiE9*E zQrc|ZFKD%&I8O!VLt?%{x0JS+4~q>`$!(o@sC1zDsF;hx*@T{jzbD0)3jG7<88IIR zW+{}95z6!84TZib9d5oLzAU1Z+KZwWjs+8^SvK5!NlYlBmD^TFh6(^El{A zoK0n8&DX?m<@RHbLo2t61U4S1wdq$@BK{$|GFl$mrOXCul5QwlTf89#6w$usn_@SK zL|fUDIG->a$9b^6BvzG8H{TZTFj^zlmmOmMQ=BqGy4_NCg!#T0aHvFoEn8%MB0g5= zQTY2p%>M(qtqXlnw!-{MT&9p-zQX*Ecv+!b`B~=IB6gVM+!^Q_FG5tz%0|p?UU)ChazmWDjZNuUiLywMDma zI1N=_MDy$~O}y;r^VhS3zf93>oc*(zE24RJFo1_$eExRS&Nzaw?3o8@c}B9o9jL97 zZem15Mhw(0l_+##Mc?>9?P`VItr!P%6Qkv!?<)2+2WodIx3c&?@j==H%B?m&1?Xw% zhWtD-K3JoD92$o#i$5ubY0oQkdORa`*4|WTEu*g#x+FdVZu(s5@49$vd}l49(A}8Z z@2quG=wYB;v;hje7C$yVTw9^gNAc;#aBYJ^Un7R$+BHYXFn^3c5A?Z0fkeADQtLiX zy2TRDi(R#+^Msa*%EZ$6DDCO_67@{1h>zB$ERvi95*aa0J5r$$39QCx^x7|_b~Mml z+Ib312HIP@hS3UfNMeZ?ubD>^=L&Hoqi+;yV|2hV(v4o{Wl~qiXKODO(P(j)_OA37+A%fVI9&TeA*xk}Yu`yuu2qL?#_<#bjbiVJ z&(Q`kS}9snPl_Y8%a)VdT5(e9{`g$2`w1TBQQ8DXE5&A{A+KGi(9NkQ<9Y4MB6>N# zKzmxDcaWk*+U_S(C@aOcsSn~uYl#&FWQ$|80SblENpY-Jev)(xrN4-`X$x0MRFlq# zleBvk>XEjA_BvU*?Eth=n^Hv4#K{^x13>YOO;;sO)1FpnIyhHpx1K89=BJ;>9>{|V zodUNrwWo`yDRGu|)@hRS0>r*n8-BV(*E0G{p*!Gjt#Gl}nJ4d@$p;y4UPJ2h8 zPr$iOJ7Klt)GGTX&eN_bqJfDEw42Y6Zl#qU#Mf)bo+(i`yf$}DN8E#i=Zz%L8&^6kib<$0%vcmuL_;Z`Ar+Ako;W35lDuor-8$;udX|LNltK7q@Hk zi|EM2om$rmrN4z$$0qL4^ot}~UiCrz0c}YU4Np9%&AnK+9WBIKutqEZdRHsw zoLD2SYnjvSpV|zTWsQjSnppF`cJJ;KgY3~i&>oRUjBF02KhSpDCh>g0zCK~I5DU@&!y*63bgL^#9)+pFB-c2b z(yuhTF-4l)+3q(jrZmIUMV9T-*HvLpPAS9J7V%)29&Qx^-EGB-kYiq{a!I#CL7ozC<#*_8IR zY?m>vhg}yNm@*_xdAkG0a;c9;a>}@*o$^W-*MZ-_US@f@$uelzD*?gbP|FB7NYS9`_X^N3dm#$;jE<+n%LwYiWDKpueU85t$IWP@zp^x*{ z3ZjT0Y1GCJbc$DMWOPy<|G}^#J1)&9M=uLxTLn+!M@5I@+Hgh0jA{3i1Gy6 z$<)OwFb(l8Q$AzLw`}k1Y6`6zomvRPlp*MXIB_Q)6jRi&YopJV!ZbMif((AW`gOa* z;YhOuc3t#iinLRVh8POd6n!~o{PbO!b5EFP;W;f{Xu8;k{gSIDrffdjcZKjvIpEO)9GcZ3G%t;sn8mr|qIMYK$Zn*W^EppsKSDjM zCXQxGJ992$yPvXxDXZB$n|Y`WF$DFDrXYGikY7t&$UK*`DMOaMw(O5&jIy=4hIwRN z%X0bpW&U-$4Ci*lu8ZrDA_Hdvs6Tz0TL6D+CRYA?M0UFrQ}hBoAa;aliJf5v z#a=K&Vmi#Qm<2PU!lxX@xrCyrbL@t4B}sH;mpLy>Q?@W9GtR^?4{;WJNxI*!q)-UD zn1|RkA?=G{H<)6I)0lp4(N%`Lz3?vtPe(buF+d??DWOcA-!4-j^H7!)xuUP|`2g#AS#p5WdNI8&LUqE5~@}HffO~Ov;Tye){uV$}denU8X|z z!v5TlS}moe6v^Jf6o2E`Ngp(>Wjgm7hIpJQay0ewHm+NlBK1(pKp_|MT#m;ieZOX+ zFYC2g(8~M`Fl})=oAdiH}*{~&=xh0 zNbbfwa^Seeqmm=E<&B3y!sU%_awF&4M(xGMQ{XGua0y%=YZz;8);?*x4X2U5 zY_!E@t-Sj^$=kvCVDdg~6iz^F);=zMJh?^N5A;`<{-$<(_Y$#1Ti^YSpuyg8o>;;`BuQqUPo4x!^w^`tzse5}e4rwt2d-P|Ob3CFinzF58!|eF@uF zuzdyFSFwE++s|S9Ic#6ZVV=Y0DmGWJxrEKBkeSX@J>7g`IxJ{kjO4jHy#Vz4(;J1> zVG3^>FuZaj=u}3ZH11wGR=m{X!Ni-|yYO`?N`GqQa`A73kC1WV{9`mClCB&H5i5=(&EUb9Lnn`cL&YRNk)BX#`Dw()nxUMDddIVC8-K=gx~T zXOz4KseW{RN|WoSmB(pMI^S151ADNFG+|QJD|)}4M^;VLhx9xK_B~*KMW5L7ahQkn ze6H$E{k)#jjW_jq_DfZl3DUI7AXP?upx@YYK=lXuZ7{nty*t;<&-Dj-?pFOR=ci`8 z)f4B|43Rmoy3(LpGY6@jT}>K(WOb#U&aBbC)!%JcuZ4{sneVlFV_4>b>Uv{z=3GG< zSR#5F8=5wNvOg$&5yF%%gAF(Hr|QARrJ3tsKAgD)<|moEs;9#Be%Pb`PkY}2+}3rT zc`iT@ASr?bXuiyc;8zqyrX*6LOsTXjil8LgdYPmwrB)XLflCq=2tas{V!WP^Q@7nD zPU~#l?snRA<<`xltY)pV{|0v^}@)ivyps=J&z7>)&(# z6m;#q`@RaE+WXqgb=2(jUH5pMs9RM>Q;e-A!41NF}=w8Ij5C0M3FFgE={{x12e!%?r!(Sczg8$bZ{s!_t|8U39 zF9^-ATVH(m2;#Jazap~n5eZ4F@!@|haZ1bg41X24Ma&>_qTQc`wvJ=!rERCR86SSi zeDD6h8Kyi3cmKAu3d=Xngw1_$9fk3h=YK?a-0ZfZ_?3gt`hDgp^ZGp@Q$ajrHY9vr z@;{1v>=2}M#bRmJ`aHsd^+kkb>lVTr)|U|eL+igG+_e4_;S1K+5Ppx;|3}vULi|PR zuMxg1t^TRh|1p7cQ(C=>`cd;!Qvc%!qtG7q^%~$u&Fhl$Cc-fMfW-gztgb-V{3q)e z!duoQgnuaEA6sR_|HOJe5Hf#eeLvz~vwjfa|FnJr;WsR+IgAC+7o#C_#Ygy8eH=^L z$FZ#VZUKI`{}=BGn{kAojGvVGYSXWRGS3IzK)8i41`qw`{kUysUfI3hTr)2te9rs~ z!tXZ!0^x_Gw99-4;sfRk!UrVWC*dK<{|+;U{AtOVm+%?$?C#?Nc}l{o5+)_wl<*Y^ z-<0qz3F$`uynotak5?t0lyFnRn-abu;hPfPlF<0rDkkA^3AZG?Dd8&;z9He8622v& zZkad;CcWttQErFcx6^xP(6`;jc^hUnG1p$kMkYv_g!>TBN>&S0&t(@TP=s zNO(&^(<=2PJTBp;gf}HLZN%qRyTF&wgc-Laj7fN0!wB=QO1K$id`rTc622ng8xr1< zFxe$_Bz#4}Hzj;aLhBxu-Y4NyH{)+fX!S_Ggi{hGCESwm=Doz$3^05{!dnuWLFU9H zJTBo?2{&;(@Ol4D3Ez-#^M1*Z@C^xXY5ZNxza^pB!+1=>;}Tw#a8tsY622kfEeQ=? zw?=&lk4tz}bKb-J;}XUWNnFCK5^hR(Q^GeS{L6cS-S>Cf-OqP_U-yr9|9tmvc7Lw> z54!)X`>(nm>bcr;qvz$GpYQqIoz(Rd=)KpTsPDwRl|-{1F3eZSfF*}gyM`@_Ef-skUcH4pb6?!VBV=>Mhu-|GL* z{ayDybMFW5{jqz0?%p@#SQesJ*T27hDl z-w*nRx`(ERl0$1l9~}D8p`RK0<)QyD^rfM1426aVhTlCrIec#TpAEl0{ORF;GyL1b zUmA|=9@`zm${uebcJQcdxRVD;^PN0)`Do`$*h#@#hFwMgd(LL;I73)pw7@!b;I9j- z-EQ2J9!9taH~9DBzVmxb4EK3yfp7WYa=|+V47V(XU!haW{JDgGCE@+eA4QIa`$8-o zk???oqoLP;*rCuT5iW#&1L5-m=hYV0oN4_S+}2@S$HAMWy9nnqlE2i!(p}v@)P>!V zAHPpz{)g`sgquCge?h_z_YmekmiUiKcvEsdCgG{xf9|yHG=ByPg!sl0=s>R>pMKm77Qrp!_D*-#5+;T2U_yo zvMBQSyT!b*--A3K=-Pq!dywY?b$NIH2~Zv0cL|?FIo{5Y@Eq=;`!LTjJP$tLeFub` z^9B%~lkg&LlKb%f8NfQ1id zLVJ<(N8qy$?`bjYvPKZzV;w+#H$tE3u|^T^Md-sDQ-=_LM8c!iIC3Ts!b@R2g7`5B zC#|E%c~rv3ahuwQ`w^%!o??*Wzf;Qo6A_uL<5GZVB1TThvV?&r;9&-=}pp6BuT0rLYr zA2C1M^9f^m{nlhpn{}qA*?P9`N$Y13|Jj~v*3134DTMdfU%*fK{xCit$LE*t{Q=WA zX!*|c^y72QDhxg3D-6Bg4DP;W(FcWplV|hkO4feNTv|z%OH+lyR3WoJoy}_cV#zMn zl}x2l`AV+rm7Y!JlFO*%6wKz9@^uYP+t~t&Ph@fnMcY1?Pup{LGF_i@shBAvXF8uN zCsXD5ja;fWK4+II*>Yo!RL|tlFm9&+*Xaq_IXhL(51WNd>Y814 zDWIuok_R}QbRgFe<3h4Xf}W^kvLpfeGV|qRxl%F<8wI=C=IKl+Q_dI7sbaoTn77Mi zyk)L%=IrH6iQsFwRiEjmo*o0qip5HyjMvod$WUgGQ8>T}DFSEAPOrJk@Zf1I6_WZfT{KC}2#d)K=s~^lcyS$z+UelHi zH>AsXJ69deYAKa3W;2Usegm{#HRlCG;}LKsS6MY8CBn9|5YJ31St`fvti7Bp+XhTt zvx^stS+i`H6Xzf%x#TLdYZ5GZB9oR(KKG}D<#zdGC7Wdi#yekyvyfLBn@JbiW7iUi z6Uo#y$i~TxolTobG-}9nYa4+mPSGdUfFM3^JZsCTScO|WFa>=a8l-R*Sa#?B$BiMwI z`ON!lQ?Tu8sEjYx(a6|gsfaAZoV?K^Zk`vLFiV+iR*?UgKHpK zU8yN^WM`8F+O48vS&Z0kvy`orR!%5Q)@jje#(2su13jRk5#T8`lsXdCVmInmK<)@SOW9I$I;YW%Vm5W(Bmsn2WNV8u>_O7C8mB2xsTC|EiZC?kWI1WL zw9?@b{i^OUG0lpSo6Sm`jgSulBn|A0jH+ZZ=2Q`5Y8avsb~fM43w`O^Zrvv_wm(Q3BN1dQxlW zmzJ_vIYPBp^JP0xv`hI)5u*h6xfm<2R8|)gnbjoNdk(X)W4Fsy`B>-T1kF-*1~N#Z z$Q()bN(75xtURmBIIIy>SQ3|}60&3f@wa7RMlC9%32rfy!&ICQ#u936n&s63iA=K7 z2C2mLmWaq3l^Uc)Z!E!S;tb1lIEmSkZO*v- zKC_+Q)eN%dOqJhfcjmWd3x72#{M9G{Xw2m=)|IXWQEb3l7<1mn)yxn9gSK1Wph%EVoZ=%nS6IjD=(o@_@miQ?6mrko}*)E(8c)%%$xm2s*lu z&dgLLoyogfDKje)?lQtWok`ny?3F0)X>%6Snke%nd9} zN>jNs7r}wHX&Mt%3$p5!od3Q?7X%+7C0bwEUVo& zdSDuJ9T<%T=W{y^+1+cN&o#90it5JXW$%QiTRNLt!|K?~PL;3-grw1HA@S*CHoJ(e znmGYQ$~o0$a%m^#?#JVqWvoA{yV}!OW>yn(b~3wKO*v*j8VW`t4K5sLsMfr|mCsp?r${Y4qNh2k?OE}k-&L^GJ*K?s-$&bG^#&pfA%lewxBCiMFn=5ASO zJyo&cmeEx^hotz(HU|TiC4bRyj?QB-woY%ByW3|EG74Hqms^}x8#1dk9p6B;l9P6J z2@6=}(5aM9(`e7XcfJ}=cxzZL{Sv7a@ZN^wL$^9uP`REb&6rB39TBBfrsE1*arhrJ z>Hw=+Mi`=6KO2$`xh`P7LS-Aqpp+_NQNrCfYiF|#_>uux|ljoxv*x{-I=$o z-#-sqV#BAXPg9blqixU~F0+y~kA0sM z!)2Z?iNRxK5T$H;lMid?^3SnQ(nfQ*Ihez2#*L*`!!U|=TGY4zlXgw=XL9L!c&D&+ zl@+jq6B{GX!x#x^A=>YhN(Q*Bbj&TtOVRfl9kJGo;&7u(o;g)rc|fh%91b&Fex0$CYf3Gt=_R$LW7ni?yNx5{b9s2&cdFLH2xhqvxMV?1;CX~eZhFmDAxH_c^CFjLL8nUJtdR$O9cI<6*+FP_wXI7t-D5kdwyb%}^!AUvy4p4M~) zIdz~493&lDIJvqfG~9odifb2P6LapF!8LkLQdBlh>2|%(+7#5|1m3$2u@bhb?@P|p z`ysws17Al8J47r(;Gl-n_iSm|z&Dn}{135EzekQ%$XRT7PNtGs6z}z#xJxnJaAyhU zmz>g-OiG=bE8@q5$8Ftc1}nghaMgoJijFruGQ>a-&tPR!^o~;C$^%hl(OD`NNe8xY zHfD?Pv5%nzy=!cZWoy*~1|O=p6-^pC$kDO0LJ`~D8?LtkR4HP)iLIPjb57J;XH$Oh zS+nF4U(hRV$cE90V`T?FJ6D;efNiN+w-<3FhXkyc%nXFB3+cr?I);2E&tfeiP2lSn z7vTaOQ;T#bVXKzKhSha4&*6v=)7XCTY3FgmSLEWnWZ<9h{C3a;Zf&uESjneN1?IS9 zXO{#Hcc;Lsm_#9lSa1ijs*|3`Sb4QTSTw!Hi7&!AgQ$7~5s|V*=Pa_OKAr0o2FTMU z3$g|mZxB4L7#B?pl#VBN9CJC3dY~zjz*;vukS^|kgd|w2i%6>o8aV8iA(Q32DPtkc z8NZMu`xq=9IV{T{M~q|@uv%IMItal4Y-yIVN$8l&kM!4L@d$S<7?$Q#Cb1l{m#I#m zk~c}_bMqB==D<%J4$D@VYIY7}1lBV9ZZHASc2cGQf8|oXxN5S?CYv$^oN#brgq*Eq z>~)N0DTy&UYHyZ#UR$b9p{r@fXr{qDj2koXDg*+m4074&3oiMp^=JpMEs}5*CpHbY&{bg>HF;{jS2e+hYG6lAX!qu48ZQo|j_wg$oZ` zTk)h~jm{;8qK9cMA=tx?9^!3>9Q2l(Syd9wvN}b;)+Em84596(CC6-d ze#u}V;7nxB_~AF*U*&IAkeE9!JNx~Rp^WDjeImJ*)B-mjGXfOYse}P~Cz-ltmwn?| zomQMaXV1fDi#5h7hwZEkoeX_ATNnC0oTz|x?uk?nOr5j<(Rdvc>|}WDg;1O*9}D>z zEO7u&PeeVs3f0|XQU_D7w6uR722?14g)i;4_-DNe9E-tT2b&I_LgFX_Msv=FNrxkB z86trrK6&!M3TO1W@{z;JcE_&+udt%PT{#8<ESWg{hYy$Hfj ziGz*OspmjaKn;WRh;e3-lXk=sDXgu^a*V=*wj+nNKiwKS-8l-pKP5LspaWRO$%&g_ z$&un75%1YR05E}ZWiB@!7@3DrHu*vV;#MwlUxLLXX|RwlC$mslIaFiD#WK#X*X7u2 z&c^iyQN8L#s>#Pvi3Ek2n}ck!R2nstAam7jJ+_!g95go!Z&~3Y$;nI+n~Gvwr%8u~ zV$+HlIMiKLT3CVXi7QQ3M7jq6L$K^!rN%AfFM{4hml^7Zhhu!cQs8>PPJ8P~AR{E^ z^&=ZY1{pEwoJnrLXHdlwqQJBeXQq@&7HrTf=};Vk=z$Z8ES#KB3abHdW*$;gY!Yl8 z#0uGIj9e`1r)(BV$wJW6zrhAJmoL!5c>o;$Qm6$f#K;ZeYAhj@c4VD$f^ZRtvy~ z685BR7pAh@fKtoE#EAvwn5O2ZXJ^giLcV(Pp`61S7e;yh!O6#tCKA)h)QX*+!m55_ zHD7`AkSkI5j?<{op5}4ngH_)XIKXefaS1p&dP|+(4|j6~OCX#oVQt9is|3`Ru_-v4 zEW?#7@$=%lEa}}hA-lU~lO0x#dUG7k#vc1rJs2r&U~dLKUvwFh?2Kqfk|e0=d?JNfL+$-}*fENSGpXBYkc;!cLcY!n=?S09 z%s4iC+yQU{!QO&SH^K+;Awd z4NKPG*rldpkxAkw3O4q4aPnX>pvR1PuH9o?T$Kx{f^($n98EZVoRXyq>F)G~V$Acp zkTUb7tW%GEbchYl2kog+^>i55$+FJQKzNDTKv8BrSDoNUDV9t0bHHk3RypI&Ev3dL zj!wj}Qdo12Cu-w0s~<7r)G4a%$t3n9SnY{BRBSC&MeoLPzpMG$?HO3bY+a7n#(9iZ zT5ByK6(&F)e$a&_MJHv8CoU9vJSERF%T8Lha3pR`T6Xbt)R-AHwpi;;GVM7#)znEQ z2rv6DW?=<+Cx-8kf^%EKO~}UMch>lbHm-61vGF5!)_6i2gM5wNGJ?S^3{Ja;?hKlH zz15?tPz3v*I{y{{<2v@8PzF~}w=WNt;1j~dG)PE;kBoq!CfqV}Dm??yT7V_R3?_H1 zcsIS{Iegpu^X53-NgKt}43+TB@aty9BykO`gs_0W{kWC}Ul*?Bj~J_E8R-hj%<4Sy zSdXb=xM{Dj9!Bg*+;oC7%*iEW=1!X;;IKsoZ%yQ!(lMuW28)^kN{S6N4iU45X3-*r zcB`nB6?|;KTty7G_Rt2G+wdcja|l!TWbjKga95xP4m6Qd#CwVb^sIUHt%zHFEyXW} z_w{VFiva@sD0r5T1|2%)%nU~Jw28|Y%mb&P=XgEE}amn zEC>~FTLv}Ls7s8K5-Z{zg~wzZn0Qdi-jzxm`xu z2Z+mp{cU0Uj~mkl=Lq`7$uE8!`fu0Q8T7-}^LV}wqoI`Cw!Z*JhTpV9UAT}SS@4D; zZrbNT@pGUGA(-w8U_L7J$l|vW7*FA)j)%;cIRSj%g?7)F??SGbSwSiMSAcd5Z+{%X zvk16vFXIP5=P{|b0ScuMyN)jgxqvr;=F$7N0$vh&bPzu)cL~2(bqO4w0=8vH2zgD- zNMT%*Q>%LxeV#{+3H*(lqln>6pulC|XA+mHIy!~X6@cd$Ml}j;-jCl}7{@aP$kaiO zmr`>8zt*tdsTG6d!euMH*(f|v9raYTmXe+})_^-&)}l;!*1+uMH|>+#)YB30+ij_`YDG8- zmr7+|Y9a?QPVaBjm_3&;YJSM+x976C@7Qh2kAX+{v>yY0${nPD`$fpWX-5)gVF9L) zo`GbU2QC9+x9?+ULrZ~+>u66~^C;d*!k1>i3aS?3L{{={ha+p#?vM%!!E`e(wLQ08 zYo-LEImFa;v@1y3#hf`E>2Rm!gekoizSTTM{rScSU?fPX@oiz!F?k zl@dPlkTBMwyi>9mugIuiGg{n{61wt?GG@AxVmWak24h&%cK!H$2ecaj9BKz`4sp&J ztJf_dHLe4WQp;*zL`-EH{YFk8MU3{N9kDhoi)f#wOm>|>OeMBj+Rj?aM~o)oYWOm0 za$YiBN?}5ze9KJMqIsZUTT1c?!5~*>4Ytv_Fb}?He+K=KdrQJ+_M<7)SU_@eXwAtD z%;A7t!wuCO$W7a32iIDhj=kfs$vA~8t3eYwq2UiIhatu-4^EMoVuPI zn*KOugj3KW+Sd_t7M5uoPguIQDH(&rQIa_yq2~_8ffio+=~_fd9$4W_4vNp)Q{As$ushoqo&RQV8eIrJ3rL{sfIc~9QpsHd$c*1|4Ru6HTB>+O7~^cfy#sqQ z0le}74p9=R5!4MYJ|a+Ioly%s0~}pd8o;6Du31B%6>Z*fE$kq%0yn$$nLw#4>5z^4 z+}r|c(WcM_kWb{B*vMYDMhVz1LVC$p>Qp0#O&jxQSFpEAgY^c`p{A)f_qgk_}cI%q^ZOgnQ_>yYZ8ttY=7dmLn% zGqFBtL%15D47zg9{Lk_rQY{gz!ix3$nRAK>+11hZ{?TxM+hzWDJ zYPV%2q}GpiRLiIfxMadH01o8`)~3yk0XlF`AXlI0qfJ*hvIYrq-;MQEyV_u9&ELTk zp;9BzqQ5>}Bg3+1)0)&@;2?FWiL^zvyL!_`D5M#rVjts_X3Fn&`|47S^AdiGS8!up zu@K{PXrZ)czufCD0wQ+-D@(7(}y89e)Ai3ip zw}6eaOZ8UFN;_H!T^|t+M^4$N&4sOR;ruI{?jUCsLtIG$-D_tS(_fn!K}~lpV(oED zD3zob=ZVHOt`%fGa11@tu7I=15&c0=T$woQ8SL(L1qhBgJAHasOfTW8#lvk4^v@MJ zBn|L%W+lcNlkos!Z~i`pnl8p>cYR62dIprJx2rq!Ds|LiQCEqD?#~GwhcW1zQbhS# z0W^F+oQyeVNC%a9K0yyZX~Npv-I|Vi2{XGFgip9I3YvQMwz_7*zF75G$1arWUl8hy z0G?V|x3-0Tiok|c<65=An9#M6UnWTVk^z_a#JX{cPkMi-?X)e&(4I4tu8)oZf83ct zF1e&B?u$qPVvgDJ)He@-e9g)3y!3a3Lmg0zxsuTl5p&{Nx6&ABEUjAD2O7%=*RxBI zh#l%|_hXtvjc~Tt;f}v<2YcX7+SKev_T5SSRZsvpkK9S!0wjo1ZCd!0nK9zVyxCq? zo+4{)YZw6~g|nBgRH^Y|{ZK|5U?XMxvB%Pkg zPMQWzU7mv>29lCEs)jTY-ri`Z z;p=Sm&5+?8d+&G>q}_l=n34~lFuqMT28kvER%P`?UzZtRE*a&nMG{!)J1zX*wS(uE zd)MvO!#ydX7kLztpd?W z90qp|q``M7!h^YN-YcIYD5JH!3^I(N)+llhgBS~_dl|%=!+cmq?rFd|h<0NL58-dD zCOrnZItXQ@Pnxw@hdy~e`N8icgP3FRa2uZ1 z+(*!slwt6vPmFHg zmi^q7JIdj=pWLav!`mXzG0K6P9)oRAeZQUUQfYVFjEa>U1B_Am(55Xxo*zL-31t4L zq{s0b!{1Tlk0ECX5}QPdHk6(YTFArruq8F0`0@0UHkoCtPktRlidsOOUjlq;9`%9H zY1NLR7VTqNYO_a{Euj|AT@N9D0-te&^w=*VOd&;Vd0s?%u>TZNN2Lwz0=}aMxTrNH z)o5?2v$T2igK5vS*u?pWwBkZ8 zv|XgEa*uNvdBCyqkCti_Es1R!G0t(MN(vCzkK%L?<)p_!!H^VZzRt3Xs7oqSBG}VW zKwvMF2GW?k=D69Glusc}if)%R(qk0uD97YF$HRG)cyZjMD)&&N9jQR>uqHV`>^KHW zqSBu-p`#~tNfYvsmYcevaA`SNrqtqyw9bTJ&eJMNAfb|rtVK?1sCq(3lx9+1%SjW; zhOQ}CiK^gf>i6wT#hVsoi-1ZO)N*q@(69}26JW}6@FJ;b_e%Tj! z#W4{!by&57oS_C&bGdqAf20fhBNWnuw4=084wa_5p4u)Slucs8J`Urfl%2phh&!d3 zI!umHhDcZ1KI#DJ#&#S7N5uNHIpn*`DM|skPWljIj)I!72#Dl&8f}@TPLMN{PD&-y z)FqCCcu`genRMW~ha=OLv`$Qs`zjOE81kHyByZTWD`n)V+E%p`#FQFJei0&Ta}A+7 z!jY*4yOx8xNV%bm@QGpWF_(n3%NbYvVeN5T?H7%c&5L763wX=!M;Nni5b zUC}A;iLdGqc|%J}+{p<_5M_!KBL}F_)OK=3M@qd@ouqtH>xdg|BXL)I&z}EwYjTd4 zlx0h`n$&%2gw8+I9i}NO91C@VvPA7wO(z#fA!4so(D{y(=h(;yX+#NQ4y7QC znA&>kEv1V+(Nb}w)HKqHGNrbKDb}R+GLKf8v?3@e2*SNs+9@Q9c2PM@-%^KZ~&M<37bBKaCil#&37V<L-94brI`6dSxw{9XWuQ*F`+3fmI;-E^11e2u%}Eo^W3)b;y=a;5 z`X1D8@&DHMz&VKPDz1n;`^R;{-8~@bad!`RcMqt$nY(+yyL-U9d%(MUz;DMr;9J|z z2G`!uMxCf=TKbhq2j{7>E1-hq|x8GIj{!x$KxS#a)j}x+=^sZfcyH5A)leb+;+~GhrKqO7(o5907mc4Tf zOyCAqedfUZ=5dD|j2**K?7Q)t#G7%v2h7_jkAeC7khhn|v~6BK@2KmY5)xAHlA6l) z`@Gw)H1It=eAyL3-bU0rExb*@+b)Enw-59-W(7HoY$^Z1(!XIsxqgX(%`p5X}I z<{({pN0QWac}BXsw`E5_F>)qpz5}`LjV9iQpz7T(a0PaoQk`*=_*VAg-UX@Y>$z_& ze3kV3WrVKW*N#+2ru|#{w)ee&y9?wXspio@Xwu8j;{jum-|fD!Slhy+ZU>on$a!y7 z@4oX|(t)?(5#M#vw-cy$8χ@Zbgm$zYS9=Hez;3X8k@yNGQC?EABvJPy=?~K%2 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 Jj - - - - Debug - AnyCPU - {D2EEC76E-966B-4B59-922E-D76091D4D594} - WinExe - Properties - WhatsAppReg - WhatsAppReg - v4.0 - 512 - - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - Form - - - frmRegister.cs - - - - - frmRegister.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - {3fc9096a-c4d2-40c7-be7b-d98acab3bd2b} - WhatsAppApi - False - - - - - - - - \ No newline at end of file diff --git a/WhatsAppReg/frmRegister.Designer.cs b/WhatsAppReg/frmRegister.Designer.cs deleted file mode 100644 index ee12c0e..0000000 --- a/WhatsAppReg/frmRegister.Designer.cs +++ /dev/null @@ -1,205 +0,0 @@ -namespace WhatsAppReg -{ - partial class frmRegister - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.txtPhoneNumber = new System.Windows.Forms.TextBox(); - this.btnCodeRequest = new System.Windows.Forms.Button(); - this.label2 = new System.Windows.Forms.Label(); - this.grpStep1 = new System.Windows.Forms.GroupBox(); - this.radVoice = new System.Windows.Forms.RadioButton(); - this.radSMS = new System.Windows.Forms.RadioButton(); - this.grpStep2 = new System.Windows.Forms.GroupBox(); - this.btnRegisterCode = new System.Windows.Forms.Button(); - this.txtCode = new System.Windows.Forms.TextBox(); - this.label1 = new System.Windows.Forms.Label(); - this.txtOutput = new System.Windows.Forms.TextBox(); - this.grpResult = new System.Windows.Forms.GroupBox(); - this.grpStep1.SuspendLayout(); - this.grpStep2.SuspendLayout(); - this.grpResult.SuspendLayout(); - this.SuspendLayout(); - // - // txtPhoneNumber - // - this.txtPhoneNumber.Location = new System.Drawing.Point(88, 19); - this.txtPhoneNumber.Name = "txtPhoneNumber"; - this.txtPhoneNumber.Size = new System.Drawing.Size(165, 20); - this.txtPhoneNumber.TabIndex = 1; - // - // btnCodeRequest - // - this.btnCodeRequest.Location = new System.Drawing.Point(159, 45); - this.btnCodeRequest.Name = "btnCodeRequest"; - this.btnCodeRequest.Size = new System.Drawing.Size(94, 23); - this.btnCodeRequest.TabIndex = 2; - this.btnCodeRequest.Text = "Request code"; - this.btnCodeRequest.UseVisualStyleBackColor = true; - this.btnCodeRequest.Click += new System.EventHandler(this.btnCodeRequest_Click); - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(6, 22); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(76, 13); - this.label2.TabIndex = 3; - this.label2.Text = "Phone number"; - // - // grpStep1 - // - this.grpStep1.Controls.Add(this.radVoice); - this.grpStep1.Controls.Add(this.radSMS); - this.grpStep1.Controls.Add(this.label2); - this.grpStep1.Controls.Add(this.btnCodeRequest); - this.grpStep1.Controls.Add(this.txtPhoneNumber); - this.grpStep1.Location = new System.Drawing.Point(13, 13); - this.grpStep1.Name = "grpStep1"; - this.grpStep1.Size = new System.Drawing.Size(259, 76); - this.grpStep1.TabIndex = 4; - this.grpStep1.TabStop = false; - this.grpStep1.Text = "Step 1: Request code"; - // - // radVoice - // - this.radVoice.AutoSize = true; - this.radVoice.Location = new System.Drawing.Point(64, 50); - this.radVoice.Name = "radVoice"; - this.radVoice.Size = new System.Drawing.Size(52, 17); - this.radVoice.TabIndex = 5; - this.radVoice.Text = "Voice"; - this.radVoice.UseVisualStyleBackColor = true; - // - // radSMS - // - this.radSMS.AutoSize = true; - this.radSMS.Checked = true; - this.radSMS.Location = new System.Drawing.Point(9, 50); - this.radSMS.Name = "radSMS"; - this.radSMS.Size = new System.Drawing.Size(48, 17); - this.radSMS.TabIndex = 4; - this.radSMS.TabStop = true; - this.radSMS.Text = "SMS"; - this.radSMS.UseVisualStyleBackColor = true; - // - // grpStep2 - // - this.grpStep2.Controls.Add(this.btnRegisterCode); - this.grpStep2.Controls.Add(this.txtCode); - this.grpStep2.Controls.Add(this.label1); - this.grpStep2.Enabled = false; - this.grpStep2.Location = new System.Drawing.Point(13, 96); - this.grpStep2.Name = "grpStep2"; - this.grpStep2.Size = new System.Drawing.Size(259, 50); - this.grpStep2.TabIndex = 5; - this.grpStep2.TabStop = false; - this.grpStep2.Text = "Step 2: Confirm code"; - // - // btnRegisterCode - // - this.btnRegisterCode.Location = new System.Drawing.Point(159, 17); - this.btnRegisterCode.Name = "btnRegisterCode"; - this.btnRegisterCode.Size = new System.Drawing.Size(94, 23); - this.btnRegisterCode.TabIndex = 2; - this.btnRegisterCode.Text = "Confirm code"; - this.btnRegisterCode.UseVisualStyleBackColor = true; - this.btnRegisterCode.Click += new System.EventHandler(this.btnRegisterCode_Click); - // - // txtCode - // - this.txtCode.Location = new System.Drawing.Point(88, 19); - this.txtCode.MaxLength = 6; - this.txtCode.Name = "txtCode"; - this.txtCode.Size = new System.Drawing.Size(65, 20); - this.txtCode.TabIndex = 1; - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(6, 22); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(32, 13); - this.label1.TabIndex = 0; - this.label1.Text = "Code"; - // - // txtOutput - // - this.txtOutput.Location = new System.Drawing.Point(9, 19); - this.txtOutput.Multiline = true; - this.txtOutput.Name = "txtOutput"; - this.txtOutput.ReadOnly = true; - this.txtOutput.Size = new System.Drawing.Size(244, 102); - this.txtOutput.TabIndex = 3; - // - // grpResult - // - this.grpResult.Controls.Add(this.txtOutput); - this.grpResult.Enabled = false; - this.grpResult.Location = new System.Drawing.Point(13, 153); - this.grpResult.Name = "grpResult"; - this.grpResult.Size = new System.Drawing.Size(259, 134); - this.grpResult.TabIndex = 7; - this.grpResult.TabStop = false; - this.grpResult.Text = "Step 3: Retrieve password"; - // - // frmRegister - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(284, 300); - this.Controls.Add(this.grpResult); - this.Controls.Add(this.grpStep2); - this.Controls.Add(this.grpStep1); - this.Name = "frmRegister"; - this.Text = "WhatsApp Registration"; - this.grpStep1.ResumeLayout(false); - this.grpStep1.PerformLayout(); - this.grpStep2.ResumeLayout(false); - this.grpStep2.PerformLayout(); - this.grpResult.ResumeLayout(false); - this.grpResult.PerformLayout(); - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.TextBox txtPhoneNumber; - private System.Windows.Forms.Button btnCodeRequest; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.GroupBox grpStep1; - private System.Windows.Forms.RadioButton radSMS; - private System.Windows.Forms.GroupBox grpStep2; - private System.Windows.Forms.RadioButton radVoice; - private System.Windows.Forms.TextBox txtOutput; - private System.Windows.Forms.Button btnRegisterCode; - private System.Windows.Forms.TextBox txtCode; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.GroupBox grpResult; - } -} \ No newline at end of file diff --git a/WhatsAppReg/frmRegister.cs b/WhatsAppReg/frmRegister.cs deleted file mode 100644 index 1a27b34..0000000 --- a/WhatsAppReg/frmRegister.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Windows.Forms; - -namespace WhatsAppReg -{ - public partial class frmRegister : Form - { - protected string number; - protected string cc; - protected string phone; - protected string password; - protected string language; - protected string locale; - protected string mcc; - - public frmRegister() - { - InitializeComponent(); - } - - private void btnCodeRequest_Click(object sender, EventArgs e) - { - if (!String.IsNullOrEmpty(this.txtPhoneNumber.Text)) - { - string method = "sms"; - if (this.radVoice.Checked) - { - method = "voice"; - } - try - { - WhatsAppApi.Parser.PhoneNumber phonenumber = new WhatsAppApi.Parser.PhoneNumber(this.txtPhoneNumber.Text); - this.number = phonenumber.FullNumber; - this.cc = phonenumber.CC; - this.phone = phonenumber.Number; - this.language = phonenumber.ISO639; - this.locale = phonenumber.ISO3166; - this.mcc = phonenumber.MCC; - } - catch (Exception ex) - { - this.txtOutput.Text = String.Format("Error: {0}", ex.Message); - return; - } - if (WhatsAppApi.Register.WhatsRegisterV2.RequestCode(this.cc, this.phone, out this.password, method, null, this.language, this.locale, this.mcc)) - { - if (!string.IsNullOrEmpty(this.password)) - { - //password received - this.OnReceivePassword(); - } - else - { - this.grpStep1.Enabled = false; - this.grpStep2.Enabled = true; - } - } - } - } - - private void btnRegisterCode_Click(object sender, EventArgs e) - { - if (!String.IsNullOrEmpty(this.txtCode.Text) && this.txtCode.Text.Length == 6) - { - string code = this.txtCode.Text; - this.password = WhatsAppApi.Register.WhatsRegisterV2.RegisterCode(this.cc, this.phone, code); - if (!String.IsNullOrEmpty(this.password)) - { - this.OnReceivePassword(); - } - } - } - - private void OnReceivePassword() - { - this.txtOutput.Text = String.Format("Found password:\r\n{0}\r\n\r\nWrite it down and exit the program", this.password); - this.grpStep1.Enabled = false; - this.grpStep2.Enabled = false; - this.grpResult.Enabled = true; - } - } -} diff --git a/WhatsAppReg/frmRegister.resx b/WhatsAppReg/frmRegister.resx deleted file mode 100644 index 1af7de1..0000000 --- a/WhatsAppReg/frmRegister.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file From ba798b49b7f3755bff5dc6749aa81e1dbd51dcb8 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Sat, 24 Aug 2013 14:44:02 +0200 Subject: [PATCH 102/271] Added receipt ack --- WhatsAppApi/WhatsApp.cs | 15 +++++++-------- WhatsAppApi/WhatsSendHandler.cs | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index dcdedc4..d5ffa2f 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -722,10 +722,14 @@ protected void processInboundData(byte[] data) if (ProtocolTreeNode.TagEquals(node, "message")) { this.AddMessage(node); - if (node.GetChild("received") == null) + if (node.GetChild("request") != null) { this.sendMessageReceived(node); } + else if (node.GetChild("received") != null) + { + this.sendMessageReceived(node, "ack"); + } } if (ProtocolTreeNode.TagEquals(node, "stream:error")) { @@ -794,15 +798,10 @@ protected void processInboundData(byte[] data) /// Tell the server we recieved the message ///
/// The ProtocolTreeNode that contains the message - protected void sendMessageReceived(ProtocolTreeNode msg) + protected void sendMessageReceived(ProtocolTreeNode msg, string response = "received") { - ProtocolTreeNode requestNode = msg.GetChild("request"); - if (requestNode == null || - !requestNode.GetAttribute("xmlns").Equals("urn:xmpp:receipts", StringComparison.OrdinalIgnoreCase)) - return; - FMessage tmpMessage = new FMessage(new FMessage.Key(msg.GetAttribute("from"), true, msg.GetAttribute("id"))); - this.WhatsParser.WhatsSendHandler.SendMessageReceived(tmpMessage); + this.WhatsParser.WhatsSendHandler.SendMessageReceived(tmpMessage, response); } /// diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 3540d4e..ff1cc37 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -490,9 +490,9 @@ public void SendMessage(FMessage message) /// Tell the server the message has been recieved. /// /// An instance of the FMessage class. - public void SendMessageReceived(FMessage message) + public void SendMessageReceived(FMessage message, string response) { - var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); + var child = new ProtocolTreeNode(response, new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, child); this.whatsNetwork.SendData(this._binWriter.Write(node)); } From 2d87112e1b76dfd8d977caffc087b1f86277c459 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 27 Aug 2013 13:05:36 +0200 Subject: [PATCH 103/271] Added response output to code request --- WhatsAppApi/Register/WhatsRegisterV2.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 94e0ed7..7c566c6 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -20,6 +20,13 @@ public static string GenerateIdentity(string phoneNumber, string salt = "") public static bool RequestCode(string countryCode, string phoneNumber, out string password, string method = "sms", string id = null, string language = null, string locale = null, string mcc = "204", string salt = "") { + string response = string.Empty; + return RequestCode(countryCode, phoneNumber, out password, out response, method, id, mcc, salt); + } + + public static bool RequestCode(string countryCode, string phoneNumber, out string password, out string response, string method = "sms", string id = null, string language = null, string locale = null, string mcc = "204", string salt = "") + { + response = null; password = null; try { @@ -34,7 +41,7 @@ public static bool RequestCode(string countryCode, string phoneNumber, out strin } string token = string.Concat(WhatsConstants.WhatsRegToken + WhatsConstants.WhatsBuildHash, phoneNumber).ToMD5String(); string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc={7}&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token, mcc); - string response = GetResponse(uri); + response = GetResponse(uri); password = response.GetJsonValue("pw"); if (!string.IsNullOrEmpty(password)) { From 52e3b9950a6a65ad21d57f33111b0f6f0b694261 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 27 Aug 2013 14:11:57 +0200 Subject: [PATCH 104/271] Fixed detected CC for North America --- WhatsAppApi/Parser/PhoneNumber.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/WhatsAppApi/Parser/PhoneNumber.cs b/WhatsAppApi/Parser/PhoneNumber.cs index 5f19ab9..d73083f 100644 --- a/WhatsAppApi/Parser/PhoneNumber.cs +++ b/WhatsAppApi/Parser/PhoneNumber.cs @@ -39,6 +39,11 @@ public PhoneNumber(string number) { //matched this.Country = values[0].Trim(new char[] { '"' }); + //hook: Fix CC for North America + if (values[1].StartsWith("1")) + { + values[1] = "1"; + } this.CC = values[1]; this.Number = number.Substring(this.CC.Length); this.ISO3166 = values[3].Trim(new char[] { '"' }); From a47b4ffaf8db5c734c2d24eab26635d762a82e3b Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 30 Aug 2013 17:18:35 +0200 Subject: [PATCH 105/271] Removed old unused class --- WhatsAppApi/Register/WhatsRegister.cs | 163 -------------------------- WhatsAppApi/WhatsAppApi.csproj | 1 - 2 files changed, 164 deletions(-) delete mode 100644 WhatsAppApi/Register/WhatsRegister.cs diff --git a/WhatsAppApi/Register/WhatsRegister.cs b/WhatsAppApi/Register/WhatsRegister.cs deleted file mode 100644 index 5b3b220..0000000 --- a/WhatsAppApi/Register/WhatsRegister.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Security.Cryptography; -using System.Text; -using WhatsAppApi.Settings; - -namespace WhatsAppApi.Register -{ - public static class WhatsRegister - { - public static bool RegisterUser(string countryCode, string phoneNumber) - { - string website = "https://r.whatsapp.net/v1/code.php"; - string postData = GetRegString(countryCode, phoneNumber); - string both = website + "?" + postData; - - var result = StartWebRequest("", "", WhatsConstants.UserAgent, both); - if (WhatsApp.DEBUG && result.Length > 0) - { - Console.WriteLine(result); - } - return result.Contains("status=\"success-sent\""); - } - - public static bool VerifyRegistration(string countryCode, string phoneNumber, string password, string code) - { - string tmpPassword = password.ToPassword(); - string verifyString = string.Format("https://r.whatsapp.net/v1/register.php?cc={0}&in={1}&udid={2}&code={3}", new object[] { countryCode, phoneNumber, tmpPassword, code }); - - var result = StartWebRequest("", "", WhatsConstants.UserAgent, verifyString); - Console.WriteLine(result); - return true; - } - - public static bool ExistsAndDelete(string countrycode, string phone, string pass) - { - string webString = string.Format("https://r.whatsapp.net/v1/exist.php?cc={0}&in={1}", System.Uri.EscapeDataString(countrycode), System.Uri.EscapeDataString(phone)); - if (pass != null) - { - webString = webString + string.Format("&udid={0}", pass.ToPassword()); - } - - var result = StartWebRequest("", "", WhatsConstants.UserAgent, webString); - return result.Contains("status=\"ok\""); - } - - private static string StartWebRequest(string website, string postData, string userAgent, string both) - { - var request = (HttpWebRequest)WebRequest.Create(both); - request.UserAgent = userAgent; - try - { - var response = (HttpWebResponse)request.GetResponse(); - using (var reader = new StreamReader(response.GetResponseStream())) - { - var html = reader.ReadToEnd(); - return html; - } - } - catch (WebException) - { - return "error"; - } - } - - private static string MD5String(this string pass) - { - MD5 md5 = MD5.Create(); - byte[] dataMd5 = md5.ComputeHash(Encoding.UTF8.GetBytes(pass)); - return ByteToString(dataMd5); - } - - private static string ByteToString(byte[] dataMd5) - { - var sb = new StringBuilder(); - for (int i = 0; i < dataMd5.Length; i++) - sb.AppendFormat("{0:x2}", dataMd5[i]); - return sb.ToString(); - } - - private static string GetRegString(string countryCode, string phonenum, string codeType = "sms") - { - string tmpLangCode; - string tmpLocalCode; - GetLangAndLocale(CultureInfo.CurrentCulture, out tmpLangCode, out tmpLocalCode); - if (tmpLocalCode == "029") - { - tmpLocalCode = "US"; - } - string phoneNumber = phonenum; - const string buildHash = WhatsConstants.WhatsRegToken + WhatsConstants.WhatsBuildHash; - string tmpToken = ("k7Iy3bWARdNeSL8gYgY6WveX12A1g4uTNXrRzt1H" + buildHash + phoneNumber).MD5String().ToLower(); - string regString = string.Format("cc={0}&in={1}&lg={2}&lc={3}&method={4}&mcc=000&mnc=000&imsi=000&token={5}", new object[] { countryCode, phoneNumber, tmpLangCode, tmpLocalCode, codeType, tmpToken }); - return regString; - } - - private static string ToPassword(this string bs) - { - if (bs.Contains(":")) - { - string ps = bs.ToUpper(); - string ls = ps + ps; - return ls.MD5String(); - } - else - { - return (new string(bs.Reverse().ToArray())).MD5String(); - } - } - - private static void GetLangAndLocale(CultureInfo that, out string lang, out string locale) - { - string name = that.Name; - int index = name.IndexOf('-'); - if (index > 0) - { - int num2 = name.LastIndexOf('-'); - lang = name.Substring(0, index); - locale = name.Substring(num2 + 1); - } - else - { - lang = name; - switch (lang) - { - case "cs": - locale = "CZ"; - return; - - case "da": - locale = "DK"; - return; - - case "el": - locale = "GR"; - return; - - case "ja": - locale = "JP"; - return; - - case "ko": - locale = "KR"; - return; - - case "sv": - locale = "SE"; - return; - - case "sr": - locale = "RS"; - return; - } - locale = lang.ToUpper(); - } - } - - } -} diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index bd10ff6..4068ed0 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -73,7 +73,6 @@ True Resources.resx
- From 26c7e344fcfe5f6abcef45a710a0f51ed0ccea1e Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 30 Aug 2013 17:19:04 +0200 Subject: [PATCH 106/271] Removed registration token and build hash --- WhatsAppApi/Settings/WhatsConstants.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index e8f84ab..cd40542 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.10.750"; + public const string WhatsAppVer = "2.11.23"; /// /// The port that needs to be connected to @@ -57,14 +57,7 @@ class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.10.750 Android/4.2.1 Device/GalaxyS3"; - - /// - /// The whatsapp build hash - /// - public const string WhatsRegToken = "30820332308202f0a00302010202044c2536a4300b06072a8648ce3804030500307c310b3009060355040613025553311330110603550408130a43616c69666f726e6961311430120603550407130b53616e746120436c61726131163014060355040a130d576861747341707020496e632e31143012060355040b130b456e67696e656572696e67311430120603550403130b427269616e204163746f6e301e170d3130303632353233303731365a170d3434303231353233303731365a307c310b3009060355040613025553311330110603550408130a43616c69666f726e6961311430120603550407130b53616e746120436c61726131163014060355040a130d576861747341707020496e632e31143012060355040b130b456e67696e656572696e67311430120603550403130b427269616e204163746f6e308201b83082012c06072a8648ce3804013082011f02818100fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c70215009760508f15230bccb292b982a2eb840bf0581cf502818100f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b3d0782675159578ebad4594fe67107108180b449167123e84c281613b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06928b665e807b552564014c3bfecf492a0381850002818100d1198b4b81687bcf246d41a8a725f0a989a51bce326e84c828e1f556648bd71da487054d6de70fff4b49432b6862aa48fc2a93161b2c15a2ff5e671672dfb576e9d12aaff7369b9a99d04fb29d2bbbb2a503ee41b1ff37887064f41fe2805609063500a8e547349282d15981cdb58a08bede51dd7e9867295b3dfb45ffc6b259300b06072a8648ce3804030500032f00302c021400a602a7477acf841077237be090df436582ca2f0214350ce0268d07e71e55774ab4eacd4d071cd1efad"; - - public const string WhatsBuildHash = "022e923a364bfacff3a80de3f950b1e0"; + public const string UserAgent = "WhatsApp/2.11.23 Android/4.2.1 Device/GalaxyS3"; #endregion From 588385d4871b6b3a3d3f465a7cfacd04d038708e Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 30 Aug 2013 17:19:26 +0200 Subject: [PATCH 107/271] Added getToken method --- WhatsAppApi/Register/WhatsRegisterV2.cs | 27 ++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 7c566c6..3ffb734 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -13,11 +13,36 @@ namespace WhatsAppApi.Register { public static class WhatsRegisterV2 { + const string TOKEN_URL = "http://mywapi.nl/token"; public static string GenerateIdentity(string phoneNumber, string salt = "") { return (phoneNumber + salt).Reverse().ToSHAString(); } + private static string GetToken(string number) + { + HttpWebRequest request = WebRequest.Create(WhatsRegisterV2.TOKEN_URL) as HttpWebRequest; + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + using (StreamWriter writer = new StreamWriter(request.GetRequestStream())) + { + writer.Write(String.Format("in={0}", number)); + } + HttpWebResponse response = request.GetResponse() as HttpWebResponse; + string token = null; + try + { + using(StreamReader sr = new StreamReader(response.GetResponseStream())) + { + token = sr.ReadToEnd(); + } + } catch(Exception e) + { + throw new Exception("Could not request token", e); + } + return token; + } + public static bool RequestCode(string countryCode, string phoneNumber, out string password, string method = "sms", string id = null, string language = null, string locale = null, string mcc = "204", string salt = "") { string response = string.Empty; @@ -39,7 +64,7 @@ public static bool RequestCode(string countryCode, string phoneNumber, out strin //auto-generate id = GenerateIdentity(phoneNumber, salt); } - string token = string.Concat(WhatsConstants.WhatsRegToken + WhatsConstants.WhatsBuildHash, phoneNumber).ToMD5String(); + string token = System.Uri.EscapeDataString(WhatsRegisterV2.GetToken(phoneNumber)); string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc={7}&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token, mcc); response = GetResponse(uri); password = response.GetJsonValue("pw"); From 9ef0c12818c450e9ee584bd481aacdc68f2fa301 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 6 Sep 2013 11:11:42 +0200 Subject: [PATCH 108/271] Fixed MCC splitting --- WhatsAppApi/Parser/PhoneNumber.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/Parser/PhoneNumber.cs b/WhatsAppApi/Parser/PhoneNumber.cs index d73083f..7aa5ec0 100644 --- a/WhatsAppApi/Parser/PhoneNumber.cs +++ b/WhatsAppApi/Parser/PhoneNumber.cs @@ -48,7 +48,13 @@ public PhoneNumber(string number) this.Number = number.Substring(this.CC.Length); this.ISO3166 = values[3].Trim(new char[] { '"' }); this.ISO639 = values[4].Trim(new char[] { '"' }); - this.MCC = values[2]; + this.MCC = values[2].Trim(new char[] { '"' }); + if (this.MCC.Contains('|')) + { + //take first one + string[] parts = this.MCC.Split(new char[] { '|' }); + this.MCC = parts[0]; + } return; } } From 7dfc919e5549c4e75260aa27a20f0b1c61429db5 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 6 Sep 2013 11:12:33 +0200 Subject: [PATCH 109/271] Added iplimit detection and urlencode method --- WhatsAppApi/Register/WhatsRegisterV2.cs | 69 +++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 3ffb734..eaf0eb0 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -65,6 +65,13 @@ public static bool RequestCode(string countryCode, string phoneNumber, out strin id = GenerateIdentity(phoneNumber, salt); } string token = System.Uri.EscapeDataString(WhatsRegisterV2.GetToken(phoneNumber)); + + if (token == "iplimit") + { + response = token; + return false; + } + string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc={7}&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token, mcc); response = GetResponse(uri); password = response.GetJsonValue("pw"); @@ -74,14 +81,22 @@ public static bool RequestCode(string countryCode, string phoneNumber, out strin } return (response.GetJsonValue("status") == "sent"); } - catch + catch(Exception e) { + response = e.Message; return false; } } public static string RegisterCode(string countryCode, string phoneNumber, string code, string id = null, string salt = "") { + string response = string.Empty; + return WhatsRegisterV2.RegisterCode(countryCode, phoneNumber, code, out response, id, salt); + } + + public static string RegisterCode(string countryCode, string phoneNumber, string code, out string response, string id = null, string salt = "") + { + response = string.Empty; try { if (string.IsNullOrEmpty(id)) @@ -90,7 +105,7 @@ public static string RegisterCode(string countryCode, string phoneNumber, string id = GenerateIdentity(phoneNumber, salt); } string uri = string.Format("https://v.whatsapp.net/v2/register?cc={0}&in={1}&id={2}&code={3}", countryCode, phoneNumber, id, code); - string response = GetResponse(uri); + response = GetResponse(uri); if (response.GetJsonValue("status") == "ok") { return response.GetJsonValue("pw"); @@ -142,11 +157,57 @@ private static string ToSHAString(this IEnumerable s) return new string(s.ToArray()).ToSHAString(); } + public static string UrlEncode(string data) + { + StringBuilder sb = new StringBuilder(); + + foreach (char c in data.ToCharArray()) + { + int i = (int)c; + if ( + ( + i >= 0 && i <= 31 + ) + || + ( + i >= 32 && i <= 47 + ) + || + ( + i >= 58 && i <= 64 + ) + || + ( + i >= 91 && i <= 96 + ) + || + ( + i >= 123 && i <= 126 + ) + || + i > 127 + ) + { + //encode + sb.Append('%'); + sb.AppendFormat("{0:x2}", (byte)c); + } + else + { + //do not encode + sb.Append(c); + } + } + + return sb.ToString(); + } + private static string ToSHAString(this string s) { byte[] data = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(s)); - string str = Encoding.ASCII.GetString(data); - return System.Uri.EscapeDataString(str).ToLower(); + string str = Encoding.GetEncoding("iso-8859-1").GetString(data); + str = WhatsRegisterV2.UrlEncode(str).ToLower(); + return str; } private static string ToMD5String(this IEnumerable s) From 7eaff8943abb2df67fb99781e4e003612ff73c06 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 6 Sep 2013 12:01:17 +0200 Subject: [PATCH 110/271] Updated README.md Added credits and info on code request tokens --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 687baf5..0cb992c 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,11 @@ the Android client uses this encryption, which seems to be TLS as specified by the XMPP RFC. However, I did not really look into this. Also, the mapping of keywords to bytes seems to have also changed in the latest version of the Android app. + + +### Code request token +The current implementation for token generation is a server side solution created by [Jake](https://github.com/dynogic). + +I only support WhatsApiNet implementations for personal use, so there's a limit of 10 unique phone number token requests per IP address per day. +This is to prevent abuse by spammers and other scum. +If you disagree with this limit, send me an email, explain your case, and I might add you to my whitelist :) \ No newline at end of file From abb4d672870524ce6192e0b2cd0027cf46bf7b59 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 25 Sep 2013 20:05:10 +0200 Subject: [PATCH 111/271] Added offline token generator as found by @pastoso --- WhatsAppApi/Register/WaToken.cs | 58 +++++++++++++++++++++++++++++++++ WhatsAppApi/WhatsAppApi.csproj | 1 + 2 files changed, 59 insertions(+) create mode 100644 WhatsAppApi/Register/WaToken.cs diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs new file mode 100644 index 0000000..549bbb0 --- /dev/null +++ b/WhatsAppApi/Register/WaToken.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace WhatsAppApi.Register +{ + class WaToken + { + private static string WaPrefix = "Y29tLndoYXRzYXBw"; + private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; + private static string WaClassesMd5 = "30CnAF22oY+2PUD5pcJGqw=="; + private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; + + public static string GenerateToken(string number) + { + List key = new List(Convert.FromBase64String(WaToken.WaPrefix)); + key.AddRange(Convert.FromBase64String(WaToken.DataFile)); + + Rfc2898DeriveBytes r = new Rfc2898DeriveBytes(key.ToArray(), Convert.FromBase64String(WaToken.WaKey), 128); + key = new List(r.GetBytes(80)); + + List data = new List(Convert.FromBase64String(WaToken.WaSignature)); + data.AddRange(Convert.FromBase64String(WaToken.WaClassesMd5)); + data.AddRange(System.Text.Encoding.ASCII.GetBytes(number)); + + List opad = GetFilledList(0x5C, 64); + List ipad = GetFilledList(0x36, 64); + for (int i = 0; i < opad.Count; i++) + { + opad[i] = (byte)(opad[i] ^ key[i]); + ipad[i] = (byte)(ipad[i] ^ key[i]); + } + + SHA1 hasher = SHA1.Create(); + + ipad.AddRange(data); + data = new List(hasher.ComputeHash(ipad.ToArray())); + opad.AddRange(data); + data = new List(hasher.ComputeHash(opad.ToArray())); + + return Convert.ToBase64String(data.ToArray()); + } + + private static List GetFilledList(byte item, int length) + { + List result = new List(); + for (int i = 0; i < length; i++) + { + result.Add(item); + } + return result; + } + + private static string DataFile = "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"; + } +} diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index 4068ed0..3a946ac 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -73,6 +73,7 @@ True Resources.resx + From fd882db281f5b0ffd26a9304d375a3765f501cce Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 25 Sep 2013 20:05:26 +0200 Subject: [PATCH 112/271] Updated to latest version details --- WhatsAppApi/Settings/WhatsConstants.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index cd40542..62ee31b 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.23"; + public const string WhatsAppVer = "2.11.69"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.23 Android/4.2.1 Device/GalaxyS3"; + public const string UserAgent = "WhatsApp/2.11.69 Android/4.2.1 Device/GalaxyS3"; #endregion From 324a165ae74310e6dc48fa1e85d60b4bf9557421 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 25 Sep 2013 20:05:37 +0200 Subject: [PATCH 113/271] Implemented offline token generator --- WhatsAppApi/Register/WhatsRegisterV2.cs | 27 +------------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index eaf0eb0..521fad9 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -21,26 +21,7 @@ public static string GenerateIdentity(string phoneNumber, string salt = "") private static string GetToken(string number) { - HttpWebRequest request = WebRequest.Create(WhatsRegisterV2.TOKEN_URL) as HttpWebRequest; - request.Method = "POST"; - request.ContentType = "application/x-www-form-urlencoded"; - using (StreamWriter writer = new StreamWriter(request.GetRequestStream())) - { - writer.Write(String.Format("in={0}", number)); - } - HttpWebResponse response = request.GetResponse() as HttpWebResponse; - string token = null; - try - { - using(StreamReader sr = new StreamReader(response.GetResponseStream())) - { - token = sr.ReadToEnd(); - } - } catch(Exception e) - { - throw new Exception("Could not request token", e); - } - return token; + return WaToken.GenerateToken(number); } public static bool RequestCode(string countryCode, string phoneNumber, out string password, string method = "sms", string id = null, string language = null, string locale = null, string mcc = "204", string salt = "") @@ -66,12 +47,6 @@ public static bool RequestCode(string countryCode, string phoneNumber, out strin } string token = System.Uri.EscapeDataString(WhatsRegisterV2.GetToken(phoneNumber)); - if (token == "iplimit") - { - response = token; - return false; - } - string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc={7}&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token, mcc); response = GetResponse(uri); password = response.GetJsonValue("pw"); From 51b5f32250dd26cec338bf82d315d7224f5d8dbc Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Sun, 29 Sep 2013 22:17:04 +0200 Subject: [PATCH 114/271] Removed old code --- WhatsAppApi/Register/WhatsRegisterV2.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 521fad9..27d392e 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -13,13 +13,12 @@ namespace WhatsAppApi.Register { public static class WhatsRegisterV2 { - const string TOKEN_URL = "http://mywapi.nl/token"; public static string GenerateIdentity(string phoneNumber, string salt = "") { return (phoneNumber + salt).Reverse().ToSHAString(); } - private static string GetToken(string number) + public static string GetToken(string number) { return WaToken.GenerateToken(number); } From 8295cc25978898a4a439683f14fc6872207c35f9 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Sun, 29 Sep 2013 22:17:27 +0200 Subject: [PATCH 115/271] Changed FunXMPP to port 443 (proxy safe) --- WhatsAppApi/Settings/WhatsConstants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index 62ee31b..7d26a31 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -47,7 +47,7 @@ class WhatsConstants /// /// The port that needs to be connected to /// - public const int WhatsPort = 5222; + public const int WhatsPort = 443; /// /// iPhone device From fcfd953feb96213a59ed3560f228642f31e25452 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 9 Oct 2013 19:23:32 +0200 Subject: [PATCH 116/271] Update README.md --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 0cb992c..0185685 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,4 @@ Android app. ### Code request token -The current implementation for token generation is a server side solution created by [Jake](https://github.com/dynogic). - -I only support WhatsApiNet implementations for personal use, so there's a limit of 10 unique phone number token requests per IP address per day. -This is to prevent abuse by spammers and other scum. -If you disagree with this limit, send me an email, explain your case, and I might add you to my whitelist :) \ No newline at end of file +The current implementation for token generation is created by [pastoso](https://github.com/pastoso). From 8f5cc37a0823b3b837e8c74ff6b62e2e204b037b Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 17 Oct 2013 10:59:39 +0200 Subject: [PATCH 117/271] Renamed class and property Fixes #66 --- WhatsAppApi/Parser/FMessage.cs | 26 ++++++++++----------- WhatsAppApi/Response/MessageRecvResponse.cs | 10 ++++---- WhatsAppApi/WhatsApp.cs | 6 ++--- WhatsAppApi/WhatsSendHandler.cs | 6 ++--- WhatsAppPort/WhatsMessageHandler.cs | 18 +++++++------- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/WhatsAppApi/Parser/FMessage.cs b/WhatsAppApi/Parser/FMessage.cs index 3c2d6e5..312206f 100644 --- a/WhatsAppApi/Parser/FMessage.cs +++ b/WhatsAppApi/Parser/FMessage.cs @@ -6,7 +6,7 @@ namespace WhatsAppApi.Parser public class FMessage { public bool gap_behind; - public Key key; + public FMessageIdentifierKey identifier_key; public double latitude; public string location_details; public string location_url; @@ -26,11 +26,11 @@ public class FMessage public WhatsAppApi.Account.WhatsUser User { get; private set; } - public FMessage(Key key) + public FMessage(FMessageIdentifierKey key) { this.status = Status.Undefined; this.gap_behind = true; - this.key = key; + this.identifier_key = key; } internal FMessage(WhatsAppApi.Account.WhatsUser remote_user, bool from_me) @@ -38,13 +38,13 @@ internal FMessage(WhatsAppApi.Account.WhatsUser remote_user, bool from_me) this.status = Status.Undefined; this.gap_behind = true; this.User = remote_user; - this.key = new Key(remote_user.GetFullJid(), from_me, TicketManager.GenerateId()); + this.identifier_key = new FMessageIdentifierKey(remote_user.GetFullJid(), from_me, TicketManager.GenerateId()); } internal FMessage(string remote_jid, bool from_me) { this.status = Status.Undefined; this.gap_behind = true; - this.key = new Key(remote_jid, from_me, TicketManager.GenerateId()); + this.identifier_key = new FMessageIdentifierKey(remote_jid, from_me, TicketManager.GenerateId()); } public FMessage(string remote_jid, string data, object image) @@ -203,7 +203,7 @@ public FMessage Build() } if (((this.remote_jid != null) && this.from_me.HasValue) && (this.id != null)) { - this.message.key = new FMessage.Key(this.remote_jid, this.from_me.Value, this.id); + this.message.identifier_key = new FMessage.FMessageIdentifierKey(this.remote_jid, this.from_me.Value, this.id); } if (this.remote_resource != null) { @@ -310,12 +310,12 @@ public bool Instantiated() return (this.message != null); } - public FMessage.Key Key() + public FMessage.FMessageIdentifierKey Key() { - return new FMessage.Key(this.remote_jid, (!this.from_me.HasValue && this.from_me.Value), this.id); + return new FMessage.FMessageIdentifierKey(this.remote_jid, (!this.from_me.HasValue && this.from_me.Value), this.id); } - public FMessage.Builder Key(FMessage.Key key) + public FMessage.Builder Key(FMessage.FMessageIdentifierKey key) { this.remote_jid = key.remote_jid; this.from_me = new bool?(key.from_me); @@ -430,7 +430,7 @@ public FMessage.Builder NewIncomingInstance() "missing required property before instantiating new incoming message"); } this.message = - new FMessage(new FMessage.Key(this.remote_jid, this.from_me.Value, this.id)); + new FMessage(new FMessage.FMessageIdentifierKey(this.remote_jid, this.from_me.Value, this.id)); return this; } @@ -522,14 +522,14 @@ public FMessage.Builder Wants_receipt(bool wants_receipt) } } - public class Key + public class FMessageIdentifierKey { public bool from_me; public string id; public string remote_jid; public string serverNickname; - public Key(string remote_jid, bool from_me, string id) + public FMessageIdentifierKey(string remote_jid, bool from_me, string id) { this.remote_jid = remote_jid; this.from_me = from_me; @@ -548,7 +548,7 @@ public override bool Equals(object obj) { return false; } - FMessage.Key key = (FMessage.Key)obj; + FMessage.FMessageIdentifierKey key = (FMessage.FMessageIdentifierKey)obj; if (this.from_me != key.from_me) { return false; diff --git a/WhatsAppApi/Response/MessageRecvResponse.cs b/WhatsAppApi/Response/MessageRecvResponse.cs index 15f32b7..5723582 100644 --- a/WhatsAppApi/Response/MessageRecvResponse.cs +++ b/WhatsAppApi/Response/MessageRecvResponse.cs @@ -138,7 +138,7 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t else if (ProtocolTreeNode.TagEquals(itemNode, "body") && (tmpAttrbId != null)) { string dataString = WhatsApp.SYSEncoding.GetString(itemNode.GetData()); - var tmpMessKey = new FMessage.Key(tmpAttrFrom, false, tmpAttrbId); + var tmpMessKey = new FMessage.FMessageIdentifierKey(tmpAttrFrom, false, tmpAttrbId); builder.Key(tmpMessKey).Remote_resource(tmpAttrFromJid).NewIncomingInstance().Data(dataString); } else if (ProtocolTreeNode.TagEquals(itemNode, "media") && (tmpAttrbId != null)) @@ -208,7 +208,7 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t builder.Data(WhatsApp.SYSEncoding.GetString(itemNode.GetData())); } } - var tmpMessageKey = new FMessage.Key(tmpAttrFrom, false, tmpAttrbId); + var tmpMessageKey = new FMessage.FMessageIdentifierKey(tmpAttrFrom, false, tmpAttrbId); builder.Key(tmpMessageKey).Remote_resource(tmpAttrFromJid).NewIncomingInstance(); } else if (ProtocolTreeNode.TagEquals(itemNode, "request")) @@ -220,14 +220,14 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t string str16 = itemNode.GetAttribute("xmlns"); if ("jabber:x:event".Equals(str16) && (tmpAttrbId != null)) { - var tmpMessageKey = new FMessage.Key(tmpAttrFrom, true, tmpAttrbId); + var tmpMessageKey = new FMessage.FMessageIdentifierKey(tmpAttrFrom, true, tmpAttrbId); } } else if (ProtocolTreeNode.TagEquals(itemNode, "received")) { if (tmpAttrbId != null) { - var tmpMessageKey = new FMessage.Key(tmpAttrFrom, true, tmpAttrbId); + var tmpMessageKey = new FMessage.FMessageIdentifierKey(tmpAttrFrom, true, tmpAttrbId); if (true) { string tmpAttrType = itemNode.GetAttribute("type"); @@ -324,7 +324,7 @@ private void TypeError(ProtocolTreeNode messageNode, string tmpAttrbId, string t } if ((tmpAttrFrom != null) && (tmpAttrbId != null)) { - FMessage.Key key = new FMessage.Key(tmpAttrFrom, true, tmpAttrbId); + FMessage.FMessageIdentifierKey key = new FMessage.FMessageIdentifierKey(tmpAttrFrom, true, tmpAttrbId); } } } diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index d5ffa2f..3b1e226 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -290,7 +290,7 @@ public void Login() /// The text that needs to be send public void Message(string to, string txt) { - var tmpMessage = new FMessage(this.GetJID(to), true) { key = { id = TicketManager.GenerateId() }, data = txt }; + var tmpMessage = new FMessage(this.GetJID(to), true) { identifier_key = { id = TicketManager.GenerateId() }, data = txt }; this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage); } @@ -364,7 +364,7 @@ public void MessageImage(string to, string filepath) if (response != null && !String.IsNullOrEmpty(response.url)) { //send message - FMessage msg = new FMessage(to, true) { key = { id = TicketManager.GenerateId() }, media_wa_type = FMessage.Type.Image, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url, binary_data = this.CreateThumbnail(filepath) }; + FMessage msg = new FMessage(to, true) { identifier_key = { id = TicketManager.GenerateId() }, media_wa_type = FMessage.Type.Image, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url, binary_data = this.CreateThumbnail(filepath) }; this.WhatsSendHandler.SendMessage(msg); } @@ -800,7 +800,7 @@ protected void processInboundData(byte[] data) /// The ProtocolTreeNode that contains the message protected void sendMessageReceived(ProtocolTreeNode msg, string response = "received") { - FMessage tmpMessage = new FMessage(new FMessage.Key(msg.GetAttribute("from"), true, msg.GetAttribute("id"))); + FMessage tmpMessage = new FMessage(new FMessage.FMessageIdentifierKey(msg.GetAttribute("from"), true, msg.GetAttribute("id"))); this.WhatsParser.WhatsSendHandler.SendMessageReceived(tmpMessage, response); } diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index ff1cc37..14404e3 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -493,7 +493,7 @@ public void SendMessage(FMessage message) public void SendMessageReceived(FMessage message, string response) { var child = new ProtocolTreeNode(response, new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); - var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, child); + var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", message.identifier_key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.identifier_key.id) }, child); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -710,7 +710,7 @@ public void SendSetPrivacyBlockedList(IEnumerable jidSet, Action onSucce public void SendStatusUpdate(string status, Action onComplete, Action onError) { string id = TicketManager.GenerateId(); - FMessage message = new FMessage(new FMessage.Key("s.us", true, id)); + FMessage message = new FMessage(new FMessage.FMessageIdentifierKey("s.us", true, id)); var messageNode = GetMessageNode(message, new ProtocolTreeNode("body", null, WhatsApp.SYSEncoding.GetBytes(status))); this.whatsNetwork.SendData(this._binWriter.Write(messageNode)); } @@ -904,7 +904,7 @@ private void SendReceiptAck(string to, string id, string receiptType) /// An instance of the ProtocolTreeNode class. internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode) { - return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, pNode); + return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.identifier_key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.identifier_key.id) }, pNode); } /// diff --git a/WhatsAppPort/WhatsMessageHandler.cs b/WhatsAppPort/WhatsMessageHandler.cs index 7f1c64d..3a5651c 100644 --- a/WhatsAppPort/WhatsMessageHandler.cs +++ b/WhatsAppPort/WhatsMessageHandler.cs @@ -23,28 +23,28 @@ public WhatsMessageHandler() private void WhatsEventHandlerOnMessageRecievedEvent(FMessage mess) { - if (mess == null || mess.key.remote_jid == null || mess.key.remote_jid.Length == 0) + if (mess == null || mess.identifier_key.remote_jid == null || mess.identifier_key.remote_jid.Length == 0) return; - if(!this.messageHistory.ContainsKey(mess.key.remote_jid)) - this.messageHistory.Add(mess.key.remote_jid, new List()); + if(!this.messageHistory.ContainsKey(mess.identifier_key.remote_jid)) + this.messageHistory.Add(mess.identifier_key.remote_jid, new List()); - this.messageHistory[mess.key.remote_jid].Add(mess); + this.messageHistory[mess.identifier_key.remote_jid].Add(mess); this.CheckIfUserRegisteredAndCreate(mess); } private void CheckIfUserRegisteredAndCreate(FMessage mess) { - if (this.messageHistory.ContainsKey(mess.key.remote_jid)) + if (this.messageHistory.ContainsKey(mess.identifier_key.remote_jid)) return; - var jidSplit = mess.key.remote_jid.Split('@'); - WhatsUser tmpWhatsUser = new WhatsUser(jidSplit[0], jidSplit[1], mess.key.serverNickname); + var jidSplit = mess.identifier_key.remote_jid.Split('@'); + WhatsUser tmpWhatsUser = new WhatsUser(jidSplit[0], jidSplit[1], mess.identifier_key.serverNickname); User tmpUser = new User(jidSplit[0], jidSplit[1]); tmpUser.SetUser(tmpWhatsUser); - this.messageHistory.Add(mess.key.remote_jid, new List()); - this.messageHistory[mess.key.remote_jid].Add(mess); + this.messageHistory.Add(mess.identifier_key.remote_jid, new List()); + this.messageHistory[mess.identifier_key.remote_jid].Add(mess); } public void RegisterUser(User user, WhatsEventHandler.MessageRecievedHandler messHandler) From e69d36810ace9e7c0f3bd45f09f0bbe059390de0 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 18 Oct 2013 13:00:07 +0200 Subject: [PATCH 118/271] Updated client details to 2.11.102 --- WhatsAppApi/Register/WaToken.cs | 2 +- WhatsAppApi/Settings/WhatsConstants.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs index 549bbb0..6ff2bbe 100644 --- a/WhatsAppApi/Register/WaToken.cs +++ b/WhatsAppApi/Register/WaToken.cs @@ -10,7 +10,7 @@ class WaToken { private static string WaPrefix = "Y29tLndoYXRzYXBw"; private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; - private static string WaClassesMd5 = "30CnAF22oY+2PUD5pcJGqw=="; + private static string WaClassesMd5 = "zo7YXXvrrRqpikOi/CveTw=="; private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; public static string GenerateToken(string number) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index 7d26a31..bb3c4ae 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.69"; + public const string WhatsAppVer = "2.11.102"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.69 Android/4.2.1 Device/GalaxyS3"; + public const string UserAgent = "WhatsApp/2.11.102 Android/4.2.1 Device/GalaxyS3"; #endregion From 6eb0637104724cd0ef9d13eb1f5b25d97745d16e Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 1 Nov 2013 04:21:12 -0700 Subject: [PATCH 119/271] Updated client details to 2.11.113 --- WhatsAppApi/Register/WaToken.cs | 2 +- WhatsAppApi/Settings/WhatsConstants.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs index 6ff2bbe..70acf4f 100644 --- a/WhatsAppApi/Register/WaToken.cs +++ b/WhatsAppApi/Register/WaToken.cs @@ -10,7 +10,7 @@ class WaToken { private static string WaPrefix = "Y29tLndoYXRzYXBw"; private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; - private static string WaClassesMd5 = "zo7YXXvrrRqpikOi/CveTw=="; + private static string WaClassesMd5 = "MOpsiNsR+nHEv0dFc3dqmA=="; private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; public static string GenerateToken(string number) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index bb3c4ae..30744dd 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.102"; + public const string WhatsAppVer = "2.11.113"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.102 Android/4.2.1 Device/GalaxyS3"; + public const string UserAgent = "WhatsApp/2.11.113 Android/4.2.1 Device/GalaxyS3"; #endregion From f8bbed851d3a20fecd3970a63a336f918c4b30ae Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 11 Nov 2013 10:01:58 +0100 Subject: [PATCH 120/271] Updated to version 2.11.125 --- WhatsAppApi/Register/WaToken.cs | 2 +- WhatsAppApi/Settings/WhatsConstants.cs | 6 +++--- WhatsAppApi/WhatsApp.cs | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs index 70acf4f..cac56ac 100644 --- a/WhatsAppApi/Register/WaToken.cs +++ b/WhatsAppApi/Register/WaToken.cs @@ -10,7 +10,7 @@ class WaToken { private static string WaPrefix = "Y29tLndoYXRzYXBw"; private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; - private static string WaClassesMd5 = "MOpsiNsR+nHEv0dFc3dqmA=="; + private static string WaClassesMd5 = "JqOGtSEKUWEu/jxzCB6Bdw=="; private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; public static string GenerateToken(string number) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index 30744dd..4f12130 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.113"; + public const string WhatsAppVer = "2.11.125"; /// /// The port that needs to be connected to @@ -52,12 +52,12 @@ class WhatsConstants /// /// iPhone device /// - public const string IphoneDevice = "Android"; + public const string Device = "Android"; /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.113 Android/4.2.1 Device/GalaxyS3"; + public const string UserAgent = "WhatsApp/2.11.125 Android/4.2.1 Device/GalaxyS3"; #endregion diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 3b1e226..bba9e3b 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -148,7 +148,6 @@ public CONNECTION_STATUS ConnectionStatus public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) { this.messageQueue = new List(); - this.phoneNumber = phoneNum; this.imei = imei; this.name = nick; @@ -261,7 +260,7 @@ public void Login() Encryption.encryptionOutgoing = null; string resource = string.Format(@"{0}-{1}-{2}", - WhatsConstants.IphoneDevice, + WhatsConstants.Device, WhatsConstants.WhatsAppVer, WhatsConstants.WhatsPort); var data = this.writer.StartStream(WhatsConstants.WhatsAppServer, resource); @@ -599,9 +598,11 @@ public void sendNickname(string nickname) protected ProtocolTreeNode addAuth() { var node = new ProtocolTreeNode("auth", - new KeyValue[] { new KeyValue("xmlns", @"urn:ietf:params:xml:ns:xmpp-sasl"), - new KeyValue("mechanism", "WAUTH-1"), - new KeyValue("user", this.phoneNumber) }); + new KeyValue[] { + new KeyValue("xmlns", @"urn:ietf:params:xml:ns:xmpp-sasl"), + new KeyValue("mechanism", "WAUTH-1"), + new KeyValue("user", this.phoneNumber) + }); return node; } From 444c0a427e0d4ad922eee6b314cd9ed9b3992ef6 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 15 Nov 2013 11:18:41 +0100 Subject: [PATCH 121/271] Added SetPhoto method wrapper Applies to #79 --- WhatsAppApi/WhatsApp.cs | 5 +++++ WhatsAppApi/WhatsSendHandler.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index bba9e3b..8c0b17e 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -591,6 +591,11 @@ public void sendNickname(string nickname) this.WhatsParser.WhatsSendHandler.SendAvailableForChat(nickname); } + public void SetPhoto(byte[] imageBytes, byte[] thumbnailBytes) + { + this.WhatsSendHandler.SendSetPhoto(this.GetJID(this.phoneNumber), imageBytes, thumbnailBytes); + } + /// /// Add the authenication nodes /// diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 14404e3..8196421 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -664,7 +664,7 @@ public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, A /// The amount of bytes needed for the thumbanil /// Action to execute when the request was successful /// Action to execute when the request failed - public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes, Action onSuccess, Action onError) + public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes) { string id = TicketCounter.MakeId("set_photo_"); var list = new List { new ProtocolTreeNode("picture", new[] { new KeyValue("xmlns", "w:profile:picture") }, null, bytes) }; From 8238909fbed441cf92e3b524e2c8e4b5d04c05a3 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 20 Nov 2013 10:43:45 +0100 Subject: [PATCH 122/271] Updated client details to 2.11.134 --- WhatsAppApi/Register/WaToken.cs | 2 +- WhatsAppApi/Settings/WhatsConstants.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs index cac56ac..bdb34af 100644 --- a/WhatsAppApi/Register/WaToken.cs +++ b/WhatsAppApi/Register/WaToken.cs @@ -10,7 +10,7 @@ class WaToken { private static string WaPrefix = "Y29tLndoYXRzYXBw"; private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; - private static string WaClassesMd5 = "JqOGtSEKUWEu/jxzCB6Bdw=="; + private static string WaClassesMd5 = "r4WQV17nVTl3+uFlF9mvEg=="; private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; public static string GenerateToken(string number) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index 4f12130..b7307d8 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.125"; + public const string WhatsAppVer = "2.11.134"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.125 Android/4.2.1 Device/GalaxyS3"; + public const string UserAgent = "WhatsApp/2.11.134 Android/4.2.1 Device/GalaxyS3"; #endregion From 5886c8551050b84b6ef857cf2cc04dfc0be354bf Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 21 Nov 2013 11:56:38 +0100 Subject: [PATCH 123/271] Added response output to exist request --- WhatsAppApi/Register/WhatsRegisterV2.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index 27d392e..d5f3186 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -94,6 +94,13 @@ public static string RegisterCode(string countryCode, string phoneNumber, string public static string RequestExist(string countryCode, string phoneNumber, string id = null) { + string response = string.Empty; + return RequestExist(countryCode, phoneNumber, out response, id); + } + + public static string RequestExist(string countryCode, string phoneNumber, out string response, string id = null) + { + response = string.Empty; try { if (String.IsNullOrEmpty(id)) @@ -101,7 +108,7 @@ public static string RequestExist(string countryCode, string phoneNumber, string id = phoneNumber.Reverse().ToSHAString(); } string uri = string.Format("https://v.whatsapp.net/v2/exist?cc={0}&in={1}&id={2}", countryCode, phoneNumber, id); - string response = GetResponse(uri); + response = GetResponse(uri); if (response.GetJsonValue("status") == "ok") { return response.GetJsonValue("pw"); From 2e5e6c562e238943640bf92b5d74efed9e375e41 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 26 Nov 2013 12:18:40 +0100 Subject: [PATCH 124/271] Update ProtocolTreeNode.cs Hell why not? Fixes #67 --- WhatsAppApi/Helper/ProtocolTreeNode.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/Helper/ProtocolTreeNode.cs b/WhatsAppApi/Helper/ProtocolTreeNode.cs index 5ff2f8d..a1b9d17 100644 --- a/WhatsAppApi/Helper/ProtocolTreeNode.cs +++ b/WhatsAppApi/Helper/ProtocolTreeNode.cs @@ -51,10 +51,10 @@ public string NodeString(string indent = "") } } ret += ">"; - //if (this.data.Length > 0) - //{ - // ret += WhatsApp.SYSEncoding.GetString(this.data); - //} + if (this.data.Length > 0 && this.data.Length <= 1024) + { + ret += WhatsApp.SYSEncoding.GetString(this.data); + } if (this.children != null && this.children.Count() > 0) { foreach (var item in this.children) From f5a92218f3f968cb055167fd4dbec614427ed420 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 26 Nov 2013 12:55:15 +0100 Subject: [PATCH 125/271] Added MessageVideo Fixes #73 --- WhatsAppApi/WhatsApp.cs | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 8c0b17e..f67722d 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -369,6 +369,49 @@ public void MessageImage(string to, string filepath) } + public void MessageVideo(string to, string filepath) + { + to = this.GetJID(to); + FileInfo finfo = new FileInfo(filepath); + string type = string.Empty; + switch (finfo.Extension) + { + case ".mov": + type = "video/quicktime"; + break; + case ".avi": + type = "video/x-msvideo"; + break; + default: + type = "video/mp4"; + break; + } + + //create hash + string filehash = string.Empty; + using (FileStream fs = File.OpenRead(filepath)) + { + using (BufferedStream bs = new BufferedStream(fs)) + { + using (HashAlgorithm sha = HashAlgorithm.Create("sha256")) + { + byte[] raw = sha.ComputeHash(bs); + filehash = Convert.ToBase64String(raw); + } + } + } + + //request upload + UploadResponse response = this.UploadFile(filehash, "video", finfo.Length, filepath, to, type); + + if (response != null && !String.IsNullOrEmpty(response.url)) + { + //send message + FMessage msg = new FMessage(to, true) { identifier_key = { id = TicketManager.GenerateId() }, media_wa_type = FMessage.Type.Video, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url }; + this.WhatsSendHandler.SendMessage(msg); + } + } + private byte[] CreateThumbnail(string path) { if (File.Exists(path)) From 7b4d33cc4faf59509fb5293d8d98dec95846ebd0 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 26 Nov 2013 12:56:34 +0100 Subject: [PATCH 126/271] Added MessageAudio Also for #73 --- WhatsAppApi/WhatsApp.cs | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index f67722d..92478e1 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -412,6 +412,58 @@ public void MessageVideo(string to, string filepath) } } + public void MessageAudio(string to, string filepath) + { + to = this.GetJID(to); + FileInfo finfo = new FileInfo(filepath); + string type = string.Empty; + switch (finfo.Extension) + { + case ".wav": + type = "audio/wav"; + break; + case ".ogg": + type = "audio/ogg"; + break; + case ".aif": + type = "audio/x-aiff"; + break; + case ".aac": + type = "audio/aac"; + break; + case ".m4a": + type = "audio/mp4"; + break; + default: + type = "audio/mpeg"; + break; + } + + //create hash + string filehash = string.Empty; + using (FileStream fs = File.OpenRead(filepath)) + { + using (BufferedStream bs = new BufferedStream(fs)) + { + using (HashAlgorithm sha = HashAlgorithm.Create("sha256")) + { + byte[] raw = sha.ComputeHash(bs); + filehash = Convert.ToBase64String(raw); + } + } + } + + //request upload + UploadResponse response = this.UploadFile(filehash, "audio", finfo.Length, filepath, to, type); + + if (response != null && !String.IsNullOrEmpty(response.url)) + { + //send message + FMessage msg = new FMessage(to, true) { identifier_key = { id = TicketManager.GenerateId() }, media_wa_type = FMessage.Type.Audio, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url }; + this.WhatsSendHandler.SendMessage(msg); + } + } + private byte[] CreateThumbnail(string path) { if (File.Exists(path)) From 0446b414e6b1fa20d78c561b3360d9e26d0f1512 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 2 Dec 2013 10:44:50 +0100 Subject: [PATCH 127/271] Changed GetJID to static method --- WhatsAppApi/WhatsApp.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 92478e1..741292d 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -213,12 +213,12 @@ public AccountInfo GetAccountInfo() public void GetStatus(string jid) { - this.WhatsSendHandler.SendGetStatus(this.GetJID(jid)); + this.WhatsSendHandler.SendGetStatus(GetJID(jid)); } public void PresenceSubscription(string target) { - this.WhatsSendHandler.SendPresenceSubscriptionRequest(this.GetJID(target)); + this.WhatsSendHandler.SendPresenceSubscriptionRequest(GetJID(target)); } /// @@ -289,7 +289,7 @@ public void Login() /// The text that needs to be send public void Message(string to, string txt) { - var tmpMessage = new FMessage(this.GetJID(to), true) { identifier_key = { id = TicketManager.GenerateId() }, data = txt }; + var tmpMessage = new FMessage(GetJID(to), true) { identifier_key = { id = TicketManager.GenerateId() }, data = txt }; this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage); } @@ -297,7 +297,7 @@ public void Message(string to, string txt) /// Convert the input string to a JID if necessary /// /// Phonenumber or JID - public string GetJID(string target) + public static string GetJID(string target) { if (!target.Contains('@')) { @@ -327,7 +327,7 @@ public string GetJID(string target) /// Icon public void MessageImage(string to, string filepath) { - to = this.GetJID(to); + to = GetJID(to); FileInfo finfo = new FileInfo(filepath); string type = string.Empty; switch (finfo.Extension) @@ -371,7 +371,7 @@ public void MessageImage(string to, string filepath) public void MessageVideo(string to, string filepath) { - to = this.GetJID(to); + to = GetJID(to); FileInfo finfo = new FileInfo(filepath); string type = string.Empty; switch (finfo.Extension) @@ -414,7 +414,7 @@ public void MessageVideo(string to, string filepath) public void MessageAudio(string to, string filepath) { - to = this.GetJID(to); + to = GetJID(to); FileInfo finfo = new FileInfo(filepath); string type = string.Empty; switch (finfo.Extension) @@ -674,7 +674,7 @@ public void Pong(string msgid) /// Jabber id public void RequestLastSeen(string jid) { - this.WhatsParser.WhatsSendHandler.SendQueryLastOnline(this.GetJID(jid)); + this.WhatsParser.WhatsSendHandler.SendQueryLastOnline(GetJID(jid)); } /// @@ -688,7 +688,7 @@ public void sendNickname(string nickname) public void SetPhoto(byte[] imageBytes, byte[] thumbnailBytes) { - this.WhatsSendHandler.SendSetPhoto(this.GetJID(this.phoneNumber), imageBytes, thumbnailBytes); + this.WhatsSendHandler.SendSetPhoto(GetJID(this.phoneNumber), imageBytes, thumbnailBytes); } /// From 8d1e1772dca48216f337afd340f75fdae07362b4 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 2 Dec 2013 10:45:34 +0100 Subject: [PATCH 128/271] Added message broadcasting Text messages only atm --- WhatsAppApi/WhatsSendHandler.cs | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 8196421..6d51c9a 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -486,6 +486,64 @@ public void SendMessage(FMessage message) } } + //overload + public void SendMessageBroadcast(string[] to, string message) + { + this.SendMessageBroadcast(to, new FMessage(string.Empty, true) { data = message, media_wa_type = FMessage.Type.Undefined}); + } + + //overload + public void SendMessageBroadcast(string to, FMessage message) + { + this.SendMessageBroadcast(new string[] { to }, message); + } + + //overload + public void SendMessageBroadcast(string to, string message) + { + this.SendMessageBroadcast(new string[] { to }, new FMessage(string.Empty, true) { data = message, media_wa_type = FMessage.Type.Undefined }); + } + + //send broadcast + public void SendMessageBroadcast(string[] to, FMessage message) + { + if (to != null && to.Length > 0 && message != null && !string.IsNullOrEmpty(message.data)) + { + ProtocolTreeNode child; + if (message.media_wa_type == FMessage.Type.Undefined) + { + //text broadcast + child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); + } + else + { + throw new NotImplementedException(); + } + + //compose broadcast envelope + ProtocolTreeNode xnode = new ProtocolTreeNode("x", new KeyValue[] { + new KeyValue("xmlns", "jabber:x:event") + }, new ProtocolTreeNode("server", null)); + List toNodes = new List(); + foreach (string target in to) + { + toNodes.Add(new ProtocolTreeNode("to", new KeyValue[] { new KeyValue("jid", WhatsAppApi.WhatsApp.GetJID(target)) })); + } + + ProtocolTreeNode broadcastNode = new ProtocolTreeNode("broadcast", null, toNodes); + ProtocolTreeNode messageNode = new ProtocolTreeNode("message", new KeyValue[] { + new KeyValue("to", "broadcast"), + new KeyValue("type", "chat"), + new KeyValue("id", TicketManager.GenerateId()) + }, new ProtocolTreeNode[] { + broadcastNode, + xnode, + child + }); + this.SendNode(messageNode); + } + } + /// /// Tell the server the message has been recieved. /// From 28ab5852258c37a8b20cf7c9cbfc5089fd5bd04e Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 5 Dec 2013 10:31:35 +0100 Subject: [PATCH 129/271] Updated client details to 2.11.139 Also inconspicuously made the WhatsConstants class public for use with external programs (e.g. MissVenom) Muahaha --- WhatsAppApi/Register/WaToken.cs | 2 +- WhatsAppApi/Settings/WhatsConstants.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs index bdb34af..962df74 100644 --- a/WhatsAppApi/Register/WaToken.cs +++ b/WhatsAppApi/Register/WaToken.cs @@ -10,7 +10,7 @@ class WaToken { private static string WaPrefix = "Y29tLndoYXRzYXBw"; private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; - private static string WaClassesMd5 = "r4WQV17nVTl3+uFlF9mvEg=="; + private static string WaClassesMd5 = "rzoCoMEYKQ6zoUWINKC9oQ=="; private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; public static string GenerateToken(string number) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index b7307d8..dbe30f5 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -9,7 +9,7 @@ namespace WhatsAppApi.Settings /// /// Holds constant information used to connect to whatsapp server /// - class WhatsConstants + public class WhatsConstants { #region ServerConstants @@ -42,7 +42,7 @@ class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.134"; + public const string WhatsAppVer = "2.11.139"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.134 Android/4.2.1 Device/GalaxyS3"; + public const string UserAgent = "WhatsApp/2.11.139 Android/4.2.1 Device/GalaxyS3"; #endregion From 4bda5aa7eb1a6c8c2a77c0a3d7ef07afd7192a1a Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 5 Dec 2013 16:01:52 +0100 Subject: [PATCH 130/271] Added SendUnavailable and fixed messages Added "x" node to messages --- WhatsAppApi/WhatsSendHandler.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 6d51c9a..b4474f5 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -83,6 +83,12 @@ public void SendAvailableForChat(string nickName) this.whatsNetwork.SendData(this._binWriter.Write(node)); } + public void SendUnavailable() + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); + this.whatsNetwork.SendData(this._binWriter.Write(node)); + } + public void SendClearDirty(IEnumerable categoryNames) { string id = TicketCounter.MakeId("clean_dirty_"); @@ -962,7 +968,15 @@ private void SendReceiptAck(string to, string id, string receiptType) /// An instance of the ProtocolTreeNode class. internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode) { - return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.identifier_key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.identifier_key.id) }, pNode); + return new ProtocolTreeNode("message", new[] { + new KeyValue("to", message.identifier_key.remote_jid), + new KeyValue("type", "chat"), + new KeyValue("id", message.identifier_key.id) + }, + new ProtocolTreeNode[] { + new ProtocolTreeNode("x", new KeyValue[] { new KeyValue("xmlns", "jabber:x:event") }, new ProtocolTreeNode("server", null)), + pNode + }); } /// From b4cbc78c6537e98836e655bf198aa11d4bbd9a18 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 5 Dec 2013 16:03:32 +0100 Subject: [PATCH 131/271] Added hiding online status First concept, seems to be working --- WhatsAppApi/WhatsApp.cs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 741292d..7e5bf41 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -55,6 +55,11 @@ public void ClearIncomplete() /// private string imei; + /// + /// Hide online status + /// + protected bool hidden; + /// /// Holds the login status /// @@ -145,12 +150,13 @@ public CONNECTION_STATUS ConnectionStatus /// The imei / mac /// User nickname /// Debug on or off, false by default - public WhatsApp(string phoneNum, string imei, string nick, bool debug = false) + public WhatsApp(string phoneNum, string imei, string nick, bool debug = false, bool hidden = false) { this.messageQueue = new List(); this.phoneNumber = phoneNum; this.imei = imei; this.name = nick; + this.hidden = hidden; WhatsApp.DEBUG = debug; string[] dict = DecodeHelper.getDictionary(); this.writer = new BinTreeNodeWriter(dict); @@ -279,7 +285,16 @@ public void Login() System.Threading.Thread.Sleep(50); } while ((cnt++ < 100) && (this.loginStatus == CONNECTION_STATUS.DISCONNECTED)); - this.sendNickname(this.name); + + //hidden mode + if (!this.hidden) + { + this.sendNickname(this.name); + } + else + { + this.sendOffline(); + } } /// @@ -686,6 +701,14 @@ public void sendNickname(string nickname) this.WhatsParser.WhatsSendHandler.SendAvailableForChat(nickname); } + /// + /// Send unavailable status + /// + public void sendOffline() + { + this.WhatsParser.WhatsSendHandler.SendUnavailable(); + } + public void SetPhoto(byte[] imageBytes, byte[] thumbnailBytes) { this.WhatsSendHandler.SendSetPhoto(GetJID(this.phoneNumber), imageBytes, thumbnailBytes); From f5dca73d8cfbca81d84eaa324564317efbc973f6 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 6 Dec 2013 10:45:34 +0100 Subject: [PATCH 132/271] Added JID conversion to SendGetPhoto Fixes #85 --- WhatsAppApi/WhatsSendHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index b4474f5..e81aba3 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -374,7 +374,7 @@ public string SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, attrList.Add(new KeyValue("id", expectedPhotoId)); } var child = new ProtocolTreeNode("picture", attrList.ToArray()); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid) }, child); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(jid)) }, child); this.whatsNetwork.SendData(this._binWriter.Write(node)); return id; } From 675b5d1cf7110aafb6aaa1cd3b4a2ef1653d5c6c Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 13 Dec 2013 09:58:53 +0100 Subject: [PATCH 133/271] Updated to 2.11.144 --- WhatsAppApi/Register/WaToken.cs | 2 +- WhatsAppApi/Settings/WhatsConstants.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs index 962df74..40cbdf5 100644 --- a/WhatsAppApi/Register/WaToken.cs +++ b/WhatsAppApi/Register/WaToken.cs @@ -10,7 +10,7 @@ class WaToken { private static string WaPrefix = "Y29tLndoYXRzYXBw"; private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; - private static string WaClassesMd5 = "rzoCoMEYKQ6zoUWINKC9oQ=="; + private static string WaClassesMd5 = "QtHrjQzwQjujOOOd1oObgQ=="; private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; public static string GenerateToken(string number) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index dbe30f5..7081039 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ public class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.139"; + public const string WhatsAppVer = "2.11.144"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ public class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.139 Android/4.2.1 Device/GalaxyS3"; + public const string UserAgent = "WhatsApp/2.11.144 Android/4.2.1 Device/GalaxyS3"; #endregion From 45ba8ab4e7250f0b143d346da3b7a8b1ddfaef4c Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 13 Dec 2013 09:59:23 +0100 Subject: [PATCH 134/271] Rewritten hidden mode handling --- WhatsAppApi/WhatsApp.cs | 22 +++++++++++----------- WhatsAppApi/WhatsSendHandler.cs | 19 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 7e5bf41..c6799ba 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -286,15 +286,7 @@ public void Login() } while ((cnt++ < 100) && (this.loginStatus == CONNECTION_STATUS.DISCONNECTED)); - //hidden mode - if (!this.hidden) - { - this.sendNickname(this.name); - } - else - { - this.sendOffline(); - } + this.sendNickname(this.name); } /// @@ -305,7 +297,7 @@ public void Login() public void Message(string to, string txt) { var tmpMessage = new FMessage(GetJID(to), true) { identifier_key = { id = TicketManager.GenerateId() }, data = txt }; - this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage); + this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage, this.hidden); } /// @@ -698,7 +690,7 @@ public void RequestLastSeen(string jid) /// The nickname public void sendNickname(string nickname) { - this.WhatsParser.WhatsSendHandler.SendAvailableForChat(nickname); + this.WhatsParser.WhatsSendHandler.SendAvailableForChat(nickname, this.hidden); } /// @@ -722,6 +714,7 @@ protected ProtocolTreeNode addAuth() { var node = new ProtocolTreeNode("auth", new KeyValue[] { + new KeyValue("passive", this.hidden?"true":"false"), new KeyValue("xmlns", @"urn:ietf:params:xml:ns:xmpp-sasl"), new KeyValue("mechanism", "WAUTH-1"), new KeyValue("user", this.phoneNumber) @@ -729,6 +722,11 @@ protected ProtocolTreeNode addAuth() return node; } + public void sendDeleteAccount() + { + this.WhatsSendHandler.SendDeleteAccount(); + } + /// /// Add the auth response to protocoltreenode /// @@ -769,9 +767,11 @@ protected ProtocolTreeNode addFeatures() { var child = new ProtocolTreeNode("receipt_acks", null); var child2 = new ProtocolTreeNode("w:profile:picture", new KeyValue[] { new KeyValue("type", "all") }); + var child3 = new ProtocolTreeNode("status", null); var childList = new List(); childList.Add(child); childList.Add(child2); + childList.Add(child3); var parent = new ProtocolTreeNode("stream:features", null, childList, null); return parent; } diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index e81aba3..0267898 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -77,9 +77,9 @@ public void SendAddParticipants(string gjid, IEnumerable participants, A this.SendVerbParticipants(gjid, participants, id, "add"); } - public void SendAvailableForChat(string nickName) + public void SendAvailableForChat(string nickName, bool isHidden = false) { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName) }); + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName), new KeyValue("type", isHidden?"inactive":"active") }); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -212,7 +212,7 @@ public void SendCreateGroupChat(string subject, Action onSuccess, Action /// /// The action to be executed when the request was successful. /// The action to be executed when the request failed. - public void SendDeleteAccount(Action onSuccess, Action onError) + public void SendDeleteAccount() { string id = TicketCounter.MakeId("del_acct_"); var node = new ProtocolTreeNode("iq", @@ -480,7 +480,7 @@ public void SendLeaveGroups(IEnumerable gjids, Action onSuccess, Action< /// Sends a message, message properties are defined in the instance of FMessage. /// /// An instance of the FMessage class. - public void SendMessage(FMessage message) + public void SendMessage(FMessage message, bool hidden = false) { if (message.media_wa_type != FMessage.Type.Undefined) { @@ -488,7 +488,7 @@ public void SendMessage(FMessage message) } else { - this.SendMessageWithBody(message); + this.SendMessageWithBody(message, hidden); } } @@ -837,10 +837,10 @@ internal void SendGetGroups(string id, string type) /// Send a message with a body (Plain text); /// /// An instance of the FMessage class. - internal void SendMessageWithBody(FMessage message) + internal void SendMessageWithBody(FMessage message, bool hidden = false) { var child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); - this.whatsNetwork.SendData(this._binWriter.Write(GetMessageNode(message, child))); + this.whatsNetwork.SendData(this._binWriter.Write(GetMessageNode(message, child, hidden))); } /// @@ -966,7 +966,7 @@ private void SendReceiptAck(string to, string id, string receiptType) /// the message /// The protocol tree node /// An instance of the ProtocolTreeNode class. - internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode) + internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode, bool hidden = false) { return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.identifier_key.remote_jid), @@ -975,7 +975,8 @@ internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNo }, new ProtocolTreeNode[] { new ProtocolTreeNode("x", new KeyValue[] { new KeyValue("xmlns", "jabber:x:event") }, new ProtocolTreeNode("server", null)), - pNode + pNode, + new ProtocolTreeNode("offline", null) }); } From 4ac575aa2cffad8738378a203c5fee0a01f1e00f Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 13 Dec 2013 16:59:05 +0100 Subject: [PATCH 135/271] Added overloaded codeRequest method added request url output --- WhatsAppApi/Register/WhatsRegisterV2.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs index d5f3186..490d9df 100644 --- a/WhatsAppApi/Register/WhatsRegisterV2.cs +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -30,9 +30,16 @@ public static bool RequestCode(string countryCode, string phoneNumber, out strin } public static bool RequestCode(string countryCode, string phoneNumber, out string password, out string response, string method = "sms", string id = null, string language = null, string locale = null, string mcc = "204", string salt = "") + { + string request = string.Empty; + return RequestCode(countryCode, phoneNumber, out password, out request, out response, method, id, language, locale, mcc, salt); + } + + public static bool RequestCode(string countryCode, string phoneNumber, out string password, out string request, out string response, string method = "sms", string id = null, string language = null, string locale = null, string mcc = "204", string salt = "") { response = null; password = null; + request = null; try { if (string.IsNullOrEmpty(language) || string.IsNullOrEmpty(locale)) @@ -46,8 +53,8 @@ public static bool RequestCode(string countryCode, string phoneNumber, out strin } string token = System.Uri.EscapeDataString(WhatsRegisterV2.GetToken(phoneNumber)); - string uri = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc={7}&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token, mcc); - response = GetResponse(uri); + request = string.Format("https://v.whatsapp.net/v2/code?cc={0}&in={1}&to={0}{1}&lg={2}&lc={3}&mcc={7}&mnc=008&method={4}&id={5}&token={6}", countryCode, phoneNumber, language, locale, method, id, token, mcc); + response = GetResponse(request); password = response.GetJsonValue("pw"); if (!string.IsNullOrEmpty(password)) { From 61c840ee83fae127db65ca79a1c1430a17f8dd55 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 17 Dec 2013 12:35:51 +0100 Subject: [PATCH 136/271] Added GetJID to group methods --- WhatsAppApi/WhatsSendHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 0267898..8c6bff5 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -308,7 +308,7 @@ public void SendGetGroupInfo(string gjid) { string id = TicketCounter.MakeId("get_g_info_"); var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "w:g") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] {child}); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(gjid)) }, new ProtocolTreeNode[] {child}); this.whatsNetwork.SendData(this._binWriter.Write(node)); } @@ -340,7 +340,7 @@ public void SendGetParticipants(string gjid) { string id = TicketCounter.MakeId("get_participants_"); var child = new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:g") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", gjid) }, child); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", WhatsApp.GetJID(gjid)) }, child); this.whatsNetwork.SendData(this._binWriter.Write(node)); } From 501154deb5e8aa020d4fb311b91446021bea1a37 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 17 Dec 2013 12:36:27 +0100 Subject: [PATCH 137/271] Added group query response handling --- WhatsAppApi/WhatsApp.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index c6799ba..7fc2059 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -889,6 +889,24 @@ protected void processInboundData(byte[] data) { this.Pong(node.GetAttribute("id")); } + if (ProtocolTreeNode.TagEquals(node, "iq") + && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.First(), "group")) + { + //group(s) info + foreach (ProtocolTreeNode group in node.children) + { + this.AddMessage(group); + } + } + if (ProtocolTreeNode.TagEquals(node, "iq") + && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.First(), "participant")) + { + //group participants + this.AddMessage(node); + } + if (ProtocolTreeNode.TagEquals(node, "stream:error")) { var textNode = node.GetChild("text"); From 2db4a95bfaa0a6a55587d3885d9a5e17e42b5331 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 17 Dec 2013 14:50:01 +0100 Subject: [PATCH 138/271] Removed secondary ID generation Fixes #90 --- WhatsAppApi/WhatsApp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 7fc2059..d1ce1f2 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -296,7 +296,7 @@ public void Login() /// The text that needs to be send public void Message(string to, string txt) { - var tmpMessage = new FMessage(GetJID(to), true) { identifier_key = { id = TicketManager.GenerateId() }, data = txt }; + var tmpMessage = new FMessage(GetJID(to), true) { data = txt }; this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage, this.hidden); } From 6e5b436e7a64d7df3ea9c5cd75e6133e12e300a6 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 17 Dec 2013 14:52:23 +0100 Subject: [PATCH 139/271] Updated FMessage identifier Contributed by JCarles --- WhatsAppApi/Parser/FMessage.cs | 7 ++----- WhatsAppApi/Response/MessageRecvResponse.cs | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/WhatsAppApi/Parser/FMessage.cs b/WhatsAppApi/Parser/FMessage.cs index 312206f..67365fc 100644 --- a/WhatsAppApi/Parser/FMessage.cs +++ b/WhatsAppApi/Parser/FMessage.cs @@ -183,6 +183,7 @@ public class Builder internal string thumb_image; internal DateTime? timestamp; internal bool? wants_receipt; + internal string serverNickname; public byte[] BinaryData() { @@ -204,6 +205,7 @@ public FMessage Build() if (((this.remote_jid != null) && this.from_me.HasValue) && (this.id != null)) { this.message.identifier_key = new FMessage.FMessageIdentifierKey(this.remote_jid, this.from_me.Value, this.id); + this.message.identifier_key.serverNickname = this.serverNickname; } if (this.remote_resource != null) { @@ -310,11 +312,6 @@ public bool Instantiated() return (this.message != null); } - public FMessage.FMessageIdentifierKey Key() - { - return new FMessage.FMessageIdentifierKey(this.remote_jid, (!this.from_me.HasValue && this.from_me.Value), this.id); - } - public FMessage.Builder Key(FMessage.FMessageIdentifierKey key) { this.remote_jid = key.remote_jid; diff --git a/WhatsAppApi/Response/MessageRecvResponse.cs b/WhatsAppApi/Response/MessageRecvResponse.cs index 5723582..a588349 100644 --- a/WhatsAppApi/Response/MessageRecvResponse.cs +++ b/WhatsAppApi/Response/MessageRecvResponse.cs @@ -257,7 +257,7 @@ private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string t builder.from_me = false; builder.id = tmpAttrbId; builder.remote_jid = tmpAttrFromJid; - builder.Key().serverNickname = tmpAttrName; + builder.serverNickname = tmpAttrName; } } } From e06e3f360699e67faabb32b368fdad7747c4a4f8 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 17 Dec 2013 15:30:09 +0100 Subject: [PATCH 140/271] Cleaned up all secondary id generation Applies to #90 as well --- WhatsAppApi/WhatsApp.cs | 6 +++--- WhatsAppApi/WhatsSendHandler.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index d1ce1f2..9144ba3 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -370,7 +370,7 @@ public void MessageImage(string to, string filepath) if (response != null && !String.IsNullOrEmpty(response.url)) { //send message - FMessage msg = new FMessage(to, true) { identifier_key = { id = TicketManager.GenerateId() }, media_wa_type = FMessage.Type.Image, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url, binary_data = this.CreateThumbnail(filepath) }; + FMessage msg = new FMessage(to, true) { media_wa_type = FMessage.Type.Image, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url, binary_data = this.CreateThumbnail(filepath) }; this.WhatsSendHandler.SendMessage(msg); } @@ -414,7 +414,7 @@ public void MessageVideo(string to, string filepath) if (response != null && !String.IsNullOrEmpty(response.url)) { //send message - FMessage msg = new FMessage(to, true) { identifier_key = { id = TicketManager.GenerateId() }, media_wa_type = FMessage.Type.Video, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url }; + FMessage msg = new FMessage(to, true) { media_wa_type = FMessage.Type.Video, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url }; this.WhatsSendHandler.SendMessage(msg); } } @@ -466,7 +466,7 @@ public void MessageAudio(string to, string filepath) if (response != null && !String.IsNullOrEmpty(response.url)) { //send message - FMessage msg = new FMessage(to, true) { identifier_key = { id = TicketManager.GenerateId() }, media_wa_type = FMessage.Type.Audio, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url }; + FMessage msg = new FMessage(to, true) { media_wa_type = FMessage.Type.Audio, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url }; this.WhatsSendHandler.SendMessage(msg); } } diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 8c6bff5..8158b76 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -540,7 +540,7 @@ public void SendMessageBroadcast(string[] to, FMessage message) ProtocolTreeNode messageNode = new ProtocolTreeNode("message", new KeyValue[] { new KeyValue("to", "broadcast"), new KeyValue("type", "chat"), - new KeyValue("id", TicketManager.GenerateId()) + new KeyValue("id", message.identifier_key.id) }, new ProtocolTreeNode[] { broadcastNode, xnode, From c160a1f63b080dbf2ab0cf9b07af494f2455331b Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 17 Dec 2013 18:13:39 +0100 Subject: [PATCH 141/271] First draft of new event system --- WhatsAppApi/WhatsApp.cs | 48 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 9144ba3..46a772e 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -187,7 +187,22 @@ public void AddMessage(ProtocolTreeNode node) /// public void Connect() { - this.whatsNetwork.Connect(); + try + { + this.whatsNetwork.Connect(); + //success + if (this.OnConnectSuccess != null) + { + this.OnConnectSuccess(); + } + } + catch (Exception e) + { + if (this.OnConnectFailed != null) + { + this.OnConnectFailed(e); + } + } } /// @@ -197,6 +212,10 @@ public void Disconnect() { this.whatsNetwork.Disconenct(); this.loginStatus = CONNECTION_STATUS.DISCONNECTED; + if (this.OnDisconnect != null) + { + this.OnDisconnect(null); + } } /// @@ -838,10 +857,18 @@ protected void processInboundData(byte[] data) node.GetAttribute("kind"), node.GetAttribute("creation"), node.GetAttribute("expiration")); + if (this.OnLoginSuccess != null) + { + this.OnLoginSuccess(node.GetData()); + } } else if (ProtocolTreeNode.TagEquals(node, "failure")) { this.loginStatus = CONNECTION_STATUS.UNAUTHORIZED; + if (this.OnLoginFailed != null) + { + this.OnLoginFailed(node.children.First().tag); + } } if (ProtocolTreeNode.TagEquals(node, "message")) { @@ -969,5 +996,24 @@ private void PrintInfo(string p) { this.DebugPrint(p); } + + + + + + // events + public event NullDelegate OnConnectSuccess; + public event ExceptionDelegate OnConnectFailed; + public event ExceptionDelegate OnDisconnect; + public event ByteArrayDelegate OnLoginSuccess; + public event StringDelegate OnLoginFailed; + + + + //event delegates + public delegate void NullDelegate(); + public delegate void ExceptionDelegate(Exception ex); + public delegate void ByteArrayDelegate(byte[] data); + public delegate void StringDelegate(string data); } } From 9a026223701521c668814034e43c8f8f3792d2c7 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 17 Dec 2013 21:05:34 +0100 Subject: [PATCH 142/271] More events And more NotImplementedExceptions for TODO stuff --- WhatsAppApi/WhatsApp.cs | 61 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 46a772e..18e84c8 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -844,7 +844,11 @@ protected void processInboundData(byte[] data) if (node.tag == "iq" && node.GetAttribute("type") == "error") { - this.AddMessage(node); + //this.AddMessage(node); + if (this.OnError != null) + { + this.OnError(node.GetAttribute("id"), node.children.First().tag); + } } if (ProtocolTreeNode.TagEquals(node, "challenge")) { @@ -872,7 +876,40 @@ protected void processInboundData(byte[] data) } if (ProtocolTreeNode.TagEquals(node, "message")) { - this.AddMessage(node); + if (node.GetChild("body") != null) + { + //text message + if (this.OnGetMessage != null) + { + this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), (node.GetChild("notify") != null?node.GetChild("notify").GetAttribute("name"):null), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); + } + } + if (node.GetChild("media") != null) + { + //media message + throw new NotImplementedException(); + } + ProtocolTreeNode x = node.GetChild("x"); + if (x != null && x.GetAttribute("xmlns") == "jabber:x:event") + { + if (x.GetChild("server") != null && this.OnGetMessageReceivedServer != null) + { + this.OnGetMessageReceivedServer(node.GetAttribute("from"), node.GetAttribute("id")); + } + else + { + throw new NotImplementedException(); + } + } + ProtocolTreeNode notification = node.GetChild("notification"); + if (notification != null) + { + if (notification.GetAttribute("type") == "picture" && this.OnNotificationPicture != null) + { + this.OnNotificationPicture(notification.tag, notification.GetAttribute("jid"), notification.GetAttribute("id")); + } + } + if (node.GetChild("request") != null) { this.sendMessageReceived(node); @@ -892,7 +929,7 @@ protected void processInboundData(byte[] data) ) { //last seen - this.AddMessage(node); + throw new NotImplementedException(); } if (ProtocolTreeNode.TagEquals(node, "iq") && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) @@ -908,7 +945,7 @@ protected void processInboundData(byte[] data) ) { //profile picture - this.AddMessage(node); + throw new NotImplementedException(); } if (ProtocolTreeNode.TagEquals(node, "iq") && node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) @@ -923,7 +960,7 @@ protected void processInboundData(byte[] data) //group(s) info foreach (ProtocolTreeNode group in node.children) { - this.AddMessage(group); + throw new NotImplementedException(); } } if (ProtocolTreeNode.TagEquals(node, "iq") @@ -931,7 +968,7 @@ protected void processInboundData(byte[] data) && ProtocolTreeNode.TagEquals(node.children.First(), "participant")) { //group participants - this.AddMessage(node); + throw new NotImplementedException(); } if (ProtocolTreeNode.TagEquals(node, "stream:error")) @@ -952,7 +989,7 @@ protected void processInboundData(byte[] data) if (ProtocolTreeNode.TagEquals(node, "presence")) { //presence node - this.AddMessage(node); + throw new NotImplementedException(); } node = this.reader.nextTree(); } @@ -1008,12 +1045,22 @@ private void PrintInfo(string p) public event ByteArrayDelegate OnLoginSuccess; public event StringDelegate OnLoginFailed; + public event OnGetMessageDelegate OnGetMessage; + public event OnErrorDelegate OnError; + public event OnNotificationPictureDelegate OnNotificationPicture; + + public event OnGetMessageReceivedDelegate OnGetMessageReceivedServer; + public event OnGetMessageReceivedDelegate OnGetMessageReceivedClient; //event delegates public delegate void NullDelegate(); public delegate void ExceptionDelegate(Exception ex); public delegate void ByteArrayDelegate(byte[] data); public delegate void StringDelegate(string data); + public delegate void OnErrorDelegate(string id, string error); + public delegate void OnGetMessageReceivedDelegate(string from, string id); + public delegate void OnNotificationPictureDelegate(string type, string jid, string id); + public delegate void OnGetMessageDelegate(string from, string id, string name, string message); } } From bda85b41aba25dd80ebd78576716738093a1d70f Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 23 Dec 2013 17:22:42 +0100 Subject: [PATCH 143/271] Changed WhatsUser method to use generic GetJID --- WhatsAppApi/Account/WhatsUser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/Account/WhatsUser.cs b/WhatsAppApi/Account/WhatsUser.cs index d55a5e6..ad15fc1 100644 --- a/WhatsAppApi/Account/WhatsUser.cs +++ b/WhatsAppApi/Account/WhatsUser.cs @@ -21,7 +21,7 @@ public WhatsUser(string jid, string srvUrl, string nickname = "") public string GetFullJid() { - return string.Format("{0}@{1}", this.Jid, this.serverUrl); + return WhatsAppApi.WhatsApp.GetJID(this.Jid); } internal void SetServerUrl(string srvUrl) From bff3b0d22cd75ba2315c22380068b914739ee1a1 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 23 Dec 2013 17:23:33 +0100 Subject: [PATCH 144/271] Finished up events in WhatsApp.cs --- WhatsAppApi/WhatsApp.cs | 197 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 184 insertions(+), 13 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 18e84c8..36326c4 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -208,13 +208,13 @@ public void Connect() /// /// Disconnect from the whatsapp network /// - public void Disconnect() + public void Disconnect(Exception ex = null) { this.whatsNetwork.Disconenct(); this.loginStatus = CONNECTION_STATUS.DISCONNECTED; if (this.OnDisconnect != null) { - this.OnDisconnect(null); + this.OnDisconnect(ex); } } @@ -884,10 +884,78 @@ protected void processInboundData(byte[] data) this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), (node.GetChild("notify") != null?node.GetChild("notify").GetAttribute("name"):null), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); } } + if (node.GetChild("received") != null) + { + //client received + if (this.OnGetMessageReceivedClient != null) + { + this.OnGetMessageReceivedClient(node.GetAttribute("from"), node.GetAttribute("id")); + } + } if (node.GetChild("media") != null) { + ProtocolTreeNode media = node.GetChild("media"); //media message - throw new NotImplementedException(); + + //define variables in switch + string file, url, from, name, id; + int size; + byte[] preview, dat; + id = node.GetAttribute("id"); + from = node.GetAttribute("from"); + switch (media.GetAttribute("type")) + { + case "image": + if (this.OnGetMessageImage != null) + { + url = media.GetAttribute("url"); + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + preview = media.GetData(); + this.OnGetMessageImage(from, id, file, size, url, preview); + } + break; + case "audio": + if (this.OnGetMessageAudio != null) + { + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + url = media.GetAttribute("url"); + preview = media.GetData(); + this.OnGetMessageAudio(from, id, file, size, url, preview); + } + break; + case "video": + if(this.OnGetMessageVideo != null) + { + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + url = media.GetAttribute("url"); + preview = media.GetData(); + this.OnGetMessageVideo(from, id, file, size, url, preview); + } + break; + case "location": + if (this.OnGetMessageLocation != null) + { + double lon = double.Parse(media.GetAttribute("longitude")); + double lat = double.Parse(media.GetAttribute("latitude")); + preview = media.GetData(); + name = media.GetAttribute("name"); + url = media.GetAttribute("url"); + this.OnGetMessageLocation(from, id, lon, lat, url, name, preview); + } + break; + case "vcard": + if (this.OnGetMessageVcard != null) + { + ProtocolTreeNode vcard = media.GetChild("vcard"); + name = vcard.GetAttribute("name"); + dat = vcard.GetData(); + this.OnGetMessageVcard(from, id, name, dat); + } + break; + } } ProtocolTreeNode x = node.GetChild("x"); if (x != null && x.GetAttribute("xmlns") == "jabber:x:event") @@ -896,10 +964,6 @@ protected void processInboundData(byte[] data) { this.OnGetMessageReceivedServer(node.GetAttribute("from"), node.GetAttribute("id")); } - else - { - throw new NotImplementedException(); - } } ProtocolTreeNode notification = node.GetChild("notification"); if (notification != null) @@ -918,6 +982,22 @@ protected void processInboundData(byte[] data) { this.sendMessageReceived(node, "ack"); } + if (node.GetChild("composing") != null) + { + //typing + if (this.OnGetTyping != null) + { + this.OnGetTyping(node.GetAttribute("from")); + } + } + if (node.GetChild("paused") != null) + { + //paused + if (this.OnGetPaused != null) + { + this.OnGetPaused(node.GetAttribute("from")); + } + } } if (ProtocolTreeNode.TagEquals(node, "stream:error")) { @@ -926,10 +1006,15 @@ protected void processInboundData(byte[] data) if (ProtocolTreeNode.TagEquals(node, "iq") && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) && ProtocolTreeNode.TagEquals(node.children.First(), "query") + && node.children.First().GetAttribute("xmlns") == "jabber:iq:last" ) { //last seen - throw new NotImplementedException(); + DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.First().GetAttribute("seconds")) * -1); + if(this.OnGetLastSeen != null) + { + this.OnGetLastSeen(node.GetAttribute("from"), lastSeen); + } } if (ProtocolTreeNode.TagEquals(node, "iq") && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) @@ -945,7 +1030,24 @@ protected void processInboundData(byte[] data) ) { //profile picture - throw new NotImplementedException(); + string from = node.GetAttribute("from"); + string id = node.GetChild("picture").GetAttribute("id"); + byte[] dat = node.GetChild("picture").GetData(); + string type = node.GetChild("picture").GetAttribute("type"); + if (type == "preview") + { + if (this.OnGetPhotoPreview != null) + { + this.OnGetPhotoPreview(from, id, dat); + } + } + else + { + if (this.OnGetPhoto != null) + { + this.OnGetPhoto(from, id, dat); + } + } } if (ProtocolTreeNode.TagEquals(node, "iq") && node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) @@ -958,9 +1060,21 @@ protected void processInboundData(byte[] data) && ProtocolTreeNode.TagEquals(node.children.First(), "group")) { //group(s) info + List groups = new List(); foreach (ProtocolTreeNode group in node.children) { - throw new NotImplementedException(); + groups.Add(new GroupInfo( + group.GetAttribute("id"), + group.GetAttribute("owner"), + long.Parse(group.GetAttribute("creation")), + group.GetAttribute("subject"), + long.Parse(group.GetAttribute("s_t")), + group.GetAttribute("s_o") + )); + } + if (this.OnGetGroups != null) + { + this.OnGetGroups(groups.ToArray()); } } if (ProtocolTreeNode.TagEquals(node, "iq") @@ -968,7 +1082,18 @@ protected void processInboundData(byte[] data) && ProtocolTreeNode.TagEquals(node.children.First(), "participant")) { //group participants - throw new NotImplementedException(); + List participants = new List(); + foreach (ProtocolTreeNode part in node.GetAllChildren()) + { + if (part.tag == "participant" && !string.IsNullOrEmpty(part.GetAttribute("jid"))) + { + participants.Add(part.GetAttribute("jid")); + } + } + if (this.OnGetGroupParticipants != null) + { + this.OnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); + } } if (ProtocolTreeNode.TagEquals(node, "stream:error")) @@ -980,7 +1105,7 @@ protected void processInboundData(byte[] data) Console.WriteLine("Error : " + content); if (content.Equals("Replaced by new connection", StringComparison.OrdinalIgnoreCase)) { - this.Disconnect(); + this.Disconnect(new Exception(content)); this.Connect(); this.Login(); } @@ -989,7 +1114,10 @@ protected void processInboundData(byte[] data) if (ProtocolTreeNode.TagEquals(node, "presence")) { //presence node - throw new NotImplementedException(); + if (this.OnGetPresence != null) + { + this.OnGetPresence(node.GetAttribute("from"), node.GetAttribute("type")); + } } node = this.reader.nextTree(); } @@ -1046,6 +1174,11 @@ private void PrintInfo(string p) public event StringDelegate OnLoginFailed; public event OnGetMessageDelegate OnGetMessage; + public event OnGetMediaDelegate OnGetMessageImage; + public event OnGetMediaDelegate OnGetMessageVideo; + public event OnGetMediaDelegate OnGetMessageAudio; + public event OnGetLocationDelegate OnGetMessageLocation; + public event OnGetVcardDelegate OnGetMessageVcard; public event OnErrorDelegate OnError; public event OnNotificationPictureDelegate OnNotificationPicture; @@ -1053,6 +1186,15 @@ private void PrintInfo(string p) public event OnGetMessageReceivedDelegate OnGetMessageReceivedServer; public event OnGetMessageReceivedDelegate OnGetMessageReceivedClient; + public event OnGetPresenceDelegate OnGetPresence; + public event OnGetGroupParticipantsDelegate OnGetGroupParticipants; + public event OnGetLastSeenDelegate OnGetLastSeen; + public event OnGetchatStateDelegate OnGetTyping; + public event OnGetchatStateDelegate OnGetPaused; + public event OnGetPictureDelegate OnGetPhoto; + public event OnGetPictureDelegate OnGetPhotoPreview; + public event OnGetGroupsDelegate OnGetGroups; + //event delegates public delegate void NullDelegate(); public delegate void ExceptionDelegate(Exception ex); @@ -1062,5 +1204,34 @@ private void PrintInfo(string p) public delegate void OnGetMessageReceivedDelegate(string from, string id); public delegate void OnNotificationPictureDelegate(string type, string jid, string id); public delegate void OnGetMessageDelegate(string from, string id, string name, string message); + public delegate void OnGetPresenceDelegate(string from, string type); + public delegate void OnGetGroupParticipantsDelegate(string gjid, string[] jids); + public delegate void OnGetLastSeenDelegate(string from, DateTime lastSeen); + public delegate void OnGetchatStateDelegate(string from); + public delegate void OnGetMediaDelegate(string from, string id, string fileName, int fileSize, string url, byte[] preview); + public delegate void OnGetLocationDelegate(string from, string id, double lon, double lat, string url, string name, byte[] preview); + public delegate void OnGetVcardDelegate(string from, string id, string name, byte[] data); + public delegate void OnGetPictureDelegate(string from, string id, byte[] data); + public delegate void OnGetGroupsDelegate(GroupInfo[] groups); + + public class GroupInfo + { + public readonly string id; + public readonly string owner; + public readonly long creation; + public readonly string subject; + public readonly long subjectChangedTime; + public readonly string subjectChangedBy; + + internal GroupInfo(string id, string owner, long creation, string subject, long subjectChanged, string subjectChangedBy) + { + this.id = id; + this.owner = owner; + this.creation = creation; + this.subject = subject; + this.subjectChangedTime = subjectChanged; + this.subjectChangedBy = subjectChangedBy; + } + } } } From c900ba49a97bb678122f8bb4d100781609fe7238 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Mon, 23 Dec 2013 17:26:11 +0100 Subject: [PATCH 145/271] Updated example project to use events --- WhatsTest/Program.cs | 218 ++++++++++++++++++++++++++++++++----------- 1 file changed, 162 insertions(+), 56 deletions(-) diff --git a/WhatsTest/Program.cs b/WhatsTest/Program.cs index 4b8bfe1..ba65f48 100644 --- a/WhatsTest/Program.cs +++ b/WhatsTest/Program.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Net; using System.Runtime.CompilerServices; @@ -22,23 +23,166 @@ private static void Main(string[] args) var tmpEncoding = Encoding.UTF8; System.Console.OutputEncoding = Encoding.Default; System.Console.InputEncoding = Encoding.Default; - string nickname = "WhatsAPI Test"; - string sender = "3526********"; // Mobile number with country code (but without + or 00) - string password = "JJ9gQWk******************Y=";//v2 password + string nickname = "WhatsApiNet"; + string sender = "316********"; // Mobile number with country code (but without + or 00) + string password = "K/1**************yeix87Q=";//v2 password string target = "316********";// Mobile number to send the message to WhatsApp wa = new WhatsApp(sender, password, nickname, true); - + + //event bindings + wa.OnLoginSuccess += wa_OnLoginSuccess; + wa.OnLoginFailed += wa_OnLoginFailed; + wa.OnGetMessage += wa_OnGetMessage; + wa.OnGetMessageReceivedClient += wa_OnGetMessageReceivedClient; + wa.OnGetMessageReceivedServer += wa_OnGetMessageReceivedServer; + wa.OnNotificationPicture += wa_OnNotificationPicture; + wa.OnGetPresence += wa_OnGetPresence; + wa.OnGetGroupParticipants += wa_OnGetGroupParticipants; + wa.OnGetLastSeen += wa_OnGetLastSeen; + wa.OnGetTyping += wa_OnGetTyping; + wa.OnGetPaused += wa_OnGetPaused; + wa.OnGetMessageImage += wa_OnGetMessageImage; + wa.OnGetMessageAudio += wa_OnGetMessageAudio; + wa.OnGetMessageVideo += wa_OnGetMessageVideo; + wa.OnGetMessageLocation += wa_OnGetMessageLocation; + wa.OnGetMessageVcard += wa_OnGetMessageVcard; + wa.OnGetPhoto += wa_OnGetPhoto; + wa.OnGetPhotoPreview += wa_OnGetPhotoPreview; + wa.OnGetGroups += wa_OnGetGroups; + wa.Connect(); wa.Login(); - wa.PollMessages(); - wa.Message(target, "Hi this is sent using WhatsApiNet"); - wa.PollMessages(); + ProcessChat(wa, target); + Console.ReadKey(); + } - ProcessChat(wa, ""); + static void wa_OnGetGroups(WhatsApp.GroupInfo[] groups) + { + Console.WriteLine("Got groups:"); + foreach (WhatsAppApi.WhatsApp.GroupInfo info in groups) + { + Console.WriteLine("\t{0} {1}", info.subject, info.id); + } + } - Console.ReadKey(); + static void wa_OnGetPhotoPreview(string from, string id, byte[] data) + { + Console.WriteLine("Got preview photo for {0}", from); + File.WriteAllBytes(string.Format("preview_{0}.jpg", from), data); + } + + static void wa_OnGetPhoto(string from, string id, byte[] data) + { + Console.WriteLine("Got full photo for {0}", from); + File.WriteAllBytes(string.Format("{0}.jpg", from), data); + } + + static void wa_OnGetMessageVcard(string from, string id, string name, byte[] data) + { + Console.WriteLine("Got vcard \"{0}\" from {1}", name, from); + File.WriteAllBytes(string.Format("{0}.vcf", name), data); + } + + static void wa_OnGetMessageLocation(string from, string id, double lon, double lat, string url, string name, byte[] preview) + { + Console.WriteLine("Got location from {0} ({1}, {2})", from, lat, lon); + if(!string.IsNullOrEmpty(name)) + { + Console.WriteLine("\t{0}", name); + } + File.WriteAllBytes(string.Format("{0}{1}.jpg", lat, lon), preview); + } + + static void wa_OnGetMessageVideo(string from, string id, string fileName, int fileSize, string url, byte[] preview) + { + Console.WriteLine("Got video from {0}", from, fileName); + OnGetMedia(fileName, url, preview); + } + + static void OnGetMedia(string file, string url, byte[] data) + { + //save preview + File.WriteAllBytes(string.Format("preview_{0}.jpg", file), data); + //download + using (WebClient wc = new WebClient()) + { + wc.DownloadFileAsync(new Uri(url), file, null); + } + } + + static void wa_OnGetMessageAudio(string from, string id, string fileName, int fileSize, string url, byte[] preview) + { + Console.WriteLine("Got audio from {0}", from, fileName); + OnGetMedia(fileName, url, preview); + } + + static void wa_OnGetMessageImage(string from, string id, string fileName, int size, string url, byte[] preview) + { + Console.WriteLine("Got image from {0}", from, fileName); + OnGetMedia(fileName, url, preview); + } + + static void wa_OnGetPaused(string from) + { + Console.WriteLine("{0} stopped typing", from); + } + + static void wa_OnGetTyping(string from) + { + Console.WriteLine("{0} is typing...", from); + } + + static void wa_OnGetLastSeen(string from, DateTime lastSeen) + { + Console.WriteLine("{0} last seen on {1}", from, lastSeen.ToString()); + } + + static void wa_OnGetMessageReceivedServer(string from, string id) + { + Console.WriteLine("Message {0} to {1} received by server", id, from); + } + + static void wa_OnGetMessageReceivedClient(string from, string id) + { + Console.WriteLine("Message {0} to {1} received by client", id, from); + } + + static void wa_OnGetGroupParticipants(string gjid, string[] jids) + { + Console.WriteLine("Got participants from {0}:", gjid); + foreach (string jid in jids) + { + Console.WriteLine("\t{0}", jid); + } + } + + static void wa_OnGetPresence(string from, string type) + { + Console.WriteLine("Presence from {0}: {1}", from, type); + } + + static void wa_OnNotificationPicture(string type, string jid, string id) + { + //TODO + throw new NotImplementedException(); + } + + static void wa_OnGetMessage(string from, string id, string name, string message) + { + Console.WriteLine("Message from {0} (1): {2}", name, from, message); + } + + private static void wa_OnLoginFailed(string data) + { + Console.WriteLine("Login failed. Reason: {0}", data); + } + + private static void wa_OnLoginSuccess(byte[] data) + { + Console.WriteLine("Login success. Next password:"); + Console.WriteLine(data); } @@ -50,20 +194,18 @@ private static void ProcessChat(WhatsApp wa, string dst) { while (wa != null) { - if (!wa.HasMessages()) - { - wa.PollMessages(); - Thread.Sleep(100); - continue; - } - var buff = wa.GetAllMessages(); + wa.PollMessages(); + Thread.Sleep(100); + continue; } + } catch (ThreadAbortException) { } }) {IsBackground = true}; thRecv.Start(); + WhatsUserManager usrMan = new WhatsUserManager(); var tmpUser = usrMan.CreateUser(dst, "User"); @@ -78,13 +220,13 @@ private static void ProcessChat(WhatsApp wa, string dst) { case "/query": //var dst = dst//trim(strstr($line, ' ', FALSE)); - PrintToConsole("[] Interactive conversation with {0}:", tmpUser); + Console.WriteLine("[] Interactive conversation with {0}:", tmpUser); break; case "/accountinfo": - PrintToConsole("[] Account Info: {0}", wa.GetAccountInfo().ToString()); + Console.WriteLine("[] Account Info: {0}", wa.GetAccountInfo().ToString()); break; case "/lastseen": - PrintToConsole("[] Request last seen {0}", tmpUser); + Console.WriteLine("[] Request last seen {0}", tmpUser); wa.RequestLastSeen(tmpUser.GetFullJid()); break; case "/exit": @@ -97,48 +239,12 @@ private static void ProcessChat(WhatsApp wa, string dst) case "/pause": wa.WhatsSendHandler.SendPaused(tmpUser.GetFullJid()); break; - case "/register": - { - RegisterAccount(); - break; - } default: - PrintToConsole("[] Send message to {0}: {1}", tmpUser, line); + Console.WriteLine("[] Send message to {0}: {1}", tmpUser, line); wa.Message(tmpUser.GetFullJid(), line); break; } } } - - private static void RegisterAccount() - { - Console.Write("CountryCode (ex. 31): "); - string countryCode = Console.ReadLine(); - Console.Write("Phonenumber (ex. 650568134): "); - string phoneNumber = Console.ReadLine(); - string password = null; - if (!WhatsRegisterV2.RequestCode(countryCode, phoneNumber, out password)) - return; - Console.Write("Enter received code: "); - string tmpCode = Console.ReadLine(); - - password = WhatsRegisterV2.RegisterCode(countryCode, phoneNumber, tmpCode); - if (String.IsNullOrEmpty(password)) - { - Console.WriteLine("Error registering code"); - } - else - { - Console.WriteLine(String.Format("Registration succesful. Password = {0}", password)); - } - Console.ReadLine(); - } - - - [MethodImpl(MethodImplOptions.Synchronized)] - private static void PrintToConsole(string value, params object[] tmpParams) - { - Console.WriteLine(value, tmpParams); - } } } From 50df3365dc33e1faad32fa50861fcd1943931994 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 24 Dec 2013 11:15:36 +0100 Subject: [PATCH 146/271] Updated client details to Android 2.11.151 --- WhatsAppApi/Register/WaToken.cs | 18 ++++++++++-------- WhatsAppApi/Settings/WhatsConstants.cs | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs index 40cbdf5..94c93f2 100644 --- a/WhatsAppApi/Register/WaToken.cs +++ b/WhatsAppApi/Register/WaToken.cs @@ -8,18 +8,20 @@ namespace WhatsAppApi.Register { class WaToken { - private static string WaPrefix = "Y29tLndoYXRzYXBw"; + //private static string WaPrefix = "Y29tLndoYXRzYXBw"; private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; - private static string WaClassesMd5 = "QtHrjQzwQjujOOOd1oObgQ=="; - private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; + private static string WaClassesMd5 = "94bjoO7brhy/QJZRceJHYw=="; + //private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; public static string GenerateToken(string number) { - List key = new List(Convert.FromBase64String(WaToken.WaPrefix)); - key.AddRange(Convert.FromBase64String(WaToken.DataFile)); + //List key = new List(Convert.FromBase64String(WaToken.WaPrefix)); + //key.AddRange(Convert.FromBase64String(WaToken.DataFile)); - Rfc2898DeriveBytes r = new Rfc2898DeriveBytes(key.ToArray(), Convert.FromBase64String(WaToken.WaKey), 128); - key = new List(r.GetBytes(80)); + //Rfc2898DeriveBytes r = new Rfc2898DeriveBytes(key.ToArray(), Convert.FromBase64String(WaToken.WaKey), 128); + //key = new List(r.GetBytes(80)); + + List key = new List(Convert.FromBase64String("/UIGKU1FVQa+ATM2A0za7G2KI9S/CwPYjgAbc67v7ep42eO/WeTLx1lb1cHwxpsEgF4+PmYpLd2YpGUdX/A2JQitsHzDwgcdBpUf7psX1BU=")); List data = new List(Convert.FromBase64String(WaToken.WaSignature)); data.AddRange(Convert.FromBase64String(WaToken.WaClassesMd5)); @@ -53,6 +55,6 @@ private static List GetFilledList(byte item, int length) return result; } - private static string DataFile = "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"; + //private static string DataFile = "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"; } } diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index 7081039..f226002 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ public class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.144"; + public const string WhatsAppVer = "2.11.151"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ public class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.144 Android/4.2.1 Device/GalaxyS3"; + public const string UserAgent = "WhatsApp/2.11.151 Android/4.2.1 Device/GalaxyS3"; #endregion From 2f99fd5f165095abd3fdf08324619bb128dd5cb3 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 25 Dec 2013 00:00:33 +0100 Subject: [PATCH 147/271] Fixed array out of bounds exception --- WhatsAppApi/Helper/BinTreeNodeReader.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WhatsAppApi/Helper/BinTreeNodeReader.cs b/WhatsAppApi/Helper/BinTreeNodeReader.cs index cdf1542..0f0a86f 100644 --- a/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -69,6 +69,10 @@ public ProtocolTreeNode nextTree(byte[] pInput = null, bool useDecrypt = true) protected void decode(int stanzaSize, bool useDecrypt) { int size = stanzaSize; + if (stanzaSize > this.buffer.Count) + { + throw new IncompleteMessageException(System.Text.Encoding.UTF8.GetString(buffer.ToArray())); + } byte[] data = new byte[size]; byte[] dataReal = null; Buffer.BlockCopy(this.buffer.ToArray(), 0, data, 0, size); From c6e222992ae8b6338bbe02c550f15279e4c83c88 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 25 Dec 2013 00:01:08 +0100 Subject: [PATCH 148/271] Updated events (again) --- WhatsAppApi/WhatsApp.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 36326c4..42548d1 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -847,7 +847,7 @@ protected void processInboundData(byte[] data) //this.AddMessage(node); if (this.OnError != null) { - this.OnError(node.GetAttribute("id"), node.children.First().tag); + this.OnError(node.GetAttribute("id"), node.GetAttribute("from"), Int32.Parse(node.GetChild("error").GetAttribute("code")), node.GetChild("error").GetAttribute("text")); } } if (ProtocolTreeNode.TagEquals(node, "challenge")) @@ -876,12 +876,20 @@ protected void processInboundData(byte[] data) } if (ProtocolTreeNode.TagEquals(node, "message")) { + if (node.GetChild("notify") != null) + { + string name = node.GetChild("notify").GetAttribute("name"); + if (this.OnGetContactName != null) + { + this.OnGetContactName(node.GetAttribute("from"), name); + } + } if (node.GetChild("body") != null) { //text message if (this.OnGetMessage != null) { - this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), (node.GetChild("notify") != null?node.GetChild("notify").GetAttribute("name"):null), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); + this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); } } if (node.GetChild("received") != null) @@ -898,7 +906,7 @@ protected void processInboundData(byte[] data) //media message //define variables in switch - string file, url, from, name, id; + string file, url, from, id; int size; byte[] preview, dat; id = node.GetAttribute("id"); @@ -1194,16 +1202,18 @@ private void PrintInfo(string p) public event OnGetPictureDelegate OnGetPhoto; public event OnGetPictureDelegate OnGetPhotoPreview; public event OnGetGroupsDelegate OnGetGroups; + public event OnContactNameDelegate OnGetContactName; //event delegates + public delegate void OnContactNameDelegate(string from, string contactName); public delegate void NullDelegate(); public delegate void ExceptionDelegate(Exception ex); public delegate void ByteArrayDelegate(byte[] data); public delegate void StringDelegate(string data); - public delegate void OnErrorDelegate(string id, string error); + public delegate void OnErrorDelegate(string id, string from, int code, string text); public delegate void OnGetMessageReceivedDelegate(string from, string id); public delegate void OnNotificationPictureDelegate(string type, string jid, string id); - public delegate void OnGetMessageDelegate(string from, string id, string name, string message); + public delegate void OnGetMessageDelegate(string from, string id, string message); public delegate void OnGetPresenceDelegate(string from, string type); public delegate void OnGetGroupParticipantsDelegate(string gjid, string[] jids); public delegate void OnGetLastSeenDelegate(string from, DateTime lastSeen); From f31da0588efdd8f9c71698f3e00910d7cf840572 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Fri, 27 Dec 2013 13:31:49 +0100 Subject: [PATCH 149/271] Fixed parsing location to double --- WhatsAppApi/WhatsApp.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 36326c4..c77a00e 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -938,8 +938,8 @@ protected void processInboundData(byte[] data) case "location": if (this.OnGetMessageLocation != null) { - double lon = double.Parse(media.GetAttribute("longitude")); - double lat = double.Parse(media.GetAttribute("latitude")); + double lon = double.Parse(media.GetAttribute("longitude"), System.Globalization.CultureInfo.InvariantCulture); + double lat = double.Parse(media.GetAttribute("latitude"), System.Globalization.CultureInfo.InvariantCulture); preview = media.GetData(); name = media.GetAttribute("name"); url = media.GetAttribute("url"); From 7177cd6013ca55eb82a51b12bcd30057581faf95 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 2 Jan 2014 11:12:10 +0100 Subject: [PATCH 150/271] Fixed nullPointerException in media upload And added more media attributes to uploadResponse class --- WhatsAppApi/WhatsApp.cs | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 42548d1..826c101 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -433,7 +433,7 @@ public void MessageVideo(string to, string filepath) if (response != null && !String.IsNullOrEmpty(response.url)) { //send message - FMessage msg = new FMessage(to, true) { media_wa_type = FMessage.Type.Video, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url }; + FMessage msg = new FMessage(to, true) { media_wa_type = FMessage.Type.Video, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url, media_duration_seconds = response.duration }; this.WhatsSendHandler.SendMessage(msg); } } @@ -485,7 +485,7 @@ public void MessageAudio(string to, string filepath) if (response != null && !String.IsNullOrEmpty(response.url)) { //send message - FMessage msg = new FMessage(to, true) { media_wa_type = FMessage.Type.Audio, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url }; + FMessage msg = new FMessage(to, true) { media_wa_type = FMessage.Type.Audio, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url, media_duration_seconds = response.duration }; this.WhatsSendHandler.SendMessage(msg); } } @@ -658,6 +658,12 @@ public class UploadResponse public int width { get; set; } public int height { get; set; } + public int duration { get; set; } + public string acodec { get; set; } + public int asampfreq { get; set; } + public string asampfmt { get; set; } + public int abitrate { get; set; } + public UploadResponse() { } @@ -666,13 +672,25 @@ public UploadResponse(ProtocolTreeNode node) node = node.GetChild("duplicate"); if (node != null) { + int oSize, oWidth, oHeight, oDuration, oAsampfreq, oAbitrate; this.url = node.GetAttribute("url"); this.mimetype = node.GetAttribute("mimetype"); - this.size = Int32.Parse(node.GetAttribute("size")); + Int32.TryParse(node.GetAttribute("size"), out oSize); this.filehash = node.GetAttribute("filehash"); this.type = node.GetAttribute("type"); - this.width = Int32.Parse(node.GetAttribute("width")); - this.height = Int32.Parse(node.GetAttribute("height")); + Int32.TryParse(node.GetAttribute("width"), out oWidth); + Int32.TryParse(node.GetAttribute("height"), out oHeight); + Int32.TryParse(node.GetAttribute("duration"), out oDuration); + this.acodec = node.GetAttribute("acodec"); + Int32.TryParse(node.GetAttribute("asampfreq"), out oAsampfreq); + this.asampfmt = node.GetAttribute("asampfmt"); + Int32.TryParse(node.GetAttribute("abitrate"), out oAbitrate); + this.size = oSize; + this.width = oWidth; + this.height = oHeight; + this.duration = oDuration; + this.asampfreq = oAsampfreq; + this.abitrate = oAbitrate; } } } @@ -976,9 +994,9 @@ protected void processInboundData(byte[] data) ProtocolTreeNode notification = node.GetChild("notification"); if (notification != null) { - if (notification.GetAttribute("type") == "picture" && this.OnNotificationPicture != null) + if (notification.GetAttribute("type") == "picture" && this.OnNotificationPicture != null && notification.GetChild("set") != null) { - this.OnNotificationPicture(notification.tag, notification.GetAttribute("jid"), notification.GetAttribute("id")); + this.OnNotificationPicture(notification.tag, notification.GetChild("set").GetAttribute("jid"), notification.GetChild("set").GetAttribute("id")); } } From 42c06987384f4ba62f1f2502e36651d0af4d154b Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Sat, 4 Jan 2014 17:57:59 +0100 Subject: [PATCH 151/271] Fixed onGetMessageDelegate --- WhatsAppApi/WhatsApp.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 8155b30..15b7750 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -907,7 +907,7 @@ protected void processInboundData(byte[] data) //text message if (this.OnGetMessage != null) { - this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); + this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), node.GetChild("notify").GetAttribute("name"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); } } if (node.GetChild("received") != null) @@ -1231,7 +1231,7 @@ private void PrintInfo(string p) public delegate void OnErrorDelegate(string id, string from, int code, string text); public delegate void OnGetMessageReceivedDelegate(string from, string id); public delegate void OnNotificationPictureDelegate(string type, string jid, string id); - public delegate void OnGetMessageDelegate(string from, string id, string message); + public delegate void OnGetMessageDelegate(string from, string id, string name, string message); public delegate void OnGetPresenceDelegate(string from, string type); public delegate void OnGetGroupParticipantsDelegate(string gjid, string[] jids); public delegate void OnGetLastSeenDelegate(string from, DateTime lastSeen); From 145a40a20498499c762ba5a621e4118722962b37 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 23 Jan 2014 10:56:36 +0100 Subject: [PATCH 152/271] Added SendSync --- WhatsAppApi/WhatsApp.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 8155b30..2edb49f 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -319,6 +319,33 @@ public void Message(string to, string txt) this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage, this.hidden); } + public void SendSync(string[] numbers, string mode = "full", string context = "registration", int index = 0, bool last = true) + { + List users = new List(); + foreach (string number in numbers) + { + users.Add(new ProtocolTreeNode("user", null, System.Text.Encoding.UTF8.GetBytes(number))); + } + ProtocolTreeNode node = new ProtocolTreeNode("iq", new KeyValue[] + { + new KeyValue("to", GetJID(this.phoneNumber)), + new KeyValue("type", "get"), + new KeyValue("id", TicketCounter.MakeId("sendsync_")), + new KeyValue("xmlns", "urn:xmpp:whatsapp:sync") + }, new ProtocolTreeNode("sync", new KeyValue[] + { + new KeyValue("mode", mode), + new KeyValue("context", context), + new KeyValue("sid", DateTime.Now.ToFileTimeUtc().ToString()), + new KeyValue("index", index.ToString()), + new KeyValue("last", last.ToString()) + }, + users.ToArray() + ) + ); + this.WhatsSendHandler.SendNode(node); + } + /// /// Convert the input string to a JID if necessary /// From 5e4dd16eabcaa58c18e2791eba6c0d7a39caeb4d Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 28 Jan 2014 11:24:37 +0100 Subject: [PATCH 153/271] Got WAUTH2 to finally work Woohoo! --- WhatsAppApi/Helper/BinTreeNodeReader.cs | 151 +++---- WhatsAppApi/Helper/BinTreeNodeWriter.cs | 73 ++-- WhatsAppApi/Helper/KeyStream.cs | 85 ++++ WhatsAppApi/Helper/TokenMap.cs | 513 ++++++++++++++++++++++++ WhatsAppApi/WhatsApp.cs | 118 +++--- WhatsAppApi/WhatsAppApi.csproj | 5 +- WhatsAppApi/WhatsNetwork.cs | 33 +- WhatsAppApi/WhatsSendHandler.cs | 98 ++--- 8 files changed, 822 insertions(+), 254 deletions(-) create mode 100644 WhatsAppApi/Helper/KeyStream.cs create mode 100644 WhatsAppApi/Helper/TokenMap.cs diff --git a/WhatsAppApi/Helper/BinTreeNodeReader.cs b/WhatsAppApi/Helper/BinTreeNodeReader.cs index 0f0a86f..db800b9 100644 --- a/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ b/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -1,129 +1,90 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Sockets; using System.Text; namespace WhatsAppApi.Helper { public class BinTreeNodeReader { - private string[] dictionary; - public byte[] Encryptionkey { get; set; } + public KeyStream Key; private List buffer; - public BinTreeNodeReader(string[] dict) + public BinTreeNodeReader() { - this.dictionary = dict; - this.Encryptionkey = null; + + } + + public void SetKey(byte[] key, byte[] mac) + { + this.Key = new KeyStream(key, mac); } public ProtocolTreeNode nextTree(byte[] pInput = null, bool useDecrypt = true) { - if (pInput != null) + + if (pInput != null && pInput.Length > 0) { - if (pInput.Length == 0) - return null; this.buffer = new List(); this.buffer.AddRange(pInput); - } - - int stanzaFlag = (this.peekInt8() & 0xF0) >> 4; - int stanzaSize = this.peekInt16(1); - - int flags = stanzaFlag; - int size = stanzaSize; - - if (stanzaSize > this.buffer.Count) - { - var exception = new IncompleteMessageException("Incomplete message"); - exception.setInput(this.buffer.ToArray()); - throw exception; - } - - this.readInt24(); - - bool isEncrypted = (stanzaFlag & 8) != 0; - - if (isEncrypted) - { - if (Encryptionkey != null) - { - decode(size, useDecrypt); - } - else - { - throw new Exception("Received encrypted message, encryption key not set"); - } - } - if(stanzaSize > 0) - { - ProtocolTreeNode node = this.nextTreeInternal(); - if (node != null) - this.DebugPrint(node.NodeString("RECVD: ")); - return node; - } - return null; - } - - protected void decode(int stanzaSize, bool useDecrypt) - { - int size = stanzaSize; - if (stanzaSize > this.buffer.Count) - { - throw new IncompleteMessageException(System.Text.Encoding.UTF8.GetString(buffer.ToArray())); - } - byte[] data = new byte[size]; - byte[] dataReal = null; - Buffer.BlockCopy(this.buffer.ToArray(), 0, data, 0, size); + int stanzaFlag = (this.peekInt8() & 0xF0) >> 4; + int stanzaSize = this.peekInt16(1); - byte[] packet = new byte[size - 4]; + int flags = stanzaFlag; + int size = stanzaSize; - byte[] hashServerByte = new byte[4]; - Buffer.BlockCopy(data, 0, hashServerByte, 0, 4); - Buffer.BlockCopy(data, 4, packet, 0, size - 4); + this.readInt24(); - System.Security.Cryptography.HMACSHA1 h = new System.Security.Cryptography.HMACSHA1(this.Encryptionkey); - byte[] hashByte = new byte[4]; - Buffer.BlockCopy(h.ComputeHash(packet, 0, packet.Length), 0, hashByte, 0, 4); + bool isEncrypted = (stanzaFlag & 8) != 0; - if (hashServerByte.SequenceEqual(hashByte)) - { - this.buffer.RemoveRange(0, 4); - if (useDecrypt) + if (isEncrypted) { - dataReal = Encryption.WhatsappDecrypt(this.Encryptionkey, packet); - } - else - { - dataReal = Encryption.WhatsappEncrypt(this.Encryptionkey, packet, true); - //dataReal = new byte[foo.Length - 4]; - //Buffer.BlockCopy(foo, 0, dataReal, 0, dataReal.Length); + if (this.Key != null) + { + var realStanzaSize = stanzaSize - 4; + var macOffset = stanzaSize - 4; + var treeData = this.buffer.ToArray(); + try + { + this.Key.DecodeMessage(treeData, macOffset, 0, realStanzaSize); + } + catch (Exception e) + { + Console.WriteLine(e); + } + this.buffer.Clear(); + this.buffer.AddRange(treeData); + } + else + { + throw new Exception("Received encrypted message, encryption key not set"); + } } - for (int i = 0; i < size - 4; i++) + if (stanzaSize > 0) { - this.buffer[i] = dataReal[i]; + ProtocolTreeNode node = this.nextTreeInternal(); + if (node != null) + this.DebugPrint(node.NodeString("RECVD: ")); + return node; } } - else - { - throw new Exception("Hash doesnt match"); - } + return null; } protected string getToken(int token) { - string ret = ""; - if ((token >= 0) && (token < this.dictionary.Length)) + string tokenString = null; + int num = -1; + new TokenDictionary().GetToken(token, ref num, ref tokenString); + if (tokenString == null) { - ret = this.dictionary[token]; + token = readInt8(); + new TokenDictionary().GetToken(token, ref num, ref tokenString); } - else - { - throw new Exception("BinTreeNodeReader->getToken: Invalid token " + token); - } - return ret; + return tokenString; } protected byte[] readBytes(int token) @@ -133,7 +94,7 @@ protected byte[] readBytes(int token) { throw new Exception("BinTreeNodeReader->readString: Invalid token " + token); } - if ((token > 4) && (token < 0xf5)) + if ((token > 2) && (token < 245)) { ret = WhatsApp.SYSEncoding.GetBytes(this.getToken(token)); } @@ -141,22 +102,22 @@ protected byte[] readBytes(int token) { ret = new byte[0]; } - else if (token == 0xfc) + else if (token == 252) { int size = this.readInt8(); ret = this.fillArray(size); } - else if (token == 0xfd) + else if (token == 253) { int size = this.readInt24(); ret = this.fillArray(size); } - else if (token == 0xfe) + else if (token == 254) { int tmpToken = this.readInt8(); ret = WhatsApp.SYSEncoding.GetBytes(this.getToken(tmpToken + 0xf5)); } - else if (token == 0xfa) + else if (token == 250) { string user = WhatsApp.SYSEncoding.GetString(this.readBytes(this.readInt8())); string server = WhatsApp.SYSEncoding.GetString(this.readBytes(this.readInt8())); diff --git a/WhatsAppApi/Helper/BinTreeNodeWriter.cs b/WhatsAppApi/Helper/BinTreeNodeWriter.cs index e065517..140e1ab 100644 --- a/WhatsAppApi/Helper/BinTreeNodeWriter.cs +++ b/WhatsAppApi/Helper/BinTreeNodeWriter.cs @@ -8,23 +8,10 @@ namespace WhatsAppApi.Helper public class BinTreeNodeWriter { private List buffer; - private Dictionary tokenMap; - public byte[] Encryptionkey { get; set; } + public KeyStream Key; - public BinTreeNodeWriter(string[] dict) + public BinTreeNodeWriter() { - this.tokenMap = new Dictionary(); - for (int i = 0; i < dict.Length; i++) - { - if (dict[i] != null && dict[i].Length > 0) - { - if (!this.tokenMap.ContainsKey(dict[i])) - this.tokenMap.Add(dict[i], i); - else - this.tokenMap[dict[i]] = i; - } - } - buffer = new List(); } @@ -44,7 +31,7 @@ public byte[] StartStream(string domain, string resource) this.buffer.Add((byte)'W'); this.buffer.Add((byte)'A'); this.buffer.Add(0x1); - this.buffer.Add(0x2); + this.buffer.Add(0x4); this.buffer.AddRange(ret); ret = buffer.ToArray(); this.buffer = new List(); @@ -70,19 +57,26 @@ protected byte[] flushBuffer(bool encrypt = true) byte[] data = this.buffer.ToArray(); byte[] data2 = new byte[data.Length + 4]; Buffer.BlockCopy(data, 0, data2, 0, data.Length); - int len = data.Length; - byte[] size = this.GetInt24(len); - if (encrypt && this.Encryptionkey != null) + byte[] size = this.GetInt24(data.Length); + if (encrypt && this.Key != null) { - data = Encryption.WhatsappEncrypt(Encryptionkey, data, true); - len += 4; - size = this.GetInt24(len); - size[0] |= (1 << 4); + byte[] paddedData = new byte[data.Length + 4]; + Array.Copy(data, paddedData, data.Length); + this.Key.EncodeMessage(paddedData, paddedData.Length - 4, 0, paddedData.Length - 4); + data = paddedData; + + //add encryption signature + uint encryptedBit = 0u; + encryptedBit |= 8u; + long dataLength = data.Length; + size[0] = (byte)((ulong)((ulong)encryptedBit << 4) | (ulong)((dataLength & 16711680L) >> 16)); + size[1] = (byte)((dataLength & 65280L) >> 8); + size[2] = (byte)(dataLength & 255L); } byte[] ret = new byte[data.Length + 3]; Buffer.BlockCopy(size, 0, ret, 0, 3); - Buffer.BlockCopy(data, 0, ret, 3, len); + Buffer.BlockCopy(data, 0, ret, 3, data.Length); this.buffer = new List(); return ret; } @@ -219,25 +213,26 @@ protected void writeListStart(int len) protected void writeString(string tag) { - if (this.tokenMap.ContainsKey(tag)) + int intValue = -1; + int num = -1; + if (new TokenDictionary().TryGetToken(tag, ref num, ref intValue)) { - int value = this.tokenMap[tag]; - this.writeToken(value); - } - else - { - int index = tag.IndexOf('@'); - if (index != -1) - { - string server = tag.Substring(index + 1); - string user = tag.Substring(0, index); - this.writeJid(user, server); - } - else + if (num >= 0) { - this.writeBytes(tag); + this.writeToken(num); } + this.writeToken(intValue); + return; + } + int num2 = tag.IndexOf('@'); + if (num2 < 1) + { + this.writeBytes(tag); + return; } + string server = tag.Substring(num2 + 1); + string user = tag.Substring(0, num2); + this.writeJid(user, server); } protected void writeToken(int token) diff --git a/WhatsAppApi/Helper/KeyStream.cs b/WhatsAppApi/Helper/KeyStream.cs new file mode 100644 index 0000000..6ea4232 --- /dev/null +++ b/WhatsAppApi/Helper/KeyStream.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace WhatsAppApi.Helper +{ + public class KeyStream + { + public const string AuthMethod = "WAUTH-2"; + private const int Drop = 768; + private RC4 rc4; + private HMACSHA1 mac; + private uint seq; + + public KeyStream(byte[] key, byte[] macKey) + { + this.rc4 = new RC4(key, 768); + this.mac = new HMACSHA1(macKey); + } + + public static byte[][] GenerateKeys(byte[] password, byte[] nonce) + { + byte[][] array = new byte[4][]; + byte[][] array2 = array; + byte[] array3 = new byte[] + { + 1, + 2, + 3, + 4 + }; + byte[] array4 = new byte[nonce.Length + 1]; + for (int i = 0; i < nonce.Length; i++) + { + array4[i] = nonce[i]; + } + nonce = array4; + for (int j = 0; j < array2.Length; j++) + { + nonce[nonce.Length - 1] = array3[j]; + Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, nonce, 2); + array2[j] = rfc2898DeriveBytes.GetBytes(20); + } + return array2; + } + + public void DecodeMessage(byte[] buffer, int macOffset, int offset, int length) + { + byte[] array = this.ComputeMac(buffer, offset, length); + for (int i = 0; i < 4; i++) + { + if (buffer[macOffset + i] != array[i]) + { + throw new Exception(string.Format("MAC mismatch on index {0}! {1} != {2}", i, buffer[macOffset + i], array[i])); + } + } + this.rc4.Cipher(buffer, offset, length); + } + + public void EncodeMessage(byte[] buffer, int macOffset, int offset, int length) + { + this.rc4.Cipher(buffer, offset, length); + byte[] array = this.ComputeMac(buffer, offset, length); + Array.Copy(array, 0, buffer, macOffset, 4); + } + + private byte[] ComputeMac(byte[] buffer, int offset, int length) + { + this.mac.Initialize(); + this.mac.TransformBlock(buffer, offset, length, buffer, offset); + byte[] array = new byte[] + { + (byte)(this.seq >> 24), + (byte)(this.seq >> 16), + (byte)(this.seq >> 8), + (byte)this.seq + }; + this.mac.TransformFinalBlock(array, 0, array.Length); + this.seq += 1u; + return this.mac.Hash; + } + } +} diff --git a/WhatsAppApi/Helper/TokenMap.cs b/WhatsAppApi/Helper/TokenMap.cs new file mode 100644 index 0000000..5620216 --- /dev/null +++ b/WhatsAppApi/Helper/TokenMap.cs @@ -0,0 +1,513 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WhatsAppApi.Helper +{ + class TokenMap + { + public T First + { + get; + set; + } + public U Second + { + get; + set; + } + public TokenMap() + { + } + public TokenMap(T first, U second) + { + this.First = first; + this.Second = second; + } + } + + public class TokenDictionary + { + private const int secondaryStringsStart = 236; + private static string[] primaryStrings = new string[] + { + default(string), + default(string), + default(string), + "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" + }; + private static string[][] secondaryStrings = new string[][] + { + new string[] + { + "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" + } + }; + + private Dictionary primaryStringDict = new Dictionary(); + private Dictionary> secondaryStringDict = new Dictionary>(); + + public TokenDictionary() + { + for (int i = 0; i < TokenDictionary.primaryStrings.Length; i++) + { + string text = TokenDictionary.primaryStrings[i]; + if (text != null) + { + this.primaryStringDict.Add(text, i); + } + } + for (int j = 0; j < TokenDictionary.secondaryStrings.Length; j++) + { + string[] array = TokenDictionary.secondaryStrings[j]; + for (int k = 0; k < array.Length; k++) + { + string text2 = array[k]; + if (text2 != null) + { + this.secondaryStringDict.Add(text2, new TokenMap + { + First = j + 236, + Second = k + }); + } + } + } + } + + public bool TryGetToken(string str, ref int subdict, ref int token) + { + if (this.primaryStringDict.TryGetValue(str, out token)) + { + return true; + } + TokenMap tokenMap; + if (this.secondaryStringDict.TryGetValue(str, out tokenMap)) + { + subdict = tokenMap.First; + token = tokenMap.Second; + return true; + } + return false; + } + public void GetToken(int token, ref int subdict, ref string str) + { + string[] array = null; + if (subdict >= 0) + { + if (subdict >= TokenDictionary.secondaryStrings.Length) + { + throw new Exception("Invalid subdictionary " + subdict); + } + array = TokenDictionary.secondaryStrings[subdict]; + } + else + { + if (token >= 236 && token < 236 + TokenDictionary.secondaryStrings.Length) + { + subdict = token - 236; + } + else + { + array = TokenDictionary.primaryStrings; + } + } + if (array != null) + { + if (token < 0 || token > array.Length) + { + throw new Exception("Invalid token " + token); + } + str = array[token]; + if (str == null) + { + throw new Exception("invalid token/length in getToken"); + } + } + } + } +} diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 2edb49f..5d15169 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -98,11 +98,6 @@ public CONNECTION_STATUS ConnectionStatus /// private BinTreeNodeReader reader; - /// - /// An instance of the BinaryTreeNodeWriter class - /// - private BinTreeNodeWriter writer; - /// /// The timeout for the connection with the Whatsapp servers /// @@ -158,12 +153,10 @@ public WhatsApp(string phoneNum, string imei, string nick, bool debug = false, b this.name = nick; this.hidden = hidden; WhatsApp.DEBUG = debug; - string[] dict = DecodeHelper.getDictionary(); - this.writer = new BinTreeNodeWriter(dict); - this.reader = new BinTreeNodeReader(dict); + this.reader = new BinTreeNodeReader(); this.loginStatus = CONNECTION_STATUS.DISCONNECTED; this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.timeout); - this.WhatsParser = new WhatsParser(this.whatsNetwork, this.writer); + this.WhatsParser = new WhatsParser(this.whatsNetwork, new BinTreeNodeWriter()); this.WhatsSendHandler = this.WhatsParser.WhatsSendHandler; _incompleteBytes = new List(); @@ -278,25 +271,23 @@ public bool HasMessages() public void Login() { //reset stuff - this.reader.Encryptionkey = null; - this.writer.Encryptionkey = null; + this.reader.Key = null; + this.WhatsSendHandler.BinWriter.Key = null; this._challengeBytes = null; - Encryption.encryptionIncoming = null; - Encryption.encryptionOutgoing = null; string resource = string.Format(@"{0}-{1}-{2}", WhatsConstants.Device, WhatsConstants.WhatsAppVer, WhatsConstants.WhatsPort); - var data = this.writer.StartStream(WhatsConstants.WhatsAppServer, resource); + var data = this.WhatsSendHandler.BinWriter.StartStream(WhatsConstants.WhatsAppServer, resource); var feat = this.addFeatures(); var auth = this.addAuth(); this.whatsNetwork.SendData(data); - this.whatsNetwork.SendData(this.writer.Write(feat, false)); - this.whatsNetwork.SendData(this.writer.Write(auth, false)); + this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(feat, false)); + this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(auth, false)); this.PollMessages(); ProtocolTreeNode authResp = this.addAuthResponse(); - this.whatsNetwork.SendData(this.writer.Write(authResp, false)); + this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(authResp, false)); int cnt = 0; do { @@ -727,7 +718,7 @@ public UploadResponse(ProtocolTreeNode node) /// public void PollMessages() { - this.processInboundData(this.whatsNetwork.ReadData()); + this.processInboundData(); } /// @@ -780,7 +771,7 @@ protected ProtocolTreeNode addAuth() new KeyValue[] { new KeyValue("passive", this.hidden?"true":"false"), new KeyValue("xmlns", @"urn:ietf:params:xml:ns:xmpp-sasl"), - new KeyValue("mechanism", "WAUTH-1"), + new KeyValue("mechanism", Helper.KeyStream.AuthMethod), new KeyValue("user", this.phoneNumber) }); return node; @@ -803,24 +794,29 @@ protected ProtocolTreeNode addAuthResponse() System.Threading.Thread.Sleep(500); } - Rfc2898DeriveBytes r = new Rfc2898DeriveBytes(this.encryptPassword(), _challengeBytes, 16); - this._encryptionKey = r.GetBytes(20); - this.reader.Encryptionkey = _encryptionKey; - this.writer.Encryptionkey = _encryptionKey; + if (this._challengeBytes != null) + { + + byte[][] keys = KeyStream.GenerateKeys(this.encryptPassword(), this._challengeBytes); - List b = new List(); - b.AddRange(WhatsApp.SYSEncoding.GetBytes(this.phoneNumber)); - b.AddRange(this._challengeBytes); - b.AddRange(WhatsApp.SYSEncoding.GetBytes(Func.GetNowUnixTimestamp().ToString())); + this.reader.Key = new KeyStream(keys[2], keys[3]); + this.WhatsSendHandler.BinWriter.Key = new KeyStream(keys[0], keys[1]); - byte[] data = b.ToArray(); + List b = new List(); + b.AddRange(new byte[] { 0, 0, 0, 0 }); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(this.phoneNumber)); + b.AddRange(this._challengeBytes); - byte[] response = Encryption.WhatsappEncrypt(_encryptionKey, data, false); - var node = new ProtocolTreeNode("response", - new KeyValue[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") }, - response); - return node; + byte[] data = b.ToArray(); + this.WhatsSendHandler.BinWriter.Key.EncodeMessage(data, 0, 4, data.Length - 4); + var node = new ProtocolTreeNode("response", + new KeyValue[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") }, + data); + + return node; + } + throw new Exception("Auth response error"); } /// @@ -829,15 +825,7 @@ protected ProtocolTreeNode addAuthResponse() /// protected ProtocolTreeNode addFeatures() { - var child = new ProtocolTreeNode("receipt_acks", null); - var child2 = new ProtocolTreeNode("w:profile:picture", new KeyValue[] { new KeyValue("type", "all") }); - var child3 = new ProtocolTreeNode("status", null); - var childList = new List(); - childList.Add(child); - childList.Add(child2); - childList.Add(child3); - var parent = new ProtocolTreeNode("stream:features", null, childList, null); - return parent; + return new ProtocolTreeNode("stream:features", null); } /// @@ -865,24 +853,25 @@ protected void processChallenge(ProtocolTreeNode node) /// Process inbound data /// /// Data to process - protected void processInboundData(byte[] data) + protected void processInboundData() { try { - List foo = new List(); - if (this._incompleteBytes.Count > 0) - { - foreach (IncompleteMessageException e in this._incompleteBytes) - { - foo.AddRange(e.getInput()); - } - this._incompleteBytes.Clear(); - } - if (data != null) - { - foo.AddRange(data); - } - ProtocolTreeNode node = this.reader.nextTree(foo.ToArray()); + byte[] msgdata = this.whatsNetwork.ReadNextNode(); + //List foo = new List(); + //if (this._incompleteBytes.Count > 0) + //{ + // foreach (IncompleteMessageException e in this._incompleteBytes) + // { + // foo.AddRange(e.getInput()); + // } + // this._incompleteBytes.Clear(); + //} + //if (data != null) + //{ + // foo.AddRange(data); + //} + ProtocolTreeNode node = this.reader.nextTree(msgdata); while (node != null) { //this.WhatsParser.ParseProtocolNode(node); @@ -934,7 +923,7 @@ protected void processInboundData(byte[] data) //text message if (this.OnGetMessage != null) { - this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); + this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), node.GetChild("notify").GetAttribute("name"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); } } if (node.GetChild("received") != null) @@ -979,7 +968,7 @@ protected void processInboundData(byte[] data) } break; case "video": - if(this.OnGetMessageVideo != null) + if (this.OnGetMessageVideo != null) { file = media.GetAttribute("file"); size = Int32.Parse(media.GetAttribute("size")); @@ -1064,7 +1053,7 @@ protected void processInboundData(byte[] data) { //last seen DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.First().GetAttribute("seconds")) * -1); - if(this.OnGetLastSeen != null) + if (this.OnGetLastSeen != null) { this.OnGetLastSeen(node.GetAttribute("from"), lastSeen); } @@ -1142,7 +1131,7 @@ protected void processInboundData(byte[] data) { participants.Add(part.GetAttribute("jid")); } - } + } if (this.OnGetGroupParticipants != null) { this.OnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); @@ -1175,9 +1164,10 @@ protected void processInboundData(byte[] data) node = this.reader.nextTree(); } } - catch (IncompleteMessageException ex) + catch (Exception e) { - this._incompleteBytes.Add(ex); + //whatever + this._incompleteBytes.Clear(); } } @@ -1258,7 +1248,7 @@ private void PrintInfo(string p) public delegate void OnErrorDelegate(string id, string from, int code, string text); public delegate void OnGetMessageReceivedDelegate(string from, string id); public delegate void OnNotificationPictureDelegate(string type, string jid, string id); - public delegate void OnGetMessageDelegate(string from, string id, string message); + public delegate void OnGetMessageDelegate(string from, string id, string name, string message); public delegate void OnGetPresenceDelegate(string from, string type); public delegate void OnGetGroupParticipantsDelegate(string gjid, string[] jids); public delegate void OnGetLastSeenDelegate(string from, DateTime lastSeen); diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index 3a946ac..6c18d53 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -51,14 +51,13 @@ - - - + + diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index abc8434..ddb8d1d 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net.Sockets; using System.Text; @@ -71,6 +72,8 @@ public void Connect() this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.socket.Connect(this.whatsHost, this.whatsPort); this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.recvTimeout); + this.socket.ReceiveBufferSize = Int32.MaxValue; + //this.socket.SendBufferSize = Int32.MaxValue; if (!this.socket.Connected) throw new ConnectionException("Cannot connect"); @@ -91,11 +94,9 @@ public void Disconenct() /// Read 1024 bytes /// /// - public byte[] ReadData() + public byte[] ReadData(int length = 1024) { - List buff = new List(); - byte[] ret = Socket_read(1024); - return ret; + return Socket_read(length); } /// @@ -115,6 +116,30 @@ public void SendData(byte[] data) { Socket_send(data); } + + public byte[] ReadNextNode() + { + byte[] nodeHeader = this.ReadData(3); + if (nodeHeader.Length != 3) + { + throw new Exception("Failed to read node header"); + } + int nodeLength = 0; + nodeLength = (int)nodeHeader[1] << 8; + nodeLength |= (int)nodeHeader[2] << 0; + + byte[] nodeData = this.ReadData(nodeLength); + if (nodeData.Length != nodeLength) + { + throw new Exception("Read Next Tree error"); + } + + byte[] fullData = new byte[nodeHeader.Length + nodeData.Length]; + List buff = new List(); + buff.AddRange(nodeHeader); + buff.AddRange(nodeData); + return buff.ToArray(); + } /// /// Read in a message with a specific length diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 8158b76..415ba1d 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -27,7 +27,7 @@ public class WhatsSendHandler /// /// Holds an instance of the BinTreeNodeWriter /// - private BinTreeNodeWriter _binWriter; + internal BinTreeNodeWriter BinWriter; /// /// Holds an instance of the WhatsNetwork class @@ -42,7 +42,7 @@ public class WhatsSendHandler internal WhatsSendHandler(WhatsNetwork net, BinTreeNodeWriter writer) { this.whatsNetwork = net; - this._binWriter = writer; + this.BinWriter = writer; } /// @@ -51,7 +51,7 @@ internal WhatsSendHandler(WhatsNetwork net, BinTreeNodeWriter writer) public void SendActive() { var node = new ProtocolTreeNode("presence", new[] {new KeyValue("type", "active")}); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -80,13 +80,13 @@ public void SendAddParticipants(string gjid, IEnumerable participants, A public void SendAvailableForChat(string nickName, bool isHidden = false) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName), new KeyValue("type", isHidden?"inactive":"active") }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } public void SendUnavailable() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } public void SendClearDirty(IEnumerable categoryNames) @@ -100,7 +100,7 @@ public void SendClearDirty(IEnumerable categoryNames) new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "s.whatsapp.net") }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } public void SendClearDirty(string category) @@ -119,7 +119,7 @@ public void SendClientConfig(string platform, string lg, string lc) string v = TicketCounter.MakeId("config_"); var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("lg", lg), new KeyValue("lc", lc) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", whatsAppRealm) }, new ProtocolTreeNode[] { child }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -161,7 +161,7 @@ public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, }, this.ProcessGroupSettings(groups)) }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -170,7 +170,7 @@ public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, public void SendClose() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -181,7 +181,7 @@ public void SendComposing(string to) { var child = new ProtocolTreeNode("composing", new[] { new KeyValue("xmlns", "http://jabber.org/protocol/chatstates") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "chat") }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -204,7 +204,7 @@ public void SendCreateGroupChat(string subject, Action onSuccess, Action string id = TicketCounter.MakeId("create_group_"); var child = new ProtocolTreeNode("group", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("action", "create"), new KeyValue("subject", subject) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "g.us") }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -229,7 +229,7 @@ public void SendDeleteAccount() new KeyValue("xmlns", "urn:xmpp:whatsapp:account") }) }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -242,7 +242,7 @@ public void SendDeleteFromRoster(string jid) var innerChild = new ProtocolTreeNode("item", new[] { new KeyValue("jid", jid), new KeyValue("subscription", "remove") }); var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "jabber:iq:roster") }, new ProtocolTreeNode[] {innerChild}); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "set"), new KeyValue("id", v) }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -275,7 +275,7 @@ public void SendEndGroupChat(string gjid, Action onSuccess, Action onError) string id = TicketCounter.MakeId("remove_group_"); var child = new ProtocolTreeNode("group", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("action", "delete") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -286,7 +286,7 @@ public void SendGetClientConfig() string id = TicketCounter.MakeId("get_config_"); var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", this.whatsAppRealm) }, new ProtocolTreeNode[] { child }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -297,7 +297,7 @@ public void SendGetDirty() string id = TicketCounter.MakeId("get_dirty_"); var child = new ProtocolTreeNode("status", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -309,7 +309,7 @@ public void SendGetGroupInfo(string gjid) string id = TicketCounter.MakeId("get_g_info_"); var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "w:g") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(gjid)) }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -341,7 +341,7 @@ public void SendGetParticipants(string gjid) string id = TicketCounter.MakeId("get_participants_"); var child = new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:g") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", WhatsApp.GetJID(gjid)) }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -375,7 +375,7 @@ public string SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, } var child = new ProtocolTreeNode("picture", attrList.ToArray()); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(jid)) }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); return id; } @@ -389,7 +389,7 @@ public void SendGetPhotoIds(IEnumerable jids) var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", this.MyJID) }, new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:profile:picture") }, (from jid in jids select new ProtocolTreeNode("user", new[] { new KeyValue("jid", jid) })).ToArray())); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -401,7 +401,7 @@ public void SendGetPrivacyList() var innerChild = new ProtocolTreeNode("list", new[] { new KeyValue("name", "default") }); var child = new ProtocolTreeNode("query", new KeyValue[] { new KeyValue("xmlns", "jabber:iq:privacy") }, innerChild); var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "get") }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -412,7 +412,7 @@ public void SendGetServerProperties() string id = TicketCounter.MakeId("get_server_properties_"); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, new ProtocolTreeNode("props", new[] { new KeyValue("xmlns", "w") })); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -428,7 +428,7 @@ public void SendGetStatus(string jid) string v = TicketManager.GenerateId(); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "action"), new KeyValue("id", v) }, new ProtocolTreeNode("action", new[] { new KeyValue("type", "get") })); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } } @@ -438,7 +438,7 @@ public void SendGetStatus(string jid) public void SendInactive() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "inactive") }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -473,7 +473,7 @@ public void SendLeaveGroups(IEnumerable gjids, Action onSuccess, Action< IEnumerable innerChilds = from gjid in gjids select new ProtocolTreeNode("group", new[] { new KeyValue("id", gjid) }); var child = new ProtocolTreeNode("leave", new KeyValue[] { new KeyValue("xmlns", "w:g") }, innerChilds); var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "g.us") }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -558,7 +558,7 @@ public void SendMessageReceived(FMessage message, string response) { var child = new ProtocolTreeNode(response, new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", message.identifier_key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.identifier_key.id) }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -566,7 +566,7 @@ public void SendMessageReceived(FMessage message, string response) /// public void SendNop() { - this.whatsNetwork.SendData(this._binWriter.Write(null)); + this.whatsNetwork.SendData(this.BinWriter.Write(null)); } /// @@ -578,7 +578,7 @@ public void SendNotificationReceived(string jid, string id) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "notification"), new KeyValue("id", id) }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -589,7 +589,7 @@ public void SendPaused(string to) { var child = new ProtocolTreeNode("paused", new[] { new KeyValue("xmlns", "http://jabber.org/protocol/chatstates") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "chat") }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -600,7 +600,7 @@ public void SendPing() string id = TicketCounter.MakeId("ping_"); var child = new ProtocolTreeNode("ping", new[] { new KeyValue("xmlns", "w:p") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get") }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -610,7 +610,7 @@ public void SendPing() public void SendPong(string id) { var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "result"), new KeyValue("to", this.whatsAppRealm), new KeyValue("id", id) }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -620,7 +620,7 @@ public void SendPong(string id) public void SendPresenceSubscriptionRequest(string to) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "subscribe"), new KeyValue("to", to) }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -632,7 +632,7 @@ public void SendQueryLastOnline(string jid) string id = TicketCounter.MakeId("last_"); var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "jabber:iq:last") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid) }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -645,7 +645,7 @@ public void SendRelayCapable(string platform, bool value) string v = TicketCounter.MakeId("relay_"); var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("relay", value ? "1" : "0") }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", this.whatsAppRealm) }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -657,7 +657,7 @@ public void SendRelayComplete(string id, int millis) { var child = new ProtocolTreeNode("relay", new[] { new KeyValue("elapsed", millis.ToString(CultureInfo.InvariantCulture)) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "result"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -669,7 +669,7 @@ public void SendRelayTimeout(string id) var innerChild = new ProtocolTreeNode("remote-server-timeout", new[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas") }); var child = new ProtocolTreeNode("error", new[] { new KeyValue("code", "504"), new KeyValue("type", "wait") }, innerChild); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "error"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -717,7 +717,7 @@ public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, A string id = TicketCounter.MakeId("set_group_subject_"); var child = new ProtocolTreeNode("subject", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("value", subject) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -737,7 +737,7 @@ public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes) list.Add(new ProtocolTreeNode("picture", new[] { new KeyValue("type", "preview") }, null, thumbnailBytes)); } var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", jid) }, list.ToArray()); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -762,7 +762,7 @@ public void SendSetPrivacyBlockedList(IEnumerable jidSet, Action onSucce var child = new ProtocolTreeNode("list", new KeyValue[] { new KeyValue("name", "default") }, (nodeArray.Length == 0) ? null : nodeArray); var node2 = new ProtocolTreeNode("query", new KeyValue[] { new KeyValue("xmlns", "jabber:iq:privacy") }, child); var node3 = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set") }, node2); - this.whatsNetwork.SendData(this._binWriter.Write(node3)); + this.whatsNetwork.SendData(this.BinWriter.Write(node3)); } /// @@ -776,7 +776,7 @@ public void SendStatusUpdate(string status, Action onComplete, Action onErr string id = TicketManager.GenerateId(); FMessage message = new FMessage(new FMessage.FMessageIdentifierKey("s.us", true, id)); var messageNode = GetMessageNode(message, new ProtocolTreeNode("body", null, WhatsApp.SYSEncoding.GetBytes(status))); - this.whatsNetwork.SendData(this._binWriter.Write(messageNode)); + this.whatsNetwork.SendData(this.BinWriter.Write(messageNode)); } /// @@ -788,7 +788,7 @@ public void SendSubjectReceived(string to, string id) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = GetSubjectMessage(to, id, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -798,7 +798,7 @@ public void SendSubjectReceived(string to, string id) public void SendUnsubscribeHim(string jid) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribed"), new KeyValue("to", jid) }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -808,7 +808,7 @@ public void SendUnsubscribeHim(string jid) public void SendUnsubscribeMe(string jid) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribe"), new KeyValue("to", jid) }); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -830,7 +830,7 @@ internal void SendGetGroups(string id, string type) { var child = new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("type", type) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "g.us") }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -840,7 +840,7 @@ internal void SendGetGroups(string id, string type) internal void SendMessageWithBody(FMessage message, bool hidden = false) { var child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); - this.whatsNetwork.SendData(this._binWriter.Write(GetMessageNode(message, child, hidden))); + this.whatsNetwork.SendData(this.BinWriter.Write(GetMessageNode(message, child, hidden))); } /// @@ -899,12 +899,12 @@ internal void SendMessageWithMedia(FMessage message) } node = new ProtocolTreeNode("media", list.ToArray(), null, data); } - this.whatsNetwork.SendData(this._binWriter.Write(GetMessageNode(message, node))); + this.whatsNetwork.SendData(this.BinWriter.Write(GetMessageNode(message, node))); } public void SendNode(ProtocolTreeNode node) { - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -919,7 +919,7 @@ internal void SendVerbParticipants(string gjid, IEnumerable participants IEnumerable source = from jid in participants select new ProtocolTreeNode("participant", new[] { new KeyValue("jid", jid) }); var child = new ProtocolTreeNode(inner_tag, new[] { new KeyValue("xmlns", "w:g") }, source); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, child); - this.whatsNetwork.SendData(this._binWriter.Write(node)); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } /// @@ -957,7 +957,7 @@ private void SendReceiptAck(string to, string id, string receiptType) new KeyValue("type", "chat"), new KeyValue("id", id) }, tmpChild); - this.whatsNetwork.SendData(this._binWriter.Write(resultNode)); + this.whatsNetwork.SendData(this.BinWriter.Write(resultNode)); } /// From bdd9b9bb69a03d41bb98f58628293cc9c95ace2d Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 28 Jan 2014 11:24:57 +0100 Subject: [PATCH 154/271] Removed deprecated files --- WhatsAppApi/Helper/ContactSync.cs | 163 ---------------- WhatsAppApi/Helper/DecodeHelper.cs | 303 ----------------------------- WhatsAppApi/Helper/Encryption.cs | 48 ----- 3 files changed, 514 deletions(-) delete mode 100644 WhatsAppApi/Helper/ContactSync.cs delete mode 100644 WhatsAppApi/Helper/DecodeHelper.cs delete mode 100644 WhatsAppApi/Helper/Encryption.cs diff --git a/WhatsAppApi/Helper/ContactSync.cs b/WhatsAppApi/Helper/ContactSync.cs deleted file mode 100644 index 1605511..0000000 --- a/WhatsAppApi/Helper/ContactSync.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Security.Cryptography; -using System.Text; -using System.Web.Script.Serialization; - -namespace WhatsAppApi.Helper -{ - public class ContactSync - { - const string RequestURL = "https://sro.whatsapp.net/v2/sync/a"; - const string ExecureURL = "https://sro.whatsapp.net/v2/sync/q"; - protected string username; - protected string password; - protected HttpWebRequest request; - - public ContactSync(string username, string password) - { - this.username = username; - this.password = password; - } - - public ContactSyncResult[] Sync(string[] contacts) - { - string nonce = this._getSyncNonce(); - string res = this._executeSync(nonce, contacts); - - JavaScriptSerializer jss = new JavaScriptSerializer(); - ContactSyncResultContainer c = jss.Deserialize(res); - - return c.c; - } - - protected string _getPostfields(string[] contacts) - { - string fields = "ut=all&t=c"; - foreach (string contact in contacts) - { - string con = contact; - if (!con.Contains('+')) - { - con = "%2B" + con; - } - fields += "&u[]=" + con; - } - return fields; - } - - protected string _executeSync(string cnonce, string[] contacts) - { - this.request = WebRequest.Create(ExecureURL) as HttpWebRequest; - string postfields = this._getPostfields(contacts); - request.Method = "POST"; - this._setHeaders(cnonce, postfields.Length); - using (var writer = new StreamWriter(request.GetRequestStream())) - { - writer.Write(postfields); - } - try - { - HttpWebResponse response = this.request.GetResponse() as HttpWebResponse; - StreamReader reader = new StreamReader(response.GetResponseStream()); - return reader.ReadToEnd(); - } - catch (Exception) - { - return null; - } - } - - protected string _getSyncNonce() - { - this.request = WebRequest.Create(RequestURL) as HttpWebRequest; - this._setHeaders("0", 0); - HttpWebResponse response = this.request.GetResponse() as HttpWebResponse; - string cnonce = this._getCnonce(response.Headers.Get("WWW-Authenticate")); - return cnonce; - } - - protected string _getCnonce(string header) - { - string[] parts = header.Split(','); - parts = parts.Last().Replace('\\', '\0').Split('"'); - return parts[1]; - } - - protected static string _getCnonce() - { - string foo = _hash(DateTime.Now.Ticks.ToString()).Substring(0, 10);//random - return _hash(foo); - } - - protected static string _hash(string data) - { - return _hash(data, false); - } - - protected static string _hash(string data, bool raw) - { - byte[] bytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(data); - MD5 md5 = MD5.Create(); - md5.ComputeHash(bytes); - if (!raw) - { - return _hexEncode(md5.Hash); - } - else - { - return Encoding.GetEncoding("ISO-8859-1").GetString(md5.Hash); - } - } - - protected static string _hexEncode(byte[] data) - { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < data.Length; i++) - { - sb.Append(data[i].ToString("X2")); - } - return sb.ToString().ToLower(); - } - - protected void _setHeaders(string nonce, long contentlength) - { - this.request.Headers.Clear(); - this.request.UserAgent = WhatsAppApi.Settings.WhatsConstants.UserAgent; - this.request.Accept = "text/json"; - this.request.ContentType = "application/x-www-form-urlencoded"; - string foo = this._generateAuth(nonce); - this.request.Headers.Add("Authorization", foo); - this.request.Headers.Add("Accept-Encoding", "identity"); - this.request.ContentLength = contentlength; - } - - protected string _generateAuth(string nonce) - { - string cnonce = _getCnonce(); - string nc = "00000001"; - string digestUri = "WAWA/s.whatsapp.net"; - string credentials = this.username + ":s.whatsapp.net:"; - credentials += Encoding.GetEncoding("ISO-8859-1").GetString(Convert.FromBase64String(this.password)); - string response = _hash(_hash(_hash(credentials, true) + ":" + nonce + ":" + cnonce) + ":" + nonce + ":" + nc + ":" + cnonce + ":auth:" + _hash("AUTHENTICATE:" + digestUri)); - return "X-WAWA:username=\"" + this.username + "\",realm=\"s.whatsapp.net\",nonce=\"" + nonce + "\",cnonce=\"" + cnonce + "\",nc=\"" + nc + "\",qop=\"auth\",digest-uri=\"" + digestUri + "\",response=\"" + response + "\",charset=\"utf-8\""; - } - } - - public class ContactSyncResult - { - public string p { get; set; } - public string n { get; set; } - public string s { get; set; } - public long t { get; set; } - public int w { get; set; } - } - - public class ContactSyncResultContainer - { - public ContactSyncResult[] c { get; set; } - } -} \ No newline at end of file diff --git a/WhatsAppApi/Helper/DecodeHelper.cs b/WhatsAppApi/Helper/DecodeHelper.cs deleted file mode 100644 index 79d33a0..0000000 --- a/WhatsAppApi/Helper/DecodeHelper.cs +++ /dev/null @@ -1,303 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace WhatsAppApi.Helper -{ - public static class DecodeHelper - { - private static string[] dictList = null; - public static string decode(string hex) - { - string[] tmpSub = hex.SplitStringN(2); - var strBuilder = new StringBuilder(); - foreach (var s in tmpSub) - { - strBuilder.AppendFormat(" {0}", getToken(Int32.Parse(s, NumberStyles.HexNumber))); - } - return strBuilder.ToString(); - } - - public static string[] SplitStringN(this string value, int count) - { - var returnList = new List(); - for (int i = 0; i < value.Length; i += count) - { - returnList.Add(value.Substring(i, count)); - } - if (value.Length % count != 0) - { - int tmpRest = value.Length % count; - returnList.Add(value.Substring(value.Length - 1 - tmpRest, tmpRest)); - } - return returnList.ToArray(); - } - - public static string[] getDictionary() - { - if (dictList != null) - { - return dictList; - } - dictList = new string[249]; - dictList[0] = null; - dictList[1] = null; - dictList[2] = null; - dictList[3] = null; - dictList[4] = null; - dictList[5] = "account"; - dictList[6] = "ack"; - dictList[7] = "action"; - dictList[8] = "active"; - dictList[9] = "add"; - dictList[10] = "after"; - dictList[11] = "ib"; - dictList[12] = "all"; - dictList[13] = "allow"; - dictList[14] = "apple"; - dictList[15] = "audio"; - dictList[16] = "auth"; - dictList[17] = "author"; - dictList[18] = "available"; - dictList[19] = "bad-protocol"; - dictList[20] = "bad-request"; - dictList[21] = "before"; - dictList[22] = "Bell.caf"; - dictList[23] = "body"; - dictList[24] = "Boing.caf"; - dictList[25] = "cancel"; - dictList[26] = "category"; - dictList[27] = "challenge"; - dictList[28] = "chat"; - dictList[29] = "clean"; - dictList[30] = "code"; - dictList[31] = "composing"; - dictList[32] = "config"; - dictList[33] = "conflict"; - dictList[34] = "contacts"; - dictList[35] = "count"; - dictList[36] = "create"; - dictList[37] = "creation"; - dictList[38] = "default"; - dictList[39] = "delay"; - dictList[40] = "delete"; - dictList[41] = "delivered"; - dictList[42] = "deny"; - dictList[43] = "digest"; - dictList[44] = "DIGEST-MD5-1"; - dictList[45] = "DIGEST-MD5-2"; - dictList[46] = "dirty"; - dictList[47] = "elapsed"; - dictList[48] = "broadcast"; - dictList[49] = "enable"; - dictList[50] = "encoding"; - dictList[51] = "duplicate"; - dictList[52] = "error"; - dictList[53] = "event"; - dictList[54] = "expiration"; - dictList[55] = "expired"; - dictList[56] = "fail"; - dictList[57] = "failure"; - dictList[58] = "false"; - dictList[59] = "favorites"; - dictList[60] = "feature"; - dictList[61] = "features"; - dictList[62] = "field"; - dictList[63] = "first"; - dictList[64] = "free"; - dictList[65] = "from"; - dictList[66] = "g.us"; - dictList[67] = "get"; - dictList[68] = "Glass.caf"; - dictList[69] = "google"; - dictList[70] = "group"; - dictList[71] = "groups"; - dictList[72] = "g_notify"; - dictList[73] = "g_sound"; - dictList[74] = "Harp.caf"; - dictList[75] = "http://etherx.jabber.org/streams"; - dictList[76] = "http://jabber.org/protocol/chatstates"; - dictList[77] = "id"; - dictList[78] = "image"; - dictList[79] = "img"; - dictList[80] = "inactive"; - dictList[81] = "index"; - dictList[82] = "internal-server-error"; - dictList[83] = "invalid-mechanism"; - dictList[84] = "ip"; - dictList[85] = "iq"; - dictList[86] = "item"; - dictList[87] = "item-not-found"; - dictList[88] = "user-not-found"; - dictList[89] = "jabber:iq:last"; - dictList[90] = "jabber:iq:privacy"; - dictList[91] = "jabber:x:delay"; - dictList[92] = "jabber:x:event"; - dictList[93] = "jid"; - dictList[94] = "jid-malformed"; - dictList[95] = "kind"; - dictList[96] = "last"; - dictList[97] = "latitude"; - dictList[98] = "lc"; - dictList[99] = "leave"; - dictList[100] = "leave-all"; - dictList[101] = "lg"; - dictList[102] = "list"; - dictList[103] = "location"; - dictList[104] = "longitude"; - dictList[105] = "max"; - dictList[106] = "max_groups"; - dictList[107] = "max_participants"; - dictList[108] = "max_subject"; - dictList[109] = "mechanism"; - dictList[110] = "media"; - dictList[111] = "message"; - dictList[112] = "message_acks"; - dictList[113] = "method"; - dictList[114] = "microsoft"; - dictList[115] = "missing"; - dictList[116] = "modify"; - dictList[117] = "mute"; - dictList[118] = "name"; - dictList[119] = "nokia"; - dictList[120] = "none"; - dictList[121] = "not-acceptable"; - dictList[122] = "not-allowed"; - dictList[123] = "not-authorized"; - dictList[124] = "notification"; - dictList[125] = "notify"; - dictList[126] = "off"; - dictList[127] = "offline"; - dictList[128] = "order"; - dictList[129] = "owner"; - dictList[130] = "owning"; - dictList[131] = "paid"; - dictList[132] = "participant"; - dictList[133] = "participants"; - dictList[134] = "participating"; - dictList[135] = "password"; - dictList[136] = "paused"; - dictList[137] = "picture"; - dictList[138] = "pin"; - dictList[139] = "ping"; - dictList[140] = "platform"; - dictList[141] = "pop_mean_time"; - dictList[142] = "pop_plus_minus"; - dictList[143] = "port"; - dictList[144] = "presence"; - dictList[145] = "preview"; - dictList[146] = "probe"; - dictList[147] = "proceed"; - dictList[148] = "prop"; - dictList[149] = "props"; - dictList[150] = "p_o"; - dictList[151] = "p_t"; - dictList[152] = "query"; - dictList[153] = "raw"; - dictList[154] = "reason"; - dictList[155] = "receipt"; - dictList[156] = "receipt_acks"; - dictList[157] = "received"; - dictList[158] = "registration"; - dictList[159] = "relay"; - dictList[160] = "remote-server-timeout"; - dictList[161] = "remove"; - dictList[162] = "Replaced by new connection"; - dictList[163] = "request"; - dictList[164] = "required"; - dictList[165] = "resource"; - dictList[166] = "resource-constraint"; - dictList[167] = "response"; - dictList[168] = "result"; - dictList[169] = "retry"; - dictList[170] = "rim"; - dictList[171] = "s.whatsapp.net"; - dictList[172] = "s.us"; - dictList[173] = "seconds"; - dictList[174] = "server"; - dictList[175] = "server-error"; - dictList[176] = "service-unavailable"; - dictList[177] = "set"; - dictList[178] = "show"; - dictList[179] = "sid"; - dictList[180] = "silent"; - dictList[181] = "sound"; - dictList[182] = "stamp"; - dictList[183] = "unsubscribe"; - dictList[184] = "stat"; - dictList[185] = "status"; - dictList[186] = "stream:error"; - dictList[187] = "stream:features"; - dictList[188] = "subject"; - dictList[189] = "subscribe"; - dictList[190] = "success"; - dictList[191] = "sync"; - dictList[192] = "system-shutdown"; - dictList[193] = "s_o"; - dictList[194] = "s_t"; - dictList[195] = "t"; - dictList[196] = "text"; - dictList[197] = "timeout"; - dictList[198] = "TimePassing.caf"; - dictList[199] = "timestamp"; - dictList[200] = "to"; - dictList[201] = "Tri-tone.caf"; - dictList[202] = "true"; - dictList[203] = "type"; - dictList[204] = "unavailable"; - dictList[205] = "uri"; - dictList[206] = "url"; - dictList[207] = "urn:ietf:params:xml:ns:xmpp-sasl"; - dictList[208] = "urn:ietf:params:xml:ns:xmpp-stanzas"; - dictList[209] = "urn:ietf:params:xml:ns:xmpp-streams"; - dictList[210] = "urn:xmpp:delay"; - dictList[211] = "urn:xmpp:ping"; - dictList[212] = "urn:xmpp:receipts"; - dictList[213] = "urn:xmpp:whatsapp"; - dictList[214] = "urn:xmpp:whatsapp:account"; - dictList[215] = "urn:xmpp:whatsapp:dirty"; - dictList[216] = "urn:xmpp:whatsapp:mms"; - dictList[217] = "urn:xmpp:whatsapp:push"; - dictList[218] = "user"; - dictList[219] = "username"; - dictList[220] = "value"; - dictList[221] = "vcard"; - dictList[222] = "version"; - dictList[223] = "video"; - dictList[224] = "w"; - dictList[225] = "w:g"; - dictList[226] = "w:p"; - dictList[227] = "w:p:r"; - dictList[228] = "w:profile:picture"; - dictList[229] = "wait"; - dictList[230] = "x"; - dictList[231] = "xml-not-well-formed"; - dictList[232] = "xmlns"; - dictList[233] = "xmlns:stream"; - dictList[234] = "Xylophone.caf"; - dictList[235] = "1"; - dictList[236] = "WAUTH-1"; - dictList[237] = null; - dictList[238] = null; - dictList[239] = null; - dictList[240] = null; - dictList[241] = null; - dictList[242] = null; - dictList[243] = null; - dictList[244] = null; - dictList[245] = null; - dictList[246] = null; - dictList[247] = null; - dictList[248] = "XXX"; - return dictList; - } - - public static string getToken(int index) - { - string[] dicList = getDictionary(); - return dicList[index]; - } - } -} diff --git a/WhatsAppApi/Helper/Encryption.cs b/WhatsAppApi/Helper/Encryption.cs deleted file mode 100644 index 035ccc6..0000000 --- a/WhatsAppApi/Helper/Encryption.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; - -namespace WhatsAppApi.Helper -{ - public static class Encryption - { - public static RC4 encryptionOutgoing = null; - public static RC4 encryptionIncoming = null; - - public static byte[] WhatsappEncrypt(byte[] key, byte[] data, bool appendHash) - { - if(encryptionOutgoing == null) - encryptionOutgoing = new RC4(key, 256); - HMACSHA1 h = new HMACSHA1(key); - byte[] buff = new byte[data.Length]; - Buffer.BlockCopy(data, 0, buff, 0, data.Length); - - encryptionOutgoing.Cipher(buff); - byte[] hashByte = h.ComputeHash(buff); - byte[] response = new byte[4 + buff.Length]; - if (appendHash) - { - Buffer.BlockCopy(buff, 0, response, 0, buff.Length); - Buffer.BlockCopy(hashByte, 0, response, buff.Length, 4); - } - else - { - Buffer.BlockCopy(hashByte, 0, response, 0, 4); - Buffer.BlockCopy(buff, 0, response, 4, buff.Length); - } - - return response; - } - public static byte[] WhatsappDecrypt(byte[] key, byte[] data) - { - if (encryptionIncoming == null) - encryptionIncoming = new RC4(key, 256); - byte[] buff = new byte[data.Length]; - Buffer.BlockCopy(data, 0, buff, 0, data.Length); - encryptionIncoming.Cipher(buff); - return buff; - } - } -} From 33623b20bb43c40166ef117970964a133e2a7ff4 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 28 Jan 2014 11:31:38 +0100 Subject: [PATCH 155/271] Fixed text message handler Using new format --- WhatsAppApi/WhatsApp.cs | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 5d15169..909de84 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -858,27 +858,12 @@ protected void processInboundData() try { byte[] msgdata = this.whatsNetwork.ReadNextNode(); - //List foo = new List(); - //if (this._incompleteBytes.Count > 0) - //{ - // foreach (IncompleteMessageException e in this._incompleteBytes) - // { - // foo.AddRange(e.getInput()); - // } - // this._incompleteBytes.Clear(); - //} - //if (data != null) - //{ - // foo.AddRange(data); - //} ProtocolTreeNode node = this.reader.nextTree(msgdata); while (node != null) { - //this.WhatsParser.ParseProtocolNode(node); if (node.tag == "iq" && node.GetAttribute("type") == "error") { - //this.AddMessage(node); if (this.OnError != null) { this.OnError(node.GetAttribute("id"), node.GetAttribute("from"), Int32.Parse(node.GetChild("error").GetAttribute("code")), node.GetChild("error").GetAttribute("text")); @@ -910,9 +895,9 @@ protected void processInboundData() } if (ProtocolTreeNode.TagEquals(node, "message")) { - if (node.GetChild("notify") != null) + if (!string.IsNullOrEmpty(node.GetAttribute("notify"))) { - string name = node.GetChild("notify").GetAttribute("name"); + string name = node.GetAttribute("notify"); if (this.OnGetContactName != null) { this.OnGetContactName(node.GetAttribute("from"), name); @@ -923,7 +908,7 @@ protected void processInboundData() //text message if (this.OnGetMessage != null) { - this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), node.GetChild("notify").GetAttribute("name"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); + this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); } } if (node.GetChild("received") != null) From 179746c521316025e42752df80a01bfd05a8392d Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 28 Jan 2014 12:53:11 +0100 Subject: [PATCH 156/271] Implementing new WAUTH2 FunXMPP --- WhatsAppApi/WhatsApp.cs | 242 ++++++++++++++++++++-------------------- 1 file changed, 121 insertions(+), 121 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 909de84..2bb1c60 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -911,14 +911,6 @@ protected void processInboundData() this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); } } - if (node.GetChild("received") != null) - { - //client received - if (this.OnGetMessageReceivedClient != null) - { - this.OnGetMessageReceivedClient(node.GetAttribute("from"), node.GetAttribute("id")); - } - } if (node.GetChild("media") != null) { ProtocolTreeNode media = node.GetChild("media"); @@ -984,142 +976,94 @@ protected void processInboundData() break; } } - ProtocolTreeNode x = node.GetChild("x"); - if (x != null && x.GetAttribute("xmlns") == "jabber:x:event") - { - if (x.GetChild("server") != null && this.OnGetMessageReceivedServer != null) - { - this.OnGetMessageReceivedServer(node.GetAttribute("from"), node.GetAttribute("id")); - } - } - ProtocolTreeNode notification = node.GetChild("notification"); - if (notification != null) + } + if (ProtocolTreeNode.TagEquals(node, "iq")) + { + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.First(), "query") + && node.children.First().GetAttribute("xmlns") == "jabber:iq:last" + ) { - if (notification.GetAttribute("type") == "picture" && this.OnNotificationPicture != null && notification.GetChild("set") != null) + //last seen + DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.First().GetAttribute("seconds")) * -1); + if (this.OnGetLastSeen != null) { - this.OnNotificationPicture(notification.tag, notification.GetChild("set").GetAttribute("jid"), notification.GetChild("set").GetAttribute("id")); + this.OnGetLastSeen(node.GetAttribute("from"), lastSeen); } } - - if (node.GetChild("request") != null) - { - this.sendMessageReceived(node); - } - else if (node.GetChild("received") != null) + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && (ProtocolTreeNode.TagEquals(node.children.First(), "media") || ProtocolTreeNode.TagEquals(node.children.First(), "duplicate")) + ) { - this.sendMessageReceived(node, "ack"); + //media upload + this.uploadResponse = node; } - if (node.GetChild("composing") != null) + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.First(), "picture") + ) { - //typing - if (this.OnGetTyping != null) + //profile picture + string from = node.GetAttribute("from"); + string id = node.GetChild("picture").GetAttribute("id"); + byte[] dat = node.GetChild("picture").GetData(); + string type = node.GetChild("picture").GetAttribute("type"); + if (type == "preview") { - this.OnGetTyping(node.GetAttribute("from")); + if (this.OnGetPhotoPreview != null) + { + this.OnGetPhotoPreview(from, id, dat); + } } - } - if (node.GetChild("paused") != null) - { - //paused - if (this.OnGetPaused != null) + else { - this.OnGetPaused(node.GetAttribute("from")); + if (this.OnGetPhoto != null) + { + this.OnGetPhoto(from, id, dat); + } } } - } - if (ProtocolTreeNode.TagEquals(node, "stream:error")) - { - Console.Write(node.NodeString()); - } - if (ProtocolTreeNode.TagEquals(node, "iq") - && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "query") - && node.children.First().GetAttribute("xmlns") == "jabber:iq:last" - ) - { - //last seen - DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.First().GetAttribute("seconds")) * -1); - if (this.OnGetLastSeen != null) + if (node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.First(), "ping")) { - this.OnGetLastSeen(node.GetAttribute("from"), lastSeen); + this.Pong(node.GetAttribute("id")); } - } - if (ProtocolTreeNode.TagEquals(node, "iq") - && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && (ProtocolTreeNode.TagEquals(node.children.First(), "media") || ProtocolTreeNode.TagEquals(node.children.First(), "duplicate")) - ) - { - //media upload - this.uploadResponse = node; - } - if (ProtocolTreeNode.TagEquals(node, "iq") - && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "picture") - ) - { - //profile picture - string from = node.GetAttribute("from"); - string id = node.GetChild("picture").GetAttribute("id"); - byte[] dat = node.GetChild("picture").GetData(); - string type = node.GetChild("picture").GetAttribute("type"); - if (type == "preview") + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.First(), "group")) { - if (this.OnGetPhotoPreview != null) + //group(s) info + List groups = new List(); + foreach (ProtocolTreeNode group in node.children) { - this.OnGetPhotoPreview(from, id, dat); + groups.Add(new GroupInfo( + group.GetAttribute("id"), + group.GetAttribute("owner"), + long.Parse(group.GetAttribute("creation")), + group.GetAttribute("subject"), + long.Parse(group.GetAttribute("s_t")), + group.GetAttribute("s_o") + )); } - } - else - { - if (this.OnGetPhoto != null) + if (this.OnGetGroups != null) { - this.OnGetPhoto(from, id, dat); + this.OnGetGroups(groups.ToArray()); } } - } - if (ProtocolTreeNode.TagEquals(node, "iq") - && node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "ping")) - { - this.Pong(node.GetAttribute("id")); - } - if (ProtocolTreeNode.TagEquals(node, "iq") - && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "group")) - { - //group(s) info - List groups = new List(); - foreach (ProtocolTreeNode group in node.children) - { - groups.Add(new GroupInfo( - group.GetAttribute("id"), - group.GetAttribute("owner"), - long.Parse(group.GetAttribute("creation")), - group.GetAttribute("subject"), - long.Parse(group.GetAttribute("s_t")), - group.GetAttribute("s_o") - )); - } - if (this.OnGetGroups != null) - { - this.OnGetGroups(groups.ToArray()); - } - } - if (ProtocolTreeNode.TagEquals(node, "iq") - && node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "participant")) - { - //group participants - List participants = new List(); - foreach (ProtocolTreeNode part in node.GetAllChildren()) + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.First(), "participant")) { - if (part.tag == "participant" && !string.IsNullOrEmpty(part.GetAttribute("jid"))) + //group participants + List participants = new List(); + foreach (ProtocolTreeNode part in node.GetAllChildren()) { - participants.Add(part.GetAttribute("jid")); + if (part.tag == "participant" && !string.IsNullOrEmpty(part.GetAttribute("jid"))) + { + participants.Add(part.GetAttribute("jid")); + } + } + if (this.OnGetGroupParticipants != null) + { + this.OnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); } - } - if (this.OnGetGroupParticipants != null) - { - this.OnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); } } @@ -1146,6 +1090,62 @@ protected void processInboundData() this.OnGetPresence(node.GetAttribute("from"), node.GetAttribute("type")); } } + + if (node.tag == "ib") + { + throw new NotImplementedException(node.NodeString()); + } + + if (node.tag == "chatstate") + { + string state = node.children.FirstOrDefault().tag; + switch (state) + { + case "composing": + if (this.OnGetTyping != null) + { + this.OnGetTyping(node.GetAttribute("from")); + } + break; + case "paused": + if (this.OnGetPaused != null) + { + this.OnGetPaused(node.GetAttribute("from")); + } + break; + default: + throw new NotImplementedException(node.NodeString()); + } + } + + if (node.tag == "ack") + { + throw new NotImplementedException(node.NodeString()); + } + + if (node.tag == "notification") + { + if(!String.IsNullOrEmpty(node.GetAttribute("notify"))) + { + if(this.OnGetContactName != null) + { + this.OnGetContactName(node.GetAttribute("from"), node.GetAttribute("notify")); + } + } + string type = node.GetAttribute("type"); + switch(type) + { + case "picture": + ProtocolTreeNode child = node.children.FirstOrDefault(); + if (this.OnNotificationPicture != null) + { + this.OnNotificationPicture(child.tag, child.GetAttribute("jid"), child.GetAttribute("id")); + } + break; + default: + throw new NotImplementedException(node.NodeString()); + } + } node = this.reader.nextTree(); } } From b736b96d51688c5364abac114e794bc5514074e7 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Tue, 28 Jan 2014 23:54:42 +0100 Subject: [PATCH 157/271] Bedtime checkin --- WhatsAppApi/WhatsApp.cs | 95 +++++++++++++++++++++++++++++---- WhatsAppApi/WhatsSendHandler.cs | 24 +++++---- 2 files changed, 99 insertions(+), 20 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 2bb1c60..5d76c5b 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -890,7 +890,7 @@ protected void processInboundData() this.loginStatus = CONNECTION_STATUS.UNAUTHORIZED; if (this.OnLoginFailed != null) { - this.OnLoginFailed(node.children.First().tag); + this.OnLoginFailed(node.children.FirstOrDefault().tag); } } if (ProtocolTreeNode.TagEquals(node, "message")) @@ -903,6 +903,10 @@ protected void processInboundData() this.OnGetContactName(node.GetAttribute("from"), name); } } + if (node.GetAttribute("type") == "error") + { + throw new NotImplementedException(node.NodeString()); + } if (node.GetChild("body") != null) { //text message @@ -910,6 +914,7 @@ protected void processInboundData() { this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); } + this.sendMessageReceived(node); } if (node.GetChild("media") != null) { @@ -975,31 +980,32 @@ protected void processInboundData() } break; } + this.sendMessageReceived(node); } } if (ProtocolTreeNode.TagEquals(node, "iq")) { if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "query") - && node.children.First().GetAttribute("xmlns") == "jabber:iq:last" + && node.children.FirstOrDefault().tag == "query" + && node.children.FirstOrDefault().GetAttribute("xmlns") == "jabber:iq:last" ) { //last seen - DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.First().GetAttribute("seconds")) * -1); + DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.FirstOrDefault().GetAttribute("seconds")) * -1); if (this.OnGetLastSeen != null) { this.OnGetLastSeen(node.GetAttribute("from"), lastSeen); } } if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && (ProtocolTreeNode.TagEquals(node.children.First(), "media") || ProtocolTreeNode.TagEquals(node.children.First(), "duplicate")) + && (ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "media") || ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "duplicate")) ) { //media upload this.uploadResponse = node; } if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "picture") + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "picture") ) { //profile picture @@ -1023,12 +1029,12 @@ protected void processInboundData() } } if (node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "ping")) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "ping")) { this.Pong(node.GetAttribute("id")); } if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "group")) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "group")) { //group(s) info List groups = new List(); @@ -1049,7 +1055,7 @@ protected void processInboundData() } } if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.First(), "participant")) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "participant")) { //group participants List participants = new List(); @@ -1093,7 +1099,20 @@ protected void processInboundData() if (node.tag == "ib") { - throw new NotImplementedException(node.NodeString()); + foreach (ProtocolTreeNode child in node.children) + { + switch (child.tag) + { + case "dirty": + this.WhatsSendHandler.SendClearDirty(child.GetAttribute("type")); + break; + case "offline": + //this.SendQrSync(null); + break; + default: + throw new NotImplementedException(node.NodeString()); + } + } } if (node.tag == "chatstate") @@ -1120,7 +1139,8 @@ protected void processInboundData() if (node.tag == "ack") { - throw new NotImplementedException(node.NodeString()); + //do nothing + //throw new NotImplementedException(node.NodeString()); } if (node.tag == "notification") @@ -1142,9 +1162,17 @@ protected void processInboundData() this.OnNotificationPicture(child.tag, child.GetAttribute("jid"), child.GetAttribute("id")); } break; + case "status": + ProtocolTreeNode child2 = node.children.FirstOrDefault(); + if (this.OnGetStatus != null) + { + this.OnGetStatus(node.GetAttribute("from"), child2.tag, node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(child2.GetData())); + } + break; default: throw new NotImplementedException(node.NodeString()); } + this.SendNotificationAck(node); } node = this.reader.nextTree(); } @@ -1156,6 +1184,49 @@ protected void processInboundData() } } + private void SendNotificationAck(ProtocolTreeNode node) + { + string from = node.GetAttribute("from"); + string to = node.GetAttribute("to"); + string participant = node.GetAttribute("participant"); + string id = node.GetAttribute("id"); + string type = node.GetAttribute("type"); + List attributes = new List(); + if(!string.IsNullOrEmpty(to)) + { + attributes.Add(new KeyValue("from", to)); + } + if(!string.IsNullOrEmpty(participant)) + { + attributes.Add(new KeyValue("participant", participant)); + } + attributes.AddRange(new [] { + new KeyValue("to", from), + new KeyValue("class", "notification"), + new KeyValue("id", id), + new KeyValue("type", type) + }); + ProtocolTreeNode sendNode = new ProtocolTreeNode("ack", attributes.ToArray()); + this.WhatsSendHandler.SendNode(sendNode); + } + + protected void SendQrSync(byte[] qrkey, byte[] token = null) + { + string id = TicketCounter.MakeId("qrsync_"); + List children = new List(); + children.Add(new ProtocolTreeNode("sync", null, qrkey)); + if (token != null) + { + children.Add(new ProtocolTreeNode("code", null, token)); + } + ProtocolTreeNode node = new ProtocolTreeNode("iq", new[] { + new KeyValue("type", "set"), + new KeyValue("id", id), + new KeyValue("xmlns", "w:web") + }, children.ToArray()); + this.WhatsSendHandler.SendNode(node); + } + /// /// Tell the server we recieved the message /// @@ -1223,6 +1294,7 @@ private void PrintInfo(string p) public event OnGetPictureDelegate OnGetPhotoPreview; public event OnGetGroupsDelegate OnGetGroups; public event OnContactNameDelegate OnGetContactName; + public event OnGetStatusDelegate OnGetStatus; //event delegates public delegate void OnContactNameDelegate(string from, string contactName); @@ -1243,6 +1315,7 @@ private void PrintInfo(string p) public delegate void OnGetVcardDelegate(string from, string id, string name, byte[] data); public delegate void OnGetPictureDelegate(string from, string id, byte[] data); public delegate void OnGetGroupsDelegate(GroupInfo[] groups); + public delegate void OnGetStatusDelegate(string from, string type, string name, string status); public class GroupInfo { diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 415ba1d..444c9c6 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -79,7 +79,7 @@ public void SendAddParticipants(string gjid, IEnumerable participants, A public void SendAvailableForChat(string nickName, bool isHidden = false) { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName), new KeyValue("type", isHidden?"inactive":"active") }); + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName) }); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } @@ -92,15 +92,21 @@ public void SendUnavailable() public void SendClearDirty(IEnumerable categoryNames) { string id = TicketCounter.MakeId("clean_dirty_"); - IEnumerable source = from category in categoryNames select new ProtocolTreeNode("category", new[] { new KeyValue("name", category) }); - var child = new ProtocolTreeNode("clean", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") }, source); + List children = new List(); + foreach (string category in categoryNames) + { + ProtocolTreeNode cat = new ProtocolTreeNode("clean", new[] { new KeyValue("type", category) }); + children.Add(cat); + } var node = new ProtocolTreeNode("iq", new[] { - new KeyValue("id", id), new KeyValue("type", "set"), - new KeyValue("to", "s.whatsapp.net") - }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); + new KeyValue("id", id), + new KeyValue("type", "set"), + new KeyValue("to", "s.whatsapp.net"), + new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") + }, children); + this.SendNode(node); } public void SendClearDirty(string category) @@ -630,8 +636,8 @@ public void SendPresenceSubscriptionRequest(string to) public void SendQueryLastOnline(string jid) { string id = TicketCounter.MakeId("last_"); - var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "jabber:iq:last") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid) }, child); + var child = new ProtocolTreeNode("query", null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid), new KeyValue("xmlns", "jabber:iq:last") }, child); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } From 3606cf69c7ffebcb517c602027b68d9d99b7433c Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Wed, 29 Jan 2014 09:39:36 +0100 Subject: [PATCH 158/271] Fixed sending text messages --- WhatsAppApi/WhatsSendHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 444c9c6..1225519 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -976,7 +976,7 @@ internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNo { return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.identifier_key.remote_jid), - new KeyValue("type", "chat"), + new KeyValue("type", "text"), new KeyValue("id", message.identifier_key.id) }, new ProtocolTreeNode[] { From 031cbbdc6f5028ca55f0b64b6d88710720c8c542 Mon Sep 17 00:00:00 2001 From: Max Kovaljov Date: Thu, 13 Feb 2014 11:09:53 +0100 Subject: [PATCH 159/271] Working sync and event handler --- WhatsAppApi/WhatsApp.cs | 29 ++++++++++++++++++++++++++++- WhatsTest/Program.cs | 24 +++++++++++++++++++----- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 5d76c5b..12605db 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -796,7 +796,6 @@ protected ProtocolTreeNode addAuthResponse() if (this._challengeBytes != null) { - byte[][] keys = KeyStream.GenerateKeys(this.encryptPassword(), this._challengeBytes); this.reader.Key = new KeyStream(keys[2], keys[3]); @@ -985,6 +984,31 @@ protected void processInboundData() } if (ProtocolTreeNode.TagEquals(node, "iq")) { + if (node.GetChild("sync") != null) + { + //sync result + ProtocolTreeNode sync = node.GetChild("sync"); + ProtocolTreeNode existing = sync.GetChild("in"); + ProtocolTreeNode nonexisting = sync.GetChild("out"); + //process existing first + Dictionary existingUsers = new Dictionary(); + foreach (ProtocolTreeNode child in existing.GetAllChildren()) + { + existingUsers.Add(System.Text.Encoding.UTF8.GetString(child.GetData()), child.GetAttribute("jid")); + } + //now process failed numbers + List failedNumbers = new List(); + foreach (ProtocolTreeNode child in nonexisting.GetAllChildren()) + { + failedNumbers.Add(System.Text.Encoding.UTF8.GetString(child.GetData())); + } + int index = 0; + Int32.TryParse(sync.GetAttribute("index"), out index); + if (this.OnGetSyncResult != null) + { + this.OnGetSyncResult(index, sync.GetAttribute("sid"), existingUsers, failedNumbers.ToArray()); + } + } if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) && node.children.FirstOrDefault().tag == "query" && node.children.FirstOrDefault().GetAttribute("xmlns") == "jabber:iq:last" @@ -1296,6 +1320,8 @@ private void PrintInfo(string p) public event OnContactNameDelegate OnGetContactName; public event OnGetStatusDelegate OnGetStatus; + public event OnGetSyncResultDelegate OnGetSyncResult; + //event delegates public delegate void OnContactNameDelegate(string from, string contactName); public delegate void NullDelegate(); @@ -1316,6 +1342,7 @@ private void PrintInfo(string p) public delegate void OnGetPictureDelegate(string from, string id, byte[] data); public delegate void OnGetGroupsDelegate(GroupInfo[] groups); public delegate void OnGetStatusDelegate(string from, string type, string name, string status); + public delegate void OnGetSyncResultDelegate(int index, string sid, Dictionary existingUsers, string[] failedNumbers); public class GroupInfo { diff --git a/WhatsTest/Program.cs b/WhatsTest/Program.cs index ba65f48..5138b5c 100644 --- a/WhatsTest/Program.cs +++ b/WhatsTest/Program.cs @@ -24,9 +24,9 @@ private static void Main(string[] args) System.Console.OutputEncoding = Encoding.Default; System.Console.InputEncoding = Encoding.Default; string nickname = "WhatsApiNet"; - string sender = "316********"; // Mobile number with country code (but without + or 00) - string password = "K/1**************yeix87Q=";//v2 password - string target = "316********";// Mobile number to send the message to + string sender = "316******3"; // Mobile number with country code (but without + or 00) + string password = "xLl***************GSA=";//v2 password + string target = "316********6";// Mobile number to send the message to WhatsApp wa = new WhatsApp(sender, password, nickname, true); @@ -50,6 +50,7 @@ private static void Main(string[] args) wa.OnGetPhoto += wa_OnGetPhoto; wa.OnGetPhotoPreview += wa_OnGetPhotoPreview; wa.OnGetGroups += wa_OnGetGroups; + wa.OnGetSyncResult += wa_OnGetSyncResult; wa.Connect(); wa.Login(); @@ -58,6 +59,19 @@ private static void Main(string[] args) Console.ReadKey(); } + static void wa_OnGetSyncResult(int index, string sid, Dictionary existingUsers, string[] failedNumbers) + { + Console.WriteLine("Sync result for {0}:", sid); + foreach (KeyValuePair item in existingUsers) + { + Console.WriteLine("Existing: {0} (username {1})", item.Key, item.Value); + } + foreach(string item in failedNumbers) + { + Console.WriteLine("Non-Existing: {0}", item); + } + } + static void wa_OnGetGroups(WhatsApp.GroupInfo[] groups) { Console.WriteLine("Got groups:"); @@ -166,7 +180,7 @@ static void wa_OnGetPresence(string from, string type) static void wa_OnNotificationPicture(string type, string jid, string id) { //TODO - throw new NotImplementedException(); + //throw new NotImplementedException(); } static void wa_OnGetMessage(string from, string id, string name, string message) @@ -182,7 +196,7 @@ private static void wa_OnLoginFailed(string data) private static void wa_OnLoginSuccess(byte[] data) { Console.WriteLine("Login success. Next password:"); - Console.WriteLine(data); + Console.WriteLine(System.Text.Encoding.UTF8.GetString(data)); } From bf099c8470f3a6aa68a7a52036ece705b057e47c Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 7 Mar 2014 00:44:08 +0100 Subject: [PATCH 160/271] Fixed message receipts --- WhatsAppApi/WhatsSendHandler.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index 1225519..e0ca611 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -562,8 +562,11 @@ public void SendMessageBroadcast(string[] to, FMessage message) /// An instance of the FMessage class. public void SendMessageReceived(FMessage message, string response) { - var child = new ProtocolTreeNode(response, new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); - var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", message.identifier_key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.identifier_key.id) }, child); + ProtocolTreeNode node = new ProtocolTreeNode("receipt", new[] { + new KeyValue("to", message.identifier_key.remote_jid), + new KeyValue("id", message.identifier_key.id) + }); + this.whatsNetwork.SendData(this.BinWriter.Write(node)); } From a6f9928f2440b4342aaad7877a4bde3fedced5b6 Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 7 Mar 2014 00:53:54 +0100 Subject: [PATCH 161/271] Fixed chat states (typing/paused) --- WhatsAppApi/WhatsSendHandler.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index e0ca611..fe48494 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -185,8 +185,14 @@ public void SendClose() /// The recipient, the one the client is talking to. public void SendComposing(string to) { - var child = new ProtocolTreeNode("composing", new[] { new KeyValue("xmlns", "http://jabber.org/protocol/chatstates") }); - var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "chat") }, new ProtocolTreeNode[] {child}); + this.SendChatState(to, "composing"); + } + + protected void SendChatState(string to, string type) + { + var node = new ProtocolTreeNode("chatstate", new[] { new KeyValue("to", WhatsApp.GetJID(to)) }, new [] { + new ProtocolTreeNode(type, null) + }); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } @@ -596,9 +602,7 @@ public void SendNotificationReceived(string jid, string id) /// The jabber id of the reciever public void SendPaused(string to) { - var child = new ProtocolTreeNode("paused", new[] { new KeyValue("xmlns", "http://jabber.org/protocol/chatstates") }); - var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "chat") }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); + this.SendChatState(to, "paused"); } /// From 2798aa6a9e849cb6f6c4ea8dbacea819d4450901 Mon Sep 17 00:00:00 2001 From: shirioko Date: Sun, 16 Mar 2014 14:45:36 +0100 Subject: [PATCH 162/271] Added option to not auto receipt messages --- WhatsAppApi/WhatsApp.cs | 15 +++++++++------ WhatsTest/Program.cs | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 12605db..0ea03a9 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -716,9 +716,9 @@ public UploadResponse(ProtocolTreeNode node) /// /// Retrieve messages from the server /// - public void PollMessages() + public void PollMessages(bool autoReceipt = false) { - this.processInboundData(); + this.processInboundData(autoReceipt); } /// @@ -852,7 +852,7 @@ protected void processChallenge(ProtocolTreeNode node) /// Process inbound data /// /// Data to process - protected void processInboundData() + protected void processInboundData(bool autoReceipt = false) { try { @@ -911,9 +911,12 @@ protected void processInboundData() //text message if (this.OnGetMessage != null) { - this.OnGetMessage(node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData())); + this.OnGetMessage(node, node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData()), autoReceipt); + } + if (autoReceipt) + { + this.sendMessageReceived(node); } - this.sendMessageReceived(node); } if (node.GetChild("media") != null) { @@ -1331,7 +1334,7 @@ private void PrintInfo(string p) public delegate void OnErrorDelegate(string id, string from, int code, string text); public delegate void OnGetMessageReceivedDelegate(string from, string id); public delegate void OnNotificationPictureDelegate(string type, string jid, string id); - public delegate void OnGetMessageDelegate(string from, string id, string name, string message); + public delegate void OnGetMessageDelegate(ProtocolTreeNode messageNode, string from, string id, string name, string message, bool receipt_sent); public delegate void OnGetPresenceDelegate(string from, string type); public delegate void OnGetGroupParticipantsDelegate(string gjid, string[] jids); public delegate void OnGetLastSeenDelegate(string from, DateTime lastSeen); diff --git a/WhatsTest/Program.cs b/WhatsTest/Program.cs index 5138b5c..0ef7344 100644 --- a/WhatsTest/Program.cs +++ b/WhatsTest/Program.cs @@ -183,7 +183,7 @@ static void wa_OnNotificationPicture(string type, string jid, string id) //throw new NotImplementedException(); } - static void wa_OnGetMessage(string from, string id, string name, string message) + static void wa_OnGetMessage(ProtocolTreeNode node, string from, string id, string name, string message, bool receipt_sent) { Console.WriteLine("Message from {0} (1): {2}", name, from, message); } From a6092d62127db7df4a0bc891e9a9e66f3c38bafd Mon Sep 17 00:00:00 2001 From: shirioko Date: Sun, 16 Mar 2014 14:47:24 +0100 Subject: [PATCH 163/271] Changed default auto receipt to true whoops --- WhatsAppApi/WhatsApp.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 0ea03a9..b5784a2 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -716,7 +716,7 @@ public UploadResponse(ProtocolTreeNode node) /// /// Retrieve messages from the server /// - public void PollMessages(bool autoReceipt = false) + public void PollMessages(bool autoReceipt = true) { this.processInboundData(autoReceipt); } @@ -852,7 +852,7 @@ protected void processChallenge(ProtocolTreeNode node) /// Process inbound data /// /// Data to process - protected void processInboundData(bool autoReceipt = false) + protected void processInboundData(bool autoReceipt = true) { try { From 29f52979f2848468ed6d912a7e297d9c32bb6921 Mon Sep 17 00:00:00 2001 From: shirioko Date: Sun, 16 Mar 2014 14:49:10 +0100 Subject: [PATCH 164/271] Changed sendMessageReceived to public in addition to previous commit so you can manually receipt messages --- WhatsAppApi/WhatsApp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index b5784a2..21be60f 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -1258,7 +1258,7 @@ protected void SendQrSync(byte[] qrkey, byte[] token = null) /// Tell the server we recieved the message /// /// The ProtocolTreeNode that contains the message - protected void sendMessageReceived(ProtocolTreeNode msg, string response = "received") + public void sendMessageReceived(ProtocolTreeNode msg, string response = "received") { FMessage tmpMessage = new FMessage(new FMessage.FMessageIdentifierKey(msg.GetAttribute("from"), true, msg.GetAttribute("id"))); this.WhatsParser.WhatsSendHandler.SendMessageReceived(tmpMessage, response); From 201c6c4dcbc3af98917f0069d64dfe95f98972b7 Mon Sep 17 00:00:00 2001 From: shirioko Date: Sun, 16 Mar 2014 17:17:51 +0100 Subject: [PATCH 165/271] Fixed media upload & sending Fixes #100 --- WhatsAppApi/WhatsApp.cs | 4 ++-- WhatsAppApi/WhatsSendHandler.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 21be60f..9a37277 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -550,7 +550,6 @@ private byte[] CreateThumbnail(string path) private UploadResponse UploadFile(string b64hash, string type, long size, string path, string to, string contenttype) { ProtocolTreeNode media = new ProtocolTreeNode("media", new KeyValue[] { - new KeyValue("xmlns", "w:m"), new KeyValue("hash", b64hash), new KeyValue("type", type), new KeyValue("size", size.ToString()) @@ -559,7 +558,8 @@ private UploadResponse UploadFile(string b64hash, string type, long size, string ProtocolTreeNode node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("to", WhatsConstants.WhatsAppServer), - new KeyValue("type", "set") + new KeyValue("type", "set"), + new KeyValue("xmlns", "w:m") }, media); this.uploadResponse = null; this.WhatsSendHandler.SendNode(node); diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index fe48494..b3162b0 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -983,7 +983,7 @@ internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNo { return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.identifier_key.remote_jid), - new KeyValue("type", "text"), + new KeyValue("type", message.media_wa_type == FMessage.Type.Undefined?"text":"media"), new KeyValue("id", message.identifier_key.id) }, new ProtocolTreeNode[] { From 5320da2cfa260815c9d49a38d0c8b6316696a3a5 Mon Sep 17 00:00:00 2001 From: shirioko Date: Sat, 22 Mar 2014 21:09:34 +0100 Subject: [PATCH 166/271] Res bump to 2.11.200 --- WhatsAppApi/Register/WaToken.cs | 2 +- WhatsAppApi/Settings/WhatsConstants.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs index 94c93f2..a8d671b 100644 --- a/WhatsAppApi/Register/WaToken.cs +++ b/WhatsAppApi/Register/WaToken.cs @@ -10,7 +10,7 @@ class WaToken { //private static string WaPrefix = "Y29tLndoYXRzYXBw"; private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; - private static string WaClassesMd5 = "94bjoO7brhy/QJZRceJHYw=="; + private static string WaClassesMd5 = "8PZch1s/VRHIKujxCxOXig=="; //private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; public static string GenerateToken(string number) diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index f226002..297b5ea 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ public class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.151"; + public const string WhatsAppVer = "2.11.200"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ public class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.151 Android/4.2.1 Device/GalaxyS3"; + public const string UserAgent = "WhatsApp/2.11.200 Android/4.4.2 Device/GalaxyS4"; #endregion From 50ca40962a826e363851c6ee85b13344628978f8 Mon Sep 17 00:00:00 2001 From: shirioko Date: Sun, 23 Mar 2014 21:54:58 +0100 Subject: [PATCH 167/271] Fixed group functions Fixes #99 --- WhatsAppApi/WhatsSendHandler.cs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index b3162b0..bd919be 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -214,8 +214,8 @@ public void SendCreateGroupChat(string subject) public void SendCreateGroupChat(string subject, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("create_group_"); - var child = new ProtocolTreeNode("group", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("action", "create"), new KeyValue("subject", subject) }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "g.us") }, new ProtocolTreeNode[] {child}); + var child = new ProtocolTreeNode("group", new[] { new KeyValue("action", "create"), new KeyValue("subject", subject) }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", "g.us") }, new ProtocolTreeNode[] { child }); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } @@ -285,8 +285,8 @@ public void SendEndGroupChat(string gjid) public void SendEndGroupChat(string gjid, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("remove_group_"); - var child = new ProtocolTreeNode("group", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("action", "delete") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] {child}); + var child = new ProtocolTreeNode("group", new[] { new KeyValue("action", "delete") }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] { child }); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } @@ -319,8 +319,8 @@ public void SendGetDirty() public void SendGetGroupInfo(string gjid) { string id = TicketCounter.MakeId("get_g_info_"); - var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "w:g") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(gjid)) }, new ProtocolTreeNode[] {child}); + var child = new ProtocolTreeNode("query", null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(gjid)) }, new ProtocolTreeNode[] { child }); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } @@ -351,8 +351,8 @@ public void SendGetOwningGroups() public void SendGetParticipants(string gjid) { string id = TicketCounter.MakeId("get_participants_"); - var child = new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:g") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", WhatsApp.GetJID(gjid)) }, child); + var child = new ProtocolTreeNode("list", null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g"), new KeyValue("to", WhatsApp.GetJID(gjid)) }, child); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } @@ -483,8 +483,8 @@ public void SendLeaveGroups(IEnumerable gjids, Action onSuccess, Action< { string id = TicketCounter.MakeId("leave_group_"); IEnumerable innerChilds = from gjid in gjids select new ProtocolTreeNode("group", new[] { new KeyValue("id", gjid) }); - var child = new ProtocolTreeNode("leave", new KeyValue[] { new KeyValue("xmlns", "w:g") }, innerChilds); - var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", "g.us") }, child); + var child = new ProtocolTreeNode("leave", null, innerChilds); + var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", "g.us") }, child); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } @@ -728,8 +728,8 @@ public void SendSetGroupSubject(string gjid, string subject) public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("set_group_subject_"); - var child = new ProtocolTreeNode("subject", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("value", subject) }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, child); + var child = new ProtocolTreeNode("subject", new[] { new KeyValue("value", subject) }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", gjid) }, child); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } @@ -841,8 +841,8 @@ public void SendVisibleReceiptAck(string to, string id) /// The type internal void SendGetGroups(string id, string type) { - var child = new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:g"), new KeyValue("type", type) }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "g.us") }, child); + var child = new ProtocolTreeNode("list", new[] { new KeyValue("type", type) }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g"), new KeyValue("to", "g.us") }, child); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } @@ -930,8 +930,8 @@ public void SendNode(ProtocolTreeNode node) internal void SendVerbParticipants(string gjid, IEnumerable participants, string id, string inner_tag) { IEnumerable source = from jid in participants select new ProtocolTreeNode("participant", new[] { new KeyValue("jid", jid) }); - var child = new ProtocolTreeNode(inner_tag, new[] { new KeyValue("xmlns", "w:g") }, source); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", gjid) }, child); + var child = new ProtocolTreeNode(inner_tag, null, source); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", gjid) }, child); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } From c2ba63763c47fa933b39ae44248805dea0f3aeda Mon Sep 17 00:00:00 2001 From: shirioko Date: Sun, 23 Mar 2014 22:38:05 +0100 Subject: [PATCH 168/271] Fixed setting profile picture --- WhatsAppApi/WhatsSendHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index bd919be..e033950 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -744,12 +744,12 @@ public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, A public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes) { string id = TicketCounter.MakeId("set_photo_"); - var list = new List { new ProtocolTreeNode("picture", new[] { new KeyValue("xmlns", "w:profile:picture") }, null, bytes) }; + var list = new List { new ProtocolTreeNode("picture", null, null, bytes) }; if (thumbnailBytes != null) { list.Add(new ProtocolTreeNode("picture", new[] { new KeyValue("type", "preview") }, null, thumbnailBytes)); } - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("to", jid) }, list.ToArray()); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:profile:picture"), new KeyValue("to", jid) }, list.ToArray()); this.whatsNetwork.SendData(this.BinWriter.Write(node)); } From 24ab9263dde0d660e4cd24768fab4ff75e626f71 Mon Sep 17 00:00:00 2001 From: shirioko Date: Tue, 25 Mar 2014 22:06:43 +0100 Subject: [PATCH 169/271] fixed setting profile picture --- WhatsAppApi/WhatsSendHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs index e033950..cd6a945 100644 --- a/WhatsAppApi/WhatsSendHandler.cs +++ b/WhatsAppApi/WhatsSendHandler.cs @@ -376,7 +376,7 @@ public string SendGetPhoto(string jid, bool largeFormat) public string SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, Action onComplete) { string id = TicketCounter.MakeId("get_photo_"); - var attrList = new List { new KeyValue("xmlns", "w:profile:picture") }; + var attrList = new List(); if (!largeFormat) { attrList.Add(new KeyValue("type", "preview")); @@ -386,7 +386,7 @@ public string SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, attrList.Add(new KeyValue("id", expectedPhotoId)); } var child = new ProtocolTreeNode("picture", attrList.ToArray()); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(jid)) }, child); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:profile:picture"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(jid)) }, child); this.whatsNetwork.SendData(this.BinWriter.Write(node)); return id; } From 0620c240e6c2a44798a68dfe756c32245099a000 Mon Sep 17 00:00:00 2001 From: shirioko Date: Tue, 8 Apr 2014 09:16:32 +0200 Subject: [PATCH 170/271] Updated registration constants to 2.11.209 --- WhatsAppApi/Register/WaToken.cs | 11 +---------- WhatsAppApi/Settings/WhatsConstants.cs | 4 ++-- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs index a8d671b..56607f4 100644 --- a/WhatsAppApi/Register/WaToken.cs +++ b/WhatsAppApi/Register/WaToken.cs @@ -10,17 +10,10 @@ class WaToken { //private static string WaPrefix = "Y29tLndoYXRzYXBw"; private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; - private static string WaClassesMd5 = "8PZch1s/VRHIKujxCxOXig=="; - //private static string WaKey = "PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"; + private static string WaClassesMd5 = "+XW/7rCZDX9T7YrGQqTmcg=="; public static string GenerateToken(string number) { - //List key = new List(Convert.FromBase64String(WaToken.WaPrefix)); - //key.AddRange(Convert.FromBase64String(WaToken.DataFile)); - - //Rfc2898DeriveBytes r = new Rfc2898DeriveBytes(key.ToArray(), Convert.FromBase64String(WaToken.WaKey), 128); - //key = new List(r.GetBytes(80)); - List key = new List(Convert.FromBase64String("/UIGKU1FVQa+ATM2A0za7G2KI9S/CwPYjgAbc67v7ep42eO/WeTLx1lb1cHwxpsEgF4+PmYpLd2YpGUdX/A2JQitsHzDwgcdBpUf7psX1BU=")); List data = new List(Convert.FromBase64String(WaToken.WaSignature)); @@ -54,7 +47,5 @@ private static List GetFilledList(byte item, int length) } return result; } - - //private static string DataFile = "iVBORw0KGgoAAAANSUhEUgAAAIYAAACPCAYAAAA/dqNZAAAgAElEQVR42u19d7xcVbX/d+99zpRbk3uTkEISUgkJEFoSQTpSpArSgwiW8MDCe/iej99DURHEivpQUFBBEQQpkWCkCYTeEkogPaT3cvu9M3POLr8/djlnTmbmzk0Coo/JZz5zM+XMmbO/e63v+q611ybYxZtSCh/d/nlvhJCSz3v/hy4AtX+WedwB86UelVLy/8L18v7FBp8kBtzeWeL/1Nzj70uCwgJCxP6vCCEq+VwMNOojYHx4gIAEAKj524sBwD7vxV5PPiaBIUs8cvMoYo/ubwOafwmgeP+kQEBsUFns0StxZwD8Sy+9dI8DPz5p9D6Txx9W31A/MpPONkMpogAMaBw0glHGACAX9oTtXa0bKagAIao739HV2d69ePk7q158avZzy2bOnNliAFLqLuLgiQEF/2xAIR928lkGDDQ+6ObuAUgBSF155ZUjTjr72GOH7jn46IH9B+89uGHPfoRQsjvOp8DzclvnxvZ121e+t3rdqhdn3fHkQ3f/4d5NAAIAYeJugRK3PB8qgJQjnx9aYJQABIvdUwYMaQDpww47rP83v/ffp40at9enh/Tfc0xDtin9QV7crZ0b8mu2r1ixbMmyB6+44Gv3t7a29gDIJ8DCY27nQ2NF/mmAkQBE3Dr41iIAyOy///79rvvRN0+ZuN+Ei/YaNH6sz1L0H2DNdniuJ+gSSzbOX7pk4dI/XnDypQ8ZgBQMSIKYJZHm/g8FSDlgYHcAY3fcS0QRPoAsgAYAAwDsCWD8d7/73VPfXfXGC4Uwz1XiJqX8h96Tt65Ce/jcwr89e8El5xwLYAyAYQCaAdQDyMTcofvtu+t69uG6fzgtRgkLQWIWImPuNXfe/dtjjjju0P8Yvcc+4/8ZBLb4zwpFQS1YN2/RXx9+9MZvXnn9qwB6YpaEJ3jIB2pBPnSupAwgWAIQtfc8eNdJxxx79DWD++05MGml/llUxfhPXbTxzQ0P/PnBq6/99xteTgAkTOgjHwhAPlSupITbSAGoAdAfwFAA466//vozV25eujzpKoQQ/5T3uKsRkqvX33v27U9NP+U4AKMBDAbQaK6B/0G6l/fttguAoDHr0ABgEIC9Tj/99I+/s2LeHCH5+waIvnKH9wsgPUGnfPzNB2ePGjVqMoARAAYa/pFKiHXvG0D+4RyjhEpJYyFnFkD9vQ/98ZRTPnnaDXWZhow9tpRyt5jy3WUyd8dss+djz2lj+5qe3/3ptv/4xuU3PA2gK8E/3tfo5R/GMSqolTbqqD300EMH3X7PLT+etNcBR+zqxU9e9ORtddtSbOvehJbcZrTktqCjsG2H90gAw+vHIMXSGFS7J5prBmNY46jdDpT4uXIZ4rkFj//97KMuvqq1tbUtxj+CmPax2wWyfwgwyoDCM1YiA6Dhl7fdfNj06Rfe3FjTVA8AUso+X+RyYFjVshgLt87Fou1vYFXHQmzLrQdlBJQSgACUEkjD8+xHlQKI0iNAAAih/6O4wpC60dirYQImDpyKSYOmYHD9iN0CEkqpO/cVWxe13HLLLTN+8u1fvGHA0WPA8b6IYx84MMool3HX0fjYs498/rjDT7rKox6xbqMPrmkHQAQijzfXv4Dn1zyCRS2vIo8eUEYAqkAo0X8TDQIJwGMUUikNAmIcOiFQUoErgEJBxodCKEipoAQguEK914wDBx2Jjw07HgcOO2IHgPTFDRJCHEC6gw75h1m3f/uKc/7zPgAdxnLkY65lt4HjAwVGgk8gltDKAqjp379//xfmPvPDiaMnn2itRLUXMX4B7e3N9c/jqZUPYv62FyBIHtSnIB4BYwSEEQBKPyqlgRG3FCCa9BACqRQoIVBKQjoyRMww6M9zLgEJCKlABKAkEAYCDawJUwYfj0+MOQejmvbZaYAwxpxr+durD9x7xmEXXmfA0R0La3cbOD4wYJRJhTs+MW3atCH3z/rT74YPGrUPAAghqr5wjDFQqpXvgOfx3KrZ+OuyO7GlsArUI6A+AaEK1GPOShCiQAgDpcZNEYASCgWlgWL8iJT6b0aLXyOEagtjXlcKEEKCgkApBSIJOJdQXEIIQBQkJvafitP3/lyRFZFSQgjR59/56vJn3v7k1E9/prW1tdW4lVws74Jd5R0fCDAqgKIGQN306dNH3/TLH90zqHHIIKUUhBBVuY74heIyxN8W/xEzl/wG3WgD9QHqEzCPgnkUgAJjDCAKhBAIpd0ToxRKKVBCwaW+pjTmhoSU7m9lLIc0n9MDq0Cpti9CaatBKdGfUwRSKAguoSQgCgqSSwxJj8YF+16Jj408HvEoq5qJQCnVvwPA/HWvrjv98PPOWL169TYTtew2cLzvwCgDipQNRT/3uc+N/fHPf3hP/7rmZqUUOOfVRDRFoHhlzZO4a/6PsCVYB5YiSKWYi/YtqVRKDxiXEj6j2nsQaC6hLBjMzDdVepQQBwQhFXxGwaV0z3MpoRTgUeqOFb8JoUBAwCggORxIZKggAom9Gw/BZw/4b4xpnlRkPar5/b7vAwDeXvvq+jOOOO/01atXbwfQaThHuKsRy/sKjDIahYs8LrzwwtG33Hbznxtrm/pLKcE5r8pK2Bmzvn0lfvXatVjSPg9IKaTSDNTT/EFCuSjDDrwdZEI0F/CMtZDQAyiVZhCM6M8zY0Uooe5zjFBjbQBmLIN9nVHNO4TS48EI1YkOJR0nsdYDAuCBhAwUPj74NFx6yNVozDTBWszerEccHO+ue33DKR8/+/Q1a9ZsjekduwSO9w0Y2LHGktrUOICGk046aeRd9/3+vgENgwZJKRGGYa8n6nmesxKzFt6BPy34OZAOAQ9IpakmkgQaFIRAKD3LrbmPWTEIqaDM4GlyqU+VSwGPsuL3GiBocAFSwRFToTSvgBsBpYGlAEoQc0PWOinNQ4TSFiTQ91r0wxcP+jYOHXkCLMfqbaJYcBBC8M661zYcud+JJ7e1tbUYQppLRCt9Asf7BYxyamYGQP3w4cMHvfTGc/fsOWCvsdZS9KJ7uAuwvXszbn7parzT+jL8LAPzCRRT8DwTK1A9CMpYLWbMvD6+nuEurI05YssfhCGTHtOvcqFi/p2Y90jHTaz7sP9XMTdq3ZOQCh6jEFJbFS6ktjxSQnJNVEUgEeYkjhn+acyY+k2kvAz6em1eXvb0gsPGH3c+gDYDjnyMc/QpWnk/gJEMSW11VRZAHYD+C957+6cTR+9/rFIKQRBU/OGUUqRSKQDAwk1z8aMXrkQHtiOVpaA+hSISlOnBpzSKIrTp1/8nRJt92IjB/GiZEJ0oIdr0m4GUSjlrE3OPBnARiCx/IUb30O/Rf3MhdyCzjBqdRGogUQWtgQQSvCCxZ2Ycrjn6NgysGwprTXsDRzqti9Nmvnj3zLMOv+gaAO3GrRSSItiuAIPuJmXVytxpE4HUz3n5qRkTR+9/LIBeQcEYc6D4+9IH8a05n0U72Qo/SyGZgiQSIPpiS6UQhMJYBO0alHERBEDIJUKuJ47WEWSRGqnHXplBjyutClwIcCFAYpYl/qiUcs4k5BJBKFAszCmEXB/DRTqGAzFKoAhATKUJSROszS/D1x79NJZseRuUUqTT6Yo5HaWUc8VnHHb+mf977/c+a5JuNcZ9x5NuSZGxzwO6u11I7Z13/e6EI6cdfYUFhb1wpe6UUkeu7pp3E26eezWQ5kjVUEgqQZgeTAVDJoV0A8uF1EIT4lK0BomQeqBDLt1AKxOZhDw6HxELHz1KAcMRCAAuhDtO/P0hlw6QUkpwIQ0Qou9H4ndKKSGVBjYY4Kcp/CxBN92Oa566AHOWzwIAZxHKXS/OOTjnoITh82d+5etfu+FLhxsLnYllZXcZHGQ3gCKuVTSee+65Y+74w2//UpOuy3DOK5LNuKW4/ZUbMHvlnfCzFPAAP0VdxEFiIabTE8zg29mc5ABxF0MpiXneyBUR8zSNXTt7HG0dCBSUi0zi5xF3N5rrRN9vXZfHKLjQ+kjRd0gdFlMFiFCBFyRUgeIrU7+P48adCQDI5/MVrWwqlQJjDCu3Le4+ZN9Dj23Z3LbFKKQ5E6lUtcZld3GMUqu84jUVTeu3rrln6IDhE4QQKBQKFUFhZ8ftr9yA2SvuAM0QeCkKL60JG6WRyGQHQhaZdn0GJOb/7eDagSfO+Bcfx83KBDC0+TffY0honEPY5Jr9Lks0LQeJHxcKYIwUP2+eE8KIaEJCCaDQI0BChi8fciOOG38WlFK9giOTyYBSikffemDeyQeecymAlnLSeV+BQXeRfHoxEavu2RfnXDJ0wPAJUkoEQVCRaFpQ/O6VG/Hw0t8CvjavhAE8FA7rRAFSSAiu75JLKKnDEWoSXzAzGFILS0QBSioQJ1/r4wguNWmVCkJE7wtDYXQHBSX0a5DKHFuBhzpUJQCkOYYU+nmioCVxoWV0KSSkUO48BZc2htXnKfXnlFQOpJQCXoqC+BI/e+nreHrpw45oVvIE1hqfOPnMg6/79dfPNC4ljajImO6sS6G7aC1sCr3u8ssvH/exqR+7wp5wOV5hzSAA/HXBH/HA4tvAMgQ0RSCJ1hyUObrlECBa2bSm3/p6LqTjCwRacOJSk0+rYhIChJaLmJxHKDTvCEWUzZWGs1Cmk29wPEWaY0mjiURKqn3efj7k0d/xejUhtbhmP2M/J5VCKAQEFIinQNMEJKvw05f+E+9ufN1FalXxjXO/dM3B0w4cAqDWjElyvW6fwEH6CAySqKuwoemA1RtX3Dli8Kj9OOcVXUgmkwFjDG+tewnXPvVZsKwEUgR+yugDJhy1vjzOLYixizaEtOacIJK8rX5AiZbILQgYM9zDhKjWDUilhSrKdL7DuSqgiE/E3Q1lOnFmNQvtGvVzzB5bRLkX62q4kHGqA59Rd37WIhXyArIA1Kp++MkpD2FYv71Q7TV9ev5fXz1u8mkzAGxP6BuuAj3pUnbVlZSqrbBupO6WX/3ysBGDR+2nlEKhUCiLcEuY1retwo1zroDyOWiawPNt5jMyxy78CyW4Nd8uJOQgRCLkIbjgCMIAQgoIqWeQVBIgykUkhMAdg5iwNzQmntrZHkrHAaRGgxPMGNUk1KqoypJeEw3ZCERbJGmsJWKRi7YQSil4RgeBMi7Lkl2irR1hBH6GoIe04bonL0N3oROe58HzvLLX1brtw/Y5duqFl39635jV8HY2hCU74UJojFc0NjQ0DFy+ZsnMgY2DBwdBUJZbMMaQzWbBRYivPnQGVucWwq+hoB4B84gxsXp2qVjxTCKQh4JEKDkUOPJhAOppXqAIwBSDz1JgngcGBkgCSpkLIw1NdaQynmCzaqi2PlbAgiOLMFYLJCKr1lrYz+jUvtVKtGVRUhNf7XGUu5zUuUX9vBXaCIjmU4FC2C1x7IhP47+OuwlKKfT09JQlo5lMBp7n4fX3nlszdexRZxoi2lFKMo9bjd1BPuO8wlZi1fz5oT+dPbBx8GAbhVSyFgBw99z/xaquBYAPUA+abBo/bQUnQvUgOF5CFBQEcmEB3WEXungrtvRsxuaeVqxra8Oq9nZs7GzHllwLtuc3oyPXjp5CHlJJkxyzOQ89q6HsYBkyqrSZjxRNBUrh9A/OpXEDRssQsuj8CAUYg4t/iNFdhJCag7h8CqCM5bDWghCrmejvtzUjnk/A0gRPrXoAzy6b7chouetrr/2UMUeO+M4vrj7JTNxUwmqgWqtB+mgt4qn0xrFjxw6bN//12Q3ZfrX5fL6sZpHJZOD7PhZtehP//sinQLMKqRrqTtfNSKtTxGVvAkgpIMHRE3ajLd+JtgKQ9Ufg8CEnRdkBAnAvwBtbHgOXm9CUrkGtX48US4FSBlt7k8xx6PwGcZoIoQDncZU0mueRJYnlX+yxGDFlgMpwEH1cLqwrwg4SO6NRKGs5kp2tQmoJvdAlUKsacfs5T6OpdhAKhUJZq5xOp5FKpbB00zttew/Z/wQA2xLaxg5WY1dbLcXbD1jpu/bW2355ekO2Xy3nvKIL8X0foQjxg6evBE1J+BnqLryQCorqEjlCo2HgXIJQAkgJQTh6wk60Bt3Y1J3CN6b8BJcfeDkYZTt8X47ncMIDR6AlN08X2SgKj9oiPrjsqx5ILYsLIQGrjQiA2LI/qvUGJU29h+EMQihHVj2PmPBV12V4nv6MPS4hhqgad2m5B+dGoRWWm2hXpBR09lgoCCiwNEFndxt+8szXccOpd8L3/bJENJ/Pw/M8jB+8X7/v3vo/n/zm5d97MAaKolVuhBBSKZdC++BCaMxiZOrr6+sOPPjAS+LxdDkUA8DD8+/E+q73AGaJmjanSipIrqezCCM9gBJAhByFMEAu342Wrm6s2sZw61H34ssHfxmMMheuxe9ZL4sfHnYzNncBXbkuBGEBRClzXICH2rSLUGsiwhBUrTMY0mktBNfnpgtwpNEktFWQXOdBlOElwvyfm+NKW10uJBiF0UIiHcO+136eUV1gLIVCGNg8kPZ7fprg1XVP4O11L4NSikwmU/Z62xT+SSecNMOo0VlEq9uKiOjO6hjlVE5tLX59y1HNDQObrLUo5fcsm+7It+GPb/wULEXAfK1KcRFFCdaPIxZBcCEgIRDKPFpznVjfCdx85B04c58zoZRCLpdDoVDY4S6lxKEjDsXeDYejvQAEIgcuOWCASM1M1lK5dl2cS/dc/G894/VzUd1nPP+h3Puiuo74QinluIiUOvKx77XkNh4xaWIc8RQCBVBAEm05fvnCtZBKIJVKgdr0fwmuAQAHjTps2CVXXjB5Z7kG7aO18Iz8nT3qE0d8Po7Qcno+APz2pe+jS7YZzOqkV8T67QDAhaiapClwGaIz345NXcDJe34GnznwM04qLlf5ZAtuL9/3crTkgbzIIeAhBBc7CG1RgW+UoLOXyr5mz9MOaBhKk/qPQlGrs8QHXYjovRr0ygGFECAMI+Akz4FSAxalwMw1ox6won0BHpl/V5ElTt5s+p4Shku+ePG/m9DVtlwg1VoN2keVMwUgc9VVV43fc+DI0UIIp+eXsxabO9bhb0vvBvUB4umwMhSa5VtVkdBIbOJCQSgJrjhyPIfWPJCiI3DzSTcjnq3tTSY+e7+zQQoD0doD5HkekmhxySqpVjlVRpFUMETXKKZWCbX/t6/Zf1zY8q5iRdPeCY3UUftZxBRdyoAglO499p9WWxUo0+caCglFAQ4F4gN/mPszhCJwRTulrn0+nwcATBt/5MQx40c1GSufLmU1ygGkWlcSJ53Z8y8+58z47KxkLWa+9TsoGoJQw/JNPoIROD6hpNKLe4QOTYiU4GGIfNCDLZ3AjVNvRGO20fGI3mpQhRBIeSnMmDQDnQGQD7sRBsZqCOXyKO5v6AJemytRNpRU2t8TWE4BxwEkl+ChBA+iXIj+bMQdqHm/CHXuRUn9t87bKMc5lMmvhIHUfEhG10IKZRZCaRBuz23E00sermg1bLlgxq8h//2jr16U4BlkVywGSoDCtjnKjh0z7lgAZXULG4nkgm48suguEKYLZUOhVcz4e4UhakopKJs/kAL5MI+2HqDZH4XzDjrPWYtqbtZqzJgyA609DF0FgCOAlAKEGPNN9KxUsXoOYfhFlI/Rr0ljHaSQWq8gAGXanVCmn7cr3BBTb7nhKJTpiEvaWpKYHgLzPYIb/iP1dbDXiRoLQ6iCIgosRXDfvFt1SFmFGjp50oEnGFeSLsE1+sQxyrkRD0D229/+9oT+dc0NlYpYo0jkD+iR7VAUbiWYDhW1D2ZM6wY6ra2chqGUQIHn0J4HLpxwoYtAqi1+FkJACIERzSNw0sBT0NIN5Ao9kBDafZiZKEwmlfNIqKKmMMhGKyImxSulP0eIfrQZVqeNEH1Mp22YaCQMpSOqxPAHWBdqOIUl4MyAyErmwuZSzLkRBixvfRdvrnmpqHSh3OSYPHLaoP2mTBicAEUSGKQvHCNuLawbyZxy1idPsV9cDq227P/hd+7QJfvU+HDzbdywcyGlGSCTSgcQcoGACwQS6AqAMyecid5IbkUSOuVytBSA7lAi4AGEKfuzg20SFy5JZ8+NMaJV2Jh2Ef+7mMiacJsXRyzCzHrrLuPlgUmibdVTHmoXpSwncfUdmp9JSFAPmPn2HToR5/slx0AIgTAMkfYy+Oo3LzuvXIXXznKMeMsCH0Bm/JjxR1cy6zZR9s6617G27T1QRpxJJiRKNlFm6iOsSTUmFlAQPERBAIKnsN+w/dAbnykXzyulcNJ+J2Gv1Fh0hYBQBSgqXAjpaiWItlRhKFzuxIKDEMNDiJHmzYDKBDCcdO9kcquIRi4mCnGl+7yNZIThLO7ix1wPoaYmhCqAKiiq8OLKR1Hgefi+75ZaJG92jA6YeMjhMVeS1DSqciWkTIrdB5A555xzhjbU9GuIVzQn77Z+84lFD4Cm9Epz5muTyQMZsXhZPFuEIX+AhFAchRDYu3FvpLzUTjVPsbMGAC6ffDlau4HOvM7GSiWLQlYpTF2F1SqMVQhDU3QjJIRQ4KGJHKxlgHaL8cyvK9ARBuQisjD29yqlBTMexPQRq2vQmBVC3EJFSyMoA/Iyj+eWPlrRalhgjBsycYixGD5KdArsC8cgJSxGesaMGVN6UzotMJ5/79Gi2USZIe9GTRShcsRPcgVKjbmVCkoJ8BAY6A0sykf09WbP85KPXQLOs+gpaMJMlIxFJIYscuUiBxuhWPcA6AiGUn3elOjoQ4YSIrRJHuUGXV9EcxwTefGCdMezkruygpnUx5LC1H8aNVirrcpUmptzIdrieL7C04tnFkWApTQNIQQaa5ro5//rwgNL8IyyjfZplbmRFIDUiDHDDo4vByjFLSilWL55IbZ0r3PCvA7FIq5BaGQ1tB5gqpqEqfxWEkICTbVNuwQMe2Ga6ppw3vjzkOOAQAgBWaQzcMsZrKugMV5EtEVTUOBcmUotYyGoFp/ccbhVQpUr+IX9zZ4h2LHvgxHCQPTr9n3CElXznOARKAjRXEMx4KVVTzpNo1xluZ0chx0z9TjjTqqSx6uxGPZAqYEDBu1biQjaE3x5xd+1mfWjAbeMHdAm2fls6+9DG7LqiyAl0NXdVbFmoFquAQBHjTgKPDSmOZROsrZ5G5hIQ5kUrOUhgCoKse3sBSLLEhFJBcr0Iw/1+3hgiah5nRoLZdyP4AZIIjoeMYAU5jpJw194aKrLmdZBQuSxeONbLllZ6feP22ufiQmOUbUrIWVELQYgNXny5Ib+9c394767HDDeXPMyCIt8JqFGCzDhHmXRyi47w5STjAEoAgpga27rLgPD83QCeeHWhaZSzNZKWCBoi+ZMu5Gw49K2dXEaQHCf1QQxGnibBLPcQ7shWwNiBtbMfBvJxCcDnMZiCnhIFA0Jc93s8UG0xXpr7ctF176cOx07ZJ8hMVfiJywG2RmL4QHwzz///L3ibL+cDA4A765/zcm7hCh3gewPsuRNGmnYFtBEC4YIfB9Y375+l4Dh+z4YY1izbQ1ufeNWE6sxKEkiMQpRaGn/b0Pn+OImO2h6gyMNdhuOapDYgiLlCKsLXa0ARs0xYu7GWaKYNiJMbsXyHAseHmrAAQrM049vr3u5IgG14zWk3/DU+PHj62OgYCXAQaoBRtEeIJMnTx5RKWy0/GJ962q0FrbZKjq4RX0KkELflYrfoySUfiSg1AMDsCm3Ccs3LnftlfpU/h5bC3vFfVeAsi7UZwBG9MzSg2CkabP2Q3AU1Vro/iqk6HwF188JHhXnKAXjpuDqOBBr9mY79ph+LYgHWVoVJYlrYrlH8r3ReZnCM8xf93qRZayk6Zx20fH7xiITWilkpRWsRXwrCG/IsD2G9wYMAFixZTEUUU7CtegnFK5kT2sVkRpoZwShFpEUKQaQFPDy8pcr+tByi2isInjr32/F7A2z0T8FZDwGosxxSPHywfj/7bmpWGGy4CanY/QXq1paa0JZtARSmGjCiWdWbZUqCkfN9/GwOIXvziNmTXgozer+2Hukzri2BVuxpWMDKKVlJ48ds7ETR0+KhatsZ1xJPJzxAPi1jTUVXYkDxtZF8IyZs6YQ0IkiZS5Qkc82ZM/WQ+rKbQpPAY1ZYObbM/sMjHQ6DUopnnzrSXzl0a9gWD+gXw1ApQ/G9AJjySPRzRYKEWPfKIU7N/sbbB2okppASrPIyH7GiqiSS0cwRRj9XilMCt0qocLWeypA6c9QahKMxr3aUNeG1ErEioOIzqkoqbB2+wpnJcupoAAwYEDzsBKLkUilcJWUuTt3Uldbt4cNAStZjA1tqyDtanLPluvBJKFUkZJoo4B4XypCAcUJfALU+sBjax9DS2dLUcul3kDBGMM7772Dc35/DmobBeookCI+fM/TXXFkzIIJFZuJcDPU1UgYMEiuYoU60Wv2M05JJcpVfkf1FspFQzyMjim4ihU+w1kyYRZJa15iC5cj/uJCa6OHrG19r6I7scBo7jdwQIwesErupFodg2UzNfXVAGNj+1ojGev3Wt1fyqjKmrDii+7YvJ1lABj1kPWAvJ/DXc/fVZF5J7kFAJz1/bPQTtvRmAFqswQe8w0XMEsdTZUWjDyvYjK5W1oIfT62mt0ulVQlVrKLUBoRz2Zp48JelA6wbtQOtr2e9ti2OMguybRRjXNPNLpuhAJgChvb1hb99lJ6DgDU1zY0llE+eyWfpSwGBUAzqWzWoq+UubKRQ1ehAy6vZAqXdDho2iOZ3piAXnMBs2IM0Cu/CSUgioASBh9AUxa46eWbIKQoasFUCRhCCGzq3gSkACoBCAolCWSoiR6hxBFPpQAeqNgaD2MFpD7XOAEkNCKbrkFKqFwDFQ3womUw7rcmSbcl51LoY0hheo0Kaz3M6rVQFX2fkjCqcXR9W7u3VXQlFhjZVE0mMeFL8QtSTVTi9hHJpLKpSiqkA0bQ7tZV2H9azDFX15BSu/LMru+woayUEsQDmMeQYgQ1FFjTswa3PXFbRfk3bjYZY7jyhCuBPBAoIGESOLsAACAASURBVBQCUglnqaSQALOiVmy9iLUGUKA+nFYglbZ8Qkp3DJhw1SqbYPp32bDWkUXovhhW1YzXjFrtBiy6DoQZ16GkU2J1ZjVyIYTBfJ8ene6gshBogVGbqUvWY9DeOEYlnsGGDRuW6k2atieVy+dMOb4tcDE/SEQ9LJT1l7GBUNIMgoze7/s+6jJAvwbgG09/A125rqJufpUEnWunX4uJtRPRkge6C0DIA3ApnBRNSMQvbMKKelGfC8H1wh+b/3BhtQESDyINw/YblzyWfTWRjrWYmqfoYxZpI0JBhua3i6jmQ1nBjequf3bpv3IdAW0qQSLguar0HkqZhx03KK6qHiOJHgqANDY2+tWEiADQE3TpAxiTTRAtznGJJutarMux5ldG7J6AgEJbjf41QAtacMNfbuiVa9gCopSfwh2fuQO8haE9AApS91ZUMCAVbjGnW1Wk15TEdiUySTBKAOaRqIW0hGvowjwCZRcoUQLFjS5humHpyMWQSAFA2I7F5miMgHr6uzTXiI2UjAZDCUBxQIUq6mkq9fN5A4xKmWbtSmrjibM+50pQiZT0tiFKKAqAMaN2OR6IyaqKKJWsZJTSLqpjYDGJWih4xEOWAc01wE0v3oSFaxZWrFyyJYdSSkzdeyqumnIVtncCrd1ArhCAB6E279yk2Q3hi1TMWEitopnsrAtRRX23BNfk1PW7sOWCJeidK/gNJIStFVUxK2FCUmXWrYBEx4qTdpunsZYn5IEDcxUb1pTb5rwq5bPoQ0qpXjXpGCp18asBhF3D6XwjNb7WNCqzFx8Ezo/akjYQAiUJPEbQkAFIU4Dpv5qOIAzgeV5Fl2JrEa6/4HocnDkYG7uBTg4ECHX45plqbmaEKqiiTKnlBNKcu4oV2oBat6cjKB6YSEcWV51bbmCjFs0fAOLbrGlUsGOvRcTJlLt20nALIaTLuEoDSsKAFMtWlYUOeaASHBJ94Rg7/L1o0aJCbz7MnlSt3+jYtuTRuk638lvqtso2aiGmLYCSKDLJxHRJYYzCUzp0bc4AbxXewrfu/VavRDTuUh7+ysMY2D0QG7cDHd0AVyGgpKmQ0ufHPGP+jXvTopf5zdbF2Wgl1LyAmC6xxDR2iX9OBirmJrVbtRdWcRV1DLLXz7gYQLsm+/spJe77KDUNZUNVtPdz1q9FNYEBlyEvMfl3abW74kIftBw47Eml/YyzDCAoivsJi4lA8VQ3oppG12iNmJkJgHoUmQxDQwZoagR+9OqP8MKCF4paNpVzKUIIDBs4DA/PeBhoS2FrHugsAPkgAPFM3alZNuhqJ5g998jaFcnRcWtnxGVFojUkNnqQKkqsgZp0v2kOo8w6EWWvQay3BpitJZGuLoMaK6MSllYKhbpMAyppTHbMQh7wCh6iz+tKFAD0FLrzlUQUC4y6dEM086zfMyRJck3itHXQiKdMP6/s4l6uHPFS9mcITUR9BTT6AK0VuPjOi9GV64LneRXJqFu2OOlQ3D39bvC2FLZ0AF0FINcTQnItMFBKIAJlyKRJbhmyqYQhfzKapTrKkgjzIUIeIJfLg8sAIQ+glDDdXrV1setmi0glIYDQrxOi54HtQ05I7D3m/ZI7YdRZXP0eoDZVV9Fi2DHr7OkoVAOK3jgG4qujc/merkrAsLLrHv2G6ZAsJhtTEznHJWQ9A+BkYeprcYelSNHKLaXMDBG6ZXJNBtijH7BSrcRlv7nMuZRKgLXrX84+4mzcffbdyLcwbGgHuhVQEBwSuqEsSxGTuVTu0crSdpYST1tDzjlCGSBPBDoCiU4OtAUKXVyiKx8iVAE4DwEvxqlMOwTixSQmk+mFU4P1daA+idRNEqUUrHZhj6kADO43vGLBtL02XfnOrtiYVlyNVq0rkR2dHduqAkb9MN1PihBQRgFlZqAJXSGJ7lOltF+nhABKq52MUUDq9+n3mO41AvB8CqIo0sxDLQMG9QPuWXIPfjH7Fy5HUkngsUspzz7ybNx7wb3gbQzrtwPdAihwDlBbXwkwj5ombRTM0xyDKL0nilLQtZ5EoDsHbO8A1mwBVrcCqzcDa7YB2wtAWxdQgAAXAQTnmnxS03dcEffIPL1bE1H2mtnrYHZfItpiMWZ2dnIdfow9kwQjmsZUBYy29tZWJDb83RWOoQDI9jZdNVMuErAntVfzOFOmB5caJnZhka2otpXhXM8My8Li6WcptfXQPlZL0foiMtRkGRpTQOMA4GtPfg0vL3y5V76RBMdTlz2F5nAw1rQD27uBnoDr1WoQJp2ur47g+vtt6CilgmAcXXlgcxcAPhCvXDEX6gcKcz43B9PHXoLWrQxr2oB1rcC2LqA7FAh4gJCHEFKYSMWEnMZaggLUI665i20DpK8hiarEbS9bFbWWHDVwfMW1N3bMtm3btilyhpB9BYZKggKA3LaxdW2lDJ5VHCcMnayRLQGY/IT2tdpPU0Lg+VTnLWDyF8SKRlRHIT4tsh6KR/7eYww+81GXpWjOAH5jgDNuPwMrN67sVd+w4JBS4qgDjsLc/5mLKamp2NQCbO0COnsU8mEIZXuMKGupiGu4ont2KLQVgKAnhYcueAgHjzpY15XuexTumHEHVl+7GldOuBKqtQ7rtwGbuoC2HJAXAlwF4CF37hNGuCLKXAPbrUfErKwZBWoLlni0N5yPNIb04kosMNauWr8Wie0rSox5rxZDxYGxePHirdUUnY4ZtA+yXh2UBJhP9Y4B0BeWUArmU0jjWigjoJ5+nZg2iDasdeEcMeYWUQgLUPgshfoaYFAd0JbdijNuPgPt3e3wPK8qcAghMGzAMDx/zfOYMWEGtm8DNnYBbQWgJ+QIRQFCiWh1vMmvFIRAaw/Q2gLceOyNOHzS4ZBSoqenpygK+tnnfobV31uN6w+/Hl5LE9a1ARvage1d2joFIg/BuV6+SSOirkNgrYZSj5pQl4IwairDtItWUruT8XtMAqWsqiUdz/zlxYWIdihAJZdCSxFNFPehVgDEfffdt7E3OToIAlDKsM+QA822D2YBsDWVxpVQjwBmnxBqqgLifSkIMysvGAHxiBOGrCnVK8QoUiyN+gwwuAF4l7yDs/73LCd+VQKHbRVgdY5fX/ZrPPG5JzBMjsX6NmBDN9BSUMiJAIHMI+QBJBHIhQHaA2BzO3DmqDNx1alXucjH1lfm83kHkKaGJlxz9jVY/YPV+MmRP4HX2oR1HcDGbqC1AOR4CC4LTjAjjLiCZGKKiaivFxiBmNctATXq7EEjjkCltT62squ70CmfeXpOWwlXovrqSuyHBQDxyiuvdHTlOrorlZDZk9t36CEg0EQJJlyldn8y08fQ7u2hZCxMMwTLCjs2bJNc+18rFClhfjBhSLEUGmuA4f2Ap7uexvm3nl8VOOyA2nD2+AOPx4LvLMB3pnwH4fYs1rQDG9uB9oJCR8jRXgjRkgM2dAL71e2HP1z2BzcZkvqBBYi1THU1dbjqjKuw+oer8ZMjfoJ0biDWtAAtBaCnIMFF6ABBGQVLUZdrgq2aNyTUCWVEZ5MOGXUkKi0Zta5/1eblrTFrwcsYgl5dSSlw8K3bt66w4WFyQ1xCiAPGISOPAARAJAEFMYNIQEFNwzTrLvRrzKPmx9OYIBNFKsyjpmcnMTsZGRavKBjxkPXS6JcBRvQDZm6eiem3TK8aHJxz5HI5Zz2uPedarLt+Ha7Z9xqQlias2gasbtHRxsatwODcYDz6pUdRV1OH3nZXsI1lcrmcBki2DledehVWf3c1bj72ZgS5JnSEQCjN3miCFCutMQGawLhURQCuuYfkCgeOPMxNylJjYtXhNRtXrTGASG6TparhGKqUG7HAWLl09VuV3IlF7dTRR6HWyLRW7o0n4LyUtgbMNxIvN2XxRmiC1EIY80yHPetqYtqIffQ9BkYZsqkIHA9sfQDTb6seHNZ62BneVN+E6y+4HqtvXI2ffPwnGBmORbARGNwxGA/PeBjDBg5Db7srlOI1FiDZdBZfPuHLePXLr6Kth6EnBBQRYD5xvxOmi6FLZJiqLeYTl6mdMOhANNb0d73bK/GLd+cvmI/i7bFUCSOgKnEMlSCeCrodIH/88cdXVcpR2IW0aT+Lj+91CqTZrp55VMfihkQq8xw15lI3kDd6BiFgPnUVULpk3swYn7r3Wa1BCoARBgoPGT+NfmlgRCPw4IYHcP7t5yNXyMHzPGQymV7rFewMdy4gW4erTrsKy364DMu+swzLfr4MUydNhZSyalCUA4hSCmMHjUWjPxBcmqWOwuRIzN1eH6t1MI+6a6E4cOKks9HbWuJ0Og0pBe782Z/mIWrr2GeLgTIWQwIIf/7zn6/PBz35Skvvbf+nI8efpGNvI4W7pBAjiO/G6fnUcQdCozoDGDdioxcLFgsey03s677nwWcpZNMZ9EsDezUDj2yYiSN+egTWbFkDxpjb36O3W9wFcM4hpcTYEWNRV1vn+MOubKken92BDKBopIzaJKKL2kxizVoSalysksCJkz5ddM2TNzuB39u0uH3hwoVdMYshExMfvXGMJLdQMbISFgqFwtqNaxcBcDOwHM84Zp/TUOc1QildjGLvUuowjJrwSwgd1hLrL0j0XmULaUxNJWXRe/S+qzqMU0oX8VPKwKiPmmwN+mU1IV0q5mHaL6fhtWWvuR6Z1RQV2wEsFArI5XLI5XIuJN0VUDjiSAi68l1oybWYOlgdadnrRT0KtziB6IVYNlxVgmDy0MMwuN9whGEIznnJsbD9QBe+9+4iYy3CClajTxbDEU9zwOClOa+8ZE1Uudlm3cmJE891yTIlifnxxFUpWZeiC2GjXQl1+jGSffUsjyqmLFm1aXCb8iaEgBEGj/pI01r0r2EYXAPk05tw9O+Oxn2v3OcIWTWuJQmSXQVEMtu5cO1CgAHpNOD7nnOP1t1S52a15ZScQISACBROnnR+r27EToAH7pr5DHbc9UiWoQ8qDoxy/ELGwBECCK677rplIQ9EpcSVNW1nHHCxzgRKvciHmc1oKI1jVMHzzb4eHjFqI9FrUlxjdrMw2KiR8a+1JlZ/NtqXzGMesqksGjM+9mwE+jXncP6s8zH9tulunUo2m63aeuzOmw0hX171MmpqAJ9Ah/c2OxAbFdv4XudQtGvulx6AUyaf36sb8TwPW9o3FP54+32rYm5ExAio7KuOkQSHtRrBihUrelatWzm/kjspFArgnGOfoQdgyvCjdHm8Ka235pB5Wvq2oan1o65EX5KYldAhq5JR6b8GgplRNHofMRyFUQpKNTgaslkMrgNGNgMPb7sHE344ATNfn1lkPfqy0m1Xb3ZCzVw2Ew0ZIMWg+6KbZQOUUlfc5JZZKEAEWv/51ORLkfazTkir5EZenv/8mwACYzGCEqAomTehvYBCJS0GgNwTs//+fCV3Eg9dL/7Yf0CF2i9qSYY4lyFdBtGqmcTtJeL5kVmlZtGvJarMi97LWBTpEKKtknudUBDK4JE0sqwWgxoYhtUCrN9WnPXXszD9N5H1yGQyHwhA7Eq5hRsW4oXNL6CGARnTtwzm/GHcqRQGHIo4TYPxFM495IsVrUXcjfzmpt8/FnMjIsYvZF/IZ5J4iqTFABD8z//8z3tduc5u3/fLmuKenh4IITBt9DGYOOjgKC5nFhjEqZ/2/1DmkRSLOjrZqC2J50UhqyWvCsStWLfH0VaGwPcYPMbAaAoZrxbNtVkMygBjBwCPbLkHI386Et+a9S23NMECxHbe3d2g8DwPQgpc8cgVaK4RaMgA6VTK8QidSSVa66F6Qtnqc1kATtvvIjTX7YEwDMuqndlsFowxLF43f8tf//LoZjtuCXfSZ/JZChxxAlro6OjIzZs77zF7EqVMWdxqXHns9VCcQAUEiutZ4bHovYxS+B51z/sedQqnVQA9j+q9T2EVUwMASuD7+hiM6s96HjW/IAp9U74mpSmaQf+6ejRlfIxsAJrru3DTu9dh5I9H4usPfR2b2ja5/WBramp2C0isafc8DwEP8IX7v4D5Hc9iUD2Q9TNg8OEbywept78g5rd5KS34iQKQITX4tyO/4axFqeseX+n/yKy/zjbWIp8ARjniGWVkK1SHUyRaISDqxpJ6Z/67XV/44heO8z2flAvhwjBEKpXC0P4jsHjDW1jfuUK7B+M7KbNijlmTQYydMEkjaotTKJw1sDPK6h6eR2PLCImpnYQTzuLfYxm+BpmPdNpDigrUZxSIn8MLG1/CD175GeatnAc/9DFq4Cik/JTrdGzdTF+6CKZSKbf6flPbJpx656mYs3UWhtQB9ekUMuksmOcZThVbj0ON9ZS65FCGBJ//2NWYOuoYBEGAnp6eslYpm81ie+eW/Kmf+NQfhRAdiDazsTwjTLgW1ZvyiRIco5Q7KcybN6990ZKFzwNATU1NWfRGVuN7YCKli3iELkShJsMKt2uhidVN3sRyEQsE7S6Iq1PwWEReo8/qqMZaGstFXFU6AZQ0CTiaQX1NAxozdRjamMKYAcCo/gKvt87CJc+cg0E/HoTpd0/H7HdmQ0jh6j1qamqcW0haEkqpk+FramqctZk1fxYOue0QLM2/gGF1QP9sGplUDRg8bSWYTqczLxL6qCnzUwIYVj8KF0y9Qq/2y+XKXm9LOh97ZvaThUIhH7MWYcKVqDK1GTtYjOQag3iPDJawHD4Af8nipS0Xfeaio33PJ7aTX6kkle/76F/bDMkl3lz3EqhHwTw94HqFl/lhiGa27igTk4ZBnERuF/m63ZlN1pYZNm+XwbhMbuwztsBFuxttFH3qw/NS8FkKNWmG2pRAraeQ8gIs634Hf1h8D25+/las3LoSjV4jRjaPdADwfR+pVKro0S6+JoTgheUv4Pw/n49fvPMj9KvrxB71QJ2fRZplkfJ01yPGYtlUFU0GvXyRQBaAb3/ydoxoGuNyOuWsUzabRXtPS3jyMWf8rqenpxVAp7EWuQTXKJU3KVsEWqp/uN2iOwO9Y049gEYATQCa33r3jfMmTzrwiEKhgO7u7rIMub6+HlIJXHb3J7Gs8214aeIKf3WLx+hUGCUIuWnbbBJwzNP7jLHY1tn2E8wzz3GV0Auo2YTGuitTqkeiveGJsVS2VkQpBaEEuOQQKkQ+yCEvFLoCoDsA2vLAHplhOHP0mTh8+OE4dNShGNE8wn3nvFXzsLx1OZ5d+SweW/sYtoYr0S8NNNUANcyHR7LwmQ+PMFBG3VbeJFbwyxgBDxUggLBH4qwJM/DVY6+HEAIdHR1lhbb6+nr4vo+7Z93xyEVnfO5v0HuitRpX0gWgx1iQfMKC7FCbQcoApaibDkwfcejtDWqh93HvB6D52GOPHfHEk49/k1GPdnR0lK07rK2tRTqdxrqWlfj8n45F6PfAy1J4PtGb0FHbNY+4jXel3XTXVE8LEW3KSw0R1fWjpJgkuQXCxmeTWIsDRJvU2I3rPLukwWbuCNGr5j2AC72fa4EHCFUeXCl0FYAeDgRCL0PIh0BTpgntvAUpCmQ9IEWB+oxu/uIThppULXzmgyoG6lHdw8vlRqx1Va60jwcKvEdiaHYMfnfR00h7GXR1dVXcKK+2thbbOjfnRg8bd21nZ+cW6C022w0wesy9UCJ8rUg+S5HQOAEt5VK8lStXkuNPPJ6PGD5yb0pp2bqAMAzheR761zVjQM1gvLjicZc9JLHEmsuomtDUJpGslsFMxBHt5Kz5ieeZPIsJfS3nsC7KcRAQDQTrqWyoG0vISQl4THf3Y4yBEYaUl0KKZpD20qjxGep8iTpPobkWGFALNGRyGJjVfzdlgQF1Hmq9LOpSdajN1IAhBcY8vaMjdMRhM6nWTRLDpSQHREGBiRR+cNo92KNhTwRBUDESqa2tBaUUd9z923v/cv+sFcaFxK1EnHgmQ9aqViElm7PZnY3SMXdSE7Ma/YcMGbLHkvcW/7/6bEO9TTSVU/waGhpACMH/zvkGZi3/LWiKgKWocSfxJJOe2YzFtqA0FsRuaWH/b98nhIp9FynaArw4fIx6jGqSF3XKIyQ6B9vv3B5LKrOZjdkJ2uzYbnZhNhZIaH2GEqoLj1RUsxq3Vq5CDVGDFWk22BGBgsgpXH3UL3H8PmeBc47Ozs6ykY8lw+9tWrxt7JB9vg+9hfc2A44O6O28czEiGsQKdkqW+PVmMSqRUbtsxuvq6qJD9hi6Zdq0aYcwFhWmltI2pJRIpVKYMvIozF/3Grbk1mohyqTVdVcYfZqeje0RpdmdlmFOkRL9nDJV5p5pvmZ3YmYsKiKmTjcxSiqjxnRHz9toIFJkqbM6zCTzfI/Bowwe9UCVrgNJeykQ5SObSsGjPjzqaWHN/gZzLsW/I5YwI8SAAlAF4NxJX8a5B18GpZTjbuUqtLLZLLgM1WVfnHHrogVLNpfgFEEiMqmoelZT86kSqXcRY7TWV/UAyH31q19dvnzV0jcopaipqSmLbqvYUcIwsv94U+oHnX439Rc2xIwDijHiXIuVwi2YitfUalcgZVQiaF0OYzF3FfseKVH0aNsheR4tOrb9XtsKiRASKbFGhHNbdMfO3557/HttyyYLaGspVABMG3oCZhx+jVOQK61JteHpg4/d+8RD983aYMcj5jp4CaIpe1t0VPXygXhdRuxukzPdAHrOOOXMv+YKPT2+75dNsOkLpQ3V/A2vOL8fv5D2YtvBjRJtKAo7YVesIfqMHbw4yDyrhhYNXpSvsZ+1r1EjnDnRTBWDzB47DpA4oOy5w1TK27s9L8Yi5RamdpMHCjIADhh4FL71ydudulmu1oIQgpqaGjDGsHzjwi0Xn/mFZ43riAMj7AUUO70hb6ksqygBjDyA3MKFCztv/vkv/lRp/YkFRke+Davbl2kxx9dJMxVb30kZgZ+irlhHF+9QV6xj3wNiajpgyusNyaQeAYyaap+3P4R5VBcL2WwsMdtz21I6n4J6Ufmgl4qynTY7LJUtMDLbcxPzPo/CS2mhSoG44xBKIPSeeTqzbJ4jpiJLFICDBh2NG077PXzmIwiCiuWDVjPJhzn5xc986Y4gCOKRR1CiMKecylkSHF4FQJASleI05kriwHD3LVu29MT9YblahIUb5wJMgjEGu4OHY+lUEzkbxtKEu6BUm2QuFDxGYVpVRZ33THdAj1G9EZ0w+6ibA9nIQ0qltQ5DZu2PFaa9kj0nK8QJoeD51GzVTaM+nIq4C6a7BWkw+ylSdNmJ+W2QpphZEYQFCRUqTNnjWFx70m3wmY8wDF0EUm5lWVY3UcTv//ybv8x5as7mEuFonFOUshgVXYlXhcVAmdqMpEsJAfBPnHrM2ErL5Sww3t7wKjyfgVBmtoki8CjRHfNMYC+F1P43BgrdyA2ghMK3OxqazfbsYHpe1BbRN+Gt7lyjE1TUqCGaLyikjP8XZrM+xiikUkXmlJqdhaRSMeIIh0i95jQS4BgxQDJbf+mkIBCa1tNEEMhAQRWAU8ddgiuO+DYo0cQ9l8tVrOWwoJjzxhNz/+2ir75mXXlMvArKAKPIWiilVDnweb2AopzVSGZc7d9i/N7jJtnoo9SXWhezcPNcs2uhDleFfb9y25jqulCjNQgTOjJT/qUAM3janfhmBieFItejEzospYToFksOaMR9H2Nm9T0x/TAU3MAKadYZW+5DI1nebuZrsoDwTCjt+xTSWDW7fZVHCEKuEBYUEFBcPuXbOGP/Sxwxr2QpbDabUorF695Zd+Khp80y0Ud3zFoECVfCS9Rf9Fqj2JvFqMZquEUs6XSaDB80alg5i2H5RYHn8V77O/BqGZhPzFbVZpaaQhXmBCy4XIhbumoLemjUgUfC7CoBWwZnQsPEjNYagz6QkAoMFhRwoOA8Og9FbIEQzF4rBAyxhJxxCdZS2ecJifiEEEZv4WaD3gLQSJtw1SduwpSRR7vEWG87Rdo6i03t67pOOOyUOw2viGsU+RJupFT9BXYVGKWshkyQGYfEr3zlK4N85jMLiiTyrbVYtOkNEE8axRHwfRblLWDT8nZPUup2NBSm/6XWN/Sg6dxCcdhqe3Nb8QvWulBreYjZX0UPGrMilhG/PLPQCTFxy0rtLjpCrGMviVaOSbNPi8colNTbZBHTe4wHEjyv8LHBx+PKI29EY7a5aAV+JUth6znac63hZ8675Fdr165tTViLXMJiVOQWqpfK5mosRjmrIZNI/MQpx0ys5EbcRjebXtfl/2YFPDeKpVtsY/72PGY2qWW6qz80OEIp4TMKrpRxLZpcciFNuEqMJdF8QQF6W24Q11/CCmlW2JJKWxO7Aa8VwQgIuPFHNEaorfVxoCMEXCp4lIJB75JEADBJEAYKoiCQQS2+PO1bOGHCOa6i3tayVAKFTfF35TvCiy/6zC1/f/ypLQlQ5EuQznLRCHaHxUhaDZSpCSUA2F5jRh5UiXhaUvnu5lehSBS2CSmdgmm1CmstKAh4QbpZx6AHQPral0cpeyDFWKxNU3Q8KAXfY9rvezS21EEn0OweaLY+hCGeu9Gzn1Git8SS2kKlPW39hFLwiRHplAI12ohdNiwDBZmXOH7UefjMIV9DU81AV91WqfQ/CYrOfDu/aPr0W2c9NHuj0SviliJfAhTl1qn2ai36YjGAyqujSTabpSP22GsvezHLEU+pBJa2vo1UbTSIvs9MO2k9c5VUoITqtsxmcbQIFBpTzThxzAV4c+PzWNb2FsAAL0UBotd0auFeF/CI2DkQc1zmUTe7leEz0rgcCq2FRMmSiFyazs/OxYEYEFihDET3z5AAl0qv0A8AEUhMHng4Ljn4vzFmwMSidTe9WQkrdzPG0Jlv55decsmtsx6avSmWHOuO8Yp8CTdSUuVUVS6O6YsrISUEEWXzJ5deeunQbKo2VW5hjrUWS7a8DUEKoIRp7cAtvaNuZyH9k5ROJgUKE5sPwckHXoTDR30SlDBceMCVWLBpLu5/91d4e8uzILaxm0f0rsYmdhLEgAAqUlYNEIRAUcLMtdim+QAAC5pJREFUklSYyMUtglNR2p6YVlCUUihut/3WlkTv26p7eKZUFkfueRpOGH8u9h402eWIOOdVbUVu6zYppWjt3pa7ePolt/314dnxHIglnD0JHalXbrE7XUnF32CTap88/YT9qwlTF2yeq2c5tWgzYhMXUILoNSihAhNpHD3iVJy2z8UYbWabnXGEEEwafAgmDf4NVm5fgofevR0vrn0UASvotSqxLoGKmmZmUgselBoZnFFwIV3anhgA2dyNJb/KaBKuCFnplLgQMgIz1/UTY/tNxgljzsWRY05F1q9xgLDNaEsR8lLXya45XbFpyabjP37y71esWNFewlLkErpFqSWIsq8uZFeAUXLPtDETRk2rVChrLcaCLa8ZWdv0neKaqIEDPBDYIzsSp0y4CMeM/RTq0/3cxbUzzv422zJ6VPPe+NpRP8YV4XV4a/0LeG7VLLy15QUEKgd4GhiSSBCmXQyIJpa6z6re7UhCwWd6rSgPpcvsUkVNA1ggCKWRr82eJFK7jywacPiIU3Hi+POxV9PeRQC2974UDtsJNG/xK28fPeX42V1dXZ0xTtEVsxS5MqSz6kRZb4Pcl/fRRLlfLYBmAIPae1ofbMj2qylXMZ7JZCCVwEV/moa832k65Jjm84HCgXscgVP3/iwOHn5krNJK9npx7TYV8eWSXIaYt/Y5vL7uGSza/jo2dq+EhC4SpkYvsRlbyyWElIasErc7MjO1H7r8QkvrRFLsM+Bg7N18APYZeDAmDz0UKS9TBGC72U9fVqbZOlEuuXr48fufOPvkC19PWInumOzdk7AaNjXByywRKGstdkb5rGQpiupCL7744qEN2X41JTrdF335mpbl6Mq3wwODEgpZ1oCjR34KJ+99EYY2jnRRgL241ZTpW+DYIl/G9PqRaSOPw7SRxwEAOgttWLl9ETZ0rMZ7LQuQF13Y2LlaN4hXAdZ1LtffSSgIUWAqhVH9x0JJYMSA8WjK7IEBtUMwunkiRjfvA48WL7KqBsBlTbbnuTB+Y9vaji/N+PLdM++ftSUBiO4SlqK3qqydciE760pKbtb7qU+fvl9V/GLDXCiusGd2HE6deDGOHH0KMjFfbC9wb2gud4tvuscYc2CpT/fD/kMPxf5DD90tK8oswY5/X1/P14LYWro5c5949awTzp/T2traXYJg9sQqvSuFpzu4EbWTS/S9nXA7cVB4AFLj9h39cTvbK8bkrAY3nnov9h16yA6zrS+LeKq5JQFWbrVcuQG1v8WeV3yPkl1i64QU7e22pX1D+3e/c/0Dv/jprRsTYLAkM24lkryizwU47wfHSFaN15olBINaOrfe379uQEMYhmVdSXyNa3y27a6eEx/2m80TWUDkgu7wzzPv/duMi69YHARBLgaKUi4j6T5KFeGInXEhu8Ixyu6VBsD7xCc+MaB/3YCGcvzCAsFWj1d6X2+mN143+s8CKNv+0gKCS65eeuPZly/81GdfXr9+fdJF5EoAIR/Lg5RzHzsFiveDY7jq8c9/4fP7VlI7S5nnvvriOCgsfyjl4z9M1sHyG3ve+TDH585/9bWrLv/666+//npciyjlJvIVVM2gQvSxW0DRF2CU2r3ZB5AaP3nUYdXwi75eWHtR7YXtzLcFT734+Bv90oPy06ZOnZZN1WZtqBongx8G6xAHcVtPS8+Tf3/8mf+44r+WrF+/3g5yLvEYlABCUqMolRzbJRFrd1kMih2XL6aGDx25785GEZUAYW+rti7bdv+fHphzzX9+a1MY6r21fd9/5+qrr66/6AvnHzpu+ITx1CzpspakzCZx75tlSJLZgOfF0tWLljx8399eve47120PgiApW+cTUnahBBgKZdLoPJbZVu8HKKohn6QM8awH0DRlypQxr776yr2EUFJNDqA3/mAvboHn5dxFLy766Xd/+eKD98/sTswSxME5btw4/7rrrht8yOEHHDhi8KhRKS/tlXJf5R6rJWflHp2oJkKxfN3i5ff/cearN/34p9vb2tqChPlPDnQBJWpmS3CJUq5jt4FidwhcSPKLL874wgRCKKmGX1Sabfa2vWtzz+Nz/vbS1//tmwvWr19fSOj/8VVT1np5y5Yt8y644IIuAKt83/cvv/zy2pM/deKoffebNGnYgJFDyw3krvKfXNATrN2ycsOCdxcuffShJ1feeeed3aHOoyfrYMOEKyiUAUuSRyQJJi/hOna7peirxbCcwjdhaj8AA19758Wrp+x72El9iRJKAWLphnfX3/Hr3z/9/et+vAU7lqaFZWL0Uk1ddvj7tNNOy0ydOjW73wH7No8cs+ewwXsM2TOdTmf71w5oqOZ8W7u2dXT2tHds3rx543tLVq1b/d7atmeffbbr0UcfDbBj7Wuygt4+FySeC8pYj7CE2yjnOnYLKMpNhGqAUUQ2jRvpB2DQ+u2rfz+0acTQakhfEgw9QRd/7d0X5n/7qu+/+OyzzyaVvIK5EAGKK8WAqAQgvmwyvtjajwEkvpQyeXeEety4cfSggw7yAODdd98NFyxYUKm1pUyY9eQgWrcXJsAdJKxHUMKahIljVWyktjssxc4Ao1QUkgFQB6B5+PDhe65ctfIhRhmtBIwkIDa0ru6Y/dhfn/v6Fd9Y1tbWVijhhytdHFUCtElgxMEQf47GHpNkOnkd4iCU2LHeNVn3amd1iNLLK0qBIyxxF9ixgLdkE7Xd5T52lWPEB8EH4H/ta18bzyij5ZTO+BdKJbFg1Rvv3XLTbXN+9YvbWxK+ttQFSwKj1JJ9WgEYSUCUsxgoA4xktZoscxeJweS9ACMs8Xy531q2Ebz6ANS9viqfzlwffPjkjyURl0RfV749eOnN5+Zd9cX/99qCBQvyJchXKT8cYsf1KnEfWwkYXpXASO5KHX8st8uTKgOKUgu+eYJ3hCWe4xUshPxHAaJaYJSUwQF4I0eOOKic4rl627JtDz0w8+mrr/zmqiAIkssZwwTJCirMqGqBQROgSLqSsvyijEstt+K/N3dSChy8DBB44lgVm7KqDzgH4FURrZCEf6a1tbV0SNPw4XErEYqCmv/e3IU33fCLx+/5w71dJS5SkABD2Iu5LRe7A+V7dtAYKLzkeZcBRjUWoxqeIUpYgCQIkr9HfhjcRl8tBilDTtX3vve9UR71GQC051pyL7w+55mrvvj/nl+6dGmY8MuihElN3ku5kEo+NwnYJHCrdSOkAvlUVbqSciCRCYsgyliGUi7jHw6KneEYACCPOeHI/Ve3LFk9e9Zjd3/p0n9fEpud9n0KO/bUKAeMsEKoVkrpQwWrUcqC0ASAylkLUoZ4lrIaKnZOohewlALChxYQvYWr5aTwDIDaCRMmNC9evDgF3YsrFfPvNBHyyURMH1ZwIUlAVLIWpaIlUgIIrIRlqUQ8USUBVSUGWyQeewPDhwIQfdUxSgHDh27nGG/Qlo4Bw0uEgeVaJpQCQylQlFuhrVC6TxgtYUVIBUtBqhD4VC/gUCUGX1Z4b8lmJf9IC7GrOkZ8fzRLHmls8OMWg6B0c/pKIVslP1y2Qz6Kq9dLDXrSQtAyVqKcxUi6FlklWMoBoeS22bt7l4MPOu2eBEbcJ/PERVcV8gjVAEJVAYr4oMoSACjFj2gVoKgEDtWLJQF63zf9n6L0rNpFzUk2zmMXWcRMd9K6iApiTpKgqTJxvKri/JJ8QZbhEdUAAlVaj3IAUL0cA/9KwIiHnyTBIzhK79+aVAVFmUij17CtigtLynyuEhB2BhjVntc/fYWzVyUo4sBIuhaGHdsklLIwvcXwOwOIvryvN3eE9+l7/+WAoRKaRLnXaQkNQFWI43tl6bvxgv+fH+D302Ik3UncKpAS1gJlOIPcTdbho9uHKCqRCV9eChiqD6HdR7P1Q34jVbxGUL7xfDlOgg/QXXx0+wdGJaW4RTWh3Ufu4l/YYvQ1xPuXiOM/Asbuec9HTP+j20e3j24f3T66fXT76Ba//X8z8P+YdAZAPQAAAABJRU5ErkJggg=="; } } diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index 297b5ea..cfe580e 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -42,7 +42,7 @@ public class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.200"; + public const string WhatsAppVer = "2.11.209"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ public class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.200 Android/4.4.2 Device/GalaxyS4"; + public const string UserAgent = "WhatsApp/2.11.209 Android/4.4.2 Device/GalaxyS4"; #endregion From 710f25a3b1c42d1282a270fedc96c7e92a1c3872 Mon Sep 17 00:00:00 2001 From: shirioko Date: Sat, 19 Apr 2014 18:57:11 +0200 Subject: [PATCH 171/271] Updated version details to 2.11.224 --- WhatsAppApi/Register/WaToken.cs | 3 +-- WhatsAppApi/Settings/WhatsConstants.cs | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/WhatsAppApi/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs index 56607f4..3ae362b 100644 --- a/WhatsAppApi/Register/WaToken.cs +++ b/WhatsAppApi/Register/WaToken.cs @@ -8,9 +8,8 @@ namespace WhatsAppApi.Register { class WaToken { - //private static string WaPrefix = "Y29tLndoYXRzYXBw"; private static string WaSignature = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; - private static string WaClassesMd5 = "+XW/7rCZDX9T7YrGQqTmcg=="; + private static string WaClassesMd5 = "ZGdhtLiFqomD5ovSpQ+Odw=="; public static string GenerateToken(string number) { diff --git a/WhatsAppApi/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs index cfe580e..8d432dc 100644 --- a/WhatsAppApi/Settings/WhatsConstants.cs +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -21,7 +21,7 @@ public class WhatsConstants /// /// The whatsapp host /// - public const string WhatsAppHost = "c.whatsapp.net"; + public const string WhatsAppHost = "c3.whatsapp.net"; /// /// The whatsapp XMPP realm @@ -42,7 +42,7 @@ public class WhatsConstants /// /// The whatsapp version the client complies to /// - public const string WhatsAppVer = "2.11.209"; + public const string WhatsAppVer = "2.11.224"; /// /// The port that needs to be connected to @@ -57,7 +57,7 @@ public class WhatsConstants /// /// The useragent used for http requests /// - public const string UserAgent = "WhatsApp/2.11.209 Android/4.4.2 Device/GalaxyS4"; + public const string UserAgent = "WhatsApp/2.11.224 Android/4.4.2 Device/GalaxyS4"; #endregion From 9cded2b12547cb7bd238a98152c89ae4eb1e2829 Mon Sep 17 00:00:00 2001 From: shirioko Date: Thu, 24 Apr 2014 17:14:47 +0200 Subject: [PATCH 172/271] Added buffered reading of socket Should fix all MAC errors Fixes #9 Fixes #5 Fixes #7 Fixes #104 Fixes #101 Fixes #113 --- WhatsAppApi/WhatsNetwork.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index ddb8d1d..7a5142b 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -128,16 +128,24 @@ public byte[] ReadNextNode() nodeLength = (int)nodeHeader[1] << 8; nodeLength |= (int)nodeHeader[2] << 0; - byte[] nodeData = this.ReadData(nodeLength); - if (nodeData.Length != nodeLength) + //buffered read + int toRead = nodeLength; + List nodeData = new List(); + do + { + byte[] nodeBuff = this.ReadData(toRead); + nodeData.AddRange(nodeBuff); + toRead -= nodeBuff.Length; + } while (toRead > 0); + + if (nodeData.Count != nodeLength) { throw new Exception("Read Next Tree error"); } - byte[] fullData = new byte[nodeHeader.Length + nodeData.Length]; List buff = new List(); buff.AddRange(nodeHeader); - buff.AddRange(nodeData); + buff.AddRange(nodeData.ToArray()); return buff.ToArray(); } From 359c796e0eaee59a4d7633731ffdaa6a75b23c80 Mon Sep 17 00:00:00 2001 From: shirioko Date: Thu, 24 Apr 2014 17:19:50 +0200 Subject: [PATCH 173/271] Removed while loop in Socket_read Fixes #6 Socket_read retry should be implemented by client --- WhatsAppApi/WhatsNetwork.cs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index 7a5142b..94e6587 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -163,26 +163,23 @@ private byte[] Socket_read(int length) var buff = new byte[length]; int receiveLength = 0; - do + + try + { + receiveLength = socket.Receive(buff, 0, length, 0); + } + catch (SocketException excpt) { - try + if (excpt.SocketErrorCode == SocketError.TimedOut) { - receiveLength = socket.Receive(buff, 0, length, 0); + Console.WriteLine("Socket-Timout"); + return null; } - catch (SocketException excpt) + else { - if (excpt.SocketErrorCode == SocketError.TimedOut) - { - Console.WriteLine("Socket-Timout"); - return null; - } - else - { - throw new ConnectionException("Unknown error occured", excpt); - } + throw new ConnectionException("Unknown error occured", excpt); } - } - while (receiveLength <= 0); + } byte[] tmpRet = new byte[receiveLength]; if (receiveLength > 0) From 29a74c733532dd020bd54ce2e0a0596593c3a226 Mon Sep 17 00:00:00 2001 From: shirioko Date: Thu, 24 Apr 2014 19:18:38 +0200 Subject: [PATCH 174/271] Cleaned up login and inbound data processing --- WhatsAppApi/WhatsApp.cs | 60 ++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 9a37277..62536a3 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -33,11 +33,6 @@ public enum CONNECTION_STATUS LOGGEDIN } - public void ClearIncomplete() - { - this._incompleteBytes.Clear(); - } - private ProtocolTreeNode uploadResponse; /// @@ -123,21 +118,11 @@ public CONNECTION_STATUS ConnectionStatus /// public static readonly Encoding SYSEncoding = Encoding.UTF8; - /// - /// Empty bytes to hold the encryption key - /// - private byte[] _encryptionKey; - /// /// Empty bytes to hold the challenge /// private byte[] _challengeBytes; - /// - /// A list of exceptions for incomplete bytes - /// - private List _incompleteBytes; - /// /// Default class constructor /// @@ -158,8 +143,6 @@ public WhatsApp(string phoneNum, string imei, string nick, bool debug = false, b this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.timeout); this.WhatsParser = new WhatsParser(this.whatsNetwork, new BinTreeNodeWriter()); this.WhatsSendHandler = this.WhatsParser.WhatsSendHandler; - - _incompleteBytes = new List(); } /// @@ -285,16 +268,15 @@ public void Login() this.whatsNetwork.SendData(data); this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(feat, false)); this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(auth, false)); - this.PollMessages(); + + this.pollMessage();//stream start + this.pollMessage();//features + this.pollMessage();//challenge + ProtocolTreeNode authResp = this.addAuthResponse(); this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(authResp, false)); int cnt = 0; - do - { - this.PollMessages(); - System.Threading.Thread.Sleep(50); - } - while ((cnt++ < 100) && (this.loginStatus == CONNECTION_STATUS.DISCONNECTED)); + this.pollMessage(); this.sendNickname(this.name); } @@ -718,7 +700,17 @@ public UploadResponse(ProtocolTreeNode node) /// public void PollMessages(bool autoReceipt = true) { - this.processInboundData(autoReceipt); + while (pollMessage(autoReceipt)) ; + } + + public bool pollMessage(bool autoReceipt = true) + { + byte[] nodeData = this.whatsNetwork.ReadNextNode(); + if(nodeData != null) + { + return this.processInboundData(nodeData, autoReceipt); + } + return false; } /// @@ -788,12 +780,6 @@ public void sendDeleteAccount() /// An instance of the ProtocolTreeNode protected ProtocolTreeNode addAuthResponse() { - while (this._challengeBytes == null) - { - this.PollMessages(); - System.Threading.Thread.Sleep(500); - } - if (this._challengeBytes != null) { byte[][] keys = KeyStream.GenerateKeys(this.encryptPassword(), this._challengeBytes); @@ -852,13 +838,12 @@ protected void processChallenge(ProtocolTreeNode node) /// Process inbound data /// /// Data to process - protected void processInboundData(bool autoReceipt = true) + protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) { try { - byte[] msgdata = this.whatsNetwork.ReadNextNode(); ProtocolTreeNode node = this.reader.nextTree(msgdata); - while (node != null) + if(node != null) { if (node.tag == "iq" && node.GetAttribute("type") == "error") @@ -1201,14 +1186,15 @@ protected void processInboundData(bool autoReceipt = true) } this.SendNotificationAck(node); } - node = this.reader.nextTree(); + + return true; } } catch (Exception e) { - //whatever - this._incompleteBytes.Clear(); + throw e; } + return false; } private void SendNotificationAck(ProtocolTreeNode node) From f83442dfebae5af5cce6df42d8c75966b580e33d Mon Sep 17 00:00:00 2001 From: shirioko Date: Thu, 24 Apr 2014 19:19:40 +0200 Subject: [PATCH 175/271] Don't throw exception on receiving 0 bytes --- WhatsAppApi/WhatsNetwork.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index 94e6587..eef702a 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -120,6 +120,13 @@ public void SendData(byte[] data) public byte[] ReadNextNode() { byte[] nodeHeader = this.ReadData(3); + + if (nodeHeader.Length == 0) + { + //empty response + return null; + } + if (nodeHeader.Length != 3) { throw new Exception("Failed to read node header"); From 4b3b7e97a819122cde9c10451eb024150d43baad Mon Sep 17 00:00:00 2001 From: shirioko Date: Thu, 24 Apr 2014 19:21:41 +0200 Subject: [PATCH 176/271] Revert "Removed while loop in Socket_read" This reverts commit 359c796e0eaee59a4d7633731ffdaa6a75b23c80. --- WhatsAppApi/WhatsNetwork.cs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index eef702a..ca2be64 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -170,23 +170,26 @@ private byte[] Socket_read(int length) var buff = new byte[length]; int receiveLength = 0; - - try - { - receiveLength = socket.Receive(buff, 0, length, 0); - } - catch (SocketException excpt) + do { - if (excpt.SocketErrorCode == SocketError.TimedOut) + try { - Console.WriteLine("Socket-Timout"); - return null; + receiveLength = socket.Receive(buff, 0, length, 0); } - else + catch (SocketException excpt) { - throw new ConnectionException("Unknown error occured", excpt); + if (excpt.SocketErrorCode == SocketError.TimedOut) + { + Console.WriteLine("Socket-Timout"); + return null; + } + else + { + throw new ConnectionException("Unknown error occured", excpt); + } } - } + } + while (receiveLength <= 0); byte[] tmpRet = new byte[receiveLength]; if (receiveLength > 0) From a1952b6a5ea6948dd76c00d1a86f96bff9b33e8b Mon Sep 17 00:00:00 2001 From: shirioko Date: Thu, 24 Apr 2014 19:26:51 +0200 Subject: [PATCH 177/271] Added sleep when waiting for incoming data That's more like it! Better fix for #6 --- WhatsAppApi/WhatsNetwork.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index ca2be64..8792748 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -165,7 +165,7 @@ private byte[] Socket_read(int length) { if (!socket.Connected) { - throw new ConnectionException(); + throw new ConnectionException("Socket not connected"); } var buff = new byte[length]; @@ -188,6 +188,12 @@ private byte[] Socket_read(int length) throw new ConnectionException("Unknown error occured", excpt); } } + + //sleep to prevent CPU intensive loop + if (receiveLength <= 0) + { + System.Threading.Thread.Sleep(100); + } } while (receiveLength <= 0); From 43d80b9cf213d066624d06d535c21e6fbecb5971 Mon Sep 17 00:00:00 2001 From: shirioko Date: Thu, 24 Apr 2014 19:40:39 +0200 Subject: [PATCH 178/271] Cleaned up auth header --- WhatsAppApi/WhatsApp.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 62536a3..f31ac27 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -759,13 +759,14 @@ public void SetPhoto(byte[] imageBytes, byte[] thumbnailBytes) /// An instance of the ProtocolTreeNode class protected ProtocolTreeNode addAuth() { - var node = new ProtocolTreeNode("auth", - new KeyValue[] { - new KeyValue("passive", this.hidden?"true":"false"), - new KeyValue("xmlns", @"urn:ietf:params:xml:ns:xmpp-sasl"), - new KeyValue("mechanism", Helper.KeyStream.AuthMethod), - new KeyValue("user", this.phoneNumber) - }); + List attr = new List(new KeyValue[] { + new KeyValue("mechanism", Helper.KeyStream.AuthMethod), + new KeyValue("user", this.phoneNumber)}); + if (this.hidden) + { + attr.Add(new KeyValue("passive", "true")); + } + var node = new ProtocolTreeNode("auth", attr.ToArray()); return node; } From 368bfdf5e2c0f4120cbdf322e59a15ab3b90e308 Mon Sep 17 00:00:00 2001 From: shirioko Date: Thu, 24 Apr 2014 20:09:50 +0200 Subject: [PATCH 179/271] Fixed server and client receipt events Fixes #37 Fixes #110 --- WhatsAppApi/WhatsApp.cs | 53 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index f31ac27..14fd64e 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -275,7 +275,6 @@ public void Login() ProtocolTreeNode authResp = this.addAuthResponse(); this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(authResp, false)); - int cnt = 0; this.pollMessage(); this.sendNickname(this.name); @@ -878,6 +877,33 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) this.OnLoginFailed(node.children.FirstOrDefault().tag); } } + else if (ProtocolTreeNode.TagEquals(node, "receipt")) + { + string from = node.GetAttribute("from"); + string id = node.GetAttribute("id"); + string type = node.GetAttribute("type") ?? "delivery"; + switch (type) + { + case "delivery": + //delivered to target + if (this.OnGetMessageReceivedClient != null) + { + this.OnGetMessageReceivedClient(from, id); + } + break; + case "read": + //read by target + //todo + break; + case "played": + //played by target + //todo + break; + } + + //send ack + SendNotificationAck(node, type); + } if (ProtocolTreeNode.TagEquals(node, "message")) { if (!string.IsNullOrEmpty(node.GetAttribute("notify"))) @@ -1152,8 +1178,20 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) if (node.tag == "ack") { - //do nothing - //throw new NotImplementedException(node.NodeString()); + string cls = node.GetAttribute("class"); + if (cls == "message") + { + //server receipt + if (this.OnGetMessageReceivedServer != null) + { + this.OnGetMessageReceivedServer(node.GetAttribute("from"), node.GetAttribute("id")); + } + } + else if(cls == "receipt") + { + //message received ack + //do nothing + } } if (node.tag == "notification") @@ -1198,13 +1236,16 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) return false; } - private void SendNotificationAck(ProtocolTreeNode node) + private void SendNotificationAck(ProtocolTreeNode node, string type = null) { string from = node.GetAttribute("from"); string to = node.GetAttribute("to"); string participant = node.GetAttribute("participant"); string id = node.GetAttribute("id"); - string type = node.GetAttribute("type"); + if (type == null) + { + type = node.GetAttribute("type"); + } List attributes = new List(); if(!string.IsNullOrEmpty(to)) { @@ -1216,7 +1257,7 @@ private void SendNotificationAck(ProtocolTreeNode node) } attributes.AddRange(new [] { new KeyValue("to", from), - new KeyValue("class", "notification"), + new KeyValue("class", node.tag), new KeyValue("id", id), new KeyValue("type", type) }); From 2b8e5f712811e6c7e217dedde4c4283592e83c13 Mon Sep 17 00:00:00 2001 From: shirioko Date: Thu, 24 Apr 2014 23:28:31 +0200 Subject: [PATCH 180/271] Fixed media upload Relevant to #116 --- WhatsAppApi/WhatsApp.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 14fd64e..3507a8b 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -545,10 +545,10 @@ private UploadResponse UploadFile(string b64hash, string type, long size, string this.uploadResponse = null; this.WhatsSendHandler.SendNode(node); int i = 0; - while (this.uploadResponse == null && i < 5) + while (this.uploadResponse == null && i <= 10) { i++; - this.PollMessages(); + this.pollMessage(); } if (this.uploadResponse != null && this.uploadResponse.GetChild("duplicate") != null) { From 94d368994d58bdb15c8c2b2b7ff595749cb926d8 Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 08:50:51 +0200 Subject: [PATCH 181/271] Fixed nullPointerException on empty response Applies to #116 --- WhatsAppApi/WhatsNetwork.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index 8792748..9b4faa4 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -121,7 +121,7 @@ public byte[] ReadNextNode() { byte[] nodeHeader = this.ReadData(3); - if (nodeHeader.Length == 0) + if (nodeHeader == null || nodeHeader.Length == 0) { //empty response return null; From 3f256a438e9bfecc4260f8fd83a673ef61f145ae Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 10:21:11 +0200 Subject: [PATCH 182/271] Improved exception handling on socket disconnect --- WhatsAppApi/WhatsApp.cs | 18 +++++++++++++++--- WhatsAppApi/WhatsNetwork.cs | 10 +--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 3507a8b..354dd61 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -166,6 +166,7 @@ public void Connect() try { this.whatsNetwork.Connect(); + this.loginStatus = CONNECTION_STATUS.CONNECTED; //success if (this.OnConnectSuccess != null) { @@ -704,10 +705,21 @@ public void PollMessages(bool autoReceipt = true) public bool pollMessage(bool autoReceipt = true) { - byte[] nodeData = this.whatsNetwork.ReadNextNode(); - if(nodeData != null) + if (this.loginStatus == CONNECTION_STATUS.CONNECTED || this.loginStatus == CONNECTION_STATUS.LOGGEDIN) { - return this.processInboundData(nodeData, autoReceipt); + byte[] nodeData; + try + { + nodeData = this.whatsNetwork.ReadNextNode(); + if (nodeData != null) + { + return this.processInboundData(nodeData, autoReceipt); + } + } + catch (ConnectionException cex) + { + this.Disconnect(); + } } return false; } diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index 9b4faa4..da2037a 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -178,15 +178,7 @@ private byte[] Socket_read(int length) } catch (SocketException excpt) { - if (excpt.SocketErrorCode == SocketError.TimedOut) - { - Console.WriteLine("Socket-Timout"); - return null; - } - else - { - throw new ConnectionException("Unknown error occured", excpt); - } + throw new ConnectionException(String.Format("Socket exception: {0}", excpt.Message), excpt); } //sleep to prevent CPU intensive loop From ff9d196eabd7bf79434e325cfd8064ff37df9054 Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 12:30:00 +0200 Subject: [PATCH 183/271] Added oneshot login using nextChallenge data --- WhatsAppApi/WhatsApp.cs | 61 +++++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 354dd61..eefa3af 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -68,6 +68,8 @@ public CONNECTION_STATUS ConnectionStatus } } + protected KeyStream outputKey; + /// /// A lock for a message /// @@ -252,13 +254,18 @@ public bool HasMessages() /// /// Logs us in to the server /// - public void Login() + public void Login(byte[] nextChallenge = null) { //reset stuff this.reader.Key = null; this.WhatsSendHandler.BinWriter.Key = null; this._challengeBytes = null; + if (nextChallenge != null) + { + this._challengeBytes = nextChallenge; + } + string resource = string.Format(@"{0}-{1}-{2}", WhatsConstants.Device, WhatsConstants.WhatsAppVer, @@ -272,11 +279,15 @@ public void Login() this.pollMessage();//stream start this.pollMessage();//features - this.pollMessage();//challenge + this.pollMessage();//challenge or success - ProtocolTreeNode authResp = this.addAuthResponse(); - this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(authResp, false)); - this.pollMessage(); + if (this.loginStatus != CONNECTION_STATUS.LOGGEDIN) + { + //oneshot failed + ProtocolTreeNode authResp = this.addAuthResponse(); + this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(authResp, false)); + this.pollMessage(); + } this.sendNickname(this.name); } @@ -777,10 +788,42 @@ protected ProtocolTreeNode addAuth() { attr.Add(new KeyValue("passive", "true")); } - var node = new ProtocolTreeNode("auth", attr.ToArray()); + var node = new ProtocolTreeNode("auth", attr.ToArray(), null, this.getAuthBlob()); return node; } + protected byte[] getAuthBlob() + { + byte[] data = null; + if (this._challengeBytes != null) + { + byte[][] keys = KeyStream.GenerateKeys(this.encryptPassword(), this._challengeBytes); + + this.reader.Key = new KeyStream(keys[2], keys[3]); + + this.outputKey = new KeyStream(keys[0], keys[1]); + + PhoneNumber pn = new PhoneNumber(this.phoneNumber); + + List b = new List(); + b.AddRange(new byte[] { 0, 0, 0, 0 }); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(this.phoneNumber)); + b.AddRange(this._challengeBytes); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(Helper.Func.GetNowUnixTimestamp().ToString())); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(WhatsConstants.UserAgent)); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(String.Format(" MccMnc/{0}001", pn.MCC))); + data = b.ToArray(); + + this._challengeBytes = null; + + this.outputKey.EncodeMessage(data, 0, 4, data.Length - 4); + + this.WhatsSendHandler.BinWriter.Key = this.outputKey; + } + + return data; + } + public void sendDeleteAccount() { this.WhatsSendHandler.SendDeleteAccount(); @@ -878,7 +921,7 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) node.GetAttribute("expiration")); if (this.OnLoginSuccess != null) { - this.OnLoginSuccess(node.GetData()); + this.OnLoginSuccess(this.phoneNumber, node.GetData()); } } else if (ProtocolTreeNode.TagEquals(node, "failure")) @@ -1336,7 +1379,7 @@ private void PrintInfo(string p) public event NullDelegate OnConnectSuccess; public event ExceptionDelegate OnConnectFailed; public event ExceptionDelegate OnDisconnect; - public event ByteArrayDelegate OnLoginSuccess; + public event LoginSuccessDelegate OnLoginSuccess; public event StringDelegate OnLoginFailed; public event OnGetMessageDelegate OnGetMessage; @@ -1369,7 +1412,7 @@ private void PrintInfo(string p) public delegate void OnContactNameDelegate(string from, string contactName); public delegate void NullDelegate(); public delegate void ExceptionDelegate(Exception ex); - public delegate void ByteArrayDelegate(byte[] data); + public delegate void LoginSuccessDelegate(string phoneNumber, byte[] data); public delegate void StringDelegate(string data); public delegate void OnErrorDelegate(string id, string from, int code, string text); public delegate void OnGetMessageReceivedDelegate(string from, string id); From 1e3b27479f0c2b807fcd10a31109a916a46d5a68 Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 12:33:02 +0200 Subject: [PATCH 184/271] Added oneshot login to WhatsTest project --- WhatsTest/Program.cs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/WhatsTest/Program.cs b/WhatsTest/Program.cs index 0ef7344..16d0635 100644 --- a/WhatsTest/Program.cs +++ b/WhatsTest/Program.cs @@ -53,12 +53,30 @@ private static void Main(string[] args) wa.OnGetSyncResult += wa_OnGetSyncResult; wa.Connect(); - wa.Login(); + + string datFile = getDatFileName(sender); + byte[] nextChallenge = null; + if (File.Exists(datFile)) + { + try + { + string foo = File.ReadAllText(datFile); + nextChallenge = Convert.FromBase64String(foo); + } + catch (Exception e) { }; + } + + wa.Login(nextChallenge); ProcessChat(wa, target); Console.ReadKey(); } + static string getDatFileName(string pn) + { + return string.Format("{0}.next.dat", pn); + } + static void wa_OnGetSyncResult(int index, string sid, Dictionary existingUsers, string[] failedNumbers) { Console.WriteLine("Sync result for {0}:", sid); @@ -193,10 +211,16 @@ private static void wa_OnLoginFailed(string data) Console.WriteLine("Login failed. Reason: {0}", data); } - private static void wa_OnLoginSuccess(byte[] data) + private static void wa_OnLoginSuccess(string phoneNumber, byte[] data) { Console.WriteLine("Login success. Next password:"); - Console.WriteLine(System.Text.Encoding.UTF8.GetString(data)); + string sdata = Convert.ToBase64String(data); + Console.WriteLine(sdata); + try + { + File.WriteAllText(getDatFileName(phoneNumber), sdata); + } + catch (Exception e) { } } From f6833a968abc098b6efd5056b1070f83990466f5 Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 15:31:00 +0200 Subject: [PATCH 185/271] Added exception handling on sending data --- WhatsAppApi/WhatsApp.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index eefa3af..79d6c9a 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -273,9 +273,9 @@ public void Login(byte[] nextChallenge = null) var data = this.WhatsSendHandler.BinWriter.StartStream(WhatsConstants.WhatsAppServer, resource); var feat = this.addFeatures(); var auth = this.addAuth(); - this.whatsNetwork.SendData(data); - this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(feat, false)); - this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(auth, false)); + this.SendData(data); + this.SendData(this.WhatsSendHandler.BinWriter.Write(feat, false)); + this.SendData(this.WhatsSendHandler.BinWriter.Write(auth, false)); this.pollMessage();//stream start this.pollMessage();//features @@ -285,13 +285,25 @@ public void Login(byte[] nextChallenge = null) { //oneshot failed ProtocolTreeNode authResp = this.addAuthResponse(); - this.whatsNetwork.SendData(this.WhatsSendHandler.BinWriter.Write(authResp, false)); + this.SendData(this.WhatsSendHandler.BinWriter.Write(authResp, false)); this.pollMessage(); } this.sendNickname(this.name); } + protected void SendData(byte[] data) + { + try + { + this.whatsNetwork.SendData(data); + } + catch (Exception ex) + { + this.Disconnect(); + } + } + /// /// Send a message to a person /// From 2c8b56b519b98c9106cbc944db54166b8e50b700 Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 17:20:04 +0200 Subject: [PATCH 186/271] Moved WhatsSendHandler code to WhatsApp --- WhatsAppApi/WhatsApp.cs | 739 +++++++++++++++++++++++++++++++++++----- 1 file changed, 650 insertions(+), 89 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 79d6c9a..c34e6f7 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Drawing; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -105,16 +106,6 @@ public CONNECTION_STATUS ConnectionStatus /// private WhatsNetwork whatsNetwork; - /// - /// An instance of the WhatsSendHandler class - /// - public WhatsSendHandler WhatsSendHandler { get; private set; } - - /// - /// An instance of the WhatsParser class - /// - public WhatsParser WhatsParser { get; private set; } - /// /// Holds the encoding we use, default is UTF8 /// @@ -142,9 +133,8 @@ public WhatsApp(string phoneNum, string imei, string nick, bool debug = false, b WhatsApp.DEBUG = debug; this.reader = new BinTreeNodeReader(); this.loginStatus = CONNECTION_STATUS.DISCONNECTED; + this.BinWriter = new BinTreeNodeWriter(); this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.timeout); - this.WhatsParser = new WhatsParser(this.whatsNetwork, new BinTreeNodeWriter()); - this.WhatsSendHandler = this.WhatsParser.WhatsSendHandler; } /// @@ -215,16 +205,6 @@ public AccountInfo GetAccountInfo() return this.accountinfo; } - public void GetStatus(string jid) - { - this.WhatsSendHandler.SendGetStatus(GetJID(jid)); - } - - public void PresenceSubscription(string target) - { - this.WhatsSendHandler.SendPresenceSubscriptionRequest(GetJID(target)); - } - /// /// Retrieve all messages /// @@ -258,7 +238,7 @@ public void Login(byte[] nextChallenge = null) { //reset stuff this.reader.Key = null; - this.WhatsSendHandler.BinWriter.Key = null; + this.BinWriter.Key = null; this._challengeBytes = null; if (nextChallenge != null) @@ -270,12 +250,12 @@ public void Login(byte[] nextChallenge = null) WhatsConstants.Device, WhatsConstants.WhatsAppVer, WhatsConstants.WhatsPort); - var data = this.WhatsSendHandler.BinWriter.StartStream(WhatsConstants.WhatsAppServer, resource); + var data = this.BinWriter.StartStream(WhatsConstants.WhatsAppServer, resource); var feat = this.addFeatures(); var auth = this.addAuth(); this.SendData(data); - this.SendData(this.WhatsSendHandler.BinWriter.Write(feat, false)); - this.SendData(this.WhatsSendHandler.BinWriter.Write(auth, false)); + this.SendData(this.BinWriter.Write(feat, false)); + this.SendData(this.BinWriter.Write(auth, false)); this.pollMessage();//stream start this.pollMessage();//features @@ -285,11 +265,11 @@ public void Login(byte[] nextChallenge = null) { //oneshot failed ProtocolTreeNode authResp = this.addAuthResponse(); - this.SendData(this.WhatsSendHandler.BinWriter.Write(authResp, false)); + this.SendData(this.BinWriter.Write(authResp, false)); this.pollMessage(); } - this.sendNickname(this.name); + this.SendAvailableForChat(this.name, this.hidden); } protected void SendData(byte[] data) @@ -298,7 +278,7 @@ protected void SendData(byte[] data) { this.whatsNetwork.SendData(data); } - catch (Exception ex) + catch (Exception) { this.Disconnect(); } @@ -312,7 +292,7 @@ protected void SendData(byte[] data) public void Message(string to, string txt) { var tmpMessage = new FMessage(GetJID(to), true) { data = txt }; - this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage, this.hidden); + this.SendMessage(tmpMessage, this.hidden); } public void SendSync(string[] numbers, string mode = "full", string context = "registration", int index = 0, bool last = true) @@ -339,7 +319,7 @@ public void SendSync(string[] numbers, string mode = "full", string context = "r users.ToArray() ) ); - this.WhatsSendHandler.SendNode(node); + this.SendNode(node); } /// @@ -413,7 +393,7 @@ public void MessageImage(string to, string filepath) { //send message FMessage msg = new FMessage(to, true) { media_wa_type = FMessage.Type.Image, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url, binary_data = this.CreateThumbnail(filepath) }; - this.WhatsSendHandler.SendMessage(msg); + this.SendMessage(msg); } } @@ -457,7 +437,7 @@ public void MessageVideo(string to, string filepath) { //send message FMessage msg = new FMessage(to, true) { media_wa_type = FMessage.Type.Video, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url, media_duration_seconds = response.duration }; - this.WhatsSendHandler.SendMessage(msg); + this.SendMessage(msg); } } @@ -509,7 +489,7 @@ public void MessageAudio(string to, string filepath) { //send message FMessage msg = new FMessage(to, true) { media_wa_type = FMessage.Type.Audio, media_mime_type = response.mimetype, media_name = response.url.Split('/').Last(), media_size = response.size, media_url = response.url, media_duration_seconds = response.duration }; - this.WhatsSendHandler.SendMessage(msg); + this.SendMessage(msg); } } @@ -567,7 +547,7 @@ private UploadResponse UploadFile(string b64hash, string type, long size, string new KeyValue("xmlns", "w:m") }, media); this.uploadResponse = null; - this.WhatsSendHandler.SendNode(node); + this.SendNode(node); int i = 0; while (this.uploadResponse == null && i <= 10) { @@ -739,7 +719,7 @@ public bool pollMessage(bool autoReceipt = true) return this.processInboundData(nodeData, autoReceipt); } } - catch (ConnectionException cex) + catch (ConnectionException) { this.Disconnect(); } @@ -747,46 +727,6 @@ public bool pollMessage(bool autoReceipt = true) return false; } - /// - /// Send a pong to the whatsapp server - /// - /// Message id - public void Pong(string msgid) - { - this.WhatsParser.WhatsSendHandler.SendPong(msgid); - } - - /// - /// Request last seen - /// - /// Jabber id - public void RequestLastSeen(string jid) - { - this.WhatsParser.WhatsSendHandler.SendQueryLastOnline(GetJID(jid)); - } - - /// - /// Send nickname - /// - /// The nickname - public void sendNickname(string nickname) - { - this.WhatsParser.WhatsSendHandler.SendAvailableForChat(nickname, this.hidden); - } - - /// - /// Send unavailable status - /// - public void sendOffline() - { - this.WhatsParser.WhatsSendHandler.SendUnavailable(); - } - - public void SetPhoto(byte[] imageBytes, byte[] thumbnailBytes) - { - this.WhatsSendHandler.SendSetPhoto(GetJID(this.phoneNumber), imageBytes, thumbnailBytes); - } - /// /// Add the authenication nodes /// @@ -830,17 +770,12 @@ protected byte[] getAuthBlob() this.outputKey.EncodeMessage(data, 0, 4, data.Length - 4); - this.WhatsSendHandler.BinWriter.Key = this.outputKey; + this.BinWriter.Key = this.outputKey; } return data; } - public void sendDeleteAccount() - { - this.WhatsSendHandler.SendDeleteAccount(); - } - /// /// Add the auth response to protocoltreenode /// @@ -852,7 +787,7 @@ protected ProtocolTreeNode addAuthResponse() byte[][] keys = KeyStream.GenerateKeys(this.encryptPassword(), this._challengeBytes); this.reader.Key = new KeyStream(keys[2], keys[3]); - this.WhatsSendHandler.BinWriter.Key = new KeyStream(keys[0], keys[1]); + this.BinWriter.Key = new KeyStream(keys[0], keys[1]); List b = new List(); b.AddRange(new byte[] { 0, 0, 0, 0 }); @@ -861,7 +796,7 @@ protected ProtocolTreeNode addAuthResponse() byte[] data = b.ToArray(); - this.WhatsSendHandler.BinWriter.Key.EncodeMessage(data, 0, 4, data.Length - 4); + this.BinWriter.Key.EncodeMessage(data, 0, 4, data.Length - 4); var node = new ProtocolTreeNode("response", new KeyValue[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") }, data); @@ -1137,7 +1072,7 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) if (node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "ping")) { - this.Pong(node.GetAttribute("id")); + this.SendPong(node.GetAttribute("id")); } if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "group")) @@ -1210,7 +1145,7 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) switch (child.tag) { case "dirty": - this.WhatsSendHandler.SendClearDirty(child.GetAttribute("type")); + this.SendClearDirty(child.GetAttribute("type")); break; case "offline": //this.SendQrSync(null); @@ -1329,7 +1264,7 @@ private void SendNotificationAck(ProtocolTreeNode node, string type = null) new KeyValue("type", type) }); ProtocolTreeNode sendNode = new ProtocolTreeNode("ack", attributes.ToArray()); - this.WhatsSendHandler.SendNode(sendNode); + this.SendNode(sendNode); } protected void SendQrSync(byte[] qrkey, byte[] token = null) @@ -1346,7 +1281,7 @@ protected void SendQrSync(byte[] qrkey, byte[] token = null) new KeyValue("id", id), new KeyValue("xmlns", "w:web") }, children.ToArray()); - this.WhatsSendHandler.SendNode(node); + this.SendNode(node); } /// @@ -1356,7 +1291,7 @@ protected void SendQrSync(byte[] qrkey, byte[] token = null) public void sendMessageReceived(ProtocolTreeNode msg, string response = "received") { FMessage tmpMessage = new FMessage(new FMessage.FMessageIdentifierKey(msg.GetAttribute("from"), true, msg.GetAttribute("id"))); - this.WhatsParser.WhatsSendHandler.SendMessageReceived(tmpMessage, response); + this.SendMessageReceived(tmpMessage, response); } /// @@ -1461,5 +1396,631 @@ internal GroupInfo(string id, string owner, long creation, string subject, long this.subjectChangedBy = subjectChangedBy; } } + + internal BinTreeNodeWriter BinWriter; + + public void SendActive() + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "active") }); + this.SendNode(node); + } + + public void SendAddParticipants(string gjid, IEnumerable participants) + { + this.SendAddParticipants(gjid, participants, null, null); + } + + public void SendAddParticipants(string gjid, IEnumerable participants, Action onSuccess, Action onError) + { + string id = TicketCounter.MakeId("add_group_participants_"); + this.SendVerbParticipants(gjid, participants, id, "add"); + } + + public void SendAvailableForChat(string nickName, bool isHidden = false) + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName) }); + this.SendNode(node); + } + + public void SendUnavailable() + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); + this.SendNode(node); + } + + public void SendClearDirty(IEnumerable categoryNames) + { + string id = TicketCounter.MakeId("clean_dirty_"); + List children = new List(); + foreach (string category in categoryNames) + { + ProtocolTreeNode cat = new ProtocolTreeNode("clean", new[] { new KeyValue("type", category) }); + children.Add(cat); + } + var node = new ProtocolTreeNode("iq", + new[] + { + new KeyValue("id", id), + new KeyValue("type", "set"), + new KeyValue("to", "s.whatsapp.net"), + new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") + }, children); + this.SendNode(node); + } + + public void SendClearDirty(string category) + { + this.SendClearDirty(new string[] { category }); + } + + public void SendClientConfig(string platform, string lg, string lc) + { + string v = TicketCounter.MakeId("config_"); + var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("lg", lg), new KeyValue("lc", lc) }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", WhatsConstants.WhatsAppRealm) }, new ProtocolTreeNode[] { child }); + this.SendNode(node); + } + + public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, bool preview, bool defaultSetting, bool groupsSetting, IEnumerable groups, Action onCompleted, Action onError) + { + string id = TicketCounter.MakeId("config_"); + var node = new ProtocolTreeNode("iq", + new[] + { + new KeyValue("id", id), new KeyValue("type", "set"), + new KeyValue("to", "") //this.Login.Domain) + }, + new ProtocolTreeNode[] + { + new ProtocolTreeNode("config", + new[] + { + new KeyValue("xmlns","urn:xmpp:whatsapp:push"), + new KeyValue("platform", platform), + new KeyValue("lg", lg), + new KeyValue("lc", lc), + new KeyValue("clear", "0"), + new KeyValue("id", pushUri.ToString()), + new KeyValue("preview",preview ? "1" : "0"), + new KeyValue("default",defaultSetting ? "1" : "0"), + new KeyValue("groups",groupsSetting ? "1" : "0") + }, + this.ProcessGroupSettings(groups)) + }); + this.SendNode(node); + } + + public void SendClose() + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); + this.SendNode(node); + } + + public void SendComposing(string to) + { + this.SendChatState(to, "composing"); + } + + protected void SendChatState(string to, string type) + { + var node = new ProtocolTreeNode("chatstate", new[] { new KeyValue("to", WhatsApp.GetJID(to)) }, new[] { + new ProtocolTreeNode(type, null) + }); + this.SendNode(node); + } + + public void SendCreateGroupChat(string subject) + { + string id = TicketCounter.MakeId("create_group_"); + var child = new ProtocolTreeNode("group", new[] { new KeyValue("action", "create"), new KeyValue("subject", subject) }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", "g.us") }, new ProtocolTreeNode[] { child }); + this.SendNode(node); + } + + public void SendDeleteAccount() + { + string id = TicketCounter.MakeId("del_acct_"); + var node = new ProtocolTreeNode("iq", + new[] + { + new KeyValue("id", id), new KeyValue("type", "get"), + new KeyValue("to", "s.whatsapp.net") + }, + new ProtocolTreeNode[] + { + new ProtocolTreeNode("remove", + new[] + { + new KeyValue("xmlns", "urn:xmpp:whatsapp:account") + }) + }); + this.SendNode(node); + } + + public void SendDeleteFromRoster(string jid) + { + string v = TicketCounter.MakeId("roster_"); + var innerChild = new ProtocolTreeNode("item", new[] { new KeyValue("jid", jid), new KeyValue("subscription", "remove") }); + var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "jabber:iq:roster") }, new ProtocolTreeNode[] { innerChild }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "set"), new KeyValue("id", v) }, new ProtocolTreeNode[] { child }); + this.SendNode(node); + } + + public void SendDeliveredReceiptAck(string to, string id) + { + this.SendReceiptAck(to, id, "delivered"); + } + + public void SendEndGroupChat(string gjid) + { + string id = TicketCounter.MakeId("remove_group_"); + var child = new ProtocolTreeNode("group", new[] { new KeyValue("action", "delete") }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] { child }); + this.SendNode(node); + } + + public void SendGetClientConfig() + { + string id = TicketCounter.MakeId("get_config_"); + var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push") }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", WhatsConstants.WhatsAppRealm) }, new ProtocolTreeNode[] { child }); + this.SendNode(node); + } + + public void SendGetDirty() + { + string id = TicketCounter.MakeId("get_dirty_"); + var child = new ProtocolTreeNode("status", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, new ProtocolTreeNode[] { child }); + this.SendNode(node); + } + + public void SendGetGroupInfo(string gjid) + { + string id = TicketCounter.MakeId("get_g_info_"); + var child = new ProtocolTreeNode("query", null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(gjid)) }, new ProtocolTreeNode[] { child }); + this.SendNode(node); + } + + public void SendGetGroups() + { + string id = TicketCounter.MakeId("get_groups_"); + this.SendGetGroups(id, "participating"); + } + + public void SendGetOwningGroups() + { + string id = TicketCounter.MakeId("get_owning_groups_"); + this.SendGetGroups(id, "owning"); + } + + public void SendGetParticipants(string gjid) + { + string id = TicketCounter.MakeId("get_participants_"); + var child = new ProtocolTreeNode("list", null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g"), new KeyValue("to", WhatsApp.GetJID(gjid)) }, child); + this.SendNode(node); + } + + public string SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat) + { + string id = TicketCounter.MakeId("get_photo_"); + var attrList = new List(); + if (!largeFormat) + { + attrList.Add(new KeyValue("type", "preview")); + } + if (expectedPhotoId != null) + { + attrList.Add(new KeyValue("id", expectedPhotoId)); + } + var child = new ProtocolTreeNode("picture", attrList.ToArray()); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:profile:picture"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(jid)) }, child); + this.SendNode(node); + return id; + } + + public void SendGetPhotoIds(IEnumerable jids) + { + string id = TicketCounter.MakeId("get_photo_id_"); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", GetJID(this.phoneNumber)) }, + new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:profile:picture") }, + (from jid in jids select new ProtocolTreeNode("user", new[] { new KeyValue("jid", jid) })).ToArray())); + this.SendNode(node); + } + + public void SendGetPrivacyList() + { + string id = TicketCounter.MakeId("privacylist_"); + var innerChild = new ProtocolTreeNode("list", new[] { new KeyValue("name", "default") }); + var child = new ProtocolTreeNode("query", new KeyValue[] { new KeyValue("xmlns", "jabber:iq:privacy") }, innerChild); + var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "get") }, child); + this.SendNode(node); + } + + public void SendGetServerProperties() + { + string id = TicketCounter.MakeId("get_server_properties_"); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, + new ProtocolTreeNode("props", new[] { new KeyValue("xmlns", "w") })); + this.SendNode(node); + } + + public void SendGetStatus(string jid) + { + int index = jid.IndexOf('@'); + if (index > 0) + { + jid = string.Format("{0}@{1}", jid.Substring(0, index), "s.us"); + string v = TicketManager.GenerateId(); + var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "action"), new KeyValue("id", v) }, + new ProtocolTreeNode("action", new[] { new KeyValue("type", "get") })); + this.SendNode(node); + } + } + + public void SendInactive() + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "inactive") }); + this.SendNode(node); + } + + public void SendLeaveGroup(string gjid) + { + this.SendLeaveGroups(new string[] { gjid }); + } + + public void SendLeaveGroups(IEnumerable gjids) + { + string id = TicketCounter.MakeId("leave_group_"); + IEnumerable innerChilds = from gjid in gjids select new ProtocolTreeNode("group", new[] { new KeyValue("id", gjid) }); + var child = new ProtocolTreeNode("leave", null, innerChilds); + var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", "g.us") }, child); + this.SendNode(node); + } + + public void SendMessage(FMessage message, bool hidden = false) + { + if (message.media_wa_type != FMessage.Type.Undefined) + { + this.SendMessageWithMedia(message); + } + else + { + this.SendMessageWithBody(message, hidden); + } + } + + //overload + public void SendMessageBroadcast(string[] to, string message) + { + this.SendMessageBroadcast(to, new FMessage(string.Empty, true) { data = message, media_wa_type = FMessage.Type.Undefined }); + } + + //overload + public void SendMessageBroadcast(string to, FMessage message) + { + this.SendMessageBroadcast(new string[] { to }, message); + } + + //overload + public void SendMessageBroadcast(string to, string message) + { + this.SendMessageBroadcast(new string[] { to }, new FMessage(string.Empty, true) { data = message, media_wa_type = FMessage.Type.Undefined }); + } + + //send broadcast + public void SendMessageBroadcast(string[] to, FMessage message) + { + if (to != null && to.Length > 0 && message != null && !string.IsNullOrEmpty(message.data)) + { + ProtocolTreeNode child; + if (message.media_wa_type == FMessage.Type.Undefined) + { + //text broadcast + child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); + } + else + { + throw new NotImplementedException(); + } + + //compose broadcast envelope + ProtocolTreeNode xnode = new ProtocolTreeNode("x", new KeyValue[] { + new KeyValue("xmlns", "jabber:x:event") + }, new ProtocolTreeNode("server", null)); + List toNodes = new List(); + foreach (string target in to) + { + toNodes.Add(new ProtocolTreeNode("to", new KeyValue[] { new KeyValue("jid", WhatsAppApi.WhatsApp.GetJID(target)) })); + } + + ProtocolTreeNode broadcastNode = new ProtocolTreeNode("broadcast", null, toNodes); + ProtocolTreeNode messageNode = new ProtocolTreeNode("message", new KeyValue[] { + new KeyValue("to", "broadcast"), + new KeyValue("type", "chat"), + new KeyValue("id", message.identifier_key.id) + }, new ProtocolTreeNode[] { + broadcastNode, + xnode, + child + }); + this.SendNode(messageNode); + } + } + + public void SendMessageReceived(FMessage message, string response) + { + ProtocolTreeNode node = new ProtocolTreeNode("receipt", new[] { + new KeyValue("to", message.identifier_key.remote_jid), + new KeyValue("id", message.identifier_key.id) + }); + + this.SendNode(node); + } + + public void SendNop() + { + this.SendNode(null); + } + + public void SendNotificationReceived(string jid, string id) + { + var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); + var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "notification"), new KeyValue("id", id) }, child); + this.SendNode(node); + } + + public void SendPaused(string to) + { + this.SendChatState(to, "paused"); + } + + public void SendPing() + { + string id = TicketCounter.MakeId("ping_"); + var child = new ProtocolTreeNode("ping", new[] { new KeyValue("xmlns", "w:p") }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get") }, child); + this.SendNode(node); + } + + public void SendPong(string id) + { + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "result"), new KeyValue("to", WhatsConstants.WhatsAppRealm), new KeyValue("id", id) }); + this.SendNode(node); + } + + public void SendPresenceSubscriptionRequest(string to) + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "subscribe"), new KeyValue("to", GetJID(to)) }); + this.SendNode(node); + } + + public void SendQueryLastOnline(string jid) + { + string id = TicketCounter.MakeId("last_"); + var child = new ProtocolTreeNode("query", null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid), new KeyValue("xmlns", "jabber:iq:last") }, child); + this.SendNode(node); + } + + public void SendRelayCapable(string platform, bool value) + { + string v = TicketCounter.MakeId("relay_"); + var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("relay", value ? "1" : "0") }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", WhatsConstants.WhatsAppRealm) }, child); + this.SendNode(node); + } + + public void SendRelayComplete(string id, int millis) + { + var child = new ProtocolTreeNode("relay", new[] { new KeyValue("elapsed", millis.ToString(CultureInfo.InvariantCulture)) }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "result"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); + this.SendNode(node); + } + + public void SendRelayTimeout(string id) + { + var innerChild = new ProtocolTreeNode("remote-server-timeout", new[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas") }); + var child = new ProtocolTreeNode("error", new[] { new KeyValue("code", "504"), new KeyValue("type", "wait") }, innerChild); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "error"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); + this.SendNode(node); + } + + public void SendRemoveParticipants(string gjid, List participants) + { + string id = TicketCounter.MakeId("remove_group_participants_"); + this.SendVerbParticipants(gjid, participants, id, "remove"); + } + + public void SendSetGroupSubject(string gjid, string subject) + { + string id = TicketCounter.MakeId("set_group_subject_"); + var child = new ProtocolTreeNode("subject", new[] { new KeyValue("value", subject) }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", gjid) }, child); + this.SendNode(node); + } + + public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes) + { + string id = TicketCounter.MakeId("set_photo_"); + var list = new List { new ProtocolTreeNode("picture", null, null, bytes) }; + if (thumbnailBytes != null) + { + list.Add(new ProtocolTreeNode("picture", new[] { new KeyValue("type", "preview") }, null, thumbnailBytes)); + } + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:profile:picture"), new KeyValue("to", jid) }, list.ToArray()); + this.SendNode(node); + } + + public void SendSetPrivacyBlockedList(IEnumerable jidSet) + { + string id = TicketCounter.MakeId("privacy_"); + ProtocolTreeNode[] nodeArray = Enumerable.Select(jidSet, (Func)((jid, index) => new ProtocolTreeNode("item", new KeyValue[] { new KeyValue("type", "jid"), new KeyValue("value", jid), new KeyValue("action", "deny"), new KeyValue("order", index.ToString(CultureInfo.InvariantCulture)) }))).ToArray(); + var child = new ProtocolTreeNode("list", new KeyValue[] { new KeyValue("name", "default") }, (nodeArray.Length == 0) ? null : nodeArray); + var node2 = new ProtocolTreeNode("query", new KeyValue[] { new KeyValue("xmlns", "jabber:iq:privacy") }, child); + var node3 = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set") }, node2); + this.SendNode(node3); + } + + public void SendStatusUpdate(string status) + { + string id = TicketManager.GenerateId(); + FMessage message = new FMessage(new FMessage.FMessageIdentifierKey("s.us", true, id)); + var messageNode = GetMessageNode(message, new ProtocolTreeNode("body", null, WhatsApp.SYSEncoding.GetBytes(status))); + this.SendNode(messageNode); + } + + public void SendSubjectReceived(string to, string id) + { + var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); + var node = GetSubjectMessage(to, id, child); + this.SendNode(node); + } + + public void SendUnsubscribeHim(string jid) + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribed"), new KeyValue("to", jid) }); + this.SendNode(node); + } + + public void SendUnsubscribeMe(string jid) + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribe"), new KeyValue("to", jid) }); + this.SendNode(node); + } + + public void SendVisibleReceiptAck(string to, string id) + { + this.SendReceiptAck(to, id, "visible"); + } + + internal void SendGetGroups(string id, string type) + { + var child = new ProtocolTreeNode("list", new[] { new KeyValue("type", type) }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g"), new KeyValue("to", "g.us") }, child); + this.SendNode(node); + } + + internal void SendMessageWithBody(FMessage message, bool hidden = false) + { + var child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); + this.SendNode(GetMessageNode(message, child, hidden)); + } + + internal void SendMessageWithMedia(FMessage message) + { + ProtocolTreeNode node; + if (FMessage.Type.System == message.media_wa_type) + { + throw new SystemException("Cannot send system message over the network"); + } + + List list = new List(new KeyValue[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:mms"), new KeyValue("type", FMessage.GetMessage_WA_Type_StrValue(message.media_wa_type)) }); + if (FMessage.Type.Location == message.media_wa_type) + { + list.AddRange(new KeyValue[] { new KeyValue("latitude", message.latitude.ToString(CultureInfo.InvariantCulture)), new KeyValue("longitude", message.longitude.ToString(CultureInfo.InvariantCulture)) }); + if (message.location_details != null) + { + list.Add(new KeyValue("name", message.location_details)); + } + if (message.location_url != null) + { + list.Add(new KeyValue("url", message.location_url)); + } + } + else if (((FMessage.Type.Contact != message.media_wa_type) && (message.media_name != null)) && ((message.media_url != null) && (message.media_size > 0L))) + { + list.AddRange(new KeyValue[] { new KeyValue("file", message.media_name), new KeyValue("size", message.media_size.ToString(CultureInfo.InvariantCulture)), new KeyValue("url", message.media_url) }); + if (message.media_duration_seconds > 0) + { + list.Add(new KeyValue("seconds", message.media_duration_seconds.ToString(CultureInfo.InvariantCulture))); + } + } + if ((FMessage.Type.Contact == message.media_wa_type) && (message.media_name != null)) + { + node = new ProtocolTreeNode("media", list.ToArray(), new ProtocolTreeNode("vcard", new KeyValue[] { new KeyValue("name", message.media_name) }, WhatsApp.SYSEncoding.GetBytes(message.data))); + } + else + { + byte[] data = message.binary_data; + if ((data == null) && !string.IsNullOrEmpty(message.data)) + { + try + { + data = Convert.FromBase64String(message.data); + } + catch (Exception) + { + } + } + if (data != null) + { + list.Add(new KeyValue("encoding", "raw")); + } + node = new ProtocolTreeNode("media", list.ToArray(), null, data); + } + this.SendNode(GetMessageNode(message, node)); + } + + internal void SendVerbParticipants(string gjid, IEnumerable participants, string id, string inner_tag) + { + IEnumerable source = from jid in participants select new ProtocolTreeNode("participant", new[] { new KeyValue("jid", jid) }); + var child = new ProtocolTreeNode(inner_tag, null, source); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", gjid) }, child); + this.SendNode(node); + } + + public void SendNode(ProtocolTreeNode node) + { + this.SendData(this.BinWriter.Write(node)); + } + + private IEnumerable ProcessGroupSettings(IEnumerable groups) + { + ProtocolTreeNode[] nodeArray = null; + if ((groups != null) && groups.Any()) + { + DateTime now = DateTime.Now; + nodeArray = (from @group in groups + select new ProtocolTreeNode("item", new[] + { new KeyValue("jid", @group.Jid), + new KeyValue("notify", @group.Enabled ? "1" : "0"), + new KeyValue("mute", string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", new object[] { (!@group.MuteExpiry.HasValue || (@group.MuteExpiry.Value <= now)) ? 0 : ((int) (@group.MuteExpiry.Value - now).TotalSeconds) })) })).ToArray(); + } + return nodeArray; + } + + private void SendReceiptAck(string to, string id, string receiptType) + { + var tmpChild = new ProtocolTreeNode("ack", new[] { new KeyValue("xmlns", "urn:xmpp:receipts"), new KeyValue("type", receiptType) }); + var resultNode = new ProtocolTreeNode("message", new[] + { + new KeyValue("to", to), + new KeyValue("type", "chat"), + new KeyValue("id", id) + }, tmpChild); + this.SendNode(resultNode); + } + + internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode, bool hidden = false) + { + return new ProtocolTreeNode("message", new[] { + new KeyValue("to", message.identifier_key.remote_jid), + new KeyValue("type", message.media_wa_type == FMessage.Type.Undefined?"text":"media"), + new KeyValue("id", message.identifier_key.id) + }, + new ProtocolTreeNode[] { + new ProtocolTreeNode("x", new KeyValue[] { new KeyValue("xmlns", "jabber:x:event") }, new ProtocolTreeNode("server", null)), + pNode, + new ProtocolTreeNode("offline", null) + }); + } + + public static ProtocolTreeNode GetSubjectMessage(string to, string id, ProtocolTreeNode child) + { + return new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "subject"), new KeyValue("id", id) }, child); + } } } From 835b9e6229ceed50fbf8fe7e7a6d5e91a10ba66a Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 17:20:22 +0200 Subject: [PATCH 187/271] Removed unused property in FMessage --- WhatsAppApi/Parser/FMessage.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/WhatsAppApi/Parser/FMessage.cs b/WhatsAppApi/Parser/FMessage.cs index 67365fc..3d5351e 100644 --- a/WhatsAppApi/Parser/FMessage.cs +++ b/WhatsAppApi/Parser/FMessage.cs @@ -183,7 +183,6 @@ public class Builder internal string thumb_image; internal DateTime? timestamp; internal bool? wants_receipt; - internal string serverNickname; public byte[] BinaryData() { @@ -205,7 +204,6 @@ public FMessage Build() if (((this.remote_jid != null) && this.from_me.HasValue) && (this.id != null)) { this.message.identifier_key = new FMessage.FMessageIdentifierKey(this.remote_jid, this.from_me.Value, this.id); - this.message.identifier_key.serverNickname = this.serverNickname; } if (this.remote_resource != null) { From 2a5892efb7361e498706d30d7f04a6d9a66550ab Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 17:20:41 +0200 Subject: [PATCH 188/271] Removed unused classes --- WhatsAppApi/Response/MessageRecvResponse.cs | 331 ------ WhatsAppApi/Response/WhatsParser.cs | 162 --- WhatsAppApi/WhatsAppApi.csproj | 3 - WhatsAppApi/WhatsSendHandler.cs | 1007 ------------------- 4 files changed, 1503 deletions(-) delete mode 100644 WhatsAppApi/Response/MessageRecvResponse.cs delete mode 100644 WhatsAppApi/Response/WhatsParser.cs delete mode 100644 WhatsAppApi/WhatsSendHandler.cs diff --git a/WhatsAppApi/Response/MessageRecvResponse.cs b/WhatsAppApi/Response/MessageRecvResponse.cs deleted file mode 100644 index a588349..0000000 --- a/WhatsAppApi/Response/MessageRecvResponse.cs +++ /dev/null @@ -1,331 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using WhatsAppApi.Helper; -using WhatsAppApi.Parser; -using WhatsAppApi.Settings; - -namespace WhatsAppApi.Response -{ - /// - /// Respond to a recieved message - /// - class MessageRecvResponse - { - /// - /// An instance of the WhatsSendHandler class - /// - private WhatsSendHandler sendHandler; - - /// - /// Default class constructor - /// - /// An instance of the WhatsSendHandler class - public MessageRecvResponse(WhatsSendHandler sendHandler) - { - this.sendHandler = sendHandler; - } - - /// - /// Parse recieved message - /// - /// TreeNode that contains the recieved message - public void ParseMessageRecv(ProtocolTreeNode messageNode) - { - FMessage.Builder builder = new FMessage.Builder(); - string tmpAttrbId = messageNode.GetAttribute("id"); - string tmpAttrFrom = messageNode.GetAttribute("from"); - string tmpAttrFromName = messageNode.GetAttribute(""); - string tmpAttrFromJid = messageNode.GetAttribute("author") ?? ""; - string tmpAttrType = messageNode.GetAttribute("type"); - string tmpTAttribT = messageNode.GetAttribute("t"); - - long result = 0L; - if (!string.IsNullOrEmpty(tmpTAttribT) && long.TryParse(tmpTAttribT, out result)) - { - builder.Timestamp(new DateTime?(WhatsConstants.UnixEpoch.AddSeconds((double)result))); - } - - if ("error".Equals(tmpAttrType)) - { - TypeError(messageNode, tmpAttrbId, tmpAttrFrom); - } - else if ("subject".Equals(tmpAttrType)) - { - TypeSubject(messageNode, tmpAttrFrom, tmpAttrFromJid, tmpAttrbId, tmpTAttribT); - } - else if ("chat".Equals(tmpAttrType)) - { - TypeChat(messageNode, tmpAttrFrom, tmpAttrbId, builder, tmpAttrFromJid); - } - else if ("notification".Equals(tmpAttrType)) - { - TypeNotification(messageNode, tmpAttrFrom, tmpAttrbId); - } - } - - /// - /// Notify typing - /// - /// The protocoltreenode - /// From? - /// Message id - private void TypeNotification(ProtocolTreeNode messageNode, string tmpAttrFrom, string tmpAttrbId) - { - foreach (ProtocolTreeNode tmpChild in (messageNode.GetAllChildren() ?? new ProtocolTreeNode[0])) - { - if (ProtocolTreeNode.TagEquals(tmpChild, "notification")) - { - string tmpChildType = tmpChild.GetAttribute("type"); - if (StringComparer.Ordinal.Equals(tmpChildType, "picture")) - { - TypeNotificationPicture(tmpChild, tmpAttrFrom); - } - } - else if (ProtocolTreeNode.TagEquals(tmpChild, "request")) - { - this.sendHandler.SendNotificationReceived(tmpAttrFrom, tmpAttrbId); - } - } - } - - /// - /// Notify typing picture - /// - /// Child - /// From? - private static void TypeNotificationPicture(ProtocolTreeNode tmpChild, string tmpFrom) - { - foreach (ProtocolTreeNode item in (tmpChild.GetAllChildren() ?? new ProtocolTreeNode[0])) - { - if (ProtocolTreeNode.TagEquals(item, "set")) - { - string photoId = item.GetAttribute("id"); - if (photoId != null) - { - WhatsEventHandler.OnPhotoChangedEventHandler(tmpFrom, item.GetAttribute("author"), photoId); - } - } - else if (ProtocolTreeNode.TagEquals(item, "delete")) - { - WhatsEventHandler.OnPhotoChangedEventHandler(tmpFrom, item.GetAttribute("author"), null); - } - } - } - - /// - /// Notify typing chat - /// - /// - /// - /// - /// - /// - private void TypeChat(ProtocolTreeNode messageNode, string tmpAttrFrom, string tmpAttrbId, FMessage.Builder builder, string tmpAttrFromJid) - { - foreach (ProtocolTreeNode itemNode in (messageNode.GetAllChildren() ?? new ProtocolTreeNode[0])) - { - if (ProtocolTreeNode.TagEquals(itemNode, "composing")) - { - WhatsEventHandler.OnIsTypingEventHandler(tmpAttrFrom, true); - } - else if (ProtocolTreeNode.TagEquals(itemNode, "paused")) - { - WhatsEventHandler.OnIsTypingEventHandler(tmpAttrFrom, false); - } - else if (ProtocolTreeNode.TagEquals(itemNode, "body") && (tmpAttrbId != null)) - { - string dataString = WhatsApp.SYSEncoding.GetString(itemNode.GetData()); - var tmpMessKey = new FMessage.FMessageIdentifierKey(tmpAttrFrom, false, tmpAttrbId); - builder.Key(tmpMessKey).Remote_resource(tmpAttrFromJid).NewIncomingInstance().Data(dataString); - } - else if (ProtocolTreeNode.TagEquals(itemNode, "media") && (tmpAttrbId != null)) - { - long tmpMediaSize; - int tmpMediaDuration; - - builder.Media_wa_type(FMessage.GetMessage_WA_Type(itemNode.GetAttribute("type"))).Media_url( - itemNode.GetAttribute("url")).Media_name(itemNode.GetAttribute("file")); - - if (long.TryParse(itemNode.GetAttribute("size"), WhatsConstants.WhatsAppNumberStyle, - CultureInfo.InvariantCulture, out tmpMediaSize)) - { - builder.Media_size(tmpMediaSize); - } - string tmpAttrSeconds = itemNode.GetAttribute("seconds"); - if ((tmpAttrSeconds != null) && - int.TryParse(tmpAttrSeconds, WhatsConstants.WhatsAppNumberStyle, CultureInfo.InvariantCulture, out tmpMediaDuration)) - { - builder.Media_duration_seconds(tmpMediaDuration); - } - - if (builder.Media_wa_type().HasValue && (builder.Media_wa_type().Value == FMessage.Type.Location)) - { - double tmpLatitude = 0; - double tmpLongitude = 0; - string tmpAttrLatitude = itemNode.GetAttribute("latitude"); - string tmpAttrLongitude = itemNode.GetAttribute("longitude"); - if ((tmpAttrLatitude == null) || (tmpAttrLongitude == null)) - { - tmpAttrLatitude = "0"; - tmpAttrLongitude = "0"; - } - else if (!double.TryParse(tmpAttrLatitude, WhatsConstants.WhatsAppNumberStyle, CultureInfo.InvariantCulture, out tmpLatitude) || - !double.TryParse(tmpAttrLongitude, WhatsConstants.WhatsAppNumberStyle, CultureInfo.InvariantCulture, out tmpLongitude)) - { - throw new CorruptStreamException("location message exception parsing lat or long attribute: " + tmpAttrLatitude + " " + tmpAttrLongitude); - } - - builder.Latitude(tmpLatitude).Longitude(tmpLongitude); - - string tmpAttrName = itemNode.GetAttribute("name"); - string tmpAttrUrl = itemNode.GetAttribute("url"); - if (tmpAttrName != null) - { - builder.Location_details(tmpAttrName); - } - if (tmpAttrUrl != null) - { - builder.Location_url(tmpAttrUrl); - } - } - - if (builder.Media_wa_type().HasValue && (builder.Media_wa_type().Value) == FMessage.Type.Contact) - { - ProtocolTreeNode tmpChildMedia = itemNode.GetChild("media"); - if (tmpChildMedia != null) - { - builder.Media_name(tmpChildMedia.GetAttribute("name")).Data(WhatsApp.SYSEncoding.GetString(tmpChildMedia.GetData())); - } - } - else - { - string tmpAttrEncoding = itemNode.GetAttribute("encoding") ?? "text"; - if (tmpAttrEncoding == "text") - { - builder.Data(WhatsApp.SYSEncoding.GetString(itemNode.GetData())); - } - } - var tmpMessageKey = new FMessage.FMessageIdentifierKey(tmpAttrFrom, false, tmpAttrbId); - builder.Key(tmpMessageKey).Remote_resource(tmpAttrFromJid).NewIncomingInstance(); - } - else if (ProtocolTreeNode.TagEquals(itemNode, "request")) - { - builder.Wants_receipt(true); - } - else if (ProtocolTreeNode.TagEquals(itemNode, "x")) - { - string str16 = itemNode.GetAttribute("xmlns"); - if ("jabber:x:event".Equals(str16) && (tmpAttrbId != null)) - { - var tmpMessageKey = new FMessage.FMessageIdentifierKey(tmpAttrFrom, true, tmpAttrbId); - } - } - else if (ProtocolTreeNode.TagEquals(itemNode, "received")) - { - if (tmpAttrbId != null) - { - var tmpMessageKey = new FMessage.FMessageIdentifierKey(tmpAttrFrom, true, tmpAttrbId); - if (true) - { - string tmpAttrType = itemNode.GetAttribute("type"); - if ((tmpAttrType != null) && !tmpAttrType.Equals("delivered")) - { - if (tmpAttrType.Equals("visible")) - { - this.sendHandler.SendVisibleReceiptAck(tmpAttrFrom, tmpAttrbId); - } - } - else - { - this.sendHandler.SendDeliveredReceiptAck(tmpAttrFrom, tmpAttrbId); - } - } - } - } - else if (ProtocolTreeNode.TagEquals(itemNode, "offline")) - { - builder.Offline(true); - } - else if (ProtocolTreeNode.TagEquals(itemNode, "notify")) - { - var tmpAttrName = itemNode.GetAttribute("name"); - if (tmpAttrName != null) - { - builder.from_me = false; - builder.id = tmpAttrbId; - builder.remote_jid = tmpAttrFromJid; - builder.serverNickname = tmpAttrName; - } - } - } - if (!builder.Timestamp().HasValue) - { - builder.Timestamp(new DateTime?(DateTime.Now)); - } - FMessage message = builder.Build(); - if (message != null) - { - WhatsEventHandler.OnMessageRecievedEventHandler(message); - } - } - - /// - /// Type subject - /// - /// - /// - /// - /// - /// - private void TypeSubject(ProtocolTreeNode messageNode, string tmpFrom, string uJid, string tmpId, string tmpT) - { - bool flag = false; - foreach (ProtocolTreeNode item in messageNode.GetAllChildren("request")) - { - if (item.GetAttribute("xmlns").Equals("urn:xmpp:receipts")) - { - flag = true; - } - } - ProtocolTreeNode child = messageNode.GetChild("body"); - string subject = (child == null) ? null : WhatsApp.SYSEncoding.GetString(child.GetData()); - if (subject != null) - { - WhatsEventHandler.OnGroupNewSubjectEventHandler(tmpFrom, uJid, subject, int.Parse(tmpT, CultureInfo.InvariantCulture)); - } - if (flag) - { - this.sendHandler.SendSubjectReceived(tmpFrom, tmpId); - } - } - - /// - /// Type error - /// - /// - /// - /// - private void TypeError(ProtocolTreeNode messageNode, string tmpAttrbId, string tmpAttrFrom) - { - int num2 = 0; - foreach (ProtocolTreeNode node in messageNode.GetAllChildren("error")) - { - string tmpCode = node.GetAttribute("code"); - try - { - num2 = int.Parse(tmpCode, CultureInfo.InvariantCulture); - } - catch (Exception) - { - } - } - if ((tmpAttrFrom != null) && (tmpAttrbId != null)) - { - FMessage.FMessageIdentifierKey key = new FMessage.FMessageIdentifierKey(tmpAttrFrom, true, tmpAttrbId); - } - } - } -} diff --git a/WhatsAppApi/Response/WhatsParser.cs b/WhatsAppApi/Response/WhatsParser.cs deleted file mode 100644 index 8614c08..0000000 --- a/WhatsAppApi/Response/WhatsParser.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using WhatsAppApi.Helper; -using WhatsAppApi.Settings; - -namespace WhatsAppApi.Response -{ - /// - /// Parses whatsapp messages - /// - public class WhatsParser - { - /// - /// An instance of the WhatsSendHandler class - /// - public WhatsSendHandler WhatsSendHandler { get; private set; } - - /// - /// An instnce of the WhatsNetwork class - /// - private WhatsNetwork whatsNetwork; - - /// - /// An instance of the MessageRecvResponse class - /// - private MessageRecvResponse messResponseHandler; - - - /// - /// An instance of the Binary Tree node writer class - /// - private BinTreeNodeWriter _binWriter; - - /// - /// Default constructor - /// - /// An instance of the WhatsNetwork class - /// An instance of the BinTreeNodeWriter class - internal WhatsParser(WhatsNetwork whatsNet, BinTreeNodeWriter writer) - { - this.WhatsSendHandler = new WhatsSendHandler(whatsNet, writer); - this.whatsNetwork = whatsNet; - this.messResponseHandler = new MessageRecvResponse(this.WhatsSendHandler); - this._binWriter = writer; - } - - /// - /// Parse a tree node - /// - /// An instance of the ProtocolTreeNode class that needs to be parsed. - public void ParseProtocolNode(ProtocolTreeNode protNode) - { - if (ProtocolTreeNode.TagEquals(protNode, "iq")) - { - string attributeValue = protNode.GetAttribute("type"); - string id = protNode.GetAttribute("id"); - string str3 = protNode.GetAttribute("from"); - if (attributeValue == null) - { - throw new Exception("Message-Corrupt: missing 'type' attribute in iq stanza"); - } - if (!attributeValue.Equals("result")) - { - if (!attributeValue.Equals("get")) - { - if (!attributeValue.Equals("set")) - { - throw new Exception("Message-Corrupt: unknown iq type attribute: " + attributeValue); - } - ProtocolTreeNode child = protNode.GetChild("query"); - if (child != null) - { - string str8 = child.GetAttribute("xmlns"); - if ("jabber:iq:roster" == str8) - { - foreach (ProtocolTreeNode node5 in child.GetAllChildren("item")) - { - node5.GetAttribute("jid"); - node5.GetAttribute("subscription"); - node5.GetAttribute("ask"); - } - } - } - } - else - { - ProtocolTreeNode node3 = protNode.GetChild("ping"); - if ((!ProtocolTreeNode.TagEquals(node3, "query") || (str3 == null)) && (ProtocolTreeNode.TagEquals(node3, "relay") && (str3 != null))) - { - int num; - string pin = node3.GetAttribute("pin"); - string tmpTimeout = node3.GetAttribute("timeout"); - if ( !int.TryParse(tmpTimeout ?? "0", WhatsConstants.WhatsAppNumberStyle, CultureInfo.InvariantCulture, out num)) - { - throw new CorruptStreamException("relay-iq exception parsing timeout attribute: " + tmpTimeout); - } - } - } - } - } - else if (ProtocolTreeNode.TagEquals(protNode, "presence")) - { - string str9 = protNode.GetAttribute("xmlns"); - string jid = protNode.GetAttribute("from"); - if (((str9 == null) || "urn:xmpp".Equals(str9)) && (jid != null)) - { - string str11 = protNode.GetAttribute("type"); - } - else if ("w".Equals(str9) && (jid != null)) - { - string str12 = protNode.GetAttribute("add"); - string str13 = protNode.GetAttribute("remove"); - string str14 = protNode.GetAttribute("status"); - if (str12 == null) - { - if (str13 == null) - { - if ("dirty".Equals(str14)) - { - Dictionary categories = ParseCategories(protNode); - } - } - } - } - } - else if (ProtocolTreeNode.TagEquals(protNode, "message")) - { - this.messResponseHandler.ParseMessageRecv(protNode); - } - } - - /// - /// Parse categories - /// - /// An instance of the ProtocolTreeNode class - /// A dictionary with the categories used - internal static Dictionary ParseCategories(ProtocolTreeNode dirtyNode) - { - var dictionary = new Dictionary(); - if (dirtyNode.children != null) - { - for (int i = 0; i < dirtyNode.children.Count(); i++) - { - ProtocolTreeNode node = dirtyNode.children.ElementAt(i); - if (ProtocolTreeNode.TagEquals(node, "category")) - { - long num2; - string attributeValue = node.GetAttribute("name"); - if (long.TryParse(node.GetAttribute("timestamp"), WhatsConstants.WhatsAppNumberStyle, - CultureInfo.InvariantCulture, out num2)) - { - dictionary[attributeValue] = num2; - } - } - } - } - return dictionary; - } - } -} diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index 6c18d53..3d477bd 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -75,14 +75,11 @@ - - - diff --git a/WhatsAppApi/WhatsSendHandler.cs b/WhatsAppApi/WhatsSendHandler.cs deleted file mode 100644 index cd6a945..0000000 --- a/WhatsAppApi/WhatsSendHandler.cs +++ /dev/null @@ -1,1007 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using WhatsAppApi.Helper; -using WhatsAppApi.Parser; - -namespace WhatsAppApi -{ - /// - /// Handles sending messages to the Whatsapp servers - /// - public class WhatsSendHandler - { - /// - /// Holds the jabber id that is used to authenticate - /// - private string MyJID = ""; - - /// - /// The whatsapp realm, defined in WhatsConstants. - /// - private string whatsAppRealm = WhatsAppApi.Settings.WhatsConstants.WhatsAppRealm; - - /// - /// Holds an instance of the BinTreeNodeWriter - /// - internal BinTreeNodeWriter BinWriter; - - /// - /// Holds an instance of the WhatsNetwork class - /// - private WhatsNetwork whatsNetwork; - - /// - /// Default class constructor - /// - /// An instance of the WhatsNetwork class - /// An instance of the BinTreeNodeWriter - internal WhatsSendHandler(WhatsNetwork net, BinTreeNodeWriter writer) - { - this.whatsNetwork = net; - this.BinWriter = writer; - } - - /// - /// Sends a message to the Whatsapp server to tell that the user is 'online' / 'active' - /// - public void SendActive() - { - var node = new ProtocolTreeNode("presence", new[] {new KeyValue("type", "active")}); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Sends a request to the server to add participants to a group chat - /// - /// Group jabber id - /// A list of participants (List of jids) - public void SendAddParticipants(string gjid, IEnumerable participants) - { - this.SendAddParticipants(gjid, participants, null, null); - } - - /// - /// Sends a request to the server to add participants to a group chat - /// - /// Group jabber id - /// A list of participants (List of jids) - /// The action to be executed when the request is successfull. - /// The action to be executed when the request fails - public void SendAddParticipants(string gjid, IEnumerable participants, Action onSuccess, Action onError) - { - string id = TicketCounter.MakeId("add_group_participants_"); - this.SendVerbParticipants(gjid, participants, id, "add"); - } - - public void SendAvailableForChat(string nickName, bool isHidden = false) - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName) }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - public void SendUnavailable() - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - public void SendClearDirty(IEnumerable categoryNames) - { - string id = TicketCounter.MakeId("clean_dirty_"); - List children = new List(); - foreach (string category in categoryNames) - { - ProtocolTreeNode cat = new ProtocolTreeNode("clean", new[] { new KeyValue("type", category) }); - children.Add(cat); - } - var node = new ProtocolTreeNode("iq", - new[] - { - new KeyValue("id", id), - new KeyValue("type", "set"), - new KeyValue("to", "s.whatsapp.net"), - new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") - }, children); - this.SendNode(node); - } - - public void SendClearDirty(string category) - { - this.SendClearDirty(new string[] { category }); - } - - /// - /// Sends the client configuration to the Whatsapp server. - /// - /// The string identifying the client. - /// ? - /// ? - public void SendClientConfig(string platform, string lg, string lc) - { - string v = TicketCounter.MakeId("config_"); - var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("lg", lg), new KeyValue("lc", lc) }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", whatsAppRealm) }, new ProtocolTreeNode[] { child }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Sends the client configuration to the Whatsapp server. - /// - /// The string identifying the client. - /// ? - /// ? - /// ? - /// ? - /// Default settings. - /// Settings regarding groups. - /// A list of groups - /// Action to be executed when the request was successfull. - /// Action to be executed when the request failed. - public void SendClientConfig(string platform, string lg, string lc, Uri pushUri, bool preview, bool defaultSetting, bool groupsSetting, IEnumerable groups, Action onCompleted, Action onError) - { - string id = TicketCounter.MakeId("config_"); - var node = new ProtocolTreeNode("iq", - new[] - { - new KeyValue("id", id), new KeyValue("type", "set"), - new KeyValue("to", "") //this.Login.Domain) - }, - new ProtocolTreeNode[] - { - new ProtocolTreeNode("config", - new[] - { - new KeyValue("xmlns","urn:xmpp:whatsapp:push"), - new KeyValue("platform", platform), - new KeyValue("lg", lg), - new KeyValue("lc", lc), - new KeyValue("clear", "0"), - new KeyValue("id", pushUri.ToString()), - new KeyValue("preview",preview ? "1" : "0"), - new KeyValue("default",defaultSetting ? "1" : "0"), - new KeyValue("groups",groupsSetting ? "1" : "0") - }, - this.ProcessGroupSettings(groups)) - }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Change status to 'Offline' - /// - public void SendClose() - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Send the status of 'writing'/'typing'/'composing' to the Whatsapp server - /// - /// The recipient, the one the client is talking to. - public void SendComposing(string to) - { - this.SendChatState(to, "composing"); - } - - protected void SendChatState(string to, string type) - { - var node = new ProtocolTreeNode("chatstate", new[] { new KeyValue("to", WhatsApp.GetJID(to)) }, new [] { - new ProtocolTreeNode(type, null) - }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Requests to create a new and empty group chat. - /// - /// The subject of the group chat. - public void SendCreateGroupChat(string subject) - { - this.SendCreateGroupChat(subject, null, null); - } - - /// - /// Requests to create a new and empty group chat. - /// - /// The subjecct of the group chat. - /// Acction to be executed when the request was successful. - /// Action to be executed when the request failed. - public void SendCreateGroupChat(string subject, Action onSuccess, Action onError) - { - string id = TicketCounter.MakeId("create_group_"); - var child = new ProtocolTreeNode("group", new[] { new KeyValue("action", "create"), new KeyValue("subject", subject) }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", "g.us") }, new ProtocolTreeNode[] { child }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Make a reques to the Whatsapp servers to delete a specific account. - /// - /// The action to be executed when the request was successful. - /// The action to be executed when the request failed. - public void SendDeleteAccount() - { - string id = TicketCounter.MakeId("del_acct_"); - var node = new ProtocolTreeNode("iq", - new[] - { - new KeyValue("id", id), new KeyValue("type", "get"), - new KeyValue("to", "s.whatsapp.net") - }, - new ProtocolTreeNode[] - { - new ProtocolTreeNode("remove", - new[] - { - new KeyValue("xmlns", "urn:xmpp:whatsapp:account") - }) - }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Request to be delete from a group chat. - /// - /// - public void SendDeleteFromRoster(string jid) - { - string v = TicketCounter.MakeId("roster_"); - var innerChild = new ProtocolTreeNode("item", new[] { new KeyValue("jid", jid), new KeyValue("subscription", "remove") }); - var child = new ProtocolTreeNode("query", new[] { new KeyValue("xmlns", "jabber:iq:roster") }, new ProtocolTreeNode[] {innerChild}); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "set"), new KeyValue("id", v) }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Sends the 'Delivered' status to a specific client - /// - /// The JID of the person the chat is with. - /// The message id. - public void SendDeliveredReceiptAck(string to, string id) - { - this.SendReceiptAck(to, id, "delivered"); - } - - /// - /// Sends a request to end and remove the group chat. - /// - /// The group jabber id. - public void SendEndGroupChat(string gjid) - { - this.SendEndGroupChat(gjid, null, null); - } - - /// - /// Sends a request to end and remove the group chat. - /// - /// The group jabber id. - /// The action to be executed when the request was successful. - /// The action to be executed when the request failed. - public void SendEndGroupChat(string gjid, Action onSuccess, Action onError) - { - string id = TicketCounter.MakeId("remove_group_"); - var child = new ProtocolTreeNode("group", new[] { new KeyValue("action", "delete") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] { child }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Request the client confguration - /// - public void SendGetClientConfig() - { - string id = TicketCounter.MakeId("get_config_"); - var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", this.whatsAppRealm) }, new ProtocolTreeNode[] { child }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Request dirty - /// - public void SendGetDirty() - { - string id = TicketCounter.MakeId("get_dirty_"); - var child = new ProtocolTreeNode("status", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, new ProtocolTreeNode[] {child}); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Send a request to retrieve group information - /// - /// The group jabber id - public void SendGetGroupInfo(string gjid) - { - string id = TicketCounter.MakeId("get_g_info_"); - var child = new ProtocolTreeNode("query", null); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(gjid)) }, new ProtocolTreeNode[] { child }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Make a request to retrieve all groups where the client is participating in - /// - /// The action to be executed when the request was successful. - /// The action to be executed when the request failed. - public void SendGetGroups(Action onSuccess, Action onError) - { - string id = TicketCounter.MakeId("get_groups_"); - this.SendGetGroups(id, "participating"); - } - - /// - /// Make a request to retrieve all groups where the client is the owner of. - /// - public void SendGetOwningGroups() - { - string id = TicketCounter.MakeId("get_owning_groups_"); - this.SendGetGroups(id, "owning"); - } - - /// - /// Make a request to retrieve all group participents - /// - /// The group jabber id - public void SendGetParticipants(string gjid) - { - string id = TicketCounter.MakeId("get_participants_"); - var child = new ProtocolTreeNode("list", null); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g"), new KeyValue("to", WhatsApp.GetJID(gjid)) }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Make a request to retrieve a photo - /// - /// The group jabber id. - /// If set to true, the photo will be retrieved in the highest resolution. - public string SendGetPhoto(string jid, bool largeFormat) - { - return this.SendGetPhoto(jid, null, largeFormat, delegate { }); - } - - /// - /// Make a request to retrieve a photo for a specific photo id. - /// - /// The group jabber id. - /// The specific photo that needs to be retrieved. - /// If set to true, the photo will be retrieved in the highest resolution. - /// The action to be executed when the request was successful. - public string SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat, Action onComplete) - { - string id = TicketCounter.MakeId("get_photo_"); - var attrList = new List(); - if (!largeFormat) - { - attrList.Add(new KeyValue("type", "preview")); - } - if (expectedPhotoId != null) - { - attrList.Add(new KeyValue("id", expectedPhotoId)); - } - var child = new ProtocolTreeNode("picture", attrList.ToArray()); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:profile:picture"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(jid)) }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - return id; - } - - /// - /// Make a request to retrieve a list of photo id's - /// - /// The list of jabber id's the photos need to be retrieved of. - public void SendGetPhotoIds(IEnumerable jids) - { - string id = TicketCounter.MakeId("get_photo_id_"); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", this.MyJID) }, - new ProtocolTreeNode("list", new[] { new KeyValue("xmlns", "w:profile:picture") }, - (from jid in jids select new ProtocolTreeNode("user", new[] { new KeyValue("jid", jid) })).ToArray())); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Make a request to retrieve a list of privacy's - /// - public void SendGetPrivacyList() - { - string id = TicketCounter.MakeId("privacylist_"); - var innerChild = new ProtocolTreeNode("list", new[] { new KeyValue("name", "default") }); - var child = new ProtocolTreeNode("query", new KeyValue[] { new KeyValue("xmlns", "jabber:iq:privacy") }, innerChild); - var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "get") }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Make a request to retrieve information (properties) about the server. - /// - public void SendGetServerProperties() - { - string id = TicketCounter.MakeId("get_server_properties_"); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, - new ProtocolTreeNode("props", new[] { new KeyValue("xmlns", "w") })); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Make a request to retrieve the status for specific jabber id. - /// - /// The jabber id the the status should be retrieved from. - public void SendGetStatus(string jid) - { - int index = jid.IndexOf('@'); - if (index > 0) - { - jid = string.Format("{0}@{1}", jid.Substring(0, index), "s.us"); - string v = TicketManager.GenerateId(); - var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "action"), new KeyValue("id", v) }, - new ProtocolTreeNode("action", new[] { new KeyValue("type", "get") })); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - } - - /// - /// Make a request to change our status to inactive. - /// - public void SendInactive() - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "inactive") }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Make a request to leave a group chat - /// - /// The group jabber id - public void SendLeaveGroup(string gjid) - { - this.SendLeaveGroup(gjid, null, null); - } - - /// - /// Make a rquest to leave a group chat - /// - /// The group jabber id. - /// The action to be executed when the request was successful. - /// The action to be executed when the request failed. - public void SendLeaveGroup(string gjid, Action onSuccess, Action onError) - { - this.SendLeaveGroups(new string[] { gjid }, onSuccess, onError); - } - - /// - /// Make a request to leave multiple groups at the same time. - /// - /// The group jabber id. - /// The action to be executed when the request was successful. - /// The action to be executed when the request failed. - public void SendLeaveGroups(IEnumerable gjids, Action onSuccess, Action onError) - { - string id = TicketCounter.MakeId("leave_group_"); - IEnumerable innerChilds = from gjid in gjids select new ProtocolTreeNode("group", new[] { new KeyValue("id", gjid) }); - var child = new ProtocolTreeNode("leave", null, innerChilds); - var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", "g.us") }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Sends a message, message properties are defined in the instance of FMessage. - /// - /// An instance of the FMessage class. - public void SendMessage(FMessage message, bool hidden = false) - { - if (message.media_wa_type != FMessage.Type.Undefined) - { - this.SendMessageWithMedia(message); - } - else - { - this.SendMessageWithBody(message, hidden); - } - } - - //overload - public void SendMessageBroadcast(string[] to, string message) - { - this.SendMessageBroadcast(to, new FMessage(string.Empty, true) { data = message, media_wa_type = FMessage.Type.Undefined}); - } - - //overload - public void SendMessageBroadcast(string to, FMessage message) - { - this.SendMessageBroadcast(new string[] { to }, message); - } - - //overload - public void SendMessageBroadcast(string to, string message) - { - this.SendMessageBroadcast(new string[] { to }, new FMessage(string.Empty, true) { data = message, media_wa_type = FMessage.Type.Undefined }); - } - - //send broadcast - public void SendMessageBroadcast(string[] to, FMessage message) - { - if (to != null && to.Length > 0 && message != null && !string.IsNullOrEmpty(message.data)) - { - ProtocolTreeNode child; - if (message.media_wa_type == FMessage.Type.Undefined) - { - //text broadcast - child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); - } - else - { - throw new NotImplementedException(); - } - - //compose broadcast envelope - ProtocolTreeNode xnode = new ProtocolTreeNode("x", new KeyValue[] { - new KeyValue("xmlns", "jabber:x:event") - }, new ProtocolTreeNode("server", null)); - List toNodes = new List(); - foreach (string target in to) - { - toNodes.Add(new ProtocolTreeNode("to", new KeyValue[] { new KeyValue("jid", WhatsAppApi.WhatsApp.GetJID(target)) })); - } - - ProtocolTreeNode broadcastNode = new ProtocolTreeNode("broadcast", null, toNodes); - ProtocolTreeNode messageNode = new ProtocolTreeNode("message", new KeyValue[] { - new KeyValue("to", "broadcast"), - new KeyValue("type", "chat"), - new KeyValue("id", message.identifier_key.id) - }, new ProtocolTreeNode[] { - broadcastNode, - xnode, - child - }); - this.SendNode(messageNode); - } - } - - /// - /// Tell the server the message has been recieved. - /// - /// An instance of the FMessage class. - public void SendMessageReceived(FMessage message, string response) - { - ProtocolTreeNode node = new ProtocolTreeNode("receipt", new[] { - new KeyValue("to", message.identifier_key.remote_jid), - new KeyValue("id", message.identifier_key.id) - }); - - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Send a null byte to the server - /// - public void SendNop() - { - this.whatsNetwork.SendData(this.BinWriter.Write(null)); - } - - /// - /// Send a notification to a specific user that the message has been recieved - /// - /// The jabber id - /// The id of the message - public void SendNotificationReceived(string jid, string id) - { - var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); - var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "notification"), new KeyValue("id", id) }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Send pause - /// - /// The jabber id of the reciever - public void SendPaused(string to) - { - this.SendChatState(to, "paused"); - } - - /// - /// Send a ping to the server - /// - public void SendPing() - { - string id = TicketCounter.MakeId("ping_"); - var child = new ProtocolTreeNode("ping", new[] { new KeyValue("xmlns", "w:p") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get") }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Send a pong to a specific user - /// - /// - public void SendPong(string id) - { - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "result"), new KeyValue("to", this.whatsAppRealm), new KeyValue("id", id) }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Send a subscription request - /// - /// - public void SendPresenceSubscriptionRequest(string to) - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "subscribe"), new KeyValue("to", to) }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Request to retrieve the LastOnline string - /// - /// The jabber id - public void SendQueryLastOnline(string jid) - { - string id = TicketCounter.MakeId("last_"); - var child = new ProtocolTreeNode("query", null); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", jid), new KeyValue("xmlns", "jabber:iq:last") }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Tell the server wether the platform is replay capable - /// - /// The platform - /// Capable or not - public void SendRelayCapable(string platform, bool value) - { - string v = TicketCounter.MakeId("relay_"); - var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("relay", value ? "1" : "0") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", this.whatsAppRealm) }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Tell the server the relay was complete - /// - /// The id - /// Miliseconds it took to relay the message - public void SendRelayComplete(string id, int millis) - { - var child = new ProtocolTreeNode("relay", new[] { new KeyValue("elapsed", millis.ToString(CultureInfo.InvariantCulture)) }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "result"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Send a relay timeout - /// - /// The id - public void SendRelayTimeout(string id) - { - var innerChild = new ProtocolTreeNode("remote-server-timeout", new[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas") }); - var child = new ProtocolTreeNode("error", new[] { new KeyValue("code", "504"), new KeyValue("type", "wait") }, innerChild); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "error"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Request to remove participants from a group - /// - /// The group jabber id - /// A list of participants - public void SendRemoveParticipants(string gjid, List participants) - { - this.SendRemoveParticipants(gjid, participants, null, null); - } - - /// - /// Request to remove participants from a group - /// - /// The group jabber id - /// A list of participants - /// Action to execute when the request was successful - /// Action to execute when the request failed - public void SendRemoveParticipants(string gjid, List participants, Action onSuccess, Action onError) - { - string id = TicketCounter.MakeId("remove_group_participants_"); - this.SendVerbParticipants(gjid, participants, id, "remove"); - } - - /// - /// Request to set the group subject - /// - /// The group jabber id - /// The new group subject - public void SendSetGroupSubject(string gjid, string subject) - { - this.SendSetGroupSubject(gjid, subject, null, null); - } - - /// - /// Request to set the group subject - /// - /// The group jabber id - /// The new group subject - /// Action to execute when the request was successful - /// Action to execute when the request failed - public void SendSetGroupSubject(string gjid, string subject, Action onSuccess, Action onError) - { - string id = TicketCounter.MakeId("set_group_subject_"); - var child = new ProtocolTreeNode("subject", new[] { new KeyValue("value", subject) }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", gjid) }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Change the profile picture - /// - /// The user jabber id - /// The ammount of bytes needed for the photo - /// The amount of bytes needed for the thumbanil - /// Action to execute when the request was successful - /// Action to execute when the request failed - public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes) - { - string id = TicketCounter.MakeId("set_photo_"); - var list = new List { new ProtocolTreeNode("picture", null, null, bytes) }; - if (thumbnailBytes != null) - { - list.Add(new ProtocolTreeNode("picture", new[] { new KeyValue("type", "preview") }, null, thumbnailBytes)); - } - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:profile:picture"), new KeyValue("to", jid) }, list.ToArray()); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Set the list of people that have been blocked - /// - /// The list of people that have been blocked. - public void SendSetPrivacyBlockedList(IEnumerable list) - { - this.SendSetPrivacyBlockedList(list, null, null); - } - - /// - /// Set the list of people that have been blocked - /// - /// List of jabber id's - /// Action to execute when the request was successful - /// Action to execute when the request failed - public void SendSetPrivacyBlockedList(IEnumerable jidSet, Action onSuccess, Action onError) - { - string id = TicketCounter.MakeId("privacy_"); - ProtocolTreeNode[] nodeArray = Enumerable.Select(jidSet, (Func)((jid, index) => new ProtocolTreeNode("item", new KeyValue[] { new KeyValue("type", "jid"), new KeyValue("value", jid), new KeyValue("action", "deny"), new KeyValue("order", index.ToString(CultureInfo.InvariantCulture)) }))).ToArray(); - var child = new ProtocolTreeNode("list", new KeyValue[] { new KeyValue("name", "default") }, (nodeArray.Length == 0) ? null : nodeArray); - var node2 = new ProtocolTreeNode("query", new KeyValue[] { new KeyValue("xmlns", "jabber:iq:privacy") }, child); - var node3 = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set") }, node2); - this.whatsNetwork.SendData(this.BinWriter.Write(node3)); - } - - /// - /// Send a status update - /// - /// The status - /// Action to execute when the request was successful - /// Action to execute when the request failed - public void SendStatusUpdate(string status, Action onComplete, Action onError) - { - string id = TicketManager.GenerateId(); - FMessage message = new FMessage(new FMessage.FMessageIdentifierKey("s.us", true, id)); - var messageNode = GetMessageNode(message, new ProtocolTreeNode("body", null, WhatsApp.SYSEncoding.GetBytes(status))); - this.whatsNetwork.SendData(this.BinWriter.Write(messageNode)); - } - - /// - /// Tell the server the new subject has been recieved - /// - /// The recipient - /// The id - public void SendSubjectReceived(string to, string id) - { - var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); - var node = GetSubjectMessage(to, id, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Unsubscibe him - /// - /// The jabber id - public void SendUnsubscribeHim(string jid) - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribed"), new KeyValue("to", jid) }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Unsubscribe me - /// - /// The jabber id - public void SendUnsubscribeMe(string jid) - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribe"), new KeyValue("to", jid) }); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Tell the server the 'visible' status has been acknowledged - /// - /// Recipient - /// The id - public void SendVisibleReceiptAck(string to, string id) - { - this.SendReceiptAck(to, id, "visible"); - } - - /// - /// Request to retrieve all groups - /// - /// The id - /// The type - internal void SendGetGroups(string id, string type) - { - var child = new ProtocolTreeNode("list", new[] { new KeyValue("type", type) }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g"), new KeyValue("to", "g.us") }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Send a message with a body (Plain text); - /// - /// An instance of the FMessage class. - internal void SendMessageWithBody(FMessage message, bool hidden = false) - { - var child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); - this.whatsNetwork.SendData(this.BinWriter.Write(GetMessageNode(message, child, hidden))); - } - - /// - /// Send a message with media (photo/sound/movie) - /// - /// An instance of the FMessage class. - internal void SendMessageWithMedia(FMessage message) - { - ProtocolTreeNode node; - if (FMessage.Type.System == message.media_wa_type) - { - throw new SystemException("Cannot send system message over the network"); - } - - List list = new List(new KeyValue[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:mms"), new KeyValue("type", FMessage.GetMessage_WA_Type_StrValue(message.media_wa_type)) }); - if (FMessage.Type.Location == message.media_wa_type) - { - list.AddRange(new KeyValue[] { new KeyValue("latitude", message.latitude.ToString(CultureInfo.InvariantCulture)), new KeyValue("longitude", message.longitude.ToString(CultureInfo.InvariantCulture)) }); - if (message.location_details != null) - { - list.Add(new KeyValue("name", message.location_details)); - } - if (message.location_url != null) - { - list.Add(new KeyValue("url", message.location_url)); - } - } - else if (((FMessage.Type.Contact != message.media_wa_type) && (message.media_name != null)) && ((message.media_url != null) && (message.media_size > 0L))) - { - list.AddRange(new KeyValue[] { new KeyValue("file", message.media_name), new KeyValue("size", message.media_size.ToString(CultureInfo.InvariantCulture)), new KeyValue("url", message.media_url) }); - if (message.media_duration_seconds > 0) - { - list.Add(new KeyValue("seconds", message.media_duration_seconds.ToString(CultureInfo.InvariantCulture))); - } - } - if ((FMessage.Type.Contact == message.media_wa_type) && (message.media_name != null)) - { - node = new ProtocolTreeNode("media", list.ToArray(), new ProtocolTreeNode("vcard", new KeyValue[] { new KeyValue("name", message.media_name) }, WhatsApp.SYSEncoding.GetBytes(message.data))); - } - else - { - byte[] data = message.binary_data; - if ((data == null) && !string.IsNullOrEmpty(message.data)) - { - try - { - data = Convert.FromBase64String(message.data); - } - catch (Exception) - { - } - } - if (data != null) - { - list.Add(new KeyValue("encoding", "raw")); - } - node = new ProtocolTreeNode("media", list.ToArray(), null, data); - } - this.whatsNetwork.SendData(this.BinWriter.Write(GetMessageNode(message, node))); - } - - public void SendNode(ProtocolTreeNode node) - { - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Send a verb of group participants - /// - /// The group jabber id - /// List of participants - /// The id - /// Inner tag - internal void SendVerbParticipants(string gjid, IEnumerable participants, string id, string inner_tag) - { - IEnumerable source = from jid in participants select new ProtocolTreeNode("participant", new[] { new KeyValue("jid", jid) }); - var child = new ProtocolTreeNode(inner_tag, null, source); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g"), new KeyValue("to", gjid) }, child); - this.whatsNetwork.SendData(this.BinWriter.Write(node)); - } - - /// - /// Processes group settings - /// - /// A list of instances of the GroupSetting class. - /// A list of ProtocolTreeNodes - private IEnumerable ProcessGroupSettings(IEnumerable groups) - { - ProtocolTreeNode[] nodeArray = null; - if ((groups != null) && groups.Any()) - { - DateTime now = DateTime.Now; - nodeArray = (from @group in groups - select new ProtocolTreeNode("item", new[] - { new KeyValue("jid", @group.Jid), - new KeyValue("notify", @group.Enabled ? "1" : "0"), - new KeyValue("mute", string.Format(CultureInfo.InvariantCulture, "{0}", new object[] { (!@group.MuteExpiry.HasValue || (@group.MuteExpiry.Value <= now)) ? 0 : ((int) (@group.MuteExpiry.Value - now).TotalSeconds) })) })).ToArray(); - } - return nodeArray; - } - - /// - /// Tell the server the reciepient has been acknowledged - /// - /// The reciepient - /// The id - /// The receipt type - private void SendReceiptAck(string to, string id, string receiptType) - { - var tmpChild = new ProtocolTreeNode("ack", new[] { new KeyValue("xmlns", "urn:xmpp:receipts"), new KeyValue("type", receiptType) }); - var resultNode = new ProtocolTreeNode("message", new[] - { - new KeyValue("to", to), - new KeyValue("type", "chat"), - new KeyValue("id", id) - }, tmpChild); - this.whatsNetwork.SendData(this.BinWriter.Write(resultNode)); - } - - /// - /// Get the message node - /// - /// the message - /// The protocol tree node - /// An instance of the ProtocolTreeNode class. - internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode, bool hidden = false) - { - return new ProtocolTreeNode("message", new[] { - new KeyValue("to", message.identifier_key.remote_jid), - new KeyValue("type", message.media_wa_type == FMessage.Type.Undefined?"text":"media"), - new KeyValue("id", message.identifier_key.id) - }, - new ProtocolTreeNode[] { - new ProtocolTreeNode("x", new KeyValue[] { new KeyValue("xmlns", "jabber:x:event") }, new ProtocolTreeNode("server", null)), - pNode, - new ProtocolTreeNode("offline", null) - }); - } - - /// - /// Get the message node - /// - /// the message - /// The protocol tree node - /// An instance of the ProtocolTreeNode class. - public static ProtocolTreeNode GetSubjectMessage(string to, string id, ProtocolTreeNode child) - { - return new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "subject"), new KeyValue("id", id) }, child); - } - } -} \ No newline at end of file From f183e16bd9e00c5647f2797b093b6ad7766d5dfa Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 18:14:41 +0200 Subject: [PATCH 189/271] Added MNC to countries.csv This was a lot of work --- WhatsAppApi/Parser/countries.csv | 508 +++++++++++++++---------------- 1 file changed, 254 insertions(+), 254 deletions(-) diff --git a/WhatsAppApi/Parser/countries.csv b/WhatsAppApi/Parser/countries.csv index 2d91880..e956f4b 100644 --- a/WhatsAppApi/Parser/countries.csv +++ b/WhatsAppApi/Parser/countries.csv @@ -1,254 +1,254 @@ -"Afghanistan",93,412,"AF","ps" -"Albania",355,276,"AL","sq" -"Alberta",1403,302,"CA","en" -"Alberta",1780,302,"CA","en" -"Algeria",213,603,"DZ","ar" -"Andorra",376,213,"AD","ca" -"Angola",244,631,"AO","pt" -"Anguilla",1264,"365","AI","en" -"Antarctica (Australian bases)",6721,232,"AQ","en" -"Antigua and Barbuda",1268,"344","AG","en" -"Argentina",54,722,"AR","es" -"Armenia",374,283,"AM","hy" -"Aruba",297,363,"AW","nl" -"Ascension",247,658,"AC","en" -"Australia",61,505,"AU","en" -"Austria",43,232,"AT","de" -"Azerbaijan",994,400,"AZ","az" -"Bahamas",1242,"364","BS","en" -"Bahrain",973,426,"BH","ar" -"Bangladesh",880,470,"BD","bn" -"Barbados",1246,"342","BB","en" -"Belarus",375,257,"BY","be" -"Belgium",32,206,"BE","nl" -"Belize",501,702,"BZ","es" -"Benin",229,616,"BJ","fr" -"Bermuda",1441,"350","BM","en" -"Bhutan",975,402,"BT","dz" -"Bolivia",591,736,"BO","es" -"Bosnia and Herzegovina",387,218,"BA","bs" -"Botswana",267,652,"BW","en" -"Brazil",55,724,"BR","pt" -"British Columbia", 1250,302,"CA","en" -"British Columbia", 1604,302,"CA","en" -"British Columbia", 1778,302,"CA","en" -"British Indian Ocean Territory",246,348,"IO","en" -"British Virgin Islands",1284,"348","GB","en" -"Brunei",673,528,"BN","ms" -"Bulgaria",359,284,"BG","bg" -"Burkina Faso",226,613,"BF","fr" -"Burundi",257,642,"BI","rn" -"Cambodia",855,456,"KH","km" -"Cameroon",237,624,"CM","fr" -"Cape Verde",238,625,"CV","pt" -"Cayman Islands",1345,"346","GB","en" -"Central African Republic",236,623,"CF","sg" -"Chad",235,622,"TD","fr" -"Chile",56,730,"CL","es" -"China",86,"460|461","CN","en" -"Colombia",57,732,"CO","es" -"Comoros",269,654,"KM","fr" -"Democratic Republic of the Congo",243,630,"CD","fr" -"Republic of the Congo",242,629,"CG","fr" -"Cook Islands",682,548,"CK","en" -"Costa Rica",506,658,"CR","es" -"Cote d'Ivoire",712,"612","CI","fr" -"Croatia",385,219,"HR","hr" -"Cuba",53,368,"CU","es" -"Cyprus",357,280,"CY","el" -"Czech Republic",420,230,"CZ","cs" -"Denmark",45,238,"DK","da" -"Djibouti",253,638,"DJ","fr" -"Dominica",1767,"366","DM","en" -"Dominican Republic",1809,"370","DO","es" -"Dominican Republic",1829,"370","DO","en" -"East Timor",670,514,"TL","pt" -"Ecuador",593,740,"EC","es" -"Egypt",20,602,"EG","ar" -"El Salvador",503,706,"SV","es" -"Equatorial Guinea",240,627,"GQ","es" -"Eritrea",291,657,"ER","ti" -"Estonia",372,248,"EE","et" -"Ethiopia",251,636,"ET","am" -"Falkland Islands",500,750,"FK","en" -"Faroe Islands",298,288,"FO","fo" -"Fiji",679,542,"FJ","en" -"Finland",358,244,"FI","fi" -"France",33,208,"FR","fr" -"French Guiana",594,742,"GF","fr" -"French Polynesia",689,547,"PF","fr" -"Gabon",241,628,"GA","fr" -"Gambia",220,607,"GM","en" -"Gaza Strip",970,0,"PS","ar" -"Georgia",995,282,"GE","ka" -"Germany",49,262,"DE","de" -"Ghana",233,620,"GH","ak" -"Gibraltar",350,266,"GI","en" -"Greece",30,202,"GR","el" -"Greenland",299,290,"GL","kl" -"Grenada",1473,"352","GD","en" -"Guadeloupe",590,340,"GP","fr" -"Guam",1671,"535","GU","en" -"Guatemala",502,704,"GT","es" -"Guinea",224,611,"GN","fr" -"Guinea-Bissau",245,632,"GW","pt" -"Guyana",592,738,"GY","pt" -"Haiti",509,372,"HT","fr" -"Honduras",504,708,"HN","es" -"Hong Kong",852,454,"HK","zh" -"Hungary",36,216,"HU","hu" -"Iceland",354,274,"IS","is" -"India",91,"404|405|406","IN","hi" -"Indonesia",62,510,"ID","id" -"Iraq",964,418,"IQ","ar" -"Iran",98,432,"IR","fa" -"Ireland (Eire)",353,272,"IE","en" -"Israel",972,425,"IL","he" -"Italy",39,222,"IT","it" -"Jamaica",1876,"338","JM","en" -"Japan",81,"440|441","JP","ja" -"Jordan",962,416,"JO","ar" -"Kazakhstan",7,401,"KZ","kk" -"Kenya",254,639,"KE","sw" -"Kiribati",686,545,"KI","en" -"Kuwait",965,419,"KW","ar" -"Kyrgyzstan",996,437,"KG","ky" -"Laos",856,457,"LA","lo" -"Latvia",371,247,"LV","lv" -"Lebanon",961,415,"LB","ar" -"Lesotho",266,651,"LS","st" -"Liberia",231,618,"LR","en" -"Libya",218,606,"LY","ar" -"Liechtenstein",423,295,"LI","de" -"Lithuania",370,246,"LT","lt" -"Luxembourg",352,270,"LU","fr" -"Macau",853,455,"MO","pt" -"Republic of Macedonia",389,294,"MK","mk" -"Madagascar",261,646,"MG","mg" -"Malawi",265,650,"MW","ny" -"Malaysia",60,502,"MY","en" -"Maldives",960,472,"MV","dv" -"Mali",223,610,"ML","fr" -"Malta",356,278,"MT","mt" -"Manitoba",1204,302,"CA","en" -"Marshall Islands",692,551,"MH","mh" -"Martinique",596,340,"MQ","fr" -"Mauritania",222,609,"MR","ar" -"Mauritius",230,617,"MU","en" -"Mayotte",262,654,"YT","fr" -"Mexico",52,334,"MX","es" -"Federated States of Micronesia",691,550,"FM","en" -"Moldova",373,259,"MD","ru" -"Monaco",377,212,"MC","fr" -"Mongolia",976,428,"MN","mn" -"Montenegro",382,297,"ME","sr" -"Montserrat",1664,"354",MS,"en" -"Morocco",212,"604","MA","ar" -"Mozambique",258,643,"MZ","pt" -"Myanmar",95,414,"MM","my" -"Namibia",264,649,"NA","en" -"Nauru",674,536,"NR","na" -"Netherlands",31,204,"NL","nl" -"Netherlands Antilles",599,362,"AN","nl" -"Nepal",977,429,"NP","ne" -"New Brunswick",1506,302,"CA","en" -"New Caledonia",687,546,"NC","fr" -"New Zealand",64,530,"NZ","en" -"Newfoundland",1709,302,"CA","en" -"Nicaragua",505,710,"NI","es" -"Niger",227,614,"NE","fr" -"Nigeria",234,621,"NG","ha" -"Niue",683,555,"NU","en" -"Norfolk Island",6723,505,"NF","en" -"North Korea",850,467,"KP","ko" -"Northern Mariana Islands",1670,"534","MP","en" -"Northwest Territories",1867,302,"CA","en" -"Norway",47,242,"NO","nb" -"Nova Scotia",1902,302,"CA","en" -"Oman",968,422,"OM","ar" -"Ontario",1416,302,"CA","en" -"Ontario",1519,302,"CA","en" -"Ontario",1613,302,"CA","en" -"Ontario",1647,302,"CA","en" -"Ontario",1705,302,"CA","en" -"Ontario",1807,302,"CA","en" -"Ontario",1905,302,"CA","en" -"Pakistan",92,410,"PK","en" -"Palau",680,552,"PW","en" -"Palestine",970,425,"PS","ar" -"Panama",507,714,"PA","es" -"Papua New Guinea",675,537,"PG","ho" -"Paraguay",595,744,"PY","es" -"Peru",51,716,"PE","es" -"Philippines",63,515,"PH","fil" -"Poland",48,260,"PL","pl" -"Portugal",351,268,"PT","pt" -"Qatar",974,427,"QA","ar" -"Quebec",1418,302,"CA","en" -"Quebec",1450,302,"CA","en" -"Quebec",1514,302,"CA","en" -"Quebec",1819,302,"CA","en" -"Reunion",262,647,"RE","fr" -"Romania",40,226,"RO","ro" -"Russia",7,250,"RU","ru" -"Rwanda",250,635,"RW","rw" -"Saint-Barthelemy",590,340,"BL","fr" -"Saint Helena",290,658,"SH","en" -"Saint Kitts and Nevis",1869,"356","KN","en" -"Saint Lucia",1758,"358","LC","en" -"Saint Martin (French side)",590,340, "MF","fr" -"Saint Pierre and Miquelon",508,308,"PM","fr" -"Saint Vincent and the Grenadines",1670,"360","VC","en" -"Samoa",685,549,"WS","sm" -"Sao Tome and Principe",239,626,"ST","pt" -"Saskatchewan",1306,302,"CA","en" -"Saudi Arabia",966,420,"SA","ar" -"Senegal",221,608,"SN","wo" -"Serbia",381,220,"RS","sr" -"Seychelles",248,633,"SC","fr" -"Sierra Leone",232,619,"SL","en" -"Singapore",65,525,"SG","en" -"Slovakia",421,231,"SK","sk" -"Slovenia",386,293,"SI","sl" -"Solomon Islands",677,540,"SB","en" -"Somalia",252,637,"SO","so" -"South Africa",27,655,"ZA","xh" -"South Korea",82,450,"KR","ko" -"South Sudan",211,659,"SS","en" -"Spain",34,214,"ES","es" -"Sri Lanka",94,413,"LK","si" -"Sudan",249,634,"SD","ar" -"Suriname",597,746,"SR","nl" -"Swaziland",268,653,"SZ","ss" -"Sweden",46,240,"SE","sv" -"Switzerland",41,228,"CH","de" -"Syria",963,417,"SY","ar" -"Taiwan",886,466,"TW","cmn" -"Tajikistan",992,436,"TJ","tg" -"Tanzania",255,640,"TZ","sw" -"Thailand",66,520,"TH","th" -"Togo",228,615,"TG","fr" -"Tokelau",690,690,"TK","tkl" -"Tonga",676,539,"TO","to" -"Trinidad and Tobago",1868,"374","TT","en" -"Tunisia",216,605,"TN","ar" -"Turkey",90,286,"TR","tr" -"Turkmenistan",993,438,"TM","tk" -"Turks and Caicos Islands",1649,"376","TC","en" -"Tuvalu",688,553,"TV","tvl" -"Uganda",256,641,"UG","sw" -"Ukraine",380,255,"UA","uk" -"United Arab Emirates",971,"424|430|431","AE","ar" -"United Kingdom",44,"234|235","GB","en" -"United States of America",1,"310|311|312|313|314|315|316","US","en" -"Uruguay",598,748,"UY","es" -"Uzbekistan",998,434,"UZ","uz" -"Vanuatu",678,541,"VU","bi" -"Venezuela",58,734,"VE","es" -"Vietnam",84,452,"VN","vi" -"U.S. Virgin Islands",1340,"332","VI","en" -"Wallis and Futuna",681,543,"WF","fr" -"West Bank",970,0,"PS","ar" -"Yemen",967,421,"YE","ar" -"Zambia",260,645,"ZM","en" -"Zimbabwe",263,648,"ZW","en" +"Afghanistan",93,412,1,"AF","ps" +"Albania",355,276,1,"AL","sq" +"Alberta",1403,302,720,"CA","en" +"Alberta",1780,302,720,"CA","en" +"Algeria",213,603,1,"DZ","ar" +"Andorra",376,213,3,"AD","ca" +"Angola",244,631,2,"AO","pt" +"Anguilla",1264,365,10,"AI","en" +"Antarctica (Australian bases)",6721,232,1,"AQ","en" +"Antigua and Barbuda",1268,344,50,"AG","en" +"Argentina",54,722,10,"AR","es" +"Armenia",374,283,10,"AM","hy" +"Aruba",297,363,1,"AW","nl" +"Ascension",247,658,1,"AC","en" +"Australia",61,505,1,"AU","en" +"Austria",43,232,3,"AT","de" +"Azerbaijan",994,400,1,"AZ","az" +"Bahamas",1242,364,39,"BS","en" +"Bahrain",973,426,1,"BH","ar" +"Bangladesh",880,470,1,"BD","bn" +"Barbados",1246,342,750,"BB","en" +"Belarus",375,257,1,"BY","be" +"Belgium",32,206,1,"BE","nl" +"Belize",501,702,67,"BZ","es" +"Benin",229,616,1,"BJ","fr" +"Bermuda",1441,350,1,"BM","en" +"Bhutan",975,402,11,"BT","dz" +"Bolivia",591,736,1,"BO","es" +"Bosnia and Herzegovina",387,218,3,"BA","bs" +"Botswana",267,652,4,"BW","en" +"Brazil",55,724,2,"BR","pt" +"British Columbia",1250,302,720,"CA","en" +"British Columbia",1604,302,720,"CA","en" +"British Columbia",1778,302,720,"CA","en" +"British Indian Ocean Territory",246,348,1,"IO","en" +"British Virgin Islands",1284,348,170,"GB","en" +"Brunei",673,528,11,"BN","ms" +"Bulgaria",359,284,3,"BG","bg" +"Burkina Faso",226,613,1,"BF","fr" +"Burundi",257,642,82,"BI","rn" +"Cambodia",855,456,2,"KH","km" +"Cameroon",237,624,1,"CM","fr" +"Cape Verde",238,625,1,"CV","pt" +"Cayman Islands",1345,346,50,"GB","en" +"Central African Republic",236,623,3,"CF","sg" +"Chad",235,622,4,"TD","fr" +"Chile",56,730,2,"CL","es" +"China",86,"460|461",3,"CN","en" +"Colombia",57,732,102,"CO","es" +"Comoros",269,654,1,"KM","fr" +"Democratic Republic of the Congo",243,630,1,"CD","fr" +"Republic of the Congo",242,629,1,"CG","fr" +"Cook Islands",682,548,1,"CK","en" +"Costa Rica",506,658,4,"CR","es" +"Cote d'Ivoire",712,612,1,"CI","fr" +"Croatia",385,219,1,"HR","hr" +"Cuba",53,368,1,"CU","es" +"Cyprus",357,280,1,"CY","el" +"Czech Republic",420,230,2,"CZ","cs" +"Denmark",45,238,1,"DK","da" +"Djibouti",253,638,1,"DJ","fr" +"Dominica",1767,366,20,"DM","en" +"Dominican Republic",1809,370,1,"DO","es" +"Dominican Republic",1829,370,1,"DO","en" +"East Timor",670,514,1,"TL","pt" +"Ecuador",593,740,0,"EC","es" +"Egypt",20,602,2,"EG","ar" +"El Salvador",503,706,1,"SV","es" +"Equatorial Guinea",240,627,3,"GQ","es" +"Eritrea",291,657,1,"ER","ti" +"Estonia",372,248,3,"EE","et" +"Ethiopia",251,636,11,"ET","am" +"Falkland Islands",500,750,1,"FK","en" +"Faroe Islands",298,288,2,"FO","fo" +"Fiji",679,542,1,"FJ","en" +"Finland",358,244,5,"FI","fi" +"France",33,208,9,"FR","fr" +"French Guiana",594,742,1,"GF","fr" +"French Polynesia",689,547,15,"PF","fr" +"Gabon",241,628,1,"GA","fr" +"Gambia",220,607,1,"GM","en" +"Gaza Strip",970,0,0,"PS","ar" +"Georgia",995,282,1,"GE","ka" +"Germany",49,262,1,"DE","de" +"Ghana",233,620,2,"GH","ak" +"Gibraltar",350,266,9,"GI","en" +"Greece",30,202,5,"GR","el" +"Greenland",299,290,1,"GL","kl" +"Grenada",1473,352,30,"GD","en" +"Guadeloupe",590,340,1,"GP","fr" +"Guam",1671,535,32,"GU","en" +"Guatemala",502,704,1,"GT","es" +"Guinea",224,611,1,"GN","fr" +"Guinea-Bissau",245,632,3,"GW","pt" +"Guyana",592,738,1,"GY","pt" +"Haiti",509,372,2,"HT","fr" +"Honduras",504,708,2,"HN","es" +"Hong Kong",852,454,0,"HK","zh" +"Hungary",36,216,70,"HU","hu" +"Iceland",354,274,2,"IS","is" +"India",91,"404|405|406",30,"IN","hi" +"Indonesia",62,510,10,"ID","id" +"Iraq",964,418,20,"IQ","ar" +"Iran",98,432,35,"IR","fa" +"Ireland (Eire)",353,272,1,"IE","en" +"Israel",972,425,1,"IL","he" +"Italy",39,222,10,"IT","it" +"Jamaica",1876,338,50,"JM","en" +"Japan",81,"440|441",1,"JP","ja" +"Jordan",962,416,77,"JO","ar" +"Kazakhstan",7,401,77,"KZ","kk" +"Kenya",254,639,7,"KE","sw" +"Kiribati",686,545,1,"KI","en" +"Kuwait",965,419,4,"KW","ar" +"Kyrgyzstan",996,437,1,"KG","ky" +"Laos",856,457,1,"LA","lo" +"Latvia",371,247,2,"LV","lv" +"Lebanon",961,415,1,"LB","ar" +"Lesotho",266,651,1,"LS","st" +"Liberia",231,618,7,"LR","en" +"Libya",218,606,0,"LY","ar" +"Liechtenstein",423,295,2,"LI","de" +"Lithuania",370,246,3,"LT","lt" +"Luxembourg",352,270,99,"LU","fr" +"Macau",853,455,2,"MO","pt" +"Republic of Macedonia",389,294,1,"MK","mk" +"Madagascar",261,646,2,"MG","mg" +"Malawi",265,650,1,"MW","ny" +"Malaysia",60,502,16,"MY","en" +"Maldives",960,472,1,"MV","dv" +"Mali",223,610,2,"ML","fr" +"Malta",356,278,1,"MT","mt" +"Manitoba",1204,302,720,"CA","en" +"Marshall Islands",692,551,1,"MH","mh" +"Martinique",596,340,1,"MQ","fr" +"Mauritania",222,609,2,"MR","ar" +"Mauritius",230,617,1,"MU","en" +"Mayotte",262,654,1,"YT","fr" +"Mexico",52,334,3,"MX","es" +"Federated States of Micronesia",691,550,1,"FM","en" +"Moldova",373,259,1,"MD","ru" +"Monaco",377,212,1,"MC","fr" +"Mongolia",976,428,91,"MN","mn" +"Montenegro",382,297,2,"ME","sr" +"Montserrat",1664,354,860,"MS","en" +"Morocco",212,604,0,"MA","ar" +"Mozambique",258,643,4,"MZ","pt" +"Myanmar",95,414,1,"MM","my" +"Namibia",264,649,3,"NA","en" +"Nauru",674,536,2,"NR","na" +"Netherlands",31,204,4,"NL","nl" +"Netherlands Antilles",599,362,51,"AN","nl" +"Nepal",977,429,1,"NP","ne" +"New Brunswick",1506,302,720,"CA","en" +"New Caledonia",687,546,1,"NC","fr" +"New Zealand",64,530,1,"NZ","en" +"Newfoundland",1709,302,720,"CA","en" +"Nicaragua",505,710,30,"NI","es" +"Niger",227,614,4,"NE","fr" +"Nigeria",234,621,20,"NG","ha" +"Niue",683,555,1,"NU","en" +"Norfolk Island",6723,505,10,"NF","en" +"North Korea",850,467,193,"KP","ko" +"Northern Mariana Islands",1670,534,1,"MP","en" +"Northwest Territories",1867,302,720,"CA","en" +"Norway",47,242,4,"NO","nb" +"Nova Scotia",1902,302,720,"CA","en" +"Oman",968,422,2,"OM","ar" +"Ontario",1416,302,720,"CA","en" +"Ontario",1519,302,720,"CA","en" +"Ontario",1613,302,720,"CA","en" +"Ontario",1647,302,720,"CA","en" +"Ontario",1705,302,720,"CA","en" +"Ontario",1807,302,720,"CA","en" +"Ontario",1905,302,720,"CA","en" +"Pakistan",92,410,1,"PK","en" +"Palau",680,552,80,"PW","en" +"Palestine",970,425,6,"PS","ar" +"Panama",507,714,2,"PA","es" +"Papua New Guinea",675,537,3,"PG","ho" +"Paraguay",595,744,6,"PY","es" +"Peru",51,716,6,"PE","es" +"Philippines",63,515,2,"PH","fil" +"Poland",48,260,3,"PL","pl" +"Portugal",351,268,1,"PT","pt" +"Qatar",974,427,2,"QA","ar" +"Quebec",1418,302,720,"CA","en" +"Quebec",1450,302,720,"CA","en" +"Quebec",1514,302,720,"CA","en" +"Quebec",1819,302,720,"CA","en" +"Reunion",262,647,0,"RE","fr" +"Romania",40,226,1,"RO","ro" +"Russia",7,250,20,"RU","ru" +"Rwanda",250,635,10,"RW","rw" +"Saint-Barthelemy",590,340,1,"BL","fr" +"Saint Helena",290,658,1,"SH","en" +"Saint Kitts and Nevis",1869,356,50,"KN","en" +"Saint Lucia",1758,358,50,"LC","en" +"Saint Martin (French side)",590,340,1,"MF","fr" +"Saint Pierre and Miquelon",508,308,2,"PM","fr" +"Saint Vincent and the Grenadines",1670,360,70,"VC","en" +"Samoa",685,549,1,"WS","sm" +"Sao Tome and Principe",239,626,1,"ST","pt" +"Saskatchewan",1306,302,720,"CA","en" +"Saudi Arabia",966,420,4,"SA","ar" +"Senegal",221,608,1,"SN","wo" +"Serbia",381,220,1,"RS","sr" +"Seychelles",248,633,10,"SC","fr" +"Sierra Leone",232,619,4,"SL","en" +"Singapore",65,525,1,"SG","en" +"Slovakia",421,231,4,"SK","sk" +"Slovenia",386,293,31,"SI","sl" +"Solomon Islands",677,540,2,"SB","en" +"Somalia",252,637,82,"SO","so" +"South Africa",27,655,1,"ZA","xh" +"South Korea",82,450,5,"KR","ko" +"South Sudan",211,659,2,"SS","en" +"Spain",34,214,1,"ES","es" +"Sri Lanka",94,413,1,"LK","si" +"Sudan",249,634,7,"SD","ar" +"Suriname",597,746,3,"SR","nl" +"Swaziland",268,653,10,"SZ","ss" +"Sweden",46,240,7,"SE","sv" +"Switzerland",41,228,3,"CH","de" +"Syria",963,417,1,"SY","ar" +"Taiwan",886,466,1,"TW","cmn" +"Tajikistan",992,436,1,"TJ","tg" +"Tanzania",255,640,4,"TZ","sw" +"Thailand",66,520,0,"TH","th" +"Togo",228,615,1,"TG","fr" +"Tokelau",690,690,1,"TK","tkl" +"Tonga",676,539,1,"TO","to" +"Trinidad and Tobago",1868,374,12,"TT","en" +"Tunisia",216,605,1,"TN","ar" +"Turkey",90,286,2,"TR","tr" +"Turkmenistan",993,438,1,"TM","tk" +"Turks and Caicos Islands",1649,376,50,"TC","en" +"Tuvalu",688,553,1,"TV","tvl" +"Uganda",256,641,14,"UG","sw" +"Ukraine",380,255,1,"UA","uk" +"United Arab Emirates",971,"424|430|431",2,"AE","ar" +"United Kingdom",44,"234|235",10,"GB","en" +"United States of America",1,"310|311|312|313|314|315|316",4,"US","en" +"Uruguay",598,748,7,"UY","es" +"Uzbekistan",998,434,7,"UZ","uz" +"Vanuatu",678,541,5,"VU","bi" +"Venezuela",58,734,4,"VE","es" +"Vietnam",84,452,1,"VN","vi" +"U.S. Virgin Islands",1340,332,4,"VI","en" +"Wallis and Futuna",681,543,1,"WF","fr" +"West Bank",970,0,1,"PS","ar" +"Yemen",967,421,2,"YE","ar" +"Zambia",260,645,2,"ZM","en" +"Zimbabwe",263,648,2,"ZW","en" From b53b357be56dc22bd5bd4c357d218e98cfc10e7b Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 18:18:39 +0200 Subject: [PATCH 190/271] Modified PhoneNumber to use new MNC data --- WhatsAppApi/Parser/PhoneNumber.cs | 32 ++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/WhatsAppApi/Parser/PhoneNumber.cs b/WhatsAppApi/Parser/PhoneNumber.cs index 7aa5ec0..e64035c 100644 --- a/WhatsAppApi/Parser/PhoneNumber.cs +++ b/WhatsAppApi/Parser/PhoneNumber.cs @@ -21,7 +21,24 @@ public string FullNumber } public string ISO3166; public string ISO639; - public string MCC; + protected string _mcc; + protected string _mnc; + + public string MCC + { + get + { + return this._mcc.PadLeft(3, '0'); + } + } + + public string MNC + { + get + { + return this._mnc.PadLeft(3, '0'); + } + } public PhoneNumber(string number) { @@ -46,14 +63,15 @@ public PhoneNumber(string number) } this.CC = values[1]; this.Number = number.Substring(this.CC.Length); - this.ISO3166 = values[3].Trim(new char[] { '"' }); - this.ISO639 = values[4].Trim(new char[] { '"' }); - this.MCC = values[2].Trim(new char[] { '"' }); - if (this.MCC.Contains('|')) + this.ISO3166 = values[4].Trim(new char[] { '"' }); + this.ISO639 = values[5].Trim(new char[] { '"' }); + this._mcc = values[2].Trim(new char[] { '"' }); + this._mnc = values[3].Trim(new char[] { '"' }); + if (this._mcc.Contains('|')) { //take first one - string[] parts = this.MCC.Split(new char[] { '|' }); - this.MCC = parts[0]; + string[] parts = this._mcc.Split(new char[] { '|' }); + this._mcc = parts[0]; } return; } From 3f98cde7a39d5df86597b67af935fe4bdae2d7da Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 19:13:01 +0200 Subject: [PATCH 191/271] Fixed WhatsAppPort build errors --- WhatsAppPort/frmUserChat.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhatsAppPort/frmUserChat.cs b/WhatsAppPort/frmUserChat.cs index 3f6489b..466145c 100644 --- a/WhatsAppPort/frmUserChat.cs +++ b/WhatsAppPort/frmUserChat.cs @@ -66,7 +66,7 @@ private void txtBxSentText_TextChanged(object sender, EventArgs e) if (!this.isTyping) { this.isTyping = true; - WhatSocket.Instance.WhatsSendHandler.SendComposing(this.user.WhatsUser.GetFullJid()); + WhatSocket.Instance.SendComposing(this.user.WhatsUser.GetFullJid()); this.timerTyping.Start(); } } @@ -78,7 +78,7 @@ private void timerTyping_Tick(object sender, EventArgs e) this.isTyping = false; return; } - WhatSocket.Instance.WhatsSendHandler.SendPaused(this.user.WhatsUser.GetFullJid()); + WhatSocket.Instance.SendPaused(this.user.WhatsUser.GetFullJid()); this.timerTyping.Stop(); } } From bde2fb61c7464984c0b4e4696f73ef019d56d62b Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 19:26:39 +0200 Subject: [PATCH 192/271] Added exception handler for Socket_send Applies to #116 --- WhatsAppApi/WhatsNetwork.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsNetwork.cs b/WhatsAppApi/WhatsNetwork.cs index da2037a..a59c49b 100644 --- a/WhatsAppApi/WhatsNetwork.cs +++ b/WhatsAppApi/WhatsNetwork.cs @@ -213,7 +213,14 @@ private void Socket_send(string data, int length, int flags) /// The data that needs to be send as a byte array private void Socket_send(byte[] data) { - this.socket.Send(data); + if (this.socket != null && this.socket.Connected) + { + this.socket.Send(data); + } + else + { + throw new ConnectionException("Socket not connected"); + } } /// From 35b53b17c894ba2f9edc5c5e0a528e4eacd2d458 Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 19:28:05 +0200 Subject: [PATCH 193/271] HurrDurr --- WhatsAppApi/WhatsApp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index c34e6f7..6eac167 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -278,7 +278,7 @@ protected void SendData(byte[] data) { this.whatsNetwork.SendData(data); } - catch (Exception) + catch (ConnectionException) { this.Disconnect(); } From 490a1282d3f81f081079bb67757aa36b018a9d1c Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 23:15:09 +0200 Subject: [PATCH 194/271] Added sendGetStatuses() And changed stream:error handling in processInboundData Applies to #118 --- WhatsAppApi/WhatsApp.cs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 6eac167..1095940 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -1121,13 +1121,8 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) { string content = WhatsApp.SYSEncoding.GetString(textNode.GetData()); Console.WriteLine("Error : " + content); - if (content.Equals("Replaced by new connection", StringComparison.OrdinalIgnoreCase)) - { - this.Disconnect(new Exception(content)); - this.Connect(); - this.Login(); - } } + this.Disconnect(); } if (ProtocolTreeNode.TagEquals(node, "presence")) { @@ -1647,17 +1642,24 @@ public void SendGetServerProperties() this.SendNode(node); } - public void SendGetStatus(string jid) + public void SendGetStatuses(string[] jids) { - int index = jid.IndexOf('@'); - if (index > 0) + List targets = new List(); + foreach (string jid in jids) { - jid = string.Format("{0}@{1}", jid.Substring(0, index), "s.us"); - string v = TicketManager.GenerateId(); - var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "action"), new KeyValue("id", v) }, - new ProtocolTreeNode("action", new[] { new KeyValue("type", "get") })); - this.SendNode(node); + targets.Add(new ProtocolTreeNode("user", new[] { new KeyValue("jid", GetJID(jid)) }, null, null)); } + + ProtocolTreeNode node = new ProtocolTreeNode("iq", new[] { + new KeyValue("to", "s.whatsapp.net"), + new KeyValue("type", "get"), + new KeyValue("xmlns", "status"), + new KeyValue("id", TicketCounter.MakeId("getstatus")) + }, new[] { + new ProtocolTreeNode("status", null, targets.ToArray(), null) + }, null); + + this.SendNode(node); } public void SendInactive() From bacb51ad114aec8568530cb79f47993d77ce2db2 Mon Sep 17 00:00:00 2001 From: shirioko Date: Fri, 25 Apr 2014 23:24:06 +0200 Subject: [PATCH 195/271] Moved node handlers into separate methods to slim down processInboundData --- WhatsAppApi/WhatsApp.cs | 514 +++++++++++++++++++++------------------- 1 file changed, 276 insertions(+), 238 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 1095940..effdfa1 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -847,14 +847,6 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) ProtocolTreeNode node = this.reader.nextTree(msgdata); if(node != null) { - if (node.tag == "iq" - && node.GetAttribute("type") == "error") - { - if (this.OnError != null) - { - this.OnError(node.GetAttribute("id"), node.GetAttribute("from"), Int32.Parse(node.GetChild("error").GetAttribute("code")), node.GetChild("error").GetAttribute("text")); - } - } if (ProtocolTreeNode.TagEquals(node, "challenge")) { this.processChallenge(node); @@ -879,7 +871,12 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) this.OnLoginFailed(node.children.FirstOrDefault().tag); } } - else if (ProtocolTreeNode.TagEquals(node, "receipt")) + + + + + + if (ProtocolTreeNode.TagEquals(node, "receipt")) { string from = node.GetAttribute("from"); string id = node.GetAttribute("id"); @@ -906,214 +903,21 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) //send ack SendNotificationAck(node, type); } + + if (ProtocolTreeNode.TagEquals(node, "message")) { - if (!string.IsNullOrEmpty(node.GetAttribute("notify"))) - { - string name = node.GetAttribute("notify"); - if (this.OnGetContactName != null) - { - this.OnGetContactName(node.GetAttribute("from"), name); - } - } - if (node.GetAttribute("type") == "error") - { - throw new NotImplementedException(node.NodeString()); - } - if (node.GetChild("body") != null) - { - //text message - if (this.OnGetMessage != null) - { - this.OnGetMessage(node, node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData()), autoReceipt); - } - if (autoReceipt) - { - this.sendMessageReceived(node); - } - } - if (node.GetChild("media") != null) - { - ProtocolTreeNode media = node.GetChild("media"); - //media message - - //define variables in switch - string file, url, from, id; - int size; - byte[] preview, dat; - id = node.GetAttribute("id"); - from = node.GetAttribute("from"); - switch (media.GetAttribute("type")) - { - case "image": - if (this.OnGetMessageImage != null) - { - url = media.GetAttribute("url"); - file = media.GetAttribute("file"); - size = Int32.Parse(media.GetAttribute("size")); - preview = media.GetData(); - this.OnGetMessageImage(from, id, file, size, url, preview); - } - break; - case "audio": - if (this.OnGetMessageAudio != null) - { - file = media.GetAttribute("file"); - size = Int32.Parse(media.GetAttribute("size")); - url = media.GetAttribute("url"); - preview = media.GetData(); - this.OnGetMessageAudio(from, id, file, size, url, preview); - } - break; - case "video": - if (this.OnGetMessageVideo != null) - { - file = media.GetAttribute("file"); - size = Int32.Parse(media.GetAttribute("size")); - url = media.GetAttribute("url"); - preview = media.GetData(); - this.OnGetMessageVideo(from, id, file, size, url, preview); - } - break; - case "location": - if (this.OnGetMessageLocation != null) - { - double lon = double.Parse(media.GetAttribute("longitude"), System.Globalization.CultureInfo.InvariantCulture); - double lat = double.Parse(media.GetAttribute("latitude"), System.Globalization.CultureInfo.InvariantCulture); - preview = media.GetData(); - name = media.GetAttribute("name"); - url = media.GetAttribute("url"); - this.OnGetMessageLocation(from, id, lon, lat, url, name, preview); - } - break; - case "vcard": - if (this.OnGetMessageVcard != null) - { - ProtocolTreeNode vcard = media.GetChild("vcard"); - name = vcard.GetAttribute("name"); - dat = vcard.GetData(); - this.OnGetMessageVcard(from, id, name, dat); - } - break; - } - this.sendMessageReceived(node); - } + this.handleMessage(node, autoReceipt); } + + if (ProtocolTreeNode.TagEquals(node, "iq")) { - if (node.GetChild("sync") != null) - { - //sync result - ProtocolTreeNode sync = node.GetChild("sync"); - ProtocolTreeNode existing = sync.GetChild("in"); - ProtocolTreeNode nonexisting = sync.GetChild("out"); - //process existing first - Dictionary existingUsers = new Dictionary(); - foreach (ProtocolTreeNode child in existing.GetAllChildren()) - { - existingUsers.Add(System.Text.Encoding.UTF8.GetString(child.GetData()), child.GetAttribute("jid")); - } - //now process failed numbers - List failedNumbers = new List(); - foreach (ProtocolTreeNode child in nonexisting.GetAllChildren()) - { - failedNumbers.Add(System.Text.Encoding.UTF8.GetString(child.GetData())); - } - int index = 0; - Int32.TryParse(sync.GetAttribute("index"), out index); - if (this.OnGetSyncResult != null) - { - this.OnGetSyncResult(index, sync.GetAttribute("sid"), existingUsers, failedNumbers.ToArray()); - } - } - if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && node.children.FirstOrDefault().tag == "query" - && node.children.FirstOrDefault().GetAttribute("xmlns") == "jabber:iq:last" - ) - { - //last seen - DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.FirstOrDefault().GetAttribute("seconds")) * -1); - if (this.OnGetLastSeen != null) - { - this.OnGetLastSeen(node.GetAttribute("from"), lastSeen); - } - } - if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && (ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "media") || ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "duplicate")) - ) - { - //media upload - this.uploadResponse = node; - } - if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "picture") - ) - { - //profile picture - string from = node.GetAttribute("from"); - string id = node.GetChild("picture").GetAttribute("id"); - byte[] dat = node.GetChild("picture").GetData(); - string type = node.GetChild("picture").GetAttribute("type"); - if (type == "preview") - { - if (this.OnGetPhotoPreview != null) - { - this.OnGetPhotoPreview(from, id, dat); - } - } - else - { - if (this.OnGetPhoto != null) - { - this.OnGetPhoto(from, id, dat); - } - } - } - if (node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "ping")) - { - this.SendPong(node.GetAttribute("id")); - } - if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "group")) - { - //group(s) info - List groups = new List(); - foreach (ProtocolTreeNode group in node.children) - { - groups.Add(new GroupInfo( - group.GetAttribute("id"), - group.GetAttribute("owner"), - long.Parse(group.GetAttribute("creation")), - group.GetAttribute("subject"), - long.Parse(group.GetAttribute("s_t")), - group.GetAttribute("s_o") - )); - } - if (this.OnGetGroups != null) - { - this.OnGetGroups(groups.ToArray()); - } - } - if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "participant")) - { - //group participants - List participants = new List(); - foreach (ProtocolTreeNode part in node.GetAllChildren()) - { - if (part.tag == "participant" && !string.IsNullOrEmpty(part.GetAttribute("jid"))) - { - participants.Add(part.GetAttribute("jid")); - } - } - if (this.OnGetGroupParticipants != null) - { - this.OnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); - } - } + this.handleIq(node); } + + if (ProtocolTreeNode.TagEquals(node, "stream:error")) { var textNode = node.GetChild("text"); @@ -1124,6 +928,9 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) } this.Disconnect(); } + + + if (ProtocolTreeNode.TagEquals(node, "presence")) { //presence node @@ -1133,6 +940,8 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) } } + + if (node.tag == "ib") { foreach (ProtocolTreeNode child in node.children) @@ -1151,6 +960,8 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) } } + + if (node.tag == "chatstate") { string state = node.children.FirstOrDefault().tag; @@ -1173,6 +984,8 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) } } + + if (node.tag == "ack") { string cls = node.GetAttribute("class"); @@ -1191,38 +1004,15 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) } } + + if (node.tag == "notification") { - if(!String.IsNullOrEmpty(node.GetAttribute("notify"))) - { - if(this.OnGetContactName != null) - { - this.OnGetContactName(node.GetAttribute("from"), node.GetAttribute("notify")); - } - } - string type = node.GetAttribute("type"); - switch(type) - { - case "picture": - ProtocolTreeNode child = node.children.FirstOrDefault(); - if (this.OnNotificationPicture != null) - { - this.OnNotificationPicture(child.tag, child.GetAttribute("jid"), child.GetAttribute("id")); - } - break; - case "status": - ProtocolTreeNode child2 = node.children.FirstOrDefault(); - if (this.OnGetStatus != null) - { - this.OnGetStatus(node.GetAttribute("from"), child2.tag, node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(child2.GetData())); - } - break; - default: - throw new NotImplementedException(node.NodeString()); - } - this.SendNotificationAck(node); + this.handleNotification(node); } + + return true; } } @@ -1233,6 +1023,254 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) return false; } + protected void handleMessage(ProtocolTreeNode node, bool autoReceipt) + { + if (!string.IsNullOrEmpty(node.GetAttribute("notify"))) + { + string name = node.GetAttribute("notify"); + if (this.OnGetContactName != null) + { + this.OnGetContactName(node.GetAttribute("from"), name); + } + } + if (node.GetAttribute("type") == "error") + { + throw new NotImplementedException(node.NodeString()); + } + if (node.GetChild("body") != null) + { + //text message + if (this.OnGetMessage != null) + { + this.OnGetMessage(node, node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData()), autoReceipt); + } + if (autoReceipt) + { + this.sendMessageReceived(node); + } + } + if (node.GetChild("media") != null) + { + ProtocolTreeNode media = node.GetChild("media"); + //media message + + //define variables in switch + string file, url, from, id; + int size; + byte[] preview, dat; + id = node.GetAttribute("id"); + from = node.GetAttribute("from"); + switch (media.GetAttribute("type")) + { + case "image": + if (this.OnGetMessageImage != null) + { + url = media.GetAttribute("url"); + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + preview = media.GetData(); + this.OnGetMessageImage(from, id, file, size, url, preview); + } + break; + case "audio": + if (this.OnGetMessageAudio != null) + { + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + url = media.GetAttribute("url"); + preview = media.GetData(); + this.OnGetMessageAudio(from, id, file, size, url, preview); + } + break; + case "video": + if (this.OnGetMessageVideo != null) + { + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + url = media.GetAttribute("url"); + preview = media.GetData(); + this.OnGetMessageVideo(from, id, file, size, url, preview); + } + break; + case "location": + if (this.OnGetMessageLocation != null) + { + double lon = double.Parse(media.GetAttribute("longitude"), System.Globalization.CultureInfo.InvariantCulture); + double lat = double.Parse(media.GetAttribute("latitude"), System.Globalization.CultureInfo.InvariantCulture); + preview = media.GetData(); + name = media.GetAttribute("name"); + url = media.GetAttribute("url"); + this.OnGetMessageLocation(from, id, lon, lat, url, name, preview); + } + break; + case "vcard": + if (this.OnGetMessageVcard != null) + { + ProtocolTreeNode vcard = media.GetChild("vcard"); + name = vcard.GetAttribute("name"); + dat = vcard.GetData(); + this.OnGetMessageVcard(from, id, name, dat); + } + break; + } + this.sendMessageReceived(node); + } + } + + protected void handleIq(ProtocolTreeNode node) + { + if (node.GetAttribute("type") == "error") + { + if (this.OnError != null) + { + this.OnError(node.GetAttribute("id"), node.GetAttribute("from"), Int32.Parse(node.GetChild("error").GetAttribute("code")), node.GetChild("error").GetAttribute("text")); + } + } + if (node.GetChild("sync") != null) + { + //sync result + ProtocolTreeNode sync = node.GetChild("sync"); + ProtocolTreeNode existing = sync.GetChild("in"); + ProtocolTreeNode nonexisting = sync.GetChild("out"); + //process existing first + Dictionary existingUsers = new Dictionary(); + foreach (ProtocolTreeNode child in existing.GetAllChildren()) + { + existingUsers.Add(System.Text.Encoding.UTF8.GetString(child.GetData()), child.GetAttribute("jid")); + } + //now process failed numbers + List failedNumbers = new List(); + foreach (ProtocolTreeNode child in nonexisting.GetAllChildren()) + { + failedNumbers.Add(System.Text.Encoding.UTF8.GetString(child.GetData())); + } + int index = 0; + Int32.TryParse(sync.GetAttribute("index"), out index); + if (this.OnGetSyncResult != null) + { + this.OnGetSyncResult(index, sync.GetAttribute("sid"), existingUsers, failedNumbers.ToArray()); + } + } + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && node.children.FirstOrDefault().tag == "query" + && node.children.FirstOrDefault().GetAttribute("xmlns") == "jabber:iq:last" + ) + { + //last seen + DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.FirstOrDefault().GetAttribute("seconds")) * -1); + if (this.OnGetLastSeen != null) + { + this.OnGetLastSeen(node.GetAttribute("from"), lastSeen); + } + } + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && (ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "media") || ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "duplicate")) + ) + { + //media upload + this.uploadResponse = node; + } + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "picture") + ) + { + //profile picture + string from = node.GetAttribute("from"); + string id = node.GetChild("picture").GetAttribute("id"); + byte[] dat = node.GetChild("picture").GetData(); + string type = node.GetChild("picture").GetAttribute("type"); + if (type == "preview") + { + if (this.OnGetPhotoPreview != null) + { + this.OnGetPhotoPreview(from, id, dat); + } + } + else + { + if (this.OnGetPhoto != null) + { + this.OnGetPhoto(from, id, dat); + } + } + } + if (node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "ping")) + { + this.SendPong(node.GetAttribute("id")); + } + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "group")) + { + //group(s) info + List groups = new List(); + foreach (ProtocolTreeNode group in node.children) + { + groups.Add(new GroupInfo( + group.GetAttribute("id"), + group.GetAttribute("owner"), + long.Parse(group.GetAttribute("creation")), + group.GetAttribute("subject"), + long.Parse(group.GetAttribute("s_t")), + group.GetAttribute("s_o") + )); + } + if (this.OnGetGroups != null) + { + this.OnGetGroups(groups.ToArray()); + } + } + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "participant")) + { + //group participants + List participants = new List(); + foreach (ProtocolTreeNode part in node.GetAllChildren()) + { + if (part.tag == "participant" && !string.IsNullOrEmpty(part.GetAttribute("jid"))) + { + participants.Add(part.GetAttribute("jid")); + } + } + if (this.OnGetGroupParticipants != null) + { + this.OnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); + } + } + } + + protected void handleNotification(ProtocolTreeNode node) + { + if (!String.IsNullOrEmpty(node.GetAttribute("notify"))) + { + if (this.OnGetContactName != null) + { + this.OnGetContactName(node.GetAttribute("from"), node.GetAttribute("notify")); + } + } + string type = node.GetAttribute("type"); + switch (type) + { + case "picture": + ProtocolTreeNode child = node.children.FirstOrDefault(); + if (this.OnNotificationPicture != null) + { + this.OnNotificationPicture(child.tag, child.GetAttribute("jid"), child.GetAttribute("id")); + } + break; + case "status": + ProtocolTreeNode child2 = node.children.FirstOrDefault(); + if (this.OnGetStatus != null) + { + this.OnGetStatus(node.GetAttribute("from"), child2.tag, node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(child2.GetData())); + } + break; + default: + throw new NotImplementedException(node.NodeString()); + } + this.SendNotificationAck(node); + } + private void SendNotificationAck(ProtocolTreeNode node, string type = null) { string from = node.GetAttribute("from"); From 7724c0f7309368c7f0f0c29b4c4235d002df16be Mon Sep 17 00:00:00 2001 From: shirioko Date: Sat, 26 Apr 2014 00:41:21 +0200 Subject: [PATCH 196/271] Abstracted out WA stuff into separate classes --- WhatsAppApi/Response/WaGroupInfo.cs | 27 + WhatsAppApi/Response/WaUploadResponse.cs | 55 ++ WhatsAppApi/WhatsApp.cs | 672 ++--------------------- WhatsAppApi/WhatsAppApi.csproj | 4 + WhatsAppApi/WhatsAppBase.cs | 217 ++++++++ WhatsAppApi/WhatsEventBase.cs | 270 +++++++++ 6 files changed, 632 insertions(+), 613 deletions(-) create mode 100644 WhatsAppApi/Response/WaGroupInfo.cs create mode 100644 WhatsAppApi/Response/WaUploadResponse.cs create mode 100644 WhatsAppApi/WhatsAppBase.cs create mode 100644 WhatsAppApi/WhatsEventBase.cs diff --git a/WhatsAppApi/Response/WaGroupInfo.cs b/WhatsAppApi/Response/WaGroupInfo.cs new file mode 100644 index 0000000..8ff3c8e --- /dev/null +++ b/WhatsAppApi/Response/WaGroupInfo.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WhatsAppApi.Response +{ + public class WaGroupInfo + { + public readonly string id; + public readonly string owner; + public readonly long creation; + public readonly string subject; + public readonly long subjectChangedTime; + public readonly string subjectChangedBy; + + internal WaGroupInfo(string id, string owner, long creation, string subject, long subjectChanged, string subjectChangedBy) + { + this.id = id; + this.owner = owner; + this.creation = creation; + this.subject = subject; + this.subjectChangedTime = subjectChanged; + this.subjectChangedBy = subjectChangedBy; + } + } +} diff --git a/WhatsAppApi/Response/WaUploadResponse.cs b/WhatsAppApi/Response/WaUploadResponse.cs new file mode 100644 index 0000000..3967861 --- /dev/null +++ b/WhatsAppApi/Response/WaUploadResponse.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using WhatsAppApi.Helper; + +namespace WhatsAppApi.Response +{ + public class WaUploadResponse + { + public string url { get; set; } + public string mimetype { get; set; } + public int size { get; set; } + public string filehash { get; set; } + public string type { get; set; } + public int width { get; set; } + public int height { get; set; } + + public int duration { get; set; } + public string acodec { get; set; } + public int asampfreq { get; set; } + public string asampfmt { get; set; } + public int abitrate { get; set; } + + public WaUploadResponse() + { } + + public WaUploadResponse(ProtocolTreeNode node) + { + node = node.GetChild("duplicate"); + if (node != null) + { + int oSize, oWidth, oHeight, oDuration, oAsampfreq, oAbitrate; + this.url = node.GetAttribute("url"); + this.mimetype = node.GetAttribute("mimetype"); + Int32.TryParse(node.GetAttribute("size"), out oSize); + this.filehash = node.GetAttribute("filehash"); + this.type = node.GetAttribute("type"); + Int32.TryParse(node.GetAttribute("width"), out oWidth); + Int32.TryParse(node.GetAttribute("height"), out oHeight); + Int32.TryParse(node.GetAttribute("duration"), out oDuration); + this.acodec = node.GetAttribute("acodec"); + Int32.TryParse(node.GetAttribute("asampfreq"), out oAsampfreq); + this.asampfmt = node.GetAttribute("asampfmt"); + Int32.TryParse(node.GetAttribute("abitrate"), out oAbitrate); + this.size = oSize; + this.width = oWidth; + this.height = oHeight; + this.duration = oDuration; + this.asampfreq = oAsampfreq; + this.abitrate = oAbitrate; + } + } + } +} diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index effdfa1..64d3007 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -20,220 +20,13 @@ namespace WhatsAppApi /// /// Main api interface /// - public class WhatsApp + public class WhatsApp : WhatsAppBase { - - /// - /// Describes the connection status with the whatsapp server - /// - public enum CONNECTION_STATUS - { - UNAUTHORIZED, - DISCONNECTED, - CONNECTED, - LOGGEDIN - } - - private ProtocolTreeNode uploadResponse; - - /// - /// An instance of the AccountInfo class - /// - private AccountInfo accountinfo; - - /// - /// Determines wether debug mode is on or offf - /// - public static bool DEBUG; - - /// - /// The imei/mac adress - /// - private string imei; - - /// - /// Hide online status - /// - protected bool hidden; - - /// - /// Holds the login status - /// - private CONNECTION_STATUS loginStatus; - - public CONNECTION_STATUS ConnectionStatus - { - get - { - return this.loginStatus; - } - } - - protected KeyStream outputKey; - - /// - /// A lock for a message - /// - private object messageLock = new object(); - - /// - /// Que for recieved messages - /// - private List messageQueue; - - /// - /// The name of the user - /// - private string name; - - /// - /// The phonenumber - /// - private string phoneNumber; - - /// - /// An instance of the BinaryTreeNodeReader class - /// - private BinTreeNodeReader reader; - - /// - /// The timeout for the connection with the Whatsapp servers - /// - private int timeout = 300000; - - /// - /// An instance of the WhatsNetwork class - /// - private WhatsNetwork whatsNetwork; - - /// - /// Holds the encoding we use, default is UTF8 - /// - public static readonly Encoding SYSEncoding = Encoding.UTF8; - - /// - /// Empty bytes to hold the challenge - /// - private byte[] _challengeBytes; - - /// - /// Default class constructor - /// - /// The phone number - /// The imei / mac - /// User nickname - /// Debug on or off, false by default public WhatsApp(string phoneNum, string imei, string nick, bool debug = false, bool hidden = false) { - this.messageQueue = new List(); - this.phoneNumber = phoneNum; - this.imei = imei; - this.name = nick; - this.hidden = hidden; - WhatsApp.DEBUG = debug; - this.reader = new BinTreeNodeReader(); - this.loginStatus = CONNECTION_STATUS.DISCONNECTED; - this.BinWriter = new BinTreeNodeWriter(); - this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.timeout); - } - - /// - /// Add a message to the message que - /// - /// An instance of the ProtocolTreeNode class - public void AddMessage(ProtocolTreeNode node) - { - lock (messageLock) - { - this.messageQueue.Add(node); - } - - } - - /// - /// Connect to the whatsapp network - /// - public void Connect() - { - try - { - this.whatsNetwork.Connect(); - this.loginStatus = CONNECTION_STATUS.CONNECTED; - //success - if (this.OnConnectSuccess != null) - { - this.OnConnectSuccess(); - } - } - catch (Exception e) - { - if (this.OnConnectFailed != null) - { - this.OnConnectFailed(e); - } - } - } - - /// - /// Disconnect from the whatsapp network - /// - public void Disconnect(Exception ex = null) - { - this.whatsNetwork.Disconenct(); - this.loginStatus = CONNECTION_STATUS.DISCONNECTED; - if (this.OnDisconnect != null) - { - this.OnDisconnect(ex); - } - } - - /// - /// Encrypt the password (hash) - /// - /// - public byte[] encryptPassword() - { - return Convert.FromBase64String(this.imei); - } - - /// - /// Get the account information - /// - /// An instance of the AccountInfo class - public AccountInfo GetAccountInfo() - { - return this.accountinfo; - } - - /// - /// Retrieve all messages - /// - /// An array of instances of the ProtocolTreeNode class. - public ProtocolTreeNode[] GetAllMessages() - { - ProtocolTreeNode[] tmpReturn = null; - lock (messageLock) - { - tmpReturn = this.messageQueue.ToArray(); - this.messageQueue.Clear(); - } - return tmpReturn; - } - - /// - /// Checks wether we have messages to retrieve - /// - /// true or false - public bool HasMessages() - { - if (this.messageQueue == null) - return false; - return this.messageQueue.Count > 0; + this._constructBase(phoneNum, imei, nick, debug, hidden); } - /// - /// Logs us in to the server - /// public void Login(byte[] nextChallenge = null) { //reset stuff @@ -272,23 +65,6 @@ public void Login(byte[] nextChallenge = null) this.SendAvailableForChat(this.name, this.hidden); } - protected void SendData(byte[] data) - { - try - { - this.whatsNetwork.SendData(data); - } - catch (ConnectionException) - { - this.Disconnect(); - } - } - - /// - /// Send a message to a person - /// - /// The phone number to send - /// The text that needs to be send public void Message(string to, string txt) { var tmpMessage = new FMessage(GetJID(to), true) { data = txt }; @@ -322,38 +98,6 @@ public void SendSync(string[] numbers, string mode = "full", string context = "r this.SendNode(node); } - /// - /// Convert the input string to a JID if necessary - /// - /// Phonenumber or JID - public static string GetJID(string target) - { - if (!target.Contains('@')) - { - //check if group message - if (target.Contains('-')) - { - //to group - target += "@g.us"; - } - else - { - //to normal user - target += "@s.whatsapp.net"; - } - } - return target; - } - - /// - /// Send an image to a person - /// - /// The id of the message - /// the reciepient - /// The url to the image - /// Filename - /// The size of the image in string format - /// Icon public void MessageImage(string to, string filepath) { to = GetJID(to); @@ -387,7 +131,7 @@ public void MessageImage(string to, string filepath) } //request upload - UploadResponse response = this.UploadFile(filehash, "image", finfo.Length, filepath, to, type); + WaUploadResponse response = this.UploadFile(filehash, "image", finfo.Length, filepath, to, type); if (response != null && !String.IsNullOrEmpty(response.url)) { @@ -431,7 +175,7 @@ public void MessageVideo(string to, string filepath) } //request upload - UploadResponse response = this.UploadFile(filehash, "video", finfo.Length, filepath, to, type); + WaUploadResponse response = this.UploadFile(filehash, "video", finfo.Length, filepath, to, type); if (response != null && !String.IsNullOrEmpty(response.url)) { @@ -483,7 +227,7 @@ public void MessageAudio(string to, string filepath) } //request upload - UploadResponse response = this.UploadFile(filehash, "audio", finfo.Length, filepath, to, type); + WaUploadResponse response = this.UploadFile(filehash, "audio", finfo.Length, filepath, to, type); if (response != null && !String.IsNullOrEmpty(response.url)) { @@ -493,46 +237,7 @@ public void MessageAudio(string to, string filepath) } } - private byte[] CreateThumbnail(string path) - { - if (File.Exists(path)) - { - Image orig = Image.FromFile(path); - if (orig != null) - { - int newHeight = 0; - int newWidth = 0; - float imgWidth = float.Parse(orig.Width.ToString()); - float imgHeight = float.Parse(orig.Height.ToString()); - if (orig.Width > orig.Height) - { - newHeight = (int)((imgHeight / imgWidth) * 100); - newWidth = 100; - } - else - { - newWidth = (int)((imgWidth / imgHeight) * 100); - newHeight = 100; - } - - Bitmap newImage = new Bitmap(newWidth, newHeight); - using(Graphics gr = Graphics.FromImage(newImage)) - { - gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; - gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; - gr.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; - gr.DrawImage(orig, new Rectangle(0, 0, newWidth, newHeight)); - } - MemoryStream ms = new MemoryStream(); - newImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); - ms.Close(); - return ms.ToArray(); - } - } - return null; - } - - private UploadResponse UploadFile(string b64hash, string type, long size, string path, string to, string contenttype) + private WaUploadResponse UploadFile(string b64hash, string type, long size, string path, string to, string contenttype) { ProtocolTreeNode media = new ProtocolTreeNode("media", new KeyValue[] { new KeyValue("hash", b64hash), @@ -556,7 +261,7 @@ private UploadResponse UploadFile(string b64hash, string type, long size, string } if (this.uploadResponse != null && this.uploadResponse.GetChild("duplicate") != null) { - UploadResponse res = new UploadResponse(this.uploadResponse); + WaUploadResponse res = new WaUploadResponse(this.uploadResponse); this.uploadResponse = null; return res; } @@ -637,7 +342,7 @@ private UploadResponse UploadFile(string b64hash, string type, long size, string { string fooo = line.TrimEnd(new char[] { (char)0 }); JavaScriptSerializer jss = new JavaScriptSerializer(); - UploadResponse resp = jss.Deserialize(fooo); + WaUploadResponse resp = jss.Deserialize(fooo); if (!String.IsNullOrEmpty(resp.url)) { return resp; @@ -651,56 +356,6 @@ private UploadResponse UploadFile(string b64hash, string type, long size, string return null; } - public class UploadResponse - { - public string url { get; set; } - public string mimetype { get; set; } - public int size { get; set; } - public string filehash { get; set; } - public string type { get; set; } - public int width { get; set; } - public int height { get; set; } - - public int duration { get; set; } - public string acodec { get; set; } - public int asampfreq { get; set; } - public string asampfmt { get; set; } - public int abitrate { get; set; } - - public UploadResponse() - { } - - public UploadResponse(ProtocolTreeNode node) - { - node = node.GetChild("duplicate"); - if (node != null) - { - int oSize, oWidth, oHeight, oDuration, oAsampfreq, oAbitrate; - this.url = node.GetAttribute("url"); - this.mimetype = node.GetAttribute("mimetype"); - Int32.TryParse(node.GetAttribute("size"), out oSize); - this.filehash = node.GetAttribute("filehash"); - this.type = node.GetAttribute("type"); - Int32.TryParse(node.GetAttribute("width"), out oWidth); - Int32.TryParse(node.GetAttribute("height"), out oHeight); - Int32.TryParse(node.GetAttribute("duration"), out oDuration); - this.acodec = node.GetAttribute("acodec"); - Int32.TryParse(node.GetAttribute("asampfreq"), out oAsampfreq); - this.asampfmt = node.GetAttribute("asampfmt"); - Int32.TryParse(node.GetAttribute("abitrate"), out oAbitrate); - this.size = oSize; - this.width = oWidth; - this.height = oHeight; - this.duration = oDuration; - this.asampfreq = oAsampfreq; - this.abitrate = oAbitrate; - } - } - } - - /// - /// Retrieve messages from the server - /// public void PollMessages(bool autoReceipt = true) { while (pollMessage(autoReceipt)) ; @@ -727,10 +382,11 @@ public bool pollMessage(bool autoReceipt = true) return false; } - /// - /// Add the authenication nodes - /// - /// An instance of the ProtocolTreeNode class + protected ProtocolTreeNode addFeatures() + { + return new ProtocolTreeNode("stream:features", null); + } + protected ProtocolTreeNode addAuth() { List attr = new List(new KeyValue[] { @@ -776,10 +432,6 @@ protected byte[] getAuthBlob() return data; } - /// - /// Add the auth response to protocoltreenode - /// - /// An instance of the ProtocolTreeNode protected ProtocolTreeNode addAuthResponse() { if (this._challengeBytes != null) @@ -806,40 +458,11 @@ protected ProtocolTreeNode addAuthResponse() throw new Exception("Auth response error"); } - /// - /// Add stream features - /// - /// - protected ProtocolTreeNode addFeatures() - { - return new ProtocolTreeNode("stream:features", null); - } - - /// - /// Print a message to the debug console - /// - /// The message - protected void DebugPrint(string debugMsg) - { - if (WhatsApp.DEBUG && debugMsg.Length > 0) - { - Console.WriteLine(debugMsg); - } - } - - /// - /// Process the challenge - /// - /// The node that contains the challenge protected void processChallenge(ProtocolTreeNode node) { _challengeBytes = node.data; } - /// - /// Process inbound data - /// - /// Data to process protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) { try @@ -858,24 +481,14 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) node.GetAttribute("kind"), node.GetAttribute("creation"), node.GetAttribute("expiration")); - if (this.OnLoginSuccess != null) - { - this.OnLoginSuccess(this.phoneNumber, node.GetData()); - } + this.fireOnLoginSuccess(this.phoneNumber, node.GetData()); } else if (ProtocolTreeNode.TagEquals(node, "failure")) { this.loginStatus = CONNECTION_STATUS.UNAUTHORIZED; - if (this.OnLoginFailed != null) - { - this.OnLoginFailed(node.children.FirstOrDefault().tag); - } + this.fireOnLoginFailed(node.children.FirstOrDefault().tag); } - - - - if (ProtocolTreeNode.TagEquals(node, "receipt")) { string from = node.GetAttribute("from"); @@ -885,10 +498,7 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) { case "delivery": //delivered to target - if (this.OnGetMessageReceivedClient != null) - { - this.OnGetMessageReceivedClient(from, id); - } + this.fireOnGetMessageReceivedClient(from, id); break; case "read": //read by target @@ -904,7 +514,6 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) SendNotificationAck(node, type); } - if (ProtocolTreeNode.TagEquals(node, "message")) { this.handleMessage(node, autoReceipt); @@ -916,8 +525,6 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) this.handleIq(node); } - - if (ProtocolTreeNode.TagEquals(node, "stream:error")) { var textNode = node.GetChild("text"); @@ -929,19 +536,12 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) this.Disconnect(); } - - if (ProtocolTreeNode.TagEquals(node, "presence")) { //presence node - if (this.OnGetPresence != null) - { - this.OnGetPresence(node.GetAttribute("from"), node.GetAttribute("type")); - } + this.fireOnGetPresence(node.GetAttribute("from"), node.GetAttribute("type")); } - - if (node.tag == "ib") { foreach (ProtocolTreeNode child in node.children) @@ -960,59 +560,37 @@ protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) } } - - if (node.tag == "chatstate") { string state = node.children.FirstOrDefault().tag; switch (state) { case "composing": - if (this.OnGetTyping != null) - { - this.OnGetTyping(node.GetAttribute("from")); - } + this.fireOnGetTyping(node.GetAttribute("from")); break; case "paused": - if (this.OnGetPaused != null) - { - this.OnGetPaused(node.GetAttribute("from")); - } + this.fireOnGetPaused(node.GetAttribute("from")); break; default: throw new NotImplementedException(node.NodeString()); } } - - if (node.tag == "ack") { string cls = node.GetAttribute("class"); if (cls == "message") { //server receipt - if (this.OnGetMessageReceivedServer != null) - { - this.OnGetMessageReceivedServer(node.GetAttribute("from"), node.GetAttribute("id")); - } - } - else if(cls == "receipt") - { - //message received ack - //do nothing + this.fireOnGetMessageReceivedServer(node.GetAttribute("from"), node.GetAttribute("id")); } } - - if (node.tag == "notification") { this.handleNotification(node); } - - return true; } } @@ -1028,10 +606,7 @@ protected void handleMessage(ProtocolTreeNode node, bool autoReceipt) if (!string.IsNullOrEmpty(node.GetAttribute("notify"))) { string name = node.GetAttribute("notify"); - if (this.OnGetContactName != null) - { - this.OnGetContactName(node.GetAttribute("from"), name); - } + this.fireOnGetContactName(node.GetAttribute("from"), name); } if (node.GetAttribute("type") == "error") { @@ -1040,10 +615,7 @@ protected void handleMessage(ProtocolTreeNode node, bool autoReceipt) if (node.GetChild("body") != null) { //text message - if (this.OnGetMessage != null) - { - this.OnGetMessage(node, node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData()), autoReceipt); - } + this.fireOnGetMessage(node, node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData()), autoReceipt); if (autoReceipt) { this.sendMessageReceived(node); @@ -1063,54 +635,39 @@ protected void handleMessage(ProtocolTreeNode node, bool autoReceipt) switch (media.GetAttribute("type")) { case "image": - if (this.OnGetMessageImage != null) - { - url = media.GetAttribute("url"); - file = media.GetAttribute("file"); - size = Int32.Parse(media.GetAttribute("size")); - preview = media.GetData(); - this.OnGetMessageImage(from, id, file, size, url, preview); - } + url = media.GetAttribute("url"); + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + preview = media.GetData(); + this.fireOnGetMessageImage(from, id, file, size, url, preview); break; case "audio": - if (this.OnGetMessageAudio != null) - { - file = media.GetAttribute("file"); - size = Int32.Parse(media.GetAttribute("size")); - url = media.GetAttribute("url"); - preview = media.GetData(); - this.OnGetMessageAudio(from, id, file, size, url, preview); - } + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + url = media.GetAttribute("url"); + preview = media.GetData(); + this.fireOnGetMessageAudio(from, id, file, size, url, preview); break; case "video": - if (this.OnGetMessageVideo != null) - { - file = media.GetAttribute("file"); - size = Int32.Parse(media.GetAttribute("size")); - url = media.GetAttribute("url"); - preview = media.GetData(); - this.OnGetMessageVideo(from, id, file, size, url, preview); - } + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + url = media.GetAttribute("url"); + preview = media.GetData(); + this.fireOnGetMessageVideo(from, id, file, size, url, preview); break; case "location": - if (this.OnGetMessageLocation != null) - { - double lon = double.Parse(media.GetAttribute("longitude"), System.Globalization.CultureInfo.InvariantCulture); - double lat = double.Parse(media.GetAttribute("latitude"), System.Globalization.CultureInfo.InvariantCulture); - preview = media.GetData(); - name = media.GetAttribute("name"); - url = media.GetAttribute("url"); - this.OnGetMessageLocation(from, id, lon, lat, url, name, preview); - } + double lon = double.Parse(media.GetAttribute("longitude"), System.Globalization.CultureInfo.InvariantCulture); + double lat = double.Parse(media.GetAttribute("latitude"), System.Globalization.CultureInfo.InvariantCulture); + preview = media.GetData(); + name = media.GetAttribute("name"); + url = media.GetAttribute("url"); + this.fireOnGetMessageLocation(from, id, lon, lat, url, name, preview); break; case "vcard": - if (this.OnGetMessageVcard != null) - { - ProtocolTreeNode vcard = media.GetChild("vcard"); - name = vcard.GetAttribute("name"); - dat = vcard.GetData(); - this.OnGetMessageVcard(from, id, name, dat); - } + ProtocolTreeNode vcard = media.GetChild("vcard"); + name = vcard.GetAttribute("name"); + dat = vcard.GetData(); + this.fireOnGetMessageVcard(from, id, name, dat); break; } this.sendMessageReceived(node); @@ -1121,10 +678,7 @@ protected void handleIq(ProtocolTreeNode node) { if (node.GetAttribute("type") == "error") { - if (this.OnError != null) - { - this.OnError(node.GetAttribute("id"), node.GetAttribute("from"), Int32.Parse(node.GetChild("error").GetAttribute("code")), node.GetChild("error").GetAttribute("text")); - } + this.fireOnError(node.GetAttribute("id"), node.GetAttribute("from"), Int32.Parse(node.GetChild("error").GetAttribute("code")), node.GetChild("error").GetAttribute("text")); } if (node.GetChild("sync") != null) { @@ -1146,10 +700,7 @@ protected void handleIq(ProtocolTreeNode node) } int index = 0; Int32.TryParse(sync.GetAttribute("index"), out index); - if (this.OnGetSyncResult != null) - { - this.OnGetSyncResult(index, sync.GetAttribute("sid"), existingUsers, failedNumbers.ToArray()); - } + this.fireOnGetSyncResult(index, sync.GetAttribute("sid"), existingUsers, failedNumbers.ToArray()); } if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) && node.children.FirstOrDefault().tag == "query" @@ -1158,10 +709,7 @@ protected void handleIq(ProtocolTreeNode node) { //last seen DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.FirstOrDefault().GetAttribute("seconds")) * -1); - if (this.OnGetLastSeen != null) - { - this.OnGetLastSeen(node.GetAttribute("from"), lastSeen); - } + this.fireOnGetLastSeen(node.GetAttribute("from"), lastSeen); } if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) && (ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "media") || ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "duplicate")) @@ -1181,17 +729,11 @@ protected void handleIq(ProtocolTreeNode node) string type = node.GetChild("picture").GetAttribute("type"); if (type == "preview") { - if (this.OnGetPhotoPreview != null) - { - this.OnGetPhotoPreview(from, id, dat); - } + this.fireOnGetPhotoPreview(from, id, dat); } else { - if (this.OnGetPhoto != null) - { - this.OnGetPhoto(from, id, dat); - } + this.fireOnGetPhoto(from, id, dat); } } if (node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) @@ -1203,10 +745,10 @@ protected void handleIq(ProtocolTreeNode node) && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "group")) { //group(s) info - List groups = new List(); + List groups = new List(); foreach (ProtocolTreeNode group in node.children) { - groups.Add(new GroupInfo( + groups.Add(new WaGroupInfo( group.GetAttribute("id"), group.GetAttribute("owner"), long.Parse(group.GetAttribute("creation")), @@ -1215,10 +757,7 @@ protected void handleIq(ProtocolTreeNode node) group.GetAttribute("s_o") )); } - if (this.OnGetGroups != null) - { - this.OnGetGroups(groups.ToArray()); - } + this.fireOnGetGroups(groups.ToArray()); } if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "participant")) @@ -1232,10 +771,7 @@ protected void handleIq(ProtocolTreeNode node) participants.Add(part.GetAttribute("jid")); } } - if (this.OnGetGroupParticipants != null) - { - this.OnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); - } + this.fireOnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); } } @@ -1243,27 +779,18 @@ protected void handleNotification(ProtocolTreeNode node) { if (!String.IsNullOrEmpty(node.GetAttribute("notify"))) { - if (this.OnGetContactName != null) - { - this.OnGetContactName(node.GetAttribute("from"), node.GetAttribute("notify")); - } + this.fireOnGetContactName(node.GetAttribute("from"), node.GetAttribute("notify")); } string type = node.GetAttribute("type"); switch (type) { case "picture": ProtocolTreeNode child = node.children.FirstOrDefault(); - if (this.OnNotificationPicture != null) - { - this.OnNotificationPicture(child.tag, child.GetAttribute("jid"), child.GetAttribute("id")); - } + this.fireOnNotificationPicture(child.tag, child.GetAttribute("jid"), child.GetAttribute("id")); break; case "status": ProtocolTreeNode child2 = node.children.FirstOrDefault(); - if (this.OnGetStatus != null) - { - this.OnGetStatus(node.GetAttribute("from"), child2.tag, node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(child2.GetData())); - } + this.fireOnGetStatus(node.GetAttribute("from"), child2.tag, node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(child2.GetData())); break; default: throw new NotImplementedException(node.NodeString()); @@ -1351,87 +878,6 @@ private void PrintInfo(string p) this.DebugPrint(p); } - - - - - // events - public event NullDelegate OnConnectSuccess; - public event ExceptionDelegate OnConnectFailed; - public event ExceptionDelegate OnDisconnect; - public event LoginSuccessDelegate OnLoginSuccess; - public event StringDelegate OnLoginFailed; - - public event OnGetMessageDelegate OnGetMessage; - public event OnGetMediaDelegate OnGetMessageImage; - public event OnGetMediaDelegate OnGetMessageVideo; - public event OnGetMediaDelegate OnGetMessageAudio; - public event OnGetLocationDelegate OnGetMessageLocation; - public event OnGetVcardDelegate OnGetMessageVcard; - - public event OnErrorDelegate OnError; - public event OnNotificationPictureDelegate OnNotificationPicture; - - public event OnGetMessageReceivedDelegate OnGetMessageReceivedServer; - public event OnGetMessageReceivedDelegate OnGetMessageReceivedClient; - - public event OnGetPresenceDelegate OnGetPresence; - public event OnGetGroupParticipantsDelegate OnGetGroupParticipants; - public event OnGetLastSeenDelegate OnGetLastSeen; - public event OnGetchatStateDelegate OnGetTyping; - public event OnGetchatStateDelegate OnGetPaused; - public event OnGetPictureDelegate OnGetPhoto; - public event OnGetPictureDelegate OnGetPhotoPreview; - public event OnGetGroupsDelegate OnGetGroups; - public event OnContactNameDelegate OnGetContactName; - public event OnGetStatusDelegate OnGetStatus; - - public event OnGetSyncResultDelegate OnGetSyncResult; - - //event delegates - public delegate void OnContactNameDelegate(string from, string contactName); - public delegate void NullDelegate(); - public delegate void ExceptionDelegate(Exception ex); - public delegate void LoginSuccessDelegate(string phoneNumber, byte[] data); - public delegate void StringDelegate(string data); - public delegate void OnErrorDelegate(string id, string from, int code, string text); - public delegate void OnGetMessageReceivedDelegate(string from, string id); - public delegate void OnNotificationPictureDelegate(string type, string jid, string id); - public delegate void OnGetMessageDelegate(ProtocolTreeNode messageNode, string from, string id, string name, string message, bool receipt_sent); - public delegate void OnGetPresenceDelegate(string from, string type); - public delegate void OnGetGroupParticipantsDelegate(string gjid, string[] jids); - public delegate void OnGetLastSeenDelegate(string from, DateTime lastSeen); - public delegate void OnGetchatStateDelegate(string from); - public delegate void OnGetMediaDelegate(string from, string id, string fileName, int fileSize, string url, byte[] preview); - public delegate void OnGetLocationDelegate(string from, string id, double lon, double lat, string url, string name, byte[] preview); - public delegate void OnGetVcardDelegate(string from, string id, string name, byte[] data); - public delegate void OnGetPictureDelegate(string from, string id, byte[] data); - public delegate void OnGetGroupsDelegate(GroupInfo[] groups); - public delegate void OnGetStatusDelegate(string from, string type, string name, string status); - public delegate void OnGetSyncResultDelegate(int index, string sid, Dictionary existingUsers, string[] failedNumbers); - - public class GroupInfo - { - public readonly string id; - public readonly string owner; - public readonly long creation; - public readonly string subject; - public readonly long subjectChangedTime; - public readonly string subjectChangedBy; - - internal GroupInfo(string id, string owner, long creation, string subject, long subjectChanged, string subjectChangedBy) - { - this.id = id; - this.owner = owner; - this.creation = creation; - this.subject = subject; - this.subjectChangedTime = subjectChanged; - this.subjectChangedBy = subjectChangedBy; - } - } - - internal BinTreeNodeWriter BinWriter; - public void SendActive() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "active") }); diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index 3d477bd..9b3e05f 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -75,10 +75,14 @@ + + + + diff --git a/WhatsAppApi/WhatsAppBase.cs b/WhatsAppApi/WhatsAppBase.cs new file mode 100644 index 0000000..9ed09e8 --- /dev/null +++ b/WhatsAppApi/WhatsAppBase.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using WhatsAppApi.Helper; +using WhatsAppApi.Parser; +using WhatsAppApi.Settings; + +namespace WhatsAppApi +{ + public class WhatsAppBase : WhatsEventBase + { + public enum CONNECTION_STATUS + { + UNAUTHORIZED, + DISCONNECTED, + CONNECTED, + LOGGEDIN + } + + protected ProtocolTreeNode uploadResponse; + + protected AccountInfo accountinfo; + + public static bool DEBUG; + + protected string password; + + protected bool hidden; + + protected CONNECTION_STATUS loginStatus; + + public CONNECTION_STATUS ConnectionStatus + { + get + { + return this.loginStatus; + } + } + + protected KeyStream outputKey; + + protected object messageLock = new object(); + + protected List messageQueue; + + protected string name; + + protected string phoneNumber; + + protected BinTreeNodeReader reader; + + protected int timeout = 300000; + + protected WhatsNetwork whatsNetwork; + + public static readonly Encoding SYSEncoding = Encoding.UTF8; + + protected byte[] _challengeBytes; + + protected BinTreeNodeWriter BinWriter; + + protected void _constructBase(string phoneNum, string imei, string nick, bool debug, bool hidden) + { + this.messageQueue = new List(); + this.phoneNumber = phoneNum; + this.password = imei; + this.name = nick; + this.hidden = hidden; + WhatsApp.DEBUG = debug; + this.reader = new BinTreeNodeReader(); + this.loginStatus = CONNECTION_STATUS.DISCONNECTED; + this.BinWriter = new BinTreeNodeWriter(); + this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.timeout); + } + + public void Connect() + { + try + { + this.whatsNetwork.Connect(); + this.loginStatus = CONNECTION_STATUS.CONNECTED; + //success + this.fireOnConnectSuccess(); + } + catch (Exception e) + { + this.fireOnConnectFailed(e); + } + } + + public void Disconnect(Exception ex = null) + { + this.whatsNetwork.Disconenct(); + this.loginStatus = CONNECTION_STATUS.DISCONNECTED; + this.fireOnDisconnect(ex); + } + + protected byte[] encryptPassword() + { + return Convert.FromBase64String(this.password); + } + + public AccountInfo GetAccountInfo() + { + return this.accountinfo; + } + + public ProtocolTreeNode[] GetAllMessages() + { + ProtocolTreeNode[] tmpReturn = null; + lock (messageLock) + { + tmpReturn = this.messageQueue.ToArray(); + this.messageQueue.Clear(); + } + return tmpReturn; + } + + public void AddMessage(ProtocolTreeNode node) + { + lock (messageLock) + { + this.messageQueue.Add(node); + } + } + + public bool HasMessages() + { + if (this.messageQueue == null) + return false; + return this.messageQueue.Count > 0; + } + + protected void SendData(byte[] data) + { + try + { + this.whatsNetwork.SendData(data); + } + catch (ConnectionException) + { + this.Disconnect(); + } + } + + public static string GetJID(string target) + { + if (!target.Contains('@')) + { + //check if group message + if (target.Contains('-')) + { + //to group + target += "@g.us"; + } + else + { + //to normal user + target += "@s.whatsapp.net"; + } + } + return target; + } + + protected byte[] CreateThumbnail(string path) + { + if (File.Exists(path)) + { + Image orig = Image.FromFile(path); + if (orig != null) + { + int newHeight = 0; + int newWidth = 0; + float imgWidth = float.Parse(orig.Width.ToString()); + float imgHeight = float.Parse(orig.Height.ToString()); + if (orig.Width > orig.Height) + { + newHeight = (int)((imgHeight / imgWidth) * 100); + newWidth = 100; + } + else + { + newWidth = (int)((imgWidth / imgHeight) * 100); + newHeight = 100; + } + + Bitmap newImage = new Bitmap(newWidth, newHeight); + using (Graphics gr = Graphics.FromImage(newImage)) + { + gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; + gr.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; + gr.DrawImage(orig, new Rectangle(0, 0, newWidth, newHeight)); + } + MemoryStream ms = new MemoryStream(); + newImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); + ms.Close(); + return ms.ToArray(); + } + } + return null; + } + + protected void DebugPrint(string debugMsg) + { + if (WhatsApp.DEBUG && debugMsg.Length > 0) + { + Console.WriteLine(debugMsg); + } + } + + + } +} diff --git a/WhatsAppApi/WhatsEventBase.cs b/WhatsAppApi/WhatsEventBase.cs new file mode 100644 index 0000000..6f2f104 --- /dev/null +++ b/WhatsAppApi/WhatsEventBase.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using WhatsAppApi.Helper; +using WhatsAppApi.Response; + +namespace WhatsAppApi +{ + public class WhatsEventBase + { + //events + public event ExceptionDelegate OnDisconnect; + protected void fireOnDisconnect(Exception ex) + { + if (this.OnDisconnect != null) + { + this.OnDisconnect(ex); + } + } + + public event NullDelegate OnConnectSuccess; + protected void fireOnConnectSuccess() + { + if (this.OnConnectSuccess != null) + { + this.OnConnectSuccess(); + } + } + + public event ExceptionDelegate OnConnectFailed; + protected void fireOnConnectFailed(Exception ex) + { + if (this.OnConnectFailed != null) + { + this.OnConnectFailed(ex); + } + } + + public event LoginSuccessDelegate OnLoginSuccess; + protected void fireOnLoginSuccess(string pn, byte[] data) + { + if (this.OnLoginSuccess != null) + { + this.OnLoginSuccess(pn, data); + } + } + + public event StringDelegate OnLoginFailed; + protected void fireOnLoginFailed(string data) + { + if (this.OnLoginFailed != null) + { + this.OnLoginFailed(data); + } + } + + public event OnGetMessageDelegate OnGetMessage; + protected void fireOnGetMessage(ProtocolTreeNode messageNode, string from, string id, string name, string message, bool receipt_sent) + { + if (this.OnGetMessage != null) + { + this.OnGetMessage(messageNode, from, id, name, message, receipt_sent); + } + } + + public event OnGetMediaDelegate OnGetMessageImage; + protected void fireOnGetMessageImage(string from, string id, string fileName, int fileSize, string url, byte[] preview) + { + if (this.OnGetMessageImage != null) + { + this.OnGetMessageImage(from, id, fileName, fileSize, url, preview); + } + } + + public event OnGetMediaDelegate OnGetMessageVideo; + protected void fireOnGetMessageVideo(string from, string id, string fileName, int fileSize, string url, byte[] preview) + { + if (this.OnGetMessageVideo != null) + { + this.OnGetMessageVideo(from, id, fileName, fileSize, url, preview); + } + } + + public event OnGetMediaDelegate OnGetMessageAudio; + protected void fireOnGetMessageAudio(string from, string id, string fileName, int fileSize, string url, byte[] preview) + { + if (this.OnGetMessageAudio != null) + { + this.OnGetMessageAudio(from, id, fileName, fileSize, url, preview); + } + } + + public event OnGetLocationDelegate OnGetMessageLocation; + protected void fireOnGetMessageLocation(string from, string id, double lon, double lat, string url, string name, byte[] preview) + { + if (this.OnGetMessageLocation != null) + { + this.OnGetMessageLocation(from, id, lon, lat, url, name, preview); + } + } + + public event OnGetVcardDelegate OnGetMessageVcard; + protected void fireOnGetMessageVcard(string from, string id, string name, byte[] data) + { + if (this.OnGetMessageVcard != null) + { + this.OnGetMessageVcard(from, id, name, data); + } + } + + public event OnErrorDelegate OnError; + protected void fireOnError(string id, string from, int code, string text) + { + if (this.OnError != null) + { + this.OnError(id, from, code, text); + } + } + + public event OnNotificationPictureDelegate OnNotificationPicture; + protected void fireOnNotificationPicture(string type, string jid, string id) + { + if (this.OnNotificationPicture != null) + { + this.OnNotificationPicture(type, jid, id); + } + } + + public event OnGetMessageReceivedDelegate OnGetMessageReceivedServer; + protected void fireOnGetMessageReceivedServer(string from, string id) + { + if (this.OnGetMessageReceivedServer != null) + { + this.OnGetMessageReceivedServer(from, id); + } + } + + public event OnGetMessageReceivedDelegate OnGetMessageReceivedClient; + protected void fireOnGetMessageReceivedClient(string from, string id) + { + if (this.OnGetMessageReceivedClient != null) + { + this.OnGetMessageReceivedClient(from, id); + } + } + + public event OnGetPresenceDelegate OnGetPresence; + protected void fireOnGetPresence(string from, string type) + { + if (this.OnGetPresence != null) + { + this.OnGetPresence(from, type); + } + } + + public event OnGetGroupParticipantsDelegate OnGetGroupParticipants; + protected void fireOnGetGroupParticipants(string gjid, string[] jids) + { + if (this.OnGetGroupParticipants != null) + { + this.OnGetGroupParticipants(gjid, jids); + } + } + + public event OnGetLastSeenDelegate OnGetLastSeen; + protected void fireOnGetLastSeen(string from, DateTime lastSeen) + { + if (this.OnGetLastSeen != null) + { + this.OnGetLastSeen(from, lastSeen); + } + } + + public event OnGetChatStateDelegate OnGetTyping; + protected void fireOnGetTyping(string from) + { + if (this.OnGetTyping != null) + { + this.OnGetTyping(from); + } + } + + public event OnGetChatStateDelegate OnGetPaused; + protected void fireOnGetPaused(string from) + { + if (this.OnGetPaused != null) + { + this.OnGetPaused(from); + } + } + + public event OnGetPictureDelegate OnGetPhoto; + protected void fireOnGetPhoto(string from, string id, byte[] data) + { + if (this.OnGetPhoto != null) + { + this.OnGetPhoto(from, id, data); + } + } + + public event OnGetPictureDelegate OnGetPhotoPreview; + protected void fireOnGetPhotoPreview(string from, string id, byte[] data) + { + if (this.OnGetPhotoPreview != null) + { + this.OnGetPhotoPreview(from, id, data); + } + } + + public event OnGetGroupsDelegate OnGetGroups; + protected void fireOnGetGroups(WaGroupInfo[] groups) + { + if (this.OnGetGroups != null) + { + this.OnGetGroups(groups); + } + } + + public event OnContactNameDelegate OnGetContactName; + protected void fireOnGetContactName(string from, string contactName) + { + if (this.OnGetContactName != null) + { + this.OnGetContactName(from, contactName); + } + } + + public event OnGetStatusDelegate OnGetStatus; + protected void fireOnGetStatus(string from, string type, string name, string status) + { + if (this.OnGetStatus != null) + { + this.OnGetStatus(from, type, name, status); + } + } + + public event OnGetSyncResultDelegate OnGetSyncResult; + protected void fireOnGetSyncResult(int index, string sid, Dictionary existingUsers, string[] failedNumbers) + { + if (this.OnGetSyncResult != null) + { + this.OnGetSyncResult(index, sid, existingUsers, failedNumbers); + } + } + + //event delegates + public delegate void OnContactNameDelegate(string from, string contactName); + public delegate void NullDelegate(); + public delegate void ExceptionDelegate(Exception ex); + public delegate void LoginSuccessDelegate(string phoneNumber, byte[] data); + public delegate void StringDelegate(string data); + public delegate void OnErrorDelegate(string id, string from, int code, string text); + public delegate void OnGetMessageReceivedDelegate(string from, string id); + public delegate void OnNotificationPictureDelegate(string type, string jid, string id); + public delegate void OnGetMessageDelegate(ProtocolTreeNode messageNode, string from, string id, string name, string message, bool receipt_sent); + public delegate void OnGetPresenceDelegate(string from, string type); + public delegate void OnGetGroupParticipantsDelegate(string gjid, string[] jids); + public delegate void OnGetLastSeenDelegate(string from, DateTime lastSeen); + public delegate void OnGetChatStateDelegate(string from); + public delegate void OnGetMediaDelegate(string from, string id, string fileName, int fileSize, string url, byte[] preview); + public delegate void OnGetLocationDelegate(string from, string id, double lon, double lat, string url, string name, byte[] preview); + public delegate void OnGetVcardDelegate(string from, string id, string name, byte[] data); + public delegate void OnGetPictureDelegate(string from, string id, byte[] data); + public delegate void OnGetGroupsDelegate(WaGroupInfo[] groups); + public delegate void OnGetStatusDelegate(string from, string type, string name, string status); + public delegate void OnGetSyncResultDelegate(int index, string sid, Dictionary existingUsers, string[] failedNumbers); + + } +} From f7831d4d47d882f6492c5b02653e106eb7f26e3c Mon Sep 17 00:00:00 2001 From: shirioko Date: Sat, 26 Apr 2014 00:47:05 +0200 Subject: [PATCH 197/271] Added result handler for sendGetStatuses() --- WhatsAppApi/WhatsApp.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 64d3007..a035e40 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -773,6 +773,16 @@ protected void handleIq(ProtocolTreeNode node) } this.fireOnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); } + if (node.GetAttribute("type") == "result" && node.GetChild("status") != null) + { + foreach (ProtocolTreeNode status in node.GetChild("status").GetAllChildren()) + { + this.fireOnGetStatus(status.GetAttribute("jid"), + "result", + null, + WhatsApp.SYSEncoding.GetString(status.GetData())); + } + } } protected void handleNotification(ProtocolTreeNode node) From 93434e342d87ea2ffaa5b9c3ea6671d264c5a29c Mon Sep 17 00:00:00 2001 From: shirioko Date: Sat, 26 Apr 2014 00:55:26 +0200 Subject: [PATCH 198/271] More abstraction --- WhatsAppApi/WhatsApp.cs | 102 ++++-------------------------------- WhatsAppApi/WhatsAppBase.cs | 14 +++++ 2 files changed, 25 insertions(+), 91 deletions(-) diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index a035e40..53b7f6d 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -854,40 +854,12 @@ protected void SendQrSync(byte[] qrkey, byte[] token = null) this.SendNode(node); } - /// - /// Tell the server we recieved the message - /// - /// The ProtocolTreeNode that contains the message public void sendMessageReceived(ProtocolTreeNode msg, string response = "received") { FMessage tmpMessage = new FMessage(new FMessage.FMessageIdentifierKey(msg.GetAttribute("from"), true, msg.GetAttribute("id"))); this.SendMessageReceived(tmpMessage, response); } - /// - /// MD5 hashes the password - /// - /// String the needs to be hashed - /// A md5 hash - private string md5(string pass) - { - MD5 md5 = MD5.Create(); - byte[] dataMd5 = md5.ComputeHash(WhatsApp.SYSEncoding.GetBytes(pass)); - var sb = new StringBuilder(); - for (int i = 0; i < dataMd5.Length; i++) - sb.AppendFormat("{0:x2}", dataMd5[i]); - return sb.ToString(); - } - - /// - /// Prints debug - /// - /// message - private void PrintInfo(string p) - { - this.DebugPrint(p); - } - public void SendActive() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "active") }); @@ -895,11 +867,6 @@ public void SendActive() } public void SendAddParticipants(string gjid, IEnumerable participants) - { - this.SendAddParticipants(gjid, participants, null, null); - } - - public void SendAddParticipants(string gjid, IEnumerable participants, Action onSuccess, Action onError) { string id = TicketCounter.MakeId("add_group_participants_"); this.SendVerbParticipants(gjid, participants, id, "add"); @@ -1035,7 +1002,7 @@ public void SendDeleteFromRoster(string jid) this.SendNode(node); } - public void SendDeliveredReceiptAck(string to, string id) + protected void SendDeliveredReceiptAck(string to, string id) { this.SendReceiptAck(to, id, "delivered"); } @@ -1188,25 +1155,11 @@ public void SendMessage(FMessage message, bool hidden = false) } } - //overload public void SendMessageBroadcast(string[] to, string message) { this.SendMessageBroadcast(to, new FMessage(string.Empty, true) { data = message, media_wa_type = FMessage.Type.Undefined }); } - //overload - public void SendMessageBroadcast(string to, FMessage message) - { - this.SendMessageBroadcast(new string[] { to }, message); - } - - //overload - public void SendMessageBroadcast(string to, string message) - { - this.SendMessageBroadcast(new string[] { to }, new FMessage(string.Empty, true) { data = message, media_wa_type = FMessage.Type.Undefined }); - } - - //send broadcast public void SendMessageBroadcast(string[] to, FMessage message) { if (to != null && to.Length > 0 && message != null && !string.IsNullOrEmpty(message.data)) @@ -1246,7 +1199,7 @@ public void SendMessageBroadcast(string[] to, FMessage message) } } - public void SendMessageReceived(FMessage message, string response) + protected void SendMessageReceived(FMessage message, string response) { ProtocolTreeNode node = new ProtocolTreeNode("receipt", new[] { new KeyValue("to", message.identifier_key.remote_jid), @@ -1261,7 +1214,7 @@ public void SendNop() this.SendNode(null); } - public void SendNotificationReceived(string jid, string id) + protected void SendNotificationReceived(string jid, string id) { var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "notification"), new KeyValue("id", id) }, child); @@ -1281,7 +1234,7 @@ public void SendPing() this.SendNode(node); } - public void SendPong(string id) + protected void SendPong(string id) { var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "result"), new KeyValue("to", WhatsConstants.WhatsAppRealm), new KeyValue("id", id) }); this.SendNode(node); @@ -1301,29 +1254,6 @@ public void SendQueryLastOnline(string jid) this.SendNode(node); } - public void SendRelayCapable(string platform, bool value) - { - string v = TicketCounter.MakeId("relay_"); - var child = new ProtocolTreeNode("config", new[] { new KeyValue("xmlns", "urn:xmpp:whatsapp:push"), new KeyValue("platform", platform), new KeyValue("relay", value ? "1" : "0") }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", v), new KeyValue("type", "set"), new KeyValue("to", WhatsConstants.WhatsAppRealm) }, child); - this.SendNode(node); - } - - public void SendRelayComplete(string id, int millis) - { - var child = new ProtocolTreeNode("relay", new[] { new KeyValue("elapsed", millis.ToString(CultureInfo.InvariantCulture)) }); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "result"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); - this.SendNode(node); - } - - public void SendRelayTimeout(string id) - { - var innerChild = new ProtocolTreeNode("remote-server-timeout", new[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas") }); - var child = new ProtocolTreeNode("error", new[] { new KeyValue("code", "504"), new KeyValue("type", "wait") }, innerChild); - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("xmlns", "w:p:r"), new KeyValue("type", "error"), new KeyValue("to", "s.whatsapp.net"), new KeyValue("id", id) }, child); - this.SendNode(node); - } - public void SendRemoveParticipants(string gjid, List participants) { string id = TicketCounter.MakeId("remove_group_participants_"); @@ -1387,25 +1317,20 @@ public void SendUnsubscribeMe(string jid) this.SendNode(node); } - public void SendVisibleReceiptAck(string to, string id) - { - this.SendReceiptAck(to, id, "visible"); - } - - internal void SendGetGroups(string id, string type) + public void SendGetGroups(string id, string type) { var child = new ProtocolTreeNode("list", new[] { new KeyValue("type", type) }); var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g"), new KeyValue("to", "g.us") }, child); this.SendNode(node); } - internal void SendMessageWithBody(FMessage message, bool hidden = false) + protected void SendMessageWithBody(FMessage message, bool hidden = false) { var child = new ProtocolTreeNode("body", null, null, WhatsApp.SYSEncoding.GetBytes(message.data)); this.SendNode(GetMessageNode(message, child, hidden)); } - internal void SendMessageWithMedia(FMessage message) + protected void SendMessageWithMedia(FMessage message) { ProtocolTreeNode node; if (FMessage.Type.System == message.media_wa_type) @@ -1460,7 +1385,7 @@ internal void SendMessageWithMedia(FMessage message) this.SendNode(GetMessageNode(message, node)); } - internal void SendVerbParticipants(string gjid, IEnumerable participants, string id, string inner_tag) + protected void SendVerbParticipants(string gjid, IEnumerable participants, string id, string inner_tag) { IEnumerable source = from jid in participants select new ProtocolTreeNode("participant", new[] { new KeyValue("jid", jid) }); var child = new ProtocolTreeNode(inner_tag, null, source); @@ -1468,12 +1393,7 @@ internal void SendVerbParticipants(string gjid, IEnumerable participants this.SendNode(node); } - public void SendNode(ProtocolTreeNode node) - { - this.SendData(this.BinWriter.Write(node)); - } - - private IEnumerable ProcessGroupSettings(IEnumerable groups) + protected IEnumerable ProcessGroupSettings(IEnumerable groups) { ProtocolTreeNode[] nodeArray = null; if ((groups != null) && groups.Any()) @@ -1500,7 +1420,7 @@ private void SendReceiptAck(string to, string id, string receiptType) this.SendNode(resultNode); } - internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode, bool hidden = false) + protected static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode, bool hidden = false) { return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.identifier_key.remote_jid), @@ -1514,7 +1434,7 @@ internal static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNo }); } - public static ProtocolTreeNode GetSubjectMessage(string to, string id, ProtocolTreeNode child) + protected static ProtocolTreeNode GetSubjectMessage(string to, string id, ProtocolTreeNode child) { return new ProtocolTreeNode("message", new[] { new KeyValue("to", to), new KeyValue("type", "subject"), new KeyValue("id", id) }, child); } diff --git a/WhatsAppApi/WhatsAppBase.cs b/WhatsAppApi/WhatsAppBase.cs index 9ed09e8..1911275 100644 --- a/WhatsAppApi/WhatsAppBase.cs +++ b/WhatsAppApi/WhatsAppBase.cs @@ -3,6 +3,7 @@ using System.Drawing; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Text; using WhatsAppApi.Helper; using WhatsAppApi.Parser; @@ -212,6 +213,19 @@ protected void DebugPrint(string debugMsg) } } + protected string md5(string pass) + { + MD5 md5 = MD5.Create(); + byte[] dataMd5 = md5.ComputeHash(WhatsApp.SYSEncoding.GetBytes(pass)); + var sb = new StringBuilder(); + for (int i = 0; i < dataMd5.Length; i++) + sb.AppendFormat("{0:x2}", dataMd5[i]); + return sb.ToString(); + } + protected void SendNode(ProtocolTreeNode node) + { + this.SendData(this.BinWriter.Write(node)); + } } } From 7acf6d1006bb13c314854805df4f587c2bbdc3dc Mon Sep 17 00:00:00 2001 From: shirioko Date: Sat, 26 Apr 2014 01:03:22 +0200 Subject: [PATCH 199/271] Moved base classes to Base subdir --- WhatsAppApi/{ => Base}/WhatsAppBase.cs | 0 WhatsAppApi/{ => Base}/WhatsEventBase.cs | 0 WhatsAppApi/WhatsAppApi.csproj | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename WhatsAppApi/{ => Base}/WhatsAppBase.cs (100%) rename WhatsAppApi/{ => Base}/WhatsEventBase.cs (100%) diff --git a/WhatsAppApi/WhatsAppBase.cs b/WhatsAppApi/Base/WhatsAppBase.cs similarity index 100% rename from WhatsAppApi/WhatsAppBase.cs rename to WhatsAppApi/Base/WhatsAppBase.cs diff --git a/WhatsAppApi/WhatsEventBase.cs b/WhatsAppApi/Base/WhatsEventBase.cs similarity index 100% rename from WhatsAppApi/WhatsEventBase.cs rename to WhatsAppApi/Base/WhatsEventBase.cs diff --git a/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj index 9b3e05f..519ab24 100644 --- a/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -81,8 +81,8 @@ - - + + From fe1690b312a58afa1b5146169fd93aaded333d2c Mon Sep 17 00:00:00 2001 From: shirioko Date: Sat, 26 Apr 2014 01:23:50 +0200 Subject: [PATCH 200/271] Moar abstraction!!!1! fml --- WhatsAppApi/Base/WhatsSendBase.cs | 610 ++++++++++++++++++++++++++++++ WhatsAppApi/WhatsApp.cs | 602 +---------------------------- WhatsAppApi/WhatsAppApi.csproj | 1 + 3 files changed, 614 insertions(+), 599 deletions(-) create mode 100644 WhatsAppApi/Base/WhatsSendBase.cs diff --git a/WhatsAppApi/Base/WhatsSendBase.cs b/WhatsAppApi/Base/WhatsSendBase.cs new file mode 100644 index 0000000..537c2b1 --- /dev/null +++ b/WhatsAppApi/Base/WhatsSendBase.cs @@ -0,0 +1,610 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using WhatsAppApi.Helper; +using WhatsAppApi.Parser; +using WhatsAppApi.Response; +using WhatsAppApi.Settings; + +namespace WhatsAppApi +{ + public class WhatsSendBase : WhatsAppBase + { + public void Login(byte[] nextChallenge = null) + { + //reset stuff + this.reader.Key = null; + this.BinWriter.Key = null; + this._challengeBytes = null; + + if (nextChallenge != null) + { + this._challengeBytes = nextChallenge; + } + + string resource = string.Format(@"{0}-{1}-{2}", + WhatsConstants.Device, + WhatsConstants.WhatsAppVer, + WhatsConstants.WhatsPort); + var data = this.BinWriter.StartStream(WhatsConstants.WhatsAppServer, resource); + var feat = this.addFeatures(); + var auth = this.addAuth(); + this.SendData(data); + this.SendData(this.BinWriter.Write(feat, false)); + this.SendData(this.BinWriter.Write(auth, false)); + + this.pollMessage();//stream start + this.pollMessage();//features + this.pollMessage();//challenge or success + + if (this.loginStatus != CONNECTION_STATUS.LOGGEDIN) + { + //oneshot failed + ProtocolTreeNode authResp = this.addAuthResponse(); + this.SendData(this.BinWriter.Write(authResp, false)); + this.pollMessage(); + } + + this.SendAvailableForChat(this.name, this.hidden); + } + + public void PollMessages(bool autoReceipt = true) + { + while (pollMessage(autoReceipt)) ; + } + + public bool pollMessage(bool autoReceipt = true) + { + if (this.loginStatus == CONNECTION_STATUS.CONNECTED || this.loginStatus == CONNECTION_STATUS.LOGGEDIN) + { + byte[] nodeData; + try + { + nodeData = this.whatsNetwork.ReadNextNode(); + if (nodeData != null) + { + return this.processInboundData(nodeData, autoReceipt); + } + } + catch (ConnectionException) + { + this.Disconnect(); + } + } + return false; + } + + protected ProtocolTreeNode addFeatures() + { + return new ProtocolTreeNode("stream:features", null); + } + + protected ProtocolTreeNode addAuth() + { + List attr = new List(new KeyValue[] { + new KeyValue("mechanism", Helper.KeyStream.AuthMethod), + new KeyValue("user", this.phoneNumber)}); + if (this.hidden) + { + attr.Add(new KeyValue("passive", "true")); + } + var node = new ProtocolTreeNode("auth", attr.ToArray(), null, this.getAuthBlob()); + return node; + } + + protected byte[] getAuthBlob() + { + byte[] data = null; + if (this._challengeBytes != null) + { + byte[][] keys = KeyStream.GenerateKeys(this.encryptPassword(), this._challengeBytes); + + this.reader.Key = new KeyStream(keys[2], keys[3]); + + this.outputKey = new KeyStream(keys[0], keys[1]); + + PhoneNumber pn = new PhoneNumber(this.phoneNumber); + + List b = new List(); + b.AddRange(new byte[] { 0, 0, 0, 0 }); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(this.phoneNumber)); + b.AddRange(this._challengeBytes); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(Helper.Func.GetNowUnixTimestamp().ToString())); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(WhatsConstants.UserAgent)); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(String.Format(" MccMnc/{0}001", pn.MCC))); + data = b.ToArray(); + + this._challengeBytes = null; + + this.outputKey.EncodeMessage(data, 0, 4, data.Length - 4); + + this.BinWriter.Key = this.outputKey; + } + + return data; + } + + protected ProtocolTreeNode addAuthResponse() + { + if (this._challengeBytes != null) + { + byte[][] keys = KeyStream.GenerateKeys(this.encryptPassword(), this._challengeBytes); + + this.reader.Key = new KeyStream(keys[2], keys[3]); + this.BinWriter.Key = new KeyStream(keys[0], keys[1]); + + List b = new List(); + b.AddRange(new byte[] { 0, 0, 0, 0 }); + b.AddRange(WhatsApp.SYSEncoding.GetBytes(this.phoneNumber)); + b.AddRange(this._challengeBytes); + + + byte[] data = b.ToArray(); + this.BinWriter.Key.EncodeMessage(data, 0, 4, data.Length - 4); + var node = new ProtocolTreeNode("response", + new KeyValue[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") }, + data); + + return node; + } + throw new Exception("Auth response error"); + } + + protected void processChallenge(ProtocolTreeNode node) + { + _challengeBytes = node.data; + } + + protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) + { + try + { + ProtocolTreeNode node = this.reader.nextTree(msgdata); + if (node != null) + { + if (ProtocolTreeNode.TagEquals(node, "challenge")) + { + this.processChallenge(node); + } + else if (ProtocolTreeNode.TagEquals(node, "success")) + { + this.loginStatus = CONNECTION_STATUS.LOGGEDIN; + this.accountinfo = new AccountInfo(node.GetAttribute("status"), + node.GetAttribute("kind"), + node.GetAttribute("creation"), + node.GetAttribute("expiration")); + this.fireOnLoginSuccess(this.phoneNumber, node.GetData()); + } + else if (ProtocolTreeNode.TagEquals(node, "failure")) + { + this.loginStatus = CONNECTION_STATUS.UNAUTHORIZED; + this.fireOnLoginFailed(node.children.FirstOrDefault().tag); + } + + if (ProtocolTreeNode.TagEquals(node, "receipt")) + { + string from = node.GetAttribute("from"); + string id = node.GetAttribute("id"); + string type = node.GetAttribute("type") ?? "delivery"; + switch (type) + { + case "delivery": + //delivered to target + this.fireOnGetMessageReceivedClient(from, id); + break; + case "read": + //read by target + //todo + break; + case "played": + //played by target + //todo + break; + } + + //send ack + SendNotificationAck(node, type); + } + + if (ProtocolTreeNode.TagEquals(node, "message")) + { + this.handleMessage(node, autoReceipt); + } + + + if (ProtocolTreeNode.TagEquals(node, "iq")) + { + this.handleIq(node); + } + + if (ProtocolTreeNode.TagEquals(node, "stream:error")) + { + var textNode = node.GetChild("text"); + if (textNode != null) + { + string content = WhatsApp.SYSEncoding.GetString(textNode.GetData()); + Console.WriteLine("Error : " + content); + } + this.Disconnect(); + } + + if (ProtocolTreeNode.TagEquals(node, "presence")) + { + //presence node + this.fireOnGetPresence(node.GetAttribute("from"), node.GetAttribute("type")); + } + + if (node.tag == "ib") + { + foreach (ProtocolTreeNode child in node.children) + { + switch (child.tag) + { + case "dirty": + this.SendClearDirty(child.GetAttribute("type")); + break; + case "offline": + //this.SendQrSync(null); + break; + default: + throw new NotImplementedException(node.NodeString()); + } + } + } + + if (node.tag == "chatstate") + { + string state = node.children.FirstOrDefault().tag; + switch (state) + { + case "composing": + this.fireOnGetTyping(node.GetAttribute("from")); + break; + case "paused": + this.fireOnGetPaused(node.GetAttribute("from")); + break; + default: + throw new NotImplementedException(node.NodeString()); + } + } + + if (node.tag == "ack") + { + string cls = node.GetAttribute("class"); + if (cls == "message") + { + //server receipt + this.fireOnGetMessageReceivedServer(node.GetAttribute("from"), node.GetAttribute("id")); + } + } + + if (node.tag == "notification") + { + this.handleNotification(node); + } + + return true; + } + } + catch (Exception e) + { + throw e; + } + return false; + } + + protected void handleMessage(ProtocolTreeNode node, bool autoReceipt) + { + if (!string.IsNullOrEmpty(node.GetAttribute("notify"))) + { + string name = node.GetAttribute("notify"); + this.fireOnGetContactName(node.GetAttribute("from"), name); + } + if (node.GetAttribute("type") == "error") + { + throw new NotImplementedException(node.NodeString()); + } + if (node.GetChild("body") != null) + { + //text message + this.fireOnGetMessage(node, node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData()), autoReceipt); + if (autoReceipt) + { + this.sendMessageReceived(node); + } + } + if (node.GetChild("media") != null) + { + ProtocolTreeNode media = node.GetChild("media"); + //media message + + //define variables in switch + string file, url, from, id; + int size; + byte[] preview, dat; + id = node.GetAttribute("id"); + from = node.GetAttribute("from"); + switch (media.GetAttribute("type")) + { + case "image": + url = media.GetAttribute("url"); + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + preview = media.GetData(); + this.fireOnGetMessageImage(from, id, file, size, url, preview); + break; + case "audio": + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + url = media.GetAttribute("url"); + preview = media.GetData(); + this.fireOnGetMessageAudio(from, id, file, size, url, preview); + break; + case "video": + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + url = media.GetAttribute("url"); + preview = media.GetData(); + this.fireOnGetMessageVideo(from, id, file, size, url, preview); + break; + case "location": + double lon = double.Parse(media.GetAttribute("longitude"), System.Globalization.CultureInfo.InvariantCulture); + double lat = double.Parse(media.GetAttribute("latitude"), System.Globalization.CultureInfo.InvariantCulture); + preview = media.GetData(); + name = media.GetAttribute("name"); + url = media.GetAttribute("url"); + this.fireOnGetMessageLocation(from, id, lon, lat, url, name, preview); + break; + case "vcard": + ProtocolTreeNode vcard = media.GetChild("vcard"); + name = vcard.GetAttribute("name"); + dat = vcard.GetData(); + this.fireOnGetMessageVcard(from, id, name, dat); + break; + } + this.sendMessageReceived(node); + } + } + + protected void handleIq(ProtocolTreeNode node) + { + if (node.GetAttribute("type") == "error") + { + this.fireOnError(node.GetAttribute("id"), node.GetAttribute("from"), Int32.Parse(node.GetChild("error").GetAttribute("code")), node.GetChild("error").GetAttribute("text")); + } + if (node.GetChild("sync") != null) + { + //sync result + ProtocolTreeNode sync = node.GetChild("sync"); + ProtocolTreeNode existing = sync.GetChild("in"); + ProtocolTreeNode nonexisting = sync.GetChild("out"); + //process existing first + Dictionary existingUsers = new Dictionary(); + foreach (ProtocolTreeNode child in existing.GetAllChildren()) + { + existingUsers.Add(System.Text.Encoding.UTF8.GetString(child.GetData()), child.GetAttribute("jid")); + } + //now process failed numbers + List failedNumbers = new List(); + foreach (ProtocolTreeNode child in nonexisting.GetAllChildren()) + { + failedNumbers.Add(System.Text.Encoding.UTF8.GetString(child.GetData())); + } + int index = 0; + Int32.TryParse(sync.GetAttribute("index"), out index); + this.fireOnGetSyncResult(index, sync.GetAttribute("sid"), existingUsers, failedNumbers.ToArray()); + } + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && node.children.FirstOrDefault().tag == "query" + && node.children.FirstOrDefault().GetAttribute("xmlns") == "jabber:iq:last" + ) + { + //last seen + DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.FirstOrDefault().GetAttribute("seconds")) * -1); + this.fireOnGetLastSeen(node.GetAttribute("from"), lastSeen); + } + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && (ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "media") || ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "duplicate")) + ) + { + //media upload + this.uploadResponse = node; + } + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "picture") + ) + { + //profile picture + string from = node.GetAttribute("from"); + string id = node.GetChild("picture").GetAttribute("id"); + byte[] dat = node.GetChild("picture").GetData(); + string type = node.GetChild("picture").GetAttribute("type"); + if (type == "preview") + { + this.fireOnGetPhotoPreview(from, id, dat); + } + else + { + this.fireOnGetPhoto(from, id, dat); + } + } + if (node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "ping")) + { + this.SendPong(node.GetAttribute("id")); + } + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "group")) + { + //group(s) info + List groups = new List(); + foreach (ProtocolTreeNode group in node.children) + { + groups.Add(new WaGroupInfo( + group.GetAttribute("id"), + group.GetAttribute("owner"), + long.Parse(group.GetAttribute("creation")), + group.GetAttribute("subject"), + long.Parse(group.GetAttribute("s_t")), + group.GetAttribute("s_o") + )); + } + this.fireOnGetGroups(groups.ToArray()); + } + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "participant")) + { + //group participants + List participants = new List(); + foreach (ProtocolTreeNode part in node.GetAllChildren()) + { + if (part.tag == "participant" && !string.IsNullOrEmpty(part.GetAttribute("jid"))) + { + participants.Add(part.GetAttribute("jid")); + } + } + this.fireOnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); + } + if (node.GetAttribute("type") == "result" && node.GetChild("status") != null) + { + foreach (ProtocolTreeNode status in node.GetChild("status").GetAllChildren()) + { + this.fireOnGetStatus(status.GetAttribute("jid"), + "result", + null, + WhatsApp.SYSEncoding.GetString(status.GetData())); + } + } + } + + protected void handleNotification(ProtocolTreeNode node) + { + if (!String.IsNullOrEmpty(node.GetAttribute("notify"))) + { + this.fireOnGetContactName(node.GetAttribute("from"), node.GetAttribute("notify")); + } + string type = node.GetAttribute("type"); + switch (type) + { + case "picture": + ProtocolTreeNode child = node.children.FirstOrDefault(); + this.fireOnNotificationPicture(child.tag, child.GetAttribute("jid"), child.GetAttribute("id")); + break; + case "status": + ProtocolTreeNode child2 = node.children.FirstOrDefault(); + this.fireOnGetStatus(node.GetAttribute("from"), child2.tag, node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(child2.GetData())); + break; + default: + throw new NotImplementedException(node.NodeString()); + } + this.SendNotificationAck(node); + } + + private void SendNotificationAck(ProtocolTreeNode node, string type = null) + { + string from = node.GetAttribute("from"); + string to = node.GetAttribute("to"); + string participant = node.GetAttribute("participant"); + string id = node.GetAttribute("id"); + if (type == null) + { + type = node.GetAttribute("type"); + } + List attributes = new List(); + if (!string.IsNullOrEmpty(to)) + { + attributes.Add(new KeyValue("from", to)); + } + if (!string.IsNullOrEmpty(participant)) + { + attributes.Add(new KeyValue("participant", participant)); + } + attributes.AddRange(new[] { + new KeyValue("to", from), + new KeyValue("class", node.tag), + new KeyValue("id", id), + new KeyValue("type", type) + }); + ProtocolTreeNode sendNode = new ProtocolTreeNode("ack", attributes.ToArray()); + this.SendNode(sendNode); + } + + public void sendMessageReceived(ProtocolTreeNode msg, string response = "received") + { + FMessage tmpMessage = new FMessage(new FMessage.FMessageIdentifierKey(msg.GetAttribute("from"), true, msg.GetAttribute("id"))); + this.SendMessageReceived(tmpMessage, response); + } + + public void SendAvailableForChat(string nickName, bool isHidden = false) + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName) }); + this.SendNode(node); + } + + public void SendClearDirty(IEnumerable categoryNames) + { + string id = TicketCounter.MakeId("clean_dirty_"); + List children = new List(); + foreach (string category in categoryNames) + { + ProtocolTreeNode cat = new ProtocolTreeNode("clean", new[] { new KeyValue("type", category) }); + children.Add(cat); + } + var node = new ProtocolTreeNode("iq", + new[] + { + new KeyValue("id", id), + new KeyValue("type", "set"), + new KeyValue("to", "s.whatsapp.net"), + new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") + }, children); + this.SendNode(node); + } + + public void SendClearDirty(string category) + { + this.SendClearDirty(new string[] { category }); + } + + protected void SendDeliveredReceiptAck(string to, string id) + { + this.SendReceiptAck(to, id, "delivered"); + } + + protected void SendMessageReceived(FMessage message, string response) + { + ProtocolTreeNode node = new ProtocolTreeNode("receipt", new[] { + new KeyValue("to", message.identifier_key.remote_jid), + new KeyValue("id", message.identifier_key.id) + }); + + this.SendNode(node); + } + + protected void SendNotificationReceived(string jid, string id) + { + var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); + var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "notification"), new KeyValue("id", id) }, child); + this.SendNode(node); + } + + protected void SendPong(string id) + { + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "result"), new KeyValue("to", WhatsConstants.WhatsAppRealm), new KeyValue("id", id) }); + this.SendNode(node); + } + + private void SendReceiptAck(string to, string id, string receiptType) + { + var tmpChild = new ProtocolTreeNode("ack", new[] { new KeyValue("xmlns", "urn:xmpp:receipts"), new KeyValue("type", receiptType) }); + var resultNode = new ProtocolTreeNode("message", new[] + { + new KeyValue("to", to), + new KeyValue("type", "chat"), + new KeyValue("id", id) + }, tmpChild); + this.SendNode(resultNode); + } + } +} diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs index 53b7f6d..c94aecf 100644 --- a/WhatsAppApi/WhatsApp.cs +++ b/WhatsAppApi/WhatsApp.cs @@ -20,51 +20,13 @@ namespace WhatsAppApi /// /// Main api interface /// - public class WhatsApp : WhatsAppBase + public class WhatsApp : WhatsSendBase { public WhatsApp(string phoneNum, string imei, string nick, bool debug = false, bool hidden = false) { this._constructBase(phoneNum, imei, nick, debug, hidden); } - public void Login(byte[] nextChallenge = null) - { - //reset stuff - this.reader.Key = null; - this.BinWriter.Key = null; - this._challengeBytes = null; - - if (nextChallenge != null) - { - this._challengeBytes = nextChallenge; - } - - string resource = string.Format(@"{0}-{1}-{2}", - WhatsConstants.Device, - WhatsConstants.WhatsAppVer, - WhatsConstants.WhatsPort); - var data = this.BinWriter.StartStream(WhatsConstants.WhatsAppServer, resource); - var feat = this.addFeatures(); - var auth = this.addAuth(); - this.SendData(data); - this.SendData(this.BinWriter.Write(feat, false)); - this.SendData(this.BinWriter.Write(auth, false)); - - this.pollMessage();//stream start - this.pollMessage();//features - this.pollMessage();//challenge or success - - if (this.loginStatus != CONNECTION_STATUS.LOGGEDIN) - { - //oneshot failed - ProtocolTreeNode authResp = this.addAuthResponse(); - this.SendData(this.BinWriter.Write(authResp, false)); - this.pollMessage(); - } - - this.SendAvailableForChat(this.name, this.hidden); - } - public void Message(string to, string txt) { var tmpMessage = new FMessage(GetJID(to), true) { data = txt }; @@ -237,7 +199,7 @@ public void MessageAudio(string to, string filepath) } } - private WaUploadResponse UploadFile(string b64hash, string type, long size, string path, string to, string contenttype) + protected WaUploadResponse UploadFile(string b64hash, string type, long size, string path, string to, string contenttype) { ProtocolTreeNode media = new ProtocolTreeNode("media", new KeyValue[] { new KeyValue("hash", b64hash), @@ -356,487 +318,6 @@ private WaUploadResponse UploadFile(string b64hash, string type, long size, stri return null; } - public void PollMessages(bool autoReceipt = true) - { - while (pollMessage(autoReceipt)) ; - } - - public bool pollMessage(bool autoReceipt = true) - { - if (this.loginStatus == CONNECTION_STATUS.CONNECTED || this.loginStatus == CONNECTION_STATUS.LOGGEDIN) - { - byte[] nodeData; - try - { - nodeData = this.whatsNetwork.ReadNextNode(); - if (nodeData != null) - { - return this.processInboundData(nodeData, autoReceipt); - } - } - catch (ConnectionException) - { - this.Disconnect(); - } - } - return false; - } - - protected ProtocolTreeNode addFeatures() - { - return new ProtocolTreeNode("stream:features", null); - } - - protected ProtocolTreeNode addAuth() - { - List attr = new List(new KeyValue[] { - new KeyValue("mechanism", Helper.KeyStream.AuthMethod), - new KeyValue("user", this.phoneNumber)}); - if (this.hidden) - { - attr.Add(new KeyValue("passive", "true")); - } - var node = new ProtocolTreeNode("auth", attr.ToArray(), null, this.getAuthBlob()); - return node; - } - - protected byte[] getAuthBlob() - { - byte[] data = null; - if (this._challengeBytes != null) - { - byte[][] keys = KeyStream.GenerateKeys(this.encryptPassword(), this._challengeBytes); - - this.reader.Key = new KeyStream(keys[2], keys[3]); - - this.outputKey = new KeyStream(keys[0], keys[1]); - - PhoneNumber pn = new PhoneNumber(this.phoneNumber); - - List b = new List(); - b.AddRange(new byte[] { 0, 0, 0, 0 }); - b.AddRange(WhatsApp.SYSEncoding.GetBytes(this.phoneNumber)); - b.AddRange(this._challengeBytes); - b.AddRange(WhatsApp.SYSEncoding.GetBytes(Helper.Func.GetNowUnixTimestamp().ToString())); - b.AddRange(WhatsApp.SYSEncoding.GetBytes(WhatsConstants.UserAgent)); - b.AddRange(WhatsApp.SYSEncoding.GetBytes(String.Format(" MccMnc/{0}001", pn.MCC))); - data = b.ToArray(); - - this._challengeBytes = null; - - this.outputKey.EncodeMessage(data, 0, 4, data.Length - 4); - - this.BinWriter.Key = this.outputKey; - } - - return data; - } - - protected ProtocolTreeNode addAuthResponse() - { - if (this._challengeBytes != null) - { - byte[][] keys = KeyStream.GenerateKeys(this.encryptPassword(), this._challengeBytes); - - this.reader.Key = new KeyStream(keys[2], keys[3]); - this.BinWriter.Key = new KeyStream(keys[0], keys[1]); - - List b = new List(); - b.AddRange(new byte[] { 0, 0, 0, 0 }); - b.AddRange(WhatsApp.SYSEncoding.GetBytes(this.phoneNumber)); - b.AddRange(this._challengeBytes); - - - byte[] data = b.ToArray(); - this.BinWriter.Key.EncodeMessage(data, 0, 4, data.Length - 4); - var node = new ProtocolTreeNode("response", - new KeyValue[] { new KeyValue("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") }, - data); - - return node; - } - throw new Exception("Auth response error"); - } - - protected void processChallenge(ProtocolTreeNode node) - { - _challengeBytes = node.data; - } - - protected bool processInboundData(byte[] msgdata, bool autoReceipt = true) - { - try - { - ProtocolTreeNode node = this.reader.nextTree(msgdata); - if(node != null) - { - if (ProtocolTreeNode.TagEquals(node, "challenge")) - { - this.processChallenge(node); - } - else if (ProtocolTreeNode.TagEquals(node, "success")) - { - this.loginStatus = CONNECTION_STATUS.LOGGEDIN; - this.accountinfo = new AccountInfo(node.GetAttribute("status"), - node.GetAttribute("kind"), - node.GetAttribute("creation"), - node.GetAttribute("expiration")); - this.fireOnLoginSuccess(this.phoneNumber, node.GetData()); - } - else if (ProtocolTreeNode.TagEquals(node, "failure")) - { - this.loginStatus = CONNECTION_STATUS.UNAUTHORIZED; - this.fireOnLoginFailed(node.children.FirstOrDefault().tag); - } - - if (ProtocolTreeNode.TagEquals(node, "receipt")) - { - string from = node.GetAttribute("from"); - string id = node.GetAttribute("id"); - string type = node.GetAttribute("type") ?? "delivery"; - switch (type) - { - case "delivery": - //delivered to target - this.fireOnGetMessageReceivedClient(from, id); - break; - case "read": - //read by target - //todo - break; - case "played": - //played by target - //todo - break; - } - - //send ack - SendNotificationAck(node, type); - } - - if (ProtocolTreeNode.TagEquals(node, "message")) - { - this.handleMessage(node, autoReceipt); - } - - - if (ProtocolTreeNode.TagEquals(node, "iq")) - { - this.handleIq(node); - } - - if (ProtocolTreeNode.TagEquals(node, "stream:error")) - { - var textNode = node.GetChild("text"); - if (textNode != null) - { - string content = WhatsApp.SYSEncoding.GetString(textNode.GetData()); - Console.WriteLine("Error : " + content); - } - this.Disconnect(); - } - - if (ProtocolTreeNode.TagEquals(node, "presence")) - { - //presence node - this.fireOnGetPresence(node.GetAttribute("from"), node.GetAttribute("type")); - } - - if (node.tag == "ib") - { - foreach (ProtocolTreeNode child in node.children) - { - switch (child.tag) - { - case "dirty": - this.SendClearDirty(child.GetAttribute("type")); - break; - case "offline": - //this.SendQrSync(null); - break; - default: - throw new NotImplementedException(node.NodeString()); - } - } - } - - if (node.tag == "chatstate") - { - string state = node.children.FirstOrDefault().tag; - switch (state) - { - case "composing": - this.fireOnGetTyping(node.GetAttribute("from")); - break; - case "paused": - this.fireOnGetPaused(node.GetAttribute("from")); - break; - default: - throw new NotImplementedException(node.NodeString()); - } - } - - if (node.tag == "ack") - { - string cls = node.GetAttribute("class"); - if (cls == "message") - { - //server receipt - this.fireOnGetMessageReceivedServer(node.GetAttribute("from"), node.GetAttribute("id")); - } - } - - if (node.tag == "notification") - { - this.handleNotification(node); - } - - return true; - } - } - catch (Exception e) - { - throw e; - } - return false; - } - - protected void handleMessage(ProtocolTreeNode node, bool autoReceipt) - { - if (!string.IsNullOrEmpty(node.GetAttribute("notify"))) - { - string name = node.GetAttribute("notify"); - this.fireOnGetContactName(node.GetAttribute("from"), name); - } - if (node.GetAttribute("type") == "error") - { - throw new NotImplementedException(node.NodeString()); - } - if (node.GetChild("body") != null) - { - //text message - this.fireOnGetMessage(node, node.GetAttribute("from"), node.GetAttribute("id"), node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData()), autoReceipt); - if (autoReceipt) - { - this.sendMessageReceived(node); - } - } - if (node.GetChild("media") != null) - { - ProtocolTreeNode media = node.GetChild("media"); - //media message - - //define variables in switch - string file, url, from, id; - int size; - byte[] preview, dat; - id = node.GetAttribute("id"); - from = node.GetAttribute("from"); - switch (media.GetAttribute("type")) - { - case "image": - url = media.GetAttribute("url"); - file = media.GetAttribute("file"); - size = Int32.Parse(media.GetAttribute("size")); - preview = media.GetData(); - this.fireOnGetMessageImage(from, id, file, size, url, preview); - break; - case "audio": - file = media.GetAttribute("file"); - size = Int32.Parse(media.GetAttribute("size")); - url = media.GetAttribute("url"); - preview = media.GetData(); - this.fireOnGetMessageAudio(from, id, file, size, url, preview); - break; - case "video": - file = media.GetAttribute("file"); - size = Int32.Parse(media.GetAttribute("size")); - url = media.GetAttribute("url"); - preview = media.GetData(); - this.fireOnGetMessageVideo(from, id, file, size, url, preview); - break; - case "location": - double lon = double.Parse(media.GetAttribute("longitude"), System.Globalization.CultureInfo.InvariantCulture); - double lat = double.Parse(media.GetAttribute("latitude"), System.Globalization.CultureInfo.InvariantCulture); - preview = media.GetData(); - name = media.GetAttribute("name"); - url = media.GetAttribute("url"); - this.fireOnGetMessageLocation(from, id, lon, lat, url, name, preview); - break; - case "vcard": - ProtocolTreeNode vcard = media.GetChild("vcard"); - name = vcard.GetAttribute("name"); - dat = vcard.GetData(); - this.fireOnGetMessageVcard(from, id, name, dat); - break; - } - this.sendMessageReceived(node); - } - } - - protected void handleIq(ProtocolTreeNode node) - { - if (node.GetAttribute("type") == "error") - { - this.fireOnError(node.GetAttribute("id"), node.GetAttribute("from"), Int32.Parse(node.GetChild("error").GetAttribute("code")), node.GetChild("error").GetAttribute("text")); - } - if (node.GetChild("sync") != null) - { - //sync result - ProtocolTreeNode sync = node.GetChild("sync"); - ProtocolTreeNode existing = sync.GetChild("in"); - ProtocolTreeNode nonexisting = sync.GetChild("out"); - //process existing first - Dictionary existingUsers = new Dictionary(); - foreach (ProtocolTreeNode child in existing.GetAllChildren()) - { - existingUsers.Add(System.Text.Encoding.UTF8.GetString(child.GetData()), child.GetAttribute("jid")); - } - //now process failed numbers - List failedNumbers = new List(); - foreach (ProtocolTreeNode child in nonexisting.GetAllChildren()) - { - failedNumbers.Add(System.Text.Encoding.UTF8.GetString(child.GetData())); - } - int index = 0; - Int32.TryParse(sync.GetAttribute("index"), out index); - this.fireOnGetSyncResult(index, sync.GetAttribute("sid"), existingUsers, failedNumbers.ToArray()); - } - if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && node.children.FirstOrDefault().tag == "query" - && node.children.FirstOrDefault().GetAttribute("xmlns") == "jabber:iq:last" - ) - { - //last seen - DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.FirstOrDefault().GetAttribute("seconds")) * -1); - this.fireOnGetLastSeen(node.GetAttribute("from"), lastSeen); - } - if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && (ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "media") || ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "duplicate")) - ) - { - //media upload - this.uploadResponse = node; - } - if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "picture") - ) - { - //profile picture - string from = node.GetAttribute("from"); - string id = node.GetChild("picture").GetAttribute("id"); - byte[] dat = node.GetChild("picture").GetData(); - string type = node.GetChild("picture").GetAttribute("type"); - if (type == "preview") - { - this.fireOnGetPhotoPreview(from, id, dat); - } - else - { - this.fireOnGetPhoto(from, id, dat); - } - } - if (node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "ping")) - { - this.SendPong(node.GetAttribute("id")); - } - if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "group")) - { - //group(s) info - List groups = new List(); - foreach (ProtocolTreeNode group in node.children) - { - groups.Add(new WaGroupInfo( - group.GetAttribute("id"), - group.GetAttribute("owner"), - long.Parse(group.GetAttribute("creation")), - group.GetAttribute("subject"), - long.Parse(group.GetAttribute("s_t")), - group.GetAttribute("s_o") - )); - } - this.fireOnGetGroups(groups.ToArray()); - } - if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) - && ProtocolTreeNode.TagEquals(node.children.FirstOrDefault(), "participant")) - { - //group participants - List participants = new List(); - foreach (ProtocolTreeNode part in node.GetAllChildren()) - { - if (part.tag == "participant" && !string.IsNullOrEmpty(part.GetAttribute("jid"))) - { - participants.Add(part.GetAttribute("jid")); - } - } - this.fireOnGetGroupParticipants(node.GetAttribute("from"), participants.ToArray()); - } - if (node.GetAttribute("type") == "result" && node.GetChild("status") != null) - { - foreach (ProtocolTreeNode status in node.GetChild("status").GetAllChildren()) - { - this.fireOnGetStatus(status.GetAttribute("jid"), - "result", - null, - WhatsApp.SYSEncoding.GetString(status.GetData())); - } - } - } - - protected void handleNotification(ProtocolTreeNode node) - { - if (!String.IsNullOrEmpty(node.GetAttribute("notify"))) - { - this.fireOnGetContactName(node.GetAttribute("from"), node.GetAttribute("notify")); - } - string type = node.GetAttribute("type"); - switch (type) - { - case "picture": - ProtocolTreeNode child = node.children.FirstOrDefault(); - this.fireOnNotificationPicture(child.tag, child.GetAttribute("jid"), child.GetAttribute("id")); - break; - case "status": - ProtocolTreeNode child2 = node.children.FirstOrDefault(); - this.fireOnGetStatus(node.GetAttribute("from"), child2.tag, node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(child2.GetData())); - break; - default: - throw new NotImplementedException(node.NodeString()); - } - this.SendNotificationAck(node); - } - - private void SendNotificationAck(ProtocolTreeNode node, string type = null) - { - string from = node.GetAttribute("from"); - string to = node.GetAttribute("to"); - string participant = node.GetAttribute("participant"); - string id = node.GetAttribute("id"); - if (type == null) - { - type = node.GetAttribute("type"); - } - List attributes = new List(); - if(!string.IsNullOrEmpty(to)) - { - attributes.Add(new KeyValue("from", to)); - } - if(!string.IsNullOrEmpty(participant)) - { - attributes.Add(new KeyValue("participant", participant)); - } - attributes.AddRange(new [] { - new KeyValue("to", from), - new KeyValue("class", node.tag), - new KeyValue("id", id), - new KeyValue("type", type) - }); - ProtocolTreeNode sendNode = new ProtocolTreeNode("ack", attributes.ToArray()); - this.SendNode(sendNode); - } - protected void SendQrSync(byte[] qrkey, byte[] token = null) { string id = TicketCounter.MakeId("qrsync_"); @@ -853,13 +334,7 @@ protected void SendQrSync(byte[] qrkey, byte[] token = null) }, children.ToArray()); this.SendNode(node); } - - public void sendMessageReceived(ProtocolTreeNode msg, string response = "received") - { - FMessage tmpMessage = new FMessage(new FMessage.FMessageIdentifierKey(msg.GetAttribute("from"), true, msg.GetAttribute("id"))); - this.SendMessageReceived(tmpMessage, response); - } - + public void SendActive() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "active") }); @@ -872,43 +347,12 @@ public void SendAddParticipants(string gjid, IEnumerable participants) this.SendVerbParticipants(gjid, participants, id, "add"); } - public void SendAvailableForChat(string nickName, bool isHidden = false) - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName) }); - this.SendNode(node); - } - public void SendUnavailable() { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); this.SendNode(node); } - public void SendClearDirty(IEnumerable categoryNames) - { - string id = TicketCounter.MakeId("clean_dirty_"); - List children = new List(); - foreach (string category in categoryNames) - { - ProtocolTreeNode cat = new ProtocolTreeNode("clean", new[] { new KeyValue("type", category) }); - children.Add(cat); - } - var node = new ProtocolTreeNode("iq", - new[] - { - new KeyValue("id", id), - new KeyValue("type", "set"), - new KeyValue("to", "s.whatsapp.net"), - new KeyValue("xmlns", "urn:xmpp:whatsapp:dirty") - }, children); - this.SendNode(node); - } - - public void SendClearDirty(string category) - { - this.SendClearDirty(new string[] { category }); - } - public void SendClientConfig(string platform, string lg, string lc) { string v = TicketCounter.MakeId("config_"); @@ -1002,11 +446,6 @@ public void SendDeleteFromRoster(string jid) this.SendNode(node); } - protected void SendDeliveredReceiptAck(string to, string id) - { - this.SendReceiptAck(to, id, "delivered"); - } - public void SendEndGroupChat(string gjid) { string id = TicketCounter.MakeId("remove_group_"); @@ -1199,28 +638,11 @@ public void SendMessageBroadcast(string[] to, FMessage message) } } - protected void SendMessageReceived(FMessage message, string response) - { - ProtocolTreeNode node = new ProtocolTreeNode("receipt", new[] { - new KeyValue("to", message.identifier_key.remote_jid), - new KeyValue("id", message.identifier_key.id) - }); - - this.SendNode(node); - } - public void SendNop() { this.SendNode(null); } - protected void SendNotificationReceived(string jid, string id) - { - var child = new ProtocolTreeNode("received", new[] { new KeyValue("xmlns", "urn:xmpp:receipts") }); - var node = new ProtocolTreeNode("message", new[] { new KeyValue("to", jid), new KeyValue("type", "notification"), new KeyValue("id", id) }, child); - this.SendNode(node); - } - public void SendPaused(string to) { this.SendChatState(to, "paused"); @@ -1234,12 +656,6 @@ public void SendPing() this.SendNode(node); } - protected void SendPong(string id) - { - var node = new ProtocolTreeNode("iq", new[] { new KeyValue("type", "result"), new KeyValue("to", WhatsConstants.WhatsAppRealm), new KeyValue("id", id) }); - this.SendNode(node); - } - public void SendPresenceSubscriptionRequest(string to) { var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "subscribe"), new KeyValue("to", GetJID(to)) }); @@ -1408,18 +824,6 @@ protected IEnumerable ProcessGroupSettings(IEnumerable