From b32bd0a340de47fda10fbca63c6cf664327533cf Mon Sep 17 00:00:00 2001 From: George Hazan Date: Wed, 2 Sep 2015 18:01:59 +0000 Subject: WhatsApp: - added ability to send media files (patch by Cassio); - fix for wrongly sent acks (patch by Cassio); - project files cleaned up; - version bump; git-svn-id: http://svn.miranda-ng.org/main/trunk@15154 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/WhatsApp/WhatsApp_10.vcxproj | 117 +++------------ protocols/WhatsApp/WhatsApp_10.vcxproj.filters | 9 ++ protocols/WhatsApp/WhatsApp_12.vcxproj | 110 +++----------- protocols/WhatsApp/WhatsApp_12.vcxproj.filters | 9 ++ protocols/WhatsApp/WhatsApp_14.vcxproj | 110 +++----------- protocols/WhatsApp/WhatsApp_14.vcxproj.filters | 9 ++ .../WhatsApp/src/WhatsAPI++/MediaUploader.cpp | 140 ++++++++++++++++++ protocols/WhatsApp/src/WhatsAPI++/MediaUploader.h | 18 +++ protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp | 159 +++++++++++++++++++-- protocols/WhatsApp/src/WhatsAPI++/WAConnection.h | 17 +++ protocols/WhatsApp/src/media.cpp | 77 ++++++++++ protocols/WhatsApp/src/proto.cpp | 4 +- protocols/WhatsApp/src/proto.h | 2 + protocols/WhatsApp/src/utils.cpp | 17 +++ protocols/WhatsApp/src/utils.h | 3 + protocols/WhatsApp/src/version.h | 4 +- 16 files changed, 501 insertions(+), 304 deletions(-) create mode 100644 protocols/WhatsApp/src/WhatsAPI++/MediaUploader.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/MediaUploader.h create mode 100644 protocols/WhatsApp/src/media.cpp (limited to 'protocols') diff --git a/protocols/WhatsApp/WhatsApp_10.vcxproj b/protocols/WhatsApp/WhatsApp_10.vcxproj index 1025f8a435..ca2ff8a228 100644 --- a/protocols/WhatsApp/WhatsApp_10.vcxproj +++ b/protocols/WhatsApp/WhatsApp_10.vcxproj @@ -178,6 +178,7 @@ + NotUsing @@ -201,10 +202,7 @@ NotUsing - NotUsing - NotUsing - NotUsing - NotUsing + NotUsing @@ -214,124 +212,40 @@ - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use + ../common.h + + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h @@ -355,6 +269,7 @@ + diff --git a/protocols/WhatsApp/WhatsApp_10.vcxproj.filters b/protocols/WhatsApp/WhatsApp_10.vcxproj.filters index e59297c958..fd17f946a2 100644 --- a/protocols/WhatsApp/WhatsApp_10.vcxproj.filters +++ b/protocols/WhatsApp/WhatsApp_10.vcxproj.filters @@ -125,6 +125,12 @@ Miranda Plugin\Source Files + + Miranda Plugin\Source Files + + + WhatsAPI++\Source Files + @@ -211,6 +217,9 @@ Miranda Plugin\Header Files + + WhatsAPI++\Header Files + diff --git a/protocols/WhatsApp/WhatsApp_12.vcxproj b/protocols/WhatsApp/WhatsApp_12.vcxproj index f369f65cde..b39a2a5923 100644 --- a/protocols/WhatsApp/WhatsApp_12.vcxproj +++ b/protocols/WhatsApp/WhatsApp_12.vcxproj @@ -180,6 +180,7 @@ + NotUsing @@ -213,120 +214,40 @@ - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - ../common.h - ../common.h - ../common.h - ../common.h + ../common.h + + + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h @@ -349,6 +270,7 @@ + diff --git a/protocols/WhatsApp/WhatsApp_12.vcxproj.filters b/protocols/WhatsApp/WhatsApp_12.vcxproj.filters index 851eaa9000..5d66b2a650 100644 --- a/protocols/WhatsApp/WhatsApp_12.vcxproj.filters +++ b/protocols/WhatsApp/WhatsApp_12.vcxproj.filters @@ -128,6 +128,12 @@ Miranda Plugin\Source Files + + Miranda Plugin\Source Files + + + WhatsAPI++\Source Files + @@ -211,6 +217,9 @@ WhatsAPI++\Header Files + + WhatsAPI++\Header Files + diff --git a/protocols/WhatsApp/WhatsApp_14.vcxproj b/protocols/WhatsApp/WhatsApp_14.vcxproj index 53530c1a09..bfe1416d27 100644 --- a/protocols/WhatsApp/WhatsApp_14.vcxproj +++ b/protocols/WhatsApp/WhatsApp_14.vcxproj @@ -180,6 +180,7 @@ + NotUsing @@ -213,120 +214,40 @@ - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - ../common.h - ../common.h - ../common.h - ../common.h + ../common.h + + + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h - NotUsing - Use - ../common.h - Use - ../common.h - Use - ../common.h - Use - ../common.h + ../common.h @@ -349,6 +270,7 @@ + diff --git a/protocols/WhatsApp/WhatsApp_14.vcxproj.filters b/protocols/WhatsApp/WhatsApp_14.vcxproj.filters index 851eaa9000..79a42103cd 100644 --- a/protocols/WhatsApp/WhatsApp_14.vcxproj.filters +++ b/protocols/WhatsApp/WhatsApp_14.vcxproj.filters @@ -128,6 +128,12 @@ Miranda Plugin\Source Files + + WhatsAPI++\Source Files + + + Miranda Plugin\Source Files + @@ -211,6 +217,9 @@ WhatsAPI++\Header Files + + WhatsAPI++\Header Files + diff --git a/protocols/WhatsApp/src/WhatsAPI++/MediaUploader.cpp b/protocols/WhatsApp/src/WhatsAPI++/MediaUploader.cpp new file mode 100644 index 0000000000..319ad02555 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/MediaUploader.cpp @@ -0,0 +1,140 @@ +#include "../common.h" +#include "MediaUploader.h" + +// TODO get rid of unneeded headers added by NETLIBHTTPREQUEST. it sould look like this: +//POST https://mmiXYZ.whatsapp.net/u/gOzeKj6U64LABC +//Content-Type: multipart/form-data; boundary=zzXXzzYYzzXXzzQQ +//Host: mmiXYZ.whatsapp.net +//User-Agent: WhatsApp/2.12.96 S40Version/14.26 Device/Nokia302 +//Content-Length: 9999999999 +// +//So remove these somehow: +//Accept-Encoding: deflate, gzip +//Connection: Keep-Alive +//Proxy-Connection: Keep-Alive + +static NETLIBHTTPHEADER s_imageHeaders[] = +{ + { "User-Agent", ACCOUNT_USER_AGENT }, + { "Content-Type", "multipart/form-data; boundary=zzXXzzYYzzXXzzQQ" } +}; + +static std::vector* sttFileToMem(const TCHAR *ptszFileName) +{ + HANDLE hFile = CreateFile(ptszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return NULL; + + DWORD upperSize, lowerSize = GetFileSize(hFile, &upperSize); + std::vector *result = new std::vector(lowerSize); + ReadFile(hFile, (void*)result->data(), lowerSize, &upperSize, NULL); + CloseHandle(hFile); + return result; +} + +namespace MediaUploader +{ + std::string sendData(std::string host, std::string head, std::string filePath, std::string tail) + { + // TODO string crap: can this be done more nicely? + std::wstring stemp = std::wstring(filePath.begin(), filePath.end()); + LPCWSTR sw = stemp.c_str(); + + vector *dataVector = sttFileToMem(sw); + + vector allVector(head.begin(), head.end()); + allVector.insert(allVector.end(), dataVector->begin(), dataVector->end()); + allVector.insert(allVector.end(), tail.begin(), tail.end()); + + NETLIBHTTPREQUEST nlhr = { sizeof(NETLIBHTTPREQUEST) }; + nlhr.requestType = REQUEST_POST; + nlhr.szUrl = (char*)host.c_str(); + nlhr.headers = s_imageHeaders; + nlhr.headersCount = _countof(s_imageHeaders); + nlhr.flags = NLHRF_HTTP11 | NLHRF_GENERATEHOST | NLHRF_REMOVEHOST | NLHRF_SSL; + nlhr.pData = (char*)allVector.data(); + nlhr.dataLength = (int)allVector.size(); + + NETLIBHTTPREQUEST* pnlhr = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, + (WPARAM)WASocketConnection::hNetlibUser, (LPARAM)&nlhr); + + string data = pnlhr->pData; + + if (!data.empty()) + return data; + else return 0; + } + + std::string pushfile(std::string url, FMessage * message, std::string from) + { + return getPostString(url, message, from); + } + + std::string getPostString(std::string url, FMessage * message, std::string from) + { + string filePath = message->media_url; + string to = message->key.remote_jid; + long fileSize = message->media_size; + string extension = split(filePath, '.')[1]; + + const BYTE *path = (const BYTE*)filePath.c_str(); + + uint8_t digest[16]; + md5_string(filePath, digest); + char dest[33]; + bin2hex(digest, sizeof(digest), dest); + + string cryptoname = dest; + cryptoname += "." + extension; + string boundary = "zzXXzzYYzzXXzzQQ"; + + string hBAOS = "--" + boundary + "\r\n"; + hBAOS += "Content-Disposition: form-data; name=\"to\"\r\n\r\n"; + hBAOS += to + "\r\n"; + hBAOS += "--" + boundary + "\r\n"; + hBAOS += "Content-Disposition: form-data; name=\"from\"\r\n\r\n"; + hBAOS += from + "\r\n"; + hBAOS += "--" + boundary + "\r\n"; + hBAOS += "Content-Disposition: form-data; name=\"file\"; filename=\"" + cryptoname + "\"\r\n"; + hBAOS += "Content-Type: " + getMimeFromExtension(extension) + "\r\n\r\n"; + + string fBAOS = "\r\n--" + boundary + "--\r\n"; + long contentlength = sizeof(hBAOS) + sizeof(fBAOS) + fileSize; + + return sendData(url, hBAOS, filePath, fBAOS); + } + + static map extensions; + + std::string getMimeFromExtension(const string &extension) + { + if (extensions.empty()) { + extensions["audio/3gpp"] = "3gp"; + extensions["audio/x-caf"] = "caf"; + extensions["audio/wav"] = "wav"; + extensions["audio/mpeg"] = "mp3"; + extensions["audio/mpeg3"] = "mp3"; + extensions["audio/x-mpeg-32"] = "mp3"; + extensions["audio/x-ms-wma"] = "wma"; + extensions["audio/ogg"] = "ogg"; + extensions["audio/aiff"] = "aif"; + extensions["audio/x-aiff"] = "aif"; + extensions["audio/mp4"] = "m4a"; + extensions["image/jpeg"] = "jpg"; + extensions["image/gif"] = "gif"; + extensions["image/png"] = "png"; + extensions["video/3gpp"] = "3gp"; + extensions["video/mp4"] = "mp4"; + extensions["video/quicktime"] = "mov"; + extensions["video/avi"] = "avi"; + extensions["video/msvideo"] = "avi"; + extensions["video/x-msvideo"] = "avi"; + } + + for (auto it = extensions.begin(); it != extensions.end(); ++it) + if ((*it).second == extension) + return (*it).first; + + return ""; + } +} diff --git a/protocols/WhatsApp/src/WhatsAPI++/MediaUploader.h b/protocols/WhatsApp/src/WhatsAPI++/MediaUploader.h new file mode 100644 index 0000000000..7fb6612095 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/MediaUploader.h @@ -0,0 +1,18 @@ +/* +* +*/ +#ifndef MEDIAUPLOADER_H_ +#define MEDIAUPLOADER_H_ + +using namespace std; + +namespace MediaUploader +{ + std::string pushfile(std::string url, FMessage * message, std::string from); + std::string getPostString(std::string url, FMessage * message, std::string from); + std::string sendData(std::string host, std::string head, std::string filePath, std::string tail); + std::string getExtensionFromMime(string mime); + std::string getMimeFromExtension(const string &extension); +}; + +#endif /* MEDIAUPLOADER_H_ */ diff --git a/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp index 113a69d2f1..c271a83be2 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp +++ b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp @@ -198,6 +198,9 @@ bool WAConnection::read() throw(WAException) parseReceipt(node); else if (ProtocolTreeNode::tagEquals(node, "chatstate")) parseChatStates(node); + else { + rawConn->log("Warning: Node parsing not handled:\n", tmp.c_str()); + } delete node; return true; @@ -546,15 +549,11 @@ void WAConnection::parseNotification(ProtocolTreeNode *node) throw(WAException) if (bodyNode != NULL && m_pGroupEventHandler != NULL) m_pGroupEventHandler->onGroupNewSubject(from, participant, bodyNode->getDataAsString(), ts); } + else { + rawConn->log("Warning: Unknown Notification received:\n", node->toString().c_str()); + } - ProtocolTreeNode sendNode("ack"); - sendNode << XATTR("to", from) << XATTR("id", id) << XATTR("type", type) << XATTR("class", "notification"); - const string &to = node->getAttributeValue("to"); - if (!to.empty()) - sendNode << XATTR("from", to); - if (!participant.empty()) - sendNode << XATTR("participant", participant); - out.write(sendNode); + sendAck(node, "notification"); } void WAConnection::parsePresense(ProtocolTreeNode *node) throw(WAException) @@ -606,8 +605,7 @@ void WAConnection::parseReceipt(ProtocolTreeNode *node) throw(WAException) m_pEventHandler->onMessageStatusUpdate(msg); } - out.write(ProtocolTreeNode("ack") - << XATTR("to", from) << XATTR("id", id) << XATTR("type", "read") << XATTRI("t", time(0))); + sendAck(node,"receipt"); } std::vector* WAConnection::processGroupSettings(const std::vector& groups) @@ -730,6 +728,26 @@ void WAConnection::sendInactive() throw(WAException) out.write(ProtocolTreeNode("presence") << XATTR("type", "inactive")); } +void WAConnection::sendAck(ProtocolTreeNode *node, const char *classType) +{ + const string &from = node->getAttributeValue("from"); + const string &to = node->getAttributeValue("to"); + const string &participant = node->getAttributeValue("participant"); + const string &id = node->getAttributeValue("id"); + const string &type = node->getAttributeValue("type"); + + ProtocolTreeNode sendNode("ack"); + sendNode << XATTR("to", from) << XATTR("id", id) << XATTR("class", classType); + if (!to.empty()) + sendNode << XATTR("from", to); + if (!participant.empty()) + sendNode << XATTR("participant", participant); + if (!type.empty()) + sendNode << XATTR("type", type); + + out.write(sendNode); +} + ///////////////////////////////////////////////////////////////////////////////////////// ProtocolTreeNode* WAConnection::getMessageNode(FMessage* message, ProtocolTreeNode *child) @@ -757,6 +775,21 @@ void WAConnection::sendMessageWithMedia(FMessage* message) throw (WAException) if (message->media_wa_type == FMessage::WA_TYPE_SYSTEM) throw new WAException("Cannot send system message over the network"); + // request node for image, audio or video upload + if (message->media_wa_type == FMessage::WA_TYPE_IMAGE || message->media_wa_type == FMessage::WA_TYPE_AUDIO || message->media_wa_type == FMessage::WA_TYPE_VIDEO) { + std::string id = makeId("iq_"); + this->pending_server_requests[id] = new MediaUploadResponseHandler(this, *message); + + ProtocolTreeNode *mediaNode = new ProtocolTreeNode("media"); + mediaNode << XATTR("hash", message->media_name) << XATTR("type", FMessage::getMessage_WA_Type_StrValue(message->media_wa_type)) << XATTR("size", std::to_string(message->media_size)); + + ProtocolTreeNode *n = new ProtocolTreeNode("iq", mediaNode); + n << XATTR("id", id) << XATTR("to", this->domain) << XATTR("type", "set") << XATTR("xmlns", "w:m"); + out.write(*n); + delete n; + return; + } + ProtocolTreeNode *mediaNode; if (message->media_wa_type == FMessage::WA_TYPE_CONTACT && !message->media_name.empty()) { ProtocolTreeNode *vcardNode = new ProtocolTreeNode("vcard", new std::vector(message->data.begin(), message->data.end())) @@ -781,6 +814,110 @@ void WAConnection::sendMessageWithMedia(FMessage* message) throw (WAException) ProtocolTreeNode *n = WAConnection::getMessageNode(message, mediaNode); out.write(*n); delete n; + +} + +// TODO remove this code from WA purple +static std::string query_field(std::string work, std::string lo, bool integer = false) +{ + size_t p = work.find("\"" + lo + "\""); + if (p == std::string::npos) + return ""; + + work = work.substr(p + ("\"" + lo + "\"").size()); + + p = work.find("\""); + if (integer) + p = work.find(":"); + if (p == std::string::npos) + return ""; + + work = work.substr(p + 1); + + p = 0; + while (p < work.size()) { + if (work[p] == '"' && (p == 0 || work[p - 1] != '\\')) + break; + p++; + } + if (integer) { + p = 0; + while (p < work.size() && work[p] >= '0' && work[p] <= '9') + p++; + } + if (p == std::string::npos) + return ""; + + work = work.substr(0, p); + + return work; +} + +void WAConnection::processUploadResponse(ProtocolTreeNode * node, FMessage * message) +{ + ProtocolTreeNode* duplicate = node->getChild("duplicate"); + + // setup vars for media message + string fileType; + string caption, url, fileName, fileSize, filePath, fileHash; + caption = message->data; + + // parse node + if (duplicate != NULL) { + url = duplicate->getAttributeValue("url"); + fileSize = duplicate->getAttributeValue("size"); + fileHash = duplicate->getAttributeValue("filehash"); + fileType = duplicate->getAttributeValue("type"); + string tempfileName = duplicate->getAttributeValue("url"); + size_t index = tempfileName.find_last_of('/')+1; + fileName = tempfileName.substr(index); + } + else { + string json = MediaUploader::pushfile(node->getChild("media")->getAttributeValue("url"),message, this->user); + if (json.empty()) + return; + + //TODO why does the JSONNode not work? -> Throws some exception when trying to access elements after parsing. + + /*JSONNode resp = JSONNode::parse(json.c_str()); + fileName = resp["name"].as_string(); + url = resp["url"].as_string(); + fileSize = resp["size"].as_string(); + fileHash = resp["filehash"].as_string(); + fileType = resp["type"].as_string(); + */ + + // TODO remove this code from WA purple + size_t offset = json.find("{"); + if (offset == std::string::npos) + return; + json = json.substr(offset + 1); + + /* Look for closure */ + size_t cl = json.find("{"); + if (cl == std::string::npos) + cl = json.size(); + std::string work = json.substr(0, cl); + + fileName = query_field(work, "name"); + url = query_field(work, "url"); + fileSize = query_field(work, "size"); + fileHash = query_field(work, "filehash"); + fileType = query_field(work, "type"); + + } + + // TODO show caption and(?) link to media file in message window and history + ProtocolTreeNode *mediaNode = new ProtocolTreeNode("media"); + mediaNode << XATTR("type", fileType) + << XATTR("url", url) << XATTR("encoding", "raw") << XATTR("file", fileName) + << XATTR("size", fileSize)<< XATTR("caption", caption); + + ProtocolTreeNode * messageNode = new ProtocolTreeNode("message", mediaNode); + messageNode << XATTR("to", message->key.remote_jid) << XATTR("type", "media") + << XATTR("id", message->key.id) << XATTRI("t", (int)time(0)); + out.write(*messageNode); + delete messageNode; } void WAConnection::sendMessageWithBody(FMessage* message) throw (WAException) @@ -794,7 +931,7 @@ void WAConnection::sendMessageWithBody(FMessage* message) throw (WAException) void WAConnection::sendMessageReceived(const FMessage &message) throw(WAException) { out.write(ProtocolTreeNode("receipt") << XATTR("type", "read") - << XATTR("to", message.key.remote_jid) << XATTR("id", message.key.id) << XATTRI("t", (int)time(0))); + << XATTR("to", message.key.remote_jid) << XATTR("id", message.key.id)); } ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h index 03637b8c3a..a1a366c13e 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h +++ b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h @@ -20,6 +20,7 @@ #include "utilities.h" #include "BinTreeNodeReader.h" #include "BinTreeNodeWriter.h" +#include "MediaUploader.h" #pragma warning(disable : 4290) @@ -314,6 +315,19 @@ class WAConnection } }; + class MediaUploadResponseHandler : public IqResultHandler { + private: + FMessage message; + public: + MediaUploadResponseHandler(WAConnection* con, const FMessage &message) :IqResultHandler(con) { this->message = message; } + virtual void parse(ProtocolTreeNode* node, const std::string &from) throw (WAException) { + this->con->processUploadResponse(node, &message); + } + void error(ProtocolTreeNode* node) throw (WAException) { + con->logData("Error on Media Upload Request: %s", node->toString().c_str()); + } + }; + friend class WALogin; private: @@ -335,6 +349,8 @@ private: void parseReceipt(ProtocolTreeNode *node) throw (WAException); std::map parseCategories(ProtocolTreeNode* node) throw(WAException); + void processUploadResponse(ProtocolTreeNode *node, FMessage *message); + void sendMessageWithMedia(FMessage* message) throw(WAException); void sendMessageWithBody(FMessage* message) throw(WAException); ProtocolTreeNode* getReceiptAck(const std::string &to, const std::string &id, const std::string &receiptType) throw(WAException); @@ -381,6 +397,7 @@ public: bool read() throw(WAException); + void sendAck(ProtocolTreeNode * node, const char *classType); void sendPing() throw(WAException); void sendQueryLastOnline(const std::string &jid) throw (WAException); void sendPong(const std::string &id) throw(WAException); diff --git a/protocols/WhatsApp/src/media.cpp b/protocols/WhatsApp/src/media.cpp new file mode 100644 index 0000000000..0435eea06d --- /dev/null +++ b/protocols/WhatsApp/src/media.cpp @@ -0,0 +1,77 @@ +#include "common.h" + +HANDLE WhatsAppProto::SendFile(MCONTACT hContact, const TCHAR* desc, TCHAR **ppszFiles) { + if (!isOnline()) + return 0; + + ptrA jid(getStringA(hContact, "ID")); + if (jid == NULL) + return 0; + + // input validation + char *name = mir_utf8encodeW(ppszFiles[0]); + string mime = MediaUploader::getMimeFromExtension(split(name, '.')[1]); + if (mime.empty()) + return 0; + + // get file size + FILE *hFile = _tfopen(ppszFiles[0], _T("rb")); + if (hFile == NULL) { + debugLogA(__FUNCTION__": cannot open file %s", ppszFiles[0]); + return 0; + } + _fseeki64(hFile, 0, SEEK_END); + uint64_t fileSize = _ftelli64(hFile); + fclose(hFile); + + // get filetype from mimeType + int fileType = FMessage::getMessage_WA_Type(split(mime, '/')[0]); + + // check max file sizes + switch (fileType) { + case FMessage::WA_TYPE_IMAGE: + if (fileSize >= 5 * 1024 * 1024) + return 0; + break; + case FMessage::WA_TYPE_AUDIO: + if (fileSize >= 10 * 1024 * 1024) + return 0; + break; + case FMessage::WA_TYPE_VIDEO: + if (fileSize >= 20 * 1024 * 1024) + return 0; + break; + default: + return 0; + } + + int msgId = GetSerial(); + time_t now = time(NULL); + std::string msgid = Utilities::intToStr(now) + "-" + Utilities::intToStr(msgId); + FMessage * fmsg = new FMessage(std::string(jid), true, msgid); + fmsg->media_url = name; + fmsg->media_size = fileSize; + fmsg->media_wa_type = fileType; + fmsg->data = mir_utf8encodeW(desc); + + // calculate file hash + unsigned char hash[MIR_SHA256_HASH_SIZE]; + SHA256_CONTEXT sha256; + mir_sha256_init(&sha256); + + FILE *fd = _tfopen(ppszFiles[0], _T("rb")); + int read = 0; + do { + char buf[1024]; + read = (int)fread(buf, 1, 1024, fd); + mir_sha256_write(&sha256, buf, read); + } while (read > 0); + fclose(fd); + + mir_sha256_final(&sha256, hash); + fmsg->media_name = mir_base64_encode((BYTE*)hash,sizeof(hash)); + + // request media upload url + m_pConnection->sendMessage(fmsg); + return (HANDLE)fmsg; // TODO what to return here to make the upload shown complete when done and how to handle errors? +} \ No newline at end of file diff --git a/protocols/WhatsApp/src/proto.cpp b/protocols/WhatsApp/src/proto.cpp index 9f7099ba98..f3400a94f4 100644 --- a/protocols/WhatsApp/src/proto.cpp +++ b/protocols/WhatsApp/src/proto.cpp @@ -86,13 +86,13 @@ DWORD_PTR WhatsAppProto::GetCaps(int type, MCONTACT hContact) { switch (type) { case PFLAGNUM_1: - return PF1_IM | PF1_CHAT | PF1_BASICSEARCH | PF1_ADDSEARCHRES | PF1_MODEMSGRECV; + return PF1_IM | PF1_FILESEND | PF1_CHAT | PF1_BASICSEARCH | PF1_ADDSEARCHRES | PF1_MODEMSGRECV; case PFLAGNUM_2: return PF2_ONLINE | PF2_INVISIBLE; case PFLAGNUM_3: return 0; case PFLAGNUM_4: - return PF4_NOCUSTOMAUTH | PF4_FORCEADDED | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE | PF4_SUPPORTTYPING | PF4_AVATARS; + return PF4_NOCUSTOMAUTH | PF4_FORCEADDED | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE | PF4_OFFLINEFILES | PF4_SUPPORTTYPING | PF4_AVATARS; case PFLAGNUM_5: return 0; case PFLAG_UNIQUEIDTEXT: diff --git a/protocols/WhatsApp/src/proto.h b/protocols/WhatsApp/src/proto.h index 7c153ef781..0f7000fc5c 100644 --- a/protocols/WhatsApp/src/proto.h +++ b/protocols/WhatsApp/src/proto.h @@ -50,6 +50,8 @@ public: virtual int __cdecl SendMsg(MCONTACT hContact, int flags, const char* msg); + virtual HANDLE __cdecl SendFile(MCONTACT hContact, const TCHAR*, TCHAR **ppszFiles); + virtual int __cdecl SetStatus(int iNewStatus); virtual int __cdecl UserIsTyping(MCONTACT hContact, int type); diff --git a/protocols/WhatsApp/src/utils.cpp b/protocols/WhatsApp/src/utils.cpp index 6d7e77b15a..4458f976f0 100644 --- a/protocols/WhatsApp/src/utils.cpp +++ b/protocols/WhatsApp/src/utils.cpp @@ -76,3 +76,20 @@ void md5_string(const std::string &data, BYTE digest[16]) { utils::md5string(data, digest); } + +std::vector &split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + if (item.length() > 0) { + elems.push_back(item); + } + } + return elems; +} + +std::vector split(const std::string &s, char delim) { + std::vector elems; + split(s, delim, elems); + return elems; +} diff --git a/protocols/WhatsApp/src/utils.h b/protocols/WhatsApp/src/utils.h index d9a1218312..0e760f5013 100644 --- a/protocols/WhatsApp/src/utils.h +++ b/protocols/WhatsApp/src/utils.h @@ -31,6 +31,9 @@ __forceinline TCHAR* str2t(const std::string &str) { return mir_utf8decodeT(str.c_str()); } +std::vector split(const std::string &s, char delim); +std::vector &split(const std::string &s, char delim, std::vector &elems); + namespace utils { TCHAR* removeA(TCHAR *str); diff --git a/protocols/WhatsApp/src/version.h b/protocols/WhatsApp/src/version.h index a9cf6d26e6..cc650ef424 100644 --- a/protocols/WhatsApp/src/version.h +++ b/protocols/WhatsApp/src/version.h @@ -1,7 +1,7 @@ #define __MAJOR_VERSION 0 #define __MINOR_VERSION 1 -#define __RELEASE_NUM 2 -#define __BUILD_NUM 15 +#define __RELEASE_NUM 3 +#define __BUILD_NUM 1 #include -- cgit v1.2.3