Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement XEP-0363: HTTP File Upload: Request IQs #188

Merged
merged 1 commit into from May 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/xep.doc
Expand Up @@ -47,6 +47,7 @@ Ongoing:
- XEP-0009: Jabber-RPC (API is not finalized yet)
- XEP-0060: Publish-Subscribe (Only basic IQ implemented)
- XEP-0077: In-Band Registration (Only basic IQ implemented)
- XEP-0363: HTTP File Upload (v0.9.0)
- XEP-0369: Mediated Information eXchange (MIX) (Only IQ queries implemented) (v0.14.2)
- XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements (Only IQ queries implemented) (v0.4.0)

Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Expand Up @@ -19,6 +19,7 @@ set(INSTALL_HEADER_FILES
base/QXmppDiscoveryIq.h
base/QXmppElement.h
base/QXmppEntityTimeIq.h
base/QXmppHttpUploadIq.h
base/QXmppIbbIq.h
base/QXmppIq.h
base/QXmppJingleIq.h
Expand Down Expand Up @@ -95,6 +96,7 @@ set(SOURCE_FILES
base/QXmppDiscoveryIq.cpp
base/QXmppElement.cpp
base/QXmppEntityTimeIq.cpp
base/QXmppHttpUploadIq.cpp
base/QXmppIbbIq.cpp
base/QXmppIq.cpp
base/QXmppJingleIq.cpp
Expand Down
2 changes: 2 additions & 0 deletions src/base/QXmppConstants.cpp
Expand Up @@ -132,6 +132,8 @@ const char* ns_idle = "urn:xmpp:idle:1";
const char* ns_chat_markers = "urn:xmpp:chat-markers:0";
// XEP-0352: Client State Indication
const char* ns_csi = "urn:xmpp:csi:0";
// XEP-0363: HTTP File Upload
const char* ns_http_upload = "urn:xmpp:http:upload:0";
// XEP-0369: Mediated Information eXchange (MIX)
const char* ns_mix = "urn:xmpp:mix:core:1";
const char* ns_mix_create_channel = "urn:xmpp:mix:core:1#create-channel";
Expand Down
2 changes: 2 additions & 0 deletions src/base/QXmppConstants_p.h
Expand Up @@ -144,6 +144,8 @@ extern const char* ns_idle;
extern const char* ns_chat_markers;
// XEP-0352: Client State Indication
extern const char* ns_csi;
// XEP-0363: HTTP File Upload
extern const char* ns_http_upload;
// XEP-0369: Mediated Information eXchange (MIX)
extern const char* ns_mix;
extern const char* ns_mix_create_channel;
Expand Down
250 changes: 250 additions & 0 deletions src/base/QXmppHttpUploadIq.cpp
@@ -0,0 +1,250 @@
/*
* Copyright (C) 2008-2019 The QXmpp developers
*
* Authors:
* Linus Jahn <lnj@kaidan.im>
*
* Source:
* https://github.com/qxmpp-project/qxmpp
*
* This file is a part of QXmpp library.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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.
*
*/

#include <QDomElement>
#include <QMimeDatabase>

#include "QXmppHttpUploadIq.h"
#include "QXmppConstants_p.h"

class QXmppHttpUploadRequestIqPrivate
{
public:
QString fileName;
qint64 size;
QMimeType contentType;
};

QXmppHttpUploadRequestIq::QXmppHttpUploadRequestIq()
: d(new QXmppHttpUploadRequestIqPrivate())
{
}

QXmppHttpUploadRequestIq::~QXmppHttpUploadRequestIq()
{
delete d;
}

/// Returns the file name of the file to be uploaded.

QString QXmppHttpUploadRequestIq::fileName() const
lnjX marked this conversation as resolved.
Show resolved Hide resolved
{
return d->fileName;
}

/// Sets the file name. The upload service will use this to create the upload/
/// download URLs. This may also differ from the actual file name to get a
/// different URL. It's not required to replace special characters (this is the
/// server's job).

void QXmppHttpUploadRequestIq::setFileName(const QString &fileName)
{
d->fileName = fileName;
}

/// Returns the file's size in bytes.

qint64 QXmppHttpUploadRequestIq::size() const
Copy link
Contributor

@jlaine jlaine Jan 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry for pointing out these things one by one.. but why fileName() vs size() ? I would expect either name() and size() or fileName() and fileSize().

QXmppTransferFileInfo use name() and size()

Ah.. actually forget this I guess it comes from the XML attribute names.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah right, I took the XML attr. names.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that ok or should I better use consistent names?

{
return d->size;
}

/// Sets the file's size in bytes.

void QXmppHttpUploadRequestIq::setSize(qint64 size)
{
d->size = size;
}

/// Returns the (optional) MIME-type of the file.

QMimeType QXmppHttpUploadRequestIq::contentType() const
{
return d->contentType;
}

/// Sets the MIME-type of the file. This is optional.

void QXmppHttpUploadRequestIq::setContentType(const QMimeType &type)
{
d->contentType = type;
}

bool QXmppHttpUploadRequestIq::isHttpUploadRequestIq(const QDomElement &element)
{
if (element.tagName() == "iq") {
QDomElement request = element.firstChildElement("request");
return !request.isNull() && request.namespaceURI() == ns_http_upload;
}
return false;
}

/// \cond
void QXmppHttpUploadRequestIq::parseElementFromChild(const QDomElement &element)
{
QDomElement request = element.firstChildElement("request");
d->fileName = request.attribute("filename");
d->size = request.attribute("size").toLongLong();
if (request.hasAttribute("content-type")) {
QMimeDatabase mimeDb;
QMimeType type = mimeDb.mimeTypeForName(request.attribute("content-type"));
if (!type.isDefault() && type.isValid())
d->contentType = type;
}
}

void QXmppHttpUploadRequestIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
{
writer->writeStartElement("request");
writer->writeAttribute("xmlns", ns_http_upload);
// filename and size are required
writer->writeAttribute("filename", d->fileName);
writer->writeAttribute("size", QString::number(d->size));
// content-type is optional
if (!d->contentType.isDefault() && d->contentType.isValid())
writer->writeAttribute("content-type", d->contentType.name());
writer->writeEndElement();
}
/// \endcond

class QXmppHttpUploadSlotIqPrivate
{
public:
QUrl putUrl;
QUrl getUrl;
QMap<QString, QString> putHeaders;
};

QXmppHttpUploadSlotIq::QXmppHttpUploadSlotIq()
: d(new QXmppHttpUploadSlotIqPrivate())
{
}

QXmppHttpUploadSlotIq::~QXmppHttpUploadSlotIq()
{
delete d;
}

/// Returns the URL for uploading via. HTTP PUT.

QUrl QXmppHttpUploadSlotIq::putUrl() const
{
return d->putUrl;
}

/// Sets the URL the client should use for uploading.

void QXmppHttpUploadSlotIq::setPutUrl(const QUrl &putUrl)
{
d->putUrl = putUrl;
}

/// Returns the URL to where the file will be served.

QUrl QXmppHttpUploadSlotIq::getUrl() const
{
return d->getUrl;
}

/// Sets the download URL.

void QXmppHttpUploadSlotIq::setGetUrl(const QUrl &getUrl)
{
d->getUrl = getUrl;
}

/// Returns a map of header fields (header name -> value) that need to be
/// included in the PUT (upload) request. This won't contain any other fields
/// than: "Authorization", "Cookie" or "Expires".

QMap<QString, QString> QXmppHttpUploadSlotIq::putHeaders() const
{
return d->putHeaders;
}

/// Sets the header fields the client needs to include in the PUT (upload)
/// request. All fields other than "Authorization", "Cookie" or "Expires" will
/// be ignored.

void QXmppHttpUploadSlotIq::setPutHeaders(const QMap<QString, QString> &putHeaders)
{
d->putHeaders.clear();
for (QString &name : putHeaders.keys()) {
if (name == "Authorization" || name == "Cookie" || name == "Expires")
d->putHeaders[name] = putHeaders[name];
}
}

bool QXmppHttpUploadSlotIq::isHttpUploadSlotIq(const QDomElement &element)
{
if (element.tagName() == "iq") {
QDomElement slot = element.firstChildElement("slot");
return !slot.isNull() && slot.namespaceURI() == ns_http_upload;
}
return false;
}

/// \cond
void QXmppHttpUploadSlotIq::parseElementFromChild(const QDomElement &element)
{
QDomElement slot = element.firstChildElement("slot");
QDomElement put = slot.firstChildElement("put");
d->getUrl = QUrl::fromEncoded(slot.firstChildElement("get").attribute("url").toUtf8());
d->putUrl = QUrl::fromEncoded(put.attribute("url").toUtf8());
if (put.hasChildNodes()) {
QMap<QString, QString> headers;
QDomElement header = put.firstChildElement("header");
while (!header.isNull()) {
headers[header.attribute("name")] = header.text();

header = header.nextSiblingElement("header");
}

setPutHeaders(headers);
}
}

void QXmppHttpUploadSlotIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
{
writer->writeStartElement("slot");
writer->writeAttribute("xmlns", ns_http_upload);

writer->writeStartElement("put");
writer->writeAttribute("url", d->putUrl.toEncoded());
if (!d->putHeaders.isEmpty()) {
for (const QString &name : d->putHeaders.keys()) {
writer->writeStartElement("header");
writer->writeAttribute("name", name);
writer->writeCharacters(d->putHeaders[name]);
writer->writeEndElement();
}
}
writer->writeEndElement();

writer->writeStartElement("get");
writer->writeAttribute("url", d->getUrl.toEncoded());
writer->writeEndElement();

writer->writeEndElement();
}
/// \endcond
100 changes: 100 additions & 0 deletions src/base/QXmppHttpUploadIq.h
@@ -0,0 +1,100 @@
/*
* Copyright (C) 2008-2019 The QXmpp developers
*
* Authors:
* Linus Jahn <lnj@kaidan.im>
*
* Source:
* https://github.com/qxmpp-project/qxmpp
*
* This file is a part of QXmpp library.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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.
*
*/

#ifndef QXMPPHTTPUPLOADIQ_H
#define QXMPPHTTPUPLOADIQ_H

#include <QMap>
#include <QMimeType>
#include <QUrl>

#include "QXmppIq.h"

class QXmppHttpUploadRequestIqPrivate;
class QXmppHttpUploadSlotIqPrivate;

/// \brief Represents an HTTP File Upload IQ for requesting an upload slot as
/// defined by XEP-0363: HTTP File Upload [v0.9.0].
///
/// \ingroup Stanzas

class QXMPP_EXPORT QXmppHttpUploadRequestIq : public QXmppIq
{
public:
QXmppHttpUploadRequestIq();
~QXmppHttpUploadRequestIq();

QString fileName() const;
void setFileName(const QString &filename);

qint64 size() const;
void setSize(qint64 size);

QMimeType contentType() const;
lnjX marked this conversation as resolved.
Show resolved Hide resolved
void setContentType(const QMimeType &type);

static bool isHttpUploadRequestIq(const QDomElement &element);

protected:
/// \cond
void parseElementFromChild(const QDomElement &element);
void toXmlElementFromChild(QXmlStreamWriter *writer) const;
/// \endcond

private:
QXmppHttpUploadRequestIqPrivate* const d;
};

/// \brief Represents an HTTP File Upload IQ result for receiving an upload slot as
/// defined by XEP-0363: HTTP File Upload [v0.9.0].
///
/// \ingroup Stanzas

class QXMPP_EXPORT QXmppHttpUploadSlotIq : public QXmppIq
{
public:
QXmppHttpUploadSlotIq();
~QXmppHttpUploadSlotIq();

QUrl putUrl() const;
void setPutUrl(const QUrl &putUrl);

QUrl getUrl() const;
void setGetUrl(const QUrl &getUrl);

QMap<QString, QString> putHeaders() const;
void setPutHeaders(const QMap<QString, QString> &putHeaders);

static bool isHttpUploadSlotIq(const QDomElement &element);

protected:
/// \cond
void parseElementFromChild(const QDomElement &element);
void toXmlElementFromChild(QXmlStreamWriter *writer) const;
/// \endcond

private:
QXmppHttpUploadSlotIqPrivate* const d;
};

#endif // QXMPPHTTPUPLOADIQ_H