diff --git a/.gitattributes b/.gitattributes index a664be3..412eeda 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,49 +1,22 @@ -*.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 +# Auto detect text files and perform LF normalization +* text=auto -*.jpg binary -*.png binary -*.gif binary +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union -*.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 +# 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 index aa8811c..db3352a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ - ################# ## Eclipse ################# @@ -6,13 +5,15 @@ *.pydevproject .project .metadata -bin/** -tmp/** -tmp/**/* +bin/ +obj/ +tmp/ *.tmp *.bak *.swp *~.nib +*.pdb +*.cache local.properties .classpath .settings/ @@ -44,8 +45,8 @@ local.properties *.sln.docstates # Build results -**/[Dd]ebug/ -**/[Rr]elease/ +[Dd]ebug/ +[Rr]elease/ *_i.c *_p.c *.ilk @@ -63,10 +64,10 @@ local.properties *.tmp *.vspscc .builds -**/*.dotCover +*.dotCover ## TODO: If you have NuGet Package Restore enabled, uncomment this -#**/packages/ +#packages/ # Visual C++ cache files ipch/ @@ -82,7 +83,7 @@ ipch/ # ReSharper is a .NET coding add-in _ReSharper* -# Installshield output folder +# Installshield output folder [Ee]xpress # DocProject is a documentation generator add-in @@ -123,7 +124,7 @@ UpgradeLog*.XML ############ # Windows image file caches -Thumbs.db +Thumbs.db # Folder config file Desktop.ini 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 index e69de29..0185685 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,38 @@ +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. + +## 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. + +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) + +### 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. + + +### Code request token +The current implementation for token generation is created by [pastoso](https://github.com/pastoso). diff --git a/src/WhatsAppApi/Account/WhatsUser.cs b/WhatsAppApi/Account/WhatsUser.cs similarity index 91% rename from src/WhatsAppApi/Account/WhatsUser.cs rename to WhatsAppApi/Account/WhatsUser.cs index d55a5e6..ad15fc1 100644 --- a/src/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) diff --git a/src/WhatsAppApi/Account/WhatsUserManager.cs b/WhatsAppApi/Account/WhatsUserManager.cs similarity index 78% rename from src/WhatsAppApi/Account/WhatsUserManager.cs rename to WhatsAppApi/Account/WhatsUserManager.cs index fd5497d..7ada04a 100644 --- a/src/WhatsAppApi/Account/WhatsUserManager.cs +++ b/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/WhatsAppApi/Base/ApiBase.cs b/WhatsAppApi/Base/ApiBase.cs new file mode 100644 index 0000000..e553014 --- /dev/null +++ b/WhatsAppApi/Base/ApiBase.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace WhatsAppApi +{ + public class ApiBase + { + public enum CONNECTION_STATUS + { + UNAUTHORIZED, + DISCONNECTED, + CONNECTED, + LOGGEDIN + } + + public enum ImageType + { + JPEG, + GIF, + PNG + } + + public enum VideoType + { + MOV, + AVI, + MP4 + } + + public enum AudioType + { + WAV, + OGG, + MP3 + } + + public enum VisibilityCategory + { + ProfilePhoto, + Status, + LastSeenTime + } + + public enum VisibilitySetting + { + None, + Contacts, + Everyone + } + + public enum SyncMode + { + Full, + Delta, + Query, + Chunked + } + + public enum SyncContext + { + Interactive, + Background, + Registration + } + + protected string privacySettingToString(VisibilitySetting s) + { + switch (s) + { + case VisibilitySetting.None: + return "none"; + case VisibilitySetting.Contacts: + return "contacts"; + case VisibilitySetting.Everyone: + return "all"; + default: + throw new Exception("Invalid visibility setting"); + } + } + + protected string privacyCategoryToString(VisibilityCategory c) + { + switch (c) + { + case VisibilityCategory.LastSeenTime: + return "last"; + case VisibilityCategory.Status: + return "status"; + case VisibilityCategory.ProfilePhoto: + return "profile"; + default: + throw new Exception("Invalid privacy category"); + } + } + + protected VisibilityCategory parsePrivacyCategory(string data) + { + switch (data) + { + case "last": + return VisibilityCategory.LastSeenTime; + case "status": + return VisibilityCategory.Status; + case "profile": + return VisibilityCategory.ProfilePhoto; + default: + throw new Exception(String.Format("Could not parse {0} as privacy category", data)); + } + } + + protected VisibilitySetting parsePrivacySetting(string data) + { + switch (data) + { + case "none": + return VisibilitySetting.None; + case "contacts": + return VisibilitySetting.Contacts; + case "all": + return VisibilitySetting.Everyone; + default: + throw new Exception(string.Format("Cound not parse {0} as privacy setting", data)); + } + } + + protected byte[] CreateThumbnail(byte[] imageData) + { + Image image; + using (System.IO.MemoryStream m = new System.IO.MemoryStream(imageData)) + { + image = Image.FromStream(m); + } + if (image != null) + { + int newHeight = 0; + int newWidth = 0; + float imgWidth = float.Parse(image.Width.ToString()); + float imgHeight = float.Parse(image.Height.ToString()); + if (image.Width > image.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(image, new Rectangle(0, 0, newWidth, newHeight)); + } + System.IO.MemoryStream ms = new System.IO.MemoryStream(); + newImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); + ms.Close(); + return ms.ToArray(); + } + return null; + } + + protected static DateTime GetDateTimeFromTimestamp(string timestamp) + { + long data = 0; + if (long.TryParse(timestamp, out data)) + { + return GetDateTimeFromTimestamp(data); + } + return DateTime.Now; + } + + protected static DateTime GetDateTimeFromTimestamp(long timestamp) + { + DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0); + return UnixEpoch.AddSeconds(timestamp); + } + + protected byte[] ProcessProfilePicture(byte[] bytes) + { + Bitmap image; + using (System.IO.MemoryStream m = new System.IO.MemoryStream(bytes)) + { + image = new Bitmap(Image.FromStream(m)); + } + if (image != null) + { + int size = 640; + if (size > image.Width) + size = image.Width; + if (size > image.Height) + size = image.Height; + + int newHeight = 0; + int newWidth = 0; + float imgWidth = float.Parse(image.Width.ToString()); + float imgHeight = float.Parse(image.Height.ToString()); + if (image.Width < image.Height) + { + newHeight = (int)((imgHeight / imgWidth) * size); + newWidth = size; + } + else + { + newWidth = (int)((imgWidth / imgHeight) * size); + newHeight = size; + } + + 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(image, new Rectangle(0, 0, newWidth, newHeight)); + } + + //crop square + Bitmap dest = newImage.Clone(new Rectangle( + new Point(0, 0), + new Size(size, size) + ), image.PixelFormat); + + System.IO.MemoryStream ms = new System.IO.MemoryStream(); + + System.Drawing.Imaging.Encoder enc = System.Drawing.Imaging.Encoder.Quality; + EncoderParameters encParams = new EncoderParameters(1); + + EncoderParameter param = new EncoderParameter(enc, 50L); + encParams.Param[0] = param; + dest.Save(ms, GetEncoder(ImageFormat.Jpeg), encParams); + ms.Close(); + return ms.ToArray(); + } + return bytes; + } + + private static ImageCodecInfo GetEncoder(ImageFormat format) + { + + ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders(); + + foreach (ImageCodecInfo codec in codecs) + { + if (codec.FormatID == format.Guid) + { + return codec; + } + } + return null; + } + + 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(); + } + + public static string GetJID(string target) + { + target = target.TrimStart(new char[] { '+', '0' }); + 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; + } + } +} diff --git a/WhatsAppApi/Base/WhatsAppBase.cs b/WhatsAppApi/Base/WhatsAppBase.cs new file mode 100644 index 0000000..ddf3749 --- /dev/null +++ b/WhatsAppApi/Base/WhatsAppBase.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using WhatsAppApi.Helper; +using WhatsAppApi.Parser; +using WhatsAppApi.Settings; + +namespace WhatsAppApi +{ + public class WhatsAppBase : WhatsEventBase + { + public long m_LastSentInfo = 0; + + 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 password, string nick, bool debug, bool hidden) + { + this.messageQueue = new List(); + this.phoneNumber = phoneNum; + this.password = password; + 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; + } + + protected 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(); + } + } + + protected void SendNode(ProtocolTreeNode node) + { + m_LastSentInfo = DateTime.UtcNow.ToFileTime(); + this.SendData(this.BinWriter.Write(node)); + } + } +} diff --git a/WhatsAppApi/Base/WhatsEventBase.cs b/WhatsAppApi/Base/WhatsEventBase.cs new file mode 100644 index 0000000..0130db9 --- /dev/null +++ b/WhatsAppApi/Base/WhatsEventBase.cs @@ -0,0 +1,340 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using WhatsAppApi.Helper; +using WhatsAppApi.Response; + +namespace WhatsAppApi +{ + public class WhatsEventBase : ApiBase + { + //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(ProtocolTreeNode mediaNode, string from, string id, string fileName, int fileSize, string url, byte[] preview, string name) + { + if (this.OnGetMessageImage != null) + { + this.OnGetMessageImage(mediaNode, from, id, fileName, fileSize, url, preview, name); + } + } + + public event OnGetMediaDelegate OnGetMessageVideo; + protected void fireOnGetMessageVideo(ProtocolTreeNode mediaNode, string from, string id, string fileName, int fileSize, string url, byte[] preview, string name) + { + if (this.OnGetMessageVideo != null) + { + this.OnGetMessageVideo(mediaNode, from, id, fileName, fileSize, url, preview, name); + } + } + + public event OnGetMediaDelegate OnGetMessageAudio; + protected void fireOnGetMessageAudio(ProtocolTreeNode mediaNode, string from, string id, string fileName, int fileSize, string url, byte[] preview, string name) + { + if (this.OnGetMessageAudio != null) + { + this.OnGetMessageAudio(mediaNode, from, id, fileName, fileSize, url, preview, name); + } + } + + public event OnGetLocationDelegate OnGetMessageLocation; + protected void fireOnGetMessageLocation(ProtocolTreeNode locationNode, string from, string id, double lon, double lat, string url, string name, byte[] preview, string User) + { + if (this.OnGetMessageLocation != null) + { + this.OnGetMessageLocation(locationNode, from, id, lon, lat, url, name, preview, User); + } + } + + public event OnGetVcardDelegate OnGetMessageVcard; + protected void fireOnGetMessageVcard(ProtocolTreeNode vcardNode, string from, string id, string name, byte[] data) + { + if (this.OnGetMessageVcard != null) + { + this.OnGetMessageVcard(vcardNode, 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 participant, string id) + { + if (this.OnGetMessageReceivedServer != null) + { + this.OnGetMessageReceivedServer(from, participant, id); + } + } + + public event OnGetMessageReceivedDelegate OnGetMessageReceivedClient; + protected void FireOnGetMessageReceivedClient(string from, string participant, string id) + { + if (this.OnGetMessageReceivedClient != null) + { + this.OnGetMessageReceivedClient(from, participant, id); + } + } + + public event OnGetMessageReadDelegate OnGetMessageReadClient; + protected void FireOnGetMessageReadClient(string from, string participant, string id) + { + if (OnGetMessageReadClient != null) + { + OnGetMessageReadClient(from, participant, 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); + } + } + + public event OnGetPrivacySettingsDelegate OnGetPrivacySettings; + protected void fireOnGetPrivacySettings(Dictionary settings) + { + if (this.OnGetPrivacySettings != null) + { + this.OnGetPrivacySettings(settings); + } + } + + public event OnGetParticipantAddedDelegate OnGetParticipantAdded; + protected void fireOnGetParticipantAdded(string gjid, string jid, DateTime time) + { + if (this.OnGetParticipantAdded != null) + { + this.OnGetParticipantAdded(gjid, jid, time); + } + } + + public event OnGetParticipantRemovedDelegate OnGetParticipantRemoved; + protected void fireOnGetParticipantRemoved(string gjid, string jid, string author, DateTime time) + { + if (this.OnGetParticipantRemoved != null) + { + this.OnGetParticipantRemoved(gjid, jid, author, time); + } + } + + public event OnGetParticipantRenamedDelegate OnGetParticipantRenamed; + protected void fireOnGetParticipantRenamed(string gjid, string oldJid, string newJid, DateTime time) + { + if (this.OnGetParticipantRenamed != null) + { + this.OnGetParticipantRenamed(gjid, oldJid, newJid, time); + } + } + + public event OnGetGroupSubjectDelegate OnGetGroupSubject; + protected void fireOnGetGroupSubject(string gjid, string jid, string username, string subject, DateTime time) + { + if (this.OnGetGroupSubject != null) + { + this.OnGetGroupSubject(gjid, jid, username, subject, time); + } + } + + public event OnGetBroadcastListsDelegate OnGetBroadcastLists; + + protected void fireOnGetBroadcastLists(IEnumerable listIds) + { + if (OnGetBroadcastLists != null) + { + OnGetBroadcastLists(listIds); + } + } + + //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 participant, string id); + public delegate void OnGetMessageReadDelegate(string from, string participant, 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(ProtocolTreeNode mediaNode, string from, string id, string fileName, int fileSize, string url, byte[] preview, string name); + public delegate void OnGetLocationDelegate(ProtocolTreeNode locationNode, string from, string id, double lon, double lat, string url, string name, byte[] preview, string UserName); + public delegate void OnGetVcardDelegate(ProtocolTreeNode vcardNode, 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); + public delegate void OnGetPrivacySettingsDelegate(Dictionary settings); + public delegate void OnGetParticipantAddedDelegate(string gjid, string jid, DateTime time); + public delegate void OnGetParticipantRemovedDelegate(string gjid, string jid, string author, DateTime time); + public delegate void OnGetParticipantRenamedDelegate(string gjid, string oldJid, string newJid, DateTime time); + public delegate void OnGetGroupSubjectDelegate(string gjid, string jid, string username, string subject, DateTime time); + public delegate void OnGetBroadcastListsDelegate(IEnumerable listIds); + } +} diff --git a/WhatsAppApi/Base/WhatsSendBase.cs b/WhatsAppApi/Base/WhatsSendBase.cs new file mode 100644 index 0000000..66c2ef0 --- /dev/null +++ b/WhatsAppApi/Base/WhatsSendBase.cs @@ -0,0 +1,735 @@ +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 + { + protected bool m_usePoolMessages = false; + + 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(); + } + + Int32 unixTimestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; + TicketCounter.setLoginTime(unixTimestamp.ToString()); + this.SendAvailableForChat(this.name, this.hidden); + } + + public void PollMessages(bool autoReceipt = true) + { + m_usePoolMessages = true; + while (pollMessage(autoReceipt)) ; + m_usePoolMessages = false; + } + + 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() + { + ProtocolTreeNode readReceipts = new ProtocolTreeNode("readreceipts", null, null, null); + ProtocolTreeNode groups_v2 = new ProtocolTreeNode("groups_v2", null, null, null); + ProtocolTreeNode privacy = new ProtocolTreeNode("privacy", null, null, null); + ProtocolTreeNode presencev2 = new ProtocolTreeNode("presence", null, null, null); + return new ProtocolTreeNode("stream:features", null, new ProtocolTreeNode[] { readReceipts, groups_v2, privacy, presencev2 }, 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())); + 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", null, null, 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) + { + //foreach ( ProtocolTreeNode x in node.GetAllChildren() ) + //{ + // Console.Write(x.GetData().ToString()); + //} + + 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 participant = node.GetAttribute("participant"); + string id = node.GetAttribute("id"); + string type = node.GetAttribute("type") ?? "delivery"; + switch (type) + { + case "delivery": + //delivered to target + FireOnGetMessageReceivedClient(from, participant, id); + break; + case "read": + FireOnGetMessageReadClient(from, participant, id); + //todo + break; + case "played": + //played by target + //todo + break; + } + + var list = node.GetChild("list"); + if (list != null) + foreach (var receipt in list.GetAllChildren()) + { + FireOnGetMessageReceivedClient(from, participant, receipt.GetAttribute("id")); + } + + //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()); + Helper.DebugAdapter.Instance.fireOnPrintDebug("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 + FireOnGetMessageReceivedServer(node.GetAttribute("from"), node.GetAttribute("participant"), 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 || node.GetChild("enc") != null) + { + // text message + // encrypted messages have no body node. Instead, the encrypted cipher text is provided within the enc node + var contentNode = node.GetChild("body") ?? node.GetChild("enc"); + if (contentNode != null) + { + this.fireOnGetMessage(node, node.GetAttribute("from"), node.GetAttribute("id"), + node.GetAttribute("notify"), System.Text.Encoding.UTF8.GetString(contentNode.GetData()), + autoReceipt); + if (autoReceipt) + { + this.sendMessageReceived(node); + } + } + } + if (node.GetChild("media") != null) + { + ProtocolTreeNode media = node.GetChild("media"); + //media message + + //define variables in switch + string UserName; + string file, url, from, id; + int size; + byte[] preview, dat; + id = node.GetAttribute("id"); + from = node.GetAttribute("from"); + UserName = node.GetAttribute("notify"); + 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(node, from, id, file, size, url, preview, UserName); + break; + case "audio": + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + url = media.GetAttribute("url"); + preview = media.GetData(); + this.fireOnGetMessageAudio(node, from, id, file, size, url, preview, UserName); + break; + case "video": + file = media.GetAttribute("file"); + size = Int32.Parse(media.GetAttribute("size")); + url = media.GetAttribute("url"); + preview = media.GetData(); + this.fireOnGetMessageVideo(node, from, id, file, size, url, preview, UserName); + 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(node, from, id, lon, lat, url, name, preview, UserName); + break; + case "vcard": + ProtocolTreeNode vcard = media.GetChild("vcard"); + name = vcard.GetAttribute("name"); + dat = vcard.GetData(); + this.fireOnGetMessageVcard(node, 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(); + if (existing != null) + { + 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(); + if (nonexisting != null) + { + 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()); + } + + // last seen + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && node.GetChild("query") != null + ) + { + DateTime lastSeen = DateTime.Now.AddSeconds(double.Parse(node.children.FirstOrDefault().GetAttribute("seconds")) * -1); + this.fireOnGetLastSeen(node.GetAttribute("from"), lastSeen); + } + + // media upload + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && (node.GetChild("media") != null || node.GetChild("duplicate") != null) + ) + { + this.uploadResponse = node; + } + + // profile picture + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && node.GetChild("picture") != null + ) + { + 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); + } + } + + // ping + if (node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) + && node.GetChild("ping") != null) + { + this.SendPong(node.GetAttribute("id")); + } + + // group(s) info + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && node.GetChild("group") != null) + { + List groups = new List(); + foreach (ProtocolTreeNode group in node.children) + { + groups.Add(new WaGroupInfo( + group.GetAttribute("id"), + group.GetAttribute("owner"), + group.GetAttribute("creation"), + group.GetAttribute("subject"), + group.GetAttribute("s_t"), + group.GetAttribute("s_o") + )); + } + this.fireOnGetGroups(groups.ToArray()); + } + + // group participants + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && node.GetChild("participant") != null) + { + 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()); + } + + // status + 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())); + } + } + + // privacy + if (node.GetAttribute("type") == "result" && node.GetChild("privacy") != null) + { + Dictionary settings = new Dictionary(); + foreach (ProtocolTreeNode child in node.GetChild("privacy").GetAllChildren("category")) + { + settings.Add(this.parsePrivacyCategory( + child.GetAttribute("name")), + this.parsePrivacySetting(child.GetAttribute("value")) + ); + } + this.fireOnGetPrivacySettings(settings); + } + + // broadcast lists + if (node.GetAttribute("type").Equals("result", StringComparison.OrdinalIgnoreCase) + && node.GetChild("list") != null + ) + { + var lists = new List(); + foreach (var list in node.GetAllChildren("list")) + { + lists.Add(list.GetAttribute("id")); + } + fireOnGetBroadcastLists(lists); + } + } + + 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; + case "subject": + //fire username notify + this.fireOnGetContactName(node.GetAttribute("participant"), + node.GetAttribute("notify")); + //fire subject notify + this.fireOnGetGroupSubject(node.GetAttribute("from"), + node.GetAttribute("participant"), + node.GetAttribute("notify"), + System.Text.Encoding.UTF8.GetString(node.GetChild("body").GetData()), + GetDateTimeFromTimestamp(node.GetAttribute("t"))); + break; + case "contacts": + //TODO + break; + case "participant": + string gjid = node.GetAttribute("from"); + string t = node.GetAttribute("t"); + foreach (ProtocolTreeNode child3 in node.GetAllChildren()) + { + if (child3.tag == "add") + { + this.fireOnGetParticipantAdded(gjid, + child3.GetAttribute("jid"), + GetDateTimeFromTimestamp(t)); + } + else if (child3.tag == "remove") + { + this.fireOnGetParticipantRemoved(gjid, + child3.GetAttribute("jid"), + child3.GetAttribute("author"), + GetDateTimeFromTimestamp(t)); + } + else if (child3.tag == "modify") + { + this.fireOnGetParticipantRenamed(gjid, + child3.GetAttribute("remove"), + child3.GetAttribute("add"), + GetDateTimeFromTimestamp(t)); + } + } + break; + } + 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"); + + List attributes = new List(); + if (!string.IsNullOrEmpty(to)) + { + attributes.Add(new KeyValue("from", to)); + } + if (!string.IsNullOrEmpty(participant)) + { + attributes.Add(new KeyValue("participant", participant)); + } + + if (!string.IsNullOrEmpty(type)) + { + attributes.Add(new KeyValue("type", type)); + } + attributes.AddRange(new[] { + new KeyValue("to", from), + new KeyValue("class", node.tag), + new KeyValue("id", id) + }); + + ProtocolTreeNode sendNode = new ProtocolTreeNode("ack", attributes.ToArray()); + this.SendNode(sendNode); + } + + protected void sendMessageReceived(ProtocolTreeNode msg, string type = "read") + { + FMessage tmpMessage = new FMessage(new FMessage.FMessageIdentifierKey(msg.GetAttribute("from"), true, msg.GetAttribute("id"))); + this.SendMessageReceived(tmpMessage, type); + } + + public void SendAvailableForChat(string nickName = null, bool isHidden = false) + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", (!String.IsNullOrEmpty(nickName) ? nickName : this.name)) }); + this.SendNode(node); + } + + protected void SendClearDirty(IEnumerable categoryNames) + { + string id = TicketCounter.MakeId(); + 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); + } + + protected 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 type = "read") + { + + KeyValue toAttrib = new KeyValue("to", message.identifier_key.remote_jid); + KeyValue idAttrib = new KeyValue("id", message.identifier_key.id); + + var attribs = new List(); + attribs.Add(toAttrib); + attribs.Add(idAttrib); + if (type.Equals("read")) + { + KeyValue typeAttrib = new KeyValue("type", type); + attribs.Add(typeAttrib); + } + + ProtocolTreeNode node = new ProtocolTreeNode("receipt", attribs.ToArray()); + + 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") }); + 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/src/WhatsAppApi/Helper/AccountInfo.cs b/WhatsAppApi/Helper/AccountInfo.cs similarity index 84% rename from src/WhatsAppApi/Helper/AccountInfo.cs rename to WhatsAppApi/Helper/AccountInfo.cs index 2e4ea33..963d82f 100644 --- a/src/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 new file mode 100644 index 0000000..baecfa1 --- /dev/null +++ b/WhatsAppApi/Helper/BinTreeNodeReader.cs @@ -0,0 +1,360 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; + +namespace WhatsAppApi.Helper +{ + public class BinTreeNodeReader + { + public KeyStream Key; + private List buffer; + + public BinTreeNodeReader() + { + + } + + 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 && pInput.Length > 0) + { + this.buffer = new List(); + this.buffer.AddRange(pInput); + } + + int firstByte = this.peekInt8(); + int stanzaFlag = (firstByte & 0xF0) >> 4; + int stanzaSize = this.peekInt16(1) | ((firstByte & 0x0F) << 16); + + int flags = stanzaFlag; + int size = stanzaSize; + + this.readInt24(); + + bool isEncrypted = (stanzaFlag & 8) != 0; + + if (isEncrypted) + { + 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) + { + Helper.DebugAdapter.Instance.fireOnPrintDebug(e); + } + this.buffer.Clear(); + this.buffer.AddRange(treeData.Take(realStanzaSize).ToArray()); + } + 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("rx ")); + return node; + } + + return null; + } + + protected string getToken(int token) + { + string tokenString = null; + int num = -1; + new TokenDictionary().GetToken(token, ref num, ref tokenString); + if (tokenString == null) + { + token = readInt8(); + new TokenDictionary().GetToken(token, ref num, ref tokenString); + } + return tokenString; + } + + protected byte[] readBytes(int token) + { + byte[] ret = new byte[0]; + if (token == -1) + { + throw new Exception("BinTreeNodeReader->readString: Invalid token " + token); + } + if ((token > 2) && (token < 245)) + { + ret = WhatsApp.SYSEncoding.GetBytes(this.getToken(token)); + } + else if (token == 0) + { + ret = new byte[0]; + } + else if (token == 252) + { + int size = this.readInt8(); + ret = this.fillArray(size); + } + else if (token == 253) + { + int size = this.readInt24(); + ret = this.fillArray(size); + } + else if (token == 254) + { + int tmpToken = this.readInt8(); + ret = WhatsApp.SYSEncoding.GetBytes(this.getToken(tmpToken + 0xf5)); + } + else if (token == 250) + { + 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 = WhatsApp.SYSEncoding.GetBytes(user + "@" + server); + } + else if (server.Length > 0) + { + ret = WhatsApp.SYSEncoding.GetBytes(server); + } + } + else if (token == 255) + { + ret = WhatsApp.SYSEncoding.GetBytes(ReadNibble()); + } + return ret; + } + + protected string ReadNibble() + { + var nextByte = readInt8(); + + var ignoreLastNibble = (nextByte & 0x80) != 0; + var size = (nextByte & 0x7f); + var nrOfNibbles = size * 2 - (ignoreLastNibble ? 1 : 0); + + var data = fillArray(size); + var chars = new List(); + + for (int i = 0; i < nrOfNibbles; i++) + { + nextByte = data[(int)Math.Floor(i / 2.0)]; + + var shift = 4 * (1 - i % 2); + byte dec = (byte)((nextByte & (15 << shift)) >> shift); + + switch (dec) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + chars.Add(dec.ToString()[0]); + break; + case 10: + chars.Add('-'); + break; + case 11: + chars.Add('.'); + break; + default: + throw new Exception("Bad nibble: " + dec); + } + } + return new string(chars.ToArray()); + } + + protected IEnumerable readAttributes(int size) + { + var attributes = new List(); + int attribCount = (size - 2 + size % 2) / 2; + for (int i = 0; i < attribCount; i++) + { + 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; + } + + protected ProtocolTreeNode nextTreeInternal() + { + int token1 = this.readInt8(); + int size = this.readListSize(token1); + int token2 = this.readInt8(); + if (token2 == 1) + { + var attributes = this.readAttributes(size); + return new ProtocolTreeNode("start", attributes); + } + if (token2 == 2) + { + return null; + } + string tag = WhatsApp.SYSEncoding.GetString(this.readBytes(token2)); + var tmpAttributes = this.readAttributes(size); + + if ((size % 2) == 1) + { + return new ProtocolTreeNode(tag, tmpAttributes); + } + int token3 = this.readInt8(); + if (this.isListTag(token3)) + { + return new ProtocolTreeNode(tag, tmpAttributes, this.readList(token3)); + } + + return new ProtocolTreeNode(tag, tmpAttributes, null, this.readBytes(token3)); + } + + protected bool isListTag(int token) + { + return ((token == 248) || (token == 0) || (token == 249)); + } + + protected List readList(int token) + { + int size = this.readListSize(token); + var ret = new List(); + for (int i = 0; i < size; i++) + { + ret.Add(this.nextTreeInternal()); + } + return ret; + } + + protected int readListSize(int token) + { + int size = 0; + if (token == 0) + { + size = 0; + } + else if (token == 0xf8) + { + size = this.readInt8(); + } + else if (token == 0xf9) + { + size = this.readInt16(); + } + else + { + throw new Exception("BinTreeNodeReader->readListSize: Invalid token " + token); + } + return size; + } + + 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.buffer.Count >= 3 + offset) + { + ret = (this.buffer[0 + offset] << 16) + (this.buffer[1 + offset] << 8) + this.buffer[2 + offset]; + } + return ret; + } + + protected int readInt24() + { + int ret = 0; + if (this.buffer.Count >= 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 offset = 0) + { + int ret = 0; + if (this.buffer.Count >= offset + 2) + { + ret = (int)this.buffer[0 + offset] << 8; + ret |= (int)this.buffer[1 + offset] << 0; + } + return ret; + } + + protected int readInt16() + { + int ret = 0; + if (this.buffer.Count >= 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.buffer.Count >= 1) + { + ret = (int)this.buffer[0]; + this.buffer.RemoveAt(0); + } + return ret; + } + + protected byte[] fillArray(int len) + { + byte[] ret = new byte[len]; + if (this.buffer.Count >= 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) + { + if (WhatsApp.DEBUG && debugMsg.Length > 0) + { + Helper.DebugAdapter.Instance.fireOnPrintDebug(debugMsg); + } + } + } +} diff --git a/WhatsAppApi/Helper/BinTreeNodeWriter.cs b/WhatsAppApi/Helper/BinTreeNodeWriter.cs new file mode 100644 index 0000000..5155edb --- /dev/null +++ b/WhatsAppApi/Helper/BinTreeNodeWriter.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WhatsAppApi.Helper +{ + public class BinTreeNodeWriter + { + private List buffer; + public KeyStream Key; + + public BinTreeNodeWriter() + { + buffer = new List(); + } + + public byte[] StartStream(string domain, string resource) + { + var attributes = new List(); + this.buffer = new List(); + + attributes.Add(new KeyValue("to", domain)); + attributes.Add(new KeyValue("resource", resource)); + this.writeListStart(attributes.Count * 2 + 1); + + this.buffer.Add(1); + this.writeAttributes(attributes.ToArray()); + + byte[] ret = this.flushBuffer(); + this.buffer.Add((byte)'W'); + this.buffer.Add((byte)'A'); + this.buffer.Add(0x1); + this.buffer.Add(0x5); + this.buffer.AddRange(ret); + ret = buffer.ToArray(); + this.buffer = new List(); + return ret; + } + + public byte[] Write(ProtocolTreeNode node, bool encrypt = true) + { + if (node == null) + { + this.buffer.Add(0); + } + else + { + this.DebugPrint(node.NodeString("tx ")); + this.writeInternal(node); + } + return this.flushBuffer(encrypt); + } + + 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); + + byte[] size = this.GetInt24(data.Length); + if (encrypt && this.Key != null) + { + 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, 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); + } + } + } + + private byte[] GetInt16(int len) + { + byte[] ret = new byte[2]; + ret[0] = (byte)((len & 0xff00) >> 8); + ret[1] = (byte)(len & 0x00ff); + return ret; + } + + private byte[] GetInt24(int len) + { + 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) + { + writeBytes(WhatsApp.SYSEncoding.GetBytes(bytes)); + } + protected void writeBytes(byte[] bytes) + { + int len = bytes.Length; + if (len >= 0x100) + { + this.buffer.Add(0xfd); + this.writeInt24(len); + } + else + { + this.buffer.Add(0xfc); + this.writeInt8(len); + } + this.buffer.AddRange(bytes); + } + + protected void writeInt16(int v) + { + this.buffer.Add((byte)((v & 0xff00) >> 8)); + this.buffer.Add((byte)(v & 0x00ff)); + } + + protected void writeInt24(int v) + { + 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.buffer.Add((byte)(v & 0xff)); + } + + protected void writeInternal(ProtocolTreeNode node) + { + int len = 1; + if (node.attributeHash != null) + { + len += node.attributeHash.Count() * 2; + } + if (node.children.Any()) + { + len += 1; + } + if (node.data.Length > 0) + { + len += 1; + } + this.writeListStart(len); + this.writeString(node.tag); + this.writeAttributes(node.attributeHash); + if (node.data.Length > 0) + { + this.writeBytes(node.data); + } + if (node.children != null && node.children.Any()) + { + this.writeListStart(node.children.Count()); + foreach (var item in node.children) + { + this.writeInternal(item); + } + } + } + protected void writeJid(string user, string server) + { + this.buffer.Add(0xfa); + if (user.Length > 0) + { + this.writeString(user); + } + else + { + this.writeToken(0); + } + this.writeString(server); + } + + protected void writeListStart(int len) + { + if (len == 0) + { + this.buffer.Add(0x00); + } + else if (len < 256) + { + this.buffer.Add(0xf8); + this.writeInt8(len); + } + else + { + this.buffer.Add(0xf9); + this.writeInt16(len); + } + } + + protected void writeString(string tag) + { + int intValue = -1; + int num = -1; + if (new TokenDictionary().TryGetToken(tag, ref num, ref intValue)) + { + if (num >= 0) + { + 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) + { + if (token < 0xf5) + { + this.buffer.Add((byte)token); + } + else if (token <= 0x1f4) + { + this.buffer.Add(0xfe); + this.buffer.Add((byte)(token - 0xf5)); + } + } + + protected void DebugPrint(string debugMsg) + { + if (WhatsApp.DEBUG && debugMsg.Length > 0) + { + Helper.DebugAdapter.Instance.fireOnPrintDebug(debugMsg); + } + } + } +} diff --git a/WhatsAppApi/Helper/DebugAdapter.cs b/WhatsAppApi/Helper/DebugAdapter.cs new file mode 100644 index 0000000..9439af1 --- /dev/null +++ b/WhatsAppApi/Helper/DebugAdapter.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WhatsAppApi.Helper +{ + public class DebugAdapter + { + protected static DebugAdapter _instance; + public static DebugAdapter Instance + { + get + { + if (_instance == null) + { + _instance = new DebugAdapter(); + } + return _instance; + } + } + + public event OnPrintDebugDelegate OnPrintDebug; + internal void fireOnPrintDebug(object value) + { + if (this.OnPrintDebug != null) + { + this.OnPrintDebug(value); + } + } + + public delegate void OnPrintDebugDelegate(object value); + } +} diff --git a/WhatsAppApi/Helper/DisconnectedException.cs b/WhatsAppApi/Helper/DisconnectedException.cs new file mode 100644 index 0000000..4447349 --- /dev/null +++ b/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/Func.cs b/WhatsAppApi/Helper/Func.cs similarity index 53% rename from src/WhatsAppApi/Helper/Func.cs rename to WhatsAppApi/Helper/Func.cs index 91a7e77..21a606d 100644 --- a/src/WhatsAppApi/Helper/Func.cs +++ b/WhatsAppApi/Helper/Func.cs @@ -22,7 +22,7 @@ public static int strlen_wa(string str) public static string _hex(int val) { - return (val.ToString("X").Length%2 == 0) ? val.ToString("X") : ("0" + val.ToString("X")); + return (val.ToString("X").Length % 2 == 0) ? val.ToString("X") : ("0" + val.ToString("X")); } public static string random_uuid() @@ -41,7 +41,7 @@ public static string strtohex(string str) { string hex = "0x"; for (int i = 0; i < str.Length; i++) - hex += ((int) str[i]).ToString("x"); + hex += ((int)str[i]).ToString("x"); return hex; } @@ -55,44 +55,28 @@ 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) + public static long GetUnixTimestamp(DateTime value) { - byte[] toEncodeAsBytes = enc.GetBytes(toEncode); - string returnValue = System.Convert.ToBase64String(toEncodeAsBytes); - return returnValue; + TimeSpan span = (value - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)); + return (long)span.TotalSeconds; } - public static string DecodeTo64(string toDecode, Encoding enc) + public static long GetNowUnixTimestamp() { - byte[] toDecodeAsBytes = System.Convert.FromBase64String(toDecode); - string returnValue = enc.GetString(toDecodeAsBytes); - return returnValue; + return GetUnixTimestamp(DateTime.UtcNow); } -// 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 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/WhatsAppApi/Helper/IncompleteMessageException.cs similarity index 63% rename from src/WhatsAppApi/Helper/IncompleteMessageException.cs rename to WhatsAppApi/Helper/IncompleteMessageException.cs index 838416a..d8a3b06 100644 --- a/src/WhatsAppApi/Helper/IncompleteMessageException.cs +++ b/WhatsAppApi/Helper/IncompleteMessageException.cs @@ -5,12 +5,11 @@ namespace WhatsAppApi.Helper { - class IncompleteMessageException : Exception + public class IncompleteMessageException : Exception { private int code; private string message; - private string input; - + private byte[] buffer; public IncompleteMessageException(string message, int code = 0) { @@ -18,15 +17,14 @@ public IncompleteMessageException(string message, int code = 0) this.code = code; } - public void setInput(string input) + public void setInput(byte[] input) { - this.input = input; + this.buffer = input; } - public string getInput() + public byte[] getInput() { - return this.input; + return this.buffer; } - } } 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/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/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 + { + + } +} diff --git a/src/WhatsAppApi/Helper/ProtocolTreeNode.cs b/WhatsAppApi/Helper/ProtocolTreeNode.cs similarity index 77% rename from src/WhatsAppApi/Helper/ProtocolTreeNode.cs rename to WhatsAppApi/Helper/ProtocolTreeNode.cs index f54f17a..759493c 100644 --- a/src/WhatsAppApi/Helper/ProtocolTreeNode.cs +++ b/WhatsAppApi/Helper/ProtocolTreeNode.cs @@ -10,37 +10,33 @@ 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 = "") + 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 = "") + 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 = "") + 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) { } @@ -51,14 +47,22 @@ 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; + if (this.data.Length <= 1024) + { + ret += WhatsApp.SYSEncoding.GetString(this.data); + } + else + { + ret += string.Format("--{0} byte--", this.data.Length); + } } + if (this.children != null && this.children.Count() > 0) { foreach (var item in this.children) @@ -73,11 +77,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; } @@ -88,7 +87,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; } @@ -111,7 +110,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 +123,7 @@ public IEnumerable GetAllChildren() return this.children.ToArray(); } - public string GetDataString() + public byte[] GetData() { return this.data; } diff --git a/WhatsAppApi/Helper/RC4.cs b/WhatsAppApi/Helper/RC4.cs new file mode 100644 index 0000000..70b4d55 --- /dev/null +++ b/WhatsAppApi/Helper/RC4.cs @@ -0,0 +1,55 @@ +using System; + +namespace WhatsAppApi.Helper +{ + public 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/WhatsAppApi/Helper/TicketManager.cs b/WhatsAppApi/Helper/TicketManager.cs new file mode 100644 index 0000000..e9d166b --- /dev/null +++ b/WhatsAppApi/Helper/TicketManager.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +namespace WhatsAppApi.Helper +{ + class TicketManager + { + private static TicketManager _instance; + private string idBase; + public static string IdBase + { + get + { + if (_instance == null) + _instance = new TicketManager(); + return _instance.idBase; + } + } + + public TicketManager() + { + idBase = Func.GetNowUnixTimestamp().ToString(); + } + + public static string GenerateId() + { + if (_instance == null) + _instance = new TicketManager(); + + return (IdBase + "-" + TicketCounter.NextTicket()); + } + } + + public static class TicketCounter + { + private static int id = 0; + private static string loginTime; + + public static int NextTicket() + { + return Interlocked.Increment(ref id); + } + + public static void setLoginTime(string Time) + { + loginTime = Time; + } + + public static string MakeId() + { + int num = NextTicket(); + return string.Format("{0}-{1}", loginTime, num); + } + } +} diff --git a/WhatsAppApi/Helper/TokenMap.cs b/WhatsAppApi/Helper/TokenMap.cs new file mode 100644 index 0000000..157b497 --- /dev/null +++ b/WhatsAppApi/Helper/TokenMap.cs @@ -0,0 +1,585 @@ +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", + "groups_v2", + "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", + "readreceipts", + "reason", + "receipt", + "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: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", + "xmlns:stream", + "xmlns", + "1", + "chatstate", + "crypto", + "phash", + "enc", + "class", + "off_cnt", + "w:g2", + "promote", + "demote", + "creator", + "Bell.caf", + "Boing.caf", + "Glass.caf", + "Harp.caf", + "TimePassing.caf", + "Tri-tone.caf", + "Xylophone.caf", + "background", + "backoff", + "chunked", + "context", + "full", + "in", + "interactive", + "out", + "registration", + "sid", + "urn:xmpp:whatsapp:sync", + "flt", + "s16", + "u8", + "adpcm", + "amrnb", + "amrwb", + "mp3", + "pcm", + "qcelp", + "wma", + "h263", + "h264", + "jpeg" + }; + private static string[][] secondaryStrings = new string[][] + { + new string[] + { + "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", + "410", + "500", + "501", + "503", + "504", + "abitrate", + "acodec", + "app_uptime", + "asampfmt", + "asampfreq", + "audio", + "clear", + "conflict", + "conn_no_nna", + "cost", + "currency", + "duration", + "extend", + "file", + "fps", + "g_notify", + "g_sound", + "gcm", + "gone", + "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", + "max_list_recipients", + "en-AU", + "en-GB", + "es-MX", + "pt-PT", + "zh-Hans", + "zh-Hant", + "relayelection", + "relaylatency", + "interruption", + "Apex.m4r", + "Beacon.m4r", + "Bulletin.m4r", + "By The Seaside.m4r", + "Chimes.m4r", + "Circuit.m4r", + "Constellation.m4r", + "Cosmic.m4r", + "Crystals.m4r", + "Hillside.m4r", + "Illuminate.m4r", + "Night Owl.m4r", + "Opening.m4r", + "Playtime.m4r", + "Presto.m4r", + "Radar.m4r", + "Radiate.m4r", + "Ripples.m4r", + "Sencha.m4r", + "Signal.m4r", + "Silk.m4r", + "Slow Rise.m4r", + "Stargaze.m4r", + "Summit.m4r", + "Twinkle.m4r", + "Uplift.m4r", + "Waves.m4r", + "voip", + "eligible", + "upgrade", + "planned", + "current", + "future", + "disable", + "expire", + "start", + "stop", + "accuracy", + "speed", + "bearing", + "recording", + "encrypt", + "key", + "identity", + "w:gp2", + "admin", + "locked", + "unlocked", + "new", + "battery", + "archive", + "adm", + "plaintext_size", + "compressed_size", + "delivered", + "msg", + "pkmsg", + "everyone", + "v", + "transport", + "call-id" + } + }; + + 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/src/WhatsAppApi/Parser/FMessage.cs b/WhatsAppApi/Parser/FMessage.cs similarity index 91% rename from src/WhatsAppApi/Parser/FMessage.cs rename to WhatsAppApi/Parser/FMessage.cs index 190f98b..7dcc4c3 100644 --- a/src/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; @@ -24,18 +24,27 @@ public class FMessage public DateTime? timestamp; public bool wants_receipt; - public FMessage(Key key) + public WhatsAppApi.Account.WhatsUser User { get; private set; } + + 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) + { + this.status = Status.Undefined; + this.gap_behind = true; + this.User = remote_user; + 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) @@ -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) { @@ -187,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) { @@ -294,12 +310,7 @@ public bool Instantiated() return (this.message != null); } - public FMessage.Key Key() - { - return new FMessage.Key(this.remote_jid, 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); @@ -414,7 +425,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; } @@ -506,14 +517,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 string serverName; - 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; @@ -532,7 +543,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/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/WhatsAppApi/Parser/PhoneNumber.cs b/WhatsAppApi/Parser/PhoneNumber.cs new file mode 100644 index 0000000..e278cd2 --- /dev/null +++ b/WhatsAppApi/Parser/PhoneNumber.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +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; + 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) + { + using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("WhatsAppApi.Parser.countries.csv")) + { + using (var reader = new System.IO.StreamReader(stream)) + { + string csv = reader.ReadToEnd(); + string[] lines = csv.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries); + foreach (string line in lines) + { + string[] values = line.Trim(new char[] { '\r' }).Split(new char[] { ',' }); + //try to match + if (number.StartsWith(values[1])) + { + //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[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]; + } + 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..e956f4b --- /dev/null +++ b/WhatsAppApi/Parser/countries.csv @@ -0,0 +1,254 @@ +"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" 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/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/Register/WaToken.cs b/WhatsAppApi/Register/WaToken.cs new file mode 100644 index 0000000..4da844e --- /dev/null +++ b/WhatsAppApi/Register/WaToken.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace WhatsAppApi.Register +{ + class WaToken + { + public static string GenerateToken(string number) + { + + //string token = "PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk1439921717185" + number; + string token = "PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk1447796090073" + number; + byte[] asciiBytes = ASCIIEncoding.ASCII.GetBytes(token); + byte[] hashedBytes = MD5CryptoServiceProvider.Create().ComputeHash(asciiBytes); + return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(); + + } + + private static List GetFilledList(byte item, int length) + { + List result = new List(); + for (int i = 0; i < length; i++) + { + result.Add(item); + } + return result; + } + } +} diff --git a/WhatsAppApi/Register/WhatsRegisterV2.cs b/WhatsAppApi/Register/WhatsRegisterV2.cs new file mode 100644 index 0000000..b4c3395 --- /dev/null +++ b/WhatsAppApi/Register/WhatsRegisterV2.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using WhatsAppApi.Parser; +using WhatsAppApi.Settings; + +namespace WhatsAppApi.Register +{ + public static class WhatsRegisterV2 + { + public static string GenerateIdentity(string phoneNumber, string salt = "") + { + return (phoneNumber + salt).Reverse().ToSHAString(); + } + + public static string GetToken(string number) + { + return WaToken.GenerateToken(number); + } + + public static bool RequestCode(string phoneNumber, out string password, string method = "sms", string id = null) + { + string response = string.Empty; + return RequestCode(phoneNumber, out password, out response, method, id); + } + + public static bool RequestCode(string phoneNumber, out string password, out string response, string method = "sms", string id = null) + { + string request = string.Empty; + return RequestCode(phoneNumber, out password, out request, out response, method, id); + } + + public static bool RequestCode(string phoneNumber, out string password, out string request, out string response, string method = "sms", string id = null) + { + response = null; + password = null; + request = null; + try + { + if (string.IsNullOrEmpty(id)) + { + //auto-generate + id = GenerateIdentity(phoneNumber); + } + PhoneNumber pn = new PhoneNumber(phoneNumber); + string token = Uri.EscapeDataString(WhatsRegisterV2.GetToken(pn.Number)); + + request = String.Format("https://v.whatsapp.net/v2/code?method={0}&in={1}&cc={2}&id={3}&lg={4}&lc={5}&token={6}&sim_mcc=000&sim_mnc=000", method, pn.Number, pn.CC, id, pn.ISO639, pn.ISO3166, token, pn.MCC, pn.MNC); + response = GetResponse(request); + password = response.GetJsonValue("pw"); + if (!string.IsNullOrEmpty(password)) + { + return true; + } + return (response.GetJsonValue("status") == "sent"); + } + catch (Exception e) + { + response = e.Message; + return false; + } + } + + public static string RegisterCode(string phoneNumber, string code, string id = null) + { + string response = string.Empty; + return WhatsRegisterV2.RegisterCode(phoneNumber, code, out response, id); + } + + public static string RegisterCode(string phoneNumber, string code, out string response, string id = null) + { + response = string.Empty; + try + { + if (string.IsNullOrEmpty(id)) + { + //auto generate + id = GenerateIdentity(phoneNumber); + } + PhoneNumber pn = new PhoneNumber(phoneNumber); + + string uri = string.Format("https://v.whatsapp.net/v2/register?cc={0}&in={1}&id={2}&code={3}", pn.CC, pn.Number, id, code); + response = GetResponse(uri); + if (response.GetJsonValue("status") == "ok") + { + return response.GetJsonValue("pw"); + } + return null; + } + catch + { + return null; + } + } + + public static string RequestExist(string phoneNumber, string id = null) + { + string response = string.Empty; + return RequestExist(phoneNumber, out response, id); + } + + public static string RequestExist(string phoneNumber, out string response, string id = null) + { + response = string.Empty; + try + { + if (String.IsNullOrEmpty(id)) + { + id = GenerateIdentity(phoneNumber); + } + PhoneNumber pn = new PhoneNumber(phoneNumber); + + string uri = string.Format("https://v.whatsapp.net/v2/exist?cc={0}&in={1}&id={2}&&lg={3}&lc={4}", pn.CC, pn.Number, id, pn.ISO639, pn.ISO3166); + response = GetResponse(uri); + if (response.GetJsonValue("status") == "ok") + { + return response.GetJsonValue("pw"); + } + return null; + } + catch + { + return null; + } + } + + private static string GetResponse(string uri) + { + HttpWebRequest request = HttpWebRequest.Create(new Uri(uri)) as HttpWebRequest; + request.KeepAlive = false; + request.UserAgent = WhatsConstants.UserAgent; + request.Accept = "text/json"; + using (var reader = new System.IO.StreamReader(request.GetResponse().GetResponseStream())) + { + return reader.ReadLine(); + } + } + + 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.GetEncoding("iso-8859-1").GetString(data); + str = WhatsRegisterV2.UrlEncode(str).ToLower(); + return 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; + 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; + } + } +} diff --git a/src/WhatsAppApi/Response/CorruptStreamException.cs b/WhatsAppApi/Response/CorruptStreamException.cs similarity index 77% rename from src/WhatsAppApi/Response/CorruptStreamException.cs rename to WhatsAppApi/Response/CorruptStreamException.cs index 07615ff..faa24c5 100644 --- a/src/WhatsAppApi/Response/CorruptStreamException.cs +++ b/WhatsAppApi/Response/CorruptStreamException.cs @@ -7,12 +7,11 @@ 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; + this.EMessage = pMessage; } } } diff --git a/WhatsAppApi/Response/WaGroupInfo.cs b/WhatsAppApi/Response/WaGroupInfo.cs new file mode 100644 index 0000000..12141c1 --- /dev/null +++ b/WhatsAppApi/Response/WaGroupInfo.cs @@ -0,0 +1,32 @@ +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) + { + this.id = id; + } + + internal WaGroupInfo(string id, string owner, string creation, string subject, string subjectChanged, string subjectChangedBy) + { + this.id = id; + this.owner = owner; + long.TryParse(creation, out this.creation); + this.subject = subject; + long.TryParse(subjectChanged, out this.subjectChangedTime); + 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/src/WhatsAppApi/Response/WhatsEventHandler.cs b/WhatsAppApi/Response/WhatsEventHandler.cs similarity index 71% rename from src/WhatsAppApi/Response/WhatsEventHandler.cs rename to WhatsAppApi/Response/WhatsEventHandler.cs index e1b1b19..3b4ffb9 100644 --- a/src/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 + /// + /// 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; @@ -37,7 +91,7 @@ internal static void OnMessageRecievedEventHandler(FMessage mess) var tmpSyncInvoke = tmpSingleCast.Target as ISynchronizeInvoke; if (tmpSyncInvoke != null && tmpSyncInvoke.InvokeRequired) { - tmpSyncInvoke.BeginInvoke(tmpSingleCast, new object[] {mess}); + tmpSyncInvoke.BeginInvoke(tmpSingleCast, new object[] { mess }); continue; } h.BeginInvoke(mess, null, null); @@ -114,19 +168,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/Settings/WhatsConstants.cs b/WhatsAppApi/Settings/WhatsConstants.cs new file mode 100644 index 0000000..6d6a0b0 --- /dev/null +++ b/WhatsAppApi/Settings/WhatsConstants.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace WhatsAppApi.Settings +{ + /// + /// Holds constant information used to connect to whatsapp server + /// + public class WhatsConstants + { + #region ServerConstants + + /// + /// The whatsapp host + /// + public const string WhatsAppHost = "c3.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.13.21"; + + /// + /// The port that needs to be connected to + /// + public const int WhatsPort = 443; + + /// + /// iPhone device + /// + public const string Device = "S40"; + + /// + /// The useragent used for http requests + /// + public const string UserAgent = "WhatsApp/2.13.21 S40Version/14.26 Device/Nokia302"; + + #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 + } +} diff --git a/WhatsAppApi/WhatsApp.cs b/WhatsAppApi/WhatsApp.cs new file mode 100644 index 0000000..f879ea8 --- /dev/null +++ b/WhatsAppApi/WhatsApp.cs @@ -0,0 +1,1030 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Web.Script.Serialization; +using WhatsAppApi.Helper; +using WhatsAppApi.Parser; +using WhatsAppApi.Response; +using WhatsAppApi.Settings; + +namespace WhatsAppApi +{ + /// + /// Main api interface + /// + public class WhatsApp : WhatsSendBase + { + public WhatsApp(string phoneNum, string password, string nick, bool debug = false, bool hidden = false) + { + this._constructBase(phoneNum, password, nick, debug, hidden); + } + + public string SendMessage(string to, string txt) + { + var tmpMessage = new FMessage(GetJID(to), true) { data = txt }; + this.SendMessage(tmpMessage, this.hidden); + return tmpMessage.identifier_key.ToString(); + } + + public void SendMessageVcard(string to, string name, string vcard_data) + { + var tmpMessage = new FMessage(GetJID(to), true) { data = vcard_data, media_wa_type = FMessage.Type.Contact, media_name = name }; + this.SendMessage(tmpMessage, this.hidden); + } + + public void SendSync(string[] numbers, SyncMode mode = SyncMode.Delta, SyncContext context = SyncContext.Background, int index = 0, bool last = true) + { + List users = new List(); + foreach (string number in numbers) + { + string _number = number; + if (!_number.StartsWith("+", StringComparison.InvariantCulture)) + _number = string.Format("+{0}", number); + 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()), + new KeyValue("xmlns", "urn:xmpp:whatsapp:sync") + }, new ProtocolTreeNode("sync", new KeyValue[] + { + new KeyValue("mode", mode.ToString().ToLowerInvariant()), + new KeyValue("context", context.ToString().ToLowerInvariant()), + new KeyValue("sid", DateTime.Now.ToFileTimeUtc().ToString()), + new KeyValue("index", index.ToString()), + new KeyValue("last", last.ToString()) + }, + users.ToArray() + ) + ); + this.SendNode(node); + } + + public void SendMessageImage(string to, byte[] ImageData, ImageType imgtype) + { + FMessage msg = this.getFmessageImage(to, ImageData, imgtype); + if (msg != null) + { + this.SendMessage(msg); + } + } + + protected FMessage getFmessageImage(string to, byte[] ImageData, ImageType imgtype) + { + to = GetJID(to); + string type = string.Empty; + string extension = string.Empty; + switch (imgtype) + { + case ImageType.PNG: + type = "image/png"; + extension = "png"; + break; + case ImageType.GIF: + type = "image/gif"; + extension = "gif"; + break; + default: + type = "image/jpeg"; + extension = "jpg"; + break; + } + + //create hash + string filehash = string.Empty; + using(HashAlgorithm sha = HashAlgorithm.Create("sha256")) + { + byte[] raw = sha.ComputeHash(ImageData); + filehash = Convert.ToBase64String(raw); + } + + //request upload + WaUploadResponse response = this.UploadFile(filehash, "image", ImageData.Length, ImageData, to, type, extension); + + if (response != null && !String.IsNullOrEmpty(response.url)) + { + //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(ImageData) + }; + return msg; + } + return null; + } + + public void SendMessageVideo(string to, byte[] videoData, VideoType vidtype) + { + FMessage msg = this.getFmessageVideo(to, videoData, vidtype); + if (msg != null) + { + this.SendMessage(msg); + } + } + + protected FMessage getFmessageVideo(string to, byte[] videoData, VideoType vidtype) + { + to = GetJID(to); + string type = string.Empty; + string extension = string.Empty; + switch (vidtype) + { + case VideoType.MOV: + type = "video/quicktime"; + extension = "mov"; + break; + case VideoType.AVI: + type = "video/x-msvideo"; + extension = "avi"; + break; + default: + type = "video/mp4"; + extension = "mp4"; + break; + } + + //create hash + string filehash = string.Empty; + using (HashAlgorithm sha = HashAlgorithm.Create("sha256")) + { + byte[] raw = sha.ComputeHash(videoData); + filehash = Convert.ToBase64String(raw); + } + + //request upload + WaUploadResponse response = this.UploadFile(filehash, "video", videoData.Length, videoData, to, type, extension); + + 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, + media_duration_seconds = response.duration + }; + return msg; + } + return null; + } + + public void SendMessageAudio(string to, byte[] audioData, AudioType audtype) + { + FMessage msg = this.getFmessageAudio(to, audioData, audtype); + if (msg != null) + { + this.SendMessage(msg); + } + } + + protected FMessage getFmessageAudio(string to, byte[] audioData, AudioType audtype) + { + to = GetJID(to); + string type = string.Empty; + string extension = string.Empty; + switch (audtype) + { + case AudioType.WAV: + type = "audio/wav"; + extension = "wav"; + break; + case AudioType.OGG: + type = "audio/ogg"; + extension = "ogg"; + break; + default: + type = "audio/mpeg"; + extension = "mp3"; + break; + } + + //create hash + string filehash = string.Empty; + using (HashAlgorithm sha = HashAlgorithm.Create("sha256")) + { + byte[] raw = sha.ComputeHash(audioData); + filehash = Convert.ToBase64String(raw); + } + + //request upload + WaUploadResponse response = this.UploadFile(filehash, "audio", audioData.Length, audioData, to, type, extension); + + 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, + media_duration_seconds = response.duration + }; + return msg; + } + return null; + } + + protected WaUploadResponse UploadFile(string b64hash, string type, long size, byte[] fileData, string to, string contenttype, string extension) + { + ProtocolTreeNode media = new ProtocolTreeNode("media", new KeyValue[] { + 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", to), + new KeyValue("type", "set"), + new KeyValue("xmlns", "w:m") + }, media); + this.uploadResponse = null; + this.SendNode(node); + int i = 0; + + while (this.uploadResponse == null && i <= 100) + { + if (m_usePoolMessages) + System.Threading.Thread.Sleep(500); + else + this.pollMessage(); + i++; + } + if (this.uploadResponse != null && this.uploadResponse.GetChild("duplicate") != null) + { + WaUploadResponse res = new WaUploadResponse(this.uploadResponse); + this.uploadResponse = null; + return res; + } + else + { + 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(b64hash)); + StringBuilder sb = new StringBuilder(); + foreach (byte b in buff) + { + sb.Append(b.ToString("X2")); + } + hashname = String.Format("{0}.{1}", sb.ToString(), extension); + + 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; + } + + List buf = new List(); + buf.AddRange(Encoding.UTF8.GetBytes(post)); + buf.AddRange(Encoding.UTF8.GetBytes(header)); + buf.AddRange(fileData); + 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(); + WaUploadResponse resp = jss.Deserialize(fooo); + if (!String.IsNullOrEmpty(resp.url)) + { + return resp; + } + } + } + } + catch (Exception) + { } + } + return null; + } + + protected void SendQrSync(byte[] qrkey, byte[] token = null) + { + string id = TicketCounter.MakeId(); + 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.SendNode(node); + } + + public void SendActive() + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "active") }); + this.SendNode(node); + } + + public void SendAddParticipants(string gjid, IEnumerable participants) + { + string id = TicketCounter.MakeId(); + this.SendVerbParticipants(gjid, participants, id, "add"); + } + + public void SendUnavailable() + { + var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); + this.SendNode(node); + } + + public void SendClientConfig(string platform, string lg, string lc) + { + string v = TicketCounter.MakeId(); + 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(); + 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, IEnumerable participants) + { + string id = TicketCounter.MakeId(); + IEnumerable participant = from jid in participants select new ProtocolTreeNode("participant", new[] { new KeyValue("jid", GetJID(jid)) }); + var child = new ProtocolTreeNode("create", new[] { new KeyValue("subject", subject) }, new ProtocolTreeNode[] { (ProtocolTreeNode)participant }); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "w:g2"), new KeyValue("to", "g.us") }, new ProtocolTreeNode[] { child }); + this.SendNode(node); + } + + public void SendDeleteAccount() + { + string id = TicketCounter.MakeId(); + var node = new ProtocolTreeNode("iq", + new KeyValue[] + { + new KeyValue("id", id), + new KeyValue("type", "get"), + new KeyValue("to", "s.whatsapp.net"), + new KeyValue("xmlns", "urn:xmpp:whatsapp:account") + }, + new ProtocolTreeNode[] + { + new ProtocolTreeNode("remove", + null + ) + }); + this.SendNode(node); + } + + public void SendDeleteFromRoster(string jid) + { + string v = TicketCounter.MakeId(); + 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 SendEndGroupChat(string gjid) + { + string id = TicketCounter.MakeId(); + 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:g2"), new KeyValue("to", gjid) }, new ProtocolTreeNode[] { child }); + this.SendNode(node); + } + + public void SendGetClientConfig() + { + string id = TicketCounter.MakeId(); + 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(); + 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(); + var child = new ProtocolTreeNode("query", null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g2"), new KeyValue("to", WhatsAppApi.WhatsApp.GetJID(gjid)) }, new ProtocolTreeNode[] { child }); + this.SendNode(node); + } + + public void SendGetGroups() + { + string id = TicketCounter.MakeId(); + this.SendGetGroups(id, "participating"); + } + + public void SendGetOwningGroups() + { + string id = TicketCounter.MakeId(); + this.SendGetGroups(id, "owning"); + } + + public void SendGetParticipants(string gjid) + { + string id = TicketCounter.MakeId(); + var child = new ProtocolTreeNode("list", null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g2"), new KeyValue("to", WhatsApp.GetJID(gjid)) }, child); + this.SendNode(node); + } + + public string SendGetPhoto(string jid, string expectedPhotoId, bool largeFormat) + { + string id = TicketCounter.MakeId(); + 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(); + 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(); + var innerChild = new ProtocolTreeNode("list", new[] { new KeyValue("name", "default") }); + var child = new ProtocolTreeNode("query", null, innerChild); + var node = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "jabber:iq:privacy") }, child); + this.SendNode(node); + } + + public void SendGetServerProperties() + { + string id = TicketCounter.MakeId(); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w"), new KeyValue("to", "s.whatsapp.net") }, + new ProtocolTreeNode("props", null)); + this.SendNode(node); + } + + public void SendGetStatuses(string[] jids) + { + List targets = new List(); + foreach (string jid in jids) + { + 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()) + }, new[] { + new ProtocolTreeNode("status", null, targets.ToArray(), null) + }, null); + + 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(); + 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:g2"), 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); + } + } + + public void SendMessageBroadcast(string[] to, string message) + { + this.SendMessageBroadcast(to, new FMessage(string.Empty, true) { data = message, media_wa_type = FMessage.Type.Undefined }); + } + + public void SendMessageBroadcastImage(string[] recipients, byte[] ImageData, ImageType imgtype) + { + string to; + List foo = new List(); + foreach (string s in recipients) + { + foo.Add(GetJID(s)); + } + to = string.Join(",", foo.ToArray()); + FMessage msg = this.getFmessageImage(to, ImageData, imgtype); + if (msg != null) + { + this.SendMessage(msg); + } + } + + public void SendMessageBroadcastAudio(string[] recipients, byte[] AudioData, AudioType audtype) + { + string to; + List foo = new List(); + foreach (string s in recipients) + { + foo.Add(GetJID(s)); + } + to = string.Join(",", foo.ToArray()); + FMessage msg = this.getFmessageAudio(to, AudioData, audtype); + if (msg != null) + { + this.SendMessage(msg); + } + } + + public void SendMessageBroadcastVideo(string[] recipients, byte[] VideoData, VideoType vidtype) + { + string to; + List foo = new List(); + foreach (string s in recipients) + { + foo.Add(GetJID(s)); + } + to = string.Join(",", foo.ToArray()); + FMessage msg = this.getFmessageVideo(to, VideoData, vidtype); + if (msg != null) + { + this.SendMessage(msg); + } + } + + 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(); + } + + 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); + Int32 unixTimestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; + ProtocolTreeNode messageNode = new ProtocolTreeNode("message", new KeyValue[] { + new KeyValue("to", unixTimestamp.ToString() + "@broadcast"), + new KeyValue("type", message.media_wa_type == FMessage.Type.Undefined?"text":"media"), + new KeyValue("id", message.identifier_key.id) + }, new ProtocolTreeNode[] { + broadcastNode, + child + }); + this.SendNode(messageNode); + } + } + + public void SendGetBroadcastLists() + { + string id = TicketCounter.MakeId(); + var listNode = new ProtocolTreeNode("lists", null); + var node = new ProtocolTreeNode("iq", new[] + { + new KeyValue("to", "s.whatsapp.net"), + new KeyValue("id", id), + new KeyValue("xmlns", "w:b"), + new KeyValue("type", "get") + }, new[] { listNode }); + + SendNode(node); + } + + public void sendDeleteBroadcastLists(IEnumerable listIds) + { + if (listIds == null) return; + + string id = TicketCounter.MakeId(); + var listNodes = new List(); + foreach (var listId in listIds) + { + listNodes.Add(new ProtocolTreeNode("list", new List { new KeyValue("id", listId) }, null, null)); + } + var deleteNode = new ProtocolTreeNode("delete", null, listNodes, null); + var node = new ProtocolTreeNode("iq", new[] + { + new KeyValue("to", "s.whatsapp.net"), + new KeyValue("id", id), + new KeyValue("xmlns", "w:b"), + new KeyValue("type", "set") + }, new[] { deleteNode }); + + SendNode(node); + } + + public void SendNop() + { + this.SendNode(null); + } + + public void SendPaused(string to) + { + this.SendChatState(to, "paused"); + } + + public void SendPing() + { + string id = TicketCounter.MakeId(); + var child = new ProtocolTreeNode("ping", null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("xmlns", "w:p"), new KeyValue("type", "get"), new KeyValue("to", "s.whatsapp.net") }, child); + 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(); + var child = new ProtocolTreeNode("query", null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("to", GetJID(jid)), new KeyValue("xmlns", "jabber:iq:last") }, child); + this.SendNode(node); + } + + public void SendRemoveParticipants(string gjid, List participants) + { + string id = TicketCounter.MakeId(); + this.SendVerbParticipants(gjid, participants, id, "remove"); + } + + public void SendSetGroupSubject(string gjid, string subject) + { + string id = TicketCounter.MakeId(); + 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:g2"), new KeyValue("to", gjid) }, child); + this.SendNode(node); + } + + public void SendSetPhoto(string jid, byte[] bytes, byte[] thumbnailBytes = null) + { + string id = TicketCounter.MakeId(); + + bytes = this.ProcessProfilePicture(bytes); + + var list = new List { new ProtocolTreeNode("picture", null, null, bytes) }; + + if (thumbnailBytes == null) + { + //auto generate + thumbnailBytes = this.CreateThumbnail(bytes); + } + + //debug + System.IO.File.WriteAllBytes("pic.jpg", bytes); + System.IO.File.WriteAllBytes("picthumb.jpg", thumbnailBytes); + + 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", GetJID(jid)) }, list.ToArray()); + this.SendNode(node); + } + + public void SendSetPrivacyBlockedList(IEnumerable jidSet) + { + string id = TicketCounter.MakeId(); + 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", null, child); + var node3 = new ProtocolTreeNode("iq", new KeyValue[] { new KeyValue("id", id), new KeyValue("type", "set"), new KeyValue("xmlns", "jabber:iq:privacy") }, node2); + this.SendNode(node3); + } + + public void SendStatusUpdate(string status) + { + string id = TicketCounter.MakeId(); + + ProtocolTreeNode node = new ProtocolTreeNode("iq", new KeyValue[] { + new KeyValue("to", "s.whatsapp.net"), + new KeyValue("type", "set"), + new KeyValue("id", id), + new KeyValue("xmlns", "status") + }, + new [] { + new ProtocolTreeNode("status", null, System.Text.Encoding.UTF8.GetBytes(status)) + }); + + this.SendNode(node); + } + + 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 SendGetGroups(string id, string type) + { + var child = new ProtocolTreeNode(type, null); + var node = new ProtocolTreeNode("iq", new[] { new KeyValue("id", id), new KeyValue("type", "get"), new KeyValue("xmlns", "w:g2"), new KeyValue("to", "g.us") }, child); + this.SendNode(node); + } + + 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)); + } + + protected 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)); + } + + 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", GetJID(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:g2"), new KeyValue("to", GetJID(gjid)) }, child); + this.SendNode(node); + } + + public void SendSetPrivacySetting(VisibilityCategory category, VisibilitySetting setting) + { + ProtocolTreeNode node = new ProtocolTreeNode("iq", new[] { + new KeyValue("to", "s.whatsapp.net"), + new KeyValue("id", TicketCounter.MakeId()), + new KeyValue("type", "set"), + new KeyValue("xmlns", "privacy") + }, new ProtocolTreeNode[] { + new ProtocolTreeNode("privacy", null, new ProtocolTreeNode[] { + new ProtocolTreeNode("category", new [] { + new KeyValue("name", this.privacyCategoryToString(category)), + new KeyValue("value", this.privacySettingToString(setting)) + }) + }) + }); + + this.SendNode(node); + } + + public void SendGetPrivacySettings() + { + ProtocolTreeNode node = new ProtocolTreeNode("iq", new KeyValue[] { + new KeyValue("to", "s.whatsapp.net"), + new KeyValue("id", TicketCounter.MakeId()), + new KeyValue("type", "get"), + new KeyValue("xmlns", "privacy") + }, new ProtocolTreeNode[] { + new ProtocolTreeNode("privacy", null) + }); + this.SendNode(node); + } + + protected 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; + } + + protected static ProtocolTreeNode GetMessageNode(FMessage message, ProtocolTreeNode pNode, bool hidden = false) + { + Int32 unixTimestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; + 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 KeyValue("t",unixTimestamp.ToString()) + }, + new ProtocolTreeNode[] { + pNode + }); + } + + 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/src/WhatsAppApi/WhatsAppApi.csproj b/WhatsAppApi/WhatsAppApi.csproj similarity index 67% rename from src/WhatsAppApi/WhatsAppApi.csproj rename to WhatsAppApi/WhatsAppApi.csproj index 5ec2a6d..b00f4fa 100644 --- a/src/WhatsAppApi/WhatsAppApi.csproj +++ b/WhatsAppApi/WhatsAppApi.csproj @@ -12,6 +12,7 @@ WhatsAppApi v3.5 512 + true @@ -21,6 +22,8 @@ DEBUG;TRACE prompt 4 + false + false pdbonly @@ -29,10 +32,13 @@ TRACE prompt 4 + false + + @@ -42,28 +48,52 @@ + + - + + + + + + + + Code + - + + True + True + Resources.resx + + + - + + + + - - + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 diff --git a/src/WhatsAppPort/frmUserChat.Designer.cs b/WhatsAppPort/frmUserChat.Designer.cs similarity index 99% rename from src/WhatsAppPort/frmUserChat.Designer.cs rename to WhatsAppPort/frmUserChat.Designer.cs index 1aa2cd5..1283f35 100644 --- a/src/WhatsAppPort/frmUserChat.Designer.cs +++ b/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/WhatsAppPort/frmUserChat.cs b/WhatsAppPort/frmUserChat.cs similarity index 84% rename from src/WhatsAppPort/frmUserChat.cs rename to WhatsAppPort/frmUserChat.cs index ddf20eb..221b540 100644 --- a/src/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.SendMessage(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.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.SendPaused(this.user.WhatsUser.GetFullJid()); this.timerTyping.Stop(); } } diff --git a/src/WhatsAppPort/frmUserChat.resx b/WhatsAppPort/frmUserChat.resx similarity index 100% rename from src/WhatsAppPort/frmUserChat.resx rename to WhatsAppPort/frmUserChat.resx diff --git a/WhatsTest/Program.cs b/WhatsTest/Program.cs new file mode 100644 index 0000000..7245529 --- /dev/null +++ b/WhatsTest/Program.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using WhatsAppApi; +using WhatsAppApi.Account; +using WhatsAppApi.Helper; +using WhatsAppApi.Register; +using WhatsAppApi.Response; + +namespace WhatsTest +{ + internal class Program + { + private static void Main(string[] args) + { + var tmpEncoding = Encoding.UTF8; + System.Console.OutputEncoding = Encoding.Default; + System.Console.InputEncoding = Encoding.Default; + string nickname = "WhatsApiNet"; + 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); + + //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.OnGetSyncResult += wa_OnGetSyncResult; + wa.OnGetStatus += wa_OnGetStatus; + wa.OnGetPrivacySettings += wa_OnGetPrivacySettings; + DebugAdapter.Instance.OnPrintDebug += Instance_OnPrintDebug; + + wa.Connect(); + + string datFile = getDatFileName(sender); + byte[] nextChallenge = null; + if (File.Exists(datFile)) + { + try + { + string foo = File.ReadAllText(datFile); + nextChallenge = Convert.FromBase64String(foo); + } + catch (Exception) { }; + } + + wa.Login(nextChallenge); + + ProcessChat(wa, target); + Console.ReadKey(); + } + + static void Instance_OnPrintDebug(object value) + { + Console.WriteLine(value); + } + + static void wa_OnGetPrivacySettings(Dictionary settings) + { + throw new NotImplementedException(); + } + + static void wa_OnGetStatus(string from, string type, string name, string status) + { + Console.WriteLine(String.Format("Got status from {0}: {1}", from, status)); + } + + static string getDatFileName(string pn) + { + string filename = string.Format("{0}.next.dat", pn); + return Path.Combine(Directory.GetCurrentDirectory(), filename); + } + + 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(WaGroupInfo[] groups) + { + Console.WriteLine("Got groups:"); + foreach (WaGroupInfo info in groups) + { + Console.WriteLine("\t{0} {1}", info.subject, info.id); + } + } + + 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(ProtocolTreeNode vcardNode, 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(ProtocolTreeNode locationNode, string from, string id, double lon, double lat, string url, string name, byte[] preview, string username) + { + 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(ProtocolTreeNode mediaNode, string from, string id, string fileName, int fileSize, string url, byte[] preview, string username) + { + 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(ProtocolTreeNode mediaNode, string from, string id, string fileName, int fileSize, string url, byte[] preview, string username) + { + Console.WriteLine("Got audio from {0}", from, fileName); + OnGetMedia(fileName, url, preview); + } + + static void wa_OnGetMessageImage(ProtocolTreeNode mediaNode, string from, string id, string fileName, int size, string url, byte[] preview, string username) + { + 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 participant, string id) + { + Console.WriteLine("Message {0} to {1} received by server", id, from); + } + + static void wa_OnGetMessageReceivedClient(string from, string participant, 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(ProtocolTreeNode node, string from, string id, string name, string message, bool receipt_sent) + { + 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(string phoneNumber, byte[] data) + { + Console.WriteLine("Login success. Next password:"); + string sdata = Convert.ToBase64String(data); + Console.WriteLine(sdata); + try + { + File.WriteAllText(getDatFileName(phoneNumber), sdata); + } + catch (Exception) { } + } + + + private static void ProcessChat(WhatsApp wa, string dst) + { + var thRecv = new Thread(t => + { + try + { + while (wa != null) + { + wa.PollMessages(); + Thread.Sleep(100); + continue; + } + + } + catch (ThreadAbortException) + { + } + }) {IsBackground = true}; + thRecv.Start(); + + WhatsUserManager usrMan = new WhatsUserManager(); + var tmpUser = usrMan.CreateUser(dst, "User"); + + while (true) + { + string line = Console.ReadLine(); + if (line == null && line.Length == 0) + continue; + + string command = line.Trim(); + switch (command) + { + case "/query": + //var dst = dst//trim(strstr($line, ' ', FALSE)); + Console.WriteLine("[] Interactive conversation with {0}:", tmpUser); + break; + case "/accountinfo": + Console.WriteLine("[] Account Info: {0}", wa.GetAccountInfo().ToString()); + break; + case "/lastseen": + Console.WriteLine("[] Request last seen {0}", tmpUser); + wa.SendQueryLastOnline(tmpUser.GetFullJid()); + break; + case "/exit": + wa = null; + thRecv.Abort(); + return; + case "/start": + wa.SendComposing(tmpUser.GetFullJid()); + break; + case "/pause": + wa.SendPaused(tmpUser.GetFullJid()); + break; + default: + Console.WriteLine("[] Send message to {0}: {1}", tmpUser, line); + wa.SendMessage(tmpUser.GetFullJid(), line); + break; + } + } + } + } +} 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 93% rename from src/WhatsTest/WhatsTest.csproj rename to WhatsTest/WhatsTest.csproj index d00e841..fba78e3 100644 --- a/src/WhatsTest/WhatsTest.csproj +++ b/WhatsTest/WhatsTest.csproj @@ -12,6 +12,7 @@ WhatsTest v3.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 + + + + 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 diff --git a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs b/src/WhatsAppApi/Helper/BinTreeNodeReader.cs deleted file mode 100644 index e3d50eb..0000000 --- a/src/WhatsAppApi/Helper/BinTreeNodeReader.cs +++ /dev/null @@ -1,239 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace WhatsAppApi.Helper -{ - internal class BinTreeNodeReader - { - private string[] dictionary; - private string input; - - public BinTreeNodeReader(string[] dict) - { - this.dictionary = dict; - } - - public ProtocolTreeNode nextTree(string pInput = null) - { - if (pInput != null) - { - this.input = pInput; - } - - int stanzaSize = this.peekInt16(); - - 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.readInt16(); - if (stanzaSize > 0) - { - return this.nextTreeInternal(); - } - return null; - } - - protected string getToken(int token) - { - string ret = ""; - if ((token >= 0) && (token < this.dictionary.Length)) - { - ret = this.dictionary[token]; - } - else - { - throw new Exception("BinTreeNodeReader->getToken: Invalid token $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 = this.fillArray(size); - } - else if (token == 0xfd) - { - int size = this.readInt24(); - ret = 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 IEnumerable readAttributes(int size) - { - var attributes = new List(); - 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()); - attributes.Add(new KeyValue(key, value)); - } - return attributes; - } - - protected ProtocolTreeNode nextTreeInternal() - { - int token = this.readInt8(); - int size = this.readListSize(token); - token = this.readInt8(); - - if (token == 1) - { - var attributes = this.readAttributes(size); - return new ProtocolTreeNode("start", attributes); - } - else if (token == 2) - { - return null; - } - string tag = this.readString(token); - var tmpAttributes = this.readAttributes(size); - - 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.readString(token)); - } - - protected bool isListTag(int token) - { - return ((token == 248) || (token == 0) || (token == 249)); - } - - protected List readList(int token) - { - int size = this.readListSize(token); - var ret = new List(); - for (int i = 0; i < size; i++) - { - ret.Add(this.nextTreeInternal()); - } - return ret; - } - - protected int readListSize(int token) - { - int size = 0; - if (token == 0xf8) - { - size = this.readInt8(); - } - else if (token == 0xf9) - { - size = this.readInt16(); - } - else - { - throw new Exception("BinTreeNodeReader->readListSize: Invalid token $token"); - } - return size; - } - - 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); - } - 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 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 readInt8() - { - int ret = 0; - if (this.input.Length >= 1) - { - ret = (int) this.input[0]; - this.input = this.input.Remove(0, 1); - } - 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; - } - } - -} diff --git a/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs b/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs deleted file mode 100644 index a75b5b2..0000000 --- a/src/WhatsAppApi/Helper/BinTreeNodeWriter.cs +++ /dev/null @@ -1,231 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace WhatsAppApi.Helper -{ - internal class BinTreeNodeWriter - { - private string output; - private Dictionary tokenMap; - - public BinTreeNodeWriter(string[] dict) - { - 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; - } - } - } - - 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 string Write(ProtocolTreeNode node) - { - if (node == null) - { - this.output += "\x00"; - } - else - { - this.writeInternal(node); - } - return this.flushBuffer(); - } - - protected string flushBuffer() - { - int size = this.output.Length; - this.output = this.GetInt16(size) + this.output; - string ret = this.output; - this.output = ""; - return ret; - } - - protected void writeAttributes(IEnumerable attributes) - { - if (attributes != null) - { - foreach (var item in attributes) - { - this.writeString(item.Key); - this.writeString(item.Value); - } - } - } - - private string GetInt16(int len) - { - string ret = chr((len & 0xff00) >> 8); - ret += chr((len & 0x00ff) >> 0); - 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 writeInt16(int v) - { - //string ret = ""; - this.output += chr((v & 0xff00) >> 8); - this.output += chr((v & 0x00ff) >> 0); - //this.output = ret + this.output; - } - - 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 writeInt8(int v) - { - this.output += chr(v & 0xff); - } - - protected void writeInternal(ProtocolTreeNode node) - { - int len = 1; - if (node.attributeHash != null) - { - len += node.attributeHash.Count()*2; - } - if (node.children.Any()) - { - len += 1; - } - if (node.data.Length > 0) - { - len += 1; - } - this.writeListStart(len); - this.writeString(node.tag); - this.writeAttributes(node.attributeHash); - if (node.data.Length > 0) - { - this.writeBytes(node.data); - } - if (node.children != null && node.children.Any()) - { - this.writeListStart(node.children.Count()); - foreach (var item in node.children) - { - this.writeInternal(item); - } - } - } - protected void writeJid(string user, string server) - { - this.output += "\xfa"; - if (user.Length > 0) - { - this.writeString(user); - } - else - { - this.writeToken(0); - } - this.writeString(server); - } - - protected void writeListStart(int len) - { - if (len == 0) - { - this.output += "\x00"; - } - else if (len < 256) - { - this.output += "\xf8"; - this.writeInt8(len); - } - else - { - this.output += "\xf9"; - this.writeInt16(len); - } - } - - protected void writeString(string tag) - { - if (this.tokenMap.ContainsKey(tag)) - { - 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 - { - 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); - } - } - - /// - /// Check if chr ist correct - /// - /// - /// - private string chr(int value) - { - char tmpRealValue = (char)(value); - return Convert.ToString(tmpRealValue); - } - } -} diff --git a/src/WhatsAppApi/Helper/DecodeHelper.cs b/src/WhatsAppApi/Helper/DecodeHelper.cs deleted file mode 100644 index 0de7e98..0000000 --- a/src/WhatsAppApi/Helper/DecodeHelper.cs +++ /dev/null @@ -1,302 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace WhatsAppApi.Helper -{ - internal 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] = "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"; - - return dictList; - } - - public static string getToken(int index) - { - string[] dicList = getDictionary(); - return dicList[index]; - } - } -} diff --git a/src/WhatsAppApi/Helper/TicketManager.cs b/src/WhatsAppApi/Helper/TicketManager.cs deleted file mode 100644 index adf9bac..0000000 --- a/src/WhatsAppApi/Helper/TicketManager.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; - -namespace WhatsAppApi.Helper -{ - class TicketManager - { - public static string IdBase { get; private set; } - - public TicketManager() - { - IdBase = DateTime.Now.Ticks.ToString(); - } - - public static string GenerateId() - { - return (IdBase + "-" + TicketCounter.NextTicket()); - } - } - - public static class TicketCounter - { - private static int id = -1; - - public static int NextTicket() - { - return Interlocked.Increment(ref id); - } - - public static string MakeId(string prefix) - { - int num = NextTicket(); - if (true)//this.IsVerboseId) - { - return (prefix + num); - } - //return num.ToString("X"); - } - } -} diff --git a/src/WhatsAppApi/Register/WhatsRegister.cs b/src/WhatsAppApi/Register/WhatsRegister.cs deleted file mode 100644 index 77e3015..0000000 --- a/src/WhatsAppApi/Register/WhatsRegister.cs +++ /dev/null @@ -1,164 +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.UserAgend, both); - 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.UserAgend, 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.UserAgend, 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 ex) - { - 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 countryCode = "49"; - 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) - { - 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/src/WhatsAppApi/Response/MessageRecvResponse.cs b/src/WhatsAppApi/Response/MessageRecvResponse.cs deleted file mode 100644 index 665657c..0000000 --- a/src/WhatsAppApi/Response/MessageRecvResponse.cs +++ /dev/null @@ -1,316 +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 -{ - class MessageRecvResponse - { - private WhatsSendHandler sendHandler; - - public MessageRecvResponse(WhatsSendHandler sendHandler) - { - this.sendHandler = sendHandler; - } - - 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); - } - } - - - 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")) - { - 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); - } - } - } - - 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) - { - //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) - { - //bool duplicate = false; - 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 = itemNode.GetDataString(); - var tmpMessKey = new FMessage.Key(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(tmpChildMedia.GetDataString()); - } - } - else - { - string tmpAttrEncoding = itemNode.GetAttribute("encoding") ?? "text"; - if (tmpAttrEncoding == "text") - { - builder.Data(itemNode.GetDataString()); - } - //else - //{ - // builder.BinaryData(messageNode.data); - //} - } - var tmpMessageKey = new FMessage.Key(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.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")) - { - 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) - { - 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.Key().serverNickname = tmpAttrName; - } - } - if (!builder.Timestamp().HasValue) - { - builder.Timestamp(new DateTime?(DateTime.Now)); - } - FMessage message = builder.Build(); - if (message != null) - { - WhatsEventHandler.OnMessageRecievedEventHandler(message); - } - } - - 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 : child.GetDataString(); - if (subject != null) - { - WhatsEventHandler.OnGroupNewSubjectEventHandler(tmpFrom, uJid, subject, int.Parse(tmpT, CultureInfo.InvariantCulture)); - } - if (flag) - { - this.sendHandler.SendSubjectReceived(tmpFrom, tmpId); - } - } - - 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.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/src/WhatsAppApi/Response/WhatsParser.cs b/src/WhatsAppApi/Response/WhatsParser.cs deleted file mode 100644 index 8acba85..0000000 --- a/src/WhatsAppApi/Response/WhatsParser.cs +++ /dev/null @@ -1,470 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using WhatsAppApi.Helper; -using WhatsAppApi.Settings; - -namespace WhatsAppApi.Response -{ - public class WhatsParser - { - public WhatsSendHandler WhatsSendHandler { get; private set; } - private WhatsNetwork whatsNetwork; - private MessageRecvResponse messResponseHandler; - - public WhatsParser(WhatsNetwork whatsNet) - { - this.WhatsSendHandler = new WhatsSendHandler(whatsNet); - this.whatsNetwork = whatsNet; - this.messResponseHandler = new MessageRecvResponse(this.WhatsSendHandler); - } - - 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("error")) - { - //IqResultHandler handler2 = this.PopIqHandler(id); - //if (handler2 != null) - //{ - // handler2.ErrorNode(node); - //} - } - else 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 (node3 != null) - { - //this.EventHandler.OnPing(id); - } - else 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); - } - if (pin != null) - { - //this.EventHandler.OnRelayRequest(pin, num, id); - } - } - } - } - 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")) - { - string str9 = protNode.GetAttribute("xmlns"); - string jid = protNode.GetAttribute("from"); - 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)) - { - 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); - //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(); - 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/src/WhatsAppApi/Settings/WhatsConstants.cs b/src/WhatsAppApi/Settings/WhatsConstants.cs deleted file mode 100644 index 8574d2d..0000000 --- a/src/WhatsAppApi/Settings/WhatsConstants.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace WhatsAppApi.Settings -{ - class WhatsConstants - { - #region ServerConstants - public const string WhatsAppDigest = "xmpp/s.whatsapp.net"; - public const string WhatsAppHost = "bin-short.whatsapp.net"; - 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 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 WhatsBuildHash = "889d4f44e479e6c38b4a834c6d8417815f999abe";//v2.8.0"c0d4db538579a3016902bf699c16d490acf91ff4"; //v2.0.0 "13944fe0a89d1e6cce0c405178f7c0d00313d558"; - #endregion - - #region ParserConstants - public static NumberStyles WhatsAppNumberStyle = (NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign); - public static DateTime UnixEpoch = new DateTime(0x7b2, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - #endregion - } -} diff --git a/src/WhatsAppApi/WhatsApp.cs b/src/WhatsAppApi/WhatsApp.cs deleted file mode 100644 index c58b0ed..0000000 --- a/src/WhatsAppApi/WhatsApp.cs +++ /dev/null @@ -1,369 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Sockets; -using System.Security.Cryptography; -using System.Text; -using WhatsAppApi.Helper; -using WhatsAppApi.Parser; -using WhatsAppApi.Response; -using WhatsAppApi.Settings; - -namespace WhatsAppApi -{ - public class WhatsApp - { - private readonly Encoding sysEncoding; - private AccountInfo accountinfo; - private Dictionary challengeArray; - private string connectedStatus = "connected"; - private bool debug; - private string disconnectedStatus = "disconnected"; - private string imei; - private string loginStatus; - private object messageLock = new object(); - private List messageQueue; - private string name; - private string phoneNumber; - private BinTreeNodeReader reader; - private BinTreeNodeWriter writer; - private int timeout = 2000; - - private WhatsNetwork whatsNetwork; - public WhatsSendHandler WhatsSendHandler { get; private set; } - public WhatsParser WhatsParser { get; private set; } - - //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; - this.name = nick; - this.debug = debug; - string[] dict = DecodeHelper.getDictionary(); - this.writer = new BinTreeNodeWriter(dict); - this.reader = new BinTreeNodeReader(dict); - - this.loginStatus = disconnectedStatus; - - this.whatsNetwork = new WhatsNetwork(WhatsConstants.WhatsAppHost, WhatsConstants.WhatsPort, this.sysEncoding, this.timeout); - this.WhatsParser = new WhatsParser(this.whatsNetwork); - this.WhatsSendHandler = this.WhatsParser.WhatsSendHandler; - } - - public void AddMessage(ProtocolTreeNode node) - { - lock (messageLock) - { - this.messageQueue.Add(node); - } - - } - - public void Connect() - { - this.whatsNetwork.Connect(); - } - - public string encryptPassword() - { - if (this.imei.Contains(":")) - { - this.imei = this.imei.ToUpper(); - return md5(this.imei + this.imei); - } - else - { - return md5(new string(this.imei.Reverse().ToArray())); - } - } - - public AccountInfo GetAccountInfo() - { - return this.accountinfo; - } - - public ProtocolTreeNode[] GetAllMessages() - { - ProtocolTreeNode[] tmpReturn = null; - lock (messageLock) - { - tmpReturn = this.messageQueue.ToArray(); - this.messageQueue.Clear(); - } - return tmpReturn; - } - - public bool HasMessages() - { - if (this.messageQueue == null) - return false; - return this.messageQueue.Count > 0; - } - - public void Login() - { - //"$this->device-$this->whatsAppVer-$this->port"; - string resource = string.Format(@"{0}-{1}-{2}", - WhatsConstants.IphoneDevice, - WhatsConstants.WhatsAppVer, - WhatsConstants.WhatsPort); - 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.PollMessages(); - ProtocolTreeNode authResp = this.addAuthResponse(); - this.whatsNetwork.SendNode(authResp); - int cnt = 0; - do - { - this.PollMessages(); - } while ((cnt++ < 100) && - (this.loginStatus.Equals(this.disconnectedStatus, StringComparison.OrdinalIgnoreCase))); - } - - 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}; - this.WhatsParser.WhatsSendHandler.SendMessage(tmpMessage); - } - - public void MessageImage(string msgid, string to, string url, string file, string size, string icon) - { - //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); - } - - public void PollMessages() - { - this.processInboundData(this.whatsNetwork.ReadData()); - } - - 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) - { - this.WhatsParser.WhatsSendHandler.SendQueryLastOnline(jid); - } - - public void sendNickname(string nickname) - { - this.WhatsParser.WhatsSendHandler.SendAvailableForChat(nickname); //es muss der nickname festgelegt werden - } - - 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") }); - 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; - } - - 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, ""); - 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 (this.debug && debugMsg.Length > 0) - { - Console.WriteLine(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("\"", "")); - } - } - - protected void processInboundData(string data) - { - try - { - var node = this.reader.nextTree(data); - while (node != null) - { - this.WhatsParser.ParseProtocolNode(node); - this.DebugPrint(node.NodeString("RECVD: ")); - if (node.tag.Equals("challenge", StringComparison.OrdinalIgnoreCase)) - { - this.processChallenge(node); - } - else if (node.tag.Equals("success", StringComparison.OrdinalIgnoreCase)) - { - this.loginStatus = this.connectedStatus; - this.accountinfo = new AccountInfo(node.GetAttribute("status"), - node.GetAttribute("kind"), - node.GetAttribute("creation"), - node.GetAttribute("expiration")); - } - if (node.tag.Equals("message", StringComparison.OrdinalIgnoreCase)) - { - this.AddMessage(node); - this.sendMessageReceived(node); - } - if (node.tag.Equals("iq", StringComparison.OrdinalIgnoreCase) - && node.GetAttribute("type").Equals("get", StringComparison.OrdinalIgnoreCase) - && node.children.First().tag.Equals("ping", StringComparison.OrdinalIgnoreCase)) - { - this.Pong(node.GetAttribute("id")); - } - node = this.reader.nextTree(); - } - } - catch (IncompleteMessageException e) - { - } - } - - 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)) - return; - - 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) - { - MD5 md5 = MD5.Create(); - byte[] dataMd5 = md5.ComputeHash(this.sysEncoding.GetBytes(pass)); - var sb = new StringBuilder(); - for (int i = 0; i < dataMd5.Length; i++) - sb.AppendFormat("{0:x2}", dataMd5[i]); - return sb.ToString(); - } - private void PrintInfo(string p) - { - this.DebugPrint(p); - } - - /** - * TODO - */ - } -} diff --git a/src/WhatsAppApi/WhatsNetwork.cs b/src/WhatsAppApi/WhatsNetwork.cs deleted file mode 100644 index 34b8222..0000000 --- a/src/WhatsAppApi/WhatsNetwork.cs +++ /dev/null @@ -1,88 +0,0 @@ -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 Encoding sysEncoding; - private readonly int recvTimeout; - private readonly string whatsHost; - private readonly int whatsPort; - - private string incomplete_message = ""; - private Socket socket; - private BinTreeNodeWriter binWriter; - - public WhatsNetwork(string whatsHost, int port, Encoding encoding, int timeout = 2000) - { - this.sysEncoding = encoding; - this.recvTimeout = timeout; - this.whatsHost = whatsHost; - this.whatsPort = port; - this.binWriter = new BinTreeNodeWriter(DecodeHelper.getDictionary()); - } - - 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); - } - - public string ReadData() - { - string buff = ""; - string ret = Socket_read(1024); - if (ret != null) - { - buff = this.incomplete_message + ret; - this.incomplete_message = ""; - } - return buff; - } - - public void SendData(string data) - { - Socket_send(data, data.Length, 0); - } - - public void SendNode(ProtocolTreeNode node) - { - //this.DebugPrint(node.NodeString("SENT: ")); - this.SendData(this.binWriter.Write(node)); - } - - private string Socket_read(int length) - { - var buff = new byte[length]; - try - { - 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); - } - string tmpRet = this.sysEncoding.GetString(buff); - return tmpRet; - } - - private void Socket_send(string data, int length, int flags) - { - var tmpBytes = this.sysEncoding.GetBytes(data); - this.socket.Send(tmpBytes); - } - } -} diff --git a/src/WhatsAppApi/WhatsSendHandler.cs b/src/WhatsAppApi/WhatsSendHandler.cs deleted file mode 100644 index 847c1f4..0000000 --- a/src/WhatsAppApi/WhatsSendHandler.cs +++ /dev/null @@ -1,866 +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 -{ - public class WhatsSendHandler - { - private string MyJID = ""; - - private string PushName = "Nickname?"; - - private string whatsAppRealm = "s.whatsapp.net"; - - //private BinTreeNodeWriter TreeNodeWriter; - private WhatsNetwork whatsNetwork; - //this.Login.domain ?? - public WhatsSendHandler(WhatsNetwork net) - { - //this.TreeNodeWriter = new BinTreeNodeWriter(DecodeHelper.getDictionary()); - this.whatsNetwork = net; - } - - - public void SendActive() - { - var node = new ProtocolTreeNode("presence", new[] {new KeyValue("type", "active")}); - this.whatsNetwork.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.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"); - } - - public void SendAvailableForChat(string nickName) - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("name", nickName) }); - this.whatsNetwork.SendNode(node); - } - - 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", - new[] - { - new KeyValue("id", id), new KeyValue("type", "set"), - new KeyValue("to", "s.whatsapp.net") - }, child); - this.whatsNetwork.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", whatsAppRealm) }, new ProtocolTreeNode[] { child }); - this.whatsNetwork.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) - { - //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[] - { - 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.SendNode(node); - } - - public void SendClose() - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unavailable") }); - this.whatsNetwork.SendNode(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); - } - - public void SendCreateGroupChat(string subject) - { - this.SendCreateGroupChat(subject, null, null); - } - - 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); - } - - 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[] - { - 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.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.whatsNetwork.SendNode(node); - } - - public void SendDeliveredReceiptAck(string to, string id) - { - //this.Login.TreeNodeWriter.Write(this.GetReceiptAck(to, id, "delivered"), this.AsyncHandler); - this.SendReceiptAck(to, id, "delivered"); - } - - public void SendEndGroupChat(string gjid) - { - this.SendEndGroupChat(gjid, null, null); - } - - 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); - } - - 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); - } - - 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); - } - - 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); - } - - //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); - } - - public void SendGetPhoto(string jid, bool largeFormat) - { - this.SendGetPhoto(jid, null, largeFormat, delegate { }); - } - - 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) - { - 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("to", jid) }, child); - this.whatsNetwork.SendNode(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); - } - - 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); - } - - 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); - } - - 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.SendNode(node); - } - } - - public void SendInactive() - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "inactive") }); - this.whatsNetwork.SendNode(node); - } - - public void SendLeaveGroup(string gjid) - { - this.SendLeaveGroup(gjid, null, null); - } - - public void SendLeaveGroup(string gjid, Action onSuccess, Action onError) - { - this.SendLeaveGroups(new string[] { gjid }, onSuccess, 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); - } - - public void SendMessage(FMessage message) - { - if (message.media_wa_type != FMessage.Type.Undefined) - { - this.SendMessageWithMedia(message); - } - else - { - this.SendMessageWithBody(message); - } - } - - 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); - } - - public void SendNop() - { - this.whatsNetwork.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.whatsNetwork.SendNode(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); - } - - 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); - } - - 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); - } - - public void SendPresenceSubscriptionRequest(string to) - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "subscribe"), new KeyValue("to", to) }); - this.whatsNetwork.SendNode(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); - } - - 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); - } - - 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); - } - - 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); - } - - public void SendRemoveParticipants(string gjid, List participants) - { - this.SendRemoveParticipants(gjid, participants, null, null); - } - - 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"); - } - - public void SendSetGroupSubject(string gjid, string subject) - { - this.SendSetGroupSubject(gjid, subject, null, null); - } - - 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); - } - - 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); - } - - public void SendSetPrivacyBlockedList(IEnumerable list) - { - this.SendSetPrivacyBlockedList(list, null, null); - } - - 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); - } - - public void SendStatusUpdate(string status, Action onComplete, Action onError) - { - 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)); - this.whatsNetwork.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.whatsNetwork.SendNode(node); - } - - public void SendUnsubscribeHim(string jid) - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribed"), new KeyValue("to", jid) }); - this.whatsNetwork.SendNode(node); - } - - public void SendUnsubscribeMe(string jid) - { - var node = new ProtocolTreeNode("presence", new[] { new KeyValue("type", "unsubscribe"), new KeyValue("to", jid) }); - this.whatsNetwork.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("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); - } - internal void SendMessageWithBody(FMessage message) - { - var child = new ProtocolTreeNode("body", null, message.data); - this.whatsNetwork.SendNode(GetMessageNode(message, child)); - } - - 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) }, 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.SendNode(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); - } - 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; - } - - 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.SendNode(resultNode); - } - - 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(); - return new ProtocolTreeNode("message", new[] { new KeyValue("to", message.key.remote_jid), new KeyValue("type", "chat"), new KeyValue("id", message.key.id) }, node); - } - - 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/src/WhatsTest/Program.cs b/src/WhatsTest/Program.cs deleted file mode 100644 index 728e049..0000000 --- a/src/WhatsTest/Program.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Runtime.CompilerServices; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using WhatsAppApi; -using WhatsAppApi.Account; -using WhatsAppApi.Register; - -namespace WhatsTest -{ - internal class Program - { - 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 = ""; // Mobile number with country code (but without + or 00) - //string imei = ""; // MAC Address for iOS IMEI for other platform (Android/etc) - - //WhatsApp wa = new WhatsApp(sender, imei, nickname, true); - - //string countrycode = sender.Substring(0, 2); - //string phonenumber = sender.Remove(0, 2); - - //if (!WhatsRegister.ExistsAndDelete(countrycode, phonenumber, imei)) - //{ - // PrintToConsole("Wrong Password"); - // return; - //} - - //wa.Connect(); - //wa.Login(); - //wa.sendNickname("test"); - - //ProcessChat(wa, ""); - RegisterAccount(); - - Console.ReadKey(); - } - - - private static void ProcessChat(WhatsApp wa, string dst) - { - var thRecv = new Thread(t => - { - try - { - while (wa != null) - { - if (!wa.HasMessages()) - { - wa.PollMessages(); - Thread.Sleep(100); - continue; - } - var buff = wa.GetAllMessages(); - } - } - catch (ThreadAbortException) - { - } - }) {IsBackground = true}; - thRecv.Start(); - WhatsUserManager usrMan = new WhatsUserManager(); - var tmpUser = usrMan.CreateUser(dst, "User"); - - while (true) - { - string line = Console.ReadLine(); - if (line == null && line.Length == 0) - continue; - - string command = line.Trim(); - switch (command) - { - case "/query": - //var dst = dst//trim(strstr($line, ' ', FALSE)); - PrintToConsole("[] Interactive conversation with {0}:", tmpUser); - break; - case "/accountinfo": - PrintToConsole("[] Account Info: {0}", wa.GetAccountInfo().ToString()); - break; - case "/lastseen": - PrintToConsole("[] Request last seen {0}", tmpUser); - wa.RequestLastSeen(tmpUser.GetFullJid()); - break; - case "/exit": - wa = null; - thRecv.Abort(); - return; - case "/start": - wa.WhatsSendHandler.SendComposing(tmpUser.GetFullJid()); - break; - case "/pause": - wa.WhatsSendHandler.SendPaused(tmpUser.GetFullJid()); - break; - case "/register": - { - RegisterAccount(); - break; - } - default: - PrintToConsole("[] Send message to {0}: {1}", tmpUser, line); - wa.Message(tmpUser.GetFullJid(), line); - break; - } - } - } - - private static void RegisterAccount() - { - Console.Write("CountryCode (ex. 49): "); - string countryCode = Console.ReadLine(); - Console.Write("Phonenumber: "); - string phoneNumber = Console.ReadLine(); - - if (!WhatsRegister.RegisterUser(countryCode, phoneNumber)) - return; - Console.Write("Enter send Code: "); - string tmpCode = Console.ReadLine(); - Console.Write("Enter your new Password: "); - string tmpPassword = Console.ReadLine(); - - WhatsRegister.VerifyRegistration(countryCode, phoneNumber, tmpPassword, tmpCode); - } - - - [MethodImpl(MethodImplOptions.Synchronized)] - private static void PrintToConsole(string value, params object[] tmpParams) - { - Console.WriteLine(value, tmpParams); - } - } -}