From 6172102e4608d453da3978e77e1aa7ae064f04ca Mon Sep 17 00:00:00 2001 From: Sergey I Date: Mon, 24 Jan 2011 19:21:56 +0000 Subject: [PATCH] Added Bits Of Binary (XEP-0231) support git-svn-id: http://delta.affinix.com/svn/trunk/iris@802 ac7a513c-5e3a-0410-af42-968c3ac6e7dc --- src/xmpp/xmpp-im/client.cpp | 14 +++ src/xmpp/xmpp-im/types.cpp | 33 ++++++ src/xmpp/xmpp-im/xmpp_bitsofbinary.cpp | 211 +++++++++++++++++++++++++++++++++ src/xmpp/xmpp-im/xmpp_bitsofbinary.h | 102 ++++++++++++++++ src/xmpp/xmpp-im/xmpp_client.h | 1 + src/xmpp/xmpp-im/xmpp_message.h | 5 + src/xmpp/xmpp-im/xmpp_status.h | 6 + src/xmpp/xmpp-im/xmpp_tasks.cpp | 133 +++++++++++++++++++++ src/xmpp/xmpp-im/xmpp_tasks.h | 28 +++++ src/xmpp/xmpp.pri | 4 +- 10 files changed, 536 insertions(+), 1 deletion(-) create mode 100644 src/xmpp/xmpp-im/xmpp_bitsofbinary.cpp create mode 100644 src/xmpp/xmpp-im/xmpp_bitsofbinary.h diff --git a/src/xmpp/xmpp-im/client.cpp b/src/xmpp/xmpp-im/client.cpp index baa4a1f..9d14e72 100644 --- a/src/xmpp/xmpp-im/client.cpp +++ b/src/xmpp/xmpp-im/client.cpp @@ -80,6 +80,7 @@ #include "xmpp_xmlcommon.h" #include "s5b.h" #include "xmpp_ibb.h" +#include "xmpp_bitsofbinary.h" #include "filetransfer.h" /*#include @@ -140,6 +141,7 @@ public: ResourceList resourceList; S5BManager *s5bman; IBBManager *ibbman; + BoBManager *bobman; FileTransferManager *ftman; bool ftEnabled; QList groupChatList; @@ -171,6 +173,8 @@ Client::Client(QObject *par) d->ibbman = new IBBManager(this); connect(d->ibbman, SIGNAL(incomingReady()), SLOT(ibb_incomingReady())); + d->bobman = new BoBManager(this); + d->ftman = 0; } @@ -257,6 +261,11 @@ IBBManager *Client::ibbManager() const return d->ibbman; } +BoBManager *Client::bobManager() const +{ + return d->bobman; +} + bool Client::isActive() const { return d->active; @@ -830,6 +839,11 @@ void Client::pmMessage(const Message &m) { debug(QString("Client: Message from %1\n").arg(m.from().full())); + // bits of binary. we can't do this in Message, since it knows nothing about Client + foreach (const BoBData &b, m.bobDataList()) { + d->bobman->append(b); + } + if(m.type() == "groupchat") { for(QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { const GroupChat &i = *it; diff --git a/src/xmpp/xmpp-im/types.cpp b/src/xmpp/xmpp-im/types.cpp index 6949892..6496e7c 100644 --- a/src/xmpp/xmpp-im/types.cpp +++ b/src/xmpp/xmpp-im/types.cpp @@ -27,6 +27,7 @@ #include #include "xmpp_xmlcommon.h" +#include "xmpp_bitsofbinary.h" #define NS_XML "http://www.w3.org/XML/1998/namespace" namespace XMPP @@ -914,6 +915,7 @@ public: XData xdata; QMap htmlElements; QDomElement sxe; + QList bobDataList; QList mucStatuses; QList mucInvites; @@ -1380,6 +1382,16 @@ void Message::setSxe(const QDomElement& e) d->sxe = e; } +void Message::addBoBData(const BoBData &bob) +{ + d->bobDataList.append(bob); +} + +QList Message::bobDataList() const +{ + return d->bobDataList; +} + bool Message::spooled() const { return d->spooled; @@ -1606,6 +1618,11 @@ Stanza Message::toStanza(Stream *stream) const s.appendChild(d->xdata.toXml(&s.doc(), submit)); } + // bits of binary + foreach(const BoBData &bd, d->bobDataList) { + s.appendChild(bd.toXml(&s.doc())); + } + return s; } @@ -1700,6 +1717,12 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf if(s.type() == "error") d->error = s.error(); + // Bits of Binary XEP-0231 + nl = childElementsByTagNameNS(root, "urn:xmpp:bob", "data"); + for(n = 0; n < nl.count(); ++n) { + addBoBData(BoBData(nl.item(n).toElement())); + } + // xhtml-im nl = childElementsByTagNameNS(root, "http://jabber.org/protocol/xhtml-im", "html"); if (nl.count()) { @@ -2264,6 +2287,16 @@ bool Status::hasPhotoHash() const return v_hasPhotoHash; } +void Status::addBoBData(const BoBData &bob) +{ + v_bobDataList.append(bob); +} + +QList Status::bobDataList() const +{ + return v_bobDataList; +} + bool Status::isAvailable() const { return v_isAvailable; diff --git a/src/xmpp/xmpp-im/xmpp_bitsofbinary.cpp b/src/xmpp/xmpp-im/xmpp_bitsofbinary.cpp new file mode 100644 index 0000000..c756820 --- /dev/null +++ b/src/xmpp/xmpp-im/xmpp_bitsofbinary.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2010 Rion + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "xmpp_bitsofbinary.h" +#include "xmpp_xmlcommon.h" +#include "xmpp_client.h" +#include "xmpp_tasks.h" +#include + +using namespace XMPP; + +class BoBData::Private : public QSharedData +{ +public: + QByteArray data; + QString type; + QString cid; + unsigned int maxAge; +}; + +BoBData::BoBData() + : d(new Private) +{ + +} + +BoBData::BoBData(const BoBData &other) + : d(other.d) +{ + +} + +BoBData::BoBData(const QDomElement &e) + : d(new Private) +{ + fromXml(e); +} + +BoBData::~BoBData() +{ + +} + +BoBData & BoBData::operator=(const BoBData &other) +{ + if (this==&other) return *this; //Protect against self-assignment + d = other.d; + return *this; +} + +bool BoBData::isNull() const +{ + return d->cid.isEmpty() || d->data.isNull(); +} + +QString BoBData::cid() const +{ + return d->cid; +} + +void BoBData::setCid(const QString &cid) +{ + d->cid = cid; +} + +QByteArray BoBData::data() const +{ + return d->data; +} + +void BoBData::setData(const QByteArray &data) +{ + d->data = data; +} + +QString BoBData::type() const +{ + return d->type; +} + +void BoBData::setType(const QString &type) +{ + d->type = type; +} + +unsigned int BoBData::maxAge() const +{ + return d->maxAge; +} + +void BoBData::setMaxAge(unsigned int maxAge) +{ + d->maxAge = maxAge; +} + +void BoBData::fromXml(const QDomElement &data) +{ + d->cid = data.attribute("cid"); + d->maxAge = data.attribute("max-age").toInt(); + d->type = data.attribute("type"); + d->data = QCA::Base64().stringToArray(data.text().replace("\n","")) + .toByteArray(); +} + +QDomElement BoBData::toXml(QDomDocument *doc) const +{ + QDomElement data = doc->createElement("data"); + data.setAttribute("xmlns", "urn:xmpp:bob"); + data.setAttribute("cid", d->cid); + data.setAttribute("max-age", d->maxAge); + data.setAttribute("type", d->type); + data.appendChild(doc->createTextNode(QCA::Base64().arrayToString(d->data))); + return data; +} + + + +// --------------------------------------------------------- +// BoBCache +// --------------------------------------------------------- +BoBCache::BoBCache(QObject *parent) + : QObject(parent) +{ + +} + + +//------------------------------------------------------------------------------ +// BoBManager +//------------------------------------------------------------------------------ +BoBManager::BoBManager(Client *client) + : QObject(client) + , _cache(0) +{ + new JT_BoBServer(client->rootTask()); +} + +void BoBManager::setCache(BoBCache *cache) +{ + _cache = cache; +} + +BoBData BoBManager::bobData(const QString &cid) +{ + BoBData bd; + if (_cache) { + bd = _cache->get(cid); + } + if (bd.isNull() && _localFiles.contains(cid)) { + QPair fileData = _localFiles.value(cid); + QFile file(fileData.first); + if (file.open(QIODevice::ReadOnly)) { + bd.setCid(cid); + bd.setData(file.readAll()); + bd.setMaxAge(0); + bd.setType(fileData.second); + } + } + return bd; +} + +BoBData BoBManager::makeBoBData(const QByteArray &data, const QString &type, + unsigned int maxAge) +{ + BoBData b; + b.setCid(QString("sha1+%1@bob.xmpp.org").arg(QString( + QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex()))); + b.setData(data); + b.setMaxAge(maxAge); + b.setType(type); + if (_cache) { + _cache->put(b); + } + return b; +} + +QString BoBManager::addLocalFile(const QString &filename, const QString &type) +{ + QFile file(filename); + if (file.open(QIODevice::ReadOnly)) { + QString cid = QString("sha1+%1@bob.xmpp.org").arg( + QString(QCryptographicHash::hash(file.readAll(), + QCryptographicHash::Sha1).toHex())); + _localFiles[cid] = QPair(filename, type); + return cid; + } + return QString(); +} + +void BoBManager::append(const BoBData &data) +{ + if (!data.isNull() && _cache) { + _cache->put(data); + } +} diff --git a/src/xmpp/xmpp-im/xmpp_bitsofbinary.h b/src/xmpp/xmpp-im/xmpp_bitsofbinary.h new file mode 100644 index 0000000..5ebf438 --- /dev/null +++ b/src/xmpp/xmpp-im/xmpp_bitsofbinary.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Rion + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef XMPP_BITSOFBINARY_H +#define XMPP_BITSOFBINARY_H + +#include +#include +#include +#include +#include "xmpp/jid/jid.h" + +namespace XMPP +{ + class JT_BitsOfBinary; + class Client; + + class BoBData + { + class Private; + public: + BoBData(); + BoBData(const BoBData &other); + BoBData(const QDomElement &); + ~BoBData(); + BoBData &operator=(const BoBData &other); + + bool isNull() const; + + QString cid() const; + void setCid(const QString &); + + QByteArray data() const; + void setData(const QByteArray &); + + QString type() const; + void setType(const QString &); + + unsigned int maxAge() const; + void setMaxAge(unsigned int); + + void fromXml(const QDomElement &); + QDomElement toXml(QDomDocument *doc) const; + + private: + QSharedDataPointer d; + }; + + + + class BoBCache : public QObject + { + Q_OBJECT + + public: + BoBCache(QObject *parent); + virtual void put(const BoBData &) = 0; + virtual BoBData get(const QString &) = 0; + }; + + + + class BoBManager : public QObject + { + Q_OBJECT + + public: + BoBManager(Client *); + void setCache(BoBCache*); + + BoBData bobData(const QString &); + BoBData makeBoBData(const QByteArray &data, const QString &type, + unsigned int maxAge = 0); + QString addLocalFile(const QString &filename, + const QString &type = "application/octet-stream"); + + void append(const BoBData &); + + private: + BoBCache *_cache; + QHash > _localFiles; //cid => (filename, mime) + }; + +} + +#endif // XMPP_BITSOFBINARY_H diff --git a/src/xmpp/xmpp-im/xmpp_client.h b/src/xmpp/xmpp-im/xmpp_client.h index 15de35a..040f988 100644 --- a/src/xmpp/xmpp-im/xmpp_client.h +++ b/src/xmpp/xmpp-im/xmpp_client.h @@ -117,6 +117,7 @@ namespace XMPP S5BManager *s5bManager() const; IBBManager *ibbManager() const; + BoBManager *bobManager() const; JidLinkManager *jidLinkManager() const; void setFileTransferEnabled(bool b); diff --git a/src/xmpp/xmpp-im/xmpp_message.h b/src/xmpp/xmpp-im/xmpp_message.h index 73f3130..8f4f552 100644 --- a/src/xmpp/xmpp-im/xmpp_message.h +++ b/src/xmpp/xmpp-im/xmpp_message.h @@ -38,6 +38,7 @@ namespace XMPP { class HTMLElement; class HttpAuthRequest; class XData; + class BoBData; typedef enum { OfflineEvent, DeliveredEvent, DisplayedEvent, ComposingEvent, CancelEvent } MsgEvent; @@ -136,6 +137,10 @@ namespace XMPP { void setSxe(const QDomElement&); const QDomElement& sxe() const; + // XEP-0231 bits of binary + void addBoBData(const BoBData &); + QList bobDataList() const; + // MUC void addMUCStatus(int); const QList& getMUCStatuses() const; diff --git a/src/xmpp/xmpp-im/xmpp_status.h b/src/xmpp/xmpp-im/xmpp_status.h index 26fce55..7b7af3e 100644 --- a/src/xmpp/xmpp-im/xmpp_status.h +++ b/src/xmpp/xmpp-im/xmpp_status.h @@ -26,6 +26,7 @@ #include #include "xmpp_muc.h" +#include "xmpp_bitsofbinary.h" namespace XMPP { @@ -99,6 +100,10 @@ namespace XMPP void setPhotoHash(const QString&); bool hasPhotoHash() const; + // XEP-0231 bits of binary + void addBoBData(const BoBData &); + QList bobDataList() const; + private: int v_priority; QString v_show, v_status, v_key; @@ -112,6 +117,7 @@ namespace XMPP // gabber song extension QString v_songTitle; QString v_capsNode, v_capsVersion, v_capsExt; + QList v_bobDataList; // MUC bool v_isMUC, v_hasMUCItem, v_hasMUCDestroy; diff --git a/src/xmpp/xmpp-im/xmpp_tasks.cpp b/src/xmpp/xmpp-im/xmpp_tasks.cpp index 7d9033d..e0e849a 100644 --- a/src/xmpp/xmpp-im/xmpp_tasks.cpp +++ b/src/xmpp/xmpp-im/xmpp_tasks.cpp @@ -26,9 +26,11 @@ //#include "xmpp_stream.h" //#include "xmpp_types.h" #include "xmpp_vcard.h" +#include "xmpp_bitsofbinary.h" #include #include +#include using namespace XMPP; @@ -275,6 +277,9 @@ bool JT_Register::take(const QDomElement &x) d->xdata.fromXml(i); d->hasXData = true; } + else if(i.tagName() == "data" && i.attribute("xmlns") == "urn:xmpp:bob") { + client()->bobManager()->append(BoBData(i)); // xep-0231 + } else { FormField f; if(f.setType(i.tagName())) { @@ -612,6 +617,11 @@ void JT_Presence::pres(const Status &s) m.appendChild(textTag(doc(), "photo", s.photoHash())); tag.appendChild(m); } + + // bits of binary + foreach(const BoBData &bd, s.bobDataList()) { + tag.appendChild(bd.toXml(doc())); + } } } @@ -773,6 +783,11 @@ bool JT_PushPresence::take(const QDomElement &e) p.setMUCDestroy(MUCDestroy(muc_e)); } } + else if (i.tagName() == "data" && i.attribute("xmlns") == "urn:xmpp:bob") { + BoBData bd(i); + client()->bobManager()->append(bd); + p.addBoBData(bd); + } } @@ -1475,6 +1490,10 @@ bool JT_ServInfo::take(const QDomElement &e) feature.setAttribute("var", "http://jabber.org/protocol/disco#info"); query.appendChild(feature); + feature = doc()->createElement("feature"); + feature.setAttribute("var", "urn:xmpp:bob"); + query.appendChild(feature); + // Client-specific features QStringList clientFeatures = client()->features().list(); for (QStringList::ConstIterator i = clientFeatures.begin(); i != clientFeatures.end(); ++i) { @@ -1928,3 +1947,117 @@ bool JT_DiscoPublish::take(const QDomElement &x) return true; } + +// --------------------------------------------------------- +// JT_BoBServer +// --------------------------------------------------------- +JT_BoBServer::JT_BoBServer(Task *parent) + : Task(parent) +{ + +} + +bool JT_BoBServer::take(const QDomElement &e) +{ + if (e.tagName() != "iq" || e.attribute("type") != "get") + return false; + + QDomElement data = e.firstChildElement("data"); + if (data.attribute("xmlns") == "urn:xmpp:bob") { + QDomElement iq; + BoBData bd = client()->bobManager()->bobData(data.attribute("cid")); + if (bd.isNull()) { + iq = createIQ(client()->doc(), "error", + e.attribute("from"), e.attribute("id")); + Stanza::Error error(Stanza::Error::Cancel, + Stanza::Error::ItemNotFound); + iq.appendChild(error.toXml(*doc(), client()->stream().baseNS())); + } + else { + iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + iq.appendChild(bd.toXml(doc())); + } + send(iq); + return true; + } + return false; +} + + +//---------------------------------------------------------------------------- +// JT_BitsOfBinary +//---------------------------------------------------------------------------- +class JT_BitsOfBinary::Private +{ +public: + Private() { } + + QDomElement iq; + Jid jid; + QString cid; + BoBData data; +}; + +JT_BitsOfBinary::JT_BitsOfBinary(Task *parent) +: Task(parent) +{ + d = new Private; +} + +JT_BitsOfBinary::~JT_BitsOfBinary() +{ + delete d; +} + +void JT_BitsOfBinary::get(const Jid &j, const QString &cid) +{ + d->jid = j; + d->cid = cid; + + d->data = client()->bobManager()->bobData(cid); + if (d->data.isNull()) { + d->iq = createIQ(doc(), "get", d->jid.full(), id()); + QDomElement data = doc()->createElement("data"); + data.setAttribute("xmlns", "urn:xmpp:bob"); + data.setAttribute("cid", cid); + d->iq.appendChild(data); + } +} + +void JT_BitsOfBinary::onGo() +{ + if (d->data.isNull()) { + send(d->iq); + } + else { + setSuccess(); + } +} + +bool JT_BitsOfBinary::take(const QDomElement &x) +{ + if (!iqVerify(x, d->jid, id())) { + return false; + } + + if (x.attribute("type") == "result") { + QDomElement data = x.firstChildElement("data"); + + if (!data.isNull() && data.attribute("cid") == d->cid) { // check xmlns? + d->data.fromXml(data); + client()->bobManager()->append(d->data); + } + + setSuccess(); + } + else { + setError(x); + } + + return true; +} + +BoBData JT_BitsOfBinary::data() +{ + return d->data; +} diff --git a/src/xmpp/xmpp-im/xmpp_tasks.h b/src/xmpp/xmpp-im/xmpp_tasks.h index 0f10f68..ee0e467 100644 --- a/src/xmpp/xmpp-im/xmpp_tasks.h +++ b/src/xmpp/xmpp-im/xmpp_tasks.h @@ -34,6 +34,7 @@ namespace XMPP { class Roster; class Status; + class BoBData; class JT_Register : public Task { @@ -419,6 +420,33 @@ namespace XMPP class Private; Private *d; }; + + class JT_BoBServer : public Task + { + Q_OBJECT + + public: + JT_BoBServer(Task *parent); + bool take(const QDomElement &); + }; + + class JT_BitsOfBinary : public Task + { + Q_OBJECT + public: + JT_BitsOfBinary(Task *); + ~JT_BitsOfBinary(); + + void get(const Jid &, const QString &); + + void onGo(); + bool take(const QDomElement &); + BoBData data(); + + private: + class Private; + Private *d; + }; } #endif diff --git a/src/xmpp/xmpp.pri b/src/xmpp/xmpp.pri index f1068ab..3f6af58 100644 --- a/src/xmpp/xmpp.pri +++ b/src/xmpp/xmpp.pri @@ -67,6 +67,7 @@ HEADERS += \ $$PWD/xmpp-im/xmpp_features.h \ $$PWD/xmpp-im/xmpp_agentitem.h \ $$PWD/xmpp-im/xmpp_discoitem.h \ + $$PWD/xmpp-im/xmpp_bitsofbinary.h \ $$PWD/xmpp-im/im.h SOURCES += \ @@ -92,4 +93,5 @@ SOURCES += \ $$PWD/xmpp-im/xmpp_vcard.cpp \ $$PWD/xmpp-im/s5b.cpp \ $$PWD/xmpp-im/xmpp_ibb.cpp \ - $$PWD/xmpp-im/filetransfer.cpp + $$PWD/xmpp-im/filetransfer.cpp \ + $$PWD/xmpp-im/xmpp_bitsofbinary.cpp -- 2.11.4.GIT