From ab7e0b08fa8c31cf1d468ab4b3c660e2b1836811 Mon Sep 17 00:00:00 2001 From: Fishbone Date: Sun, 2 Jun 2013 16:19:21 +0000 Subject: Added WhatsApp-protocol git-svn-id: http://svn.miranda-ng.org/main/trunk@4861 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- .../WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp | 349 ++++ .../WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h | 76 + .../WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp | 267 ++++ .../WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.h | 71 + protocols/WhatsApp/src/WhatsAPI++/ByteArray.cpp | 155 ++ protocols/WhatsApp/src/WhatsAPI++/ByteArray.h | 55 + protocols/WhatsApp/src/WhatsAPI++/FMessage.cpp | 128 ++ protocols/WhatsApp/src/WhatsAPI++/FMessage.h | 88 + protocols/WhatsApp/src/WhatsAPI++/IMutex.h | 14 + .../WhatsApp/src/WhatsAPI++/ISocketConnection.h | 25 + protocols/WhatsApp/src/WhatsAPI++/LICENSE | 340 ++++ .../WhatsApp/src/WhatsAPI++/ProtocolTreeNode.cpp | 135 ++ .../WhatsApp/src/WhatsAPI++/ProtocolTreeNode.h | 41 + protocols/WhatsApp/src/WhatsAPI++/README.md | 30 + protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp | 1684 ++++++++++++++++++++ protocols/WhatsApp/src/WhatsAPI++/WAConnection.h | 464 ++++++ protocols/WhatsApp/src/WhatsAPI++/WAException.h | 40 + protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp | 342 ++++ protocols/WhatsApp/src/WhatsAPI++/WALogin.h | 75 + protocols/WhatsApp/src/WhatsAPI++/base64.cpp | 103 ++ protocols/WhatsApp/src/WhatsAPI++/base64.h | 16 + protocols/WhatsApp/src/WhatsAPI++/stdafx.cpp | 8 + protocols/WhatsApp/src/WhatsAPI++/stdafx.h | 14 + protocols/WhatsApp/src/WhatsAPI++/targetver.h | 8 + protocols/WhatsApp/src/WhatsAPI++/utilities.cpp | 463 ++++++ protocols/WhatsApp/src/WhatsAPI++/utilities.h | 85 + 26 files changed, 5076 insertions(+) create mode 100644 protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/ByteArray.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/ByteArray.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/FMessage.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/FMessage.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/IMutex.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/ISocketConnection.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/LICENSE create mode 100644 protocols/WhatsApp/src/WhatsAPI++/ProtocolTreeNode.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/ProtocolTreeNode.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/README.md create mode 100644 protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/WAConnection.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/WAException.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/WALogin.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/base64.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/base64.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/stdafx.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/stdafx.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/targetver.h create mode 100644 protocols/WhatsApp/src/WhatsAPI++/utilities.cpp create mode 100644 protocols/WhatsApp/src/WhatsAPI++/utilities.h (limited to 'protocols/WhatsApp/src/WhatsAPI++') 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 + + + +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(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* 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* data; + if (obj->type == STRING) { + std::string* s = (std::string*) obj->data; + data = new std::vector(s->begin(), s->end()); + delete s; + } else { + data = (std::vector*) 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* BinTreeNodeReader::readAttributes(int attribCount) { + std::map* attribs = new std::map(); + 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* BinTreeNodeReader::readList(int token) { + int size = readListSize(token); + std::vector* list = new std::vector(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* 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* buf8 = new std::vector(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* buf24 = new std::vector(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* v = (std::vector*) 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& buff, int len, ByteArrayInputStream* in) { + int count = 0; + while (count < len) { + count += in->read(buff, count, len - count); + } +} + +void BinTreeNodeReader::fillArray(std::vector& 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(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* 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 +#include +#include + +#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* buf; + int readSize; + WAConnection* conn; + + ProtocolTreeNode* nextTreeInternal(); + bool isListTag(int b); + void decodeStream(int flags, int offset, int length); + std::map* readAttributes(int attribCount); + std::vector* readList(int token); + int readListSize(int token); + std::vector* readList(); + ReadData* readString(); + ReadData* readString(int token); + static void fillArray(std::vector& buff, int len, ByteArrayInputStream* in); + static void fillArray(std::vector& 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 +#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* 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 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* attributes) { + if (attributes != NULL) { + std::map::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::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 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 +#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 + +class WAConnection; + +class BinTreeNodeWriter { +private: + WAConnection* conn; + map 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* 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 +#include +#include "utilities.h" + +ByteArrayOutputStream::ByteArrayOutputStream(int size) { + this->buf = new std::vector(); + 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* ByteArrayOutputStream::toByteArray() { + std::vector* array = new std::vector(this->buf->size()); + for (size_t i = 0; i < this->buf->size(); i++) + (*array)[i] = (*this->buf)[i]; + return array; +} + +std::vector* 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* 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* 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& 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 +#include + +class ByteArrayOutputStream { +protected: + std::vector* buf; + size_t position; + +public: + ByteArrayOutputStream(int size = 32); + std::vector* toByteArray(); + std::vector* 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* buf; + size_t pos; + size_t mark; + size_t count; + +public: + ByteArrayInputStream(std::vector* buf, size_t off, size_t length ); + ByteArrayInputStream(std::vector* buf); + int read(); + int read(std::vector& 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 +#include +#include +#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 (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 +//#include +#include +#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 + +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& b, int length) = 0; + virtual void write(const std::vector& bytes, int offset, int length) = 0; + virtual int read(std::vector& 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. + + + Copyright (C) + + 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. + + , 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 *attributes, vector* data, vector *children) { + this->tag = tag; + this->data = data; + this->attributes = attributes; + this->children = children; +} + +ProtocolTreeNode::ProtocolTreeNode(const string& tag, map *attributes, ProtocolTreeNode* child) { + this->tag = tag; + this->data = NULL; + this->attributes = attributes; + this->children = new std::vector(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::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::iterator ii; + + for (ii = children->begin(); ii != children->end(); ii++) + out += (*ii)->toString(); + } + + out += "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::iterator it = attributes->find(attribute); + if (it == attributes->end()) + return NULL; + + return &it->second; +} + +vector* ProtocolTreeNode::getAllChildren() { + vector* ret = new vector(); + + 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::getAllChildren(const string& tag) { + vector* ret = new vector(); + + 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 +#include +#include + +using namespace std; + +class ProtocolTreeNode { +public: + vector* data; + string tag; + map *attributes; + vector *children; + + ProtocolTreeNode(const string& tag, map *attributes, ProtocolTreeNode* child); + ProtocolTreeNode(const string& tag, map *attributes, vector* data = NULL, vector *children = NULL); + string toString(); + ProtocolTreeNode* getChild(const string& id); + ProtocolTreeNode* getChild(size_t id); + string* getAttributeValue(const string& attribute); + + vector* getAllChildren(); + vector* 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 + +License: + + Copyright (c) 2012, Antonio Morales + + 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 +#include +#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* attribs = new std::map(); + (*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* attribs2 = new std::map(); + (*attribs2)["name"] = message->media_name; + ProtocolTreeNode* vcardNode = new ProtocolTreeNode("vcard", attribs2, new std::vector(message->data.begin(), message->data.end())); + mediaNode = new ProtocolTreeNode("media", attribs, vcardNode); + } else { + (*attribs)["encoding"] = "text"; + mediaNode = new ProtocolTreeNode("media", attribs, new std::vector(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(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* attrib = new std::map(); + (*attrib)["xmlns"] = "jabber:x:event"; + std::vector* children = new std::vector(1); + (*children)[0] = serverNode; + ProtocolTreeNode* xNode = new ProtocolTreeNode("x", attrib, NULL, children); + int childCount = (requestNode == NULL? 0 : 1) + 2; + std::vector* messageChildren = new std::vector(childCount); + int i = 0; + if (requestNode != NULL) { + (*messageChildren)[i] = requestNode; + i++; + } + (*messageChildren)[i] = xNode; + i++; + (*messageChildren)[i] = child; + i++; + + std::map* attrib2 = new std::map(); + (*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::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* attribs = new std::map(); + (*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::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::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* 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* 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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:p"; + ProtocolTreeNode* pingNode = new ProtocolTreeNode("ping", attribs1); + + std::map* attribs2 = new std::map(); + (*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* attribs = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "http://jabber.org/protocol/chatstates"; + ProtocolTreeNode* composingNode = new ProtocolTreeNode("composing", attribs1); + + std::map* attribs2 = new std::map(); + (*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* attribs = new std::map(); + (*attribs)["type"] = "active"; + ProtocolTreeNode* presenceNode = new ProtocolTreeNode("presence", attribs); + + this->out->write(presenceNode); + + delete presenceNode; +} + +void WAConnection::sendInactive() throw(WAException) { + std::map* attribs = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "http://jabber.org/protocol/chatstates"; + ProtocolTreeNode* pausedNode = new ProtocolTreeNode("paused", attribs1); + + std::map* attribs2 = new std::map(); + (*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* attribs1 = new std::map(); + (*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* attribs1 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "urn:xmpp:receipts"; + ProtocolTreeNode* receivedNode = new ProtocolTreeNode("received", attribs1); + + std::map* attribs2 = new std::map(); + (*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* attribs1 = new std::map(); + (*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* attribs1 = new std::map(); + (*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* attribs2 = new std::map(); + (*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& groups) throw(WAException) { + std::map* attribs1 = new std::map(); + (*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* attribs2 = new std::map(); + (*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* WAConnection::processGroupSettings(const std::vector& groups) { + std::vector* result = new std::vector(groups.size()); + if (!groups.empty()) { + time_t now = time(NULL); + for (int i = 0; i < groups.size(); i++) { + std::map* attribs = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "urn:xmpp:receipts"; + (*attribs1)["type"] = receiptType; + ProtocolTreeNode* ackNode = new ProtocolTreeNode("ack", attribs1); + + std::map* attribs2 = new std::map(); + (*attribs2)["to"] = to; + (*attribs2)["type"] = "chat"; + (*attribs2)["id"] = id; + ProtocolTreeNode* messageNode = new ProtocolTreeNode("message", attribs2, ackNode); + + return messageNode; +} + +std::map* WAConnection::parseCategories(ProtocolTreeNode* dirtyNode) throw (WAException) { + std::map* categories = new std::map(); + 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* 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* 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 myVector(0); + std::vector* 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 myVector(0); + std::vector* 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 myVector2(0); + std::vector* 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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "urn:xmpp:receipts"; + ProtocolTreeNode* child = new ProtocolTreeNode("received", attribs1); + + std::map* attribs2 = new std::map(); + (*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* attribs1 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["name"] = "default"; + ProtocolTreeNode* listNode = new ProtocolTreeNode("list", attribs1); + + std::map* attribs2 = new std::map(); + (*attribs2)["xmlns"] = "jabber:iq:privacy"; + ProtocolTreeNode* queryNode = new ProtocolTreeNode("query", attribs2, listNode); + + std::map* attribs3 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:g"; + (*attribs1)["type"] = "props"; + ProtocolTreeNode* listNode = new ProtocolTreeNode("list", attribs1); + + std::map* attribs2 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:g"; + (*attribs1)["type"] = type; + ProtocolTreeNode* listNode = new ProtocolTreeNode("list", attribs1); + + std::map* attribs2 = new std::map(); + (*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& groups) throw (WAException) { + std::vector* 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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "jabber:iq:last"; + ProtocolTreeNode* queryNode = new ProtocolTreeNode("query", attribs1); + + std::map* attribs2 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:g"; + ProtocolTreeNode* queryNode = new ProtocolTreeNode("query", attribs1); + + std::map* attribs2 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:g"; + ProtocolTreeNode* listNode = new ProtocolTreeNode("list", attribs1); + + std::map* attribs2 = new std::map(); + (*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& vector, const std::string& tag, const std::string& attribute) throw (WAException) { + std::vector* 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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:g"; + (*attribs1)["action"] = "create"; + (*attribs1)["subject"] = subject; + ProtocolTreeNode* groupNode = new ProtocolTreeNode("group", attribs1); + + std::map* attribs2 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:g"; + (*attribs1)["action"] = "delete"; + ProtocolTreeNode* groupNode = new ProtocolTreeNode("group", attribs1); + + std::map* attribs2 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["name"] = category; + ProtocolTreeNode* categoryNode = new ProtocolTreeNode("category", attribs1); + + std::map* attribs2 = new std::map(); + (*attribs2)["xmlns"] = "urn:xmpp:whatsapp:dirty"; + ProtocolTreeNode* cleanNode = new ProtocolTreeNode("clean", attribs2, categoryNode); + + std::map* attribs3 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["id"] = gjid; + ProtocolTreeNode* groupNode = new ProtocolTreeNode("group", attribs1); + + std::map* attribs2 = new std::map(); + (*attribs2)["xmlns"] = "w:g"; + ProtocolTreeNode* leaveNode = new ProtocolTreeNode("leave", attribs2, groupNode); + + std::map* attribs3 = new std::map(); + (*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& 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& 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& participants, const std::string& id, const std::string& inner_tag) throw (WAException) { + size_t size = participants.size(); + std::vector* children = new std::vector(size); + for (int i = 0; i < size; i++) { + std::map* attribs1 = new std::map(); + (*attribs1)["jid"] = participants[i]; + (*children)[i] = new ProtocolTreeNode("participant", attribs1); + } + + std::map* attribs2 = new std::map(); + (*attribs2)["xmlns"] = "w:g"; + ProtocolTreeNode* innerNode = new ProtocolTreeNode(inner_tag, attribs2, NULL, children); + + std::map* attribs3 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:g"; + (*attribs1)["value"] = subject; + ProtocolTreeNode* subjectNode = new ProtocolTreeNode("subject", attribs1); + + std::map* attribs2 = new std::map(); + (*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(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* data) throw (WAException) { + std::string id = this->makeId("set_photo_"); + this->pending_server_requests[id] = new IqResultSetPhotoHandler(this, jid); + + std::map* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:profile:picture"; + // (*attribs1)["type"] = "image"; + ProtocolTreeNode* listNode = new ProtocolTreeNode("picture", attribs1, data, NULL); + + std::map* attribs2 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:profile:picture"; + (*attribs1)["type"] = type; + ProtocolTreeNode* listNode = new ProtocolTreeNode("picture", attribs1); + + std::map* attribs2 = new std::map(); + (*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& jids) throw (WAException) { + std::string id = makeId("get_picture_ids_"); + this->pending_server_requests[id] = new IqResultGetPictureIdsHandler(this); + + std::vector* children = new std::vector(); + for (int i = 0; i < jids.size(); i++) { + std::map* attribs = new std::map(); + (*attribs)["jid"] = jids[i]; + ProtocolTreeNode* child = new ProtocolTreeNode("user", attribs); + children->push_back(child); + } + + std::map* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "w:profile:picture"; + ProtocolTreeNode* queryNode = new ProtocolTreeNode("list", attribs1, NULL, children); + + std::map* attribs2 = new std::map(); + (*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* attribs1 = new std::map(); + (*attribs1)["xmlns"] = "urn:xmpp:whatsapp:account"; + ProtocolTreeNode* node1 = new ProtocolTreeNode("remove", attribs1); + + std::map* attribs2 = new std::map(); + (*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 +#include +#include +#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& 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* ids)=0; + virtual void onSendGetPicture(const std::string& jid, const std::vector& 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* 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& paramVector)=0; + virtual void onSetSubject(const std::string& paramString)=0; + virtual void onAddGroupParticipants(const std::string& paramString, const std::vector& paramVector, int paramHashtable)=0; + virtual void onRemoveGroupParticipants(const std::string& paramString, const std::vector& paramVector, int paramHashtable)=0; + virtual void onGetParticipants(const std::string& gjid, const std::vector& participants)=0; + virtual void onParticipatingGroups(const std::vector& 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* 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 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* nodes = node->getAllChildren("prop"); + std::map 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 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* 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 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* children = groupNode->getAllChildren("user"); + std::map 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 pending_server_requests; + IMutex* mutex; + + // std:: 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* 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& groups) throw (WAException); + std::string gidToGjid(const std::string& gid); + void readAttributeList(ProtocolTreeNode* node, std::vector& vector, const std::string& tag, const std::string& attribute) throw (WAException); + void sendVerbParticipants(const std::string& gjid, const std::vector& 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* processGroupSettings(const std::vector& 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& 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& participants) throw (WAException); + void sendRemoveParticipants(const std::string& gjid, const std::vector& 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& jids) throw (WAException); + void sendSetPicture(const std::string& jid, std::vector* 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 +#include + +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 +#include +#include +#include +#include +#include + + +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* WALogin::login(const std::vector& 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* 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& challengeData) { + std::vector* authBlob = this->getAuthBlob(challengeData); + + // std::string response = this->getResponse(challengeData); + std::map *attributes = new std::map(); + (*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* children = new std::vector(); + children->push_back(child); + + std::map* attributes = new std::map(); + (*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& existingChallenge) { + std::vector* data = NULL; + if (!existingChallenge.empty()) { + data = this->getAuthBlob(existingChallenge); + } + + std::map* attributes = new std::map(); + (*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* WALogin::getAuthBlob(const std::vector& 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* list = new std::vector(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* 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 challengedata(root->data->begin(), root->data->end()); + delete root; + this->sendResponse(challengedata); + _LOGDATA("Send response"); + std::vector data = this->readSuccess(); + _LOGDATA("Read success"); + return new std::vector(data.begin(), data.end()); + } + if (ProtocolTreeNode::tagEquals(root, "success")) { + // base64_decode(*root->data); + std::vector* ret = new std::vector(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 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 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& 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 +#include +#include + +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& 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* getAuthBlob(const std::vector& nonce); + void sendResponse(const std::vector& challengeData); + void sendFeatures(); + void sendAuth(const std::vector& nonce); + std::vector* readFeaturesUntilChallengeOrSuccess(); + void parseSuccessNode(ProtocolTreeNode* node); + std::vector 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* login(const std::vector& 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 + +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 + +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 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 +#include +#include +#include +#include +#include +#include "WAException.h" +#include +#include +#include +#include + +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& 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* loadFileToBytes(const string& path) { + vector* 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(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* getChallengeData(const std::string& challengeFile) { + return loadFileToBytes(challengeFile); +} + +bool saveChallengeData(const std::vector& data, const std::string& challengeFile) { + return saveBytesToFile(data, challengeFile); +} + +std::string utf8_to_utf16(const std::string& utf8) +{ + std::vector 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 +** +** 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 +#include +#include +#include +#include +#include + +#define _LOGWIN32 // #TODO +#ifndef _LOGWIN32 +#include +#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& data, const string& filePath); + extern string removeWaDomainFromJid(const string& jid); + extern string getNameFromPath(const std::string& path); + extern vector* loadFileToBytes(const string& path); + extern bool fileExists(const std::string& path); + extern std::vector* getChallengeData(const std::string& file); + extern bool saveChallengeData(const std::vector& 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 + -- cgit v1.2.3