diff --git a/src/blackcore/context_network_impl.cpp b/src/blackcore/context_network_impl.cpp index 5109ab205..0e034a816 100644 --- a/src/blackcore/context_network_impl.cpp +++ b/src/blackcore/context_network_impl.cpp @@ -17,7 +17,7 @@ #include "vatsimmetarreader.h" #include "airspace_monitor.h" #include "webdataservices.h" -#include "blackmisc/networkutils.h" +#include "blackmisc/network/networkutils.h" #include "blackmisc/aviation/atcstationlist.h" #include "blackmisc/logmessage.h" #include "blackmisc/simplecommandparser.h" diff --git a/src/blackcore/dbus_server.cpp b/src/blackcore/dbus_server.cpp index 75195c409..277467727 100644 --- a/src/blackcore/dbus_server.cpp +++ b/src/blackcore/dbus_server.cpp @@ -8,12 +8,13 @@ */ #include "blackmisc/logmessage.h" -#include "blackmisc/networkutils.h" +#include "blackmisc/network/networkutils.h" #include "dbus_server.h" #include #include using namespace BlackMisc; +using namespace BlackMisc::Network; namespace BlackCore { @@ -270,7 +271,7 @@ namespace BlackCore } // todo: Replace assert with input validation - Q_ASSERT_X(BlackMisc::CNetworkUtils::isValidIPv4Address(p), "p2pAdress", "Wrong IP in String"); + Q_ASSERT_X(CNetworkUtils::isValidIPv4Address(p), "p2pAdress", "Wrong IP in String"); QString p2p = QString("tcp:host=%1,port=%2").arg(h).arg(p); return p2p; } diff --git a/src/blackgui/components/settingsfsxcomponent.cpp b/src/blackgui/components/settingsfsxcomponent.cpp index d7e4da74d..a046e4b2a 100644 --- a/src/blackgui/components/settingsfsxcomponent.cpp +++ b/src/blackgui/components/settingsfsxcomponent.cpp @@ -12,7 +12,7 @@ #include "blackcore/context_simulator.h" #include "blackcore/context_application.h" #include "blackmisc/simulation/fsx/simconnectutilities.h" -#include "blackmisc/networkutils.h" +#include "blackmisc/network/networkutils.h" #include "blackmisc/statusmessage.h" #include "blackmisc/logmessage.h" #include "blackmisc/simulation/fsx/fsxsimulatorsetup.h" diff --git a/src/blackmisc/network/network.h b/src/blackmisc/network/network.h index 0b7b90c20..6535f721b 100644 --- a/src/blackmisc/network/network.h +++ b/src/blackmisc/network/network.h @@ -14,7 +14,7 @@ /*! * \namespace BlackMisc::Network - * \brief Classes related to the traffic network and swift DB, such as VATSIM user etc. + * \brief Classes related to the traffic network, swift DB, such as VATSIM user, utilities, URL etc. */ #include "blackmisc/network/user.h" diff --git a/src/blackmisc/network/networkutils.cpp b/src/blackmisc/network/networkutils.cpp new file mode 100644 index 000000000..52f43901d --- /dev/null +++ b/src/blackmisc/network/networkutils.cpp @@ -0,0 +1,185 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "networkutils.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace BlackMisc::Network; + +namespace BlackMisc +{ + namespace Network + { + bool CNetworkUtils::hasConnectedInterface(bool withDebugOutput) + { + // http://stackoverflow.com/questions/2475266/verfiying-the-network-connection-using-qt-4-4 + QList interfaces = QNetworkInterface::allInterfaces(); + bool result = false; + + for (int i = 0; i < interfaces.count(); i++) + { + QNetworkInterface iface = interfaces.at(i); + + // details of connection + if (withDebugOutput) qDebug() << "name:" << iface.name() << endl << "ip addresses:" << endl << "mac:" << iface.hardwareAddress() << endl; + if (iface.flags().testFlag(QNetworkInterface::IsUp) && !iface.flags().testFlag(QNetworkInterface::IsLoopBack)) + { + // this loop is important + for (int j = 0; j < iface.addressEntries().count(); j++) + { + if (withDebugOutput) qDebug() << iface.addressEntries().at(j).ip().toString() << " / " << iface.addressEntries().at(j).netmask().toString() << endl; + + // we have an interface that is up, and has an ip address, therefore the link is present + // we will only enable this check on first positive, all later results are incorrect + if (!result) + { + result = true; + break; + } + } + } + } + return result; + } + + QStringList CNetworkUtils::getKnownIpAddresses() + { + QStringList ips; + if (!CNetworkUtils::hasConnectedInterface(false)) return ips; + foreach(const QHostAddress & address, QNetworkInterface::allAddresses()) + { + if (address.isLoopback() || address.isNull()) continue; + if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost)) + { + QString a = address.toString(); + if (CNetworkUtils::isValidIPv4Address(a)) ips.append(a); + } + } + return ips; + } + + /* + * Can connect to IP/port? + */ + bool CNetworkUtils::canConnect(const QString &hostAddress, int port, QString &message, int timeoutMs) + { + if (!CNetworkUtils::hasConnectedInterface(false)) + { + message = QObject::tr("No connected network interface", "BlackMisc"); + return false; + } + + // http://qt-project.org/forums/viewthread/9346 + QTcpSocket socket; + bool connected = false; + socket.connectToHost(hostAddress, static_cast(port)); + if (!socket.waitForConnected(timeoutMs)) + { + message = QObject::tr("Connection failed : %1", "BlackMisc").arg(socket.errorString()); + connected = false; + } + else + { + message = QObject::tr("OK, connected", "BlackMisc"); + connected = true; + } + socket.close(); + return connected; + } + + bool CNetworkUtils::canConnect(const Network::CServer &server, QString &message, int timeoutMs) + { + return CNetworkUtils::canConnect(server.getAddress(), server.getPort(), message, timeoutMs); + } + + bool CNetworkUtils::canConnect(const QString &url, QString &message, int timeoutMs) + { + if (url.isEmpty()) + { + message = QObject::tr("Missing URL", "BlackMisc"); + return false; + } + return canConnect(QUrl(url), message, timeoutMs); + } + + bool CNetworkUtils::canConnect(const QUrl &url, QString &message, int timeoutMs) + { + if (!url.isValid()) + { + message = QObject::tr("Invalid URL: %1", "BlackMisc").arg(url.toString()); + return false; + } + + if (url.isRelative()) + { + message = QObject::tr("Relative URL cannot be tested: %1", "BlackMisc").arg(url.toString()); + return false; + } + + QString host(url.host()); + QString scheme(url.scheme().toLower()); + int p = url.port(); + if (p < 0) + { + p = scheme.contains("https") ? 443 : 80; + } + return canConnect(host, p, message, timeoutMs); + } + + bool CNetworkUtils::canConnect(const CUrl &location, QString &message, int timeoutMs) + { + return canConnect(location.getHost(), location.getPort(), message, timeoutMs); + } + + bool CNetworkUtils::isValidIPv4Address(const QString &candidate) + { + QHostAddress address(candidate); + return (QAbstractSocket::IPv4Protocol == address.protocol()); + } + + bool CNetworkUtils::isValidIPv6Address(const QString &candidate) + { + QHostAddress address(candidate); + return (QAbstractSocket::IPv6Protocol == address.protocol()); + } + + bool CNetworkUtils::isValidPort(const QString &port) + { + bool success; + int p = port.toInt(&success); + if (!success) return false; + return (p >= 1 && p <= 65535); + } + + QString CNetworkUtils::buildUrl(const QString &protocol, const QString &server, const QString &baseUrl, const QString &serviceUrl) + { + Q_ASSERT_X(protocol.length() > 3, Q_FUNC_INFO, "worng protocol"); + Q_ASSERT_X(!server.isEmpty(), Q_FUNC_INFO, "missing server"); + Q_ASSERT_X(!serviceUrl.isEmpty(), Q_FUNC_INFO, "missing service URL"); + + QString url(server); + if (!baseUrl.isEmpty()) + { + url.append("/").append(baseUrl); + } + url.append("/").append(serviceUrl); + url.replace("//", "/"); + return protocol + "://" + url; + } + + void CNetworkUtils::ignoreSslVerification(QNetworkRequest &request) + { + QSslConfiguration conf = request.sslConfiguration(); + conf.setPeerVerifyMode(QSslSocket::VerifyNone); + request.setSslConfiguration(conf); + } + } // namespace +} // namespacee diff --git a/src/blackmisc/network/networkutils.h b/src/blackmisc/network/networkutils.h new file mode 100644 index 000000000..7d8e7bbd4 --- /dev/null +++ b/src/blackmisc/network/networkutils.h @@ -0,0 +1,86 @@ +/* Copyright (C) 2013 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_NETWORKUTILS_H +#define BLACKMISC_NETWORKUTILS_H + +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/network/server.h" +#include "blackmisc/network/url.h" +#include +#include +#include + +namespace BlackMisc +{ + namespace Network + { + //! Utilities, e.g. checking whether a network connection can be established + class BLACKMISC_EXPORT CNetworkUtils + { + public: + //! Is a connected interface available? + //! \param withDebugOutput enables some debugging output + //! \return + static bool hasConnectedInterface(bool withDebugOutput = false); + + //! Can connect? + //! \param hostAddress 130.4.20.3, or myserver.com + //! \param port 80, 1234 + //! \param timeoutMs + //! \param message human readable message + //! \return + static bool canConnect(const QString &hostAddress, int port, QString &message, int timeoutMs = 1500); + + //! Can connect to server? + //! \param server + //! \param message human readable message + //! \param timeoutMs + //! \return + static bool canConnect(const BlackMisc::Network::CServer &server, QString &message, int timeoutMs = 1500); + + //! Can connect to URL? + static bool canConnect(const QString &url, QString &message, int timeoutMs = 1500); + + //! Can connect to URL? + static bool canConnect(const QUrl &url, QString &message, int timeoutMs = 1500); + + //! Can connect to URL? + static bool canConnect(const BlackMisc::Network::CUrl &location, QString &message, int timeoutMs = 1500); + + //! Find out my IPv4 address, empty if not possible + static QStringList getKnownIpAddresses(); + + //! Valid IPv4 address + static bool isValidIPv4Address(const QString &candidate); + + //! Valid IPv6 address + static bool isValidIPv6Address(const QString &candidate); + + //! Valid port + static bool isValidPort(const QString &port); + + //! Build / concatenate an URL + static QString buildUrl(const QString &protocol, const QString &server, const QString &baseUrl, const QString &serviceUrl); + + //! Ignore SSL verification such as self signed certificates + static void ignoreSslVerification(QNetworkRequest &request); + + private: + //! Deleted constructor + CNetworkUtils() {} + + }; + } // namespace +} // namespace + +#endif // guard + diff --git a/src/blackmisc/network/url.cpp b/src/blackmisc/network/url.cpp new file mode 100644 index 000000000..cbe4afed1 --- /dev/null +++ b/src/blackmisc/network/url.cpp @@ -0,0 +1,242 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackmisc/network/url.h" +#include "blackmisc/blackmiscfreefunctions.h" +#include "blackmisc/network/networkutils.h" +#include "blackmisc/propertyindex.h" + +namespace BlackMisc +{ + namespace Network + { + CUrl::CUrl(const QString &fullUrl) + { + if (!fullUrl.isEmpty()) + { + setFullUrl(fullUrl); + } + } + + CUrl::CUrl(const QUrl &url) + { + this->setQUrl(url); + } + + CUrl::CUrl(const QString &address, int port) : + CUrl("", address, port, "") + { } + + CUrl::CUrl(const QString &scheme, const QString &address, int port, const QString &path) + : m_host(address.trimmed()), m_port(port), m_path(path.trimmed()) + { + this->setScheme(scheme); + } + + void CUrl::setScheme(const QString &protocol) + { + m_scheme = protocol.trimmed().toLower().replace("://", ""); + } + + void CUrl::setPath(const QString &path) + { + m_path = path.simplified(); + } + + QString CUrl::appendPath(const QString &pathToAppend) + { + QString p(getPath()); + p = p.append("/").append(pathToAppend.trimmed()).replace("//", "/"); + this->setPath(p); + return m_path; + } + + bool CUrl::hasPort() const + { + return m_port >= 0; + } + + bool CUrl::isEmpty() const + { + return m_host.isEmpty(); + } + + bool CUrl::hasDefaultPort() const + { + return isDefaultPort(m_scheme, m_port); + } + + void CUrl::setQuery(const QString &query) + { + QString q(stripQueryString(query)); + m_query = q; + } + + bool CUrl::hasQuery() const + { + return !m_query.isEmpty(); + } + + QString CUrl::appendQuery(const QString &queryToAppend) + { + QString q(stripQueryString(queryToAppend)); + if (q.isEmpty()) { return m_query; } + if (!hasQuery()) + { + this->setQuery(q); + } + else + { + m_query = m_query + "&" + q; + } + return m_query; + } + + QString CUrl::getFullUrl() const + { + Q_ASSERT_X(!m_host.isEmpty(), Q_FUNC_INFO, "missing address"); + if (m_host.isEmpty()) { return ""; } + + QString qn(m_host); + if (!hasDefaultPort() && hasPort()) { qn = qn.append(":").append(QString::number(m_port)); } + if (hasPath()) { qn = qn.append("/").append(m_path).replace("//", "/"); } + if (hasQuery()) { qn = qn.append("?").append(m_query); } + if (hasScheme()) { qn = QString(this->getScheme()).append("://").append(qn); } + return qn; + } + + void CUrl::setFullUrl(const QString &fullUrl) + { + setQUrl(QUrl(fullUrl)); + } + + QUrl CUrl::toQUrl() const + { + return QUrl(getFullUrl()); + } + + void CUrl::setQUrl(const QUrl &url) + { + this->setPort(url.port()); + this->setHost(url.host()); + this->setScheme(url.scheme()); + this->setPath(url.path()); + this->setQuery(url.query()); + } + + CUrl CUrl::withAppendedPath(const QString &path) const + { + if (path.isEmpty()) { return *this; } + CUrl url(*this); + url.appendPath(path); + return url; + } + + CUrl CUrl::withAppendedQuery(const QString &query) const + { + if (query.isEmpty()) { return *this; } + CUrl url(*this); + url.appendQuery(query); + return url; + } + + QString CUrl::convertToQString(bool i18n) const + { + Q_UNUSED(i18n); + return getFullUrl(); + } + + QJsonObject CUrl::toJson() const + { + QPair v("url", getFullUrl()); + QJsonObject json({ v }); + return json; + } + + void CUrl::convertFromJson(const QJsonObject &json) + { + QString url(json.value("url").toString()); + this->setFullUrl(url); + } + + int CUrl::protocolToDefaultPort(const QString &protocol) + { + const QString p(protocol.trimmed().toLower()); + if (p == "ftp") return 20; + if (p == "https") return 443; + if (p == "http") return 80; + return -1; + } + + bool CUrl::isDefaultPort(const QString &protocol, int port) + { + int p = protocolToDefaultPort(protocol); + if (p < 0) { return false; } + return port == p; + } + + QString CUrl::stripQueryString(const QString query) + { + QString q(query.trimmed()); + if (q.startsWith("?") || q.startsWith("&")) + { + q = q.mid(1); + } + if (q.endsWith("?") || q.endsWith("&")) + { + q = q.left(q.size() - 1); + } + return q; + } + + CVariant CUrl::propertyByIndex(const BlackMisc::CPropertyIndex &index) const + { + if (index.isMyself()) { return CVariant::from(*this); } + ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexHost: + return CVariant::fromValue(this->m_host); + case IndexPort: + return CVariant::fromValue(this->m_port); + case IndexScheme: + return CVariant::fromValue(this->m_scheme); + case IndexPath: + return CVariant::fromValue(this->m_path); + default: + return CValueObject::propertyByIndex(index); + } + } + + void CUrl::setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index) + { + if (index.isMyself()) { (*this) = variant.to(); return; } + ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexHost: + this->setHost(variant.value()); + break; + case IndexPort: + this->setPort(variant.value()); + break; + case IndexPath: + this->setPath(variant.value()); + break; + case IndexScheme: + this->setScheme(variant.value()); + break; + default: + CValueObject::setPropertyByIndex(variant, index); + break; + } + } + + } // namespace +} // namespace diff --git a/src/blackmisc/network/url.h b/src/blackmisc/network/url.h new file mode 100644 index 000000000..4c53089b9 --- /dev/null +++ b/src/blackmisc/network/url.h @@ -0,0 +1,162 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_NETWORK_NETWORKLOCATION_H +#define BLACKMISC_NETWORK_NETWORKLOCATION_H + +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/valueobject.h" +#include + +namespace BlackMisc +{ + namespace Network + { + //! Value object encapsulating information of a location, + //! kind of simplied CValueObject compliant version of QUrl + class BLACKMISC_EXPORT CUrl : public CValueObject + { + public: + //! Properties by index + enum ColumnIndex + { + IndexScheme = BlackMisc::CPropertyIndex::GlobalIndexCUrl, + IndexHost, + IndexPort, + IndexPath, + IndexQuery + }; + + //! Default constructor. + CUrl(const QString &fullUrl = QString()); + + //! By QUrl. + CUrl(const QUrl &url); + + //! Constructor. + CUrl(const QString &address, int port); + + //! Constructor. + CUrl(const QString &scheme, const QString &address, int port, const QString &path); + + //! Get host. + const QString &getHost() const { return m_host; } + + //! Set address (e.g. myserver.foo.com) + void setHost(const QString &address) { m_host = address.trimmed(); } + + //! Get protocl + const QString &getScheme() const { return m_scheme; } + + //! Set protocol + void setScheme(const QString &protocol); + + //! Protocol + bool hasScheme() const { return !m_scheme.isEmpty(); } + + //! Get path + const QString &getPath() const { return m_path; } + + //! Set path + void setPath(const QString &path); + + //! Append path + QString appendPath(const QString &pathToAppend); + + //! Has path? + bool hasPath() const { return !m_path.isEmpty(); } + + //! Get port + int getPort() const { return m_port; } + + //! Set port + void setPort(int port) { m_port = port; } + + //! Port? + bool hasPort() const; + + //! Default port + bool hasDefaultPort() const; + + //! Get query part + QString getQuery() const { return m_query; } + + //! Set query + void setQuery(const QString &query); + + //! Query string? + bool hasQuery() const; + + //! Append query + QString appendQuery(const QString &queryToAppend); + + //! Empty + bool isEmpty() const; + + //! Qualified name + QString getFullUrl() const; + + //! Set full URL + void setFullUrl(const QString &fullUrl); + + //! To QUrl + QUrl toQUrl() const; + + //! To QUrl + void setQUrl(const QUrl &url); + + //! Append path + CUrl withAppendedPath(const QString &path) const; + + //! Append path + CUrl withAppendedQuery(const QString &query) const; + + //! \copydoc CValueObject::propertyByIndex + CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + + //! \copydoc CValueObject::setPropertyByIndex + void setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index); + + //! \copydoc CValueObject::convertToQString() + QString convertToQString(bool i18n = false) const; + + //! \copydoc BlackMisc::Mixin::JsonByTuple::toJson + QJsonObject toJson() const; + + //! \copydoc BlackMisc::Mixin::JsonByTuple::convertFromJson + void convertFromJson(const QJsonObject &json); + + //! Protocol to default port + static int protocolToDefaultPort(const QString &protocol); + + //! Default port for given protocol + static bool isDefaultPort(const QString &protocol, int port); + + //! Convert to QUrl + operator QUrl() const { return this->toQUrl(); } + + private: + BLACK_ENABLE_TUPLE_CONVERSION(CUrl) + QString m_scheme; + QString m_host; + int m_port = -1; + QString m_path; + QString m_query; + + static QString stripQueryString(const QString query); + }; + } // namespace +} // namespace + +BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::Network::CUrl, (o.m_scheme, o.m_host, o.m_port, o.m_path, o.m_query)) +Q_DECLARE_METATYPE(BlackMisc::Network::CUrl) + +#endif // guard diff --git a/src/blackmisc/network/urllist.cpp b/src/blackmisc/network/urllist.cpp new file mode 100644 index 000000000..ffa8e89ef --- /dev/null +++ b/src/blackmisc/network/urllist.cpp @@ -0,0 +1,91 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackmisc/network/urllist.h" +#include "blackmisc/math/mathutils.h" + +using namespace BlackMisc::Math; + +namespace BlackMisc +{ + namespace Network + { + CUrlList::CUrlList() { } + + CUrlList::CUrlList(const QStringList &listOfUrls) + { + for (const QString &url : listOfUrls) + { + this->push_back(CUrl(url)); + } + } + + CUrlList::CUrlList(const CSequence &other) : + CSequence(other) + { } + + CUrl CUrlList::getRandomUrl() const + { + if (this->isEmpty()) { return CUrl(); } + if (this->size() == 1) { return this->front();} + int i = CMathUtils::randomInteger(0, this->size() - 1); + return (*this)[i]; + } + + CUrl CUrlList::getRandomWithout(const CUrlList &exclude) const + { + CUrlList copy(*this); + copy.removeIfIn(exclude); + if (copy.isEmpty()) { return CUrl(); } + return copy.getRandomUrl(); + } + + CUrl CUrlList::getNextUrl(bool randomStart) const + { + if (this->isEmpty()) { return CUrl(); } + if (this->size() == 1) { return this->front();} + if (m_currentIndexDistributedLoad < 0) + { + // random start point + m_currentIndexDistributedLoad = randomStart ? + CMathUtils::randomInteger(0, this->size() - 1) : + 0; + } + else + { + m_currentIndexDistributedLoad++; + if (m_currentIndexDistributedLoad >= this->size()) + { + m_currentIndexDistributedLoad = 0; + } + } + return (*this)[m_currentIndexDistributedLoad]; + } + + CUrl CUrlList::getNextUrlWithout(const CUrlList &exclude, bool randomStart) const + { + CUrlList copy(*this); + copy.removeIfIn(exclude); + if (copy.isEmpty()) { return CUrl(); } + return copy.getNextUrl(randomStart); + } + + CUrlList CUrlList::appendPath(const QString &path) const + { + if (path.isEmpty() || this->isEmpty()) { return (*this); } + CUrlList urls; + for (const CUrl &url : (*this)) + { + urls.push_back(url.withAppendedPath(path)); + } + return urls; + } + + } // namespace +} // namespace diff --git a/src/blackmisc/network/urllist.h b/src/blackmisc/network/urllist.h new file mode 100644 index 000000000..15076e26c --- /dev/null +++ b/src/blackmisc/network/urllist.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_NETWORK_NETWORKLOCATIONLIST_H +#define BLACKMISC_NETWORK_NETWORKLOCATIONLIST_H + +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/network/url.h" +#include "blackmisc/collection.h" +#include "blackmisc/sequence.h" + +namespace BlackMisc +{ + namespace Network + { + //! Value object encapsulating a list of servers. + class BLACKMISC_EXPORT CUrlList : + public CSequence, + public BlackMisc::Mixin::MetaType + { + public: + BLACKMISC_DECLARE_USING_MIXIN_METATYPE(CUrlList) + + //! Default constructor. + CUrlList(); + + //! By list of URLs + explicit CUrlList(const QStringList &listOfUrls); + + //! Construct from a base class object. + CUrlList(const CSequence &other); + + //! Random location for distributed load + CUrl getRandomUrl() const; + + //! Random location for distributed load + CUrl getRandomWithout(const CUrlList &exclude) const; + + //! Round robin with random start point + CUrl getNextUrl(bool randomStart = true) const; + + //! Round robin with random start point + CUrl getNextUrlWithout(const CUrlList &exclude, bool randomStart = true) const; + + //! Append path to all URLs + CUrlList appendPath(const QString &path) const; + + private: + mutable int m_currentIndexDistributedLoad = -1; + }; + } //namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Network::CUrlList) +Q_DECLARE_METATYPE(BlackMisc::CCollection) +Q_DECLARE_METATYPE(BlackMisc::CSequence) + +#endif //guard diff --git a/src/blackmisc/networkutils.cpp b/src/blackmisc/networkutils.cpp deleted file mode 100644 index d1d30abca..000000000 --- a/src/blackmisc/networkutils.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* Copyright (C) 2013 VATSIM Community / authors - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "networkutils.h" -#include -#include -#include -#include -#include -#include - -namespace BlackMisc -{ - /* - * Connected interface? - */ - bool CNetworkUtils::hasConnectedInterface(bool withDebugOutput) - { - // http://stackoverflow.com/questions/2475266/verfiying-the-network-connection-using-qt-4-4 - QList interfaces = QNetworkInterface::allInterfaces(); - bool result = false; - - for (int i = 0; i < interfaces.count(); i++) - { - QNetworkInterface iface = interfaces.at(i); - - // details of connection - if (withDebugOutput) qDebug() << "name:" << iface.name() << endl << "ip addresses:" << endl << "mac:" << iface.hardwareAddress() << endl; - if (iface.flags().testFlag(QNetworkInterface::IsUp) && !iface.flags().testFlag(QNetworkInterface::IsLoopBack)) - { - // this loop is important - for (int j = 0; j < iface.addressEntries().count(); j++) - { - if (withDebugOutput) qDebug() << iface.addressEntries().at(j).ip().toString() << " / " << iface.addressEntries().at(j).netmask().toString() << endl; - - // we have an interface that is up, and has an ip address, therefore the link is present - // we will only enable this check on first positive, all later results are incorrect - if (!result) - { - result = true; - break; - } - } - } - } - return result; - } - - /* - * My IP - */ - QStringList CNetworkUtils::getKnownIpAddresses() - { - QStringList ips; - if (!CNetworkUtils::hasConnectedInterface(false)) return ips; - foreach(const QHostAddress & address, QNetworkInterface::allAddresses()) - { - if (address.isLoopback() || address.isNull()) continue; - if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost)) - { - QString a = address.toString(); - if (CNetworkUtils::isValidIPv4Address(a)) ips.append(a); - } - } - return ips; - } - - /* - * Can connect to IP/port? - */ - bool CNetworkUtils::canConnect(const QString &hostAddress, int port, QString &message, int timeoutMs) - { - if (!CNetworkUtils::hasConnectedInterface(false)) - { - message = QObject::tr("No connected network interface", "BlackMisc"); - return false; - } - - // http://qt-project.org/forums/viewthread/9346 - QTcpSocket socket; - bool connected = false; - socket.connectToHost(hostAddress, static_cast(port)); - if (!socket.waitForConnected(timeoutMs)) - { - message = QObject::tr("Connection failed : %1", "BlackMisc").arg(socket.errorString()); - connected = false; - } - else - { - message = QObject::tr("OK, connected", "BlackMisc"); - connected = true; - } - socket.close(); - return connected; - } - - /* - * Can connect server? - */ - bool CNetworkUtils::canConnect(const Network::CServer &server, QString &message, int timeoutMs) - { - return CNetworkUtils::canConnect(server.getAddress(), server.getPort(), message, timeoutMs); - } - - /* - * Can connect url? - */ - bool CNetworkUtils::canConnect(const QString &url, QString &message, int timeoutMs) - { - if (url.isEmpty()) - { - message = QObject::tr("Missing URL", "BlackMisc"); - return false; - } - return canConnect(QUrl(url), message, timeoutMs); - } - - /* - * Can connect url? - */ - bool CNetworkUtils::canConnect(const QUrl &url, QString &message, int timeoutMs) - { - if (!url.isValid()) - { - message = QObject::tr("Invalid URL: %1", "BlackMisc").arg(url.toString()); - return false; - } - - if (url.isRelative()) - { - message = QObject::tr("Relative URL cannot be tested: %1", "BlackMisc").arg(url.toString()); - return false; - } - - QString scheme(url.scheme().toLower()); - int p = scheme.contains("https") ? 443 : 80; - p = url.port(80); - - QString host(url.host()); - return canConnect(host, p, message, timeoutMs); - } - - /* - * Valid IPv4 address - */ - bool CNetworkUtils::isValidIPv4Address(const QString &candidate) - { - QHostAddress address(candidate); - return (QAbstractSocket::IPv4Protocol == address.protocol()); - } - - /* - * Valid IPv6 address - */ - bool CNetworkUtils::isValidIPv6Address(const QString &candidate) - { - QHostAddress address(candidate); - return (QAbstractSocket::IPv6Protocol == address.protocol()); - } - - /* - * Valid port? - */ - bool CNetworkUtils::isValidPort(const QString &port) - { - bool success; - int p = port.toInt(&success); - if (!success) return false; - return (p >= 1 && p <= 65535); - } - - /* - * Build URL - */ - QString CNetworkUtils::buildUrl(const QString &protocol, const QString &server, const QString &baseUrl, const QString &serviceUrl) - { - Q_ASSERT_X(protocol.length() > 3, Q_FUNC_INFO, "worng protocol"); - Q_ASSERT_X(!server.isEmpty(), Q_FUNC_INFO, "missing server"); - Q_ASSERT_X(!serviceUrl.isEmpty(), Q_FUNC_INFO, "missing service URL"); - - QString url(server); - if (!baseUrl.isEmpty()) - { - url.append("/").append(baseUrl); - } - url.append("/").append(serviceUrl); - url.replace("//", "/"); - return protocol + "://" + url; - } -} // namespace diff --git a/src/blackmisc/networkutils.h b/src/blackmisc/networkutils.h deleted file mode 100644 index 367a28cd1..000000000 --- a/src/blackmisc/networkutils.h +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright (C) 2013 - * swift Project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKMISC_NETWORKUTILS_H -#define BLACKMISC_NETWORKUTILS_H - -#include "blackmiscexport.h" -#include "network/server.h" -#include -#include - -namespace BlackMisc -{ - //! Utilities, e.g. checking whether a network connection can be established - class BLACKMISC_EXPORT CNetworkUtils - { - public: - //! - //! Is a connected interface available? - //! \param withDebugOutput enables some debugging output - //! \return - static bool hasConnectedInterface(bool withDebugOutput = false); - - //! - //! Can connect? - //! \param hostAddress 130.4.20.3, or myserver.com - //! \param port 80, 1234 - //! \param timeoutMs - //! \param message human readable message - //! \return - static bool canConnect(const QString &hostAddress, int port, QString &message, int timeoutMs = 1500); - - //! - //! Can connect to server? - //! \param server - //! \param message human readable message - //! \param timeoutMs - //! \return - static bool canConnect(const BlackMisc::Network::CServer &server, QString &message, int timeoutMs = 1500); - - //! Can connect to URL? - static bool canConnect(const QString &url, QString &message, int timeoutMs = 1500); - - //! Can connect to URL? - static bool canConnect(const QUrl &url, QString &message, int timeoutMs = 1500); - - //! Find out my IPv4 address, empty if not possible - static QStringList getKnownIpAddresses(); - - //! Valid IPv4 address - static bool isValidIPv4Address(const QString &candidate); - - //! Valid IPv6 address - static bool isValidIPv6Address(const QString &candidate); - - //! Valid port - static bool isValidPort(const QString &port); - - //! Build / concatenate an URL - static QString buildUrl(const QString &protocol, const QString &server, const QString &baseUrl, const QString &serviceUrl); - - private: - //! Deleted constructor - CNetworkUtils() {} - - }; -} // namespace - -#endif // guard - diff --git a/src/swiftcore/main.cpp b/src/swiftcore/main.cpp index 22e02b6d6..1a77fbea5 100644 --- a/src/swiftcore/main.cpp +++ b/src/swiftcore/main.cpp @@ -15,7 +15,7 @@ #include "blackcore/dbus_server.h" #include "blackmisc/icons.h" #include "blackmisc/worker.h" -#include "blackmisc/networkutils.h" +#include "blackmisc/network/networkutils.h" #include "blackmisc/blackmiscfreefunctions.h" #include "blackmisc/project.h" #include "blackmisc/loghandler.h" diff --git a/src/swiftdata/main.cpp b/src/swiftdata/main.cpp index f416d3355..4dc024729 100644 --- a/src/swiftdata/main.cpp +++ b/src/swiftdata/main.cpp @@ -10,7 +10,7 @@ #include "swiftdata.h" #include "blackmisc/icons.h" #include "blackmisc/worker.h" -#include "blackmisc/networkutils.h" +#include "blackmisc/network/networkutils.h" #include "blackmisc/blackmiscfreefunctions.h" #include "blackmisc/project.h" #include "blackmisc/loghandler.h"