Commit 41397521 authored by Harald Sitter's avatar Harald Sitter 🌈
Browse files

add initial wsdiscovery support

Summary:
needs kdsoap >= 1.8.50 (current master).
builds a static variant of kdsoap-ws-discovery-client but can also
use system's version if available.

also reinvents how discovery works:
there are now Discoverers for dnssd and wsd that get started/stopped.
the Discoverers discover servers and emit them as Discoveries. Discoveries
are then converted to udsentries for kio.

BUG: 392447
FIXED-IN: 20.04.0

Test Plan:
discover all the things!

- windows 10 test vm with remote address
- https://github.com/christgau/wsdd on remote host
- avahi smb on remote host

Reviewers: dfaure, #frameworks, #dolphin, ngraham

Reviewed By: dfaure, ngraham

Subscribers: meven, schmeisser, bcooksley, ngraham, caspermeijn, davidedmundson, kde-frameworks-devel, kfm-devel

Tags: #dolphin, #frameworks

Differential Revision: https://phabricator.kde.org/D25682
parent a813d67c
option(BUILD_KDSoapWSDiscoveryClient "Automatically build WSD client if a system one isn't found." ON)
find_package(KDSoapWSDiscoveryClient QUIET)
set(INTERNAL_WSDCLIENT ${BUILD_KDSoapWSDiscoveryClient})
if(KDSoapWSDiscoveryClient_FOUND)
set(INTERNAL_WSDCLIENT OFF)
endif()
if(INTERNAL_WSDCLIENT)
# Special internal version, mangled to be a STATIC lib.
# This is only useful and necessary until the library has
# its API finalized and gotten a stable release.
add_subdirectory(kdsoap-ws-discovery-client)
endif()
add_feature_info("Internal KDSoapWSDiscoveryClient" INTERNAL_WSDCLIENT
"Building using internal client because a system-provided version could not be found.")
add_feature_info("SMB DNS-SD Discovery" HAVE_KDNSSD_WITH_SIGNAL_RACE_PROTECTION
"Discover SMB hosts via DNS-SD/Avahi/Bonjour. KF5DNSSD >= 5.54 is required to support this.")
......@@ -19,7 +34,11 @@ set(kio_smb_PART_SRCS
kio_smb_dir.cpp
kio_smb_file.cpp
kio_smb_internal.cpp
kio_smb_mount.cpp )
kio_smb_mount.cpp
wsdiscoverer.cpp
dnssddiscoverer.cpp
discovery.cpp
)
ecm_qt_declare_logging_category(kio_smb_PART_SRCS
HEADER smb-logsettings.h
......@@ -35,7 +54,9 @@ target_link_libraries(kio_smb
KF5::I18n
${SAMBA_LIBRARIES}
Qt5::Network
KF5::DNSSD)
KF5::DNSSD
KDSoap::WSDiscoveryClient
)
else()
set(kio_smb_PART_SRCS
kio_smb_win.cpp)
......@@ -51,6 +72,7 @@ target_link_libraries(kio_smb
endif()
set_target_properties(kio_smb PROPERTIES OUTPUT_NAME "smb")
set_target_properties(kio_smb PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf5/kio")
install(TARGETS kio_smb DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kio)
......
/*
Copyright 2019 Harald Sitter <sitter@kde.org>
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 3 of
the License or any later version accepted by the membership of
KDE e.V. (or its successor approved by the membership of KDE
e.V.), which shall act as a proxy defined in Section 14 of
version 3 of the license.
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, see <https://www.gnu.org/licenses/>.
*/
#include "discovery.h"
Discovery::Discovery()
{
qRegisterMetaType<Discovery::Ptr>("Discovery::Ptr");
}
Discovery::~Discovery() = default;
Discoverer::Discoverer() = default;
Discoverer::~Discoverer() = default;
/*
Copyright 2019 Harald Sitter <sitter@kde.org>
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 3 of
the License or any later version accepted by the membership of
KDE e.V. (or its successor approved by the membership of KDE
e.V.), which shall act as a proxy defined in Section 14 of
version 3 of the license.
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, see <https://www.gnu.org/licenses/>.
*/
#ifndef DISCOVERY_H
#define DISCOVERY_H
#include <QSharedPointer>
#include <KIO/UDSEntry>
class Discovery
{
public:
typedef QSharedPointer<Discovery> Ptr;
Discovery();
virtual ~Discovery();
virtual KIO::UDSEntry toEntry() const = 0;
};
class Discoverer
{
public:
Discoverer();
virtual ~Discoverer();
virtual void start() = 0;
virtual void stop() = 0;
virtual bool isFinished() const = 0;
// Implement as signal!
virtual void newDiscovery(Discovery::Ptr discovery) = 0;
virtual void finished() = 0;
};
#endif // DISCOVERY_H
/*
Copyright 2019 Harald Sitter <sitter@kde.org>
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 3 of
the License or any later version accepted by the membership of
KDE e.V. (or its successor approved by the membership of KDE
e.V.), which shall act as a proxy defined in Section 14 of
version 3 of the license.
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, see <https://www.gnu.org/licenses/>.
*/
#include "dnssddiscoverer.h"
#include "kio_smb.h"
DNSSDDiscovery::DNSSDDiscovery(KDNSSD::RemoteService::Ptr service)
: m_service(service)
{
}
KIO::UDSEntry DNSSDDiscovery::toEntry() const
{
KIO::UDSEntry entry;
entry.fastInsert(KIO::UDSEntry::UDS_NAME, m_service->serviceName());
entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH));
entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, "network-server");
// TODO: it may be better to resolve the host to an ip address. dnssd
// being able to find a service doesn't mean name resolution is
// properly set up for its domain. So, we may not be able to resolve
// this without help from avahi. OTOH KDNSSD doesn't have API for this
// and from a platform POV we should probably assume that if avahi
// is functional it is also set up as resolution provider.
// Given the plugin design on glibc's libnss however I am not sure
// that assumption will be true all the time. ~sitter, 2018
QUrl u;
u.setScheme(QStringLiteral("smb"));
u.setHost(m_service->hostName());
if (m_service->port() > 0 && m_service->port() != 445 /* default smb */) {
u.setPort(m_service->port());
}
u.setPath("/"); // https://bugs.kde.org/show_bug.cgi?id=388922
entry.fastInsert(KIO::UDSEntry::UDS_URL, u.url());
entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE,
QStringLiteral("application/x-smb-server"));
return entry;
}
DNSSDDiscoverer::DNSSDDiscoverer()
{
connect(&m_browser, &KDNSSD::ServiceBrowser::serviceAdded,
this, [=](KDNSSD::RemoteService::Ptr service){
qCDebug(KIO_SMB_LOG) << "DNSSD added:"
<< service->serviceName()
<< service->type()
<< service->domain()
<< service->hostName()
<< service->port();
// Manual contains check. We need to use the == of the underlying
// objects, not the pointers. The same service may have >1
// RemoteService* instances representing it, so the == impl of
// RemoteService::Ptr is useless here.
for (const auto &servicePtr : qAsConst(m_services)) {
if (*service == *servicePtr) {
return;
}
}
connect(service.data(), &KDNSSD::RemoteService::resolved,
this, [=] {
++m_resolvedCount;
emit newDiscovery(Discovery::Ptr(new DNSSDDiscovery(service)));
maybeFinish();
});
// Schedule resolution of hostname. We'll later call resolve
// which will block until the resolution is done. This basically
// gives us a head start on discovery.
service->resolveAsync();
m_services.append(service);
});
connect(&m_browser, &KDNSSD::ServiceBrowser::finished, this, &DNSSDDiscoverer::stop);
}
void DNSSDDiscoverer::start()
{
m_browser.startBrowse();
}
void DNSSDDiscoverer::stop()
{
m_browser.disconnect();
m_disconnected = true;
maybeFinish();
}
bool DNSSDDiscoverer::isFinished() const
{
return m_disconnected && m_services.count() == m_resolvedCount;
}
void DNSSDDiscoverer::maybeFinish()
{
if (isFinished()) {
emit finished();
}
}
/*
Copyright 2019 Harald Sitter <sitter@kde.org>
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 3 of
the License or any later version accepted by the membership of
KDE e.V. (or its successor approved by the membership of KDE
e.V.), which shall act as a proxy defined in Section 14 of
version 3 of the license.
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, see <https://www.gnu.org/licenses/>.
*/
#ifndef DNSSDDISCOVERER_H
#define DNSSDDISCOVERER_H
#include <QObject>
#include <DNSSD/ServiceBrowser>
#include <DNSSD/RemoteService>
#include "discovery.h"
class DNSSDDiscovery : public Discovery
{
public:
DNSSDDiscovery(KDNSSD::RemoteService::Ptr service);
KIO::UDSEntry toEntry() const override;
private:
KDNSSD::RemoteService::Ptr m_service;
};
class DNSSDDiscoverer : public QObject, public Discoverer
{
Q_OBJECT
public:
DNSSDDiscoverer();
void start() override;
bool isFinished() const override;
signals:
void newDiscovery(Discovery::Ptr discovery) override;
void finished() override;
private:
void stop() override;
void maybeFinish();
KDNSSD::ServiceBrowser m_browser { QStringLiteral("_smb._tcp") };
QList<KDNSSD::RemoteService::Ptr> m_services;
int m_resolvedCount = 0;
bool m_disconnected = false;
};
#endif // DNSSDDISCOVERER_H
fedora:
image: registry.gitlab.com/caspermeijn/docker-images/fedora-build-onvifviewer:latest
stage: build
script:
- cd ..
- git clone https://github.com/KDAB/KDSoap.git
- mkdir build-KDSoap
- cd build-KDSoap/
- cmake ../KDSoap/ -DCMAKE_INSTALL_PREFIX=/usr/local
- make
- make install
- cd ..
- mkdir build-kdsoap-ws-discovery-client
- cd build-kdsoap-ws-discovery-client
- cmake $CI_PROJECT_DIR
- make
- make install
# Copyright (C) 2019 Casper Meijn <casper@meijn.net>
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
cmake_minimum_required(VERSION 3.7)
project(kdsoap-ws-discovery-client VERSION 0.1)
include(FeatureSummary)
find_package(ECM 5.54.0 NO_MODULE)
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules")
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
include(KDEInstallDirs)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(KDECMakeSettings)
include(GenerateExportHeader)
set(REQUIRED_QT_VERSION 5.9.0)
find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS Core Network Xml)
find_package(KDSoap 1.8.50 REQUIRED)
set_package_properties(KDSoap PROPERTIES
DESCRIPTION "A Qt-based client-side and server-side SOAP component"
URL "http://www.kdab.com/products/kd-soap"
TYPE REQUIRED
PURPOSE "Support for SOAP client protocol"
)
include(ECMSetupVersion)
include(ECMGenerateHeaders)
include(ECMQtDeclareLoggingCategory)
include(ECMAddQch)
option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF)
add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)")
ecm_setup_version(PROJECT
VARIABLE_PREFIX WSDISCOVERY
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kdsoap_wsdiscovery_client_version.h"
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KDSoapWSDiscoveryClientConfigVersion.cmake"
)
add_subdirectory(src)
#add_subdirectory(examples)
#if (BUILD_TESTING)
# add_subdirectory(autotests)
# add_subdirectory(tests)
#endif()
## create a Config.cmake and a ConfigVersion.cmake file and install them
#set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KDSoapWSDiscoveryClient")
#if (BUILD_QCH)
# ecm_install_qch_export(
# TARGETS KDSoapWSDiscoveryClient_QCH
# FILE KDSoapWSDiscoveryClientQchTargets.cmake
# DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
# COMPONENT Devel
# )
# set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KDSoapWSDiscoveryClientQchTargets.cmake\")")
#endif()
##include(CMakePackageConfigHelpers)
##configure_package_config_file(
## "${CMAKE_CURRENT_SOURCE_DIR}/KDSoapWSDiscoveryClientConfig.cmake.in"
## "${CMAKE_CURRENT_BINARY_DIR}/KDSoapWSDiscoveryClientConfig.cmake"
## INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
##)
## install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kdsoap_wsdiscovery_client_version.h
## DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KDSoapWSDiscoveryClient
## COMPONENT Devel)
## install(FILES
## "${CMAKE_CURRENT_BINARY_DIR}/KDSoapWSDiscoveryClientConfig.cmake"
## "${CMAKE_CURRENT_BINARY_DIR}/KDSoapWSDiscoveryClientConfigVersion.cmake"
## DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
## COMPONENT Devel)
## install(EXPORT KDSoapWSDiscoveryClientTargets
## DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
## FILE KDSoapWSDiscoveryClientTargets.cmake
## NAMESPACE KDSoap::)
##feature_summary(
## WHAT ALL
## FATAL_ON_MISSING_REQUIRED_PACKAGES
##)
@PACKAGE_INIT@
find_dependency(Qt5Core @REQUIRED_QT_VERSION@)
include("${CMAKE_CURRENT_LIST_DIR}/KDSoapWSDiscoveryClientTargets.cmake")
This diff is collapsed.
# KDSoap WS-Discovery client
This project is trying to create a WS-Discovery client library based on the KDSoap
library. It uses modern C++ 11 and Qt 5. The initial development is done for
[ONVIFViewer](https://gitlab.com/caspermeijn/onvifviewer), a ONVIF camera viewer.
However the library is designed to be useful as a generic WS-Discovery client.
## Current state
The library is not yet ready for production. It needs patches to KDSoap, most are
applied to [this tree](https://github.com/caspermeijn/KDSoap). It also needs some
internal KDSoap headers.
## Contributions
Contributions to the project are appreciated. See the
[issue tracker](https://gitlab.com/caspermeijn/kdsoap-ws-discovery-client/issues)
to open tasks and feel free to open a merge request for the changes you made.
Compatibility testing with devices you own is also useful. These could be ONVIF
cameras, printers or other WS-Discovery devices. Open an issue in the
[issue tracker](https://gitlab.com/caspermeijn/kdsoap-ws-discovery-client/issues)
to report your test result (good and bad result are both welcome).
## Example
The onif-discover example will send out a Probe message for ONVIF devices and will list the properties of the responding devices.
```
$ onvif-discover
Starting ONVIF discovery for 5 seconds
ProbeMatch received:
Endpoint reference: "urn:uuid:5f5a69c2-e0ae-504f-829b-00AFA538AC17"
Type: "NetworkVideoTransmitter" in namespace "http://www.onvif.org/ver10/network/wsdl"
Scope: "onvif://www.onvif.org/Profile/Streaming"
Scope: "onvif://www.onvif.org/model/C6F0SiZ0N0P0L0"
Scope: "onvif://www.onvif.org/name/IPCAM"
Scope: "onvif://www.onvif.org/location/country/china"
XAddr: "http://192.168.128.248:8080/onvif/devices"
```
## Building
To build this project you need a copy of KDSoap and have extra-cmake-modules installed.
```
$ sudo apt install extra-cmake-modules
$ git checkout https://github.com/caspermeijn/KDSoap.git
$ git checkout https://gitlab.com/caspermeijn/kdsoap-ws-discovery-client.git
$ cd kdsoap-ws-discovery-client && mkdir build && cd build
$ cmake .. && make && make install
```
# Copyright (C) 2019 Casper Meijn <casper@meijn.net>
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
add_subdirectory(onvif-discover)
# Copyright (C) 2019 Casper Meijn <casper@meijn.net>
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
cmake_minimum_required(VERSION 3.7)
project(onvif-discover)
add_executable(${PROJECT_NAME}
"main.cpp"
"onvifdiscover.cpp"
)
target_link_libraries(${PROJECT_NAME} KDSoapWSDiscoveryClient)
/* Copyright (C) 2019 Casper Meijn <casper@meijn.net>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <QCoreApplication>
#include <QDebug>
#include <QTimer>
#include "onvifdiscover.h"
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
qDebug() << "Starting ONVIF discovery for 5 seconds";
QTimer::singleShot(5000, &app, &QCoreApplication::quit);
OnvifDiscover * onvifDiscover = new OnvifDiscover(&app);
onvifDiscover->start();
return app.exec();
}
/* Copyright (C) 2019 Casper Meijn <casper@meijn.net>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "onvifdiscover.h"
#include <QDebug>
#include <QSharedPointer>
#include <WSDiscoveryClient>
#include <WSDiscoveryProbeJob>
#include <WSDiscoveryTargetService>
OnvifDiscover::OnvifDiscover(QObject *parent) : QObject(parent)