summaryrefslogtreecommitdiff
path: root/protocols/WhatsApp/src/WhatsAPI++
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/WhatsApp/src/WhatsAPI++')
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp349
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h76
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp267
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.h71
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/ByteArray.cpp155
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/ByteArray.h55
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/FMessage.cpp128
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/FMessage.h88
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/IMutex.h14
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/ISocketConnection.h25
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/LICENSE340
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/ProtocolTreeNode.cpp135
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/ProtocolTreeNode.h41
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/README.md30
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp1684
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/WAConnection.h464
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/WAException.h40
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp342
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/WALogin.h75
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/base64.cpp103
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/base64.h16
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/stdafx.cpp8
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/stdafx.h14
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/targetver.h8
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/utilities.cpp463
-rw-r--r--protocols/WhatsApp/src/WhatsAPI++/utilities.h85
26 files changed, 5076 insertions, 0 deletions
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
+