From f2aadfefa283aafc88131c643b97acd28989ac20 Mon Sep 17 00:00:00 2001 From: Cochise César Date: Mon, 10 Jan 2022 01:27:06 -0300 Subject: Implement XEP-0080: User Location --- src/CMakeLists.txt | 4 + src/base/QXmppConstants.cpp | 3 + src/base/QXmppConstants_p.h | 3 + src/base/QXmppGeolocItem.cpp | 202 ++++++++++++++++++++++++++++++++ src/base/QXmppGeolocItem.h | 54 +++++++++ src/client/QXmppUserLocationManager.cpp | 96 +++++++++++++++ src/client/QXmppUserLocationManager.h | 39 ++++++ 7 files changed, 401 insertions(+) create mode 100644 src/base/QXmppGeolocItem.cpp create mode 100644 src/base/QXmppGeolocItem.h create mode 100644 src/client/QXmppUserLocationManager.cpp create mode 100644 src/client/QXmppUserLocationManager.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5f1e5eb5..2e9d587a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -37,6 +37,7 @@ set(INSTALL_HEADER_FILES base/QXmppElement.h base/QXmppEntityTimeIq.h base/QXmppFutureUtils_p.h + base/QXmppGeolocItem.h base/QXmppHttpUploadIq.h base/QXmppIbbIq.h base/QXmppIq.h @@ -119,6 +120,7 @@ set(INSTALL_HEADER_FILES client/QXmppTrustStorage.h client/QXmppUploadRequestManager.h client/QXmppUserTuneManager.h + client/QXmppUserLocationManager.h client/QXmppVCardManager.h client/QXmppVersionManager.h @@ -148,6 +150,7 @@ set(SOURCE_FILES base/QXmppDiscoveryIq.cpp base/QXmppElement.cpp base/QXmppEntityTimeIq.cpp + base/QXmppGeolocItem.cpp base/QXmppHttpUploadIq.cpp base/QXmppIbbIq.cpp base/QXmppIq.cpp @@ -226,6 +229,7 @@ set(SOURCE_FILES client/QXmppTrustMemoryStorage.cpp client/QXmppTrustStorage.cpp client/QXmppUploadRequestManager.cpp + client/QXmppUserLocationManager.cpp client/QXmppUserTuneManager.cpp client/QXmppVCardManager.cpp client/QXmppVersionManager.cpp diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp index d6e46ae6..b738341b 100644 --- a/src/base/QXmppConstants.cpp +++ b/src/base/QXmppConstants.cpp @@ -58,6 +58,9 @@ const char *ns_register_feature = "http://jabber.org/features/iq-register"; // XEP-0078: Non-SASL Authentication const char *ns_auth = "jabber:iq:auth"; const char *ns_authFeature = "http://jabber.org/features/iq-auth"; +// XEP-0080: User Location +const char *ns_geoloc = "http://jabber.org/protocol/geoloc"; +const char *ns_geoloc_notify = "http://jabber.org/protocol/geoloc+notify"; // XEP-0085: Chat State Notifications const char *ns_chat_states = "http://jabber.org/protocol/chatstates"; // XEP-0091: Legacy Delayed Delivery diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h index 7fa7afd5..346c60f5 100644 --- a/src/base/QXmppConstants_p.h +++ b/src/base/QXmppConstants_p.h @@ -70,6 +70,9 @@ extern const char *ns_register_feature; // XEP-0078: Non-SASL Authentication extern const char *ns_auth; extern const char *ns_authFeature; +// XEP-0080: User Location +extern const char *ns_geoloc; +extern const char *ns_geoloc_notify; // XEP-0085: Chat State Notifications extern const char *ns_chat_states; // XEP-0091: Legacy Delayed Delivery diff --git a/src/base/QXmppGeolocItem.cpp b/src/base/QXmppGeolocItem.cpp new file mode 100644 index 00000000..bef54b69 --- /dev/null +++ b/src/base/QXmppGeolocItem.cpp @@ -0,0 +1,202 @@ +// SPDX-FileCopyrightText: 2022 Cochise César +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "QXmppGeolocItem.h" + +#include "QXmppConstants_p.h" +#include "QXmppUtils.h" + +#include +#include + +/// \cond +class QXmppGeolocItemPrivate : public QSharedData +{ +public: + std::optional accuracy; + QString country; + QString locality; + std::optional latitude; + std::optional longitude; +}; +/// \endcond + +/// +/// \class QXmppGeolocItem +/// +/// This class represents a PubSub item for \xep{0080, User Location}. +/// +/// \since QXmpp 1.5 +/// + +/// +/// Default constructor +/// +QXmppGeolocItem::QXmppGeolocItem() + : d(new QXmppGeolocItemPrivate) +{ +} + +/// Copy-constructor. +QXmppGeolocItem::QXmppGeolocItem(const QXmppGeolocItem &other) = default; + +QXmppGeolocItem::~QXmppGeolocItem() = default; + +/// Assignment operator. +QXmppGeolocItem &QXmppGeolocItem::operator=(const QXmppGeolocItem &other) = default; + +/// +/// Returns the horizontal GPS error in meters. +/// +std::optional QXmppGeolocItem::accuracy() const +{ + return d->accuracy; +} + +/// +/// Sets the horizontal GPS error. +/// +void QXmppGeolocItem::setAccuracy(std::optional accuracy) +{ + d->accuracy = std::move(accuracy); +} + +/// +/// Returns the country. +/// +QString QXmppGeolocItem::country() const +{ + return d->country; +} + +/// +/// Sets the country. +/// +void QXmppGeolocItem::setCountry(QString country) +{ + d->country = std::move(country); +} + +/// +/// Returns the latitude in decimal degrees. +/// +std::optional QXmppGeolocItem::latitude() const +{ + return d->latitude; +} + +/// +/// Sets the latitude. +/// +void QXmppGeolocItem::setLatitude(std::optional lat) +{ + if (lat && (*lat > 90 || *lat < -90)) { + d->latitude.reset(); + return; + } + d->latitude = std::move(lat); +} + +/// +/// Returns the locality such as a town or a city. +/// +QString QXmppGeolocItem::locality() const +{ + return d->locality; +} + +/// +/// Sets the locality. +/// +void QXmppGeolocItem::setLocality(QString locality) +{ + d->locality = std::move(locality); +} + +/// +/// Returns the longitude in decimal degrees. +/// +std::optional QXmppGeolocItem::longitude() const +{ + return d->longitude; +} + +/// +/// Sets the longitude. +/// +void QXmppGeolocItem::setLongitude(std::optional lon) +{ + if (lon && (*lon > 180 || *lon < -180)) { + d->longitude.reset(); + return; + } + d->longitude = std::move(lon); +} + +/// +/// Returns true, if the element is a valid \xep{0080}: User Location PubSub item. +/// +bool QXmppGeolocItem::isItem(const QDomElement &itemElement) +{ + auto isPayloadValid = [](const QDomElement &payload) -> bool { + return payload.tagName() == QStringLiteral("geoloc") && + payload.namespaceURI() == ns_geoloc; + }; + + return QXmppPubSubItem::isItem(itemElement, isPayloadValid); +} + +/// \cond +std::optional parseOptDouble(const QDomElement &element) +{ + bool ok = false; + if (auto val = element.text().toDouble(&ok); ok) { + return val; + } + return {}; +} + +void QXmppGeolocItem::parsePayload(const QDomElement &tune) +{ + for (auto child = tune.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { + const auto tagName = child.tagName(); + if (tagName == QStringLiteral("accuracy")) { + d->accuracy = parseOptDouble(child); + } else if (tagName == QStringLiteral("country")) { + d->country = child.text(); + } else if (tagName == QStringLiteral("lat")) { + setLatitude(parseOptDouble(child)); + } else if (tagName == QStringLiteral("locality")) { + d->locality = child.text(); + } else if (tagName == QStringLiteral("lon")) { + setLongitude(parseOptDouble(child)); + } + } +} + +auto writeTextEl(QXmlStreamWriter *writer, const QString &name, const std::optional &val) +{ + if (val.has_value()) { + writer->writeTextElement(name, QString::number(*val)); + } +} +auto writeTextEl(QXmlStreamWriter *writer, const QString &name, const QString &val) +{ + helperToXmlAddTextElement(writer, name, val); +} + +void QXmppGeolocItem::serializePayload(QXmlStreamWriter *writer) const +{ + writer->writeStartElement(QStringLiteral("geoloc")); + writer->writeDefaultNamespace(ns_geoloc); + + writeTextEl(writer, QStringLiteral("accuracy"), d->accuracy); + writeTextEl(writer, QStringLiteral("country"), d->country); + writeTextEl(writer, QStringLiteral("lat"), d->latitude); + writeTextEl(writer, QStringLiteral("locality"), d->locality); + writeTextEl(writer, QStringLiteral("lon"), d->longitude); + + writer->writeEndElement(); +} +/// \endcond diff --git a/src/base/QXmppGeolocItem.h b/src/base/QXmppGeolocItem.h new file mode 100644 index 00000000..5b1148f2 --- /dev/null +++ b/src/base/QXmppGeolocItem.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2022 Cochise César +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef QXMPPGEOLOCITEM_H +#define QXMPPGEOLOCITEM_H + +#include "QXmppPubSubItem.h" + +#include + +#include + +class QXmppGeolocItemPrivate; + +class QXMPP_EXPORT QXmppGeolocItem : public QXmppPubSubItem +{ +public: + QXmppGeolocItem(); + QXmppGeolocItem(const QXmppGeolocItem &other); + ~QXmppGeolocItem(); + + QXmppGeolocItem &operator=(const QXmppGeolocItem &other); + + std::optional accuracy() const; + void setAccuracy(std::optional accuracy); + + QString country() const; + void setCountry(QString country); + + std::optional latitude() const; + void setLatitude(std::optional lat); + + QString locality() const; + void setLocality(QString locality); + + std::optional longitude() const; + void setLongitude(std::optional lon); + + static bool isItem(const QDomElement &itemElement); + +protected: + /// \cond + void parsePayload(const QDomElement &payloadElement) override; + void serializePayload(QXmlStreamWriter *writer) const override; + /// \endcond + +private: + QSharedDataPointer d; +}; + +Q_DECLARE_METATYPE(QXmppGeolocItem) + +#endif // QXMPPGEOLOCITEM_H diff --git a/src/client/QXmppUserLocationManager.cpp b/src/client/QXmppUserLocationManager.cpp new file mode 100644 index 00000000..d85a47d5 --- /dev/null +++ b/src/client/QXmppUserLocationManager.cpp @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2022 Cochise César +// SPDX-FileCopyrightText: 2022 Linus Jahn +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "QXmppUserLocationManager.h" + +#include "QXmppConstants_p.h" +#include "QXmppGeolocItem.h" +#include "QXmppPep_p.h" + +using namespace QXmpp::Private; + +/// +/// \class QXmppUserLocationManager +/// +/// The QXmppUserLocationManager implements \xep{0080, User Location}. You'll receive +/// location updates from all presence subscriptions. You can publish location +/// information on the user's account (publish()) and request location information +/// from specific accounts (request()). +/// +/// The manager needs to be added to the client first and also requires the +/// QXmppPubSubManager. +/// \code +/// QXmppClient client; +/// auto *pubSubManager = client.addNewExtension(); +/// auto *locationManager = client.addNewExtension(); +/// \endcode +/// +/// \since QXmpp 1.5 +/// +/// \ingroup Managers +/// + +/// +/// \typedef QXmppUserLocationManager::Item +/// +/// Used pubsub item type. +/// + +/// +/// \typedef QXmppUserLocationManager::GetResult +/// +/// Contains the User Location information or an error. +/// + +/// +/// \typedef QXmppUserLocationManager::PublishResult +/// +/// Contains the ID of the published item on success or a stanza error. +/// + +/// +/// \fn QXmppUserLocationManager::itemReceived() +/// +/// Emitted whenever a \xep{0080, User Location} items event arrives. +/// + +QXmppUserLocationManager::QXmppUserLocationManager() = default; + +QStringList QXmppUserLocationManager::discoveryFeatures() const +{ + return { + ns_geoloc, + ns_geoloc_notify, + }; +} + +/// +/// Request User Location information from an account. +/// +/// \param jid The account JID to request. +/// +auto QXmppUserLocationManager::request(const QString &jid) + -> QFuture +{ + return Pep::request(pubSub(), jid, ns_geoloc, this); +} + +/// +/// Publishes User Location information on the user's account. +/// +/// \param item The User Location item to be published. +/// +auto QXmppUserLocationManager::publish(const QXmppGeolocItem &item) + -> QFuture +{ + return pubSub()->publishPepItem(ns_geoloc, item); +} + +/// \cond +bool QXmppUserLocationManager::handlePubSubEvent(const QDomElement &element, const QString &pubSubService, const QString &nodeName) +{ + return Pep::handlePubSubEvent(element, pubSubService, nodeName, ns_geoloc, this, &QXmppUserLocationManager::itemReceived); +} +/// \endcond diff --git a/src/client/QXmppUserLocationManager.h b/src/client/QXmppUserLocationManager.h new file mode 100644 index 00000000..9033d512 --- /dev/null +++ b/src/client/QXmppUserLocationManager.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2022 Linus Jahn +// SPDX-FileCopyrightText: 2022 Cochise César +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef QXMPPUSERLOCATIONMANAGER_H +#define QXMPPUSERLOCATIONMANAGER_H + +#include "QXmppPubSubEventManager.h" + +#include + +class QXmppGeolocItem; + +class QXMPP_EXPORT QXmppUserLocationManager : public QXmppPubSubEventManager +{ + Q_OBJECT + +public: + using Item = QXmppGeolocItem; + using GetResult = std::variant; + using PublishResult = std::variant; + + QXmppUserLocationManager(); + + QStringList discoveryFeatures() const override; + + QFuture request(const QString &jid); + QFuture publish(const Item &); + + Q_SIGNAL void itemReceived(const QString &jid, const QXmppGeolocItem &); + +protected: + /// \cond + bool handlePubSubEvent(const QDomElement &element, const QString &pubSubService, const QString &nodeName) override; + /// \endcond +}; + +#endif // QXMPPUSERLOCATIONMANAGER_H -- cgit v1.2.3