File indexing completed on 2024-04-28 12:45:24

0001 /*
0002     Private classes for the SMB client
0003 
0004     SPDX-FileCopyrightText: 2018-2023 Alexander Reinholdt <alexander.reinholdt@kdemail.net>
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 // application specific includes
0009 #include "smb4kclient_p.h"
0010 #include "smb4kcustomsettings.h"
0011 #include "smb4kcustomsettingsmanager.h"
0012 #include "smb4knotification.h"
0013 #include "smb4ksettings.h"
0014 #include "smb4kwalletmanager.h"
0015 
0016 // System includes
0017 #include <errno.h>
0018 #include <sys/stat.h>
0019 
0020 // Qt includes
0021 #include <QAbstractSocket>
0022 #include <QDebug>
0023 #include <QDir>
0024 #include <QHostInfo>
0025 #include <QNetworkInterface>
0026 #include <QPrinter>
0027 #include <QTemporaryDir>
0028 #include <QTextDocument>
0029 #include <QUuid>
0030 
0031 // KDE includes
0032 #include <KFileItem>
0033 #include <KLocalizedString>
0034 
0035 #ifdef USE_WS_DISCOVERY
0036 #include <KDSoapClient/KDQName>
0037 #include <KDSoapClient/KDSoapClientInterface>
0038 #include <KDSoapClient/KDSoapMessage>
0039 #include <KDSoapClient/KDSoapMessageAddressingProperties>
0040 #include <WSDiscoveryTargetService>
0041 #endif
0042 
0043 #define SMBC_DEBUG 0
0044 
0045 using namespace Smb4KGlobal;
0046 
0047 //
0048 // Client base job
0049 //
0050 
0051 Smb4KClientBaseJob::Smb4KClientBaseJob(QObject *parent)
0052     : KJob(parent)
0053     , m_process(Smb4KGlobal::NoProcess)
0054 {
0055     pProcess = &m_process;
0056     pNetworkItem = &m_networkItem;
0057     pWorkgroups = &m_workgroups;
0058     pHosts = &m_hosts;
0059     pShares = &m_shares;
0060     pFiles = &m_files;
0061 }
0062 
0063 Smb4KClientBaseJob::~Smb4KClientBaseJob()
0064 {
0065     while (!m_workgroups.isEmpty()) {
0066         m_workgroups.takeFirst().clear();
0067     }
0068 
0069     while (!m_hosts.isEmpty()) {
0070         m_hosts.takeFirst().clear();
0071     }
0072 
0073     while (!m_shares.isEmpty()) {
0074         m_shares.takeFirst().clear();
0075     }
0076 
0077     while (!m_files.isEmpty()) {
0078         m_files.takeFirst().clear();
0079     }
0080 }
0081 
0082 void Smb4KClientBaseJob::setProcess(Smb4KGlobal::Process process)
0083 {
0084     m_process = process;
0085 }
0086 
0087 Smb4KGlobal::Process Smb4KClientBaseJob::process() const
0088 {
0089     return m_process;
0090 }
0091 
0092 void Smb4KClientBaseJob::setNetworkItem(NetworkItemPtr networkItem)
0093 {
0094     m_networkItem = networkItem;
0095 }
0096 
0097 NetworkItemPtr Smb4KClientBaseJob::networkItem() const
0098 {
0099     return m_networkItem;
0100 }
0101 
0102 QList<WorkgroupPtr> Smb4KClientBaseJob::workgroups()
0103 {
0104     return m_workgroups;
0105 }
0106 
0107 QList<HostPtr> Smb4KClientBaseJob::hosts()
0108 {
0109     return m_hosts;
0110 }
0111 
0112 QList<SharePtr> Smb4KClientBaseJob::shares()
0113 {
0114     return m_shares;
0115 }
0116 
0117 QList<FilePtr> Smb4KClientBaseJob::files()
0118 {
0119     return m_files;
0120 }
0121 
0122 QHostAddress Smb4KClientBaseJob::lookupIpAddress(const QString &name)
0123 {
0124     //
0125     // The IP address object
0126     //
0127     QHostAddress ipAddress;
0128 
0129     //
0130     // Get the IP address
0131     //
0132     // If the IP address is not to be determined for the local machine, we can use QHostInfo to
0133     // determine it. Otherwise we need to use QNetworkInterface for it.
0134     //
0135     if (name.toUpper() == QHostInfo::localHostName().toUpper() || name.toUpper() == machineNetbiosName().toUpper()) {
0136         // FIXME: Do we need to honor 'interfaces' here?
0137         QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
0138 
0139         // Get the IP address for the host. For the time being, prefer the
0140         // IPv4 address over the IPv6 address.
0141         for (const QHostAddress &addr : qAsConst(addresses)) {
0142             // We only use global addresses.
0143             if (addr.isGlobal()) {
0144                 if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
0145                     ipAddress = addr;
0146                     break;
0147                 } else if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
0148                     // FIXME: Use the right address here.
0149                     ipAddress = addr;
0150                 }
0151             }
0152         }
0153     } else {
0154         // Get the IP address
0155         QHostInfo hostInfo = QHostInfo::fromName(name);
0156 
0157         if (hostInfo.error() == QHostInfo::NoError) {
0158             QList<QHostAddress> addresses = hostInfo.addresses();
0159 
0160             // Get the IP address for the host. For the time being, prefer the
0161             // IPv4 address over the IPv6 address.
0162             for (const QHostAddress &addr : qAsConst(addresses)) {
0163                 // We only use global addresses.
0164                 if (addr.isGlobal()) {
0165                     if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
0166                         ipAddress = addr;
0167                         break;
0168                     } else if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
0169                         // FIXME: Use the right address here.
0170                         ipAddress = addr;
0171                     }
0172                 }
0173             }
0174         }
0175     }
0176 
0177     return ipAddress;
0178 }
0179 
0180 //
0181 // Authentication function for libsmbclient
0182 //
0183 static void get_auth_data_with_context_fn(SMBCCTX *context,
0184                                           const char *server,
0185                                           const char *share,
0186                                           char *workgroup,
0187                                           int maxLenWorkgroup,
0188                                           char *username,
0189                                           int maxLenUsername,
0190                                           char *password,
0191                                           int maxLenPassword)
0192 {
0193     if (context != nullptr) {
0194         Smb4KClientJob *job = static_cast<Smb4KClientJob *>(smbc_getOptionUserData(context));
0195 
0196         if (job) {
0197             job->get_auth_data_fn(server, share, workgroup, maxLenWorkgroup, username, maxLenUsername, password, maxLenPassword);
0198         }
0199     }
0200 }
0201 
0202 //
0203 // Client job
0204 //
0205 Smb4KClientJob::Smb4KClientJob(QObject *parent)
0206     : Smb4KClientBaseJob(parent)
0207 {
0208 }
0209 
0210 Smb4KClientJob::~Smb4KClientJob()
0211 {
0212 }
0213 
0214 void Smb4KClientJob::start()
0215 {
0216     QTimer::singleShot(50, this, SLOT(slotStartJob()));
0217     connect(this, &KJob::finished, this, &Smb4KClientJob::slotFinishJob);
0218 }
0219 
0220 void Smb4KClientJob::setPrintFileItem(const KFileItem &item)
0221 {
0222     m_fileItem = item;
0223 }
0224 
0225 KFileItem Smb4KClientJob::printFileItem() const
0226 {
0227     return m_fileItem;
0228 }
0229 
0230 void Smb4KClientJob::setPrintCopies(int copies)
0231 {
0232     m_copies = copies;
0233 }
0234 
0235 int Smb4KClientJob::printCopies() const
0236 {
0237     return m_copies;
0238 }
0239 
0240 void Smb4KClientJob::get_auth_data_fn(const char *server,
0241                                       const char * /*share*/,
0242                                       char *workgroup,
0243                                       int /*maxLenWorkgroup*/,
0244                                       char *username,
0245                                       int maxLenUsername,
0246                                       char *password,
0247                                       int maxLenPassword)
0248 {
0249     //
0250     // Authentication
0251     //
0252     switch ((*pNetworkItem)->type()) {
0253     case Network: {
0254         //
0255         // No authentication needed
0256         //
0257         break;
0258     }
0259     case Workgroup: {
0260         //
0261         // Only request authentication data, if the master browsers require
0262         // authentication data.
0263         //
0264         if (Smb4KSettings::masterBrowsersRequireAuth()) {
0265             if (QString::fromUtf8(server, -1).toUpper() != QString::fromUtf8(workgroup, -1).toUpper()) {
0266                 //
0267                 // This is the master browser. Create a host object for it.
0268                 //
0269                 HostPtr masterBrowser = HostPtr(new Smb4KHost());
0270                 masterBrowser->setWorkgroupName(QString::fromUtf8(workgroup, -1));
0271                 masterBrowser->setHostName(QString::fromUtf8(server, -1));
0272 
0273                 //
0274                 // Get the authentication data
0275                 //
0276                 Smb4KWalletManager::self()->readLoginCredentials(masterBrowser);
0277 
0278                 //
0279                 // Copy the authentication data
0280                 //
0281                 if (masterBrowser->hasUserInfo()) {
0282                     qstrncpy(username, masterBrowser->userName().toUtf8().data(), maxLenUsername);
0283                     qstrncpy(password, masterBrowser->password().toUtf8().data(), maxLenPassword);
0284                 }
0285             }
0286         }
0287 
0288         break;
0289     }
0290     case Host: {
0291         //
0292         // The host object
0293         //
0294         HostPtr host = (*pNetworkItem).staticCast<Smb4KHost>();
0295 
0296         //
0297         // Get the authentication data
0298         //
0299         Smb4KWalletManager::self()->readLoginCredentials(host);
0300 
0301         //
0302         // Copy the authentication data
0303         //
0304         if (host->hasUserInfo()) {
0305             qstrncpy(username, host->userName().toUtf8().data(), maxLenUsername);
0306             qstrncpy(password, host->password().toUtf8().data(), maxLenPassword);
0307         }
0308 
0309         break;
0310     }
0311     case Share: {
0312         //
0313         // The share object
0314         //
0315         SharePtr share = (*pNetworkItem).staticCast<Smb4KShare>();
0316 
0317         //
0318         // Get the authentication data
0319         //
0320         Smb4KWalletManager::self()->readLoginCredentials(share);
0321 
0322         //
0323         // Copy the authentication data
0324         //
0325         if (share->hasUserInfo()) {
0326             qstrncpy(username, share->userName().toUtf8().data(), maxLenUsername);
0327             qstrncpy(password, share->password().toUtf8().data(), maxLenPassword);
0328         }
0329 
0330         break;
0331     }
0332     case FileOrDirectory: {
0333         //
0334         // The file object
0335         //
0336         FilePtr file = (*pNetworkItem).staticCast<Smb4KFile>();
0337 
0338         if (file->isDirectory()) {
0339             SharePtr share = SharePtr(new Smb4KShare());
0340             share->setWorkgroupName(file->workgroupName());
0341             share->setHostName(file->hostName());
0342             share->setShareName(file->shareName());
0343             share->setUserName(file->userName());
0344             share->setPassword(file->password());
0345 
0346             //
0347             // Get the authentication data
0348             //
0349             Smb4KWalletManager::self()->readLoginCredentials(share);
0350 
0351             //
0352             // Copy the authentication data
0353             //
0354             if (share->hasUserInfo()) {
0355                 qstrncpy(username, share->userName().toUtf8().data(), maxLenUsername);
0356                 qstrncpy(password, share->password().toUtf8().data(), maxLenPassword);
0357             }
0358         }
0359 
0360         break;
0361     }
0362     default: {
0363         break;
0364     }
0365     }
0366 }
0367 
0368 void Smb4KClientJob::initClientLibrary()
0369 {
0370     //
0371     // Get new context
0372     //
0373     m_context = smbc_new_context();
0374 
0375     if (!m_context) {
0376         int errorCode = errno;
0377 
0378         setError(ClientError);
0379         setErrorText(QString::fromUtf8(strerror(errorCode), -1));
0380 
0381         emitResult();
0382         return;
0383     }
0384 
0385     //
0386     // Init the context
0387     //
0388     m_context = smbc_init_context(m_context);
0389 
0390     if (!m_context) {
0391         int errorCode = errno;
0392 
0393         setError(ClientError);
0394         setErrorText(QString::fromUtf8(strerror(errorCode), -1));
0395         emitResult();
0396         return;
0397     }
0398 
0399     //
0400     // Get the custom options
0401     //
0402     CustomSettingsPtr options = Smb4KCustomSettingsManager::self()->findCustomSettings(*pNetworkItem);
0403 
0404     //
0405     // Set debug level
0406     //
0407     smbc_setDebug(m_context, SMBC_DEBUG);
0408 
0409     //
0410     // Set the NetBIOS name and the workgroup to make connections
0411     //
0412     switch ((*pNetworkItem)->type()) {
0413     case Network: {
0414         //
0415         // We do not know about the servers and the domains/workgroups
0416         // present. So, do not set anything and use the default.
0417         //
0418         break;
0419     }
0420     case Workgroup: {
0421         WorkgroupPtr workgroup = (*pNetworkItem).staticCast<Smb4KWorkgroup>();
0422 
0423         //
0424         // Only set the NetBIOS name, if the workgroup entry has a master browser
0425         //
0426         if (workgroup->hasMasterBrowser()) {
0427             smbc_setNetbiosName(m_context, workgroup->masterBrowserName().toUtf8().data());
0428         }
0429 
0430         //
0431         // In case this domain/workgroup was discovered by the DNS-SD service, the workgroup/domain
0432         // might not be identical with the one defined in the network neighborhood. Thus, only set
0433         // the workgroup if no DNS-SD discovery was used.
0434         //
0435         if (!workgroup->dnsDiscovered()) {
0436             smbc_setWorkgroup(m_context, workgroup->workgroupName().toUtf8().data());
0437         }
0438 
0439         break;
0440     }
0441     case Host: {
0442         //
0443         // In case the domain/workgroup was discovered by the DNS-SD service, the
0444         // workgroup/domain might not be identical with the one defined in the network
0445         // neighborhood. Thus, only set the workgroup if no DNS-SD discovery was used.
0446         //
0447         HostPtr host = (*pNetworkItem).staticCast<Smb4KHost>();
0448         WorkgroupPtr workgroup = findWorkgroup(host->workgroupName());
0449 
0450         if (workgroup && !workgroup->dnsDiscovered()) {
0451             smbc_setWorkgroup(m_context, host->workgroupName().toUtf8().data());
0452         }
0453 
0454         //
0455         // Set the NetBIOS name
0456         //
0457         smbc_setNetbiosName(m_context, host->hostName().toUtf8().data());
0458 
0459         break;
0460     }
0461     case Share: {
0462         //
0463         // In case the domain/workgroup was discovered by the DNS-SD service, the
0464         // workgroup/domain might not be identical with the one defined in the network
0465         // neighborhood. Thus, only set the workgroup if no DNS-SD discovery was used.
0466         //
0467         SharePtr share = (*pNetworkItem).staticCast<Smb4KShare>();
0468         WorkgroupPtr workgroup = findWorkgroup(share->workgroupName());
0469 
0470         if (workgroup && !workgroup->dnsDiscovered()) {
0471             smbc_setWorkgroup(m_context, share->workgroupName().toUtf8().data());
0472         }
0473 
0474         //
0475         // Set the NetBIOS name
0476         //
0477         smbc_setNetbiosName(m_context, share->hostName().toUtf8().data());
0478 
0479         break;
0480     }
0481     case FileOrDirectory: {
0482         //
0483         // In case the domain/workgroup was discovered by the DNS-SD service, the
0484         // workgroup/domain might not be identical with the one defined in the network
0485         // neighborhood. Thus, only set the workgroup if no DNS-SD discovery was used.
0486         //
0487         FilePtr file = (*pNetworkItem).staticCast<Smb4KFile>();
0488 
0489         if (file->isDirectory()) {
0490             WorkgroupPtr workgroup = findWorkgroup(file->workgroupName());
0491 
0492             if (workgroup && !workgroup->dnsDiscovered()) {
0493                 smbc_setWorkgroup(m_context, file->workgroupName().toUtf8().data());
0494             }
0495 
0496             //
0497             // Set the NetBIOS name
0498             //
0499             smbc_setNetbiosName(m_context, file->hostName().toUtf8().data());
0500         }
0501 
0502         break;
0503     }
0504     default: {
0505         break;
0506     }
0507     }
0508 
0509     //
0510     // Set the user for making the connection
0511     //
0512     // To be able to connect to a Windows 10 server and get the list
0513     // of shared resources, use the 'guest' user here, if the URL
0514     // does not provide a user name.
0515     //
0516     if (!(*pNetworkItem)->url().userName().isEmpty()) {
0517         smbc_setUser(m_context, (*pNetworkItem)->url().userName().toUtf8().data());
0518     } else {
0519         smbc_setUser(m_context, "guest");
0520     }
0521 
0522     //
0523     // Set the port
0524     //
0525     if (options) {
0526         if (options->useSmbPort()) {
0527             smbc_setPort(m_context, options->smbPort());
0528         } else {
0529             smbc_setPort(m_context, 0 /* use the default */);
0530         }
0531     } else {
0532         if (Smb4KSettings::useRemoteSmbPort()) {
0533             smbc_setPort(m_context, Smb4KSettings::remoteSmbPort());
0534         } else {
0535             smbc_setPort(m_context, 0 /* use the default */);
0536         }
0537     }
0538 
0539     //
0540     // Set the user data (this class)
0541     //
0542     smbc_setOptionUserData(m_context, this);
0543 
0544     //
0545     // Set number of master browsers to be used
0546     //
0547     if (Smb4KSettings::largeNetworkNeighborhood()) {
0548         smbc_setOptionBrowseMaxLmbCount(m_context, 3);
0549     } else {
0550         smbc_setOptionBrowseMaxLmbCount(m_context, 0 /* all master browsers */);
0551     }
0552 
0553     //
0554     // Set the protocol version if desired
0555     //
0556     int minimal = -1;
0557     int maximal = -1;
0558     QString minimalClientProtocolVersionString, maximalClientProtocolVersionString;
0559 
0560     if (options) {
0561         if (options->useClientProtocolVersions()) {
0562             minimal = options->minimalClientProtocolVersion();
0563             maximal = options->maximalClientProtocolVersion();
0564         }
0565     } else {
0566         if (Smb4KSettings::useClientProtocolVersions()) {
0567             minimal = Smb4KSettings::minimalClientProtocolVersion();
0568             maximal = Smb4KSettings::maximalClientProtocolVersion();
0569         }
0570     }
0571 
0572     if (minimal != -1 && maximal != -1) {
0573         switch (minimal) {
0574         case Smb4KSettings::EnumMinimalClientProtocolVersion::NT1: {
0575             minimalClientProtocolVersionString = QStringLiteral("NT1");
0576             break;
0577         }
0578         case Smb4KSettings::EnumMinimalClientProtocolVersion::SMB2: {
0579             minimalClientProtocolVersionString = QStringLiteral("SMB2");
0580             break;
0581         }
0582         case Smb4KSettings::EnumMinimalClientProtocolVersion::SMB3: {
0583             minimalClientProtocolVersionString = QStringLiteral("SMB3");
0584             break;
0585         }
0586         default: {
0587             break;
0588         }
0589         }
0590 
0591         switch (Smb4KSettings::maximalClientProtocolVersion()) {
0592         case Smb4KSettings::EnumMaximalClientProtocolVersion::NT1: {
0593             maximalClientProtocolVersionString = QStringLiteral("NT1");
0594             break;
0595         }
0596         case Smb4KSettings::EnumMaximalClientProtocolVersion::SMB2: {
0597             maximalClientProtocolVersionString = QStringLiteral("SMB2");
0598             break;
0599         }
0600         case Smb4KSettings::EnumMaximalClientProtocolVersion::SMB3: {
0601             maximalClientProtocolVersionString = QStringLiteral("SMB3");
0602             break;
0603         }
0604         default: {
0605             break;
0606         }
0607         }
0608     }
0609 
0610     if (!minimalClientProtocolVersionString.isEmpty() && !maximalClientProtocolVersionString.isEmpty()) {
0611         smbc_setOptionProtocols(m_context, minimalClientProtocolVersionString.toLatin1().data(), maximalClientProtocolVersionString.toLatin1().data());
0612     } else {
0613         smbc_setOptionProtocols(m_context, nullptr, nullptr);
0614     }
0615 
0616     //
0617     // Set the encryption level
0618     //
0619     if (Smb4KSettings::useEncryptionLevel()) {
0620         switch (Smb4KSettings::encryptionLevel()) {
0621         case Smb4KSettings::EnumEncryptionLevel::None: {
0622             smbc_setOptionSmbEncryptionLevel(m_context, SMBC_ENCRYPTLEVEL_NONE);
0623             break;
0624         }
0625         case Smb4KSettings::EnumEncryptionLevel::Request: {
0626             smbc_setOptionSmbEncryptionLevel(m_context, SMBC_ENCRYPTLEVEL_REQUEST);
0627             break;
0628         }
0629         case Smb4KSettings::EnumEncryptionLevel::Require: {
0630             smbc_setOptionSmbEncryptionLevel(m_context, SMBC_ENCRYPTLEVEL_REQUIRE);
0631             break;
0632         }
0633         default: {
0634             break;
0635         }
0636         }
0637     }
0638 
0639     //
0640     // Set the usage of anonymous login
0641     //
0642     smbc_setOptionNoAutoAnonymousLogin(m_context, false);
0643 
0644     //
0645     // Set the usage of the winbind ccache
0646     //
0647     smbc_setOptionUseCCache(m_context, Smb4KSettings::useWinbindCCache());
0648 
0649     //
0650     // Set usage of Kerberos
0651     //
0652     if (options) {
0653         smbc_setOptionUseKerberos(m_context, options->useKerberos());
0654     } else {
0655         smbc_setOptionUseKerberos(m_context, Smb4KSettings::useKerberos());
0656     }
0657 
0658     smbc_setOptionFallbackAfterKerberos(m_context, 1);
0659 
0660     //
0661     // Set the channel for debug output
0662     //
0663     smbc_setOptionDebugToStderr(m_context, 1);
0664 
0665     //
0666     // Set auth callback function
0667     //
0668     smbc_setFunctionAuthDataWithContext(m_context, get_auth_data_with_context_fn);
0669 }
0670 
0671 void Smb4KClientJob::doLookups()
0672 {
0673     //
0674     // Set the new context
0675     //
0676     (void)smbc_set_context(m_context);
0677 
0678     //
0679     // Get the function to open the directory.
0680     //
0681     smbc_opendir_fn openDirectory = smbc_getFunctionOpendir(m_context);
0682 
0683     if (!openDirectory) {
0684         int errorCode = errno;
0685         setError(ClientError);
0686         setErrorText(QString::fromUtf8(strerror(errorCode), -1));
0687         return;
0688     }
0689 
0690     //
0691     // Open the directory
0692     //
0693     // If we use DNS-SD, the workgroup/domain name will most likely be unknown
0694     // for Samba and the following function fails. Since we do not want the lookup
0695     // to stop here in that case, do not throw an error when using DNS-SD and
0696     // Network and Workgroup (parent) items.
0697     //
0698     SMBCFILE *directory = openDirectory(m_context, (*pNetworkItem)->url().toString().toUtf8().data());
0699 
0700     if (!directory) {
0701         if (!(*pNetworkItem)->dnsDiscovered() && !((*pNetworkItem)->type() == Network || (*pNetworkItem)->type() == Workgroup)) {
0702             int errorCode = errno;
0703 
0704             switch (errorCode) {
0705             case EACCES:
0706             case EPERM: {
0707                 setError(AccessDeniedError);
0708                 setErrorText(QString::fromUtf8(strerror(errorCode), -1));
0709                 break;
0710             }
0711             case ENOENT: {
0712                 if ((*pNetworkItem)->type() != Network) {
0713                     setError(ClientError);
0714                     setErrorText(QString::fromUtf8(strerror(errorCode), -1));
0715                 }
0716                 break;
0717             }
0718             default: {
0719                 setError(ClientError);
0720                 setErrorText(QString::fromUtf8(strerror(errorCode), -1));
0721                 break;
0722             }
0723             }
0724         }
0725 
0726         return;
0727     }
0728 
0729     //
0730     // Read the directory
0731     //
0732     struct smbc_dirent *directoryEntry = nullptr;
0733     smbc_readdir_fn readDirectory = smbc_getFunctionReaddir(m_context);
0734 
0735     if (!readDirectory) {
0736         int errorCode = errno;
0737         setError(ClientError);
0738         setErrorText(QString::fromUtf8(strerror(errorCode), -1));
0739         return;
0740     }
0741 
0742     while ((directoryEntry = readDirectory(m_context, directory)) != nullptr) {
0743         switch (directoryEntry->smbc_type) {
0744         case SMBC_WORKGROUP: {
0745             //
0746             // Create a workgroup pointer
0747             //
0748             WorkgroupPtr workgroup = WorkgroupPtr(new Smb4KWorkgroup());
0749 
0750             //
0751             // Set the workgroup name
0752             //
0753             QString workgroupName = QString::fromUtf8(directoryEntry->name, -1);
0754             workgroup->setWorkgroupName(workgroupName);
0755 
0756             //
0757             // Set the master browser
0758             //
0759             QString masterBrowserName = QString::fromUtf8(directoryEntry->comment, -1);
0760             workgroup->setMasterBrowserName(masterBrowserName);
0761 
0762             //
0763             // Lookup IP address
0764             //
0765             QHostAddress address = lookupIpAddress(masterBrowserName);
0766 
0767             //
0768             // Process the IP address.
0769             // If the address is null, the server most likely went offline. So, skip the
0770             // workgroup and delete the pointer.
0771             //
0772             if (!address.isNull()) {
0773                 workgroup->setMasterBrowserIpAddress(address);
0774                 *pWorkgroups << workgroup;
0775             } else {
0776                 workgroup.clear();
0777             }
0778 
0779             break;
0780         }
0781         case SMBC_SERVER: {
0782             //
0783             // Create a host pointer
0784             //
0785             HostPtr host = HostPtr(new Smb4KHost());
0786 
0787             //
0788             // Set the workgroup name
0789             //
0790             host->setWorkgroupName((*pNetworkItem)->url().host());
0791 
0792             //
0793             // Set the host name
0794             //
0795             QString hostName = QString::fromUtf8(directoryEntry->name, -1);
0796             host->setHostName(hostName);
0797 
0798             //
0799             // Set the comment
0800             //
0801             QString comment = QString::fromUtf8(directoryEntry->comment, -1);
0802             host->setComment(comment);
0803 
0804             //
0805             // Lookup IP address
0806             //
0807             QHostAddress address = lookupIpAddress(hostName);
0808 
0809             //
0810             // Process the IP address.
0811             // If the address is null, the server most likely went offline. So, skip it
0812             // and delete the pointer.
0813             //
0814             if (!address.isNull()) {
0815                 host->setIpAddress(address);
0816                 *pHosts << host;
0817             } else {
0818                 host.clear();
0819             }
0820 
0821             break;
0822         }
0823         case SMBC_FILE_SHARE: {
0824             //
0825             // Create a share pointer
0826             //
0827             SharePtr share = SharePtr(new Smb4KShare());
0828 
0829             //
0830             // Set the workgroup name
0831             //
0832             share->setWorkgroupName((*pNetworkItem).staticCast<Smb4KHost>()->workgroupName());
0833 
0834             //
0835             // Set the host name
0836             //
0837             share->setHostName((*pNetworkItem)->url().host());
0838 
0839             //
0840             // Set the share name
0841             //
0842             share->setShareName(QString::fromUtf8(directoryEntry->name, -1));
0843 
0844             //
0845             // Set the comment
0846             //
0847             share->setComment(QString::fromUtf8(directoryEntry->comment, -1));
0848 
0849             //
0850             // Set share type
0851             //
0852             share->setShareType(FileShare);
0853 
0854             //
0855             // Set the authentication data
0856             //
0857             share->setUserName((*pNetworkItem)->url().userName());
0858             share->setPassword((*pNetworkItem)->url().password());
0859 
0860             //
0861             // Lookup IP address
0862             //
0863             QHostAddress address = lookupIpAddress((*pNetworkItem)->url().host());
0864 
0865             //
0866             // Process the IP address.
0867             // If the address is null, the server most likely went offline. So, skip it
0868             // and delete the pointer.
0869             //
0870             if (!address.isNull()) {
0871                 share->setHostIpAddress(address);
0872                 *pShares << share;
0873             } else {
0874                 share.clear();
0875             }
0876 
0877             break;
0878         }
0879         case SMBC_PRINTER_SHARE: {
0880             //
0881             // Create a share pointer
0882             //
0883             SharePtr share = SharePtr(new Smb4KShare());
0884 
0885             //
0886             // Set the workgroup name
0887             //
0888             share->setWorkgroupName((*pNetworkItem).staticCast<Smb4KHost>()->workgroupName());
0889 
0890             //
0891             // Set the host name
0892             //
0893             share->setHostName((*pNetworkItem)->url().host());
0894 
0895             //
0896             // Set the share name
0897             //
0898             share->setShareName(QString::fromUtf8(directoryEntry->name, -1));
0899 
0900             //
0901             // Set the comment
0902             //
0903             share->setComment(QString::fromUtf8(directoryEntry->comment, -1));
0904 
0905             //
0906             // Set share type
0907             //
0908             share->setShareType(PrinterShare);
0909 
0910             //
0911             // Set the authentication data
0912             //
0913             share->setUserName((*pNetworkItem)->url().userName());
0914             share->setPassword((*pNetworkItem)->url().password());
0915 
0916             //
0917             // Lookup IP address
0918             //
0919             QHostAddress address = lookupIpAddress((*pNetworkItem)->url().host());
0920 
0921             //
0922             // Process the IP address.
0923             // If the address is null, the server most likely went offline. So, skip it
0924             // and delete the pointer.
0925             //
0926             if (!address.isNull()) {
0927                 share->setHostIpAddress(address);
0928                 *pShares << share;
0929             } else {
0930                 share.clear();
0931             }
0932 
0933             break;
0934         }
0935         case SMBC_IPC_SHARE: {
0936             //
0937             // Create a share pointer
0938             //
0939             SharePtr share = SharePtr(new Smb4KShare());
0940 
0941             //
0942             // Set the workgroup name
0943             //
0944             share->setWorkgroupName((*pNetworkItem).staticCast<Smb4KHost>()->workgroupName());
0945 
0946             //
0947             // Set the host name
0948             //
0949             share->setHostName((*pNetworkItem)->url().host());
0950 
0951             //
0952             // Set the share name
0953             //
0954             share->setShareName(QString::fromUtf8(directoryEntry->name, -1));
0955 
0956             //
0957             // Set the comment
0958             //
0959             share->setComment(QString::fromUtf8(directoryEntry->comment, -1));
0960 
0961             //
0962             // Set share type
0963             //
0964             share->setShareType(IpcShare);
0965 
0966             //
0967             // Set the authentication data
0968             //
0969             share->setUserName((*pNetworkItem)->url().userName());
0970             share->setPassword((*pNetworkItem)->url().password());
0971 
0972             //
0973             // Lookup IP address
0974             //
0975             QHostAddress address = lookupIpAddress((*pNetworkItem)->url().host());
0976 
0977             //
0978             // Process the IP address.
0979             // If the address is null, the server most likely went offline. So, skip it
0980             // and delete the pointer.
0981             //
0982             if (!address.isNull()) {
0983                 share->setHostIpAddress(address);
0984                 *pShares << share;
0985             } else {
0986                 share.clear();
0987             }
0988 
0989             break;
0990         }
0991         case SMBC_DIR: {
0992             //
0993             // Do not process '.' and '..' directories
0994             //
0995             QString name = QString::fromUtf8(directoryEntry->name, -1);
0996 
0997             if (name != QStringLiteral(".") && name != QStringLiteral("..")) {
0998                 //
0999                 // Create the URL for the discovered item
1000                 //
1001                 QUrl u = (*pNetworkItem)->url();
1002                 u.setPath((*pNetworkItem)->url().path() + QDir::separator() + QString::fromUtf8(directoryEntry->name, -1));
1003 
1004                 //
1005                 // We do not stat directories. Directly create the directory object
1006                 //
1007                 FilePtr dir = FilePtr(new Smb4KFile(u));
1008                 dir->setDirectory(true);
1009 
1010                 //
1011                 // Set the workgroup name
1012                 //
1013                 dir->setWorkgroupName((*pNetworkItem).staticCast<Smb4KShare>()->workgroupName());
1014 
1015                 //
1016                 // Set the authentication data
1017                 //
1018                 dir->setUserName((*pNetworkItem)->url().userName());
1019                 dir->setPassword((*pNetworkItem)->url().password());
1020 
1021                 //
1022                 // Lookup IP address
1023                 //
1024                 QHostAddress address = lookupIpAddress((*pNetworkItem)->url().host());
1025 
1026                 //
1027                 // Process the IP address.
1028                 // If the address is null, the server most likely went offline. So, skip it
1029                 // and delete the pointer.
1030                 //
1031                 if (!address.isNull()) {
1032                     dir->setHostIpAddress(address);
1033                     *pFiles << dir;
1034                 } else {
1035                     dir.clear();
1036                 }
1037             }
1038 
1039             break;
1040         }
1041         case SMBC_FILE: {
1042             //
1043             // Create the URL for the discovered item
1044             //
1045             QUrl u = (*pNetworkItem)->url();
1046             u.setPath((*pNetworkItem)->url().path() + QDir::separator() + QString::fromUtf8(directoryEntry->name, -1));
1047 
1048             //
1049             // Create the file object
1050             //
1051             FilePtr file = FilePtr(new Smb4KFile(u));
1052 
1053             //
1054             // Set the workgroup name
1055             //
1056             file->setWorkgroupName((*pNetworkItem).staticCast<Smb4KShare>()->workgroupName());
1057 
1058             //
1059             // Stat the file
1060             //
1061             // FIXME
1062 
1063             //
1064             // Set the authentication data
1065             //
1066             file->setUserName((*pNetworkItem)->url().userName());
1067             file->setPassword((*pNetworkItem)->url().password());
1068 
1069             //
1070             // Lookup IP address
1071             //
1072             QHostAddress address = lookupIpAddress((*pNetworkItem)->url().host());
1073 
1074             //
1075             // Process the IP address.
1076             // If the address is null, the server most likely went offline. So, skip it
1077             // and delete the pointer.
1078             //
1079             if (!address.isNull()) {
1080                 file->setHostIpAddress(address);
1081                 *pFiles << file;
1082             } else {
1083                 file.clear();
1084             }
1085 
1086             break;
1087         }
1088         case SMBC_LINK: {
1089             qDebug() << "Processing links is not implemented.";
1090             qDebug() << directoryEntry->name;
1091             qDebug() << directoryEntry->comment;
1092             break;
1093         }
1094         default: {
1095             qDebug() << "Need to process network item " << directoryEntry->name;
1096             break;
1097         }
1098         }
1099     }
1100 
1101     //
1102     // Close the directory
1103     //
1104     smbc_closedir_fn closeDirectory = smbc_getFunctionClosedir(m_context);
1105 
1106     if (!closeDirectory) {
1107         int errorCode = errno;
1108 
1109         setError(ClientError);
1110         setErrorText(QString::fromUtf8(strerror(errorCode), -1));
1111         return;
1112     }
1113 
1114     (void)closeDirectory(m_context, directory);
1115 }
1116 
1117 void Smb4KClientJob::doPrinting()
1118 {
1119     //
1120     // Set the new context
1121     //
1122     (void)smbc_set_context(m_context);
1123 
1124     //
1125     // The URL that is to be printed
1126     //
1127     QUrl fileUrl;
1128 
1129     //
1130     // Set the temporary directory
1131     //
1132     QTemporaryDir tempDir;
1133 
1134     //
1135     // Check if we can directly print the file
1136     //
1137     if (m_fileItem.mimetype() == QStringLiteral("application/postscript") || m_fileItem.mimetype() == QStringLiteral("application/pdf")
1138         || m_fileItem.mimetype().startsWith(QStringLiteral("image"))) {
1139         //
1140         // Set the URL to the incoming file
1141         //
1142         fileUrl = m_fileItem.url();
1143     } else if (m_fileItem.mimetype() == QStringLiteral("application/x-shellscript") || m_fileItem.mimetype().startsWith(QStringLiteral("text"))
1144                || m_fileItem.mimetype().startsWith(QStringLiteral("message"))) {
1145         //
1146         // Set a printer object
1147         //
1148         QPrinter printer(QPrinter::HighResolution);
1149         printer.setCreator(QStringLiteral("Smb4K"));
1150         printer.setOutputFormat(QPrinter::PdfFormat);
1151         printer.setOutputFileName(tempDir.path() + QDir::separator() + QStringLiteral("smb4k_print.pdf"));
1152 
1153         //
1154         // Open the file that is to be printed and read it
1155         //
1156         QStringList contents;
1157 
1158         QFile file(m_fileItem.url().path());
1159 
1160         if (file.open(QFile::ReadOnly | QFile::Text)) {
1161             QTextStream ts(&file);
1162 
1163             while (!ts.atEnd()) {
1164                 contents << ts.readLine();
1165             }
1166         } else {
1167             return;
1168         }
1169 
1170         //
1171         // Convert the file to PDF
1172         //
1173         QTextDocument doc;
1174 
1175         if (m_fileItem.mimetype().endsWith(QStringLiteral("html"))) {
1176             doc.setHtml(contents.join(QStringLiteral(" ")));
1177         } else {
1178             doc.setPlainText(contents.join(QStringLiteral("\n")));
1179         }
1180 
1181         doc.print(&printer);
1182 
1183         //
1184         // Set the URL to the converted file
1185         //
1186         fileUrl.setUrl(printer.outputFileName());
1187         fileUrl.setScheme(QStringLiteral("file"));
1188     } else {
1189         Smb4KNotification::mimetypeNotSupported(m_fileItem.mimetype());
1190         return;
1191     }
1192 
1193     //
1194     // Get the open function for the printer
1195     //
1196     smbc_open_print_job_fn openPrinter = smbc_getFunctionOpenPrintJob(m_context);
1197 
1198     if (!openPrinter) {
1199         int errorCode = errno;
1200         setError(ClientError);
1201         setErrorText(QString::fromUtf8(strerror(errorCode), -1));
1202         return;
1203     }
1204 
1205     //
1206     // Open the printer for printing
1207     //
1208     SMBCFILE *printer = openPrinter(m_context, (*pNetworkItem)->url().toString().toUtf8().data());
1209 
1210     if (!printer) {
1211         int errorCode = errno;
1212 
1213         switch (errorCode) {
1214         case EACCES: {
1215             setError(AccessDeniedError);
1216             setErrorText(QString::fromUtf8(strerror(errorCode), -1));
1217             break;
1218         }
1219         default: {
1220             setError(ClientError);
1221             setErrorText(QString::fromUtf8(strerror(errorCode), -1));
1222             break;
1223         }
1224         }
1225 
1226         return;
1227     }
1228 
1229     //
1230     // Open the file
1231     //
1232     QFile file(fileUrl.path());
1233 
1234     if (!file.open(QFile::ReadOnly)) {
1235         setError(FileAccessError);
1236         setErrorText(i18n("The file %1 could not be read", fileUrl.path()));
1237         return;
1238     }
1239 
1240     //
1241     // Write X copies of the file to the printer
1242     //
1243     char buffer[4096];
1244     qint64 bytes = 0;
1245     int copy = 0;
1246 
1247     while (copy < m_copies) {
1248         while ((bytes = file.read(buffer, sizeof(buffer))) > 0) {
1249             smbc_write_fn writeFile = smbc_getFunctionWrite(m_context);
1250 
1251             if (writeFile(m_context, printer, buffer, bytes) < 0) {
1252                 setError(PrintFileError);
1253                 setErrorText(i18n("The file %1 could not be printed to %2", fileUrl.path(), (*pNetworkItem).staticCast<Smb4KShare>()->displayString()));
1254 
1255                 smbc_close_fn closePrinter = smbc_getFunctionClose(m_context);
1256                 closePrinter(m_context, printer);
1257             }
1258         }
1259 
1260         copy++;
1261     }
1262 
1263     //
1264     // Close the printer
1265     //
1266     smbc_close_fn closePrinter = smbc_getFunctionClose(m_context);
1267     closePrinter(m_context, printer);
1268 }
1269 
1270 void Smb4KClientJob::slotStartJob()
1271 {
1272     //
1273     // Initialize the client library
1274     //
1275     initClientLibrary();
1276 
1277     //
1278     // Process the given URL according to the passed process
1279     //
1280     switch (*pProcess) {
1281     case LookupDomains:
1282     case LookupDomainMembers:
1283     case LookupShares:
1284     case LookupFiles: {
1285         //
1286         // Do lookups using the client library
1287         //
1288         doLookups();
1289         break;
1290     }
1291     case PrintFile: {
1292         //
1293         // Print files using the client library
1294         //
1295         doPrinting();
1296         break;
1297     }
1298     default: {
1299         break;
1300     }
1301     }
1302 
1303     //
1304     // Emit the result
1305     //
1306     emitResult();
1307 }
1308 
1309 void Smb4KClientJob::slotFinishJob()
1310 {
1311     if (m_context != nullptr) {
1312         smbc_free_context(m_context, 1);
1313     }
1314 }
1315 
1316 Smb4KDnsDiscoveryJob::Smb4KDnsDiscoveryJob(QObject *parent)
1317     : Smb4KClientBaseJob(parent)
1318 {
1319     //
1320     // Set up the DNS-SD browser
1321     //
1322     m_serviceBrowser = new KDNSSD::ServiceBrowser(QStringLiteral("_smb._tcp"));
1323 
1324     //
1325     // Connections
1326     //
1327     connect(m_serviceBrowser, &KDNSSD::ServiceBrowser::serviceAdded, this, &Smb4KDnsDiscoveryJob::slotServiceAdded);
1328     connect(m_serviceBrowser, &KDNSSD::ServiceBrowser::finished, this, &Smb4KDnsDiscoveryJob::slotFinished);
1329 }
1330 
1331 Smb4KDnsDiscoveryJob::~Smb4KDnsDiscoveryJob()
1332 {
1333     delete m_serviceBrowser;
1334 }
1335 
1336 void Smb4KDnsDiscoveryJob::start()
1337 {
1338     switch (m_serviceBrowser->isAvailable()) {
1339     case KDNSSD::ServiceBrowser::Working: {
1340         break;
1341     }
1342     case KDNSSD::ServiceBrowser::Stopped: {
1343         Smb4KNotification::zeroconfError(i18n("The Zeroconf daemon is not running. No servers are discovered using DNS-SD."));
1344         break;
1345     }
1346     case KDNSSD::ServiceBrowser::Unsupported: {
1347         Smb4KNotification::zeroconfError(i18n("Zeroconf support is not available in this version of KDE."));
1348         break;
1349     }
1350     default: {
1351         Smb4KNotification::zeroconfError(i18n("An unknown error with Zeroconf occurred."));
1352         break;
1353     }
1354     }
1355 
1356     QTimer::singleShot(50, this, SLOT(slotStartJob()));
1357 }
1358 
1359 void Smb4KDnsDiscoveryJob::slotStartJob()
1360 {
1361     m_serviceBrowser->startBrowse();
1362 }
1363 
1364 void Smb4KDnsDiscoveryJob::slotServiceAdded(KDNSSD::RemoteService::Ptr service)
1365 {
1366     switch (*pProcess) {
1367     case LookupDomains: {
1368         //
1369         // Check if the workgroup/domain is already known
1370         //
1371         bool foundWorkgroup = false;
1372 
1373         for (const WorkgroupPtr &w : qAsConst(*pWorkgroups)) {
1374             if (QString::compare(w->workgroupName(), service->domain(), Qt::CaseInsensitive) == 0) {
1375                 foundWorkgroup = true;
1376                 break;
1377             }
1378         }
1379 
1380         //
1381         // If the workgroup is not known yet, add it to the list
1382         //
1383         if (!foundWorkgroup) {
1384             //
1385             // Create the workgroup item
1386             //
1387             WorkgroupPtr workgroup = WorkgroupPtr(new Smb4KWorkgroup());
1388 
1389             //
1390             // Set the _DNS-SD_ domain name
1391             //
1392             workgroup->setWorkgroupName(service->domain());
1393 
1394             //
1395             // Tell the program that the workgroup was discovered by DNS-SD
1396             //
1397             workgroup->setDnsDiscovered(true);
1398 
1399             //
1400             // Add the workgroup
1401             //
1402             *pWorkgroups << workgroup;
1403         }
1404         break;
1405     }
1406     case LookupDomainMembers: {
1407         //
1408         // Check if the server is already known
1409         //
1410         bool foundServer = false;
1411 
1412         for (const HostPtr &h : qAsConst(*pHosts)) {
1413             //
1414             // On a local network there will most likely be no two servers with
1415             // identical name, thus, to avoid duplicates, only test the hostname
1416             // here.
1417             //
1418             if (QString::compare(h->hostName(), service->serviceName(), Qt::CaseInsensitive) == 0) {
1419                 foundServer = true;
1420                 break;
1421             }
1422         }
1423 
1424         //
1425         // If the server is not known yet, add it to the list
1426         //
1427         if (!foundServer) {
1428             //
1429             // Create the host item
1430             //
1431             HostPtr host = HostPtr(new Smb4KHost());
1432 
1433             //
1434             // Set the _DNS-SD_ host name
1435             //
1436             host->setHostName(service->serviceName());
1437 
1438             //
1439             // Set the _DNS-SD_ domain name
1440             //
1441             host->setWorkgroupName(service->domain());
1442 
1443             //
1444             // Tell the program that the host was discovered by DNS-SD
1445             //
1446             host->setDnsDiscovered(true);
1447 
1448             //
1449             // Lookup IP address
1450             //
1451             QHostAddress address = lookupIpAddress(service->serviceName());
1452 
1453             //
1454             // Process the IP address.
1455             //
1456             if (!address.isNull()) {
1457                 host->setIpAddress(address);
1458             }
1459 
1460             //
1461             // Add the host
1462             //
1463             *pHosts << host;
1464         }
1465 
1466         break;
1467     }
1468     default: {
1469         break;
1470     }
1471     }
1472 }
1473 
1474 void Smb4KDnsDiscoveryJob::slotFinished()
1475 {
1476     emitResult();
1477 }
1478 
1479 #ifdef USE_WS_DISCOVERY
1480 Smb4KWsDiscoveryJob::Smb4KWsDiscoveryJob(QObject *parent)
1481     : Smb4KClientBaseJob(parent)
1482 {
1483     m_discoveryClient = new WSDiscoveryClient(this);
1484 
1485     m_timer = new QTimer(this);
1486     m_timer->setSingleShot(true);
1487     m_timer->setInterval(2000);
1488 
1489     connect(m_discoveryClient, &WSDiscoveryClient::probeMatchReceived, this, &Smb4KWsDiscoveryJob::slotProbeMatchReceived);
1490     connect(m_discoveryClient, &WSDiscoveryClient::resolveMatchReceived, this, &Smb4KWsDiscoveryJob::slotResolveMatchReceived);
1491     connect(m_timer, &QTimer::timeout, this, &Smb4KWsDiscoveryJob::slotDiscoveryFinished);
1492 }
1493 
1494 Smb4KWsDiscoveryJob::~Smb4KWsDiscoveryJob()
1495 {
1496 }
1497 
1498 void Smb4KWsDiscoveryJob::start()
1499 {
1500     QTimer::singleShot(50, this, SLOT(slotStartJob()));
1501 }
1502 
1503 void Smb4KWsDiscoveryJob::slotStartJob()
1504 {
1505     //
1506     // Start the client
1507     //
1508     m_discoveryClient->start();
1509 
1510     //
1511     // Define the type
1512     //
1513     KDQName type(QStringLiteral("wsdp:Device"));
1514     type.setNameSpace(QStringLiteral("http://schemas.xmlsoap.org/ws/2006/02/devprof"));
1515 
1516     //
1517     // Send the probe
1518     //
1519     m_discoveryClient->sendProbe({type}, {});
1520 
1521     //
1522     // Start the timer
1523     //
1524     m_timer->start();
1525 }
1526 
1527 void Smb4KWsDiscoveryJob::slotProbeMatchReceived(const WSDiscoveryTargetService &service)
1528 {
1529     //
1530     // Stop the timer
1531     //
1532     m_timer->stop();
1533 
1534     //
1535     // If there is no address, we need to resolve it. Otherwise,
1536     // resolve the available addresses and add the discovered network
1537     // items to the respective lists.
1538     //
1539     if (service.xAddrList().isEmpty()) {
1540         m_discoveryClient->sendResolve(service.endpointReference());
1541     } else {
1542         if (!service.xAddrList().isEmpty()) {
1543             for (const QUrl &address : service.xAddrList()) {
1544                 KDSoapClientInterface clientInterface(address.toString(), QStringLiteral("http://schemas.xmlsoap.org/ws/2004/09/transfer"));
1545                 clientInterface.setSoapVersion(KDSoapClientInterface::SoapVersion::SOAP1_2);
1546                 clientInterface.setTimeout(5000);
1547 
1548                 KDSoapMessage soapMessage;
1549                 KDSoapMessageAddressingProperties soapMessageProperties;
1550                 soapMessageProperties.setAddressingNamespace(KDSoapMessageAddressingProperties::Addressing200408);
1551                 soapMessageProperties.setAction(QStringLiteral("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get"));
1552                 soapMessageProperties.setMessageID(QStringLiteral("urn:uuid:") + QUuid::createUuid().toString(QUuid::WithoutBraces));
1553                 soapMessageProperties.setDestination(service.endpointReference());
1554                 soapMessageProperties.setReplyEndpointAddress(
1555                     KDSoapMessageAddressingProperties::predefinedAddressToString(KDSoapMessageAddressingProperties::Anonymous,
1556                                                                                  KDSoapMessageAddressingProperties::Addressing200408));
1557                 soapMessageProperties.setSourceEndpointAddress(QStringLiteral("urn:uuid:") + QUuid::createUuid().toString(QUuid::WithoutBraces));
1558                 soapMessage.setMessageAddressingProperties(soapMessageProperties);
1559 
1560                 KDSoapMessage response = clientInterface.call(QString(), soapMessage);
1561 
1562                 if (!response.isFault()) {
1563                     KDSoapValueList childValues = response.childValues();
1564 
1565                     for (const KDSoapValue &value : qAsConst(childValues)) {
1566                         QString entry = value.childValues()
1567                                             .child(QStringLiteral("Relationship"))
1568                                             .childValues()
1569                                             .child(QStringLiteral("Host"))
1570                                             .childValues()
1571                                             .child(QStringLiteral("Computer"))
1572                                             .value()
1573                                             .toString();
1574 
1575                         switch (*pProcess) {
1576                         case LookupDomains: {
1577                             //
1578                             // Get the name of the workgroup or domain
1579                             //
1580                             QString workgroupName = entry.section(QStringLiteral(":"), 1, -1);
1581 
1582                             //
1583                             // Work around an empty workgroup/domain name. Use the "LOCAL" domain from
1584                             // DNS-SD for that.
1585                             //
1586                             if (workgroupName.isEmpty()) {
1587                                 workgroupName = QStringLiteral("LOCAL");
1588                             }
1589 
1590                             //
1591                             // Process the workgroup name. Only add a new workgroup, if it
1592                             // is not present already.
1593                             //
1594                             bool foundWorkgroup = false;
1595 
1596                             for (const WorkgroupPtr &w : qAsConst(*pWorkgroups)) {
1597                                 if (QString::compare(w->workgroupName(), workgroupName, Qt::CaseInsensitive) == 0) {
1598                                     foundWorkgroup = true;
1599                                     break;
1600                                 }
1601                             }
1602 
1603                             //
1604                             // If the workgroup is unknown, add it to the list
1605                             //
1606                             if (!foundWorkgroup) {
1607                                 //
1608                                 // Create the workgroup object
1609                                 //
1610                                 WorkgroupPtr workgroup = WorkgroupPtr(new Smb4KWorkgroup());
1611 
1612                                 //
1613                                 // Set the workgroup/domain name
1614                                 //
1615                                 workgroup->setWorkgroupName(workgroupName);
1616 
1617                                 //
1618                                 // Add the workgroup
1619                                 //
1620                                 *pWorkgroups << workgroup;
1621                             }
1622 
1623                             break;
1624                         }
1625                         case LookupDomainMembers: {
1626                             //
1627                             // Get the workgroup name
1628                             //
1629                             QString workgroupName = entry.section(QStringLiteral(":"), 1, -1);
1630 
1631                             //
1632                             // Work around an empty workgroup/domain name. Use the "LOCAL" domain from
1633                             // DNS-SD for that.
1634                             //
1635                             if (workgroupName.isEmpty()) {
1636                                 workgroupName = QStringLiteral("LOCAL");
1637                             }
1638 
1639                             //
1640                             // Get the host name. Unfortunately, the delimiter depends on
1641                             // whether the host is member of a workgroup (/) or domain (\).
1642                             //
1643                             QString hostName;
1644 
1645                             if (entry.contains(QStringLiteral("/"))) {
1646                                 hostName = entry.section(QStringLiteral("/"), 0, 0);
1647                             } else if (entry.contains(QStringLiteral("\\"))) {
1648                                 hostName = entry.section(QStringLiteral("\\"), 0, 0);
1649                             }
1650 
1651                             //
1652                             // Process the host name. Only add a new host, if it
1653                             // is not present already.
1654                             //
1655                             if (!hostName.isEmpty()) {
1656                                 //
1657                                 // Check if the server is already known
1658                                 //
1659                                 bool foundServer = false;
1660 
1661                                 for (const HostPtr &h : qAsConst(*pHosts)) {
1662                                     if (QString::compare(h->hostName(), hostName, Qt::CaseInsensitive) == 0
1663                                         && QString::compare(h->workgroupName(), workgroupName, Qt::CaseInsensitive) == 0) {
1664                                         foundServer = true;
1665                                         break;
1666                                     }
1667                                 }
1668 
1669                                 //
1670                                 // If the server is unknown, add it to the list
1671                                 //
1672                                 if (!foundServer) {
1673                                     //
1674                                     // Create the host object
1675                                     //
1676                                     HostPtr host = HostPtr(new Smb4KHost());
1677 
1678                                     //
1679                                     // Set the workgroup/domain name
1680                                     //
1681                                     host->setWorkgroupName(workgroupName);
1682 
1683                                     //
1684                                     // Set the host name
1685                                     //
1686                                     host->setHostName(hostName);
1687 
1688                                     //
1689                                     // Lookup IP address
1690                                     //
1691                                     QHostAddress address = lookupIpAddress(hostName);
1692 
1693                                     //
1694                                     // Process the IP address.
1695                                     //
1696                                     if (!address.isNull()) {
1697                                         host->setIpAddress(address);
1698                                     }
1699 
1700                                     //
1701                                     // Add the host
1702                                     //
1703                                     *pHosts << host;
1704                                 }
1705                             }
1706 
1707                             break;
1708                         }
1709                         default: {
1710                             break;
1711                         }
1712                         }
1713                     }
1714                 }
1715             }
1716         }
1717     }
1718 
1719     //
1720     // Restart the timer
1721     //
1722     m_timer->start();
1723 }
1724 
1725 void Smb4KWsDiscoveryJob::slotResolveMatchReceived(const WSDiscoveryTargetService &service)
1726 {
1727     //
1728     // Stop the timer
1729     //
1730     m_timer->stop();
1731 
1732     //
1733     // If there are addresses available, resolve them and add the
1734     // discovered network items to the respective lists.
1735     //
1736     if (!service.xAddrList().isEmpty()) {
1737         for (const QUrl &address : service.xAddrList()) {
1738             KDSoapClientInterface clientInterface(address.toString(), QStringLiteral("http://schemas.xmlsoap.org/ws/2004/09/transfer"));
1739             clientInterface.setSoapVersion(KDSoapClientInterface::SoapVersion::SOAP1_2);
1740             clientInterface.setTimeout(5000);
1741 
1742             KDSoapMessage soapMessage;
1743             KDSoapMessageAddressingProperties soapMessageProperties;
1744             soapMessageProperties.setAddressingNamespace(KDSoapMessageAddressingProperties::Addressing200408);
1745             soapMessageProperties.setAction(QStringLiteral("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get"));
1746             soapMessageProperties.setMessageID(QStringLiteral("urn:uuid:") + QUuid::createUuid().toString(QUuid::WithoutBraces));
1747             soapMessageProperties.setDestination(service.endpointReference());
1748             soapMessageProperties.setReplyEndpointAddress(
1749                 KDSoapMessageAddressingProperties::predefinedAddressToString(KDSoapMessageAddressingProperties::Anonymous,
1750                                                                              KDSoapMessageAddressingProperties::Addressing200408));
1751             soapMessageProperties.setSourceEndpointAddress(QStringLiteral("urn:uuid:") + QUuid::createUuid().toString(QUuid::WithoutBraces));
1752             soapMessage.setMessageAddressingProperties(soapMessageProperties);
1753 
1754             KDSoapMessage response = clientInterface.call(QString(), soapMessage);
1755 
1756             if (!response.isFault()) {
1757                 KDSoapValueList childValues = response.childValues();
1758 
1759                 for (const KDSoapValue &value : qAsConst(childValues)) {
1760                     QString entry = value.childValues()
1761                                         .child(QStringLiteral("Relationship"))
1762                                         .childValues()
1763                                         .child(QStringLiteral("Host"))
1764                                         .childValues()
1765                                         .child(QStringLiteral("Computer"))
1766                                         .value()
1767                                         .toString();
1768 
1769                     switch (*pProcess) {
1770                     case LookupDomains: {
1771                         //
1772                         // Get the name of the workgroup or domain
1773                         //
1774                         QString workgroupName = entry.section(QStringLiteral(":"), 1, -1);
1775 
1776                         //
1777                         // Work around an empty workgroup/domain name. Use the "LOCAL" domain from
1778                         // DNS-SD for that.
1779                         //
1780                         if (workgroupName.isEmpty()) {
1781                             workgroupName = QStringLiteral("LOCAL");
1782                         }
1783 
1784                         //
1785                         // Process the workgroup name. Only add a new workgroup, if it
1786                         // is not present already.
1787                         //
1788                         bool foundWorkgroup = false;
1789 
1790                         for (const WorkgroupPtr &w : qAsConst(*pWorkgroups)) {
1791                             if (QString::compare(w->workgroupName(), workgroupName, Qt::CaseInsensitive) == 0) {
1792                                 foundWorkgroup = true;
1793                                 break;
1794                             }
1795                         }
1796 
1797                         //
1798                         // If the workgroup is unknown, add it to the list
1799                         //
1800                         if (!foundWorkgroup) {
1801                             //
1802                             // Create the workgroup object
1803                             //
1804                             WorkgroupPtr workgroup = WorkgroupPtr(new Smb4KWorkgroup());
1805 
1806                             //
1807                             // Set the workgroup/domain name
1808                             //
1809                             workgroup->setWorkgroupName(workgroupName);
1810 
1811                             //
1812                             // Add the workgroup
1813                             //
1814                             *pWorkgroups << workgroup;
1815                         }
1816 
1817                         break;
1818                     }
1819                     case LookupDomainMembers: {
1820                         //
1821                         // Get the workgroup name
1822                         //
1823                         QString workgroupName = entry.section(QStringLiteral(":"), 1, -1);
1824 
1825                         //
1826                         // Work around an empty workgroup/domain name. Use the "LOCAL" domain from
1827                         // DNS-SD for that.
1828                         //
1829                         if (workgroupName.isEmpty()) {
1830                             workgroupName = QStringLiteral("LOCAL");
1831                         }
1832 
1833                         //
1834                         // Get the host name. Unfortunately, the delimiter depends on
1835                         // whether the host is member of a workgroup (/) or domain (\).
1836                         //
1837                         QString hostName;
1838 
1839                         if (entry.contains(QStringLiteral("/"))) {
1840                             hostName = entry.section(QStringLiteral("/"), 0, 0);
1841                         } else if (entry.contains(QStringLiteral("\\"))) {
1842                             hostName = entry.section(QStringLiteral("\\"), 0, 0);
1843                         }
1844 
1845                         //
1846                         // Process the host name. Only add a new host, if it
1847                         // is not present already.
1848                         //
1849                         if (!hostName.isEmpty()) {
1850                             //
1851                             // Check if the server is already known
1852                             //
1853                             bool foundServer = false;
1854 
1855                             for (const HostPtr &h : qAsConst(*pHosts)) {
1856                                 if (QString::compare(h->hostName(), hostName, Qt::CaseInsensitive) == 0
1857                                     && QString::compare(h->workgroupName(), workgroupName, Qt::CaseInsensitive) == 0) {
1858                                     foundServer = true;
1859                                     break;
1860                                 }
1861                             }
1862 
1863                             //
1864                             // If the server is unknown, add it to the list
1865                             //
1866                             if (!foundServer) {
1867                                 //
1868                                 // Create the host object
1869                                 //
1870                                 HostPtr host = HostPtr(new Smb4KHost());
1871 
1872                                 //
1873                                 // Set the workgroup/domain name
1874                                 //
1875                                 host->setWorkgroupName(workgroupName);
1876 
1877                                 //
1878                                 // Set the host name
1879                                 //
1880                                 host->setHostName(hostName);
1881 
1882                                 //
1883                                 // Lookup IP address
1884                                 //
1885                                 QHostAddress address = lookupIpAddress(hostName);
1886 
1887                                 //
1888                                 // Process the IP address.
1889                                 //
1890                                 if (!address.isNull()) {
1891                                     host->setIpAddress(address);
1892                                 }
1893 
1894                                 //
1895                                 // Add the host
1896                                 //
1897                                 *pHosts << host;
1898                             }
1899                         }
1900 
1901                         break;
1902                     }
1903                     default: {
1904                         break;
1905                     }
1906                     }
1907                 }
1908             }
1909         }
1910     }
1911 
1912     //
1913     // Restart the timer
1914     //
1915     m_timer->start();
1916 }
1917 
1918 void Smb4KWsDiscoveryJob::slotDiscoveryFinished()
1919 {
1920     emitResult();
1921 }
1922 #endif