diff options
author | Fishbone <fishbone@miranda-ng.org> | 2013-06-02 16:19:21 +0000 |
---|---|---|
committer | Fishbone <fishbone@miranda-ng.org> | 2013-06-02 16:19:21 +0000 |
commit | ab7e0b08fa8c31cf1d468ab4b3c660e2b1836811 (patch) | |
tree | 52977603ea0f431adff16573d3d5b46a95843c7f /protocols/WhatsApp/src | |
parent | 8320783f99419db1e40346fdb292c19ee008948b (diff) |
Added WhatsApp-protocol
git-svn-id: http://svn.miranda-ng.org/main/trunk@4861 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols/WhatsApp/src')
50 files changed, 8945 insertions, 0 deletions
diff --git a/protocols/WhatsApp/src/WASocketConnection.cpp b/protocols/WhatsApp/src/WASocketConnection.cpp new file mode 100644 index 0000000000..f4e6ef8ae4 --- /dev/null +++ b/protocols/WhatsApp/src/WASocketConnection.cpp @@ -0,0 +1,138 @@ +#include "WASocketConnection.h"
+
+
+HANDLE WASocketConnection::hNetlibUser = NULL;
+
+void WASocketConnection::initNetwork(HANDLE hNetlibUser) throw (WAException) {
+ WASocketConnection::hNetlibUser = hNetlibUser;
+}
+
+void WASocketConnection::quitNetwork() {
+}
+
+WASocketConnection::WASocketConnection(const std::string& dir, int port) throw (WAException)
+{
+ NETLIBOPENCONNECTION noc = {sizeof(noc)};
+ noc.szHost = dir.c_str();
+ noc.wPort = port;
+ noc.flags = NLOCF_V2; // | NLOCF_SSL;
+ this->hConn = (HANDLE) CallService(MS_NETLIB_OPENCONNECTION, reinterpret_cast<WPARAM>(this->hNetlibUser),
+ reinterpret_cast<LPARAM>(&noc));
+ if (this->hConn == NULL)
+ {
+ throw WAException(getLastErrorMsg(), WAException::SOCKET_EX, WAException::SOCKET_EX_OPEN);
+ }
+
+ this->connected = true;
+}
+
+void WASocketConnection::write(int i) {
+ char buffer;
+ buffer = (char) i;
+
+ NETLIBBUFFER nlb;
+ nlb.buf = &buffer;
+ nlb.len = 1;
+ nlb.flags = MSG_NOHTTPGATEWAYWRAP | MSG_NODUMP;
+
+ int result = CallService(MS_NETLIB_SEND, reinterpret_cast<WPARAM>(this->hConn), reinterpret_cast<LPARAM>(&nlb));
+ if (result < 1) {
+ throw WAException(getLastErrorMsg(), WAException::SOCKET_EX, WAException::SOCKET_EX_SEND);
+ }
+}
+
+void WASocketConnection::makeNonBlock() {
+ //if (fcntl(socket->channel, F_SETFL, O_NONBLOCK) == -1) // #TODO !?
+ throw WAException("Error setting socket nonblocking!", WAException::SOCKET_EX, WAException::SOCKET_EX_OPEN);
+}
+
+int WASocketConnection::waitForRead() {
+ // #TODO Is this called at all?
+ return 0;
+
+ fd_set rfds;
+ struct timeval tv;
+ struct timeval* tvp;
+ int fd = 0;
+
+ FD_ZERO(&rfds);
+ // _LOGDATA("preparando select");
+ //fd = (this->socket)->channel; //#!?
+ // _LOGDATA("socket %d", fd);
+ FD_SET(fd, &rfds);
+ tv.tv_sec = 600; //ApplicationData::SELECT_TIMEOUT;
+ tv.tv_usec = 0; // 5000000;
+ tvp = &tv;
+ //if (ApplicationData::SELECT_TIMEOUT == -1) #TODO
+ // tvp = NULL;
+
+ int retval = select(/*fd + 1*/ 0, &rfds, NULL, NULL, tvp);
+ if (!FD_ISSET(fd, &rfds))
+ retval = 0;
+
+ return retval;
+}
+
+void WASocketConnection::flush() {}
+
+void WASocketConnection::write(const std::vector<unsigned char>& bytes, int offset, int length)
+{
+ NETLIBBUFFER nlb;
+ std::string tmpBuf = std::string(bytes.begin(), bytes.end());
+ nlb.buf = (char*) &(tmpBuf.c_str()[offset]);
+ nlb.len = length;
+ nlb.flags = 0; //MSG_NOHTTPGATEWAYWRAP | MSG_NODUMP;
+
+ int result = CallService(MS_NETLIB_SEND, reinterpret_cast<WPARAM>(this->hConn),
+ reinterpret_cast<LPARAM>(&nlb));
+ if (result < length) {
+ throw WAException(getLastErrorMsg(), WAException::SOCKET_EX, WAException::SOCKET_EX_SEND);
+ }
+}
+
+void WASocketConnection::write(const std::vector<unsigned char>& bytes, int length)
+{
+ this->write(bytes, 0, length);
+}
+
+unsigned char WASocketConnection::read() {
+ char c;
+
+ SetLastError(0);
+ int result;
+ //do {
+ result = Netlib_Recv(this->hConn, &c, 1, 0 /*MSG_NOHTTPGATEWAYWRAP | MSG_NODUMP*/);
+ //} while (WSAGetLastError() == EINTR);
+ if (result <= 0) {
+ throw WAException(getLastErrorMsg(), WAException::SOCKET_EX, WAException::SOCKET_EX_RECV);
+ }
+ return c;
+}
+
+int WASocketConnection::read(std::vector<unsigned char>& b, int off, int length) {
+ if (off < 0 || length < 0) {
+ throw new WAException("Out of bounds", WAException::SOCKET_EX, WAException::SOCKET_EX_RECV);
+ }
+ char* buffer = new char[length];
+ int result = Netlib_Recv(this->hConn, buffer, length, MSG_NOHTTPGATEWAYWRAP | MSG_NODUMP);
+
+ if (result <= 0) {
+ throw WAException(getLastErrorMsg(), WAException::SOCKET_EX, WAException::SOCKET_EX_RECV);
+ }
+
+ for (int i = 0; i < result; i++)
+ b[off + i] = buffer[i];
+
+ delete[] buffer;
+
+ return result;
+}
+
+void WASocketConnection::forceShutdown() {
+ Netlib_Shutdown(this->hConn);
+}
+
+WASocketConnection::~WASocketConnection() {
+ this->forceShutdown();
+ //SDLNet_TCP_Close(this->socket);
+}
\ No newline at end of file diff --git a/protocols/WhatsApp/src/WASocketConnection.h b/protocols/WhatsApp/src/WASocketConnection.h new file mode 100644 index 0000000000..f2d46415bc --- /dev/null +++ b/protocols/WhatsApp/src/WASocketConnection.h @@ -0,0 +1,41 @@ +#if !defined(WASOCKETCONNECTION_H)
+#define WASOCKETCONNECTION_H
+
+#include "common.h"
+#include "WhatsAPI++/ISocketConnection.h"
+#include <iostream>
+#include "WhatsAPI++/WAException.h"
+#include "WhatsAPI++/WALogin.h"
+#include "WhatsAPI++/base64.h"
+#include <windows.h>
+
+class WASocketConnection : public ISocketConnection
+{
+public:
+ static HANDLE hNetlibUser;
+
+private:
+ int readSize;
+ int maxBufRead;
+ bool connected;
+
+ HANDLE hConn;
+
+public:
+ WASocketConnection(const std::string& dir, int port) throw (WAException);
+ void write(int i);
+ unsigned char read();
+ void flush();
+ void write(const std::vector<unsigned char>& b, int length);
+ void write(const std::vector<unsigned char>& bytes, int offset, int length);
+ int read(std::vector<unsigned char>& b, int off, int length);
+ void makeNonBlock();
+ int waitForRead();
+ void forceShutdown();
+
+ virtual ~WASocketConnection();
+ static void initNetwork(HANDLE hNetlibUser) throw (WAException);
+ static void quitNetwork();
+};
+
+#endif // WASOCKETCONNECTION_H
\ No newline at end of file diff --git a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp new file mode 100644 index 0000000000..7155334c7f --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp @@ -0,0 +1,349 @@ +/*
+ * BinTreeNodeReader.cpp
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+#include "stdafx.h"
+#include "BinTreeNodeReader.h"
+#include "WAException.h"
+#include "ProtocolTreeNode.h"
+#include "utilities.h"
+#include <string>
+
+
+
+BinTreeNodeReader::BinTreeNodeReader(WAConnection* conn, ISocketConnection* connection, const char** dictionary, const int dictionarysize) {
+ this->conn = conn;
+ this->rawIn = connection;
+ this->tokenMap = dictionary;
+ this->tokenmapsize = dictionarysize;
+ this->readSize = 1;
+ this->in = NULL;
+ this->buf = new std::vector<unsigned char>(BUFFER_SIZE);
+}
+
+BinTreeNodeReader::~BinTreeNodeReader() {
+ if (this->buf != NULL)
+ delete this->buf;
+ if (this->in != NULL)
+ delete this->in;
+}
+
+ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() {
+ int b = this->in->read();
+ int size = readListSize(b);
+ b = this->in->read();
+ if (b == 2)
+ return NULL;
+
+ std::string* tag = this->readStringAsString(b);
+
+ if ((size == 0) || (tag == NULL))
+ throw WAException("nextTree sees 0 list or null tag", WAException::CORRUPT_STREAM_EX, -1);
+ int attribCount = (size - 2 + size % 2) / 2;
+ std::map<string,string>* attribs = readAttributes(attribCount);
+ if (size % 2 == 1) {
+ ProtocolTreeNode* ret = new ProtocolTreeNode(*tag, attribs);
+ delete tag;
+ return ret;
+ }
+ b = this->in->read();
+ if (isListTag(b)) {
+ ProtocolTreeNode* ret = new ProtocolTreeNode(*tag, attribs, NULL, readList(b));
+ delete tag;
+ return ret;
+ }
+
+ ReadData* obj = this->readString(b);
+ std::vector<unsigned char>* data;
+ if (obj->type == STRING) {
+ std::string* s = (std::string*) obj->data;
+ data = new std::vector<unsigned char>(s->begin(), s->end());
+ delete s;
+ } else {
+ data = (std::vector<unsigned char>*) obj->data;
+ }
+
+ ProtocolTreeNode* ret = new ProtocolTreeNode(*tag, attribs, data);
+ delete obj;
+ delete tag;
+ return ret;
+}
+
+bool BinTreeNodeReader::isListTag(int b) {
+ return (b == 248) || (b == 0) || (b == 249);
+}
+
+void BinTreeNodeReader::decodeStream(int flags, int offset, int length) {
+ if ((flags & 8) != 0) {
+ if (length < 4) {
+ throw WAException("invalid length" + length, WAException::CORRUPT_STREAM_EX, 0);
+ }
+ offset += 4;
+ length -= 4;
+ this->conn->inputKey->decodeMessage(&(*this->buf)[0], offset - 4, offset, length);
+ }
+ if (this->in != NULL)
+ delete this->in;
+ this->in = new ByteArrayInputStream(this->buf, offset, length);
+}
+
+std::map<string, string>* BinTreeNodeReader::readAttributes(int attribCount) {
+ std::map<string, string>* attribs = new std::map<string, string>();
+ for (int i = 0; i < attribCount; i++) {
+ std::string* key = readStringAsString();
+ std::string* value = readStringAsString();
+ (*attribs)[*key] = *value;
+ delete key;
+ delete value;
+ }
+ return attribs;
+}
+
+std::vector<ProtocolTreeNode*>* BinTreeNodeReader::readList(int token) {
+ int size = readListSize(token);
+ std::vector<ProtocolTreeNode*>* list = new std::vector<ProtocolTreeNode*>(size);
+ for (int i = 0; i < size; i++) {
+ (*list)[i] = nextTreeInternal();
+ }
+
+ return list;
+}
+
+int BinTreeNodeReader::readListSize(int token) {
+ int size;
+ if (token == 0) {
+ size = 0;
+ }
+ else {
+ size = 0;
+ if (token == 248) {
+ size = readInt8(this->in);
+ }
+ else
+ {
+ size = 0;
+ if (token == 249)
+ size = readInt16(this->in);
+ else
+ throw new WAException("invalid list size in readListSize: token " + token, WAException::CORRUPT_STREAM_EX, 0);
+ }
+ }
+
+ return size;
+}
+
+std::vector<ProtocolTreeNode*>* BinTreeNodeReader::readList() {
+ return readList(this->in->read());
+}
+
+ReadData* BinTreeNodeReader::readString() {
+ return readString(this->in->read());
+}
+
+ReadData* BinTreeNodeReader::readString(int token) {
+ if (token == -1) {
+ throw WAException("-1 token in readString", WAException::CORRUPT_STREAM_EX, -1);
+ }
+
+ ReadData* ret = new ReadData();
+
+ if ((token > 4) && (token < 245)) {
+ ret->type = STRING;
+ ret->data = new std::string(getToken(token));
+ return ret;
+ }
+
+ switch(token) {
+ case 0:
+ return NULL;
+ case 252: {
+ int size8 = readInt8(this->in);
+ std::vector<unsigned char>* buf8 = new std::vector<unsigned char>(size8);
+ fillArray(*buf8, size8, this->in);
+ // std::string* ret = new std::string(buf8->begin(), buf8->end());
+ // delete buf8;
+ ret->type = ARRAY;
+ ret->data = buf8;
+ return ret;
+ }
+ case 253: {
+ int size24 = readInt24(this->in);
+ std::vector<unsigned char>* buf24 = new std::vector<unsigned char>(size24);
+ fillArray(*buf24, size24, this->in);
+ // std::string* ret = new std::string(buf24->begin(), buf24->end());
+ // delete buf24;
+ ret->type = ARRAY;
+ ret->data = buf24;
+
+ return ret;
+ }
+ case 254: {
+ token = (unsigned char) this->in->read();
+ ret->type = STRING;
+ ret->data = new std::string(getToken(245 + token));
+ return ret;
+ }
+ case 250: {
+ std::string* user = readStringAsString();
+ std::string* server = readStringAsString();
+ if ((user != NULL) && (server != NULL)) {
+ std::string* result = new std::string(*user + "@" + *server);
+ delete user;
+ delete server;
+ ret->type = STRING;
+ ret->data = result;
+ return ret;
+ }
+ if (server != NULL) {
+ ret->type = STRING;
+ ret->data = server;
+ return ret;
+ }
+ throw WAException("readString couldn't reconstruct jid", WAException::CORRUPT_STREAM_EX, -1);
+ }
+ }
+ throw WAException("readString couldn't match token" + (int) token, WAException::CORRUPT_STREAM_EX, -1);
+}
+
+std::string* BinTreeNodeReader::objectAsString(ReadData* o) {
+ if (o->type == STRING) {
+ return (std::string*) o->data;
+ }
+
+ if (o->type == ARRAY) {
+ std::vector<unsigned char>* v = (std::vector<unsigned char>*) o->data;
+ std::string* ret = new std::string(v->begin(), v->end());
+ delete v;
+ return ret;
+ }
+
+ return NULL;
+}
+
+std::string* BinTreeNodeReader::readStringAsString() {
+ ReadData* o = this->readString();
+ std::string* ret = this->objectAsString(o);
+ delete o;
+ return ret;
+}
+
+std::string* BinTreeNodeReader::readStringAsString(int token) {
+ ReadData* o = this->readString(token);
+ std::string* ret = this->objectAsString(o);
+ delete o;
+ return ret;
+}
+
+
+void BinTreeNodeReader::fillArray(std::vector<unsigned char>& buff, int len, ByteArrayInputStream* in) {
+ int count = 0;
+ while (count < len) {
+ count += in->read(buff, count, len - count);
+ }
+}
+
+void BinTreeNodeReader::fillArray(std::vector<unsigned char>& buff, int len, ISocketConnection* in) {
+ int count = 0;
+ while (count < len) {
+ count += in->read(buff, count, len - count);
+ }
+}
+
+
+std::string BinTreeNodeReader::getToken(int token) {
+ std::string ret;
+
+ if ((token >= 0) && (token < this->tokenmapsize))
+ ret = std::string(this->tokenMap[token]);
+ if (ret.empty()) {
+ throw WAException("invalid token/length in getToken", WAException::CORRUPT_STREAM_EX, 0);
+ }
+ return ret;
+}
+
+ +void BinTreeNodeReader::getTopLevelStream() {
+ int stanzaSize;
+ int flags;
+ int byte = readInt8(this->rawIn);
+ flags = byte >> 4;
+ int size0 = byte & 15;
+ int size1 = readInt8(this->rawIn);
+ int size2 = readInt8(this->rawIn);
+
+ stanzaSize = (size0 << 16) + (size1 << 8) + size2;
+
+ if (this->buf->size() < (size_t) stanzaSize) {
+ int newsize = std::max((int) (this->buf->size() * 3 / 2), stanzaSize);
+ delete this->buf;
+ this->buf = new std::vector<unsigned char>(newsize);
+ }
+ fillArray(*this->buf, stanzaSize, this->rawIn);
+
+ this->decodeStream(flags, 0, stanzaSize);
+}
+
+int BinTreeNodeReader::readInt8(ByteArrayInputStream* in) {
+ return in->read();
+}
+
+int BinTreeNodeReader::readInt16(ByteArrayInputStream* in) {
+ int intTop = in->read();
+ int intBot = in->read();
+ int value = (intTop << 8) + intBot;
+ return value;
+}
+
+int BinTreeNodeReader::readInt24(ByteArrayInputStream* in) {
+ int int1 = in->read();
+ int int2 = in->read();
+ int int3 = in->read();
+ int value = (int1 << 16) + (int2 << 8) + int3;
+
+ return value;
+}
+
+ProtocolTreeNode* BinTreeNodeReader::nextTree() {
+ this->getTopLevelStream();
+ return nextTreeInternal();
+}
+
+void BinTreeNodeReader::streamStart() {
+ this->getTopLevelStream();
+
+ int tag = this->in->read();
+ int size = readListSize(tag);
+ tag = this->in->read();
+ if (tag != 1) {
+ throw WAException("expecting STREAM_START in streamStart", WAException::CORRUPT_STREAM_EX, 0);
+ }
+ int attribCount = (size - 2 + size % 2) / 2;
+
+ std::map<string,string>* attributes = readAttributes(attribCount);
+ delete attributes;
+}
+
+int BinTreeNodeReader::readInt8(ISocketConnection* in) {
+ return in->read();
+}
+
+int BinTreeNodeReader::readInt16(ISocketConnection* in) {
+ int intTop = in->read();
+ int intBot = in->read();
+ int value = (intTop << 8) + intBot;
+ return value;
+}
+
+int BinTreeNodeReader::readInt24(ISocketConnection* in) {
+ int int1 = in->read();
+ int int2 = in->read();
+ int int3 = in->read();
+ int value = (int1 << 16) + (int2 << 8) + int3;
+
+ return value;
+}
+
+
+
diff --git a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h new file mode 100644 index 0000000000..61b70b218b --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h @@ -0,0 +1,76 @@ +/*
+ * BinTreeNodeReader.h
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+#ifndef BINTREENODEREADER_H_
+#define BINTREENODEREADER_H_
+
+#include "ProtocolTreeNode.h"
+#include "ISocketConnection.h"
+#include "ByteArray.h"
+#include "WAConnection.h"
+#include <string>
+#include <vector>
+#include <map>
+
+#define BUFFER_SIZE 512
+
+class WAConnection;
+
+enum ReadType {STRING, ARRAY};
+
+class ReadData {
+public:
+ ReadData() {};
+ virtual ~ReadData() {};
+
+ ReadType type;
+ void * data;
+};
+
+class BinTreeNodeReader {
+private:
+ const char** tokenMap;
+ int tokenmapsize;
+ ISocketConnection *rawIn;
+ ByteArrayInputStream* in;
+ std::vector<unsigned char>* buf;
+ int readSize;
+ WAConnection* conn;
+
+ ProtocolTreeNode* nextTreeInternal();
+ bool isListTag(int b);
+ void decodeStream(int flags, int offset, int length);
+ std::map<string,string>* readAttributes(int attribCount);
+ std::vector<ProtocolTreeNode*>* readList(int token);
+ int readListSize(int token);
+ std::vector<ProtocolTreeNode*>* readList();
+ ReadData* readString();
+ ReadData* readString(int token);
+ static void fillArray(std::vector<unsigned char>& buff, int len, ByteArrayInputStream* in);
+ static void fillArray(std::vector<unsigned char>& buff, int len, ISocketConnection* in);
+ std::string* objectAsString(ReadData* o);
+ std::string* readStringAsString();
+ std::string* readStringAsString(int token);
+ std::string getToken(int token);
+ void getTopLevelStream();
+ static int readInt8(ByteArrayInputStream* in);
+ static int readInt8(ISocketConnection* in);
+ static int readInt16(ByteArrayInputStream* in);
+ static int readInt16(ISocketConnection* in);
+ static int readInt24(ByteArrayInputStream* in);
+ static int readInt24(ISocketConnection* in);
+
+
+public:
+ BinTreeNodeReader(WAConnection* conn, ISocketConnection* connection, const char** dictionary, const int dictionarysize);
+ virtual ~BinTreeNodeReader();
+ ProtocolTreeNode* nextTree();
+ void streamStart();
+
+};
+
+#endif /* BINTREENODEREADER_H_ */
+
diff --git a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp new file mode 100644 index 0000000000..dd35f6a8e7 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp @@ -0,0 +1,267 @@ +/*
+ * BinTreeNodeWriter.cpp
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+#include "stdafx.h"
+#include "BinTreeNodeWriter.h"
+#include <cstring>
+#include "utilities.h"
+
+BinTreeNodeWriter::BinTreeNodeWriter(WAConnection* conn, ISocketConnection* connection,
+ const char** dictionary, const int dictionarysize, IMutex* mutex) {
+ this->mutex = mutex;
+ this->conn = conn;
+ this->out = new ByteArrayOutputStream(2048);
+ this->realOut = connection;
+ for (int i = 0; i < dictionarysize; i++) {
+ std::string token(dictionary[i]);
+ if (token.compare("") != 0)
+ this->tokenMap[token] = i;
+ }
+ this->dataBegin = 0;
+}
+
+void BinTreeNodeWriter::writeDummyHeader()
+{
+ int num = 3;
+ this->dataBegin = this->out->getPosition();
+ int num2 = this->dataBegin + num;
+ this->out->setLength(num2);
+ this->out->setPosition(num2);
+}
+
+
+void BinTreeNodeWriter::processBuffer()
+{
+ bool flag = this->conn->outputKey != NULL;
+ unsigned int num = 0u;
+ if (flag)
+ {
+ long num2 = this->out->getLength() + 4L;
+ this->out->setLength(num2);
+ this->out->setPosition(num2);
+ num |= 1u;
+ }
+ long num3 = this->out->getLength() - 3L - (long) this->dataBegin;
+ if (num3 >= 1048576L)
+ {
+ throw WAException("Buffer too large: " + num3, WAException::CORRUPT_STREAM_EX, 0);
+ }
+
+ std::vector<unsigned char>* buffer = this->out->getBuffer();
+ if (flag)
+ {
+ int num4 = (int)num3 - 4;
+ this->conn->outputKey->encodeMessage(buffer->data(), this->dataBegin + 3 + num4, this->dataBegin + 3, num4);
+ }
+ (*buffer)[this->dataBegin] = (unsigned char)((unsigned long)((unsigned long)num << 4) | (unsigned long)((num3 & 16711680L) >> 16));
+ (*buffer)[this->dataBegin + 1] = (unsigned char)((num3 & 65280L) >> 8);
+ (*buffer)[this->dataBegin + 2] = (unsigned char)(num3 & 255L);
+}
+
+void BinTreeNodeWriter::streamStart(std::string domain, std::string resource) {
+ this->mutex->lock();
+ try {
+ this->out->setPosition(0);
+ this->out->setLength(0);
+ this->out->write(87);
+ this->out->write(65);
+ this->out->write(1);
+ this->out->write(2);
+
+ std::map<string, string> attributes;
+ attributes["to"] = domain;
+ attributes["resource"] = resource;
+ this->writeDummyHeader();
+ this->writeListStart(attributes.size() * 2 + 1);
+ this->out->write(1);
+ this->writeAttributes(&attributes);
+ this->processBuffer();
+ this->flushBuffer(true, 0);
+ } catch (exception& ex) {
+ this->mutex->unlock();
+ throw ex;
+ }
+ this->mutex->unlock();
+}
+
+void BinTreeNodeWriter::writeListStart(int i) {
+ if (i == 0) {
+ this->out->write(0);
+ } else if (i < 256) {
+ this->out->write(248);
+ writeInt8(i);
+ } else {
+ this->out->write(249);
+ writeInt16(i);
+ }
+}
+
+void BinTreeNodeWriter::writeInt8(int v) {
+ this->out->write(v & 0xFF);
+}
+
+void BinTreeNodeWriter::writeInt16(int v, ISocketConnection* o) {
+ o->write((v & 0xFF00) >> 8);
+ o->write((v & 0xFF) >> 0);
+}
+
+void BinTreeNodeWriter::writeInt16(int v) {
+ writeInt16(v, this->out);
+}
+
+void BinTreeNodeWriter::writeInt16(int v, ByteArrayOutputStream* o) {
+ o->write((v & 0xFF00) >> 8);
+ o->write((v & 0xFF) >> 0);
+}
+
+void BinTreeNodeWriter::writeAttributes(std::map<string, string>* attributes) {
+ if (attributes != NULL) {
+ std::map<string, string>::iterator ii;
+ for (ii = attributes->begin(); ii != attributes->end(); ii++) {
+ writeString(ii->first);
+ writeString(ii->second);
+ }
+ }
+}
+
+void BinTreeNodeWriter::writeString(const std::string& tag) {
+ std::map<string, int>::iterator it = this->tokenMap.find(tag);
+ if (it != this->tokenMap.end()) {
+ writeToken(it->second);
+ } else {
+ unsigned int atIndex = tag.find('@');
+ if (atIndex == 0 || atIndex == string::npos) {
+ writeBytes((unsigned char*) tag.data(), tag.length());
+ } else {
+ std::string server = tag.substr(atIndex + 1);
+ std::string user = tag.substr(0, atIndex);
+ writeJid(&user, server);
+ }
+ }
+}
+
+void BinTreeNodeWriter::writeJid(std::string* user, const std::string& server) {
+ this->out->write(250);
+ if (user != NULL && !user->empty()) {
+ writeString(*user);
+ } else {
+ writeToken(0);
+ }
+ writeString(server);
+
+}
+
+void BinTreeNodeWriter::writeToken(int intValue) {
+ if (intValue < 245)
+ this->out->write(intValue);
+ else if (intValue <= 500) {
+ this->out->write(254);
+ this->out->write(intValue - 245);
+ }
+}
+
+void BinTreeNodeWriter::writeBytes(unsigned char* bytes, int length) {
+ if (length >= 256) {
+ this->out->write(253);
+ writeInt24(length);
+ } else {
+ this->out->write(252);
+ writeInt8(length);
+ }
+ this->out->write(bytes, length);
+}
+
+void BinTreeNodeWriter::writeInt24(int v) {
+ this->out->write((v & 0xFF0000) >> 16);
+ this->out->write((v & 0xFF00) >> 8);
+ this->out->write(v & 0xFF);
+}
+
+void BinTreeNodeWriter::writeInternal(ProtocolTreeNode* node) {
+ writeListStart(
+ 1 + (node->attributes == NULL ? 0 : node->attributes->size() * 2)
+ + (node->children == NULL ? 0 : 1)
+ + (node->data == NULL ? 0 : 1));
+ writeString(node->tag);
+ writeAttributes(node->attributes);
+ if (node->data != NULL) {
+ writeBytes((unsigned char*) node->data->data(), node->data->size());
+ }
+ if (node->children != NULL && !node->children->empty()) {
+ writeListStart(node->children->size());
+ for (size_t a = 0; a < node->children->size(); a++) {
+ writeInternal((*node->children)[a]);
+ }
+ }
+}
+
+void BinTreeNodeWriter::flushBuffer(bool flushNetwork)
+{
+ this->flushBuffer(flushNetwork, this->dataBegin);
+}
+
+void BinTreeNodeWriter::flushBuffer(bool flushNetwork, int startingOffset) {
+ try {
+ this->processBuffer();
+ } catch (WAException& ex) {
+ this->out->setPosition(0);
+ this->out->setLength(0);
+ throw ex;
+ }
+
+ // _LOGDATA("buffer size %d, buffer position %d, dataBegin %d", this->out->getLength(), this->out->getPosition(), this->dataBegin);
+
+ std::vector<unsigned char> buffer(this->out->getBuffer()->begin(), this->out->getBuffer()->end());
+ int num = (int)(this->out->getLength() - (long)startingOffset);
+ if (flushNetwork && ((long)this->out->getCapacity() - this->out->getLength() < 3L || this->out->getLength() > 4096L))
+ {
+ delete this->out;
+ this->out = new ByteArrayOutputStream(4096);
+ }
+
+ if (flushNetwork)
+ this->realOut->write(buffer, startingOffset, num);
+}
+
+void BinTreeNodeWriter::streamEnd() {
+ this->mutex->lock();
+ try {
+ writeListStart(1);
+ this->out->write(2);
+ flushBuffer(true);
+ } catch (exception& ex) {
+ this->mutex->unlock();
+ throw ex;
+ }
+ this->mutex->unlock();
+}
+
+void BinTreeNodeWriter::write(ProtocolTreeNode* node) {
+ write(node, true);
+}
+
+void BinTreeNodeWriter::write(ProtocolTreeNode* node, bool needsFlush) {
+ this->mutex->lock();
+ try {
+ this->writeDummyHeader();
+ if (node == NULL)
+ this->out->write(0);
+ else {
+ writeInternal(node);
+ }
+ flushBuffer(needsFlush);
+ } catch (exception& ex) {
+ this->mutex->unlock();
+ throw WAException(ex.what());
+ }
+ this->mutex->unlock();
+}
+
+BinTreeNodeWriter::~BinTreeNodeWriter() {
+ if (this->out != NULL)
+ delete this->out;
+}
+
diff --git a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.h b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.h new file mode 100644 index 0000000000..20faaea4e8 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.h @@ -0,0 +1,71 @@ +/*
+ * BinTreeNodeWriter.h
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+
+
+#ifndef BINTREENODEWRITER_H_
+#define BINTREENODEWRITER_H_
+
+#include <string>
+#include "ProtocolTreeNode.h"
+#include "ISocketConnection.h"
+#include "ByteArray.h"
+#include "IMutex.h"
+#include "WAConnection.h"
+
+using namespace std;
+
+#define STREAM_START 1
+#define STREAM_END 2
+#define LIST_EMPTY 0
+#define LIST_8 248
+#define LIST_16 249
+#define JID_PAIR 250
+#define BINARY_8 252
+#define BINARY_24 253
+#define TOKEN_8 254
+
+#include <map>
+
+class WAConnection;
+
+class BinTreeNodeWriter {
+private:
+ WAConnection* conn;
+ map<string,int> tokenMap;
+ ISocketConnection *realOut;
+ ByteArrayOutputStream *out;
+ IMutex* mutex;
+ int dataBegin;
+
+ void writeListStart(int i);
+ void writeInt8(int v);
+ void writeInt16(int v, ISocketConnection* out);
+ void writeInt16(int v, ByteArrayOutputStream* out);
+ void writeInt16(int v);
+ void writeAttributes(std::map<string, string>* attributes);
+ void writeString(const std::string& tag);
+ void writeJid(std::string* user, const std::string& server);
+ void writeToken(int intValue);
+ void writeBytes(unsigned char* bytes, int length);
+ void writeInt24(int v);
+ void writeInternal(ProtocolTreeNode* node);
+ void writeDummyHeader();
+ void processBuffer();
+
+public:
+ BinTreeNodeWriter(WAConnection* conn, ISocketConnection* connection, const char** dictionary, const int dictionarysize, IMutex* mutex);
+ void streamStart(std::string domain, std::string resource);
+ void flushBuffer(bool flushNetwork);
+ void flushBuffer(bool flushNetwork, int startingOffset);
+ void streamEnd();
+ void write(ProtocolTreeNode* node);
+ void write(ProtocolTreeNode* node, bool needsFlush);
+
+ virtual ~BinTreeNodeWriter();
+};
+
+#endif /* BINTREENODEWRITER_H_ */
diff --git a/protocols/WhatsApp/src/WhatsAPI++/ByteArray.cpp b/protocols/WhatsApp/src/WhatsAPI++/ByteArray.cpp new file mode 100644 index 0000000000..44354a4e3d --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/ByteArray.cpp @@ -0,0 +1,155 @@ +/*
+ * ByteArray.cpp
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+#include "stdafx.h"
+#include "ByteArray.h"
+#include "WAException.h"
+#include <iostream>
+#include <algorithm>
+#include "utilities.h"
+
+ByteArrayOutputStream::ByteArrayOutputStream(int size) {
+ this->buf = new std::vector<unsigned char>();
+ this->buf->reserve(size);
+ this->position = 0;
+}
+
+void ByteArrayOutputStream::setLength(size_t length) {
+ this->buf->resize(length);
+}
+
+size_t ByteArrayOutputStream::getLength() {
+ return this->buf->size();
+}
+
+size_t ByteArrayOutputStream::getCapacity() {
+ return this->buf->capacity();
+}
+
+size_t ByteArrayOutputStream::getPosition() {
+ return this->position;
+}
+
+void ByteArrayOutputStream::setPosition(size_t count) {
+ this->position = count;
+}
+
+
+std::vector<unsigned char>* ByteArrayOutputStream::toByteArray() {
+ std::vector<unsigned char>* array = new std::vector<unsigned char>(this->buf->size());
+ for (size_t i = 0; i < this->buf->size(); i++)
+ (*array)[i] = (*this->buf)[i];
+ return array;
+}
+
+std::vector<unsigned char>* ByteArrayOutputStream::getBuffer() {
+ return this->buf;
+}
+
+void ByteArrayOutputStream::write(int i) {
+ if (this->position == this->buf->size())
+ this->buf->push_back((unsigned char) i);
+ else
+ (*this->buf)[this->position] = (unsigned char) i;
+ this->position = this->position + 1;
+}
+
+void ByteArrayOutputStream::write(unsigned char* b, size_t len) {
+ if (len == 0)
+ return;
+
+ for (size_t i = 0; i < len; i++)
+ write(b[i]);
+}
+
+void ByteArrayOutputStream::write(const std::string& s) {
+ for (size_t i = 0; i < s.size(); i++)
+ write((unsigned char) s[i]);
+}
+
+
+ByteArrayOutputStream::~ByteArrayOutputStream() {
+ delete this->buf;
+}
+
+
+ByteArrayInputStream::ByteArrayInputStream(std::vector<unsigned char>* buf, size_t off, size_t length ) {
+ this->buf = buf;
+ this->pos = off;
+ this->count = std::min(off + length, buf->size());
+}
+
+ByteArrayInputStream::ByteArrayInputStream(std::vector<unsigned char>* buf) {
+ this->buf = buf;
+ this->pos = 0;
+ this->count = buf->size();
+}
+
+int ByteArrayInputStream::read() {
+ return (pos < count) ? ((*this->buf)[pos++]) : -1;
+}
+
+int ByteArrayInputStream::read(std::vector<unsigned char>& b, size_t off, size_t len) {
+ if (len > (b.size() - off)) {
+ throw new WAException("Index out of bounds");
+ } else if (len == 0) {
+ return 0;
+ }
+
+ int c = read();
+ if (c == -1) {
+ return -1;
+ }
+ b[off] = (unsigned char) c;
+
+ size_t i = 1;
+ try {
+ for (; i < len ; i++) {
+ c = read();
+ if (c == -1) {
+ break;
+ }
+ b[off + i] = (unsigned char) c;
+ }
+ } catch (std::exception& ee) {
+ }
+ return i;
+}
+
+ByteArrayInputStream::~ByteArrayInputStream() {
+}
+
+void ByteArrayInputStream::print() {
+ std::cout << "[";
+ for (size_t i = 0; i < this->count; i++) {
+ std::cout << (*this->buf)[i] << " ";
+ }
+ std::cout << std::endl;
+ for (size_t i = 0; i < this->count; i++) {
+ std::cout << (int) ((signed char) (*this->buf)[i]) << " ";
+ }
+ std::cout << "]" << std::endl;
+}
+
+void ByteArrayOutputStream::print() {
+ _LOGDATA("[");
+
+ std::string chars(this->buf->begin(), this->buf->end());
+ _LOGDATA("%s ", chars.c_str());
+
+ std::string numbers = "";
+ for (size_t i = 0; i < this->buf->size(); i++) {
+ numbers += Utilities::intToStr((int) ((signed char) (*this->buf)[i])) + " ";
+ }
+ _LOGDATA("%s", numbers.c_str());
+ _LOGDATA("]");
+}
+
+
+
+
+
+
diff --git a/protocols/WhatsApp/src/WhatsAPI++/ByteArray.h b/protocols/WhatsApp/src/WhatsAPI++/ByteArray.h new file mode 100644 index 0000000000..57ac503020 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/ByteArray.h @@ -0,0 +1,55 @@ +/*
+ * ByteArray.h
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+
+
+
+#ifndef BYTEARRAY_H_
+#define BYTEARRAY_H_
+
+#include <vector>
+#include <string>
+
+class ByteArrayOutputStream {
+protected:
+ std::vector<unsigned char>* buf;
+ size_t position;
+
+public:
+ ByteArrayOutputStream(int size = 32);
+ std::vector<unsigned char>* toByteArray();
+ std::vector<unsigned char>* getBuffer();
+ size_t getPosition();
+ void setPosition(size_t count);
+ void write(int i);
+ void write(unsigned char* c, size_t length);
+ void write(const std::string& s);
+ void print();
+ void setLength(size_t length);
+ size_t getLength();
+ size_t getCapacity();
+
+ virtual ~ByteArrayOutputStream();
+};
+
+class ByteArrayInputStream {
+protected:
+ std::vector<unsigned char>* buf;
+ size_t pos;
+ size_t mark;
+ size_t count;
+
+public:
+ ByteArrayInputStream(std::vector<unsigned char>* buf, size_t off, size_t length );
+ ByteArrayInputStream(std::vector<unsigned char>* buf);
+ int read();
+ int read(std::vector<unsigned char>& b, size_t off, size_t length);
+ void print();
+
+ virtual ~ByteArrayInputStream();
+};
+
+#endif /* BYTEARRAY_H_ */
diff --git a/protocols/WhatsApp/src/WhatsAPI++/FMessage.cpp b/protocols/WhatsApp/src/WhatsAPI++/FMessage.cpp new file mode 100644 index 0000000000..e50d409ea9 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/FMessage.cpp @@ -0,0 +1,128 @@ +/*
+ * FMessage.cpp
+ *
+ * Created on: 02/07/2012
+ * Author: Antonio
+ */
+#include "stdafx.h"
+#include <ctime>
+#include <stdlib.h>
+#include <algorithm>
+#include "FMessage.h"
+#include "utilities.h"
+
+//SDL_mutex* FMessage::generating_lock = SDL_CreateMutex();
+IMutex* FMessage::generating_lock = NULL;
+int FMessage::generating_id = 0;
+std::string FMessage::generating_header = Utilities::intToStr(static_cast<int> (time(NULL))).append("-");
+
+
+FMessage::FMessage() {
+ this->key = NULL;
+ this->timestamp = 0;
+ this->media_wa_type = 0;
+ this->longitude = 0;
+ this->latitude = 0;
+ this->media_duration_seconds = 0;
+ this->media_size = 0;
+ this->media_name = "";
+ this->media_url = "";
+ this->data = "";
+}
+
+FMessage::FMessage(const std::string& remote_jid, bool from_me, const std::string& data) {
+ Key* local_key;
+ FMessage::generating_lock->lock();
+ FMessage::generating_id++;
+ local_key = new Key(remote_jid, from_me, generating_header + Utilities::intToStr(generating_id));
+ this->key = local_key;
+ FMessage::generating_lock->unlock();
+ this->data = data;
+ this->timestamp = time(NULL);
+ this->media_wa_type = 0;
+ this->longitude = 0;
+ this->latitude = 0;
+ this->media_duration_seconds = 0;
+ this->media_size = 0;
+ this->media_name = "";
+ this->media_url = "";
+}
+
+std::string FMessage::nextKeyIdNumber() {
+ int id = 0;
+ FMessage::generating_lock->lock();
+ id = (FMessage::generating_id++);
+ FMessage::generating_lock->unlock();
+ return generating_header + (Utilities::intToStr(id));
+}
+
+FMessage::FMessage(Key* key) {
+ this->key = key;
+ this->timestamp = 0;
+ this->media_wa_type = 0;
+ this->longitude = 0;
+ this->latitude = 0;
+ this->media_duration_seconds = 0;
+ this->media_size = 0;
+ this->media_name = "";
+ this->media_url = "";
+}
+
+std::string FMessage::getMessage_WA_Type_StrValue(unsigned char type) {
+ switch (type) {
+ case FMessage::WA_TYPE_UNDEFINED:
+ return "";
+ case FMessage::WA_TYPE_SYSTEM:
+ return "system";
+ case FMessage::WA_TYPE_AUDIO:
+ return "audio";
+ case FMessage::WA_TYPE_CONTACT:
+ return "vcard";
+ case FMessage::WA_TYPE_IMAGE:
+ return "image";
+ case FMessage::WA_TYPE_LOCATION:
+ return "location";
+ case FMessage::WA_TYPE_VIDEO:
+ return "video";
+ }
+
+ return "";
+}
+
+FMessage::~FMessage() {
+ if (this->key != NULL)
+ delete key;
+}
+
+Key::Key(const std::string& remote_jid, bool from_me, const std::string& id) {
+ this->remote_jid = remote_jid;
+ this->from_me = from_me;
+ this->id = id;
+}
+
+std::string Key::toString() {
+ return "Key[id=" + id + ", from_me=" + (from_me ? "true":"false") + ", remote_jid=" + remote_jid + "]";
+}
+
+
+unsigned char FMessage::getMessage_WA_Type(std::string* type) {
+ if ((type == NULL) || (type->length() == 0))
+ return WA_TYPE_UNDEFINED;
+ std::string typeLower = *type;
+ std::transform(typeLower.begin(), typeLower.end(), typeLower.begin(), ::tolower);
+ if (typeLower.compare("system") == 0)
+ return WA_TYPE_SYSTEM;
+ if (typeLower.compare("image") == 0)
+ return WA_TYPE_IMAGE;
+ if (typeLower.compare("audio") == 0)
+ return WA_TYPE_AUDIO;
+ if (typeLower.compare("video") == 0)
+ return WA_TYPE_VIDEO;
+ if (typeLower.compare("vcard") == 0)
+ return WA_TYPE_CONTACT;
+ if (typeLower.compare("location") == 0)
+ return WA_TYPE_LOCATION;
+
+ return WA_TYPE_UNDEFINED;
+}
+
diff --git a/protocols/WhatsApp/src/WhatsAPI++/FMessage.h b/protocols/WhatsApp/src/WhatsAPI++/FMessage.h new file mode 100644 index 0000000000..63ae64451a --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/FMessage.h @@ -0,0 +1,88 @@ +/*
+ * FMessage.h
+ *
+ * Created on: 02/07/2012
+ * Author: Antonio
+ */
+
+
+
+#ifndef FMESSAGE_H_
+#define FMESSAGE_H_
+
+#include <string>
+//#include <SDL.h>
+#include <time.h>
+#include "IMutex.h"
+
+class Key {
+public:
+ std::string remote_jid;
+ bool from_me;
+ std::string id;
+
+ Key(const std::string& remote_jid, bool from_me, const std::string& id);
+ std::string toString();
+
+};
+
+class FMessage {
+private:
+ static int generating_id;
+ static std::string generating_header;
+
+
+public:
+ static IMutex* generating_lock; // #WORKAROUND
+
+ Key* key;
+ unsigned char media_wa_type;
+ std::string data;
+ long long timestamp;
+ std::string remote_resource;
+ bool wants_receipt;
+ unsigned char status;
+ std::string notifyname;
+ bool offline;
+ std::string media_url;
+ std::string media_name;
+ long long media_size;
+ int media_duration_seconds;
+ double latitude;
+ double longitude;
+
+ static const unsigned char WA_TYPE_UNDEFINED = 0;
+ static const unsigned char WA_TYPE_IMAGE = 1;
+ static const unsigned char WA_TYPE_AUDIO = 2;
+ static const unsigned char WA_TYPE_VIDEO = 3;
+ static const unsigned char WA_TYPE_CONTACT = 4;
+ static const unsigned char WA_TYPE_LOCATION = 5;
+ static const unsigned char WA_TYPE_SYSTEM = 7;
+
+ static const int STATUS_UNSENT = 0;
+ static const int STATUS_UPLOADING = 1;
+ static const int STATUS_UPLOADED = 2;
+ static const int STATUS_SENT_BY_CLIENT = 3;
+ static const int STATUS_RECEIVED_BY_SERVER = 4;
+ static const int STATUS_RECEIVED_BY_TARGET = 5;
+ static const int STATUS_NEVER_SEND = 6;
+ static const int STATUS_SERVER_BOUNCE = 7;
+
+ static const int STATUS_USER_ADDED = 191;
+ static const int STATUS_USER_REMOVED = 192;
+ static const int STATUS_SUBJECT_CHANGED = 193;
+ static const int STATUS_PICTURE_CHANGED_SET = 194;
+ static const int STATUS_PICTURE_CHANGED_DELETE = 195;
+
+
+ static std::string getMessage_WA_Type_StrValue(unsigned char type);
+ static std::string nextKeyIdNumber();
+ static unsigned char getMessage_WA_Type(std::string* typeString);
+
+ FMessage();
+ FMessage(const std::string& remote_jid, bool from_me = true, const std::string& data = "");
+ FMessage(Key* key);
+ virtual ~FMessage();
+};
+
+#endif /* FMESSAGE_H_ */
diff --git a/protocols/WhatsApp/src/WhatsAPI++/IMutex.h b/protocols/WhatsApp/src/WhatsAPI++/IMutex.h new file mode 100644 index 0000000000..56a5492ac9 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/IMutex.h @@ -0,0 +1,14 @@ +#if !defined(IMUTEX_H)
+#define IMUTEX_H
+
+class IMutex
+{
+public:
+ IMutex() {}
+ virtual ~IMutex() {}
+
+ virtual void lock() = 0;
+ virtual void unlock() = 0;
+};
+
+#endif
\ No newline at end of file diff --git a/protocols/WhatsApp/src/WhatsAPI++/ISocketConnection.h b/protocols/WhatsApp/src/WhatsAPI++/ISocketConnection.h new file mode 100644 index 0000000000..a2d82efc64 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/ISocketConnection.h @@ -0,0 +1,25 @@ +#ifndef ISOCKETCONNECTION_H_
+#define ISOCKETCONNECTION_H_
+
+#include <vector>
+
+class ISocketConnection {
+
+public:
+ ISocketConnection() {}
+ virtual void write(int i) = 0;
+ virtual unsigned char read() = 0;
+ virtual void flush() = 0;
+ virtual void write(const std::vector<unsigned char>& b, int length) = 0;
+ virtual void write(const std::vector<unsigned char>& bytes, int offset, int length) = 0;
+ virtual int read(std::vector<unsigned char>& b, int off, int length) = 0;
+ virtual void makeNonBlock() = 0;
+ virtual int waitForRead() = 0;
+ virtual void forceShutdown() = 0;
+
+ virtual ~ISocketConnection() {}
+ //static void initNetwork();
+ //static void quitNetwork();
+};
+
+#endif /* ISOCKETCONNECTION_H_ */
diff --git a/protocols/WhatsApp/src/WhatsAPI++/LICENSE b/protocols/WhatsApp/src/WhatsAPI++/LICENSE new file mode 100644 index 0000000000..cccee9ef01 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/protocols/WhatsApp/src/WhatsAPI++/ProtocolTreeNode.cpp b/protocols/WhatsApp/src/WhatsAPI++/ProtocolTreeNode.cpp new file mode 100644 index 0000000000..0ed60edbb8 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/ProtocolTreeNode.cpp @@ -0,0 +1,135 @@ +/*
+ * ProtocolTreeNode.cpp
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+#include "stdafx.h"
+#include "WAException.h"
+#include "ProtocolTreeNode.h"
+
+ProtocolTreeNode::ProtocolTreeNode(const string& tag, map<string, string> *attributes, vector<unsigned char>* data, vector<ProtocolTreeNode*> *children) {
+ this->tag = tag;
+ this->data = data;
+ this->attributes = attributes;
+ this->children = children;
+}
+
+ProtocolTreeNode::ProtocolTreeNode(const string& tag, map<string, string> *attributes, ProtocolTreeNode* child) {
+ this->tag = tag;
+ this->data = NULL;
+ this->attributes = attributes;
+ this->children = new std::vector<ProtocolTreeNode*>(1);
+ (*this->children)[0] = child;
+}
+
+ProtocolTreeNode::~ProtocolTreeNode() {
+ if (this->attributes != NULL)
+ delete this->attributes;
+ if (this->children != NULL) {
+ for (size_t i = 0; i < this->children->size(); i++)
+ if (this->children->at(i) != NULL)
+ delete this->children->at(i);
+ delete this->children;
+ }
+ if (this->data != NULL)
+ delete data;
+}
+
+
+string ProtocolTreeNode::toString() {
+ string out;
+ out += "<" + this->tag;
+ if (this->attributes != NULL) {
+ map<string,string>::iterator ii;
+ for (ii = attributes->begin(); ii != attributes->end(); ii++)
+ out += "" + ii->first + "=\"" + ii->second + "\"";
+ }
+ out += ">\n";
+ std::string* data = getDataAsString();
+ out += (this->data != NULL? *data:"");
+ delete data;
+
+ if (this->children != NULL) {
+ vector<ProtocolTreeNode*>::iterator ii;
+
+ for (ii = children->begin(); ii != children->end(); ii++)
+ out += (*ii)->toString();
+ }
+
+ out += "</" + this->tag + ">\n";
+
+ return out;
+}
+
+ProtocolTreeNode* ProtocolTreeNode::getChild(const string& id) {
+ if (this->children == NULL || this->children->size() == 0)
+ return NULL;
+
+ for (std::size_t i = 0; i < this->children->size(); i++)
+ if (id.compare((*children)[i]->tag) == 0)
+ return (*children)[i];
+
+ return NULL;
+}
+
+ProtocolTreeNode* ProtocolTreeNode::getChild(size_t id) {
+ if (this->children == NULL || this->children->size() == 0)
+ return NULL;
+
+ if (children->size() > id)
+ return (*children)[id];
+
+ return NULL;
+}
+
+string* ProtocolTreeNode::getAttributeValue(const string& attribute) {
+ if (this->attributes == NULL)
+ return NULL;
+
+ map<string,string>::iterator it = attributes->find(attribute);
+ if (it == attributes->end())
+ return NULL;
+
+ return &it->second;
+}
+
+vector<ProtocolTreeNode*>* ProtocolTreeNode::getAllChildren() {
+ vector<ProtocolTreeNode*>* ret = new vector<ProtocolTreeNode*>();
+
+ if (this->children == NULL)
+ return ret;
+
+ return this->children;
+}
+
+std::string* ProtocolTreeNode::getDataAsString() {
+ if (this->data == NULL)
+ return NULL;
+ return new std::string(this->data->begin(), this->data->end());
+}
+
+vector<ProtocolTreeNode*>* ProtocolTreeNode::getAllChildren(const string& tag) {
+ vector<ProtocolTreeNode*>* ret = new vector<ProtocolTreeNode*>();
+
+ if (this->children == NULL)
+ return ret;
+
+ for (size_t i = 0; i < this->children->size(); i++)
+ if (tag.compare((*children)[i]->tag) == 0)
+ ret->push_back((*children)[i]);
+
+ return ret;
+}
+
+
+bool ProtocolTreeNode::tagEquals(ProtocolTreeNode *node, const string& tag) {
+ return (node != NULL && node->tag.compare(tag) == 0);
+}
+
+void ProtocolTreeNode::require(ProtocolTreeNode *node, const string& tag) {
+ if (!tagEquals(node, tag))
+ throw WAException("failed require. node:" + node->toString() + "tag: " + tag, WAException::CORRUPT_STREAM_EX, 0);
+}
+
+
diff --git a/protocols/WhatsApp/src/WhatsAPI++/ProtocolTreeNode.h b/protocols/WhatsApp/src/WhatsAPI++/ProtocolTreeNode.h new file mode 100644 index 0000000000..3156c8b7e7 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/ProtocolTreeNode.h @@ -0,0 +1,41 @@ +/*
+* ProtocolTreeNode.h
+*
+* Created on: 26/06/2012
+* Author: Antonio
+*/
+
+#if !defined(PROTOCOLNODE_H)
+#define PROTOCOLNODE_H
+
+#include <string>
+#include <vector>
+#include <map>
+
+using namespace std;
+
+class ProtocolTreeNode {
+public:
+ vector<unsigned char>* data;
+ string tag;
+ map<string, string> *attributes;
+ vector<ProtocolTreeNode*> *children;
+
+ ProtocolTreeNode(const string& tag, map<string, string> *attributes, ProtocolTreeNode* child);
+ ProtocolTreeNode(const string& tag, map<string, string> *attributes, vector<unsigned char>* data = NULL, vector<ProtocolTreeNode*> *children = NULL);
+ string toString();
+ ProtocolTreeNode* getChild(const string& id);
+ ProtocolTreeNode* getChild(size_t id);
+ string* getAttributeValue(const string& attribute);
+
+ vector<ProtocolTreeNode*>* getAllChildren();
+ vector<ProtocolTreeNode*>* getAllChildren(const string& tag);
+ std::string* getDataAsString();
+
+ static bool tagEquals(ProtocolTreeNode *node, const string& tag);
+ static void require(ProtocolTreeNode *node, const string& tag);
+
+ virtual ~ProtocolTreeNode();
+};
+
+#endif /* PROTOCOLNODE_H_ */
\ No newline at end of file diff --git a/protocols/WhatsApp/src/WhatsAPI++/README.md b/protocols/WhatsApp/src/WhatsAPI++/README.md new file mode 100644 index 0000000000..69e374a6ef --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/README.md @@ -0,0 +1,30 @@ +This library can be used for creating WhatsApp-clients and is based on the MojoWhatsup-project written by Antonio Morales. The original source code has been modified by Uli Hecht.
+
+-------------------------------------------------------------------------------------------------
+ORIGINAL "README.MD" BELOW
+-------------------------------------------------------------------------------------------------
+
+MojoWhatsup
+===========
+
+MojoWhatsup is an IM application for Webos that allows you to chat with your Whatsapp friends
+
+MojoWhatsup has been developed as a Webos hybrid app, i.e. a javascript webos app (based in Mojo framework)
+and a plugin (mojowhatsup_service_plugin) in C++. The plugin supports all Whatsapp communication API.
+
+Contact: Antonio Morales <amoralico@gmail.com>
+
+License:
+
+ Copyright (c) 2012, Antonio Morales <amoralico@gmail.com>
+
+ MojoWhatsup is free software: you can redistribute it and/or modify it under the terms
+ of the GNU General Public License as published by the Free Software Foundation,
+ either version 2 of the License, or (at your option) any later version.
+
+ MojoWhatsup is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with MojoWhatsup.
+ If not, see http://www.gnu.org/licenses/.
diff --git a/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp new file mode 100644 index 0000000000..da5837f682 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp @@ -0,0 +1,1684 @@ +/*
+ * WAConnection.cpp
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+#include "stdafx.h"
+#include "WAConnection.h"
+#include "ProtocolTreeNode.h"
+#include <map>
+#include <vector>
+#include "utilities.h"
+#include "base64.h"
+
+const char* WAConnection::dictionary[] = {
+ "",
+ "",
+ "",
+ "",
+ "",
+ "account",
+ "ack",
+ "action",
+ "active",
+ "add",
+ "after",
+ "ib",
+ "all",
+ "allow",
+ "apple",
+ "audio",
+ "auth",
+ "author",
+ "available",
+ "bad-protocol",
+ "bad-request",
+ "before",
+ "Bell.caf",
+ "body",
+ "Boing.caf",
+ "cancel",
+ "category",
+ "challenge",
+ "chat",
+ "clean",
+ "code",
+ "composing",
+ "config",
+ "conflict",
+ "contacts",
+ "count",
+ "create",
+ "creation",
+ "default",
+ "delay",
+ "delete",
+ "delivered",
+ "deny",
+ "digest",
+ "DIGEST-MD5-1",
+ "DIGEST-MD5-2",
+ "dirty",
+ "elapsed",
+ "broadcast",
+ "enable",
+ "encoding",
+ "duplicate",
+ "error",
+ "event",
+ "expiration",
+ "expired",
+ "fail",
+ "failure",
+ "false",
+ "favorites",
+ "feature",
+ "features",
+ "field",
+ "first",
+ "free",
+ "from",
+ "g.us",
+ "get",
+ "Glass.caf",
+ "google",
+ "group",
+ "groups",
+ "g_notify",
+ "g_sound",
+ "Harp.caf",
+ "http://etherx.jabber.org/streams",
+ "http://jabber.org/protocol/chatstates",
+ "id",
+ "image",
+ "img",
+ "inactive",
+ "index",
+ "internal-server-error",
+ "invalid-mechanism",
+ "ip",
+ "iq",
+ "item",
+ "item-not-found",
+ "user-not-found",
+ "jabber:iq:last",
+ "jabber:iq:privacy",
+ "jabber:x:delay",
+ "jabber:x:event",
+ "jid",
+ "jid-malformed",
+ "kind",
+ "last",
+ "latitude",
+ "lc",
+ "leave",
+ "leave-all",
+ "lg",
+ "list",
+ "location",
+ "longitude",
+ "max",
+ "max_groups",
+ "max_participants",
+ "max_subject",
+ "mechanism",
+ "media",
+ "message",
+ "message_acks",
+ "method",
+ "microsoft",
+ "missing",
+ "modify",
+ "mute",
+ "name",
+ "nokia",
+ "none",
+ "not-acceptable",
+ "not-allowed",
+ "not-authorized",
+ "notification",
+ "notify",
+ "off",
+ "offline",
+ "order",
+ "owner",
+ "owning",
+ "paid",
+ "participant",
+ "participants",
+ "participating",
+ "password",
+ "paused",
+ "picture",
+ "pin",
+ "ping",
+ "platform",
+ "pop_mean_time",
+ "pop_plus_minus",
+ "port",
+ "presence",
+ "preview",
+ "probe",
+ "proceed",
+ "prop",
+ "props",
+ "p_o",
+ "p_t",
+ "query",
+ "raw",
+ "reason",
+ "receipt",
+ "receipt_acks",
+ "received",
+ "registration",
+ "relay",
+ "remote-server-timeout",
+ "remove",
+ "Replaced by new connection",
+ "request",
+ "required",
+ "resource",
+ "resource-constraint",
+ "response",
+ "result",
+ "retry",
+ "rim",
+ "s.whatsapp.net",
+ "s.us",
+ "seconds",
+ "server",
+ "server-error",
+ "service-unavailable",
+ "set",
+ "show",
+ "sid",
+ "silent",
+ "sound",
+ "stamp",
+ "unsubscribe",
+ "stat",
+ "status",
+ "stream:error",
+ "stream:features",
+ "subject",
+ "subscribe",
+ "success",
+ "sync",
+ "system-shutdown",
+ "s_o",
+ "s_t",
+ "t",
+ "text",
+ "timeout",
+ "TimePassing.caf",
+ "timestamp",
+ "to",
+ "Tri-tone.caf",
+ "true",
+ "type",
+ "unavailable",
+ "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:delay",
+ "urn:xmpp:ping",
+ "urn:xmpp:receipts",
+ "urn:xmpp:whatsapp",
+ "urn:xmpp:whatsapp:account",
+ "urn:xmpp:whatsapp:dirty",
+ "urn:xmpp:whatsapp:mms",
+ "urn:xmpp:whatsapp:push",
+ "user",
+ "username",
+ "value",
+ "vcard",
+ "version",
+ "video",
+ "w",
+ "w:g",
+ "w:p",
+ "w:p:r",
+ "w:profile:picture",
+ "wait",
+ "x",
+ "xml-not-well-formed",
+ "xmlns",
+ "xmlns:stream",
+ "Xylophone.caf",
+ "1",
+ "WAUTH-1"
+};
+
+
+//const char* WAConnection::dictionary[] = {
+// "",
+// "",
+// "",
+// "",
+// "",
+// "1",
+// "1.0",
+// "ack",
+// "action",
+// "active",
+// "add",
+// "all",
+// "allow",
+// "apple",
+// "audio",
+// "auth",
+// "author",
+// "available",
+// "bad-request",
+// "base64",
+// "Bell.caf",
+// "bind",
+// "body",
+// "Boing.caf",
+// "cancel",
+// "category",
+// "challenge",
+// "chat",
+// "clean",
+// "code",
+// "composing",
+// "config",
+// "conflict",
+// "contacts",
+// "create",
+// "creation",
+// "default",
+// "delay",
+// "delete",
+// "delivered",
+// "deny",
+// "DIGEST-MD5",
+// "DIGEST-MD5-1",
+// "dirty",
+// "en",
+// "enable",
+// "encoding",
+// "error",
+// "expiration",
+// "expired",
+// "failure",
+// "false",
+// "favorites",
+// "feature",
+// "field",
+// "free",
+// "from",
+// "g.us",
+// "get",
+// "Glass.caf",
+// "google",
+// "group",
+// "groups",
+// "g_sound",
+// "Harp.caf",
+// "http://etherx.jabber.org/streams",
+// "http://jabber.org/protocol/chatstates",
+// "id",
+// "image",
+// "img",
+// "inactive",
+// "internal-server-error",
+// "iq",
+// "item",
+// "item-not-found",
+// "jabber:client",
+// "jabber:iq:last",
+// "jabber:iq:privacy",
+// "jabber:x:delay",
+// "jabber:x:event",
+// "jid",
+// "jid-malformed",
+// "kind",
+// "leave",
+// "leave-all",
+// "list",
+// "location",
+// "max_groups",
+// "max_participants",
+// "max_subject",
+// "mechanism",
+// "mechanisms",
+// "media",
+// "message",
+// "message_acks",
+// "missing",
+// "modify",
+// "name",
+// "not-acceptable",
+// "not-allowed",
+// "not-authorized",
+// "notify",
+// "Offline Storage",
+// "order",
+// "owner",
+// "owning",
+// "paid",
+// "participant",
+// "participants",
+// "participating",
+// "particpants",
+// "paused",
+// "picture",
+// "ping",
+// "PLAIN",
+// "platform",
+// "presence",
+// "preview",
+// "probe",
+// "prop",
+// "props",
+// "p_o",
+// "p_t",
+// "query",
+// "raw",
+// "receipt",
+// "receipt_acks",
+// "received",
+// "relay",
+// "remove",
+// "Replaced by new connection",
+// "request",
+// "resource",
+// "resource-constraint",
+// "response",
+// "result",
+// "retry",
+// "rim",
+// "s.whatsapp.net",
+// "seconds",
+// "server",
+// "session",
+// "set",
+// "show",
+// "sid",
+// "sound",
+// "stamp",
+// "starttls",
+// "status",
+// "stream:error",
+// "stream:features",
+// "subject",
+// "subscribe",
+// "success",
+// "system-shutdown",
+// "s_o",
+// "s_t",
+// "t",
+// "TimePassing.caf",
+// "timestamp",
+// "to",
+// "Tri-tone.caf",
+// "type",
+// "unavailable",
+// "uri",
+// "url",
+// "urn:ietf:params:xml:ns:xmpp-bind",
+// "urn:ietf:params:xml:ns:xmpp-sasl",
+// "urn:ietf:params:xml:ns:xmpp-session",
+// "urn:ietf:params:xml:ns:xmpp-stanzas",
+// "urn:ietf:params:xml:ns:xmpp-streams",
+// "urn:xmpp:delay",
+// "urn:xmpp:ping",
+// "urn:xmpp:receipts",
+// "urn:xmpp:whatsapp",
+// "urn:xmpp:whatsapp:dirty",
+// "urn:xmpp:whatsapp:mms",
+// "urn:xmpp:whatsapp:push",
+// "value",
+// "vcard",
+// "version",
+// "video",
+// "w",
+// "w:g",
+// "w:p:r",
+// "wait",
+// "x",
+// "xml-not-well-formed",
+// "xml:lang",
+// "xmlns",
+// "xmlns:stream",
+// "Xylophone.caf",
+// "account",
+// "digest",
+// "g_notify",
+// "method",
+// "password",
+// "registration",
+// "stat",
+// "text",
+// "user",
+// "username",
+// "event",
+// "latitude",
+// "longitude",
+// "true",
+// "after",
+// "before",
+// "broadcast",
+// "count",
+// "features",
+// "first",
+// "index",
+// "invalid-mechanism",
+// "l$dict",
+// "max",
+// "offline",
+// "proceed",
+// "required",
+// "sync",
+// "elapsed",
+// "ip",
+// "microsoft",
+// "mute",
+// "nokia",
+// "off",
+// "pin",
+// "pop_mean_time",
+// "pop_plus_minus",
+// "port",
+// "reason",
+// "server-error",
+// "silent",
+// "timeout",
+// "lc",
+// "lg",
+// "bad-protocol",
+// "none",
+// "remote-server-timeout",
+// "service-unavailable",
+// "w:p",
+// "w:profile:picture",
+// "notification",
+// "",
+// "",
+// "",
+// "",
+// "",
+// "XXX"
+//};
+
+WAConnection::WAConnection(IMutex* mutex, WAListener* event_handler, WAGroupListener* group_event_handler) {
+ this->init(event_handler, group_event_handler, mutex);
+}
+
+void WAConnection::init(WAListener* event_handler, WAGroupListener* group_event_handler, IMutex* mutex) {
+ this->login = NULL;
+ this->event_handler = event_handler;
+ this->group_event_handler = group_event_handler;
+ this->inputKey = NULL;
+ this->outputKey = NULL;
+ this->in = NULL;
+ this->out = NULL;
+
+ this->msg_id = 0;
+ this->state = 0; // 0 disconnected 1 connecting 2 connected
+ this->retry = true;
+
+ this->iqid = 0;
+ this->verbose = true;
+ this->lastTreeRead = 0;
+ this->expire_date = 0L;
+ this->account_kind = -1;
+ this->mutex = mutex;
+}
+
+void WAConnection::setLogin(WALogin* login) {
+ this->login = login;
+
+ if (login->expire_date != 0L) {
+ this->expire_date = login->expire_date;
+ }
+ if (login->account_kind != -1) {
+ this->account_kind = login->account_kind;
+ }
+
+ this->jid = this->login->user + "@" + this->login->domain;
+ this->fromm = this->login->user + "@" + this->login->domain + "/" + this->login->resource;
+
+ this->in = login->inn;
+ this->out = login->out;
+}
+
+WALogin* WAConnection::getLogin() {
+ return this->login;
+}
+
+void WAConnection::sendMessageWithMedia(FMessage* message) throw (WAException) {
+ _LOGDATA("Send message with media %s %d", message->media_name.c_str(), message->media_size);
+ _LOGDATA("media-url:%s", message->media_url.c_str());
+ if (message->media_wa_type == FMessage::WA_TYPE_SYSTEM)
+ throw new WAException("Cannot send system message over the network");
+ std::map<string, string>* attribs = new std::map<string, string>();
+ (*attribs)["xmlns"] = "urn:xmpp:whatsapp:mms";
+ (*attribs)["type"] = FMessage::getMessage_WA_Type_StrValue(message->media_wa_type);
+
+ if (message->media_wa_type == FMessage::WA_TYPE_LOCATION) {
+ (*attribs)["latitude"] = Utilities::doubleToStr(message->latitude);
+ (*attribs)["longitude"] = Utilities::doubleToStr(message->longitude);;
+ } else {
+ if (message->media_wa_type != FMessage::WA_TYPE_CONTACT && !message->media_name.empty() && !message->media_url.empty() && message->media_size > 0L) {
+ (*attribs)["file"] = message->media_name;
+ (*attribs)["size"] = Utilities::intToStr(message->media_size);
+ (*attribs)["url"] = message->media_url;
+ } else {
+ (*attribs)["file"] = message->media_name;
+ (*attribs)["size"] = Utilities::intToStr(message->media_size);
+ (*attribs)["url"] = message->media_url;
+ (*attribs)["seconds"] = Utilities::intToStr(message->media_duration_seconds);
+ }
+ }
+
+ ProtocolTreeNode* mediaNode;
+ if (message->media_wa_type == FMessage::WA_TYPE_CONTACT && !message->media_name.empty()) {
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["name"] = message->media_name;
+ ProtocolTreeNode* vcardNode = new ProtocolTreeNode("vcard", attribs2, new std::vector<unsigned char>(message->data.begin(), message->data.end()));
+ mediaNode = new ProtocolTreeNode("media", attribs, vcardNode);
+ } else {
+ (*attribs)["encoding"] = "text";
+ mediaNode = new ProtocolTreeNode("media", attribs, new std::vector<unsigned char>(message->data.begin(), message->data.end()), NULL);
+ }
+
+ ProtocolTreeNode* root = WAConnection::getMessageNode(message, mediaNode);
+ this->out->write(root);
+ delete root;
+}
+
+void WAConnection::sendMessageWithBody(FMessage* message) throw (WAException) {
+ ProtocolTreeNode* bodyNode = new ProtocolTreeNode("body", NULL, new std::vector<unsigned char>(message->data.begin(), message->data.end()));
+ ProtocolTreeNode* root = WAConnection::getMessageNode(message, bodyNode);
+ this->out->write(root);
+ delete root;
+}
+
+ProtocolTreeNode* WAConnection::getMessageNode(FMessage* message, ProtocolTreeNode* child) {
+ ProtocolTreeNode* requestNode = NULL;
+ ProtocolTreeNode* serverNode = new ProtocolTreeNode("server", NULL);
+ std::map<string, string>* attrib = new std::map<string, string>();
+ (*attrib)["xmlns"] = "jabber:x:event";
+ std::vector<ProtocolTreeNode*>* children = new std::vector<ProtocolTreeNode*>(1);
+ (*children)[0] = serverNode;
+ ProtocolTreeNode* xNode = new ProtocolTreeNode("x", attrib, NULL, children);
+ int childCount = (requestNode == NULL? 0 : 1) + 2;
+ std::vector<ProtocolTreeNode*>* messageChildren = new std::vector<ProtocolTreeNode*>(childCount);
+ int i = 0;
+ if (requestNode != NULL) {
+ (*messageChildren)[i] = requestNode;
+ i++;
+ }
+ (*messageChildren)[i] = xNode;
+ i++;
+ (*messageChildren)[i] = child;
+ i++;
+
+ std::map<string, string>* attrib2 = new std::map<string, string>();
+ (*attrib2)["to"] = message->key->remote_jid;
+ (*attrib2)["type"] = "chat";
+ (*attrib2)["id"] = message->key->id;
+
+ return new ProtocolTreeNode("message", attrib2, NULL, messageChildren);
+}
+
+void WAConnection::sendMessage(FMessage* message) throw(WAException) {
+ if (message->media_wa_type != 0)
+ sendMessageWithMedia(message);
+ else
+ sendMessageWithBody(message);
+}
+
+
+WAConnection::~WAConnection() {
+ if (this->inputKey != NULL)
+ delete this->inputKey;
+ if (this->outputKey != NULL)
+ delete this->outputKey;
+ std::map<string, IqResultHandler*>::iterator it;
+ for (it = this->pending_server_requests.begin(); it != this->pending_server_requests.end(); it++) {
+ delete it->second;
+ }
+}
+
+
+void WAConnection::setVerboseId(bool b) {
+ this->verbose = b;
+}
+
+void WAConnection::sendAvailableForChat() throw(WAException) {
+ std::map<string, string>* attribs = new std::map<string, string>();
+ (*attribs)["name"] = this->login->push_name;
+ ProtocolTreeNode *presenceNode = new ProtocolTreeNode("presence", attribs);
+ this->out->write(presenceNode);
+ delete presenceNode;
+}
+
+bool WAConnection::read() throw(WAException) {
+ ProtocolTreeNode* node;
+ try {
+ node = this->in->nextTree();
+ this->lastTreeRead = time(NULL);
+ } catch (exception& ex) {
+ throw WAException(ex.what(), WAException::CORRUPT_STREAM_EX, 0);
+ }
+
+ if (node == NULL) {
+ return false;
+ }
+
+ if (ProtocolTreeNode::tagEquals(node, "iq")) {
+ std::string* type = node->getAttributeValue("type");
+ std::string* id = node->getAttributeValue("id");
+ std::string* from = node->getAttributeValue("from");
+ std::string f;
+ if (from == NULL)
+ f = "";
+ else
+ f = *from;
+
+ if (type == NULL)
+ throw WAException("missing 'type' attribute in iq stanza", WAException::CORRUPT_STREAM_EX, 0);
+
+ if (type->compare("result") == 0) {
+ if (id == NULL)
+ throw WAException("missing 'id' attribute in iq stanza", WAException::CORRUPT_STREAM_EX, 0);
+
+ std::map<string, IqResultHandler*>::iterator it = this->pending_server_requests.find(*id);
+ if (it!= this->pending_server_requests.end()) {
+ it->second->parse(node, f);
+ delete it->second;
+ this->pending_server_requests.erase(*id);
+ } else if (id->compare(0, this->login->user.size(), this->login->user) == 0) {
+ ProtocolTreeNode* accountNode = node->getChild(0);
+ ProtocolTreeNode::require(accountNode, "account");
+ std::string* kind = accountNode->getAttributeValue("kind");
+ if ((kind != NULL) && (kind->compare("paid") == 0)) {
+ this->account_kind = 1;
+ } else if ((kind != NULL) && (kind->compare("free") == 0)) {
+ this->account_kind = 0;
+ } else
+ this->account_kind = -1;
+ std::string* expiration = accountNode->getAttributeValue("expiration");
+ if (expiration == NULL) {
+ throw WAException("no expiration");
+ }
+ this->expire_date = atol(expiration->c_str());
+ if (this->expire_date == 0)
+ throw WAException("invalid expire date: " + *expiration);
+ if (this->event_handler != NULL)
+ this->event_handler->onAccountChange(this->account_kind, this->expire_date);
+ }
+ } else if (type->compare("error") == 0) {
+ std::map<string, IqResultHandler*>::iterator it = this->pending_server_requests.find(*id);
+ if (it!= this->pending_server_requests.end()) {
+ it->second->error(node);
+ delete it->second;
+ this->pending_server_requests.erase(*id);
+ }
+ } else if (type->compare("get") == 0) {
+ ProtocolTreeNode* childNode = node->getChild(0);
+ if (ProtocolTreeNode::tagEquals(childNode, "ping")) {
+ if (this->event_handler != NULL)
+ this->event_handler->onPing(*id);
+ } else if (ProtocolTreeNode::tagEquals(childNode, "query") && (from != NULL)? false : // (childNode->getAttributeValue("xmlns") != NULL) && ((*childNode->getAttributeValue("xmlns")).compare("http://jabber.org/protocol/disco#info") == 0) :
+ (ProtocolTreeNode::tagEquals(childNode, "relay")) && (from != NULL)) {
+ std::string* pin = childNode->getAttributeValue("pin");
+ std::string* timeoutString = childNode->getAttributeValue("timeout");
+ int timeoutSeconds;
+ timeoutSeconds = timeoutString == NULL? 0 : atoi(timeoutString->c_str());
+ if (pin != NULL)
+ if (this->event_handler != NULL)
+ this->event_handler->onRelayRequest(*pin, timeoutSeconds, *id);
+ }
+ } else if (type->compare("set") == 0) {
+ ProtocolTreeNode* childNode = node->getChild(0);
+ if (ProtocolTreeNode::tagEquals(childNode, "query")) {
+ std::string* xmlns = childNode->getAttributeValue("xmlns");
+ if ((xmlns != NULL) && (xmlns->compare("jabber:iq:roster") == 0)) {
+ std::vector<ProtocolTreeNode*>* itemNodes = childNode->getAllChildren("item");
+ std::string ask = "";
+ for (size_t i = 0; i < itemNodes->size(); i++) {
+ ProtocolTreeNode* itemNode = (*itemNodes)[i];
+ std::string* jid = itemNode->getAttributeValue("jid");
+ std::string* subscription = itemNode->getAttributeValue("subscription");
+ ask = *itemNode->getAttributeValue("ask");
+ }
+ delete itemNodes;
+ }
+ }
+ } else
+ throw WAException("unknown iq type attribute: " + *type, WAException::CORRUPT_STREAM_EX, 0);
+ } else if (ProtocolTreeNode::tagEquals(node, "presence")) {
+ std::string* xmlns = node->getAttributeValue("xmlns");
+ std::string* from = node->getAttributeValue("from");
+ if (((xmlns == NULL) || (xmlns->compare("urn:xmpp") == 0)) && (from != NULL)) {
+ std::string* type = node->getAttributeValue("type");
+ if ((type != NULL) && (type->compare("unavailable") == 0)) {
+ if (this->event_handler != NULL)
+ this->event_handler->onAvailable(*from, false);
+ } else if ((type == NULL) || (type->compare("available") == 0)) {
+ if (this->event_handler != NULL)
+ this->event_handler->onAvailable(*from, true);
+ }
+ } else if ((xmlns->compare("w") == 0) && (from != NULL)) {
+ std::string* add = node->getAttributeValue("add");
+ std::string* remove = node->getAttributeValue("remove");
+ std::string* status = node->getAttributeValue("status");
+ if (add != NULL) {
+ if (this->group_event_handler != NULL)
+ this->group_event_handler->onGroupAddUser(*from, *add);
+ } else if (remove != NULL) {
+ if (this->group_event_handler != NULL)
+ this->group_event_handler->onGroupRemoveUser(*from, *remove);
+ } else if ((status != NULL) && (status->compare("dirty") == 0)) {
+ std::map<string, string>* categories = parseCategories(node);
+ if (this->event_handler != NULL)
+ this->event_handler->onDirty(*categories);
+ delete categories;
+ }
+ }
+ } else if (ProtocolTreeNode::tagEquals(node, "message")) {
+ parseMessageInitialTagAlreadyChecked(node);
+ }
+
+ delete node;
+ return true;
+}
+
+void WAConnection::sendNop() throw(WAException) {
+ this->out->write(NULL);
+}
+
+void WAConnection::sendPing() throw(WAException) {
+ std::string id = makeId("ping_");
+ this->pending_server_requests[id] = new IqResultPingHandler(this);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:p";
+ ProtocolTreeNode* pingNode = new ProtocolTreeNode("ping", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "get";
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, pingNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendPong(const std::string& id) throw(WAException) {
+ std::map<string, string>* attribs = new std::map<string, string>();
+ (*attribs)["type"] = "result";
+ (*attribs)["to"] = this->login->domain;
+ (*attribs)["id"] = id;
+ ProtocolTreeNode *iqNode = new ProtocolTreeNode("iq", attribs);
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendComposing(const std::string& to) throw(WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "http://jabber.org/protocol/chatstates";
+ ProtocolTreeNode* composingNode = new ProtocolTreeNode("composing", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["to"] = to;
+ (*attribs2)["type"] = "chat";
+ ProtocolTreeNode* messageNode = new ProtocolTreeNode("message", attribs2, composingNode);
+
+ this->out->write(messageNode);
+
+ delete messageNode;
+}
+
+
+void WAConnection::sendActive() throw(WAException) {
+ std::map<string, string>* attribs = new std::map<string, string>();
+ (*attribs)["type"] = "active";
+ ProtocolTreeNode* presenceNode = new ProtocolTreeNode("presence", attribs);
+
+ this->out->write(presenceNode);
+
+ delete presenceNode;
+}
+
+void WAConnection::sendInactive() throw(WAException) {
+ std::map<string, string>* attribs = new std::map<string, string>();
+ (*attribs)["type"] = "inactive";
+ ProtocolTreeNode* presenceNode = new ProtocolTreeNode("presence", attribs);
+
+ this->out->write(presenceNode);
+
+ delete presenceNode;
+}
+
+void WAConnection::sendPaused(const std::string& to) throw(WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "http://jabber.org/protocol/chatstates";
+ ProtocolTreeNode* pausedNode = new ProtocolTreeNode("paused", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["to"] = to;
+ (*attribs2)["type"] = "chat";
+ ProtocolTreeNode* messageNode = new ProtocolTreeNode("message", attribs2, pausedNode);
+
+ this->out->write(messageNode);
+
+ delete messageNode;
+}
+
+void WAConnection::sendSubjectReceived(const std::string& to, const std::string& id)throw(WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "urn:xmpp:receipts";
+ ProtocolTreeNode* receivedNode = new ProtocolTreeNode("received", attribs1);
+
+ ProtocolTreeNode* messageNode = getSubjectMessage(to, id, receivedNode);
+
+ this->out->write(messageNode);
+
+ delete messageNode;
+}
+
+ProtocolTreeNode* WAConnection::getSubjectMessage(const std::string& to, const std::string& id, ProtocolTreeNode* child) throw (WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["to"] = to;
+ (*attribs1)["type"] = "subject";
+ (*attribs1)["id"] = id;
+ ProtocolTreeNode* messageNode = new ProtocolTreeNode("message", attribs1, child);
+
+ return messageNode;
+}
+
+void WAConnection::sendMessageReceived(FMessage* message) throw(WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "urn:xmpp:receipts";
+ ProtocolTreeNode* receivedNode = new ProtocolTreeNode("received", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["to"] = message->key->remote_jid;
+ (*attribs2)["type"] = "chat";
+ (*attribs2)["id"] = message->key->id;
+
+ ProtocolTreeNode* messageNode = new ProtocolTreeNode("message", attribs2, receivedNode);
+
+ this->out->write(messageNode);
+ delete messageNode;
+}
+
+void WAConnection::sendDeliveredReceiptAck(const std::string& to,
+ const std::string& id) throw(WAException) {
+ ProtocolTreeNode *root = getReceiptAck(to, id, "delivered");
+ this->out->write(root);
+ delete root;
+}
+
+void WAConnection::sendVisibleReceiptAck(const std::string& to, const std::string& id) throw (WAException) {
+ ProtocolTreeNode *root = getReceiptAck(to, id, "visible");
+ this->out->write(root);
+ delete root;
+}
+
+void WAConnection::sendPresenceSubscriptionRequest(const std::string& to) throw(WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["type"] = "subscribe";
+ (*attribs1)["to"] = to;
+ ProtocolTreeNode* presenceNode = new ProtocolTreeNode("presence", attribs1);
+ this->out->write(presenceNode);
+ delete presenceNode;
+}
+
+void WAConnection::sendClientConfig(const std::string& sound, const std::string& pushID, bool preview, const std::string& platform) throw(WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] ="urn:xmpp:whatsapp:push";
+ (*attribs1)["sound"] =sound;
+ (*attribs1)["id"] = pushID;
+ (*attribs1)["preview"] = preview ? "1" : "0";
+ (*attribs1)["platform"] = platform;
+ ProtocolTreeNode* configNode = new ProtocolTreeNode("config", attribs1);
+
+ std::string id = makeId("config_");
+
+ this->pending_server_requests[id] = new IqSendClientConfigHandler(this);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "set";
+ (*attribs2)["to"] = this->login->domain;
+
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, configNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+
+}
+
+void WAConnection::sendClientConfig(const std::string& pushID, bool preview, const std::string& platform, bool defaultSettings, bool groupSettings, const std::vector<GroupSetting>& groups) throw(WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] ="urn:xmpp:whatsapp:push";
+ (*attribs1)["id"] = pushID;
+ (*attribs1)["lg"] = "en";
+ (*attribs1)["lc"] = "US";
+ (*attribs1)["clear"] = "0";
+ (*attribs1)["preview"] = preview ? "1" : "0";
+ (*attribs1)["platform"] = platform;
+ (*attribs1)["default"] = defaultSettings? "1": "0";
+ (*attribs1)["groups"] = groupSettings? "1" : "0";
+ ProtocolTreeNode* configNode = new ProtocolTreeNode("config", attribs1, NULL, this->processGroupSettings(groups));
+ std::string id = makeId("config_");
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "set";
+ (*attribs2)["to"] = this->login->domain;
+
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, configNode);
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+std::vector<ProtocolTreeNode*>* WAConnection::processGroupSettings(const std::vector<GroupSetting>& groups) {
+ std::vector<ProtocolTreeNode*>* result = new std::vector<ProtocolTreeNode*>(groups.size());
+ if (!groups.empty()) {
+ time_t now = time(NULL);
+ for (int i = 0; i < groups.size(); i++) {
+ std::map<string, string>* attribs = new std::map<string, string>();
+ (*attribs)["jid"] = groups[i].jid;
+ (*attribs)["notify"] = (groups[i].enabled? "1": "0");
+ (*attribs)["mute"] = Utilities::intToStr(groups[i].muteExpiry > now? (groups[i].muteExpiry - now): 0);
+ _LOGDATA("mute group %s, %s", (*attribs)["jid"].c_str(), (*attribs)["mute"].c_str());
+
+ (*result)[i] = new ProtocolTreeNode("item", attribs);
+ }
+ }
+
+ return result;
+}
+
+std::string WAConnection::makeId(const std::string& prefix) {
+ this->iqid++;
+ std::string id;
+ if (this->verbose)
+ id = prefix + Utilities::intToStr(this->iqid);
+ else
+ id = Utilities::itoa(this->iqid, 16);
+
+ return id;
+}
+
+
+ProtocolTreeNode* WAConnection::getReceiptAck(const std::string& to, const std::string& id, const std::string& receiptType) throw(WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "urn:xmpp:receipts";
+ (*attribs1)["type"] = receiptType;
+ ProtocolTreeNode* ackNode = new ProtocolTreeNode("ack", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["to"] = to;
+ (*attribs2)["type"] = "chat";
+ (*attribs2)["id"] = id;
+ ProtocolTreeNode* messageNode = new ProtocolTreeNode("message", attribs2, ackNode);
+
+ return messageNode;
+}
+
+std::map<string, string>* WAConnection::parseCategories(ProtocolTreeNode* dirtyNode) throw (WAException) {
+ std::map<string, string>* categories = new std::map<string,string>();
+ if (dirtyNode->children != NULL) {
+ for (size_t i = 0; i < dirtyNode->children->size(); i++) {
+ ProtocolTreeNode* childNode = (*dirtyNode->children)[i];
+ if (ProtocolTreeNode::tagEquals(childNode, "category")) {
+ std::string* categoryName = childNode->getAttributeValue("name");
+ std::string* timestamp = childNode->getAttributeValue("timestamp");
+ (*categories)[*categoryName] = *timestamp;
+ }
+ }
+ }
+
+ return categories;
+}
+
+void WAConnection::parseMessageInitialTagAlreadyChecked(ProtocolTreeNode* messageNode) throw (WAException){
+ std::string* id = messageNode->getAttributeValue("id");
+ std::string* attribute_t = messageNode->getAttributeValue("t");
+ std::string* from = messageNode->getAttributeValue("from");
+ std::string* authoraux = messageNode->getAttributeValue("author");
+ std::string author = "";
+
+ if (authoraux != NULL)
+ author = *authoraux;
+
+ std::string* typeAttribute = messageNode->getAttributeValue("type");
+ if (typeAttribute != NULL) {
+ if (typeAttribute->compare("error") == 0) {
+ int errorCode = 0;
+ std::vector<ProtocolTreeNode*>* errorNodes = messageNode->getAllChildren("error");
+ for (size_t i = 0; i < errorNodes->size(); i++) {
+ ProtocolTreeNode *errorNode = (*errorNodes)[i];
+ std::string* codeString = errorNode->getAttributeValue("code");
+ errorCode = atoi(codeString->c_str());
+ }
+
+ Key* key = new Key(*from, true, *id);
+ FMessage* message = new FMessage(key);
+ message->status = FMessage::STATUS_SERVER_BOUNCE;
+
+ if (this->event_handler != NULL)
+ this->event_handler->onMessageError(message, errorCode);
+ delete errorNodes;
+ delete message;
+ } else if (typeAttribute->compare("subject") == 0) {
+ bool receiptRequested = false;
+ std::vector<ProtocolTreeNode*>* requestNodes = messageNode->getAllChildren("request");
+ for (size_t i = 0; i < requestNodes->size(); i++) {
+ ProtocolTreeNode *requestNode = (*requestNodes)[i];
+ if ((requestNode->getAttributeValue("xmlns") != NULL) && (*requestNode->getAttributeValue("xmlns")).compare("urn:xmpp:receipts") == 0)
+ receiptRequested = true;
+ }
+ delete requestNodes;
+
+ ProtocolTreeNode* bodyNode = messageNode->getChild("body");
+ std::string* newSubject = bodyNode == NULL? NULL : bodyNode->getDataAsString();
+ if ((newSubject != NULL) && (this->group_event_handler != NULL))
+ this->group_event_handler->onGroupNewSubject(*from, author, *newSubject, atoi(attribute_t->c_str()));
+ if (newSubject != NULL)
+ delete newSubject;
+ if (receiptRequested)
+ sendSubjectReceived(*from, *id);
+ } else if (typeAttribute->compare("chat") == 0) {
+ FMessage* fmessage = new FMessage();
+ fmessage->wants_receipt = false;
+ bool duplicate = false;
+ std::vector<ProtocolTreeNode*> myVector(0);
+ std::vector<ProtocolTreeNode*>* messageChildren = messageNode->children == NULL? &myVector: messageNode->getAllChildren();
+ for (size_t i = 0; i < messageChildren->size(); i++) {
+ ProtocolTreeNode* childNode = (*messageChildren)[i];
+ if (ProtocolTreeNode::tagEquals(childNode, "composing")) {
+ if (this->event_handler != NULL)
+ this->event_handler->onIsTyping(*from, true);
+ } else if (ProtocolTreeNode::tagEquals(childNode, "paused")) {
+ if (this->event_handler != NULL)
+ this->event_handler->onIsTyping(*from, false);
+ } else if (ProtocolTreeNode::tagEquals(childNode, "body")) {
+ std::string* message = childNode->getDataAsString();
+ Key* key = new Key(*from, false, *id);
+ fmessage->key = key;
+ fmessage->remote_resource = author;
+ fmessage->data = *message;
+ fmessage->status = FMessage::STATUS_UNSENT;
+ if (message != NULL)
+ delete message;
+ } else if (ProtocolTreeNode::tagEquals(childNode, "media") && (id != NULL)) {
+ fmessage->media_wa_type = FMessage::getMessage_WA_Type(childNode->getAttributeValue("type"));
+ fmessage->media_url = (childNode->getAttributeValue("url") == NULL? "": *childNode->getAttributeValue("url"));
+ fmessage->media_name = (childNode->getAttributeValue("file") == NULL? "": *childNode->getAttributeValue("file"));
+
+ if (childNode->getAttributeValue("size") != NULL)
+ fmessage->media_size = Utilities::parseLongLong(*childNode->getAttributeValue("size"));
+ else
+ fmessage->media_size = 0;
+
+ if (childNode->getAttributeValue("seconds") != NULL)
+ fmessage->media_duration_seconds = atoi(childNode->getAttributeValue("seconds")->c_str());
+ else
+ fmessage->media_duration_seconds = 0;
+
+ if (fmessage->media_wa_type == FMessage::WA_TYPE_LOCATION) {
+ std::string* latitudeString = childNode->getAttributeValue("latitude");
+ std::string* longitudeString = childNode->getAttributeValue("longitude");
+ if (latitudeString == NULL || longitudeString == NULL)
+ throw WAException("location message missing lat or long attribute", WAException::CORRUPT_STREAM_EX, 0);
+
+ double latitude = atof(latitudeString->c_str());
+ double longitude = atof(longitudeString->c_str());
+ fmessage->latitude = latitude;
+ fmessage->longitude = longitude;
+ }
+
+ if (fmessage->media_wa_type == FMessage::WA_TYPE_CONTACT) {
+ ProtocolTreeNode* contactChildNode = childNode->getChild(0);
+ if (contactChildNode != NULL) {
+ fmessage->media_name = (contactChildNode->getAttributeValue("name") == NULL? "": *contactChildNode->getAttributeValue("name"));
+ std::string* data = contactChildNode->getDataAsString();
+ fmessage->data = (data == NULL? "": *data);
+ if (data != NULL)
+ delete data;
+ }
+ } else {
+ std::string* encoding = childNode->getAttributeValue("encoding");
+ std::string* data;
+ if ((encoding == NULL) || ((encoding != NULL) && (encoding->compare("text") == 0))) {
+ data = childNode->getDataAsString();
+ } else {
+ _LOGDATA("Media data encoding type '%s'", (encoding == NULL? "text":encoding->c_str()));
+ data = (childNode->data == NULL? NULL : new std::string(base64_encode(childNode->data->data(), childNode->data->size())));
+ }
+ fmessage->data = (data == NULL? "": *data);
+ if (data != NULL)
+ delete data;
+ }
+
+ Key* key = new Key(*from, false, *id);
+ fmessage->key = key;
+ fmessage->remote_resource = author;
+ } else if (!ProtocolTreeNode::tagEquals(childNode, "active")) {
+ if (ProtocolTreeNode::tagEquals(childNode, "request")) {
+ fmessage->wants_receipt = true;
+ } else if (ProtocolTreeNode::tagEquals(childNode, "notify")) {
+ fmessage->notifyname = (childNode->getAttributeValue("name") == NULL)? "": *childNode->getAttributeValue("name");
+ } else if (ProtocolTreeNode::tagEquals(childNode, "x")) {
+ std::string* xmlns = childNode->getAttributeValue("xmlns");
+ if ((xmlns != NULL) && (xmlns->compare("jabber:x:event") == 0) && (id != NULL)) {
+ Key* key = new Key(*from, true, *id);
+ FMessage* message = new FMessage(key);
+ message->status = FMessage::STATUS_RECEIVED_BY_SERVER;
+ if (this->event_handler != NULL)
+ this->event_handler->onMessageStatusUpdate(message);
+ delete message;
+ }
+// else if ((xmlns != NULL) && xmlns->compare("jabber:x:delay") == 0) {
+// std::string* stamp_str = childNode->getAttributeValue("stamp");
+// if (stamp_str != NULL) {
+// time_t stamp = Utilities::parseBBDate(*stamp_str);
+// if (stamp != 0) {
+// fmessage->timestamp = (long long) stamp;
+// fmessage->offline = true;
+// }
+// }
+// }
+ } else if (ProtocolTreeNode::tagEquals(childNode, "received")) {
+ Key* key = new Key(*from, true, *id);
+ FMessage* message = new FMessage(key);
+ message->status = FMessage::STATUS_RECEIVED_BY_TARGET;
+ if (this->event_handler != NULL)
+ this->event_handler->onMessageStatusUpdate(message);
+ delete message;
+ if (this->supportsReceiptAcks()) {
+ std::string* receipt_type = childNode->getAttributeValue("type");
+ if ((receipt_type == NULL) || (receipt_type->compare("delivered") == 0))
+ sendDeliveredReceiptAck(*from, *id);
+ else if (receipt_type->compare("visible") == 0)
+ sendVisibleReceiptAck(*from, *id);
+ }
+ } else if (ProtocolTreeNode::tagEquals(childNode, "offline")) {
+ if (attribute_t != NULL) {
+ fmessage->timestamp = atoi(attribute_t->c_str());
+ }
+ fmessage->offline = true;
+ }
+ }
+ }
+
+ if (fmessage->timestamp == 0) {
+ fmessage->timestamp = time(NULL);
+ fmessage->offline = false;
+ }
+
+ if (fmessage->key != NULL && this->event_handler != NULL)
+ this->event_handler->onMessageForMe(fmessage, duplicate);
+
+ delete fmessage;
+ } else if (typeAttribute->compare("notification") == 0) {
+ _LOGDATA("Notification node %s", messageNode->toString().c_str());
+ bool flag = false;
+ std::vector<ProtocolTreeNode*> myVector(0);
+ std::vector<ProtocolTreeNode*>* children = messageNode->children == NULL? &myVector: messageNode->getAllChildren();
+ for (int i = 0; i < children->size(); i++) {
+ ProtocolTreeNode* child = (*children)[i];
+ if (ProtocolTreeNode::tagEquals(child, "notification")) {
+ std::string* type = child->getAttributeValue("type");
+ if ((type != NULL) && (type->compare("picture") == 0) && (this->event_handler != NULL)) {
+ std::vector<ProtocolTreeNode*> myVector2(0);
+ std::vector<ProtocolTreeNode*>* children2 = child->children == NULL? &myVector2: child->getAllChildren();
+ for (int j = 0; j < children2->size(); j++) {
+ ProtocolTreeNode* child2 = (*children2)[j];
+ if (ProtocolTreeNode::tagEquals(child2, "set")) {
+ std::string* id = child2->getAttributeValue("id");
+ std::string* author = child2->getAttributeValue("author");
+ if (id != NULL) {
+ this->event_handler->onPictureChanged(*from, ((author == NULL)?"":*author), true);
+ }
+ } else if (ProtocolTreeNode::tagEquals(child2, "delete")) {
+ std::string* author = child2->getAttributeValue("author");
+ this->event_handler->onPictureChanged(*from, ((author == NULL)?"":*author), false);
+ }
+ }
+ }
+ } else if (ProtocolTreeNode::tagEquals(child, "request")) {
+ flag = true;
+ }
+ }
+ if (flag) {
+ this->sendNotificationReceived(*from, *id);
+ }
+ }
+ }
+}
+
+bool WAConnection::supportsReceiptAcks() {
+ return (this->login != NULL) && (this->login->supports_receipt_acks);
+}
+
+void WAConnection::sendNotificationReceived(const std::string& jid, const std::string& id) throw(WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "urn:xmpp:receipts";
+ ProtocolTreeNode* child = new ProtocolTreeNode("received", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "notification";
+ (*attribs2)["to"] = jid;
+ ProtocolTreeNode* node = new ProtocolTreeNode("message", attribs2, child);
+
+ this->out->write(node);
+ delete node;
+}
+
+void WAConnection::sendClose() throw(WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["type"] = "unavailable";
+ ProtocolTreeNode* presenceNode = new ProtocolTreeNode("presence", attribs1);
+ this->out->write(presenceNode);
+ delete presenceNode;
+ this->out->streamEnd();
+}
+
+void WAConnection::sendGetPrivacyList() throw (WAException) {
+ std::string id = makeId("privacylist_");
+ this->pending_server_requests[id] = new IqResultPrivayListHandler(this);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["name"] = "default";
+ ProtocolTreeNode* listNode = new ProtocolTreeNode("list", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["xmlns"] = "jabber:iq:privacy";
+ ProtocolTreeNode* queryNode = new ProtocolTreeNode("query", attribs2, listNode);
+
+ std::map<string, string>* attribs3 = new std::map<string, string>();
+ (*attribs3)["id"] = id;
+ (*attribs3)["type"] = "get";
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs3, queryNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendGetServerProperties() throw (WAException) {
+ std::string id = makeId("get_server_properties_");
+ this->pending_server_requests[id] = new IqResultServerPropertiesHandler(this);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:g";
+ (*attribs1)["type"] = "props";
+ ProtocolTreeNode* listNode = new ProtocolTreeNode("list", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "get";
+ (*attribs2)["to"] = "g.us";
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, listNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendGetGroups() throw (WAException) {
+ this->mutex->lock();
+ std::string id = makeId("get_groups_");
+ this->pending_server_requests[id] = new IqResultGetGroupsHandler(this, "participating");
+
+ sendGetGroups(id, "participating");
+ this->mutex->unlock();
+}
+
+void WAConnection::sendGetOwningGroups() throw (WAException) {
+ this->mutex->lock();
+ std::string id = makeId("get_owning_groups_");
+ this->pending_server_requests[id] = new IqResultGetGroupsHandler(this, "owning");
+
+ sendGetGroups(id, "owning");
+ this->mutex->unlock();
+}
+
+void WAConnection::sendGetGroups(const std::string& id, const std::string& type) throw (WAException) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:g";
+ (*attribs1)["type"] = type;
+ ProtocolTreeNode* listNode = new ProtocolTreeNode("list", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "get";
+ (*attribs2)["to"] = "g.us";
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, listNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::readGroupList(ProtocolTreeNode* node, std::vector<std::string>& groups) throw (WAException) {
+ std::vector<ProtocolTreeNode*>* nodes = node->getAllChildren("group");
+ for (size_t i = 0; i < nodes->size(); i++) {
+ ProtocolTreeNode* groupNode = (*nodes)[i];
+ std::string* gid = groupNode->getAttributeValue("id");
+ std::string gjid = gidToGjid(*gid);
+ std::string* owner = groupNode->getAttributeValue("owner");
+ std::string* subject = groupNode->getAttributeValue("subject");
+ std::string* subject_t = groupNode->getAttributeValue("s_t");
+ std::string* subject_owner = groupNode->getAttributeValue("s_o");
+ std::string* creation = groupNode->getAttributeValue("creation");
+ if (this->group_event_handler != NULL)
+ this->group_event_handler->onGroupInfoFromList(gjid, *owner, *subject, *subject_owner, atoi(subject_t->c_str()), atoi(creation->c_str()));
+ groups.push_back(gjid);
+ }
+ delete nodes;
+}
+
+std::string WAConnection::gidToGjid(const std::string& gid) {
+ return gid + "@g.us";
+}
+
+
+void WAConnection::sendQueryLastOnline(const std::string& jid) throw (WAException) {
+ std::string id = makeId("last_");
+ this->pending_server_requests[id] = new IqResultQueryLastOnlineHandler(this);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "jabber:iq:last";
+ ProtocolTreeNode* queryNode = new ProtocolTreeNode("query", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "get";
+ (*attribs2)["to"] = jid;
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, queryNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendGetGroupInfo(const std::string& gjid) throw (WAException) {
+ std::string id = makeId("get_g_info_");
+ this->pending_server_requests[id] = new IqResultGetGroupInfoHandler(this);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:g";
+ ProtocolTreeNode* queryNode = new ProtocolTreeNode("query", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "get";
+ (*attribs2)["to"] = gjid;
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, queryNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendGetParticipants(const std::string& gjid) throw (WAException) {
+ std::string id = makeId("get_participants_");
+ this->pending_server_requests[id] = new IqResultGetGroupParticipantsHandler(this);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:g";
+ ProtocolTreeNode* listNode = new ProtocolTreeNode("list", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "get";
+ (*attribs2)["to"] = gjid;
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, listNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::readAttributeList(ProtocolTreeNode* node, std::vector<std::string>& vector, const std::string& tag, const std::string& attribute) throw (WAException) {
+ std::vector<ProtocolTreeNode*>* nodes = node->getAllChildren(tag);
+ for (size_t i = 0; i < nodes->size(); i++) {
+ ProtocolTreeNode* tagNode = (*nodes)[i];
+ std::string* value = tagNode->getAttributeValue(attribute);
+ vector.push_back(*value);
+ }
+ delete nodes;
+}
+
+void WAConnection::sendCreateGroupChat(const std::string& subject) throw (WAException){
+ _LOGDATA("sending create group: %s", subject.c_str());
+ std::string id = makeId("create_group_");
+ this->pending_server_requests[id] = new IqResultCreateGroupChatHandler(this);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:g";
+ (*attribs1)["action"] = "create";
+ (*attribs1)["subject"] = subject;
+ ProtocolTreeNode* groupNode = new ProtocolTreeNode("group", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "set";
+ (*attribs2)["to"] = "g.us";
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, groupNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendEndGroupChat(const std::string& gjid) throw (WAException){
+ std::string id = makeId("remove_group_");
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:g";
+ (*attribs1)["action"] = "delete";
+ ProtocolTreeNode* groupNode = new ProtocolTreeNode("group", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "set";
+ (*attribs2)["to"] = gjid ;
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, groupNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendClearDirty(const std::string& category) throw (WAException) {
+ std::string id = makeId("clean_dirty_");
+ this->pending_server_requests[id] = new IqResultClearDirtyHandler(this);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["name"] = category;
+ ProtocolTreeNode* categoryNode = new ProtocolTreeNode("category", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["xmlns"] = "urn:xmpp:whatsapp:dirty";
+ ProtocolTreeNode* cleanNode = new ProtocolTreeNode("clean", attribs2, categoryNode);
+
+ std::map<string, string>* attribs3 = new std::map<string, string>();
+ (*attribs3)["id"] = id;
+ (*attribs3)["type"] = "set";
+ (*attribs3)["to"] = "s.whatsapp.net";
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs3, cleanNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendLeaveGroup(const std::string& gjid) throw (WAException) {
+ std::string id = makeId("leave_group_");
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["id"] = gjid;
+ ProtocolTreeNode* groupNode = new ProtocolTreeNode("group", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["xmlns"] = "w:g";
+ ProtocolTreeNode* leaveNode = new ProtocolTreeNode("leave", attribs2, groupNode);
+
+ std::map<string, string>* attribs3 = new std::map<string, string>();
+ (*attribs3)["id"] = id;
+ (*attribs3)["type"] = "set";
+ (*attribs3)["to"] = "g.us";
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs3, leaveNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendAddParticipants(const std::string& gjid, const std::vector<std::string>& participants) throw (WAException) {
+ std::string id = makeId("add_group_participants_");
+ this->sendVerbParticipants(gjid, participants, id, "add");
+}
+
+void WAConnection::sendRemoveParticipants(const std::string& gjid, const std::vector<std::string>& participants) throw (WAException) {
+ std::string id = makeId("remove_group_participants_");
+ this->sendVerbParticipants(gjid, participants, id, "remove");
+}
+
+void WAConnection::sendVerbParticipants(const std::string& gjid, const std::vector<std::string>& participants, const std::string& id, const std::string& inner_tag) throw (WAException) {
+ size_t size = participants.size();
+ std::vector<ProtocolTreeNode*>* children = new std::vector<ProtocolTreeNode*>(size);
+ for (int i = 0; i < size; i++) {
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["jid"] = participants[i];
+ (*children)[i] = new ProtocolTreeNode("participant", attribs1);
+ }
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["xmlns"] = "w:g";
+ ProtocolTreeNode* innerNode = new ProtocolTreeNode(inner_tag, attribs2, NULL, children);
+
+ std::map<string, string>* attribs3 = new std::map<string, string>();
+ (*attribs3)["id"] = id;
+ (*attribs3)["type"] = "set";
+ (*attribs3)["to"] = gjid;
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs3, innerNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendSetNewSubject(const std::string& gjid, const std::string& subject) throw (WAException) {
+ std::string id = this->makeId("set_group_subject_");
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:g";
+ (*attribs1)["value"] = subject;
+ ProtocolTreeNode* subjectNode = new ProtocolTreeNode("subject", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "set";
+ (*attribs2)["to"] = gjid;
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, subjectNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+std::string WAConnection::removeResourceFromJid(const std::string& jid) {
+ size_t slashidx = jid.find('/');
+ if (slashidx == std::string::npos)
+ return jid;
+
+ return jid.substr(0, slashidx + 1);
+}
+
+void WAConnection::sendStatusUpdate(std::string& status) throw (WAException) {
+ std::string id = this->makeId(Utilities::intToStr(time(NULL)));
+ FMessage* message = new FMessage(new Key("s.us", true, id));
+ ProtocolTreeNode* body = new ProtocolTreeNode("body", NULL, new std::vector<unsigned char>(status.begin(), status.end()), NULL);
+ ProtocolTreeNode* messageNode = getMessageNode(message, body);
+ this->out->write(messageNode);
+ delete messageNode;
+ delete message;
+}
+
+
+
+void WAConnection::sendSetPicture(const std::string& jid, std::vector<unsigned char>* data) throw (WAException) {
+ std::string id = this->makeId("set_photo_");
+ this->pending_server_requests[id] = new IqResultSetPhotoHandler(this, jid);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:profile:picture";
+ // (*attribs1)["type"] = "image";
+ ProtocolTreeNode* listNode = new ProtocolTreeNode("picture", attribs1, data, NULL);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "set";
+ (*attribs2)["to"] = jid;
+
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, listNode);
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendGetPicture(const std::string& jid, const std::string& type, const std::string& oldId, const std::string& newId) throw (WAException) {
+ std::string id = makeId("get_picture_");
+ this->pending_server_requests[id] = new IqResultGetPhotoHandler(this, jid, oldId, newId);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:profile:picture";
+ (*attribs1)["type"] = type;
+ ProtocolTreeNode* listNode = new ProtocolTreeNode("picture", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["to"] = jid;
+ (*attribs2)["type"] = "get";
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, listNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendGetPictureIds(const std::vector<std::string>& jids) throw (WAException) {
+ std::string id = makeId("get_picture_ids_");
+ this->pending_server_requests[id] = new IqResultGetPictureIdsHandler(this);
+
+ std::vector<ProtocolTreeNode*>* children = new std::vector<ProtocolTreeNode*>();
+ for (int i = 0; i < jids.size(); i++) {
+ std::map<string, string>* attribs = new std::map<string, string>();
+ (*attribs)["jid"] = jids[i];
+ ProtocolTreeNode* child = new ProtocolTreeNode("user", attribs);
+ children->push_back(child);
+ }
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "w:profile:picture";
+ ProtocolTreeNode* queryNode = new ProtocolTreeNode("list", attribs1, NULL, children);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "get";
+ ProtocolTreeNode* iqNode = new ProtocolTreeNode("iq", attribs2, queryNode);
+
+ this->out->write(iqNode);
+ delete iqNode;
+}
+
+void WAConnection::sendDeleteAccount() throw (WAException) {
+ std::string id = makeId("del_acct_");
+ this->pending_server_requests[id] = new IqResultSendDeleteAccount(this);
+
+ std::map<string, string>* attribs1 = new std::map<string, string>();
+ (*attribs1)["xmlns"] = "urn:xmpp:whatsapp:account";
+ ProtocolTreeNode* node1 = new ProtocolTreeNode("remove", attribs1);
+
+ std::map<string, string>* attribs2 = new std::map<string, string>();
+ (*attribs2)["id"] = id;
+ (*attribs2)["type"] = "get";
+ (*attribs2)["to"] = "s.whatsapp.net";
+
+ ProtocolTreeNode* node2 = new ProtocolTreeNode("iq", attribs2, node1);
+ this->out->write(node2);
+ delete node2;
+}
diff --git a/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h new file mode 100644 index 0000000000..b692924f46 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h @@ -0,0 +1,464 @@ +/*
+ * WAConnection.h
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+
+
+
+
+#ifndef WACONNECTION_H_
+#define WACONNECTION_H_
+
+#include <string>
+#include <time.h>
+#include <map>
+#include "WAException.h"
+#include "FMessage.h"
+#include "WALogin.h"
+#include "utilities.h"
+#include "BinTreeNodeReader.h"
+#include "BinTreeNodeWriter.h"
+
+class WALogin;
+class KeyStream;
+class BinTreeNodeReader;
+
+class WAListener {
+public:
+ virtual void onMessageForMe(FMessage* paramFMessage, bool paramBoolean) throw (WAException)=0;
+ virtual void onMessageStatusUpdate(FMessage* paramFMessage)=0;
+ virtual void onMessageError(FMessage* message, int paramInt)=0;
+ virtual void onPing(const std::string& paramString) throw (WAException)=0;
+ virtual void onPingResponseReceived()=0;
+ virtual void onAvailable(const std::string& paramString, bool paramBoolean)=0;
+ virtual void onClientConfigReceived(const std::string& paramString)=0;
+ virtual void onLastSeen(const std::string& paramString1, int paramInt, std::string* paramString2)=0;
+ virtual void onIsTyping(const std::string& paramString, bool paramBoolean)=0;
+ virtual void onAccountChange(int paramInt, long paramLong)=0;
+ virtual void onPrivacyBlockListAdd(const std::string& paramString)=0;
+ virtual void onPrivacyBlockListClear()=0;
+ virtual void onDirty(const std::map<string,string>& paramHashtable)=0;
+ virtual void onDirtyResponse(int paramHashtable)=0;
+ virtual void onRelayRequest(const std::string& paramString1, int paramInt, const std::string& paramString2)=0;
+ virtual void onSendGetPictureIds(std::map<string,string>* ids)=0;
+ virtual void onSendGetPicture(const std::string& jid, const std::vector<unsigned char>& data, const std::string& oldId, const std::string& newId)=0;
+ virtual void onPictureChanged(const std::string& from, const std::string& author, bool set)=0;
+ virtual void onDeleteAccount(bool result)=0;
+};
+
+class WAGroupListener {
+public:
+ virtual void onGroupAddUser(const std::string& paramString1, const std::string& paramString2)=0;
+ virtual void onGroupRemoveUser(const std::string& paramString1, const std::string& paramString2)=0;
+ virtual void onGroupNewSubject(const std::string& from, const std::string& author, const std::string& newSubject, int paramInt)=0;
+ virtual void onServerProperties(std::map<std::string, std::string>* nameValueMap)=0;
+ virtual void onGroupCreated(const std::string& paramString1, const std::string& paramString2)=0;
+ virtual void onGroupInfo(const std::string& paramString1, const std::string& paramString2, const std::string& paramString3, const std::string& paramString4, int paramInt1, int paramInt2)=0;
+ virtual void onGroupInfoFromList(const std::string& paramString1, const std::string& paramString2, const std::string& paramString3, const std::string& paramString4, int paramInt1, int paramInt2)=0;
+ virtual void onOwningGroups(const std::vector<string>& paramVector)=0;
+ virtual void onSetSubject(const std::string& paramString)=0;
+ virtual void onAddGroupParticipants(const std::string& paramString, const std::vector<string>& paramVector, int paramHashtable)=0;
+ virtual void onRemoveGroupParticipants(const std::string& paramString, const std::vector<string>& paramVector, int paramHashtable)=0;
+ virtual void onGetParticipants(const std::string& gjid, const std::vector<string>& participants)=0;
+ virtual void onParticipatingGroups(const std::vector<string>& paramVector)=0;
+ virtual void onLeaveGroup(const std::string& paramString)=0;
+};
+
+
+
+class MessageStore {
+public:
+ MessageStore();
+
+ virtual FMessage* get(Key* key);
+
+ virtual ~MessageStore();
+};
+
+
+class GroupSetting {
+public:
+ std::string jid;
+ bool enabled;
+ time_t muteExpiry;
+
+ GroupSetting() {
+ enabled = true;
+ jid = "";
+ muteExpiry = 0;
+ }
+};
+
+class WAConnection {
+
+ class IqResultHandler {
+ protected:
+ WAConnection* con;
+ public:
+ IqResultHandler(WAConnection* con) {this->con = con;}
+ virtual void parse(ProtocolTreeNode* paramProtocolTreeNode, const std::string& paramString) throw (WAException)=0;
+ void error(ProtocolTreeNode* node, int code) {
+ _LOGDATA("WAConnection: error node %s: code = %d", node->getAttributeValue("id")->c_str(), code);
+ }
+ void error(ProtocolTreeNode* node) throw (WAException) {
+ std::vector<ProtocolTreeNode*>* nodes = node->getAllChildren("error");
+ for (size_t i = 0; i < nodes->size(); i++) {
+ ProtocolTreeNode* errorNode = (*nodes)[i];
+ if (errorNode != NULL) {
+ std::string* errorCodeString = errorNode->getAttributeValue("code");
+ if (errorCodeString != NULL) {
+ int errorCode = atoi(errorCodeString->c_str());
+ error(node, errorCode);
+ }
+ }
+ }
+ delete nodes;
+ }
+
+ virtual ~IqResultHandler() {}
+
+ };
+
+ class IqResultPingHandler: public IqResultHandler {
+ public:
+ IqResultPingHandler(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ if (this->con->event_handler != NULL)
+ this->con->event_handler->onPingResponseReceived();
+ }
+
+ void error(ProtocolTreeNode* node) throw (WAException) {
+ if (this->con->event_handler != NULL)
+ this->con->event_handler->onPingResponseReceived();
+ }
+ };
+
+ class IqResultGetGroupsHandler: public IqResultHandler {
+ private:
+ std::string type;
+ public:
+ IqResultGetGroupsHandler(WAConnection* con, const std::string& type ):IqResultHandler(con) {this->type = type;}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ std::vector<std::string> groups;
+ this->con->readGroupList(node, groups);
+ if (this->con->group_event_handler != NULL) {
+ if (this->type.compare("participating") == 0)
+ this->con->group_event_handler->onParticipatingGroups(groups);
+ else if (this->type.compare("owning") == 0)
+ this->con->group_event_handler->onOwningGroups(groups);
+ }
+ }
+ };
+
+ class IqResultServerPropertiesHandler: public IqResultHandler {
+ public:
+ IqResultServerPropertiesHandler(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ std::vector<ProtocolTreeNode*>* nodes = node->getAllChildren("prop");
+ std::map<std::string,std::string> nameValueMap;
+ for (size_t i = 0; i < nodes->size();i++) {
+ ProtocolTreeNode* propNode = (*nodes)[i];
+ std::string* nameAttr = propNode->getAttributeValue("name");
+ std::string* valueAttr = propNode->getAttributeValue("value");
+ nameValueMap[*nameAttr] = *valueAttr;
+ }
+ delete nodes;
+ if (this->con->group_event_handler != NULL)
+ this->con->group_event_handler->onServerProperties(&nameValueMap);
+ }
+ };
+
+ class IqResultPrivayListHandler: public IqResultHandler {
+ public:
+ IqResultPrivayListHandler(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ ProtocolTreeNode* queryNode = node->getChild(0);
+ ProtocolTreeNode::require(queryNode, "query");
+ ProtocolTreeNode* listNode = queryNode->getChild(0);
+ ProtocolTreeNode::require(listNode, "list");
+ if (this->con->event_handler != NULL)
+ this->con->event_handler->onPrivacyBlockListClear();
+ if (listNode->children != NULL) {
+ for (size_t i = 0; i < listNode->children->size(); i++) {
+ ProtocolTreeNode* itemNode = (*listNode->children)[i];
+ ProtocolTreeNode::require(itemNode, "item");
+ if (itemNode->getAttributeValue("type")->compare("jid") == 0) {
+ std::string* jid = itemNode->getAttributeValue("value");
+ if (jid != NULL && this->con->event_handler != NULL)
+ this->con->event_handler->onPrivacyBlockListAdd(*jid);
+ }
+ }
+ }
+ }
+ };
+
+ class IqResultGetGroupInfoHandler: public IqResultHandler {
+ public:
+ IqResultGetGroupInfoHandler(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ ProtocolTreeNode* groupNode = node->getChild(0);
+ ProtocolTreeNode::require(groupNode, "group");
+ // std::string* gid = groupNode->getAttributeValue("id");
+ std::string* owner = groupNode->getAttributeValue("owner");
+ std::string* subject = groupNode->getAttributeValue("subject");
+ std::string* subject_t = groupNode->getAttributeValue("s_t");
+ std::string* subject_owner = groupNode->getAttributeValue("s_o");
+ std::string* creation = groupNode->getAttributeValue("creation");
+ if (this->con->group_event_handler != NULL)
+ this->con->group_event_handler->onGroupInfo(from, *owner, *subject, *subject_owner, atoi(subject_t->c_str()), atoi(creation->c_str()));
+ }
+ };
+
+ class IqResultGetGroupParticipantsHandler: public IqResultHandler {
+ public:
+ IqResultGetGroupParticipantsHandler(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ std::vector<std::string> participants;
+ this->con->readAttributeList(node, participants, "participant", "jid");
+ if (this->con->group_event_handler != NULL)
+ this->con->group_event_handler->onGetParticipants(from, participants);
+ }
+ };
+
+ class IqResultCreateGroupChatHandler: public IqResultHandler {
+ public:
+ IqResultCreateGroupChatHandler(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ ProtocolTreeNode* groupNode = node->getChild(0);
+ ProtocolTreeNode::require(groupNode, "group");
+ std::string* groupId = groupNode->getAttributeValue("id");
+ if (groupId != NULL && con->group_event_handler != NULL)
+ this->con->group_event_handler->onGroupCreated(from, *groupId);
+ }
+ };
+
+ class IqResultQueryLastOnlineHandler: public IqResultHandler {
+ public:
+ IqResultQueryLastOnlineHandler(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ ProtocolTreeNode* firstChild = node->getChild(0);
+ ProtocolTreeNode::require(firstChild, "query");
+ std::string* seconds = firstChild->getAttributeValue("seconds");
+ std::string* status = NULL;
+ status = firstChild->getDataAsString();
+ if (seconds != NULL && !from.empty()) {
+ if (this->con->event_handler != NULL)
+ this->con->event_handler->onLastSeen(from, atoi(seconds->c_str()), status);
+ }
+ delete status;
+ }
+ };
+
+ class IqResultGetPhotoHandler: public IqResultHandler {
+ private:
+ std::string jid;
+ std::string oldId;
+ std::string newId;
+ public:
+ IqResultGetPhotoHandler(WAConnection* con, const std::string& jid, const std::string& oldId, const std::string& newId):IqResultHandler(con) {
+ this->jid = jid;
+ this->oldId = oldId;
+ this->newId = newId;
+ }
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ std::string* attributeValue = node->getAttributeValue("type");
+
+ if ((attributeValue != NULL) && (attributeValue->compare("result") == 0) && (this->con->event_handler != NULL)) {
+ std::vector<ProtocolTreeNode*>* children = node->getAllChildren("picture");
+ for (int i = 0; i < children->size(); i++) {
+ ProtocolTreeNode* current = (*children)[i];
+ std::string* id = current->getAttributeValue("id");
+ if ((id != NULL) && (current->data != NULL) && (current->data->size() > 0)) {
+ if (current->data != NULL) {
+ this->con->event_handler->onSendGetPicture(this->jid, *current->data, this->oldId, this->newId);
+ }
+ break;
+ }
+ }
+ delete children;
+ }
+ }
+ void error(ProtocolTreeNode* node) throw (WAException) {
+ if (this->con->event_handler != NULL) {
+ std::vector<unsigned char> v;
+ this->con->event_handler->onSendGetPicture("error", v, "", "");
+ }
+ }
+ };
+
+ class IqResultSetPhotoHandler: public IqResultHandler {
+ private:
+ std::string jid;
+ public:
+ IqResultSetPhotoHandler(WAConnection* con, const std::string& jid):IqResultHandler(con) {this->jid = jid;}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ if (this->con->event_handler != NULL) {
+ std::string* photoId = NULL;
+ ProtocolTreeNode* child = node->getChild("picture");
+ if (child != NULL) {
+ this->con->event_handler->onPictureChanged(this->jid, "", true);
+ } else {
+ this->con->event_handler->onPictureChanged(this->jid, "", false);
+ }
+ }
+ }
+ };
+
+
+ class IqResultGetPictureIdsHandler: public IqResultHandler {
+ public:
+ IqResultGetPictureIdsHandler(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ // _LOGDATA("onGetPhotoIds %s", node->toString().c_str());
+ ProtocolTreeNode* groupNode = node->getChild("list");
+ std::vector<ProtocolTreeNode*>* children = groupNode->getAllChildren("user");
+ std::map<std::string, std::string> ids;
+ for (int i = 0; i < children->size(); i++) {
+ std::string* jid = (*children)[i]->getAttributeValue("jid");
+ std::string* id = (*children)[i]->getAttributeValue("id");
+ if (jid != NULL) {
+ ids[*jid] = (id == NULL? "": *id);
+ }
+ }
+ delete children;
+
+ if (this->con->event_handler != NULL) {
+ this->con->event_handler->onSendGetPictureIds(&ids);
+ }
+ }
+ };
+
+ class IqResultSendDeleteAccount: public IqResultHandler {
+ public:
+ IqResultSendDeleteAccount(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ if (this->con->event_handler != NULL) {
+ this->con->event_handler->onDeleteAccount(true);
+ }
+ }
+
+ void error(ProtocolTreeNode* node) throw (WAException) {
+ if (this->con->event_handler != NULL) {
+ this->con->event_handler->onDeleteAccount(false);
+ }
+ }
+ };
+
+ class IqResultClearDirtyHandler: public IqResultHandler {
+ public:
+ IqResultClearDirtyHandler(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ }
+ };
+
+ class IqSendClientConfigHandler: public IqResultHandler {
+ public:
+ IqSendClientConfigHandler(WAConnection* con):IqResultHandler(con) {}
+ virtual void parse(ProtocolTreeNode* node, const std::string& from) throw (WAException) {
+ _LOGDATA("Clientconfig response %s", node->toString().c_str());
+ }
+
+ void error(ProtocolTreeNode* node) throw (WAException) {
+ _LOGDATA("Clientconfig response error %s", node->toString().c_str());
+ }
+ };
+
+
+ private:
+ WALogin* login;
+ BinTreeNodeReader* in;
+ BinTreeNodeWriter* out;
+ WAListener* event_handler;
+ WAGroupListener* group_event_handler;
+ bool verbose;
+ int iqid;
+ std::map<string, IqResultHandler*> pending_server_requests;
+ IMutex* mutex;
+
+ // std::<string, FMessage* > message_store;
+
+ void init(WAListener* event_handler, WAGroupListener* group_event_handler, IMutex* mutex);
+ void sendMessageWithMedia(FMessage* message) throw(WAException);
+ void sendMessageWithBody(FMessage* message) throw(WAException);
+ std::map<string, string>* parseCategories(ProtocolTreeNode* node) throw(WAException);
+ void parseMessageInitialTagAlreadyChecked(ProtocolTreeNode* node) throw(WAException);
+ ProtocolTreeNode* getReceiptAck(const std::string& to, const std::string& id, const std::string& receiptType) throw(WAException);
+ std::string makeId(const std::string& prefix);
+ void sendGetGroups(const std::string& id, const std::string& type) throw (WAException);
+ void readGroupList(ProtocolTreeNode* node, std::vector<std::string>& groups) throw (WAException);
+ std::string gidToGjid(const std::string& gid);
+ void readAttributeList(ProtocolTreeNode* node, std::vector<std::string>& vector, const std::string& tag, const std::string& attribute) throw (WAException);
+ void sendVerbParticipants(const std::string& gjid, const std::vector<std::string>& participants, const std::string& id, const std::string& inner_tag) throw (WAException);
+ bool supportsReceiptAcks();
+ static ProtocolTreeNode* getMessageNode(FMessage* message, ProtocolTreeNode* node);
+ static ProtocolTreeNode* getSubjectMessage(const std::string& to, const std::string& id, ProtocolTreeNode* child) throw (WAException);
+ std::vector<ProtocolTreeNode*>* processGroupSettings(const std::vector<GroupSetting>& gruops);
+
+ public:
+ WAConnection(IMutex* mutex, WAListener* event_handler = NULL, WAGroupListener* group_event_handler = NULL);
+ virtual ~WAConnection();
+ std::string jid;
+ std::string fromm;
+ int msg_id;
+ int state;
+ bool retry;
+ time_t expire_date;
+ int account_kind;
+ time_t lastTreeRead;
+ static const int DICTIONARY_LEN = 237;
+ static const char* dictionary[];
+ static MessageStore* message_store;
+ KeyStream* inputKey;
+ KeyStream* outputKey;
+
+
+ static std::string removeResourceFromJid(const std::string& jid);
+
+ WALogin* getLogin();
+ void setLogin(WALogin* login);
+ void setVerboseId(bool b);
+ void sendMessage(FMessage* message) throw(WAException);
+ void sendAvailableForChat() throw(WAException);
+ bool read() throw(WAException);
+ void sendNop() throw(WAException);
+ void sendPing() throw(WAException);
+ void sendQueryLastOnline(const std::string& jid) throw (WAException);
+ void sendPong(const std::string& id) throw(WAException);
+ void sendComposing(const std::string& to) throw(WAException);
+ void sendActive() throw(WAException);
+ void sendInactive() throw(WAException);
+ void sendPaused(const std::string& to) throw(WAException);
+ void sendSubjectReceived(const std::string& to, const std::string& id) throw(WAException);
+ void sendMessageReceived(FMessage* message) throw(WAException);
+ void sendDeliveredReceiptAck(const std::string& to, const std::string& id) throw(WAException);
+ void sendVisibleReceiptAck(const std::string& to, const std::string& id) throw (WAException);
+ void sendPresenceSubscriptionRequest(const std::string& to) throw (WAException);
+ void sendClientConfig(const std::string& sound, const std::string& pushID, bool preview, const std::string& platform) throw(WAException);
+ void sendClientConfig(const std::string& pushID, bool preview, const std::string& platform, bool defaultSettings, bool groupSettings, const std::vector<GroupSetting>& groups) throw(WAException);
+ void sendClose() throw (WAException);
+ void sendAvailable() throw (WAException); // U.H.
+ void sendGetPrivacyList() throw (WAException);
+ void sendGetServerProperties() throw (WAException);
+ void sendGetGroups() throw (WAException);
+ void sendGetOwningGroups() throw (WAException);
+ void sendCreateGroupChat(const std::string& subject) throw (WAException);
+ void sendEndGroupChat(const std::string& gjid) throw (WAException);
+ void sendGetGroupInfo(const std::string& gjid) throw (WAException);
+ void sendGetParticipants(const std::string& gjid) throw (WAException);
+ void sendClearDirty(const std::string& category) throw (WAException);
+ void sendLeaveGroup(const std::string& gjid) throw (WAException);
+ void sendAddParticipants(const std::string& gjid, const std::vector<std::string>& participants) throw (WAException);
+ void sendRemoveParticipants(const std::string& gjid, const std::vector<std::string>& participants) throw (WAException);
+ void sendSetNewSubject(const std::string& gjid, const std::string& subject) throw (WAException);
+ void sendStatusUpdate(std::string& status) throw (WAException);
+ void sendGetPicture(const std::string& jid, const std::string& type, const std::string& oldId, const std::string& newId) throw (WAException);
+ void sendGetPictureIds(const std::vector<std::string>& jids) throw (WAException);
+ void sendSetPicture(const std::string& jid, std::vector<unsigned char>* data) throw (WAException);
+ void sendNotificationReceived(const std::string& from, const std::string& id) throw(WAException);
+ void sendDeleteAccount() throw(WAException);
+};
+
+
+#endif /* WACONNECTION_H_ */
diff --git a/protocols/WhatsApp/src/WhatsAPI++/WAException.h b/protocols/WhatsApp/src/WhatsAPI++/WAException.h new file mode 100644 index 0000000000..47d5cdfaf7 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/WAException.h @@ -0,0 +1,40 @@ +/*
+ * WAException.h
+ *
+ * Created on: 27/06/2012
+ * Author: Antonio
+ */
+
+
+
+#ifndef WAEXCEPTION_H_
+#define WAEXCEPTION_H_
+
+#include <stdexcept>
+#include <string>
+
+class WAException: public std::runtime_error {
+public:
+ int type;
+ int subtype;
+ time_t expire_date; // in seconds
+
+ static const int LOGIN_FAILURE_EX = 1;
+ static const int LOGIN_FAILURE_EX_TYPE_PASSWORD = 0;
+ static const int LOGIN_FAILURE_EX_TYPE_EXPIRED = 1;
+
+ static const int CORRUPT_STREAM_EX = 2;
+
+ static const int SOCKET_EX = 3;
+ static const int SOCKET_EX_RESOLVE_HOST = 0;
+ static const int SOCKET_EX_OPEN = 1;
+ static const int SOCKET_EX_INIT = 2;
+ static const int SOCKET_EX_SEND = 3;
+ static const int SOCKET_EX_RECV = 4;
+
+ WAException(const std::string& err): runtime_error(err) {this->type = 0; this->subtype = 0; this->expire_date = 0;};
+ WAException(const std::string& err, int type, int subtype): runtime_error(err), type(type), subtype(subtype), expire_date(0) {};
+ WAException(const std::string& err, int type, int subtype, time_t expireDate): runtime_error(err), type(type), subtype(subtype), expire_date(expireDate) {};
+};
+
+#endif /* WAEXCEPTION_H_ */
diff --git a/protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp b/protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp new file mode 100644 index 0000000000..3836a94a3c --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp @@ -0,0 +1,342 @@ +/*
+ * WALogin.cpp
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+
+#include "stdafx.h"
+#include "WALogin.h"
+#include "ByteArray.h"
+//#include "ApplicationData.h"
+#include "ProtocolTreeNode.h"
+#include "WAException.h"
+#include "base64.h"
+#include <iostream>
+#include <vector>
+#include <map>
+#include <stdlib.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+
+using namespace Utilities;
+
+const std::string WALogin::NONCE_KEY = "nonce=\"";
+
+WALogin::WALogin(WAConnection* connection, BinTreeNodeReader *reader, BinTreeNodeWriter *writer, const std::string& domain, const std::string& user, const std::string& resource, const std::string& password, const std::string& push_name) {
+ this->connection = connection;
+ this->inn = reader;
+ this->out = writer;
+ this->domain = domain;
+ this->user = user;
+ this->resource = resource;
+ this->password = password;
+ this->push_name = push_name;
+ this->supports_receipt_acks = false;
+ this->account_kind = -1;
+ this->expire_date = 0L;
+ this->outputKey = NULL;
+}
+
+std::vector<unsigned char>* WALogin::login(const std::vector<unsigned char>& authBlob) {
+ this->out->streamStart(this->domain, this->resource);
+
+ _LOGDATA("sent stream start");
+
+ sendFeatures();
+
+ _LOGDATA("sent features");
+
+ sendAuth(authBlob);
+
+ _LOGDATA("send auth, auth blob size %d", authBlob.size());
+
+ this->inn->streamStart();
+
+ _LOGDATA("read stream start");
+
+ return this->readFeaturesUntilChallengeOrSuccess();
+}
+
+BinTreeNodeReader* WALogin::getTreeNodeReader() {
+ return this->inn;
+}
+
+BinTreeNodeWriter* WALogin::getTreeNodeWriter() {
+ return this->out;
+}
+
+std::string WALogin::getResponse(const std::string& challenge) {
+ unsigned char md5_buffer[/*MD5_DIGEST_SIZE*/ MD5_DIGEST_LENGTH /*#WORKAROUND*/];
+
+ size_t i = challenge.find(WALogin::NONCE_KEY);
+ i += WALogin::NONCE_KEY.length();
+
+ size_t j = challenge.find('"', i);
+ std::string nonce = challenge.substr(i,j-i);
+
+ std::string cnonce = str(absLong(randLong()), 36);
+ _LOGDATA("cnonce = %s", cnonce.c_str());
+ std::string nc = "00000001";
+ std::string cinfo(this->user + ":" + this->domain + ":" + this->password);
+
+ _LOGDATA("cinfo = %s", cinfo.c_str());
+
+ ByteArrayOutputStream bos;
+ _LOGDATA((char*) md5digest((unsigned char*) cinfo.data(), cinfo.length(), md5_buffer), MD5_DIGEST_SIZE);
+ bos.write(md5digest((unsigned char*) cinfo.data(), cinfo.length(), md5_buffer), MD5_DIGEST_SIZE);
+ bos.write(58);
+ bos.write(nonce);
+ bos.write(58);
+ bos.write(cnonce);
+ // bos.print();
+
+ std::string digest_uri = "xmpp/" + this->domain;
+ std::vector<unsigned char>* A1 = bos.toByteArray();
+ std::string A2 = "AUTHENTICATE:" + digest_uri;
+ std::string KD((char*) bytesToHex(md5digest(&A1->front(), A1->size(), md5_buffer), MD5_DIGEST_SIZE), MD5_DIGEST_SIZE * 2);
+ KD += + ":" + nonce + ":" + nc + ":" + cnonce + ":auth:" + std::string((char*) bytesToHex(md5digest((unsigned char*) A2.data(), A2.size(), md5_buffer), MD5_DIGEST_SIZE), MD5_DIGEST_SIZE*2);
+
+ _LOGDATA("KD = %s", KD.c_str());
+
+ std::string response((char*) bytesToHex(md5digest((unsigned char*) KD.data(), KD.size(), md5_buffer), MD5_DIGEST_SIZE), MD5_DIGEST_SIZE*2);
+
+ _LOGDATA("response = %s", response.c_str());
+
+ std::string bigger_response;
+ bigger_response.append("realm=\"");
+ bigger_response.append(this->domain);
+ bigger_response.append("\",response=");
+ bigger_response.append(response);
+ bigger_response.append(",nonce=\"");
+ bigger_response.append(nonce);
+ bigger_response.append("\",digest-uri=\"");
+ bigger_response.append(digest_uri);
+ bigger_response.append("\",cnonce=\"");
+ bigger_response.append(cnonce);
+ bigger_response.append("\",qop=auth");
+ bigger_response.append(",username=\"");
+ bigger_response.append(this->user);
+ bigger_response.append("\",nc=");
+ bigger_response.append(nc);
+
+ _LOGDATA("biggerresponse = %s", bigger_response.c_str());
+
+ delete A1;
+
+ return bigger_response;
+}
+
+void WALogin::sendResponse(const std::vector<unsigned char>& challengeData) {
+ std::vector<unsigned char>* authBlob = this->getAuthBlob(challengeData);
+
+ // std::string response = this->getResponse(challengeData);
+ std::map<string, string> *attributes = new std::map<string,string>();
+ (*attributes)["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl";
+ ProtocolTreeNode node("response", attributes, authBlob);
+
+ this->out->write(&node);
+}
+
+void WALogin::sendFeatures() {
+ ProtocolTreeNode* child = new ProtocolTreeNode("receipt_acks", NULL);
+ std::vector<ProtocolTreeNode*>* children = new std::vector<ProtocolTreeNode*>();
+ children->push_back(child);
+
+ std::map<string, string>* attributes = new std::map<string, string>();
+ (*attributes)["type"] = "all";
+ ProtocolTreeNode* pictureChild = new ProtocolTreeNode("w:profile:picture", attributes);
+ children->push_back(pictureChild);
+
+ // children->push_back(new ProtocolTreeNode("status", NULL));
+
+ ProtocolTreeNode node("stream:features", NULL, NULL, children);
+ this->out->write(&node, true);
+}
+
+void WALogin::sendAuth(const std::vector<unsigned char>& existingChallenge) {
+ std::vector<unsigned char>* data = NULL;
+ if (!existingChallenge.empty()) {
+ data = this->getAuthBlob(existingChallenge);
+ }
+
+ std::map<string, string>* attributes = new std::map<string, string>();
+ (*attributes)["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl";
+ (*attributes)["mechanism"] = "WAUTH-1";
+ (*attributes)["user"] = this->user;
+
+ ProtocolTreeNode node("auth", attributes, data, NULL);
+ this->out->write(&node, true);
+}
+
+std::vector<unsigned char>* WALogin::getAuthBlob(const std::vector<unsigned char>& nonce) {
+ unsigned char out[20];
+ KeyStream::keyFromPasswordAndNonce(this->password, nonce, out);
+
+ if (this->connection->inputKey != NULL)
+ delete this->connection->inputKey;
+ this->connection->inputKey = new KeyStream(out, 20);
+
+ if (this->outputKey != NULL)
+ delete this->outputKey;
+
+ this->outputKey = new KeyStream(out, 20);
+ std::vector<unsigned char>* list = new std::vector<unsigned char>(0);
+ for (int i = 0; i < 4; i++) {
+ list->push_back(0);
+ }
+ list->insert(list->end(), this->user.begin(), this->user.end());
+ list->insert(list->end(), nonce.begin(), nonce.end());
+ time_t now;
+ std::string time = Utilities::intToStr(difftime(mktime(gmtime(&now)), 0));
+ list->insert(list->end(), time.begin(), time.end());
+
+ this->outputKey->encodeMessage(&((*list)[0]), 0, 4, list->size() - 4);
+ return list;
+}
+
+std::vector<unsigned char>* WALogin::readFeaturesUntilChallengeOrSuccess() {
+ ProtocolTreeNode* root;
+ while ((root = this->inn->nextTree()) != NULL) {
+ if (ProtocolTreeNode::tagEquals(root, "stream:features")) {
+ this->supports_receipt_acks = root->getChild("receipt_acks") != NULL;
+ delete root;
+ continue;
+ }
+ if (ProtocolTreeNode::tagEquals(root, "challenge")) {
+ // base64_decode(*root->data);
+ // _LOGDATA("Challenge data %s (%d)", root->data->c_str(), root->data->length());
+ std::vector<unsigned char> challengedata(root->data->begin(), root->data->end());
+ delete root;
+ this->sendResponse(challengedata);
+ _LOGDATA("Send response");
+ std::vector<unsigned char> data = this->readSuccess();
+ _LOGDATA("Read success");
+ return new std::vector<unsigned char>(data.begin(), data.end());
+ }
+ if (ProtocolTreeNode::tagEquals(root, "success")) {
+ // base64_decode(*root->data);
+ std::vector<unsigned char>* ret = new std::vector<unsigned char>(root->data->begin(), root->data->end());
+ this->parseSuccessNode(root);
+ delete root;
+ return ret;
+ }
+ }
+ throw WAException("fell out of loop in readFeaturesAndChallenge", WAException::CORRUPT_STREAM_EX, 0);
+}
+
+void WALogin::parseSuccessNode(ProtocolTreeNode* node) {
+ std::string* expiration = node->getAttributeValue("expiration");
+ if (expiration != NULL) {
+ this->expire_date = atol(expiration->c_str());
+ if (this->expire_date == 0)
+ throw WAException("invalid expire date: " + *expiration);
+ }
+
+
+ std::string* kind = node->getAttributeValue("kind");
+ if (kind != NULL && kind->compare("paid") == 0)
+ this->account_kind = 1;
+ else if (kind != NULL && kind->compare("free") == 0)
+ this->account_kind = 0;
+ else
+ this->account_kind = -1;
+
+ if (this->connection->outputKey != NULL)
+ delete this->connection->outputKey;
+ this->connection->outputKey = this->outputKey;
+}
+
+std::vector<unsigned char> WALogin::readSuccess() {
+ ProtocolTreeNode* node = this->inn->nextTree();
+
+ if (ProtocolTreeNode::tagEquals(node, "failure")) {
+ delete node;
+ throw WAException("Login failure", WAException::LOGIN_FAILURE_EX, WAException::LOGIN_FAILURE_EX_TYPE_PASSWORD);
+ }
+
+ ProtocolTreeNode::require(node, "success");
+ this->parseSuccessNode(node);
+
+ std::string* status = node->getAttributeValue("status");
+ if (status != NULL && status->compare("expired") == 0) {
+ delete node;
+ throw WAException("Account expired on" + std::string(ctime(&this->expire_date)), WAException::LOGIN_FAILURE_EX, WAException::LOGIN_FAILURE_EX_TYPE_EXPIRED, this->expire_date);
+ }
+ if (status != NULL && status->compare("active") == 0) {
+ if (node->getAttributeValue("expiration") == NULL) {
+ delete node;
+ throw WAException("active account with no expiration");
+ }
+ } else
+ this->account_kind = -1;
+
+ std::vector<unsigned char> data = *node->data;
+ delete node;
+ return data;
+}
+
+WALogin::~WALogin() {
+}
+
+KeyStream::KeyStream(unsigned char* key, size_t keyLength) {
+ unsigned char drop[256];
+
+ this->key = new unsigned char[keyLength];
+ memcpy(this->key, key, keyLength);
+ this->keyLength = keyLength;
+
+ RC4_set_key(&this->rc4, this->keyLength, this->key);
+ RC4(&this->rc4, 256, drop, drop);
+ HMAC_CTX_init(&this->hmac);
+}
+
+KeyStream::~KeyStream() {
+ delete [] this->key;
+ HMAC_CTX_cleanup(&this->hmac);
+}
+
+void KeyStream::keyFromPasswordAndNonce(const std::string& pass, const std::vector<unsigned char>& nonce, unsigned char *out) {
+ PKCS5_PBKDF2_HMAC_SHA1(pass.data(), pass.size(), nonce.data(), nonce.size(), 16, 20, out);
+}
+
+void KeyStream::decodeMessage(unsigned char* buffer, int macOffset, int offset, const int length) {
+ unsigned char digest[20];
+ this->hmacsha1(buffer + offset, length, digest);
+
+ for (int i = 0; i < 4; i++) {
+ if (buffer[macOffset + i] != digest[i]) {
+ throw WAException("invalid MAC", WAException::CORRUPT_STREAM_EX, 0);
+ }
+ }
+ unsigned char* out = new unsigned char[length];
+ RC4(&this->rc4, length, buffer + offset, out);
+ memcpy(buffer + offset, out, length);
+ delete[] out;
+}
+
+void KeyStream::encodeMessage(unsigned char* buffer, int macOffset, int offset, const int length) {
+ unsigned char* out = new unsigned char[length];
+ RC4(&this->rc4, length, buffer + offset, out);
+ memcpy(buffer + offset, out, length);
+ delete[] out;
+
+ unsigned char digest[20];
+ this->hmacsha1(buffer + offset, length, digest);
+
+ memcpy(buffer + macOffset, digest, 4);
+}
+
+void KeyStream::hmacsha1(unsigned char* text, int textLength, unsigned char *out) {
+ // CHMAC_SHA1 hmac;
+
+ // hmac.HMAC_SHA1(text, textLength, this->key, this->keyLength, out);
+
+ unsigned int mdLength;
+ HMAC(EVP_sha1(), this->key, this->keyLength, text, textLength, out, &mdLength);
+}
+
+
+
+
diff --git a/protocols/WhatsApp/src/WhatsAPI++/WALogin.h b/protocols/WhatsApp/src/WhatsAPI++/WALogin.h new file mode 100644 index 0000000000..92054abf54 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/WALogin.h @@ -0,0 +1,75 @@ +/*
+ * WALogin.h
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+
+#ifndef WALOGIN_H_
+#define WALOGIN_H_
+
+#include "BinTreeNodeReader.h"
+#include "BinTreeNodeWriter.h"
+#include "WAConnection.h"
+#include <openssl/rc4.h>
+#include <openssl/hmac.h>
+#include <string>
+
+class WAConnection;
+class BinTreeNodeReader;
+class BinTreeNodeWriter;
+
+class KeyStream {
+private:
+ RC4_KEY rc4;
+ HMAC_CTX hmac;
+ unsigned char* key;
+ int keyLength;
+
+ void hmacsha1(unsigned char* text, int textLength, unsigned char *out);
+
+public:
+ KeyStream(unsigned char* key, size_t keyLegnth);
+ virtual ~KeyStream();
+
+ static void keyFromPasswordAndNonce(const std::string& pass, const std::vector<unsigned char>& nonce, unsigned char *out);
+ void decodeMessage(unsigned char* buffer, int macOffset, int offset, const int length);
+ void encodeMessage(unsigned char* buffer, int macOffset, int offset, const int length);
+};
+
+
+class WALogin {
+private:
+ static const std::string NONCE_KEY;
+ KeyStream* outputKey;
+ WAConnection* connection;
+
+ std::vector<unsigned char>* getAuthBlob(const std::vector<unsigned char>& nonce);
+ void sendResponse(const std::vector<unsigned char>& challengeData);
+ void sendFeatures();
+ void sendAuth(const std::vector<unsigned char>& nonce);
+ std::vector<unsigned char>* readFeaturesUntilChallengeOrSuccess();
+ void parseSuccessNode(ProtocolTreeNode* node);
+ std::vector<unsigned char> readSuccess();
+ std::string getResponse(const std::string& challenge);
+
+public:
+ std::string user;
+ std::string domain;
+ std::string password;
+ std::string resource;
+ std::string push_name;
+ bool supports_receipt_acks;
+ time_t expire_date;
+ int account_kind;
+ BinTreeNodeReader* inn;
+ BinTreeNodeWriter* out;
+
+ WALogin(WAConnection* connection, BinTreeNodeReader *reader, BinTreeNodeWriter *writer, const std::string& domain, const std::string& user, const std::string& resource, const std::string& password, const std::string& push_name);
+ std::vector<unsigned char>* login(const std::vector<unsigned char>& blobLength);
+ BinTreeNodeReader *getTreeNodeReader();
+ BinTreeNodeWriter *getTreeNodeWriter();
+ virtual ~WALogin();
+};
+
+#endif /* WALOGIN_H_ */
diff --git a/protocols/WhatsApp/src/WhatsAPI++/base64.cpp b/protocols/WhatsApp/src/WhatsAPI++/base64.cpp new file mode 100644 index 0000000000..842ca4aed3 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/base64.cpp @@ -0,0 +1,103 @@ +/*
+ * Base64.cpp
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+#include "stdafx.h"
+#include "base64.h"
+#include <iostream>
+
+static const std::string base64_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+ return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+ std::string ret;
+ int i = 0;
+ int j = 0;
+ unsigned char char_array_3[3];
+ unsigned char char_array_4[4];
+
+ while (in_len--) {
+ char_array_3[i++] = *(bytes_to_encode++);
+ if (i == 3) {
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for(i = 0; (i <4) ; i++)
+ ret += base64_chars[char_array_4[i]];
+ i = 0;
+ }
+ }
+
+ if (i)
+ {
+ for(j = i; j < 3; j++)
+ char_array_3[j] = '\0';
+
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for (j = 0; (j < i + 1); j++)
+ ret += base64_chars[char_array_4[j]];
+
+ while((i++ < 3))
+ ret += '=';
+
+ }
+
+ return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+ int in_len = encoded_string.size();
+ int i = 0;
+ int j = 0;
+ int in_ = 0;
+ unsigned char char_array_4[4], char_array_3[3];
+ std::string ret;
+
+ while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+ char_array_4[i++] = encoded_string[in_]; in_++;
+ if (i ==4) {
+ for (i = 0; i <4; i++)
+ char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (i = 0; (i < 3); i++)
+ ret += char_array_3[i];
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j <4; j++)
+ char_array_4[j] = 0;
+
+ for (j = 0; j <4; j++)
+ char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+ }
+
+ return ret;
+}
diff --git a/protocols/WhatsApp/src/WhatsAPI++/base64.h b/protocols/WhatsApp/src/WhatsAPI++/base64.h new file mode 100644 index 0000000000..2ece162b89 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/base64.h @@ -0,0 +1,16 @@ +/*
+ * Base64.h
+ *
+ * Created on: 26/06/2012
+ * Author: Antonio
+ */
+
+#ifndef BASE64_H_
+#define BASE64_H_
+
+#include <string>
+
+std::string base64_encode(unsigned char const* , unsigned int len);
+std::string base64_decode(std::string const& s);
+
+#endif /* BASE64_H_ */
diff --git a/protocols/WhatsApp/src/WhatsAPI++/stdafx.cpp b/protocols/WhatsApp/src/WhatsAPI++/stdafx.cpp new file mode 100644 index 0000000000..8d5b5b5857 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : Quelldatei, die nur die Standard-Includes einbindet.
+// WhatsAPI++.pch ist der vorkompilierte Header.
+// stdafx.obj enthält die vorkompilierten Typinformationen.
+
+#include "stdafx.h"
+
+// TODO: Auf zusätzliche Header verweisen, die in STDAFX.H
+// und nicht in dieser Datei erforderlich sind.
diff --git a/protocols/WhatsApp/src/WhatsAPI++/stdafx.h b/protocols/WhatsApp/src/WhatsAPI++/stdafx.h new file mode 100644 index 0000000000..9649140359 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/stdafx.h @@ -0,0 +1,14 @@ +// stdafx.h : Includedatei für Standardsystem-Includedateien
+// oder häufig verwendete projektspezifische Includedateien,
+// die nur in unregelmäßigen Abständen geändert werden.
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN // Selten verwendete Teile der Windows-Header nicht einbinden.
+
+
+
+// TODO: Hier auf zusätzliche Header, die das Programm erfordert, verweisen.
diff --git a/protocols/WhatsApp/src/WhatsAPI++/targetver.h b/protocols/WhatsApp/src/WhatsAPI++/targetver.h new file mode 100644 index 0000000000..497706ee83 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/targetver.h @@ -0,0 +1,8 @@ +#pragma once
+
+// Durch Einbeziehen von"SDKDDKVer.h" wird die höchste verfügbare Windows-Plattform definiert.
+
+// Wenn Sie die Anwendung für eine frühere Windows-Plattform erstellen möchten, schließen Sie "WinSDKVer.h" ein, und
+// legen Sie das _WIN32_WINNT-Makro auf die zu unterstützende Plattform fest, bevor Sie "SDKDDKVer.h" einschließen.
+
+#include <SDKDDKVer.h>
diff --git a/protocols/WhatsApp/src/WhatsAPI++/utilities.cpp b/protocols/WhatsApp/src/WhatsAPI++/utilities.cpp new file mode 100644 index 0000000000..97b437452b --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/utilities.cpp @@ -0,0 +1,463 @@ +#include "stdafx.h"
+#include "utilities.h"
+//#include "ApplicationData.h"
+#include <iostream>
+#include <cstdio>
+#include <openssl/md5.h>
+#include <stdlib.h>
+#include <cstdlib>
+#include <sstream>
+#include "WAException.h"
+#include <stdarg.h>
+#include <time.h>
+#include <fstream>
+#include <iomanip>
+
+namespace Utilities{
+
+const int MD5_DIGEST_SIZE = MD5_DIGEST_LENGTH;
+
+const static char digits[] = {
+ '0' , '1' , '2' , '3' , '4' , '5' ,
+ '6' , '7' , '8' , '9' , 'a' , 'b' ,
+ 'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
+ 'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
+ 'o' , 'p' , 'q' , 'r' , 's' , 't' ,
+ 'u' , 'v' , 'w' , 'x' , 'y' , 'z'
+};
+
+void configureLogging(const char* ident) {
+#ifndef _LOGWIN32
+ openlog(ident, 0, LOG_USER);
+#endif
+}
+
+void closeLog() {
+#ifndef _LOGWIN32
+ closelog();
+#endif
+}
+
+std::string getCountryCode(){
+ return "34";
+}
+
+std::string reverseString(const std::string& str)
+{
+ return std::string(str.rbegin(), str.rend());
+}
+
+
+
+std::string itoa(int value, unsigned int base) {
+
+ const char digitMap[] = "0123456789abcdef";
+
+ std::string buf;
+ // Guard:
+ if (base == 0 || base > 16) {
+
+ // Error: may add more trace/log output here
+ return buf;
+ }
+ // Take care of negative int:
+ std::string sign;
+ int _value = value;
+ // Check for case when input is zero:
+ if (_value == 0) return "0";
+ if (value < 0) {
+ _value = -value;
+ sign = "-";
+ }
+
+ // Translating number to string with base:
+
+ for (int i = 30; _value && i ; --i) {
+ buf = digitMap[ _value % base ] + buf;
+ _value /= base;
+ }
+
+ return sign.append(buf);
+}
+
+
+unsigned char* md5digest(unsigned char *bytes, int length, unsigned char* buffer) {
+ MD5(bytes, length, buffer);
+ return buffer;
+}
+
+
+std::string processIdentity(const std::string& id){
+ std::string buffer_str = reverseString(id);
+ unsigned char buffer[MD5_DIGEST_LENGTH];
+
+ MD5((unsigned char*) buffer_str.data(), buffer_str.length(), buffer);
+ buffer_str.clear();
+
+ for(int i =0; i< MD5_DIGEST_LENGTH; i++){
+ int tmp = buffer[i]+128;
+ int f = tmp & 0xff;
+
+ buffer_str=buffer_str.append(itoa(f,16));
+ }
+
+ return buffer_str;
+}
+
+void debug(const std::string& msg) {
+#ifdef _LOGWIN32
+ cout << "DEBUG: " << msg << endl;
+#else
+ syslog(LOG_ERR, msg.c_str());
+#endif
+}
+
+std::string str(int64_t i, int radix ) {
+ if (radix < 2 || radix > 36)
+ throw WAException("radix must be in 2..36");
+ char buf[65];
+ int charPos = 64;
+ bool negative = (i < 0);
+
+ if (!negative) {
+ i = -i;
+ }
+
+ while (i <= -radix) {
+ buf[charPos--] = digits[(int)(-(i % radix))];
+ i = i / radix;
+ }
+ buf[charPos] = digits[(int)(-i)];
+
+ if (negative) {
+ buf[--charPos] = '-';
+ }
+
+ std::string aux(buf, 65);
+
+ return std::string(aux, charPos, (65 - charPos));
+}
+
+int64_t randLong() {
+ std::srand(time(NULL));
+ int64_t r = (int64_t) ((char) (std::rand() % 256));
+
+ for (int i = 0; i < 7 ; i++)
+ r = (r << 8) + ((char) (std::rand() % 256));
+
+ return r;
+}
+
+int64_t absLong(int64_t num) {
+ return (num >= 0? num: -num);
+}
+
+
+std::string intToStr(int i) {
+ std::stringstream convert;
+ convert << i;
+ return convert.str();
+}
+
+std::string doubleToStr(double d) {
+ std::stringstream convert;
+ convert << d;
+ return convert.str();
+}
+
+time_t parseBBDate(const string& s) {
+ _LOGDATA("parse DATE %s", s.c_str());
+ if (s.length() < 17)
+ return time(NULL);
+
+ struct tm timeinfo;
+ timeinfo.tm_year = atoi(s.substr(0, 4).c_str()) - 1900;
+ timeinfo.tm_mon = atoi(s.substr(4, 2).c_str()) - 1;
+ timeinfo.tm_mday = atoi(s.substr(6,2).c_str());
+ timeinfo.tm_hour = atoi(s.substr(9,2).c_str());
+ timeinfo.tm_min = atoi(s.substr(12,2).c_str());
+ timeinfo.tm_sec = atoi(s.substr(15,2).c_str());
+
+ //return timegm(&timeinfo);
+ return mktime(&timeinfo);
+}
+
+void logData(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+#ifdef _LOGWIN32
+ std::string formatLine = std::string(format).append("\n");
+ vprintf(formatLine.c_str(), args); fflush(stdout);
+#else
+ vsyslog(LOG_ERR, format, args);
+#endif
+
+}
+
+long long parseLongLong(const std::string& str) {
+ std::stringstream sstr(str);
+ long long val;
+ sstr >> val;
+
+ return val;
+}
+
+unsigned char* bytesToHex(unsigned char* bytes, int length) {
+ unsigned char* ret = new unsigned char[length*2];
+ int i = 0;
+ for (int c = 0; c < length; c++) {
+ int ub = bytes[c];
+
+ if (ub < 0)
+ ub += 256;
+ ret[i] = forDigit(ub >> 4);
+ i++;
+ ret[i] = forDigit(ub % 16);
+ i++;
+ }
+
+ return ret;
+}
+
+unsigned char forDigit(int b) {
+ if (b < 10)
+ return (unsigned char) (48 + b);
+ return (unsigned char) (97 + b - 10);
+}
+
+string md5String(const string& data) {
+ unsigned char md5_buffer[Utilities::MD5_DIGEST_SIZE + 1];
+ md5_buffer[Utilities::MD5_DIGEST_SIZE] = '\0';
+ md5digest((unsigned char*) data.c_str(), data.length(), md5_buffer);
+ std::string result((char*) Utilities::bytesToHex(md5_buffer, Utilities::MD5_DIGEST_SIZE), Utilities::MD5_DIGEST_SIZE*2);
+
+ return result;
+}
+
+bool saveStringToFile(const string& data, const string& filePath) {
+ std::ofstream out(filePath.c_str());
+ if (out.fail()) return false;
+ out << data;
+ if (out.fail()) return false;
+ out.close();
+ if (out.fail()) return false;
+ return true;
+}
+
+bool saveBytesToFile(const std::vector<unsigned char>& data, const string& filePath) {
+ std::fstream out(filePath.c_str(), ios::out | ios::binary);
+ if (out.fail()) return false;
+ out.write((const char*) &data[0], data.size());
+ if (out.fail()) return false;
+ out.close();
+ if (out.fail()) return false;
+ return true;
+}
+
+
+bool saveBytesToFile(const string& data, const string& filePath) {
+ std::fstream out(filePath.c_str(), ios::out | ios::binary);
+ if (out.fail()) return false;
+ out.write(data.c_str(), data.length());
+ if (out.fail()) return false;
+ out.close();
+ if (out.fail()) return false;
+ return true;
+}
+
+vector<unsigned char>* loadFileToBytes(const string& path) {
+ vector<unsigned char>* bytes = NULL;
+ std::ifstream in(path.c_str(), ios::in | ios::binary | ios::ate);
+ long size = in.tellg();
+ if (in.fail()) return NULL;
+
+ in.seekg(0, ios::beg);
+ char *buffer = new char[size];
+ in.read(buffer, size);
+ bytes = new vector<unsigned char>(buffer, buffer + size);
+ delete [] buffer;
+ in.close();
+ if (in.fail()) return NULL;
+
+ return bytes;
+}
+
+bool fileExists(const std::string& path) {
+ std::ifstream in(path.c_str());
+ return in;
+}
+
+
+string removeWaDomainFromJid(const string& jid) {
+ string result = jid;
+
+ int index = jid.find("@s.whatsapp.net");
+ if (index != string::npos) {
+ result.replace(index, 15, "");
+ return result;
+ }
+
+ index = jid.find("@g.us");
+ if (index != string::npos) {
+ result.replace(index, 5, "");
+ return result;
+ }
+
+ return jid;
+}
+
+string getNameFromPath(const std::string& path) {
+ int i = path.rfind('/');
+ if (i == string::npos)
+ i = 0;
+ else
+ i = i + 1;
+ return path.substr(i);
+}
+
+vector<unsigned char>* getChallengeData(const std::string& challengeFile) {
+ return loadFileToBytes(challengeFile);
+}
+
+bool saveChallengeData(const std::vector<unsigned char>& data, const std::string& challengeFile) {
+ return saveBytesToFile(data, challengeFile);
+}
+
+std::string utf8_to_utf16(const std::string& utf8)
+{
+ std::vector<unsigned long> unicode;
+ size_t i = 0;
+ while (i < utf8.size())
+ {
+ unsigned long uni;
+ size_t todo;
+ bool error = false;
+ unsigned char ch = utf8[i++];
+ if (ch <= 0x7F)
+ {
+ uni = ch;
+ todo = 0;
+ }
+ else if (ch <= 0xBF)
+ {
+ throw std::logic_error("not a UTF-8 string");
+ }
+ else if (ch <= 0xDF)
+ {
+ uni = ch&0x1F;
+ todo = 1;
+ }
+ else if (ch <= 0xEF)
+ {
+ uni = ch&0x0F;
+ todo = 2;
+ }
+ else if (ch <= 0xF7)
+ {
+ uni = ch&0x07;
+ todo = 3;
+ }
+ else
+ {
+ throw std::logic_error("not a UTF-8 string");
+ }
+ for (size_t j = 0; j < todo; ++j)
+ {
+ if (i == utf8.size())
+ throw std::logic_error("not a UTF-8 string");
+ unsigned char ch = utf8[i++];
+ if (ch < 0x80 || ch > 0xBF)
+ throw std::logic_error("not a UTF-8 string");
+ uni <<= 6;
+ uni += ch & 0x3F;
+ }
+ if (uni >= 0xD800 && uni <= 0xDFFF)
+ throw std::logic_error("not a UTF-8 string");
+ if (uni > 0x10FFFF)
+ throw std::logic_error("not a UTF-8 string");
+ unicode.push_back(uni);
+ }
+ std::string utf16;
+ for (size_t i = 0; i < unicode.size(); ++i)
+ {
+ unsigned long uni = unicode[i];
+ if (uni <= 0x7F) {
+ utf16 += (char) uni;
+ }
+ else
+ if (uni <= 0xFFFF)
+ {
+ stringstream value;
+ value << std::setw(4) << std::setfill('0') << Utilities::itoa(uni, 16).c_str();
+ utf16 += "\\u" + value.str();
+ }
+ else
+ {
+ stringstream value1, value2;
+ uni -= 0x10000;
+ value1 << std::setw(4) << std::setfill('0') << Utilities::itoa(((uni >> 10) + 0xD800), 16);
+ utf16 += "\\u" + value1.str();
+
+ value2 << std::setw(4) << std::setfill('0') << Utilities::itoa(((uni & 0x3FF) + 0xDC00), 16);
+ utf16 += "\\u" + value2.str();
+ }
+ }
+ return utf16;
+}
+
+std::string string_format(const char* fmt, va_list ap)
+{
+ int size = 100;
+ std::string str;
+ while (1) {
+ str.resize(size);
+ //va_start(ap, fmt);
+ int n = vsnprintf((char *)str.c_str(), size, fmt, ap);
+ //va_end(ap);
+ if (n > -1 && n < size) {
+ str.resize(n);
+ return str;
+ }
+ if (n > -1)
+ size = n + 1;
+ else
+ size *= 2;
+ }
+ return str;
+}
+
+std::string string_format(const std::string fmt, va_list ap)
+{
+ return string_format(fmt.c_str(), ap);
+ /*
+ int size = 100;
+ std::string str;
+ //va_list ap;
+ while (1) {
+ str.resize(size);
+ //va_start(ap, fmt);
+ int n = vsnprintf((char *)str.c_str(), size, fmt.c_str(), ap);
+ //va_end(ap);
+ if (n > -1 && n < size) {
+ str.resize(n);
+ return str;
+ }
+ if (n > -1)
+ size = n + 1;
+ else
+ size *= 2;
+ }
+ return str;
+ */
+}
+
+std::string string_format(const std::string fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ std::string ret = string_format(fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+}
diff --git a/protocols/WhatsApp/src/WhatsAPI++/utilities.h b/protocols/WhatsApp/src/WhatsAPI++/utilities.h new file mode 100644 index 0000000000..48db82b145 --- /dev/null +++ b/protocols/WhatsApp/src/WhatsAPI++/utilities.h @@ -0,0 +1,85 @@ +/***************************************************************************
+**
+** Copyright (c) 2012, Tarek Galal <tarek@wazapp.im>
+**
+** This file is part of Wazapp, an IM application for Meego Harmattan
+** platform that allows communication with Whatsapp users.
+**
+** Wazapp is free software: you can redistribute it and/or modify it under
+** the terms of the GNU General Public License as published by the
+** Free Software Foundation, either version 2 of the License, or
+** (at your option) any later version.
+**
+** Wazapp is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with Wazapp. If not, see http://www.gnu.org/licenses/.
+**
+****************************************************************************/
+
+#ifndef WA_UTILITIES
+#define WA_UTILITIES
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string>
+#include <openssl/md5.h>
+#include <time.h>
+#include <vector>
+
+#define _LOGWIN32 // #TODO
+#ifndef _LOGWIN32
+#include <syslog.h>
+#endif
+
+#ifdef _DEBUG
+ #define _DEBUGENABLED true
+#else
+ #define _DEBUGENABLED false
+#endif
+
+#define _LOGDATA(format, ...) if (_DEBUGENABLED) Utilities::logData(format, ##__VA_ARGS__)
+
+using namespace std;
+namespace Utilities{
+ extern void configureLogging(const char* ident);
+ extern void closeLog();
+ extern const int MD5_DIGEST_SIZE;
+ extern string getCountryCode();
+ extern string getMcc();
+ extern string getMnc();
+ extern string reverseString(const string& str);
+ extern unsigned char* md5digest(unsigned char *bytes, int length, unsigned char* buffer);
+ extern string processIdentity(const std::string& password);
+ extern int64_t randLong();
+ extern int64_t absLong(int64_t num);
+ extern string str(int64_t number, int radix);
+ extern std::string itoa(int value, unsigned int base);
+ extern std::string intToStr(int i);
+ extern std::string doubleToStr(double d);
+ extern long long parseLongLong(const std::string& str);
+ extern time_t parseBBDate(const string& s);
+ extern void logData(const char *msg, ...);
+ extern long long getCurrentTimeMillis();
+ extern unsigned char* bytesToHex(unsigned char* bytes, int length);
+ extern unsigned char forDigit(int b);
+ extern string md5String(const string& data);
+ extern bool saveStringToFile(const string& data, const string& filePath);
+ extern bool saveBytesToFile(const string& data, const string& filePath);
+ extern bool saveBytesToFile(const std::vector<unsigned char>& data, const string& filePath);
+ extern string removeWaDomainFromJid(const string& jid);
+ extern string getNameFromPath(const std::string& path);
+ extern vector<unsigned char>* loadFileToBytes(const string& path);
+ extern bool fileExists(const std::string& path);
+ extern std::vector<unsigned char>* getChallengeData(const std::string& file);
+ extern bool saveChallengeData(const std::vector<unsigned char>& data, const std::string& file);
+ extern std::string utf8_to_utf16(const std::string& utf8);
+ extern std::string string_format(const std::string fmt, ...);
+ extern std::string string_format(const std::string fmt, va_list ap);
+ extern std::string string_format(const char* fmt, va_list ap);
+}
+#endif
+
diff --git a/protocols/WhatsApp/src/cJSON.cpp b/protocols/WhatsApp/src/cJSON.cpp new file mode 100644 index 0000000000..a1f326bbe7 --- /dev/null +++ b/protocols/WhatsApp/src/cJSON.cpp @@ -0,0 +1,515 @@ +/*
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+#include <cstring>
+//#include <strings.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <float.h>
+#include <limits.h>
+#include <ctype.h>
+#include "cJSON.h"
+
+static const char *ep;
+
+const char *cJSON_GetErrorPtr() {return ep;}
+
+static int cJSON_strcasecmp(const char *s1,const char *s2)
+{
+ if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
+ for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
+ return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
+}
+
+static void *(*cJSON_malloc)(size_t sz) = malloc;
+static void (*cJSON_free)(void *ptr) = free;
+
+static char* cJSON_strdup(const char* str)
+{
+ size_t len;
+ char* copy;
+
+ len = strlen(str) + 1;
+ if (!(copy = (char*)cJSON_malloc(len))) return 0;
+ memcpy(copy,str,len);
+ return copy;
+}
+
+void cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+ if (!hooks) { /* Reset hooks */
+ cJSON_malloc = malloc;
+ cJSON_free = free;
+ return;
+ }
+
+ cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
+ cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item()
+{
+ cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
+ if (node) memset(node,0,sizeof(cJSON));
+ return node;
+}
+
+/* Delete a cJSON structure. */
+void cJSON_Delete(cJSON *c)
+{
+ cJSON *next;
+ while (c)
+ {
+ next=c->next;
+ if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
+ if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
+ if (c->string) cJSON_free(c->string);
+ cJSON_free(c);
+ c=next;
+ }
+}
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static const char *parse_number(cJSON *item,const char *num)
+{
+ double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
+
+ /* Could use sscanf for this? */
+ if (*num=='-') sign=-1,num++; /* Has sign? */
+ if (*num=='0') num++; /* is zero */
+ if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
+ if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
+ if (*num=='e' || *num=='E') /* Exponent? */
+ { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
+ while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
+ }
+
+ n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
+
+ item->valuedouble=n;
+ item->valueint=(int)n;
+ item->type=cJSON_Number;
+ return num;
+}
+
+/* Render the number nicely from the given item into a string. */
+static char *print_number(cJSON *item)
+{
+ char *str;
+ double d=item->valuedouble;
+ if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
+ {
+ str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
+ if (str) sprintf(str,"%d",item->valueint);
+ }
+ else
+ {
+ str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
+ if (str)
+ {
+ if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d);
+ else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
+ else sprintf(str,"%f",d);
+ }
+ }
+ return str;
+}
+
+/* Parse the input text into an unescaped cstring, and populate item. */
+static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+static const char *parse_string(cJSON *item,const char *str)
+{
+ const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
+ if (*str!='\"') {ep=str;return 0;} /* not a string! */
+
+ while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
+
+ out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
+ if (!out) return 0;
+
+ ptr=str+1;ptr2=out;
+ while (*ptr!='\"' && *ptr)
+ {
+ if (*ptr!='\\') *ptr2++=*ptr++;
+ else
+ {
+ ptr++;
+ switch (*ptr)
+ {
+ case 'b': *ptr2++='\b'; break;
+ case 'f': *ptr2++='\f'; break;
+ case 'n': *ptr2++='\n'; break;
+ case 'r': *ptr2++='\r'; break;
+ case 't': *ptr2++='\t'; break;
+ case 'u': /* transcode utf16 to utf8. */
+ sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */
+
+ if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid.
+
+ if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs.
+ {
+ if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate.
+ sscanf(ptr+3,"%4x",&uc2);ptr+=6;
+ if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate.
+ uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF);
+ }
+
+ len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
+
+ switch (len) {
+ case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+ case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+ case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+ case 1: *--ptr2 =(uc | firstByteMark[len]);
+ }
+ ptr2+=len;
+ break;
+ default: *ptr2++=*ptr; break;
+ }
+ ptr++;
+ }
+ }
+ *ptr2=0;
+ if (*ptr=='\"') ptr++;
+ item->valuestring=out;
+ item->type=cJSON_String;
+ return ptr;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static char *print_string_ptr(const char *str)
+{
+ const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
+
+ if (!str) return cJSON_strdup("");
+ ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
+
+ out=(char*)cJSON_malloc(len+3);
+ if (!out) return 0;
+
+ ptr2=out;ptr=str;
+ *ptr2++='\"';
+ while (*ptr)
+ {
+ if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
+ else
+ {
+ *ptr2++='\\';
+ switch (token=*ptr++)
+ {
+ case '\\': break; // *ptr2++='\\'; break;
+ case '\"': *ptr2++='\"'; break;
+ case '\b': *ptr2++='b'; break;
+ case '\f': *ptr2++='f'; break;
+ case '\n': *ptr2++='n'; break;
+ case '\r': *ptr2++='r'; break;
+ case '\t': *ptr2++='t'; break;
+ default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
+ }
+ }
+ }
+ *ptr2++='\"';*ptr2++=0;
+ return out;
+}
+/* Invote print_string_ptr (which is useful) on an item. */
+static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
+
+/* Predeclare these prototypes. */
+static const char *parse_value(cJSON *item,const char *value);
+static char *print_value(cJSON *item,int depth,int fmt);
+static const char *parse_array(cJSON *item,const char *value);
+static char *print_array(cJSON *item,int depth,int fmt);
+static const char *parse_object(cJSON *item,const char *value);
+static char *print_object(cJSON *item,int depth,int fmt);
+
+/* Utility to jump whitespace and cr/lf */
+static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
+
+/* Parse an object - create a new root, and populate. */
+cJSON *cJSON_Parse(const char *value)
+{
+ cJSON *c=cJSON_New_Item();
+ ep=0;
+ if (!c) return 0; /* memory fail */
+
+ if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
+ return c;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
+char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
+
+/* Parser core - when encountering text, process appropriately. */
+static const char *parse_value(cJSON *item,const char *value)
+{
+ if (!value) return 0; /* Fail on null. */
+ if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
+ if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
+ if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
+ if (*value=='\"') { return parse_string(item,value); }
+ if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
+ if (*value=='[') { return parse_array(item,value); }
+ if (*value=='{') { return parse_object(item,value); }
+
+ ep=value;return 0; /* failure. */
+}
+
+/* Render a value to text. */
+static char *print_value(cJSON *item,int depth,int fmt)
+{
+ char *out=0;
+ if (!item) return 0;
+ switch ((item->type)&255)
+ {
+ case cJSON_NULL: out=cJSON_strdup("null"); break;
+ case cJSON_False: out=cJSON_strdup("false");break;
+ case cJSON_True: out=cJSON_strdup("true"); break;
+ case cJSON_Number: out=print_number(item);break;
+ case cJSON_String: out=print_string(item);break;
+ case cJSON_Array: out=print_array(item,depth,fmt);break;
+ case cJSON_Object: out=print_object(item,depth,fmt);break;
+ }
+ return out;
+}
+
+/* Build an array from input text. */
+static const char *parse_array(cJSON *item,const char *value)
+{
+ cJSON *child;
+ if (*value!='[') {ep=value;return 0;} /* not an array! */
+
+ item->type=cJSON_Array;
+ value=skip(value+1);
+ if (*value==']') return value+1; /* empty array. */
+
+ item->child=child=cJSON_New_Item();
+ if (!item->child) return 0; /* memory fail */
+ value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
+ if (!value) return 0;
+
+ while (*value==',')
+ {
+ cJSON *new_item;
+ if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
+ child->next=new_item;new_item->prev=child;child=new_item;
+ value=skip(parse_value(child,skip(value+1)));
+ if (!value) return 0; /* memory fail */
+ }
+
+ if (*value==']') return value+1; /* end of array */
+ ep=value;return 0; /* malformed. */
+}
+
+/* Render an array to text */
+static char *print_array(cJSON *item,int depth,int fmt)
+{
+ char **entries;
+ char *out=0,*ptr,*ret;int len=5;
+ cJSON *child=item->child;
+ int numentries=0,i=0,fail=0;
+
+ /* How many entries in the array? */
+ while (child) numentries++,child=child->next;
+ /* Allocate an array to hold the values for each */
+ entries=(char**)cJSON_malloc(numentries*sizeof(char*));
+ if (!entries) return 0;
+ memset(entries,0,numentries*sizeof(char*));
+ /* Retrieve all the results: */
+ child=item->child;
+ while (child && !fail)
+ {
+ ret=print_value(child,depth+1,fmt);
+ entries[i++]=ret;
+ if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
+ child=child->next;
+ }
+
+ /* If we didn't fail, try to malloc the output string */
+ if (!fail) out=(char*)cJSON_malloc(len);
+ /* If that fails, we fail. */
+ if (!out) fail=1;
+
+ /* Handle failure. */
+ if (fail)
+ {
+ for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
+ cJSON_free(entries);
+ return 0;
+ }
+
+ /* Compose the output array. */
+ *out='[';
+ ptr=out+1;*ptr=0;
+ for (i=0;i<numentries;i++)
+ {
+ strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
+ if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
+ cJSON_free(entries[i]);
+ }
+ cJSON_free(entries);
+ *ptr++=']';*ptr++=0;
+ return out;
+}
+
+/* Build an object from the text. */
+static const char *parse_object(cJSON *item,const char *value)
+{
+ cJSON *child;
+ if (*value!='{') {ep=value;return 0;} /* not an object! */
+
+ item->type=cJSON_Object;
+ value=skip(value+1);
+ if (*value=='}') return value+1; /* empty array. */
+
+ item->child=child=cJSON_New_Item();
+ if (!item->child) return 0;
+ value=skip(parse_string(child,skip(value)));
+ if (!value) return 0;
+ child->string=child->valuestring;child->valuestring=0;
+ if (*value!=':') {ep=value;return 0;} /* fail! */
+ value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
+ if (!value) return 0;
+
+ while (*value==',')
+ {
+ cJSON *new_item;
+ if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
+ child->next=new_item;new_item->prev=child;child=new_item;
+ value=skip(parse_string(child,skip(value+1)));
+ if (!value) return 0;
+ child->string=child->valuestring;child->valuestring=0;
+ if (*value!=':') {ep=value;return 0;} /* fail! */
+ value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
+ if (!value) return 0;
+ }
+
+ if (*value=='}') return value+1; /* end of array */
+ ep=value;return 0; /* malformed. */
+}
+
+/* Render an object to text. */
+static char *print_object(cJSON *item,int depth,int fmt)
+{
+ char **entries=0,**names=0;
+ char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
+ cJSON *child=item->child;
+ int numentries=0,fail=0;
+ /* Count the number of entries. */
+ while (child) numentries++,child=child->next;
+ /* Allocate space for the names and the objects */
+ entries=(char**)cJSON_malloc(numentries*sizeof(char*));
+ if (!entries) return 0;
+ names=(char**)cJSON_malloc(numentries*sizeof(char*));
+ if (!names) {cJSON_free(entries);return 0;}
+ memset(entries,0,sizeof(char*)*numentries);
+ memset(names,0,sizeof(char*)*numentries);
+
+ /* Collect all the results into our arrays: */
+ child=item->child;depth++;if (fmt) len+=depth;
+ while (child)
+ {
+ names[i]=str=print_string_ptr(child->string);
+ entries[i++]=ret=print_value(child,depth,fmt);
+ if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
+ child=child->next;
+ }
+
+ /* Try to allocate the output string */
+ if (!fail) out=(char*)cJSON_malloc(len);
+ if (!out) fail=1;
+
+ /* Handle failure */
+ if (fail)
+ {
+ for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
+ cJSON_free(names);cJSON_free(entries);
+ return 0;
+ }
+
+ /* Compose the output: */
+ *out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
+ for (i=0;i<numentries;i++)
+ {
+ if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
+ strcpy(ptr,names[i]);ptr+=strlen(names[i]);
+ *ptr++=':';if (fmt) *ptr++='\t';
+ strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
+ if (i!=numentries-1) *ptr++=',';
+ if (fmt) *ptr++='\n';*ptr=0;
+ cJSON_free(names[i]);cJSON_free(entries[i]);
+ }
+
+ cJSON_free(names);cJSON_free(entries);
+ if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
+ *ptr++='}';*ptr++=0;
+ return out;
+}
+
+/* Get Array size/item / object item. */
+int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
+cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
+cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
+/* Utility for handling references. */
+static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
+
+/* Add item to array/object. */
+void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
+void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
+void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
+void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
+
+cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
+ if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
+void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
+cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
+void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
+
+/* Replace array/object items with new ones. */
+void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
+ newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
+ if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
+void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
+
+/* Create basic types: */
+cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
+cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
+cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
+cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
+cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
+cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
+cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
+cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
+
+/* Create Arrays: */
+cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
diff --git a/protocols/WhatsApp/src/cJSON.h b/protocols/WhatsApp/src/cJSON.h new file mode 100644 index 0000000000..128d0f17c2 --- /dev/null +++ b/protocols/WhatsApp/src/cJSON.h @@ -0,0 +1,127 @@ +/*
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* cJSON Types: */
+#define cJSON_False 0
+#define cJSON_True 1
+#define cJSON_NULL 2
+#define cJSON_Number 3
+#define cJSON_String 4
+#define cJSON_Array 5
+#define cJSON_Object 6
+
+#define cJSON_IsReference 256
+
+/* The cJSON structure: */
+typedef struct cJSON {
+ struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+ struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+
+ int type; /* The type of the item, as above. */
+
+ char *valuestring; /* The item's string, if type==cJSON_String */
+ int valueint; /* The item's number, if type==cJSON_Number */
+ double valuedouble; /* The item's number, if type==cJSON_Number */
+
+ char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+} cJSON;
+
+typedef struct cJSON_Hooks {
+ void *(*malloc_fn)(size_t sz);
+ void (*free_fn)(void *ptr);
+} cJSON_Hooks;
+
+/* Supply malloc, realloc and free functions to cJSON */
+extern void cJSON_InitHooks(cJSON_Hooks* hooks);
+
+
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
+extern cJSON *cJSON_Parse(const char *value);
+/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
+extern char *cJSON_Print(cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
+extern char *cJSON_PrintUnformatted(cJSON *item);
+/* Delete a cJSON entity and all subentities. */
+extern void cJSON_Delete(cJSON *c);
+
+/* Returns the number of items in an array (or object). */
+extern int cJSON_GetArraySize(cJSON *array);
+/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
+extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
+/* Get item "string" from object. Case insensitive. */
+extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
+
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+extern const char *cJSON_GetErrorPtr();
+
+/* These calls create a cJSON item of the appropriate type. */
+extern cJSON *cJSON_CreateNull();
+extern cJSON *cJSON_CreateTrue();
+extern cJSON *cJSON_CreateFalse();
+extern cJSON *cJSON_CreateBool(int b);
+extern cJSON *cJSON_CreateNumber(double num);
+extern cJSON *cJSON_CreateString(const char *string);
+extern cJSON *cJSON_CreateArray();
+extern cJSON *cJSON_CreateObject();
+
+/* These utilities create an Array of count items. */
+extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
+extern cJSON *cJSON_CreateFloatArray(float *numbers,int count);
+extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count);
+extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
+
+/* Append item to the specified array/object. */
+extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
+extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
+
+/* Remove/Detatch items from Arrays/Objects. */
+extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
+extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
+extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
+extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
+
+/* Update array items. */
+extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
+extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+
+#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
+#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
+#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
+#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
+#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/protocols/WhatsApp/src/chat.cpp b/protocols/WhatsApp/src/chat.cpp new file mode 100644 index 0000000000..a53a5077ca --- /dev/null +++ b/protocols/WhatsApp/src/chat.cpp @@ -0,0 +1,77 @@ +#include "common.h"
+
+// #TODO Remove, as we are not using the chat-module for groups anymore
+
+int WhatsAppProto::OnJoinChat(WPARAM,LPARAM)
+{
+ return 0;
+}
+
+int WhatsAppProto::OnLeaveChat(WPARAM,LPARAM)
+{
+ return 0;
+}
+
+int WhatsAppProto::OnChatOutgoing(WPARAM wParam, LPARAM lParam)
+{
+ GCHOOK *hook = reinterpret_cast<GCHOOK*>(lParam);
+ char *text;
+ char *id;
+
+ if (strcmp(hook->pDest->pszModule,m_szModuleName))
+ return 0;
+
+ switch(hook->pDest->iType)
+ {
+ case GC_USER_MESSAGE:
+ {
+ text = mir_t2a_cp(hook->ptszText,CP_UTF8);
+ std::string msg = text;
+
+ id = mir_t2a_cp(hook->pDest->ptszID,CP_UTF8);
+ std::string chat_id = id;
+
+ mir_free(text);
+ mir_free(id);
+
+ if (isOnline()) {
+ HANDLE hContact = this->ContactIDToHContact(chat_id);
+ if (hContact)
+ {
+ LOG("**Chat - Outgoing message: %s", text);
+ this->SendMsg(hContact, IS_CHAT, msg.c_str());
+
+ // #TODO Move to SendMsgWorker, otherwise all messages are "acknowledged" by Miranda
+
+ GCDEST gcd = { m_szModuleName, { NULL }, GC_EVENT_MESSAGE };
+ gcd.ptszID = hook->pDest->ptszID;
+
+ GCEVENT gce = {0};
+ gce.cbSize = sizeof(GCEVENT);
+ gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG;
+ gce.pDest = &gcd;
+ gce.ptszNick = mir_a2t(this->nick.c_str());
+ gce.ptszUID = mir_a2t(this->jid.c_str());
+ gce.time = time(NULL);
+ gce.ptszText = hook->ptszText;
+ gce.bIsMe = TRUE;
+ CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ mir_free((void*)gce.ptszUID);
+ mir_free((void*)gce.ptszNick);
+ }
+ }
+
+ break;
+ }
+
+ case GC_USER_LEAVE:
+ case GC_SESSION_TERMINATE:
+ {
+ break;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/protocols/WhatsApp/src/common.h b/protocols/WhatsApp/src/common.h new file mode 100644 index 0000000000..1c817f9f32 --- /dev/null +++ b/protocols/WhatsApp/src/common.h @@ -0,0 +1,96 @@ +/*
+
+WhatsApp plugin for Miranda NG
+Copyright © 2013 Uli Hecht
+
+*/
+
+#pragma once
+
+//#pragma warning(push)
+//#pragma warning(disable:4312)
+#pragma warning(disable:4996)
+#pragma warning(disable:4290)
+
+#define MIRANDA_VER 0x090E
+#define _WIN32_WINNT 0x0500
+#define _WIN32_WINDOWS 0x0500
+
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <cstring>
+#include <sstream>
+#include <fstream>
+#include <list>
+#include <map>
+#include <vector>
+#include <ctime>
+#include <stdarg.h>
+#include <time.h>
+#include <assert.h>
+#include <io.h>
+#include <iomanip>
+#include <windows.h>
+#include <win2k.h>
+#include <commctrl.h>
+
+#include <newpluginapi.h>
+//#include <m_version.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_avatars.h>
+#include <m_button.h>
+#include <m_chat.h>
+#include <m_clc.h>
+#include <m_clist.h>
+#include <m_clistint.h>
+#include <m_clui.h>
+#include <m_database.h>
+#include <m_history.h>
+#include <m_idle.h>
+#include <m_ignore.h>
+#include <m_langpack.h>
+#include <m_message.h>
+#include <m_netlib.h>
+#include <m_options.h>
+#include <m_popup.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_protoint.h>
+#include <m_protomod.h>
+#include <m_skin.h>
+#include <statusmodes.h>
+#include <m_userinfo.h>
+#include <m_addcontact.h>
+#include <m_icolib.h>
+#include <m_utils.h>
+#include <m_xml.h>
+#include <m_hotkeys.h>
+//#include <m_updater.h>
+#include <m_folders.h>
+#include "WhatsAPI++/WAConnection.h"
+
+class WhatsAppProto;
+
+#include "constants.h"
+#include "utils.h"
+#include "db.h"
+#include "resource.h"
+#include "dialogs.h"
+#include "theme.h"
+#include "definitions.h"
+#include "WASocketConnection.h"
+#include "proto.h"
+#include "entities.h"
+
+#if defined _DEBUG
+#include <stdlib.h>
+#include <crtdbg.h>
+#endif
+
+//#pragma warning(pop)
+
+extern HINSTANCE g_hInstance;
+extern std::string g_strUserAgent;
+extern DWORD g_mirandaVersion;
diff --git a/protocols/WhatsApp/src/connection.cpp b/protocols/WhatsApp/src/connection.cpp new file mode 100644 index 0000000000..12d9e95508 --- /dev/null +++ b/protocols/WhatsApp/src/connection.cpp @@ -0,0 +1,230 @@ +#include "common.h"
+
+void WhatsAppProto::ChangeStatus(void*)
+{
+ if (m_iDesiredStatus != ID_STATUS_OFFLINE && m_iStatus == ID_STATUS_OFFLINE)
+ {
+ ResetEvent(update_loop_lock_);
+ ForkThread(&WhatsAppProto::sentinelLoop, this);
+ ForkThread(&WhatsAppProto::stayConnectedLoop, this);
+ }
+ else if (m_iStatus == ID_STATUS_INVISIBLE && m_iDesiredStatus == ID_STATUS_ONLINE)
+ {
+ if (this->connection != NULL)
+ {
+ this->connection->sendAvailableForChat();
+ m_iStatus = ID_STATUS_ONLINE;
+ ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) m_iStatus, ID_STATUS_INVISIBLE);
+ }
+ }
+ else if (m_iStatus == ID_STATUS_ONLINE && m_iDesiredStatus == ID_STATUS_INVISIBLE)
+ {
+ if (this->connection != NULL)
+ {
+ this->connection->sendClose();
+ m_iStatus = ID_STATUS_INVISIBLE;
+ SetAllContactStatuses( ID_STATUS_OFFLINE, true );
+ ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) m_iStatus, ID_STATUS_ONLINE);
+ }
+ }
+ else if (m_iDesiredStatus == ID_STATUS_OFFLINE)
+ {
+ if (this->conn != NULL)
+ {
+ SetEvent(update_loop_lock_);
+ this->conn->forceShutdown();
+ LOG("Forced shutdown");
+ }
+ }
+}
+
+void WhatsAppProto::stayConnectedLoop(void*)
+{
+ bool error = true;
+ std::string cc, in, pass;
+ DBVARIANT dbv = {0};
+
+ if ( !db_get_s(NULL,m_szModuleName,WHATSAPP_KEY_CC,&dbv,DBVT_ASCIIZ))
+ {
+ cc = dbv.pszVal;
+ db_free(&dbv);
+ error = cc.empty();
+ }
+ if (error)
+ {
+ NotifyEvent(m_tszUserName,TranslateT("Please enter a country-code."),NULL,WHATSAPP_EVENT_CLIENT);
+ return;
+ }
+
+ error = true;
+ if ( !db_get_s(NULL,m_szModuleName,WHATSAPP_KEY_LOGIN,&dbv,DBVT_ASCIIZ))
+ {
+ in = dbv.pszVal;
+ db_free(&dbv);
+ error = in.empty();
+ }
+ if (error)
+ {
+ NotifyEvent(m_tszUserName,TranslateT("Please enter a phone-number without country code."),NULL,WHATSAPP_EVENT_CLIENT);
+ return;
+ }
+ this->phoneNumber = cc + in;
+ this->jid = this->phoneNumber + "@s.whatsapp.net";
+
+ error = true;
+ if ( !db_get_s(NULL, m_szModuleName, WHATSAPP_KEY_NICK, &dbv, DBVT_ASCIIZ))
+ {
+ this->nick = dbv.pszVal;
+ db_free(&dbv);
+ error = nick.empty();
+ }
+ if (error)
+ {
+ NotifyEvent(m_tszUserName, TranslateT("Please enter a nickname."), NULL, WHATSAPP_EVENT_CLIENT);
+ return;
+ }
+
+ error = true;
+ if ( !db_get_s(NULL,m_szModuleName,WHATSAPP_KEY_PASS,&dbv, DBVT_ASCIIZ))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING,strlen(dbv.pszVal)+1,
+ reinterpret_cast<LPARAM>(dbv.pszVal));
+ pass = dbv.pszVal;
+ db_free(&dbv);
+ error = pass.empty();
+ }
+ if (error)
+ {
+ NotifyEvent(m_tszUserName,TranslateT("Please enter a password."),NULL,WHATSAPP_EVENT_CLIENT);
+ return;
+ }
+
+ // -----------------------------
+
+ Mutex writerMutex;
+ WALogin* login = NULL;
+ int desiredStatus;
+
+ this->conn = NULL;
+
+ while (true)
+ {
+ if (connection != NULL)
+ {
+ if (connection->getLogin() == NULL && login != NULL)
+ {
+ delete login;
+ login = NULL;
+ }
+ delete connection;
+ connection = NULL;
+ }
+ if (this->conn != NULL)
+ {
+ delete this->conn;
+ this->conn = NULL;
+ }
+
+ desiredStatus = this->m_iDesiredStatus;
+ if (desiredStatus == ID_STATUS_OFFLINE || error)
+ {
+ LOG("Set status to offline");
+ SetAllContactStatuses( ID_STATUS_OFFLINE, true );
+ this->ToggleStatusMenuItems(false);
+ int prevStatus = this->m_iStatus;
+ this->m_iStatus = ID_STATUS_OFFLINE;
+ ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) m_iStatus, prevStatus);
+ break;
+ }
+
+ LOG("Connecting...");
+ this->m_iStatus = ID_STATUS_CONNECTING;
+ ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) ID_STATUS_OFFLINE, m_iStatus);
+
+ CODE_BLOCK_TRY
+
+ this->conn = new WASocketConnection("c.whatsapp.net", 5222);
+
+ connection = new WAConnection(&this->connMutex, this, this);
+ login = new WALogin(connection, new BinTreeNodeReader(connection, conn, WAConnection::dictionary, WAConnection::DICTIONARY_LEN),
+ new BinTreeNodeWriter(connection, conn, WAConnection::dictionary, WAConnection::DICTIONARY_LEN, &writerMutex),
+ "s.whatsapp.net", this->phoneNumber, std::string(ACCOUNT_RESOURCE) +"-5222", base64_decode(pass), nick);
+
+ std::vector<unsigned char>* nextChallenge = login->login(*this->challenge);
+ delete this->challenge;
+ this->challenge = nextChallenge;
+ connection->setLogin(login);
+ connection->setVerboseId(true); // ?
+ if (desiredStatus != ID_STATUS_INVISIBLE) {
+ connection->sendAvailableForChat();
+ }
+
+ LOG("Set status to online");
+ this->m_iStatus = desiredStatus;
+ ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE )m_iStatus, ID_STATUS_CONNECTING);
+ this->ToggleStatusMenuItems(true);
+
+ ForkThread(&WhatsAppProto::ProcessBuddyList, this);
+
+ // #TODO Move out of try block. Exception is expected on disconnect
+ bool cont = true;
+ while (cont == true)
+ {
+ this->lastPongTime = time(NULL);
+ cont = connection->read();
+ }
+ LOG("Exit from read-loop");
+
+ CODE_BLOCK_CATCH(WAException)
+ error = true;
+ CODE_BLOCK_CATCH(exception)
+ error = true;
+ CODE_BLOCK_CATCH_UNKNOWN
+ error = true;
+ CODE_BLOCK_END
+ }
+ LOG("Break out from loop");
+}
+
+void WhatsAppProto::sentinelLoop(void*)
+{
+ int delay = MAX_SILENT_INTERVAL;
+ int quietInterval;
+ while (WaitForSingleObjectEx(update_loop_lock_, delay * 1000, true) == WAIT_TIMEOUT)
+ {
+ if (this->m_iStatus != ID_STATUS_OFFLINE && this->connection != NULL && this->m_iDesiredStatus == this->m_iStatus)
+ {
+ // #TODO Quiet after pong or tree read?
+ quietInterval = difftime(time(NULL), this->lastPongTime);
+ if (quietInterval >= MAX_SILENT_INTERVAL)
+ {
+ CODE_BLOCK_TRY
+ LOG("send ping");
+ this->lastPongTime = time(NULL);
+ this->connection->sendPing();
+ CODE_BLOCK_CATCH(exception)
+ CODE_BLOCK_END
+ }
+ else
+ {
+ delay = MAX_SILENT_INTERVAL - quietInterval;
+ }
+ }
+ else
+ {
+ delay = MAX_SILENT_INTERVAL;
+ }
+ }
+ ResetEvent(update_loop_lock_);
+ LOG("Exiting sentinel loop");
+}
+
+void WhatsAppProto::onPing(const std::string& id)
+{
+ if (this->isOnline()) {
+ CODE_BLOCK_TRY
+ LOG("Sending pong with id %s", id.c_str());
+ this->connection->sendPong(id);
+ CODE_BLOCK_CATCH_ALL
+ }
+}
diff --git a/protocols/WhatsApp/src/constants.h b/protocols/WhatsApp/src/constants.h new file mode 100644 index 0000000000..5ef23bb98b --- /dev/null +++ b/protocols/WhatsApp/src/constants.h @@ -0,0 +1,62 @@ +#if !defined(CONSTANTS_H)
+#define CONSTANTS_H
+
+// Version management
+#define __VERSION_DWORD PLUGIN_MAKE_VERSION(0, 0, 2, 0)
+#define __PRODUCT_DWORD PLUGIN_MAKE_VERSION(0, 9, 14, 0)
+#define __VERSION_STRING "0.0.2.0"
+#define __PRODUCT_STRING "0.9.14.0"
+#define __VERSION_VS_FILE 0,0,2,
+#define __VERSION_VS_PROD 0,9,14,0
+#define __VERSION_VS_FILE_STRING "0, 0, 2, 0"
+#define __VERSION_VS_PROD_STRING "0, 9, 14, 0"
+
+#define PRODUCT_NAME _T("WhatsApp Protocol")
+
+// Limits
+#define WHATSAPP_GROUP_NAME_LIMIT 420
+
+// Defaults
+#define DEFAULT_MAP_STATUSES 0
+#define DEFAULT_SYSTRAY_NOTIFY 0
+
+#define DEFAULT_EVENT_NOTIFICATIONS_ENABLE 1
+#define DEFAULT_EVENT_FEEDS_ENABLE 1
+#define DEFAULT_EVENT_OTHER_ENABLE 1
+#define DEFAULT_EVENT_CLIENT_ENABLE 1
+#define DEFAULT_EVENT_COLBACK 0x00ffffff
+#define DEFAULT_EVENT_COLTEXT 0x00000000
+#define DEFAULT_EVENT_TIMEOUT_TYPE 0
+#define DEFAULT_EVENT_TIMEOUT -1
+
+// #TODO Move constants below to WhatsAPI++
+
+// WhatsApp
+#define WHATSAPP_LOGIN_SERVER "c.whatsapp.net"
+#define ACCOUNT_USER_AGENT "WhatsApp/2.8.3 iPhone_OS/5.0.1 Device/Unknown_(iPhone4,1)"
+#define ACCOUNT_URL_CODEREQUEST "https://r.whatsapp.net/v1/code.php"
+#define ACCOUNT_URL_CODEREQUESTV2 "https://v.whatsapp.net/v2/code"
+#define ACCOUNT_URL_REGISTERREQUEST "https://r.whatsapp.net/v1/register.php"
+#define ACCOUNT_URL_REGISTERREQUESTV2 "https://v.whatsapp.net/v2/register"
+#define ACCOUNT_URL_UPLOADREQUEST "https://mms.whatsapp.net/client/iphone/upload.php"
+#define ACCOUNT_URL_EXISTSV2 "https://v.whatsapp.net/v2/exist"
+
+// WhatsApp Nokia 302 S40
+#define ACCOUNT_RESOURCE "S40-2.3.53"
+#define ACCOUNT_USER_AGENT_REGISTRATION "WhatsApp/2.3.53 S40Version/14.26 Device/Nokia302"
+#define ACCOUNT_TOKEN_PREFIX1 "PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk"
+#define ACCOUNT_TOKEN_PREFIX2 "1354754753509"
+
+#define WHATSAPP_RECV_MESSAGE 1
+#define WHATSAPP_SEND_MESSAGE 2
+
+#define MAX_SILENT_INTERVAL 210
+
+// Event flags
+#define WHATSAPP_EVENT_CLIENT 0x10000000 // WhatsApp error or info message
+#define WHATSAPP_EVENT_NOTIFICATION 0x40000000 // WhatsApp notification
+#define WHATSAPP_EVENT_OTHER 0x80000000 // WhatsApp other event - friend requests/new messages
+
+#define IS_CHAT 1
+
+#endif
\ No newline at end of file diff --git a/protocols/WhatsApp/src/contacts.cpp b/protocols/WhatsApp/src/contacts.cpp new file mode 100644 index 0000000000..5f1ae75a67 --- /dev/null +++ b/protocols/WhatsApp/src/contacts.cpp @@ -0,0 +1,754 @@ +#include "common.h"
+
+bool WhatsAppProto::IsMyContact(HANDLE hContact, bool include_chat)
+{
+ const char *proto = GetContactProto(hContact);
+ if( proto && strcmp(m_szModuleName,proto) == 0 )
+ {
+ if( include_chat )
+ return true;
+ else
+ return db_get_b(hContact,m_szModuleName,"ChatRoom",0) == 0;
+ } else {
+ return false;
+ }
+}
+
+HANDLE WhatsAppProto::AddToContactList(const std::string& jid, BYTE type, bool dont_check, const char *new_name,
+ bool isChatRoom, bool isHidden)
+{
+ HANDLE hContact;
+
+ if (!dont_check) {
+ // First, check if this contact exists
+ hContact = ContactIDToHContact(jid);
+
+ if( hContact )
+ {
+ if (new_name != NULL)
+ {
+ DBVARIANT dbv;
+ string oldName;
+ if (db_get_s(hContact, m_szModuleName, WHATSAPP_KEY_PUSH_NAME, &dbv, DBVT_UTF8))
+ {
+ oldName = jid.c_str();
+ }
+ else
+ {
+ oldName = dbv.pszVal;
+ db_free(&dbv);
+ }
+ db_set_utf(hContact, m_szModuleName, WHATSAPP_KEY_PUSH_NAME, new_name);
+
+ if (oldName.compare(string(new_name)) != 0)
+ {
+ this->NotifyEvent(oldName.c_str(), this->TranslateStr("is now known as '%s'", new_name),
+ hContact, WHATSAPP_EVENT_OTHER);
+ }
+ }
+ if (db_get_b(hContact, "CList", "Hidden", 0) > 0)
+ {
+ db_unset(hContact, "CList", "Hidden");
+ }
+ return hContact;
+ }
+ }
+
+ // If not, make a new contact!
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_ADD, 0, 0);
+ if (hContact)
+ {
+ if (CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)m_szModuleName) == 0)
+ {
+ db_set_s(hContact ,m_szModuleName, "ID", jid.c_str());
+ LOG("Added contact %s", jid.c_str());
+ db_set_s(hContact, m_szModuleName, "MirVer", "WhatsApp");
+ db_unset(hContact, "CList", "MyHandle");
+ db_set_b(hContact, "CList", "NotOnList", 1);
+
+ /*
+ std::string newNameStr;
+ if (hasNickName)
+ {
+ newNameStr = new_name;
+ }
+
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(dbei);
+ dbei.szModule = m_szModuleName;
+ dbei.timestamp = time(NULL);
+ dbei.flags = DBEF_UTF;
+ dbei.eventType = EVENTTYPE_ADDED;
+ dbei.cbBlob = sizeof(DWORD) * 2 + newNameStr.length() + 5;
+
+ PBYTE pCurBlob = dbei.pBlob = ( PBYTE ) mir_alloc( dbei.cbBlob );
+ *(PDWORD)pCurBlob = 0; pCurBlob += sizeof(DWORD); // UID
+ *(PDWORD)pCurBlob = (DWORD)hContact; pCurBlob += sizeof(DWORD); // Contact Handle
+ strcpy((char*)pCurBlob, newNameStr.data()); pCurBlob += newNameStr.length()+1; // Nickname
+ *pCurBlob = '\0'; pCurBlob++; // First Name
+ *pCurBlob = '\0'; pCurBlob++; // Last Name
+ *pCurBlob = '\0'; pCurBlob++; // E-mail
+ *pCurBlob = '\0'; // Reason
+
+ CallService(MS_DB_EVENT_ADD, 0, (LPARAM) &dbei);
+ */
+
+ DBVARIANT dbv;
+ if( !db_get_s(NULL, m_szModuleName, WHATSAPP_KEY_DEF_GROUP, &dbv, DBVT_WCHAR))
+ {
+ db_set_ws(hContact, "CList", "Group", dbv.ptszVal);
+ db_free(&dbv);
+ }
+
+ if (new_name != NULL)
+ {
+ db_set_utf(hContact, m_szModuleName, WHATSAPP_KEY_PUSH_NAME, new_name);
+ }
+
+ if (isChatRoom)
+ {
+ db_set_b(hContact, m_szModuleName, "SimpleChatRoom", 1);
+ //ForkThread(&WhatsAppProto::SendGetGroupInfoWorker, this, (void*) &jid);
+ }
+
+ return hContact;
+ } else {
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0);
+ }
+ }
+
+ return INVALID_HANDLE_VALUE;
+}
+
+HANDLE WhatsAppProto::ContactIDToHContact(const std::string& phoneNumber)
+{
+ // Cache
+ std::map<string, HANDLE>::iterator it = this->hContactByJid.find(phoneNumber);
+ if (it != this->hContactByJid.end())
+ {
+ return it->second;
+ }
+
+ const char* id;
+ const char* idForContact = "ID";
+ const char* idForChat = "ChatRoomID";
+
+ for(HANDLE hContact = db_find_first();
+ hContact;
+ hContact = db_find_next(hContact))
+ {
+ if(!IsMyContact(hContact, true))
+ continue;
+
+ id = db_get_b(hContact, m_szModuleName, "ChatRoom", 0) > 0 ? idForChat : idForContact;
+
+ DBVARIANT dbv;
+ if( !db_get_s(hContact,m_szModuleName, id,&dbv, DBVT_ASCIIZ))
+ {
+ if( strcmp(phoneNumber.c_str(),dbv.pszVal) == 0 )
+ {
+ db_free(&dbv);
+ this->hContactByJid[phoneNumber] = hContact;
+ return hContact;
+ }
+ else
+ {
+ db_free(&dbv);
+ }
+ }
+ }
+
+ return 0;
+}
+
+void WhatsAppProto::SetAllContactStatuses(int status, bool reset_client)
+{
+ for (HANDLE hContact = db_find_first();
+ hContact;
+ hContact = db_find_next(hContact))
+ {
+ if (!IsMyContact(hContact))
+ continue;
+
+ if (reset_client) {
+ DBVARIANT dbv;
+ if (!db_get_s(hContact,m_szModuleName,"MirVer",&dbv,DBVT_WCHAR)) {
+ if (_tcscmp(dbv.ptszVal, _T("WhatsApp")))
+ db_set_ws(hContact,m_szModuleName,"MirVer", _T("WhatsApp"));
+ db_free(&dbv);
+ }
+
+ db_set_ws(hContact, "CList", "StatusMsg", _T(""));
+ }
+
+ if (db_get_w(hContact,m_szModuleName,"Status",ID_STATUS_OFFLINE) != status)
+ db_set_w(hContact,m_szModuleName,"Status",status);
+ }
+}
+
+void WhatsAppProto::ProcessBuddyList(void*)
+{
+ std::vector<std::string> jids;
+ DBVARIANT dbv;
+ for (HANDLE hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) {
+ if (!IsMyContact(hContact))
+ continue;
+
+ if (!db_get_s(hContact, m_szModuleName, WHATSAPP_KEY_ID, &dbv, DBVT_ASCIIZ)) {
+ std::string id(dbv.pszVal);
+ db_free(&dbv);
+
+ CODE_BLOCK_TRY
+ if (!db_get_b(hContact, "CList", "Hidden", 0))
+ {
+ // Do not request picture for inactive groups - this would make the group visible again
+ jids.push_back(id);
+ }
+ if (db_get_b(hContact, m_szModuleName, "SimpleChatRoom", 0) == 0)
+ {
+ this->connection->sendQueryLastOnline(id);
+ this->connection->sendPresenceSubscriptionRequest(id);
+ }
+ CODE_BLOCK_CATCH_ALL
+ }
+ }
+ if (jids.size() > 0) {
+ CODE_BLOCK_TRY
+ this->connection->sendGetPictureIds(jids);
+ CODE_BLOCK_CATCH_ALL
+ }
+ CODE_BLOCK_TRY
+ this->connection->sendGetGroups();
+ this->connection->sendGetOwningGroups();
+ CODE_BLOCK_CATCH_ALL
+}
+
+void WhatsAppProto::SearchAckThread(void *targ)
+{
+ char *id = mir_utf8encodeT((TCHAR*)targ);
+ std::string jid(id);
+ jid.append("@s.whatsapp.net");
+
+ this->connection->sendQueryLastOnline(jid);
+ this->connection->sendPresenceSubscriptionRequest(jid);
+
+ mir_free(targ);
+ mir_free(id);
+}
+
+void WhatsAppProto::onAvailable(const std::string& paramString, bool paramBoolean)
+{
+ HANDLE hContact = this->AddToContactList(paramString, 0, false);
+ if (hContact != NULL)
+ {
+ if (paramBoolean)
+ {
+ /*
+ this->connection->sendGetPicture(paramString, "image", "old", "new");
+ std::vector<std::string> ids;
+ ids.push_back(paramString);
+ this->connection->sendGetPictureIds(ids);
+ */
+ db_set_w(hContact, m_szModuleName, "Status", ID_STATUS_ONLINE);
+ }
+ else
+ {
+ db_set_w(hContact, m_szModuleName, "Status", ID_STATUS_OFFLINE);
+ this->UpdateStatusMsg(hContact);
+ }
+ }
+
+ db_set_dw(hContact, this->m_szModuleName, WHATSAPP_KEY_LAST_SEEN, 0);
+ this->UpdateStatusMsg(hContact);
+}
+
+void WhatsAppProto::onLastSeen(const std::string& paramString1, int paramInt, std::string* paramString2)
+{
+ /*
+ HANDLE hContact = this->ContactIDToHContact(paramString1);
+ if (hContact == NULL)
+ {
+ // This contact was searched
+ PROTOSEARCHRESULT isr = {0};
+ isr.cbSize = sizeof(isr);
+ isr.flags = PSR_TCHAR;
+ isr.id = mir_a2t_cp(id.c_str(), CP_UTF8);
+ isr.nick = "";
+ isr.firstName = "";
+ isr.lastName = "";
+ isr.email = "";
+ ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, targ, (LPARAM)&isr);
+ // #TODO
+ }
+ */
+ HANDLE hContact = this->AddToContactList(paramString1, 0, false);
+ db_set_dw(hContact, this->m_szModuleName, WHATSAPP_KEY_LAST_SEEN, paramInt);
+
+ this->UpdateStatusMsg(hContact);
+}
+
+void WhatsAppProto::UpdateStatusMsg(HANDLE hContact)
+{
+ std::wstringstream ss;
+
+ int lastSeen = db_get_dw(hContact, m_szModuleName, WHATSAPP_KEY_LAST_SEEN, -1);
+ if (lastSeen != -1)
+ {
+ time_t timestamp = time(NULL) - lastSeen;
+
+ tm* t = localtime(×tamp);
+ ss << _T("Last seen on ") << std::setfill(_T('0')) << std::setw(2) << (t->tm_mon + 1) <<
+ _T("/") << std::setw(2) << t->tm_mday << _T("/") << (t->tm_year + 1900) << _T(" ")
+ << std::setw(2) << t->tm_hour << _T(":") << std::setw(2) << t->tm_min;
+ }
+
+ int state = db_get_dw(hContact, m_szModuleName, WHATSAPP_KEY_LAST_MSG_STATE, 2);
+ if (state < 2 && lastSeen != -1)
+ ss << _T(" - ");
+ for (; state < 2; ++state)
+ ss << _T("\u2713");
+
+ db_set_ws(hContact, "CList", "StatusMsg", ss.str().c_str());
+}
+
+void WhatsAppProto::onPictureChanged(const std::string& from, const std::string& author, bool set)
+{
+ if (this->isOnline())
+ {
+ vector<string> ids;
+ ids.push_back(from);
+ this->connection->sendGetPictureIds(ids);
+ }
+}
+
+void WhatsAppProto::onSendGetPicture(const std::string& jid, const std::vector<unsigned char>& data, const std::string& oldId, const std::string& newId)
+{
+ HANDLE hContact = this->ContactIDToHContact(jid);
+ if (hContact)
+ {
+ LOG("Updating avatar for jid %s", jid.c_str());
+
+ // Save avatar
+ std::tstring filename = this->GetAvatarFolder() ;
+ if (_taccess(filename.c_str(), 0))
+ CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)filename.c_str());
+ filename = filename + _T("\\") + (TCHAR*) _A2T(jid.c_str()) + _T("-") + (TCHAR*) _A2T(newId.c_str()) +_T(".jpg");
+ FILE *f = _tfopen(filename.c_str(), _T("wb"));
+ int r = fwrite(std::string(data.begin(), data.end()).c_str(), 1, data.size(), f);
+ fclose(f);
+
+ PROTO_AVATAR_INFORMATIONT ai = {sizeof(ai)};
+ ai.hContact = hContact;
+ ai.format = PA_FORMAT_JPEG;
+ _tcsncpy(ai.filename, filename.c_str(), SIZEOF(ai.filename));
+ ai.filename[SIZEOF(ai.filename)-1] = 0;
+
+ int ackResult;
+ if (r > 0)
+ {
+ db_set_s(hContact, m_szModuleName, WHATSAPP_KEY_AVATAR_ID, newId.c_str());
+ ackResult = ACKRESULT_SUCCESS;
+ }
+ else
+ {
+ ackResult = ACKRESULT_FAILED;
+ }
+ ProtoBroadcastAck(m_szModuleName, ai.hContact, ACKTYPE_AVATAR, ackResult, (HANDLE)&ai, 0);
+ }
+}
+
+void WhatsAppProto::onSendGetPictureIds(std::map<string,string>* ids)
+{
+ for (std::map<string,string>::iterator it = ids->begin(); it != ids->end(); ++it)
+ {
+ HANDLE hContact = this->AddToContactList(it->first);
+ if (hContact != NULL)
+ {
+ DBVARIANT dbv;
+ std::string oldId;
+ if (db_get_s(hContact, m_szModuleName, WHATSAPP_KEY_AVATAR_ID, &dbv, DBVT_ASCIIZ))
+ {
+ oldId = "";
+ }
+ else
+ {
+ oldId = dbv.pszVal;
+ db_free(&dbv);
+ }
+
+ if (it->second.size() > 0 && it->second.compare(oldId) != 0)
+ {
+ CODE_BLOCK_TRY
+ this->connection->sendGetPicture(it->first, "image", oldId, it->second);
+ CODE_BLOCK_CATCH_ALL
+ }
+ }
+ }
+}
+
+string WhatsAppProto::GetContactDisplayName(HANDLE hContact)
+{
+ return string((CHAR*) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, 0));
+}
+
+string WhatsAppProto::GetContactDisplayName(const string& jid)
+{
+ HANDLE hContact = this->ContactIDToHContact(jid);
+ return hContact ? this->GetContactDisplayName(hContact) : (string("+")+ Utilities::removeWaDomainFromJid(jid));
+}
+
+// Group contacts --------------------------
+
+void WhatsAppProto::SendGetGroupInfoWorker(void* data)
+{
+ if (this->isOnline())
+ {
+ this->connection->sendGetGroupInfo(*((std::string*) data));
+ }
+}
+
+void WhatsAppProto::onGroupInfo(const std::string& gjid, const std::string& ownerJid, const std::string& subject, const std::string& createrJid, int paramInt1, int paramInt2)
+{
+ LOG("'%s', '%s', '%s', '%s'", gjid.c_str(), ownerJid.c_str(), subject.c_str(), createrJid.c_str());
+ HANDLE hContact = ContactIDToHContact(gjid);
+ if (!hContact)
+ {
+ LOG("Group info requested for non existing contact '%s'", gjid.c_str());
+ return;
+ }
+ db_set_b(hContact, m_szModuleName, "SimpleChatRoom", ownerJid.compare(this->jid) == 0 ? 2 : 1);
+ if (this->isOnline())
+ {
+ this->connection->sendGetParticipants(gjid);
+ }
+}
+
+void WhatsAppProto::onGroupInfoFromList(const std::string& paramString1, const std::string& paramString2, const std::string& paramString3, const std::string& paramString4, int paramInt1, int paramInt2)
+{
+ // Called before onOwningGroups() or onParticipatingGroups() is called!
+ LOG("");
+}
+
+void WhatsAppProto::onGroupNewSubject(const std::string& from, const std::string& author, const std::string& newSubject, int paramInt)
+{
+ LOG("'%s', '%s', '%s'", from.c_str(), author.c_str(), newSubject.c_str());
+ HANDLE hContact = this->AddToContactList(from, 0, false, newSubject.c_str(), true);
+}
+
+void WhatsAppProto::onGroupAddUser(const std::string& paramString1, const std::string& paramString2)
+{
+ LOG("%s - user: %s", paramString1.c_str(), paramString2.c_str());
+ HANDLE hContact = this->AddToContactList(paramString1);
+ std::string groupName(this->GetContactDisplayName(hContact));
+
+ if (paramString2.compare(this->jid) == 0)
+ {
+ this->NotifyEvent(groupName, this->TranslateStr("You have been added to the group"), hContact, WHATSAPP_EVENT_OTHER);
+ db_set_b(hContact, m_szModuleName, "IsGroupMember", 1);
+ }
+ else
+ {
+ this->NotifyEvent(groupName, this->TranslateStr("User '%s' has been added to the group",
+ this->GetContactDisplayName(paramString2).c_str()), hContact, WHATSAPP_EVENT_OTHER);
+ }
+
+ if(this->isOnline())
+ {
+ this->connection->sendGetGroupInfo(paramString1);
+ }
+}
+
+void WhatsAppProto::onGroupRemoveUser(const std::string& paramString1, const std::string& paramString2)
+{
+ LOG("%s - user: %s", paramString1.c_str(), paramString2.c_str());
+ HANDLE hContact = this->ContactIDToHContact(paramString1);
+ if (!hContact)
+ return;
+
+ string groupName(this->GetContactDisplayName(hContact));
+
+ if (paramString2.compare(this->jid) == 0)
+ {
+ //db_set_b(hContact, "CList", "Hidden", 1);
+ db_set_b(hContact, m_szModuleName, "IsGroupMember", 0);
+
+ this->NotifyEvent(groupName, this->TranslateStr("You have been removed from the group"),
+ hContact, WHATSAPP_EVENT_OTHER);
+ }
+ else if(this->isOnline())
+ {
+ this->NotifyEvent(groupName, this->TranslateStr("User '%s' has been removed from the group",
+ this->GetContactDisplayName(paramString2).c_str()), hContact, WHATSAPP_EVENT_OTHER);
+ this->connection->sendGetGroupInfo(paramString1);
+ //this->connection->sendGetParticipants(paramString1);
+ }
+}
+
+void WhatsAppProto::onLeaveGroup(const std::string& paramString)
+{
+ // Won't be called for unknown reasons!
+ LOG("%s", this->GetContactDisplayName(paramString).c_str());
+ HANDLE hContact = this->ContactIDToHContact(paramString);
+ if (hContact)
+ {
+ //db_set_b(hContact, "CList", "Hidden", 1);
+ db_set_b(hContact, m_szModuleName, "IsGroupMember", 0);
+ }
+}
+
+void WhatsAppProto::onGetParticipants(const std::string& gjid, const std::vector<string>& participants)
+{
+ LOG("%s", this->GetContactDisplayName(gjid).c_str());
+
+ HANDLE hUserContact;
+ HANDLE hContact = this->ContactIDToHContact(gjid);
+ if (!hContact)
+ return;
+
+ if (db_get_b(hContact, "CList", "Hidden", 0) == 1)
+ return;
+
+ bool isHidden = true;
+ bool isOwningGroup = db_get_b(hContact, m_szModuleName, "SimpleChatRoom", 0) == 2;
+
+ if (isOwningGroup)
+ {
+ this->isMemberByGroupContact[hContact].clear();
+ }
+
+ for (std::vector<string>::const_iterator it = participants.begin(); it != participants.end(); ++it)
+ {
+ // Hide, if we are not member of the group
+ // Sometimes the group is shown shortly after hiding it again, due to other threads which stored the contact
+ // in a cache before it has been removed (E.g. picture-id list in processBuddyList)
+ if (isHidden && this->jid.compare(*it) == 0)
+ {
+ isHidden = false;
+ if (!isOwningGroup)
+ {
+ // Break, as we don't need to collect group-members
+ break;
+ }
+ }
+
+ // #TODO Slow for big count of participants
+ // #TODO If a group is hidden it has been deleted from the local contact list
+ // => don't allow to add users anymore
+ if (isOwningGroup)
+ {
+ hUserContact = this->ContactIDToHContact(*it);
+ if (hUserContact)
+ {
+ this->isMemberByGroupContact[hContact][hUserContact] = true;
+ }
+ }
+ }
+ if (isHidden)
+ {
+ //db_set_b(hContact, "CList", "Hidden", 1);
+ // #TODO Check if it's possible to reach this point at all
+ db_set_b(hContact, m_szModuleName, "IsGroupMember", 0);
+ }
+}
+
+// Menu handler
+INT_PTR __cdecl WhatsAppProto::OnAddContactToGroup(WPARAM wParam, LPARAM, LPARAM lParam)
+{
+ string a = GetContactDisplayName((HANDLE) wParam);
+ string b = GetContactDisplayName((HANDLE) lParam);
+ LOG("Request add user %s to group %s", a.c_str(), b.c_str());
+
+ if (!this->isOnline())
+ return NULL;
+
+ DBVARIANT dbv;
+
+ if (db_get_s((HANDLE) wParam, m_szModuleName, "ID", &dbv, DBVT_ASCIIZ))
+ return NULL;
+
+ std::vector<string> participants;
+ participants.push_back(string(dbv.pszVal));
+ db_free(&dbv);
+
+ if (db_get_s((HANDLE) lParam, m_szModuleName, "ID", &dbv, DBVT_ASCIIZ))
+ return NULL;
+
+ this->connection->sendAddParticipants(string(dbv.pszVal), participants);
+
+ db_free(&dbv);
+ return NULL;
+}
+
+// Menu handler
+INT_PTR __cdecl WhatsAppProto::OnRemoveContactFromGroup(WPARAM wParam, LPARAM, LPARAM lParam)
+{
+ string a = GetContactDisplayName((HANDLE) wParam);
+ string b = GetContactDisplayName((HANDLE) lParam);
+ LOG("Request remove user %s from group %s", a.c_str(), b.c_str());
+
+ if (!this->isOnline())
+ return NULL;
+
+ DBVARIANT dbv;
+
+ if (db_get_s((HANDLE) lParam, m_szModuleName, "ID", &dbv, DBVT_ASCIIZ))
+ return NULL;
+
+ std::vector<string> participants;
+ participants.push_back(string(dbv.pszVal));
+ db_free(&dbv);
+
+ if (db_get_s((HANDLE) wParam, m_szModuleName, "ID", &dbv, DBVT_ASCIIZ))
+ return NULL;
+
+ this->connection->sendRemoveParticipants(string(dbv.pszVal), participants);
+
+ db_free(&dbv);
+ return NULL;
+}
+
+void WhatsAppProto::onOwningGroups(const std::vector<string>& paramVector)
+{
+ LOG("");
+ this->HandleReceiveGroups(paramVector, true);
+}
+
+void WhatsAppProto::onParticipatingGroups(const std::vector<string>& paramVector)
+{
+ LOG("");
+ this->HandleReceiveGroups(paramVector, false);
+}
+
+void WhatsAppProto::HandleReceiveGroups(const std::vector<string>& groups, bool isOwned)
+{
+ HANDLE hContact;
+ map<HANDLE, bool> isMember; // at the moment, only members of owning groups are stored
+
+ // This could take long time if there are many new groups which aren't
+ // yet stored to the database. But that should be a rare case
+ for (std::vector<string>::const_iterator it = groups.begin(); it != groups.end(); ++it)
+ {
+ hContact = this->AddToContactList(*it, 0, false, NULL, true);
+ db_set_b(hContact, m_szModuleName, "IsGroupMember", 1);
+ if (isOwned)
+ {
+ this->isMemberByGroupContact[hContact]; // []-operator creates entry, if it doesn't exist
+ db_set_b(hContact, m_szModuleName, "SimpleChatRoom", 2);
+ this->connection->sendGetParticipants(*it);
+ }
+ else
+ {
+ isMember[hContact] = true;
+ }
+ }
+
+ // Mark as non-meber if group only exists locally
+ if (!isOwned)
+ {
+ for (hContact = db_find_first(); hContact; hContact = db_find_next(hContact))
+ {
+ if (IsMyContact(hContact) && db_get_b(hContact, m_szModuleName, "SimpleChatRoom", 0) > 0)
+ {
+ //LOG("Set IsGroupMember to 0 for '%s'", this->GetContactDisplayName(hContact).c_str());
+ db_set_b(hContact, m_szModuleName, "IsGroupMember",
+ isMember.find(hContact) == isMember.end() ? 0 : 1);
+ }
+ }
+ }
+}
+
+void WhatsAppProto::onGroupCreated(const std::string& paramString1, const std::string& paramString2)
+{
+ // Must be received after onOwningGroups() :/
+ LOG("%s / %s", paramString1.c_str(), paramString2.c_str());
+ string jid = paramString2 +string("@")+ paramString1;
+ HANDLE hContact = this->AddToContactList(jid, 0, false, NULL, true);
+ db_set_b(hContact, m_szModuleName, "SimpleChatRoom", 2);
+}
+
+// Menu-handler
+int __cdecl WhatsAppProto::OnCreateGroup(WPARAM wParam, LPARAM lParam)
+{
+ LOG("");
+ input_box* ib = new input_box;
+ ib->defaultValue = _T("");
+ ib->limit = WHATSAPP_GROUP_NAME_LIMIT;
+ ib->proto = this;
+ ib->text = _T("Enter group subject");
+ ib->title = _T("WhatsApp - Create Group");
+ ib->thread = &WhatsAppProto::SendCreateGroupWorker;
+ HWND hDlg = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_INPUTBOX), 0, WhatsAppInputBoxProc,
+ reinterpret_cast<LPARAM>(ib));
+ ShowWindow(hDlg, SW_SHOW);
+ return FALSE;
+}
+
+void __cdecl WhatsAppProto::SendSetGroupNameWorker(void* data)
+{
+ input_box_ret* ibr(static_cast<input_box_ret*>(data));
+ string groupName(ibr->value);
+ mir_free(ibr->value);
+ DBVARIANT dbv;
+ if (!db_get_s(*((HANDLE*) ibr->userData), m_szModuleName, WHATSAPP_KEY_ID, &dbv, DBVT_ASCIIZ)
+ && this->isOnline())
+ {
+ this->connection->sendSetNewSubject(dbv.pszVal, groupName);
+ db_free(&dbv);
+ }
+ delete ibr->userData;
+ delete ibr;
+}
+
+void __cdecl WhatsAppProto::SendCreateGroupWorker(void* data)
+{
+ input_box_ret* ibr(static_cast<input_box_ret*>(data));
+ string groupName(ibr->value);
+ mir_free(ibr->value);
+ if (this->isOnline())
+ {
+ this->connection->sendCreateGroupChat(groupName);
+ }
+}
+
+int __cdecl WhatsAppProto::OnChangeGroupSubject(WPARAM wParam, LPARAM lParam)
+{
+ DBVARIANT dbv;
+ HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
+ input_box* ib = new input_box;
+
+ if (db_get_s(hContact, m_szModuleName, WHATSAPP_KEY_PUSH_NAME, &dbv, DBVT_WCHAR))
+ {
+ ib->defaultValue = _T("");
+ }
+ else
+ {
+ ib->defaultValue = dbv.ptszVal;
+ db_free(&dbv);
+ }
+ ib->limit = WHATSAPP_GROUP_NAME_LIMIT;
+ ib->text = _T("Enter new group subject");
+ ib->title = _T("WhatsApp - Change Group Subject");
+
+ ib->thread = &WhatsAppProto::SendSetGroupNameWorker;
+ ib->proto = this;
+ HANDLE* hContactPtr = new HANDLE(hContact);
+ ib->userData = (void*) hContactPtr;
+
+ HWND hDlg = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_INPUTBOX), 0, WhatsAppInputBoxProc,
+ reinterpret_cast<LPARAM>(ib));
+ ShowWindow(hDlg, SW_SHOW);
+ return 0;
+}
+
+int __cdecl WhatsAppProto::OnLeaveGroup(WPARAM wParam, LPARAM)
+{
+ DBVARIANT dbv;
+ HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
+ if (this->isOnline() && !db_get_s(hContact, m_szModuleName, WHATSAPP_KEY_ID, &dbv, DBVT_ASCIIZ))
+ {
+ db_set_b(hContact, m_szModuleName, "IsGroupMember", 0);
+ this->connection->sendLeaveGroup(dbv.pszVal);
+ db_free(&dbv);
+ }
+ return 0;
+}
\ No newline at end of file diff --git a/protocols/WhatsApp/src/db.h b/protocols/WhatsApp/src/db.h new file mode 100644 index 0000000000..d78d2e75d6 --- /dev/null +++ b/protocols/WhatsApp/src/db.h @@ -0,0 +1,47 @@ +// DB macros
+#define getByte( setting, error ) db_get_b( NULL, m_szModuleName, setting, error )
+#define setByte( setting, value ) db_set_b( NULL, m_szModuleName, setting, value )
+#define getWord( setting, error ) db_get_w( NULL, m_szModuleName, setting, error )
+#define setWord( setting, value ) db_set_w( NULL, m_szModuleName, setting, value )
+#define getDword( setting, error ) db_get_dw( NULL, m_szModuleName, setting, error )
+#define setDword( setting, value ) db_set_dw( NULL, m_szModuleName, setting, value )
+#define getString( setting, dest ) db_get_s( NULL, m_szModuleName, setting, dest, DBVT_ASCIIZ )
+#define setString( setting, value ) db_set_s( NULL, m_szModuleName, setting, value )
+#define getTString( setting, dest ) DBGetContactSettingTString( NULL, m_szModuleName, setting, dest )
+#define setTString( setting, value ) DBWriteContactSettingTString( NULL, m_szModuleName, setting, value )
+#define getU8String( setting, dest ) DBGetContactSettingUTF8String( NULL, m_szModuleName, setting, dest )
+#define setU8String( setting, value ) DBWriteContactSettingUTF8String( NULL, m_szModuleName, setting, value )
+#define deleteSetting( setting ) DBDeleteContactSetting( NULL, m_szModuleName, setting )
+
+// DB keys
+#define WHATSAPP_KEY_ID "ID"
+#define WHATSAPP_KEY_LOGIN "Login"
+#define WHATSAPP_KEY_CC "CountryCode"
+#define WHATSAPP_KEY_NICK "Nickname"
+#define WHATSAPP_KEY_PASS "Password"
+#define WHATSAPP_KEY_IDX "DeviceID"
+#define WHATSAPP_KEY_MAP_STATUSES "MapStatuses"
+#define WHATSAPP_KEY_LOGGING_ENABLE "LoggingEnable"
+#define WHATSAPP_KEY_NAME "RealName"
+#define WHATSAPP_KEY_PUSH_NAME "Nick"
+#define WHATSAPP_KEY_LAST_SEEN "LastSeen"
+#define WHATSAPP_KEY_LAST_MSG_ID_HEADER "LastMsgIdHeader"
+#define WHATSAPP_KEY_LAST_MSG_ID "LastMsgId"
+#define WHATSAPP_KEY_LAST_MSG_STATE "LastMsgState"
+#define WHATSAPP_KEY_AVATAR_ID "AvatarId"
+#define WHATSAPP_KEY_SYSTRAY_NOTIFY "UseSystrayNotify"
+#define WHATSAPP_KEY_DEF_GROUP "DefaultGroup"
+
+#define WHATSAPP_KEY_EVENT_CLIENT_ENABLE "EventClientEnable"
+#define WHATSAPP_KEY_EVENT_OTHER_ENABLE "EventOtherEnable"
+
+#define WHATSAPP_KEY_EVENT_CLIENT_COLBACK "PopupClientColorBack"
+#define WHATSAPP_KEY_EVENT_CLIENT_COLTEXT "PopupClientColorText"
+#define WHATSAPP_KEY_EVENT_CLIENT_TIMEOUT "PopupClientTimeout"
+#define WHATSAPP_KEY_EVENT_CLIENT_DEFAULT "PopupClientColorDefault"
+
+#define WHATSAPP_KEY_EVENT_OTHER_COLBACK "PopupOtherColorBack"
+#define WHATSAPP_KEY_EVENT_OTHER_COLTEXT "PopupOtherColorText"
+#define WHATSAPP_KEY_EVENT_OTHER_TIMEOUT "PopupOtherTimeout"
+#define WHATSAPP_KEY_EVENT_OTHER_DEFAULT "PopupOtherColorDefault"
+
diff --git a/protocols/WhatsApp/src/definitions.h b/protocols/WhatsApp/src/definitions.h new file mode 100644 index 0000000000..4e24139d67 --- /dev/null +++ b/protocols/WhatsApp/src/definitions.h @@ -0,0 +1,18 @@ +#if !defined(DEFINITIONS_H)
+#define DEFINITIONS_H
+
+#define FLAG_CONTAINS(x,y) ( ( x & y ) == y )
+#define REMOVE_FLAG(x,y) ( x = ( x & ~y ))
+
+#define LOG(fmt, ...) Log(__FUNCTION__, fmt, ##__VA_ARGS__)
+#define CODE_BLOCK_TRY try {
+#define CODE_BLOCK_CATCH(ex) } catch (ex& e) { LOG("Exception: %s", e.what());
+#define CODE_BLOCK_CATCH_UNKNOWN } catch (...) { LOG("Unknown exception");
+#define CODE_BLOCK_CATCH_ALL } catch (WAException& e) { LOG("Exception: %s", e.what()); \
+ } catch (exception& e) { LOG("Exception: %s", e.what()); \
+ } catch (...) { LOG("Unknown exception"); }
+#define CODE_BLOCK_END }
+
+#define NIIF_INTERN_TCHAR NIIF_INTERN_UNICODE
+
+#endif
\ No newline at end of file diff --git a/protocols/WhatsApp/src/dialogs.cpp b/protocols/WhatsApp/src/dialogs.cpp new file mode 100644 index 0000000000..81a17351fb --- /dev/null +++ b/protocols/WhatsApp/src/dialogs.cpp @@ -0,0 +1,179 @@ +#include "common.h"
+
+INT_PTR CALLBACK WhatsAppAccountProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
+{
+ WhatsAppProto *proto;
+
+ switch ( message )
+ {
+
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwnd);
+
+ proto = reinterpret_cast<WhatsAppProto*>(lparam);
+ SetWindowLongPtr(hwnd,GWLP_USERDATA,lparam);
+
+ DBVARIANT dbv;
+
+ if ( !db_get_s(0,proto->ModuleName(),WHATSAPP_KEY_CC,&dbv,DBVT_ASCIIZ))
+ {
+ SetDlgItemTextA(hwnd,IDC_CC,dbv.pszVal);
+ db_free(&dbv);
+ }
+
+ if ( !db_get_s(0,proto->ModuleName(),WHATSAPP_KEY_LOGIN,&dbv,DBVT_ASCIIZ))
+ {
+ SetDlgItemTextA(hwnd,IDC_LOGIN,dbv.pszVal);
+ db_free(&dbv);
+ }
+
+ if ( !db_get_s(0,proto->ModuleName(),WHATSAPP_KEY_NICK,&dbv,DBVT_ASCIIZ))
+ {
+ SetDlgItemTextA(hwnd,IDC_NICK,dbv.pszVal);
+ db_free(&dbv);
+ }
+
+ if ( !db_get_s(0,proto->ModuleName(),WHATSAPP_KEY_PASS,&dbv,DBVT_ASCIIZ))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING,strlen(dbv.pszVal)+1,
+ reinterpret_cast<LPARAM>(dbv.pszVal));
+ SetDlgItemTextA(hwnd,IDC_PW,dbv.pszVal);
+ db_free(&dbv);
+ }
+
+
+ if (!proto->isOffline()) {
+ SendMessage(GetDlgItem(hwnd,IDC_CC),EM_SETREADONLY,1,0);
+ SendMessage(GetDlgItem(hwnd,IDC_LOGIN),EM_SETREADONLY,1,0);
+ SendMessage(GetDlgItem(hwnd,IDC_NICK),EM_SETREADONLY,1,0);
+ SendMessage(GetDlgItem(hwnd,IDC_PW),EM_SETREADONLY,1,0);
+ }
+
+ return TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wparam) == IDC_BUTTON_REQUEST_CODE)
+ {
+ if (MessageBox(NULL, _T("An SMS with registration-code will be sent to your mobile phone.\nNotice that you are not able to use the real WhatsApp and this plugin simultaneousley!\nContinue?"),
+ PRODUCT_NAME, MB_YESNO) == IDYES)
+ {
+ proto = reinterpret_cast<WhatsAppProto*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));
+ proto->RequestCode();
+ }
+ }
+
+ if ( HIWORD( wparam ) == EN_CHANGE && reinterpret_cast<HWND>(lparam) == GetFocus())
+ {
+ switch(LOWORD(wparam))
+ {
+ case IDC_CC:
+ case IDC_LOGIN:
+ case IDC_NICK:
+ case IDC_PW:
+ SendMessage(GetParent(hwnd),PSM_CHANGED,0,0);
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ if ( reinterpret_cast<NMHDR*>(lparam)->code == PSN_APPLY )
+ {
+ proto = reinterpret_cast<WhatsAppProto*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));
+ char str[128];
+
+ GetDlgItemTextA(hwnd, IDC_CC, str, sizeof(str));
+ db_set_s(0,proto->ModuleName(),WHATSAPP_KEY_CC,str);
+
+ GetDlgItemTextA(hwnd, IDC_LOGIN, str, sizeof(str));
+ db_set_s(0,proto->ModuleName(),WHATSAPP_KEY_LOGIN,str);
+
+ GetDlgItemTextA(hwnd, IDC_NICK, str, sizeof(str));
+ db_set_s(0,proto->ModuleName(),WHATSAPP_KEY_NICK,str);
+
+ GetDlgItemTextA(hwnd,IDC_PW,str,sizeof(str));
+ CallService(MS_DB_CRYPT_ENCODESTRING,sizeof(str),reinterpret_cast<LPARAM>(str));
+ db_set_s(0,proto->ModuleName(),WHATSAPP_KEY_PASS,str);
+
+ return TRUE;
+ }
+ break;
+
+ }
+
+ return FALSE;
+
+}
+
+INT_PTR CALLBACK WhatsAppInputBoxProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
+{
+ input_box* ib;
+
+ switch(message)
+ {
+
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwnd);
+
+ ib = reinterpret_cast<input_box*>(lparam);
+ SetWindowLongPtr(hwnd,GWLP_USERDATA,lparam);
+ SendDlgItemMessage(hwnd,IDC_VALUE,EM_LIMITTEXT,ib->limit,0);
+ SetDlgItemText(hwnd, IDC_VALUE, ib->defaultValue.c_str());
+ SetDlgItemText(hwnd, IDC_TEXT, ib->text.c_str());
+
+ /*
+ DBVARIANT dbv = { DBVT_TCHAR };
+ if (!DBGetContactSettingTString(NULL,ib->proto->m_szModuleName,WHATSAPP_KEY_PUSH_NAME,&dbv))
+ {
+ SetWindowText( hwnd, dbv.ptszVal );
+ db_free( &dbv );
+ }
+ */
+ SetWindowText(hwnd, ib->title.c_str());
+ }
+
+ EnableWindow(GetDlgItem( hwnd, IDC_OK ), FALSE);
+ return TRUE;
+
+ case WM_COMMAND:
+ if ( LOWORD( wparam ) == IDC_VALUE && HIWORD( wparam ) == EN_CHANGE )
+ {
+ ib = reinterpret_cast<input_box*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));
+ size_t len = SendDlgItemMessage(hwnd,IDC_VALUE,WM_GETTEXTLENGTH,0,0);
+ TCHAR str[4];
+ _sntprintf( str, 4, TEXT( "%d" ), ib->limit - len );
+ //SetDlgItemText(hwnd,IDC_CHARACTERS,str);
+
+ EnableWindow(GetDlgItem( hwnd, IDC_OK ), len > 0);
+ return TRUE;
+ }
+ else if ( LOWORD( wparam ) == IDC_OK )
+ {
+ ib = reinterpret_cast<input_box*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));
+ TCHAR* value = new TCHAR[ib->limit+1];
+
+ GetDlgItemText(hwnd,IDC_VALUE,value, ib->limit + 1);
+ ShowWindow(hwnd,SW_HIDE);
+
+ input_box_ret* ret = new input_box_ret;
+ ret->userData = ib->userData;
+ ret->value = mir_utf8encodeT(value);
+ delete value;
+ ForkThread(ib->thread, ib->proto, ret);
+ EndDialog(hwnd, wparam);
+ delete ib;
+ return TRUE;
+ }
+ else if ( LOWORD( wparam ) == IDC_CANCEL )
+ {
+ EndDialog(hwnd, wparam);
+ ib = reinterpret_cast<input_box*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));
+ delete ib;
+ return TRUE;
+ }
+ break;
+
+ }
+
+ return FALSE;
+}
\ No newline at end of file diff --git a/protocols/WhatsApp/src/dialogs.h b/protocols/WhatsApp/src/dialogs.h new file mode 100644 index 0000000000..8b34cd0854 --- /dev/null +++ b/protocols/WhatsApp/src/dialogs.h @@ -0,0 +1,7 @@ +#if !defined(DIALOGS_H)
+#define DIALOGS_H
+
+INT_PTR CALLBACK WhatsAppAccountProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
+INT_PTR CALLBACK WhatsAppInputBoxProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
+
+#endif
\ No newline at end of file diff --git a/protocols/WhatsApp/src/entities.h b/protocols/WhatsApp/src/entities.h new file mode 100644 index 0000000000..d48fbfb573 --- /dev/null +++ b/protocols/WhatsApp/src/entities.h @@ -0,0 +1,41 @@ +#if !defined(ENTITIES_H)
+#define ENTITIES_H
+
+struct send_direct
+{
+ send_direct(HANDLE hContact,const std::string &msg, HANDLE msgid, bool isChat = false)
+ : hContact(hContact), msg(msg), msgid(msgid)
+ {}
+ HANDLE hContact;
+ std::string msg;
+ HANDLE msgid;
+};
+
+struct send_typing
+{
+ send_typing(HANDLE hContact,const int status) : hContact(hContact), status(status) {}
+ HANDLE hContact;
+ int status;
+};
+
+struct input_box
+{
+ tstring text;
+ tstring title;
+ tstring defaultValue;
+ int limit;
+
+ void (__cdecl WhatsAppProto::*thread)(void*);
+ WhatsAppProto* proto;
+ void* userData;
+};
+
+struct input_box_ret // has to be deleted by input-box handler
+{
+ void* userData; // has to be deleted by input-box handler
+ char* value; // mir_free() has to be called by input-box handler
+};
+
+
+
+#endif // ENTITIES_H
\ No newline at end of file diff --git a/protocols/WhatsApp/src/main.cpp b/protocols/WhatsApp/src/main.cpp new file mode 100644 index 0000000000..2545bb6736 --- /dev/null +++ b/protocols/WhatsApp/src/main.cpp @@ -0,0 +1,126 @@ +#include "common.h"
+
+CLIST_INTERFACE* pcli;
+int hLangpack;
+
+HINSTANCE g_hInstance;
+std::string g_strUserAgent;
+DWORD g_mirandaVersion;
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ "WhatsApp Protocol",
+ __VERSION_DWORD,
+ "Provides basic support for WhatsApp. [Built: "__DATE__" "__TIME__"]",
+ "Uli Hecht",
+ "uli.hecht@gmail.com",
+ "© 2013 Uli Hecht",
+ "http://example.com",
+ UNICODE_AWARE, //not transient
+ // {4f1ff7fa-4d75-44b9-93b0-2ced2e4f9e3e}
+ { 0x4f1ff7fa, 0x4d75, 0x44b9, { 0x93, 0xb0, 0x2c, 0xed, 0x2e, 0x4f, 0x9e, 0x3e } }
+
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Protocol instances
+static int compare_protos(const WhatsAppProto *p1, const WhatsAppProto *p2)
+{
+ return _tcscmp(p1->m_tszUserName, p2->m_tszUserName);
+}
+
+OBJLIST<WhatsAppProto> g_Instances(1, compare_protos);
+
+DWORD WINAPI DllMain(HINSTANCE hInstance,DWORD,LPVOID)
+{
+ g_hInstance = hInstance;
+ return TRUE;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ g_mirandaVersion = mirandaVersion;
+ return &pluginInfo;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Interface information
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_PROTOCOL, MIID_LAST};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load
+
+static PROTO_INTERFACE* protoInit(const char *proto_name,const TCHAR *username )
+{
+ WhatsAppProto *proto = new WhatsAppProto(proto_name,username);
+ g_Instances.insert(proto);
+ return proto;
+}
+
+static int protoUninit(PROTO_INTERFACE* proto)
+{
+ g_Instances.remove(( WhatsAppProto* )proto);
+ return EXIT_SUCCESS;
+}
+
+static HANDLE g_hEvents[1];
+
+extern "C" int __declspec(dllexport) Load(void)
+{
+ mir_getLP(&pluginInfo);
+
+ pcli = reinterpret_cast<CLIST_INTERFACE*>( CallService(
+ MS_CLIST_RETRIEVE_INTERFACE,0,reinterpret_cast<LPARAM>(g_hInstance)));
+
+ PROTOCOLDESCRIPTOR pd = { sizeof(pd) };
+ pd.szName = "WhatsApp";
+ pd.type = PROTOTYPE_PROTOCOL;
+ pd.fnInit = protoInit;
+ pd.fnUninit = protoUninit;
+ CallService(MS_PROTO_REGISTERMODULE,0,reinterpret_cast<LPARAM>(&pd));
+
+ InitIcons();
+ //InitContactMenus();
+
+ // Init native User-Agent
+ {
+ std::stringstream agent;
+// DWORD mir_ver = ( DWORD )CallService( MS_SYSTEM_GETVERSION, NULL, NULL );
+ agent << "MirandaNG/";
+ agent << (( g_mirandaVersion >> 24) & 0xFF);
+ agent << ".";
+ agent << (( g_mirandaVersion >> 16) & 0xFF);
+ agent << ".";
+ agent << (( g_mirandaVersion >> 8) & 0xFF);
+ agent << ".";
+ agent << (( g_mirandaVersion ) & 0xFF);
+ #ifdef _WIN64
+ agent << " WhatsApp Protocol x64/";
+ #else
+ agent << " WhatsApp Protocol/";
+ #endif
+ agent << __VERSION_STRING;
+ g_strUserAgent = agent.str( );
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Unload
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ //UninitContactMenus();
+ for(size_t i=0; i<SIZEOF(g_hEvents); i++)
+ UnhookEvent(g_hEvents[i]);
+
+ g_Instances.destroy();
+
+ delete FMessage::generating_lock;
+ WASocketConnection::quitNetwork();
+
+ return 0;
+}
diff --git a/protocols/WhatsApp/src/messages.cpp b/protocols/WhatsApp/src/messages.cpp new file mode 100644 index 0000000000..1c77454c97 --- /dev/null +++ b/protocols/WhatsApp/src/messages.cpp @@ -0,0 +1,200 @@ +#include "common.h"
+
+int WhatsAppProto::RecvMsg(HANDLE hContact, PROTORECVEVENT *pre)
+{
+ CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact, (LPARAM)PROTOTYPE_CONTACTTYPING_OFF);
+
+ return Proto_RecvMessage(hContact, pre);
+}
+
+void WhatsAppProto::onMessageForMe(FMessage* paramFMessage, bool paramBoolean)
+{
+ bool isChatRoom = !paramFMessage->remote_resource.empty();
+
+ std::string* msg;
+ switch (paramFMessage->media_wa_type)
+ {
+ case FMessage::WA_TYPE_IMAGE:
+ case FMessage::WA_TYPE_AUDIO:
+ case FMessage::WA_TYPE_VIDEO:
+ msg = ¶mFMessage->media_url;
+ break;
+ default:
+ msg = ¶mFMessage->data;
+ }
+
+ if (isChatRoom)
+ {
+ msg->insert(0, std::string("[").append(paramFMessage->notifyname).append("]: "));
+ }
+
+ HANDLE hContact = this->AddToContactList(paramFMessage->key->remote_jid, 0, false,
+ isChatRoom ? NULL : paramFMessage->notifyname.c_str(), isChatRoom);
+
+ PROTORECVEVENT recv = {0};
+ recv.flags = PREF_UTF;
+ recv.szMessage = const_cast<char*>(msg->c_str());
+ recv.timestamp = paramFMessage->timestamp; //time(NULL);
+ ProtoChainRecvMsg(hContact, &recv);
+
+ this->connection->sendMessageReceived(paramFMessage);
+}
+
+int WhatsAppProto::SendMsg(HANDLE hContact, int flags, const char *msg)
+{
+ LOG("");
+ int msgId = ++(this->msgId);
+
+ ForkThread( &WhatsAppProto::SendMsgWorker, this, new send_direct(hContact, msg, (HANDLE) msgId, flags & IS_CHAT));
+ return this->msgIdHeader + msgId;
+}
+
+void WhatsAppProto::SendMsgWorker(void* p)
+{
+ LOG("");
+ if (p == NULL)
+ return;
+
+ DBVARIANT dbv;
+ send_direct *data = static_cast<send_direct*>(p);
+
+ if (db_get_b(data->hContact, m_szModuleName, "SimpleChatRoom", 0) > 0 &&
+ db_get_b(data->hContact, m_szModuleName, "IsGroupMember", 0) == 0)
+ {
+ LOG("not a group member");
+ ProtoBroadcastAck(m_szModuleName,data->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED,
+ (HANDLE) (this->msgIdHeader + this->msgId), (LPARAM) "You cannot send messages to groups if you are not a member.");
+ }
+ else if (!db_get_s(data->hContact, m_szModuleName,"ID", &dbv, DBVT_ASCIIZ) &&
+ this->connection != NULL)
+ {
+ try
+ {
+ db_set_dw(data->hContact, m_szModuleName, WHATSAPP_KEY_LAST_MSG_STATE, 2);
+ db_set_dw(data->hContact, m_szModuleName, WHATSAPP_KEY_LAST_MSG_ID_HEADER, this->msgIdHeader);
+ db_set_dw(data->hContact, m_szModuleName, WHATSAPP_KEY_LAST_MSG_ID, this->msgId);
+
+ std::stringstream ss;
+ ss << this->msgIdHeader << "-" << this->msgId;
+ Key* key = new Key(Key(dbv.pszVal, true, ss.str())); // deleted by FMessage
+ FMessage fmsg(key);
+ fmsg.data = data->msg;
+ fmsg.timestamp = time(NULL);
+
+ db_free(&dbv);
+
+ this->connection->sendMessage(&fmsg);
+ }
+ catch (exception &e)
+ {
+ LOG("exception: %s", e.what());
+ ProtoBroadcastAck(m_szModuleName,data->hContact,ACKTYPE_MESSAGE,ACKRESULT_FAILED,
+ (HANDLE) (this->msgIdHeader + this->msgId), (LPARAM) e.what());
+ }
+ catch (...)
+ {
+ LOG("unknown exception");
+ ProtoBroadcastAck(m_szModuleName,data->hContact,ACKTYPE_MESSAGE,ACKRESULT_FAILED,
+ (HANDLE) (this->msgIdHeader + this->msgId), (LPARAM) "Failed sending message");
+ }
+ }
+ else
+ {
+ LOG("No connection");
+ ProtoBroadcastAck(m_szModuleName,data->hContact,ACKTYPE_MESSAGE,ACKRESULT_FAILED,
+ (HANDLE) (this->msgIdHeader + this->msgId), (LPARAM) "You cannot send messages when you are offline.");
+ }
+
+ delete data;
+}
+
+void WhatsAppProto::RecvMsgWorker(void *p)
+{
+ if (p == NULL)
+ return;
+
+ //WAConnection.cpp l1225 - message will be deleted. We cannot send the ack inside a thread!
+ //FMessage *fmsg = static_cast<FMessage*>(p);
+ //this->connection->sendMessageReceived(fmsg);
+
+ //delete fmsg;
+}
+
+void WhatsAppProto::onIsTyping(const std::string& paramString, bool paramBoolean)
+{
+ HANDLE hContact = this->AddToContactList(paramString, 0, false);
+ if (hContact != NULL)
+ {
+ CallService(MS_PROTO_CONTACTISTYPING, (WPARAM) hContact, (LPARAM)
+ paramBoolean ? PROTOTYPE_CONTACTTYPING_INFINITE : PROTOTYPE_CONTACTTYPING_OFF);
+ }
+}
+
+
+int WhatsAppProto::UserIsTyping(HANDLE hContact,int type)
+{
+ if (hContact && isOnline())
+ ForkThread(&WhatsAppProto::SendTypingWorker, this, new send_typing(hContact, type));
+
+ return 0;
+}
+
+void WhatsAppProto::SendTypingWorker(void* p)
+{
+ if(p == NULL)
+ return;
+
+ send_typing *typing = static_cast<send_typing*>(p);
+
+ // Don't send typing notifications to contacts which are offline
+ if ( db_get_w(typing->hContact,m_szModuleName,"Status", 0) == ID_STATUS_OFFLINE)
+ return;
+
+ DBVARIANT dbv;
+ if ( !db_get_s(typing->hContact,m_szModuleName,WHATSAPP_KEY_ID,&dbv, DBVT_ASCIIZ) &&
+ this->isOnline())
+ {
+ if (typing->status == PROTOTYPE_SELFTYPING_ON) {
+ this->connection->sendComposing(dbv.pszVal);
+ } else {
+ this->connection->sendPaused(dbv.pszVal);
+ }
+ }
+
+ delete typing;
+}
+
+void WhatsAppProto::onMessageStatusUpdate(FMessage* fmsg)
+{
+ LOG("");
+
+ HANDLE hContact = this->ContactIDToHContact(fmsg->key->remote_jid);
+ if (hContact == 0)
+ return;
+
+ int state = 5 - fmsg->status;
+ if (state != 0 && state != 1)
+ return;
+
+ int header;
+ int id;
+ int delimPos = fmsg->key->id.find("-");
+
+ std::stringstream ss;
+ ss << fmsg->key->id.substr(0, delimPos);
+ ss >> header;
+
+ ss.clear();
+ ss << fmsg->key->id.substr(delimPos + 1);
+ ss >> id;
+
+ if (state == 1)
+ ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE) (header + id),0);
+
+ if (db_get_dw(hContact, m_szModuleName, WHATSAPP_KEY_LAST_MSG_ID_HEADER, 0) == header &&
+ db_get_dw(hContact, m_szModuleName, WHATSAPP_KEY_LAST_MSG_ID, -1) == id)
+ {
+ db_set_dw(hContact, m_szModuleName, WHATSAPP_KEY_LAST_MSG_STATE, state);
+ this->UpdateStatusMsg(hContact);
+ }
+}
\ No newline at end of file diff --git a/protocols/WhatsApp/src/proto.cpp b/protocols/WhatsApp/src/proto.cpp new file mode 100644 index 0000000000..99a903226f --- /dev/null +++ b/protocols/WhatsApp/src/proto.cpp @@ -0,0 +1,446 @@ +#include "common.h"
+
+WhatsAppProto::WhatsAppProto(const char* proto_name, const TCHAR* username)
+{
+ ProtoConstructor(this, proto_name, username);
+
+ this->challenge = new std::vector<unsigned char>;
+ this->msgId = 0;
+ this->msgIdHeader = time(NULL);
+
+ update_loop_lock_ = CreateEvent(NULL, false, false, NULL);
+ FMessage::generating_lock = new Mutex();
+
+ CreateProtoService(m_szModuleName, PS_CREATEACCMGRUI, &WhatsAppProto::SvcCreateAccMgrUI, this);
+ CreateProtoService(m_szModuleName, PS_JOINCHAT, &WhatsAppProto::OnJoinChat, this);
+ CreateProtoService(m_szModuleName, PS_LEAVECHAT, &WhatsAppProto::OnLeaveChat, this);
+
+ HookProtoEvent(ME_CLIST_PREBUILDSTATUSMENU, &WhatsAppProto::OnBuildStatusMenu, this);
+ HookProtoEvent(ME_GC_EVENT, &WhatsAppProto::OnChatOutgoing, this);
+
+ this->InitContactMenus();
+
+ // Create standard network connection
+ TCHAR descr[512];
+ NETLIBUSER nlu = {sizeof(nlu)};
+ nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS;
+ char module[512];
+ mir_snprintf(module, SIZEOF(module), "%s", m_szModuleName);
+ nlu.szSettingsModule = module;
+ mir_sntprintf(descr, SIZEOF(descr), TranslateT("%s server connection"), m_tszUserName);
+ nlu.ptszDescriptiveName = descr;
+ m_hNetlibUser = (HANDLE) CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM) &nlu);
+ if (m_hNetlibUser == NULL)
+ MessageBox(NULL, TranslateT("Unable to get Netlib connection for WhatsApp"), m_tszUserName, MB_OK);
+
+ WASocketConnection::initNetwork(m_hNetlibUser);
+
+ TCHAR *profile = Utils_ReplaceVarsT( _T("%miranda_avatarcache%"));
+ def_avatar_folder_ = std::tstring(profile) + _T("\\") + m_tszUserName;
+ mir_free(profile);
+ hAvatarFolder_ = FoldersRegisterCustomPathT(m_szModuleName, "Avatars", def_avatar_folder_.c_str());
+
+ // Register group chat
+ GCREGISTER gcr = {0};
+ gcr.cbSize = sizeof(GCREGISTER);
+ gcr.dwFlags = GC_TYPNOTIF | GC_CHANMGR | GC_TCHAR;
+ gcr.iMaxText = 0;
+ gcr.nColors = 0;
+ gcr.pColors = NULL; //(COLORREF*)crCols;
+ gcr.ptszModuleDispName = m_tszUserName;
+ gcr.pszModule = m_szModuleName;
+ CallServiceSync(MS_GC_REGISTER, 0, (LPARAM)&gcr);
+
+ //HookProtoEvent(ME_GC_EVENT, &CMsnProto::MSN_GCEventHook);
+ //HookProtoEvent(ME_GC_BUILDMENU, &CMsnProto::MSN_GCMenuHook);
+
+ SetAllContactStatuses(ID_STATUS_OFFLINE, true);
+}
+
+WhatsAppProto::~WhatsAppProto()
+{
+ CloseHandle(update_loop_lock_);
+
+ if (this->challenge != NULL)
+ delete this->challenge;
+
+ ProtoDestructor(this);
+}
+
+DWORD_PTR WhatsAppProto::GetCaps( int type, HANDLE hContact )
+{
+ switch(type)
+ {
+ case PFLAGNUM_1:
+ {
+ DWORD_PTR flags = PF1_IM | PF1_CHAT | PF1_BASICSEARCH | PF1_ADDSEARCHRES;
+ return flags | PF1_MODEMSGRECV; // #TODO
+ }
+ case PFLAGNUM_2:
+ return PF2_ONLINE | PF2_INVISIBLE;
+ case PFLAGNUM_3:
+ return 0;
+ case PFLAGNUM_4:
+ return PF4_NOCUSTOMAUTH | PF4_IMSENDUTF | PF4_FORCEADDED | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE | PF4_NOAUTHDENYREASON | PF4_SUPPORTTYPING | PF4_AVATARS;
+ case PFLAGNUM_5:
+ return 0;
+ case PFLAG_MAXLENOFMESSAGE:
+ return 500; // #TODO
+ case PFLAG_UNIQUEIDTEXT:
+ return (DWORD_PTR) "WhatsApp ID";
+ case PFLAG_UNIQUEIDSETTING:
+ return (DWORD_PTR) "ID";
+ }
+ return 0;
+}
+
+int WhatsAppProto::SetStatus( int new_status )
+{
+ LOG("===== Beginning SetStatus process");
+
+ // Routing statuses not supported by WhatsApp
+ switch ( new_status )
+ {
+ case ID_STATUS_INVISIBLE:
+ case ID_STATUS_OFFLINE:
+ m_iDesiredStatus = new_status;
+ break;
+
+ /*
+ case ID_STATUS_CONNECTING:
+ m_iDesiredStatus = ID_STATUS_OFFLINE;
+ break;
+ */
+
+ case ID_STATUS_IDLE:
+ default:
+ m_iDesiredStatus = ID_STATUS_INVISIBLE;
+ if (db_get_b(NULL, m_szModuleName, WHATSAPP_KEY_MAP_STATUSES, DEFAULT_MAP_STATUSES))
+ break;
+ case ID_STATUS_ONLINE:
+ case ID_STATUS_FREECHAT:
+ m_iDesiredStatus = ID_STATUS_ONLINE;
+ break;
+ }
+
+ if (m_iStatus == ID_STATUS_CONNECTING)
+ {
+ LOG("===== Status is connecting, no change");
+ return 0;
+ }
+
+ if (m_iStatus == m_iDesiredStatus)
+ {
+ LOG("===== Statuses are same, no change");
+ return 0;
+ }
+
+ ForkThread( &WhatsAppProto::ChangeStatus, this );
+
+ return 0;
+}
+
+HANDLE WhatsAppProto::AddToList( int flags, PROTOSEARCHRESULT* psr )
+{
+ return NULL;
+}
+
+int WhatsAppProto::AuthRequest(HANDLE hContact,const PROTOCHAR *message)
+{
+ return this->RequestFriendship((WPARAM)hContact, NULL);
+}
+
+int WhatsAppProto::Authorize(HANDLE hDbEvent)
+{
+ return 1;
+}
+
+HANDLE WhatsAppProto::SearchBasic( const PROTOCHAR* id )
+{
+ if (isOffline())
+ return 0;
+
+ TCHAR* email = mir_tstrdup(id);
+ ForkThread(&WhatsAppProto::SearchAckThread, this, (void*)email);
+
+ return email;
+}
+
+void WhatsAppProto::RequestCode()
+{
+ std::string cc, number, idx;
+ bool error;
+ DBVARIANT dbv;
+
+ if ( WASocketConnection::hNetlibUser == NULL)
+ {
+ NotifyEvent(m_tszUserName,TranslateT("Network-connection error."),NULL,WHATSAPP_EVENT_CLIENT);
+ return;
+ }
+
+ if ( !db_get_s(NULL,m_szModuleName,WHATSAPP_KEY_IDX,&dbv,DBVT_ASCIIZ))
+ {
+ idx = dbv.pszVal;
+ if (idx.empty())
+ {
+ std::stringstream tm;
+ tm << time(NULL);
+ unsigned char* idxBuf = new unsigned char[16];
+ MD5((unsigned char*) tm.str().c_str(), tm.str().length(), idxBuf);
+ idx = std::string((const char*) idxBuf, 16);
+ db_set_s(0, m_szModuleName,WHATSAPP_KEY_IDX, idx.c_str());
+ delete idxBuf;
+ }
+ }
+ if ( !db_get_s(NULL,m_szModuleName,WHATSAPP_KEY_CC,&dbv, DBVT_ASCIIZ))
+ {
+ cc = dbv.pszVal;
+ db_free(&dbv);
+ if (cc.empty())
+ {
+ NotifyEvent(m_tszUserName,TranslateT("Please enter a country-code."),NULL,WHATSAPP_EVENT_CLIENT);
+ return;
+ }
+ }
+ if ( !db_get_s(NULL,m_szModuleName,WHATSAPP_KEY_LOGIN,&dbv, DBVT_ASCIIZ))
+ {
+ number = dbv.pszVal;
+ db_free(&dbv);
+ if (number.empty())
+ {
+ NotifyEvent(m_tszUserName,TranslateT("Please enter a phone-number without country-code."),NULL,WHATSAPP_EVENT_CLIENT);
+ return;
+ }
+ }
+
+ std::string token(Utilities::md5String(std::string(ACCOUNT_TOKEN_PREFIX1) + ACCOUNT_TOKEN_PREFIX2 + number));
+
+ NETLIBHTTPHEADER agentHdr;
+ agentHdr.szName = "User-Agent";
+ agentHdr.szValue = ACCOUNT_USER_AGENT_REGISTRATION;
+
+ NETLIBHTTPHEADER acceptHdr;
+ acceptHdr.szName = "Accept";
+ acceptHdr.szValue = "text/json";
+
+ NETLIBHTTPHEADER ctypeHdr;
+ ctypeHdr.szName = "Content-Type";
+ ctypeHdr.szValue = "application/x-www-form-urlencoded";
+
+ NETLIBHTTPHEADER headers[] = { agentHdr, acceptHdr, ctypeHdr };
+
+ NETLIBHTTPREQUEST nlhr = {sizeof(NETLIBHTTPREQUEST)};
+ nlhr.requestType = REQUEST_POST;
+ nlhr.szUrl = (char*) (std::string(ACCOUNT_URL_CODEREQUESTV2) + "?cc="+ cc + "&in="+ number +
+ "lc=US&lg=en&mcc=000&mnc=000&method=sms&id=" + idx + "&token="+ token).c_str();
+ nlhr.headers = &headers[0];
+ nlhr.headersCount = 3;
+ nlhr.flags = NLHRF_HTTP11 | NLHRF_GENERATEHOST | NLHRF_REMOVEHOST;
+
+ return;
+
+ NETLIBHTTPREQUEST* pnlhr = (NETLIBHTTPREQUEST*) CallService(MS_NETLIB_HTTPTRANSACTION,
+ (WPARAM) WASocketConnection::hNetlibUser, (LPARAM)&nlhr);
+
+ // #TODO
+}
+
+void WhatsAppProto::RegisterCode(const std::string& code)
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// EVENTS
+
+int WhatsAppProto::SvcCreateAccMgrUI(WPARAM wParam,LPARAM lParam)
+{
+ return (int)CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_WHATSAPPACCOUNT),
+ (HWND)lParam, WhatsAppAccountProc, (LPARAM)this );
+}
+
+int WhatsAppProto::RefreshBuddyList(WPARAM, LPARAM )
+{
+ LOG("");
+ if (!isOffline())
+ {
+ //ForkThread(
+ }
+ return 0;
+}
+
+int WhatsAppProto::RequestFriendship(WPARAM wParam, LPARAM lParam)
+{
+ if (wParam == NULL || isOffline())
+ return 0;
+
+ HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
+
+ DBVARIANT dbv;
+ if ( !db_get_s(hContact,m_szModuleName,WHATSAPP_KEY_ID,&dbv, DBVT_ASCIIZ))
+ {
+ std::string id(dbv.pszVal);
+ this->connection->sendQueryLastOnline(id);
+ this->connection->sendPresenceSubscriptionRequest(id);
+ db_free(&dbv);
+ }
+
+ return 0;
+}
+
+std::tstring WhatsAppProto::GetAvatarFolder()
+{
+ TCHAR path[MAX_PATH];
+ if ( hAvatarFolder_ && FoldersGetCustomPathT(hAvatarFolder_, path, SIZEOF(path), _T("")) == 0 )
+ return path;
+ else
+ return def_avatar_folder_;
+}
+
+int WhatsAppProto::Log(const char* fn, const char *fmt,...)
+{
+#if !defined(_DEBUG)
+ if (!getByte(WHATSAPP_KEY_LOGGING_ENABLE, 0))
+ return EXIT_SUCCESS;
+#endif
+
+ va_list va;
+ char text[65535];
+ ScopedLock s(log_lock_);
+
+ va_start(va,fmt);
+ mir_vsnprintf(text,sizeof(text),fmt,va);
+ va_end(va);
+
+ // Write into network log
+ //CallService(MS_NETLIB_LOG, (WPARAM)m_hNetlibUser, (LPARAM)text);
+
+ // Write into log file
+ return utils::debug::log(m_szModuleName, std::string(fn).append(" - ").append(text));
+}
+
+LRESULT CALLBACK PopupDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message)
+ {
+ case WM_COMMAND:
+ {
+ // After a click, destroy popup
+ PUDeletePopup(hwnd);
+ } break;
+
+ case WM_CONTEXTMENU:
+ PUDeletePopup(hwnd);
+ break;
+
+ case UM_FREEPLUGINDATA:
+ {
+ // After close, free
+ TCHAR* url = (TCHAR*)PUGetPluginData(hwnd);
+ if (url != NULL)
+ mir_free(url);
+ } return FALSE;
+
+ default:
+ break;
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+};
+
+void WhatsAppProto::NotifyEvent(const string& title, const string& info, HANDLE contact, DWORD flags, TCHAR* url)
+{
+ TCHAR* rawTitle = mir_a2t_cp(title.c_str(), CP_UTF8);
+ TCHAR* rawInfo = mir_a2t_cp(info.c_str(), CP_UTF8);
+ this->NotifyEvent(rawTitle, rawInfo, contact, flags, url);
+ mir_free(rawTitle);
+ mir_free(rawInfo);
+}
+
+void WhatsAppProto::NotifyEvent(TCHAR* title, TCHAR* info, HANDLE contact, DWORD flags, TCHAR* szUrl)
+{
+ int ret; int timeout; COLORREF colorBack = 0; COLORREF colorText = 0;
+
+ switch ( flags )
+ {
+ case WHATSAPP_EVENT_CLIENT:
+ if ( !getByte( WHATSAPP_KEY_EVENT_CLIENT_ENABLE, DEFAULT_EVENT_CLIENT_ENABLE ))
+ goto exit;
+ if ( !getByte( WHATSAPP_KEY_EVENT_CLIENT_DEFAULT, 0 ))
+ {
+ colorBack = getDword( WHATSAPP_KEY_EVENT_CLIENT_COLBACK, DEFAULT_EVENT_COLBACK );
+ colorText = getDword( WHATSAPP_KEY_EVENT_CLIENT_COLTEXT, DEFAULT_EVENT_COLTEXT );
+ }
+ timeout = getDword( WHATSAPP_KEY_EVENT_CLIENT_TIMEOUT, 0 );
+ flags |= NIIF_WARNING;
+ break;
+
+ case WHATSAPP_EVENT_OTHER:
+ if ( !getByte( WHATSAPP_KEY_EVENT_OTHER_ENABLE, DEFAULT_EVENT_OTHER_ENABLE ))
+ goto exit;
+ if ( !getByte( WHATSAPP_KEY_EVENT_OTHER_DEFAULT, 0 ))
+ {
+ colorBack = getDword( WHATSAPP_KEY_EVENT_OTHER_COLBACK, DEFAULT_EVENT_COLBACK );
+ colorText = getDword( WHATSAPP_KEY_EVENT_OTHER_COLTEXT, DEFAULT_EVENT_COLTEXT );
+ }
+ timeout = getDword( WHATSAPP_KEY_EVENT_OTHER_TIMEOUT, -1 );
+ SkinPlaySound( "OtherEvent" );
+ flags |= NIIF_INFO;
+ break;
+ }
+
+ if ( !getByte(WHATSAPP_KEY_SYSTRAY_NOTIFY,DEFAULT_SYSTRAY_NOTIFY))
+ {
+ if (ServiceExists(MS_POPUP_ADDPOPUP))
+ {
+ POPUPDATAT pd;
+ pd.colorBack = colorBack;
+ pd.colorText = colorText;
+ pd.iSeconds = timeout;
+ pd.lchContact = contact;
+ pd.lchIcon = Skin_GetIconByHandle(m_hProtoIcon); // TODO: Icon test
+ pd.PluginData = szUrl;
+ pd.PluginWindowProc = (WNDPROC)PopupDlgProc;
+ lstrcpy(pd.lptzContactName, title);
+ lstrcpy(pd.lptzText, info);
+ ret = PUAddPopupT(&pd);
+
+ if (ret == 0)
+ return;
+ }
+ } else {
+ if (ServiceExists(MS_CLIST_SYSTRAY_NOTIFY))
+ {
+ MIRANDASYSTRAYNOTIFY err;
+ int niif_flags = flags;
+ REMOVE_FLAG( niif_flags, WHATSAPP_EVENT_CLIENT |
+ WHATSAPP_EVENT_NOTIFICATION |
+ WHATSAPP_EVENT_OTHER );
+ err.szProto = m_szModuleName;
+ err.cbSize = sizeof(err);
+ err.dwInfoFlags = NIIF_INTERN_TCHAR | niif_flags;
+ err.tszInfoTitle = title;
+ err.tszInfo = info;
+ err.uTimeout = 1000 * timeout;
+ ret = CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM) & err);
+
+ if (ret == 0)
+ goto exit;
+ }
+ }
+
+ if (FLAG_CONTAINS(flags, WHATSAPP_EVENT_CLIENT))
+ MessageBox(NULL, info, title, MB_OK | MB_ICONINFORMATION);
+
+exit:
+ if (szUrl != NULL)
+ mir_free(szUrl);
+}
+
+string WhatsAppProto::TranslateStr(const char* str, ...)
+{
+ va_list ap;
+ va_start(ap, str);
+ string ret = Utilities::string_format(Translate(str), ap);
+ va_end(ap);
+ return ret;
+}
\ No newline at end of file diff --git a/protocols/WhatsApp/src/proto.h b/protocols/WhatsApp/src/proto.h new file mode 100644 index 0000000000..efb44f1f9d --- /dev/null +++ b/protocols/WhatsApp/src/proto.h @@ -0,0 +1,216 @@ +#if !defined(PROTO_H)
+#define PROTO_H
+
+class WASocketConnection;
+
+class WhatsAppProto : public PROTO_INTERFACE, public MZeroedObject, public WAListener, public WAGroupListener
+{
+public:
+ WhatsAppProto( const char *proto_name, const TCHAR *username );
+ ~WhatsAppProto( );
+
+ inline const char* ModuleName( ) const
+ {
+ return m_szModuleName;
+ }
+
+ inline bool isOnline( )
+ {
+ return ( m_iStatus != ID_STATUS_OFFLINE && m_iStatus != ID_STATUS_CONNECTING &&
+ connection != NULL );
+ }
+
+ inline bool isOffline( )
+ {
+ return ( m_iStatus == ID_STATUS_OFFLINE );
+ }
+
+ inline bool isInvisible( )
+ {
+ return ( m_iStatus == ID_STATUS_INVISIBLE );
+ }
+
+ //PROTO_INTERFACE
+
+ virtual HANDLE __cdecl AddToList( int flags, PROTOSEARCHRESULT* psr );
+ virtual HANDLE __cdecl AddToListByEvent( int flags, int iContact, HANDLE hDbEvent ) { return NULL; }
+
+ virtual int __cdecl Authorize( HANDLE hDbEvent );
+ virtual int __cdecl AuthDeny( HANDLE hDbEvent, const PROTOCHAR* szReason ) { return 1; }
+ virtual int __cdecl AuthRecv( HANDLE hContact, PROTORECVEVENT* ) { return 1; }
+ virtual int __cdecl AuthRequest( HANDLE hContact, const PROTOCHAR* szMessage );
+
+ virtual HANDLE __cdecl ChangeInfo( int iInfoType, void* pInfoData ) { return NULL; }
+
+ virtual HANDLE __cdecl FileAllow( HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath ) { return NULL; }
+ virtual int __cdecl FileCancel( HANDLE hContact, HANDLE hTransfer ) { return 1; }
+ virtual int __cdecl FileDeny( HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason ) { return 1; }
+ virtual int __cdecl FileResume( HANDLE hTransfer, int* action, const PROTOCHAR** szFilename ) { return 1; }
+
+ virtual DWORD_PTR __cdecl GetCaps( int type, HANDLE hContact = NULL );
+ virtual int __cdecl GetInfo( HANDLE hContact, int infoType ) { return 1; }
+
+ virtual HANDLE __cdecl SearchBasic( const PROTOCHAR* id );
+ virtual HANDLE __cdecl SearchByEmail( const PROTOCHAR* email ) { return NULL; }
+ virtual HANDLE __cdecl SearchByName( const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName ) { return NULL; }
+ virtual HWND __cdecl SearchAdvanced( HWND owner ) { return NULL; }
+ virtual HWND __cdecl CreateExtendedSearchUI( HWND owner ) { return NULL; }
+
+ virtual int __cdecl RecvContacts( HANDLE hContact, PROTORECVEVENT* ) { return 1; }
+ virtual int __cdecl RecvFile( HANDLE hContact, PROTOFILEEVENT* ) { return 1; }
+ virtual int __cdecl RecvMsg( HANDLE hContact, PROTORECVEVENT* );
+ virtual int __cdecl RecvUrl( HANDLE hContact, PROTORECVEVENT* ) { return 1; }
+
+ virtual int __cdecl SendContacts( HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList ) { return 1; }
+ virtual HANDLE __cdecl SendFile( HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles ) { return NULL; }
+ virtual int __cdecl SendMsg( HANDLE hContact, int flags, const char* msg );
+ virtual int __cdecl SendUrl( HANDLE hContact, int flags, const char* url ) { return 1; }
+
+ virtual int __cdecl SetApparentMode( HANDLE hContact, int mode ) { return 1; }
+ virtual int __cdecl SetStatus( int iNewStatus );
+
+ virtual HANDLE __cdecl GetAwayMsg( HANDLE hContact ) { return NULL; }
+ virtual int __cdecl RecvAwayMsg( HANDLE hContact, int mode, PROTORECVEVENT* evt ) { return 1; }
+ virtual int __cdecl SendAwayMsg( HANDLE hContact, HANDLE hProcess, const char* msg ) { return 1; }
+ virtual int __cdecl SetAwayMsg( int iStatus, const PROTOCHAR* msg ) { return 1; }
+
+ virtual int __cdecl UserIsTyping( HANDLE hContact, int type );
+
+ virtual int __cdecl OnEvent( PROTOEVENTTYPE iEventType, WPARAM wParam, LPARAM lParam ) { return 1; }
+
+ ////////////////////////
+
+ // Services
+ int __cdecl SvcCreateAccMgrUI( WPARAM, LPARAM);
+ int __cdecl RefreshBuddyList(WPARAM, LPARAM);
+ int __cdecl RequestFriendship(WPARAM, LPARAM);
+ int __cdecl OnJoinChat(WPARAM, LPARAM);
+ int __cdecl OnLeaveChat(WPARAM, LPARAM);
+
+ // Events
+ int __cdecl OnBuildStatusMenu(WPARAM,LPARAM);
+ int __cdecl OnChatOutgoing(WPARAM,LPARAM);
+ int __cdecl OnCreateGroup(WPARAM,LPARAM);
+ int __cdecl OnPrebuildContactMenu(WPARAM,LPARAM);
+
+ INT_PTR __cdecl OnAddContactToGroup(WPARAM, LPARAM, LPARAM);
+ INT_PTR __cdecl OnRemoveContactFromGroup(WPARAM, LPARAM, LPARAM);
+ int __cdecl OnChangeGroupSubject(WPARAM, LPARAM);
+ int __cdecl OnLeaveGroup(WPARAM, LPARAM);
+
+ // Loops
+ bool NegotiateConnection();
+ void __cdecl stayConnectedLoop(void*);
+ void __cdecl sentinelLoop(void*);
+
+ // Processing Threads
+ void __cdecl ProcessBuddyList(void*);
+ void __cdecl SearchAckThread(void*);
+
+ // Worker Threads
+ void __cdecl ChangeStatus(void*);
+ void __cdecl SendMsgWorker(void*);
+ void __cdecl RecvMsgWorker(void*);
+ void __cdecl SendTypingWorker(void*);
+ void __cdecl SendGetGroupInfoWorker(void*);
+ void __cdecl SendSetGroupNameWorker(void*);
+ void __cdecl SendCreateGroupWorker(void*);
+
+ // Contacts handling
+ HANDLE AddToContactList(const std::string& jid, BYTE type = 0, bool dont_check = false,
+ const char *new_name = NULL, bool isChatRoom = false, bool isHidden = false);
+ bool IsMyContact(HANDLE, bool include_chat = false);
+ HANDLE ContactIDToHContact(const std::string&);
+ void SetAllContactStatuses(int status, bool reset_client = false);
+ void UpdateStatusMsg(HANDLE hContact);
+ string GetContactDisplayName(HANDLE hContact);
+ string GetContactDisplayName(const string& jid);
+ void InitContactMenus();
+ void HandleReceiveGroups(const std::vector<string>& groups, bool isOwned);
+
+ bool IsGroupChat(HANDLE hC, bool checkIsAdmin = false)
+ {
+ return db_get_b(hC, m_szModuleName, "SimpleChatRoom", 0) > (checkIsAdmin ? 1 : 0);
+ }
+
+ // Registration
+ void RequestCode();
+ void RegisterCode(const std::string& code);
+
+ // Helpers
+ std::tstring GetAvatarFolder();
+ void ToggleStatusMenuItems( BOOL bEnable );
+ string TranslateStr(const char* str, ...);
+
+ // Handles, Locks
+ HGENMENU m_hMenuRoot;
+ HANDLE m_hMenuCreateGroup;
+
+ HANDLE signon_lock_;
+ HANDLE log_lock_;
+ HANDLE update_loop_lock_;
+
+ std::tstring def_avatar_folder_;
+ HANDLE hAvatarFolder_;
+
+ HANDLE m_hNetlibUser;
+ WASocketConnection* conn;
+ WAConnection* connection;
+ Mutex connMutex;
+ int lastPongTime;
+
+ std::vector<unsigned char>* challenge;
+ int msgId;
+ int msgIdHeader;
+ string phoneNumber;
+ string jid;
+ string nick;
+ std::map<string, HANDLE> hContactByJid;
+ //std::map<HANDLE, std::vector<HANDLE>> membersByGroupContact;
+ map<HANDLE, map<HANDLE, bool>> isMemberByGroupContact;
+
+ // WhatsApp Events
+ virtual void onMessageForMe(FMessage* paramFMessage, bool paramBoolean);
+ virtual void onMessageStatusUpdate(FMessage* paramFMessage);
+ virtual void onMessageError(FMessage* message, int paramInt) { LOG(""); }
+ virtual void onPing(const std::string& id) throw (WAException);
+ virtual void onPingResponseReceived() { LOG(""); }
+ virtual void onAvailable(const std::string& paramString, bool paramBoolean);
+ virtual void onClientConfigReceived(const std::string& paramString) { LOG(""); }
+ virtual void onLastSeen(const std::string& paramString1, int paramInt, std::string* paramString2);
+ virtual void onIsTyping(const std::string& paramString, bool paramBoolean);
+ virtual void onAccountChange(int paramInt, long paramLong) { LOG(""); }
+ virtual void onPrivacyBlockListAdd(const std::string& paramString) { LOG(""); }
+ virtual void onPrivacyBlockListClear() { LOG(""); }
+ virtual void onDirty(const std::map<string,string>& paramHashtable) { LOG(""); }
+ virtual void onDirtyResponse(int paramHashtable) { LOG(""); }
+ virtual void onRelayRequest(const std::string& paramString1, int paramInt, const std::string& paramString2) { LOG(""); }
+ virtual void onSendGetPictureIds(std::map<string,string>* ids);
+ virtual void onSendGetPicture(const std::string& jid, const std::vector<unsigned char>& data, const std::string& oldId, const std::string& newId);
+ virtual void onPictureChanged(const std::string& from, const std::string& author, bool set);
+ virtual void onDeleteAccount(bool result) { LOG(""); }
+
+ virtual void onGroupAddUser(const std::string& paramString1, const std::string& paramString2);
+ virtual void onGroupRemoveUser(const std::string& paramString1, const std::string& paramString2);
+ virtual void onGroupNewSubject(const std::string& from, const std::string& author, const std::string& newSubject, int paramInt);
+ virtual void onServerProperties(std::map<std::string, std::string>* nameValueMap) { LOG(""); }
+ virtual void onGroupCreated(const std::string& paramString1, const std::string& paramString2);
+ virtual void onGroupInfo(const std::string& paramString1, const std::string& paramString2, const std::string& paramString3, const std::string& paramString4, int paramInt1, int paramInt2);
+ virtual void onGroupInfoFromList(const std::string& paramString1, const std::string& paramString2, const std::string& paramString3, const std::string& paramString4, int paramInt1, int paramInt2);
+ virtual void onOwningGroups(const std::vector<string>& paramVector);
+ virtual void onSetSubject(const std::string& paramString) { LOG(""); }
+ virtual void onAddGroupParticipants(const std::string& paramString, const std::vector<string>& paramVector, int paramHashtable) { LOG(""); }
+ virtual void onRemoveGroupParticipants(const std::string& paramString, const std::vector<string>& paramVector, int paramHashtable) { LOG(""); }
+ virtual void onGetParticipants(const std::string& gjid, const std::vector<string>& participants);
+ virtual void onParticipatingGroups(const std::vector<string>& paramVector);
+ virtual void onLeaveGroup(const std::string& paramString);
+
+ // Information providing
+ int Log(const char* fn, const char *fmt,...);
+ void NotifyEvent(TCHAR* title, TCHAR* info, HANDLE contact, DWORD flags, TCHAR* url=NULL);
+ void NotifyEvent(const string& title, const string& info, HANDLE contact, DWORD flags, TCHAR* url=NULL);
+
+
+};
+
+#endif
\ No newline at end of file diff --git a/protocols/WhatsApp/src/resource.h b/protocols/WhatsApp/src/resource.h Binary files differnew file mode 100644 index 0000000000..a72296428f --- /dev/null +++ b/protocols/WhatsApp/src/resource.h diff --git a/protocols/WhatsApp/src/theme.cpp b/protocols/WhatsApp/src/theme.cpp new file mode 100644 index 0000000000..99cfc5c63d --- /dev/null +++ b/protocols/WhatsApp/src/theme.cpp @@ -0,0 +1,297 @@ +#include "common.h"
+
+extern OBJLIST<WhatsAppProto> g_Instances;
+
+static IconItem icons[] =
+{
+ { LPGEN("WhatsApp Icon"), "whatsApp", IDI_WHATSAPP },
+ { LPGEN("Add To Group"), "addContactToGroup", IDI_ADD_USER_TO_GROUP},
+ { LPGEN("Create Chat Group"), "createGroup", IDI_ADD_GROUP },
+ { LPGEN("Remove From Chat Group"), "removeContactFromGroup", IDI_REMOVE_USER_FROM_GROUP },
+ { LPGEN("Leave And Delete Group"), "leaveAndDeleteGroup", IDI_LEAVE_GROUP },
+ { LPGEN("Leave Group"), "leaveGroup", IDI_LEAVE_GROUP },
+ { LPGEN("Change Group Subject"), "changeGroupSubject", IDI_CHANGE_GROUP_SUBJECT }
+};
+
+void InitIcons(void)
+{
+ Icon_Register(g_hInstance, "Protocols/WhatsApp", icons, SIZEOF(icons), "WhatsApp");
+}
+
+HANDLE GetIconHandle(const char* name)
+{
+ for(size_t i=0; i<SIZEOF(icons); i++)
+ if(strcmp(icons[i].szName, name) == 0)
+ return icons[i].hIcolib;
+
+ return 0;
+}
+
+char *GetIconDescription(const char* name)
+{
+ for(size_t i=0; i<SIZEOF(icons); i++)
+ if(strcmp(icons[i].szName, name) == 0)
+ return icons[i].szDescr;
+
+ return "";
+}
+
+// Contact List menu stuff
+HANDLE hHookPreBuildMenu;
+HANDLE g_hContactMenuItems[CMITEMS_COUNT];
+HANDLE g_hContactMenuSvc[CMITEMS_COUNT];
+
+
+// Helper functions
+static WhatsAppProto* GetInstanceByHContact(HANDLE hContact)
+{
+ char *proto = GetContactProto(hContact);
+ if( !proto )
+ return 0;
+
+ for(int i=0; i<g_Instances.getCount(); i++)
+ if(!strcmp(proto,g_Instances[i].m_szModuleName))
+ return &g_Instances[i];
+
+ return 0;
+}
+
+template<int (__cdecl WhatsAppProto::*Fcn)(WPARAM,LPARAM)>
+INT_PTR GlobalService(WPARAM wParam,LPARAM lParam)
+{
+ WhatsAppProto *proto = GetInstanceByHContact(reinterpret_cast<HANDLE>(wParam));
+ return proto ? (proto->*Fcn)(wParam,lParam) : 0;
+}
+
+template<int (__cdecl WhatsAppProto::*Fcn)(WPARAM,LPARAM,LPARAM)>
+INT_PTR GlobalServiceParam(WPARAM wParam,LPARAM lParam, LPARAM lParam2)
+{
+ WhatsAppProto *proto = GetInstanceByHContact(reinterpret_cast<HANDLE>(wParam));
+ return proto ? (proto->*Fcn)(wParam,lParam,lParam2) : 0;
+}
+
+
+static int PrebuildContactMenu(WPARAM wParam,LPARAM lParam)
+{
+ for (size_t i=0; i<SIZEOF(g_hContactMenuItems); i++)
+ {
+ EnableMenuItem(g_hContactMenuItems[i], false);
+ }
+
+ WhatsAppProto *proto = GetInstanceByHContact(reinterpret_cast<HANDLE>(wParam));
+ return proto ? proto->OnPrebuildContactMenu(wParam,lParam) : 0;
+}
+
+void WhatsAppProto::InitContactMenus()
+{
+ hHookPreBuildMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PrebuildContactMenu);
+
+ CLISTMENUITEM mi = {sizeof(mi)};
+
+ mi.position = -2000006100;
+ mi.icolibItem = GetIconHandle("leaveGroup");
+ mi.pszName = GetIconDescription("leaveGroup");
+ mi.pszService = "WhatsAppProto/LeaveGroup";
+ g_hContactMenuSvc[CMI_LEAVE_GROUP] =
+ CreateServiceFunction(mi.pszService,GlobalService<&WhatsAppProto::OnLeaveGroup>);
+ g_hContactMenuItems[CMI_LEAVE_GROUP] = Menu_AddContactMenuItem(&mi);
+
+ mi.position = -2000006100;
+ mi.icolibItem = GetIconHandle("leaveAndDeleteGroup");
+ mi.pszName = GetIconDescription("leaveAndDeleteGroup");
+ g_hContactMenuSvc[CMI_REMOVE_GROUP] = g_hContactMenuSvc[CMI_LEAVE_GROUP];
+ g_hContactMenuItems[CMI_REMOVE_GROUP] = Menu_AddContactMenuItem(&mi);
+
+ mi.position = -2000006099;
+ mi.icolibItem = GetIconHandle("changeGroupSubject");
+ mi.pszName = GetIconDescription("changeGroupSubject");
+ mi.pszService = "WhatsAppProto/ChangeGroupSubject";
+ g_hContactMenuSvc[CMI_CHANGE_GROUP_SUBJECT] =
+ CreateServiceFunction(mi.pszService,GlobalService<&WhatsAppProto::OnChangeGroupSubject>);
+ g_hContactMenuItems[CMI_CHANGE_GROUP_SUBJECT] = Menu_AddContactMenuItem(&mi);
+
+}
+
+void EnableMenuItem(HANDLE hMenuItem, bool enable)
+{
+ CLISTMENUITEM clmi = { sizeof(clmi) };
+ clmi.flags = CMIM_FLAGS;
+ if (!enable)
+ clmi.flags |= CMIF_HIDDEN;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuItem, (LPARAM)&clmi);
+}
+
+int WhatsAppProto::OnPrebuildContactMenu(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
+ if (hContact)
+ LOG(this->GetContactDisplayName(hContact).c_str());
+ else
+ LOG("No contact found");
+
+ if (g_hContactMenuItems[CMI_ADD_CONTACT_TO_GROUP] != NULL)
+ {
+ CallService("CList/RemoveContactMenuItem", (WPARAM) g_hContactMenuItems[CMI_ADD_CONTACT_TO_GROUP], (LPARAM) 0);
+ }
+ if (g_hContactMenuItems[CMI_REMOVE_CONTACT_FROM_GROUP] != NULL)
+ {
+ CallService("CList/RemoveContactMenuItem", (WPARAM) g_hContactMenuItems[CMI_REMOVE_CONTACT_FROM_GROUP], (LPARAM) 0);
+ }
+
+ int chatType = db_get_b(hContact, m_szModuleName, "SimpleChatRoom", 0);
+
+ CLISTMENUITEM mi = {sizeof(mi)};
+
+ if (chatType == 0)
+ {
+ mi.flags = CMIF_CHILDPOPUP;
+ mi.position= -2000006102;
+ mi.icolibItem = GetIconHandle("addContactToGroup");
+ mi.pszName = GetIconDescription("addContactToGroup");
+ mi.pszService = NULL;
+ g_hContactMenuItems[CMI_ADD_CONTACT_TO_GROUP] = Menu_AddContactMenuItem(&mi);
+
+ if (!isOnline())
+ {
+ mi.flags = CMIM_FLAGS | CMIF_GRAYED;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) g_hContactMenuItems[CMI_ADD_CONTACT_TO_GROUP], (LPARAM) &mi);
+ return 0;
+ }
+
+ mi.hParentMenu = (HGENMENU) g_hContactMenuItems[CMI_ADD_CONTACT_TO_GROUP];
+ mi.flags = CMIF_ROOTHANDLE | CMIF_TCHAR;
+
+ string fullSvcName;
+ string svcName = m_szModuleName;
+ svcName += "/AddContactToGroup_";
+ DBVARIANT dbv;
+
+ for (map<HANDLE, map<HANDLE, bool>>::iterator it = this->isMemberByGroupContact.begin();
+ it != this->isMemberByGroupContact.end(); ++it)
+ {
+ map<HANDLE, bool>::iterator memberIt = it->second.find(hContact);
+ // Only, if current contact is not already member of this group
+ if ((memberIt == it->second.end() || memberIt->second == false) &&
+ !db_get_s(it->first, m_szModuleName, "ID", &dbv, DBVT_ASCIIZ))
+ {
+ fullSvcName = svcName + dbv.pszVal;
+ mi.pszService = (char*) fullSvcName.c_str();
+ mi.ptszName = mir_a2t_cp(this->GetContactDisplayName(it->first).c_str(), CP_UTF8);
+ CreateServiceFunctionParam(mi.pszService, GlobalServiceParam<&WhatsAppProto::OnAddContactToGroup>, (LPARAM) it->first);
+ Menu_AddContactMenuItem(&mi);
+ db_free(&dbv);
+ mir_free(mi.ptszName);
+ }
+ }
+ }
+ else if (chatType == 1)
+ {
+ mi.flags = CMIM_FLAGS;
+ if (!isOnline() || db_get_b(hContact, m_szModuleName, "IsGroupMember", 0) == 0)
+ {
+ mi.flags |= CMIF_GRAYED;
+ }
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) g_hContactMenuItems[CMI_LEAVE_GROUP], (LPARAM) &mi);
+ }
+ else if (chatType == 2)
+ {
+ // owning chat/group
+ mi.flags = CMIF_CHILDPOPUP;
+ mi.position= -2000006102;
+ mi.icolibItem = GetIconHandle("removeContactFromGroup");
+ mi.pszName = GetIconDescription("removeContactFromGroup");
+ mi.pszService = NULL;
+ g_hContactMenuItems[CMI_REMOVE_CONTACT_FROM_GROUP] = Menu_AddContactMenuItem(&mi);
+
+ if (isOnline() && db_get_b(hContact, m_szModuleName, "IsGroupMember", 0) == 1)
+ {
+ map<HANDLE, map<HANDLE, bool>>::iterator groupsIt = this->isMemberByGroupContact.find(hContact);
+ if (groupsIt == this->isMemberByGroupContact.end())
+ {
+ LOG("Group exists only on contact list");
+ }
+ else
+ {
+ mi.hParentMenu = (HGENMENU) g_hContactMenuItems[CMI_REMOVE_CONTACT_FROM_GROUP];
+ mi.flags = CMIF_ROOTHANDLE | CMIF_TCHAR;
+
+ string fullSvcName;
+ string svcName = m_szModuleName;
+ svcName += "/RemoveContactFromGroup_";
+ DBVARIANT dbv;
+
+ for (map<HANDLE, bool>::iterator it = groupsIt->second.begin(); it != groupsIt->second.end(); ++it)
+ {
+ if (!db_get_s(it->first, m_szModuleName, "ID", &dbv, DBVT_ASCIIZ))
+ {
+ fullSvcName = svcName + dbv.pszVal;
+ mi.pszService = (char*) fullSvcName.c_str();
+ mi.ptszName = mir_a2t_cp(this->GetContactDisplayName(it->first).c_str(), CP_UTF8);
+ CreateServiceFunctionParam(mi.pszService,
+ GlobalServiceParam<&WhatsAppProto::OnRemoveContactFromGroup>, (LPARAM) it->first);
+ Menu_AddContactMenuItem(&mi);
+ db_free(&dbv);
+ mir_free(mi.ptszName);
+ }
+ }
+ }
+ mi.flags = CMIM_FLAGS;
+ }
+ else
+ {
+ mi.flags = CMIM_FLAGS | CMIF_GRAYED;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) g_hContactMenuItems[CMI_REMOVE_CONTACT_FROM_GROUP], (LPARAM) &mi);
+ }
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) g_hContactMenuItems[CMI_REMOVE_GROUP], (LPARAM) &mi);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) g_hContactMenuItems[CMI_CHANGE_GROUP_SUBJECT], (LPARAM) &mi);
+ }
+
+ return 0;
+}
+
+int WhatsAppProto::OnBuildStatusMenu(WPARAM wParam,LPARAM lParam)
+{
+ LOG("");
+ char text[200];
+ strcpy(text,m_szModuleName);
+ char *tDest = text+strlen(text);
+
+ CLISTMENUITEM mi = {sizeof(mi)};
+ mi.pszService = text;
+
+ HGENMENU hRoot = MO_GetProtoRootMenu(m_szModuleName);
+ if (hRoot == NULL) {
+ mi.popupPosition = 500085000;
+ mi.hParentMenu = HGENMENU_ROOT;
+ mi.flags = CMIF_ROOTPOPUP | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED | ( this->isOnline() ? 0 : CMIF_GRAYED );
+ mi.icolibItem = GetIconHandle( "whatsApp" );
+ mi.ptszName = m_tszUserName;
+ hRoot = m_hMenuRoot = Menu_AddProtoMenuItem(&mi);
+ } else {
+ if ( m_hMenuRoot )
+ CallService( MS_CLIST_REMOVEMAINMENUITEM, ( WPARAM )m_hMenuRoot, 0 );
+ m_hMenuRoot = NULL;
+ }
+
+ mi.flags = CMIF_CHILDPOPUP | ( this->isOnline() ? 0 : CMIF_GRAYED );
+ mi.position = 201001;
+
+ CreateProtoService(m_szModuleName, "/CreateGroup", &WhatsAppProto::OnCreateGroup, this);
+ strcpy(tDest, "/CreateGroup");
+ mi.hParentMenu = hRoot;
+ mi.pszName = LPGEN("Create Group");
+ mi.icolibItem = GetIconHandle("createGroup");
+ m_hMenuCreateGroup = Menu_AddProtoMenuItem(&mi);
+
+ return 0;
+}
+
+void WhatsAppProto::ToggleStatusMenuItems( BOOL bEnable )
+{
+ CLISTMENUITEM clmi = {sizeof(clmi)};
+ clmi.flags = CMIM_FLAGS | (( bEnable ) ? 0 : CMIF_GRAYED);
+
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM ) m_hMenuRoot, ( LPARAM )&clmi );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM ) m_hMenuCreateGroup, ( LPARAM )&clmi );
+}
diff --git a/protocols/WhatsApp/src/theme.h b/protocols/WhatsApp/src/theme.h new file mode 100644 index 0000000000..c5881ca03a --- /dev/null +++ b/protocols/WhatsApp/src/theme.h @@ -0,0 +1,19 @@ +#if !defined(THEME_H)
+#define THEME_H
+
+void InitIcons(void);
+HANDLE GetIconHandle(const char *name);
+
+//void InitContactMenus(void);
+void EnableMenuItem(HANDLE hMenuItem, bool enable);
+
+/* Contact menu item indexes */
+#define CMI_ADD_CONTACT_TO_GROUP 0
+#define CMI_REMOVE_CONTACT_FROM_GROUP 1
+#define CMI_LEAVE_GROUP 2
+#define CMI_REMOVE_GROUP 3
+#define CMI_CHANGE_GROUP_SUBJECT 4
+
+#define CMITEMS_COUNT 5
+
+#endif
\ No newline at end of file diff --git a/protocols/WhatsApp/src/utils.cpp b/protocols/WhatsApp/src/utils.cpp new file mode 100644 index 0000000000..c635061994 --- /dev/null +++ b/protocols/WhatsApp/src/utils.cpp @@ -0,0 +1,106 @@ +#include "common.h"
+
+void UnixTimeToFileTime(time_t t, LPFILETIME pft)
+{
+ // Note that LONGLONG is a 64-bit value
+ LONGLONG ll;
+
+ ll = Int32x32To64(t, 10000000) + 116444736000000000;
+ pft->dwLowDateTime = (DWORD)ll;
+ pft->dwHighDateTime = ll >> 32;
+}
+
+DWORD utils::conversion::to_timestamp(std::string data)
+{
+ DWORD timestamp = NULL;
+ /*
+ if (!utils::conversion::from_string<DWORD>(timestamp, data, std::dec)) {
+ timestamp = static_cast<DWORD>(::time(NULL));
+ }
+ */
+ return timestamp;
+}
+
+std::string utils::text::source_get_value(std::string* data, unsigned int argument_count, ...)
+{
+ va_list arg;
+ std::string ret;
+ std::string::size_type start = 0, end = 0;
+
+ va_start(arg, argument_count);
+
+ for (unsigned int i = argument_count; i > 0; i--)
+ {
+ if (i == 1)
+ {
+ end = data->find(va_arg(arg, char*), start);
+ if (start == std::string::npos || end == std::string::npos)
+ break;
+ ret = data->substr(start, end - start);
+ } else {
+ std::string term = va_arg(arg, char*);
+ start = data->find(term, start);
+ if (start == std::string::npos)
+ break;
+ start += term.length();
+ }
+ }
+
+ va_end(arg);
+ return ret;
+}
+
+std::string getLastErrorMsg()
+{
+ // Retrieve the system error message for the last-error code
+
+ LPVOID lpMsgBuf;
+ LPVOID lpDisplayBuf;
+ DWORD dw = WSAGetLastError();
+
+ FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ dw,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR) &lpMsgBuf,
+ 0, NULL );
+
+ // Display the error message and exit the process
+ /*
+ lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
+ (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
+ StringCchPrintf((LPTSTR)lpDisplayBuf,
+ LocalSize(lpDisplayBuf) / sizeof(TCHAR),
+ TEXT("%s"),
+ lpMsgBuf);
+ */
+
+ std::string ret((LPSTR) lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ //LocalFree(lpDisplayBuf);
+
+ //return std::string((LPCTSTR)lpDisplayBuf);
+ return ret;
+}
+
+int utils::debug::log(std::string file_name, std::string text)
+{
+ char szFile[MAX_PATH];
+ GetModuleFileNameA(g_hInstance, szFile, SIZEOF(szFile));
+ std::string path = szFile;
+ path = path.substr(0, path.rfind("\\"));
+ path = path.substr(0, path.rfind("\\") + 1);
+ path = path + file_name.c_str() + ".txt";
+
+ SYSTEMTIME time;
+ GetLocalTime(&time);
+
+ std::ofstream out(path.c_str(), std::ios_base::out | std::ios_base::app | std::ios_base::ate);
+ out << "[" << (time.wHour < 10 ? "0" : "") << time.wHour << ":" << (time.wMinute < 10 ? "0" : "") << time.wMinute << ":" << (time.wSecond < 10 ? "0" : "") << time.wSecond << "] " << text << std::endl;
+ out.close();
+
+ return EXIT_SUCCESS;
+}
\ No newline at end of file diff --git a/protocols/WhatsApp/src/utils.h b/protocols/WhatsApp/src/utils.h new file mode 100644 index 0000000000..e6686d1764 --- /dev/null +++ b/protocols/WhatsApp/src/utils.h @@ -0,0 +1,111 @@ +#if !defined(WHATS_NG_UTILS_H)
+#define WHATS_NG_UTILS_H
+
+#include "WhatsAPI++/IMutex.h"
+
+template<typename T>
+void CreateProtoService(const char *module,const char *service,
+ int (__cdecl T::*serviceProc)(WPARAM,LPARAM),T *self)
+{
+ char temp[MAX_PATH*2];
+
+ mir_snprintf(temp,sizeof(temp),"%s%s",module,service);
+ CreateServiceFunctionObj(temp,( MIRANDASERVICEOBJ )*(void**)&serviceProc, self );
+}
+
+template<typename T>
+void HookProtoEvent(const char* evt, int (__cdecl T::*eventProc)(WPARAM,LPARAM), T *self)
+{
+ ::HookEventObj(evt,(MIRANDAHOOKOBJ)*(void**)&eventProc,self);
+}
+
+template<typename T>
+HANDLE ForkThreadEx(void (__cdecl T::*thread)(void*),T *self,void *data = 0)
+{
+ return reinterpret_cast<HANDLE>( mir_forkthreadowner(
+ (pThreadFuncOwner)*(void**)&thread,self,data,0));
+}
+
+template<typename T>
+void ForkThread(void (__cdecl T::*thread)(void*),T *self,void *data = 0)
+{
+ CloseHandle(ForkThreadEx(thread,self,data));
+}
+
+class ScopedLock
+{
+public:
+ ScopedLock(HANDLE h, int t = INFINITE) : handle_(h), timeout_(t)
+ {
+ WaitForSingleObject(handle_,timeout_);
+ }
+ ~ScopedLock()
+ {
+ if(handle_)
+ ReleaseMutex(handle_);
+ }
+ void Unlock()
+ {
+ ReleaseMutex(handle_);
+ handle_ = 0;
+ }
+private:
+ HANDLE handle_;
+ int timeout_;
+};
+
+class Mutex : public IMutex
+{
+private:
+ HANDLE handle;
+public:
+ Mutex() : handle(NULL) {}
+
+ virtual ~Mutex()
+ {
+ if (this->handle != NULL)
+ {
+ ReleaseMutex(this->handle);
+ }
+ }
+
+ virtual void lock()
+ {
+ if (this->handle == NULL)
+ {
+ this->handle = CreateMutex(NULL, FALSE, NULL);
+ }
+ }
+
+ virtual void unlock()
+ {
+ ReleaseMutex(this->handle);
+ this->handle = NULL;
+ }
+};
+
+
+std::string getLastErrorMsg();
+
+void UnixTimeToFileTime(time_t t, LPFILETIME pft);
+
+namespace utils
+{
+ namespace debug
+ {
+ int log(std::string file_name, std::string text);
+ };
+
+ namespace conversion
+ {
+ DWORD to_timestamp( std::string data );
+ };
+
+ namespace text
+ {
+ std::string source_get_value(std::string* data, unsigned int argument_count, ...);
+ };
+};
+
+
+#endif
\ No newline at end of file diff --git a/protocols/WhatsApp/src/version.h b/protocols/WhatsApp/src/version.h new file mode 100644 index 0000000000..21c99b92a6 --- /dev/null +++ b/protocols/WhatsApp/src/version.h @@ -0,0 +1,16 @@ +#define __MAJOR_VERSION 0
+#define __MINOR_VERSION 0
+#define __RELEASE_NUM 2
+#define __BUILD_NUM 0
+
+#define __FILEVERSION_STRING __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM
+#define __TOSTRING(x) #x
+#define __VERSION_STRING __TOSTRING(__FILEVERSION_STRING)
+
+#define __PLUGIN_NAME "WhatsApp"
+#define __FILENAME "WhatsApp.dll"
+#define __DESCRIPTION "WhatsApp protocol support for Miranda NG."
+#define __AUTHOR "Uli Hecht"
+#define __AUTHOREMAIL ""
+#define __AUTHORWEB "http://miranda-ng.org/"
+#define __COPYRIGHT "© 2013 Uli Hecht"
|