/***************************************************************************
    This class provides the interface to the libsmbclient library.
                             -------------------
    begin                : Sa Oct 20 2018
    copyright            : (C) 2018-2021 by Alexander Reinholdt
    email                : alexander.reinholdt@kdemail.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 2 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, write to the                         *
 *   Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston,*
 *   MA 02110-1335, USA                                                    *
 ***************************************************************************/

// application specific includes
#include "smb4kclient.h"
#include "smb4kclient_p.h"
#include "smb4khardwareinterface.h"
#include "smb4ksettings.h"
#include "smb4kcustomoptionsmanager.h"
#include "smb4kcustomoptions.h"
#include "smb4kbasicnetworkitem.h"
#include "smb4kglobal.h"
#include "smb4khomesshareshandler.h"
#include "smb4kwalletmanager.h"
#include "smb4knotification.h"

// Qt includes
#include <QUdpSocket>
#include <QHostAddress>
#include <QTest>
#include <QApplication>
#include <QTimer>

using namespace Smb4KGlobal;

Q_GLOBAL_STATIC(Smb4KClientStatic, p);


Smb4KClient::Smb4KClient(QObject* parent) 
: KCompositeJob(parent), d(new Smb4KClientPrivate)
{
    //
    // Connections
    // 
    connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(slotAboutToQuit()));
}


Smb4KClient::~Smb4KClient()
{
}


Smb4KClient *Smb4KClient::self()
{
    return &p->instance;
}


void Smb4KClient::start()
{
    //
    // Connect to the online state monitoring
    // 
    connect(Smb4KHardwareInterface::self(), SIGNAL(onlineStateChanged(bool)), this, SLOT(slotOnlineStateChanged(bool)), Qt::UniqueConnection);
  
    //
    // Start the scanning
    // 
    if (Smb4KHardwareInterface::self()->isOnline())
    {
        QTimer::singleShot(50, this, SLOT(slotStartJobs()));
    }
}


bool Smb4KClient::isRunning()
{
    return hasSubjobs();
}


void Smb4KClient::abort()
{
    QListIterator<KJob *> it(subjobs());
    
    while (it.hasNext())
    {
        it.next()->kill(KJob::EmitResult);
    }
}


void Smb4KClient::lookupDomains()
{
    //
    // Send Wakeup-On-LAN packages
    // 
    if (Smb4KSettings::enableWakeOnLAN())
    {
        QList<OptionsPtr> wakeOnLanEntries = Smb4KCustomOptionsManager::self()->wakeOnLanEntries();
    
        if (!wakeOnLanEntries.isEmpty())
        {
            NetworkItemPtr item = NetworkItemPtr(new Smb4KBasicNetworkItem());
            emit aboutToStart(item, WakeUp);
      
            QUdpSocket *socket = new QUdpSocket(this);
      
            for (int i = 0; i < wakeOnLanEntries.size(); ++i)
            {
                if (wakeOnLanEntries.at(i)->wolSendBeforeNetworkScan())
                {
                    QHostAddress addr;
          
                    if (wakeOnLanEntries.at(i)->hasIpAddress())
                    {
                        addr.setAddress(wakeOnLanEntries.at(i)->ipAddress());
                    }
                    else
                    {
                        addr.setAddress("255.255.255.255");
                    }
          
                    // Construct magic sequence
                    QByteArray sequence;

                    // 6 times 0xFF
                    for (int j = 0; j < 6; ++j)
                    {
                        sequence.append(QChar(0xFF).toLatin1());
                    }
          
                    // 16 times the MAC address
                    QStringList parts = wakeOnLanEntries.at(i)->macAddress().split(':', QString::SkipEmptyParts);
          
                    for (int j = 0; j < 16; ++j)
                    {
                        for (int k = 0; k < parts.size(); ++k)
                        {
                            sequence.append(QChar(QString("0x%1").arg(parts.at(k)).toInt(0, 16)).toLatin1());
                        }
                    }
          
                    socket->writeDatagram(sequence, addr, 9);
                }
            }
      
            delete socket;
      
            // Wait the defined time
            int stop = 1000 * Smb4KSettings::wakeOnLANWaitingTime() / 250;
            int i = 0;
      
            while (i++ < stop)
            {
                QTest::qWait(250);
            }
      
            emit finished(item, WakeUp);
            item.clear();
        }
    }
  
    //
    // Emit the aboutToStart() signal
    // 
    NetworkItemPtr networkItem = NetworkItemPtr(new Smb4KBasicNetworkItem(Network));
    networkItem->setUrl(QUrl("smb://"));
    emit aboutToStart(networkItem, LookupDomains);
    
    //
    // Set the busy cursor
    //
    if (!hasSubjobs() && modifyCursor())
    {
        QApplication::setOverrideCursor(Qt::BusyCursor);
    }
  
    // 
    // Create the client job
    // 
    Smb4KClientJob *clientJob = new Smb4KClientJob(this);
    clientJob->setNetworkItem(networkItem);
    clientJob->setProcess(LookupDomains);
    
#ifdef USE_WS_DISCOVERY
    //
    // Create the WS Discovery job, if desired
    // 
    Smb4KWsDiscoveryJob *wsDiscoveryJob = nullptr;
    
    if (Smb4KSettings::useWsDiscovery())
    {
        wsDiscoveryJob = new Smb4KWsDiscoveryJob(this);
        wsDiscoveryJob->setNetworkItem(networkItem);
        wsDiscoveryJob->setProcess(LookupDomains);
    }
#endif
    
    //
    // Create the DNS-SD job, if desired
    // 
    Smb4KDnsDiscoveryJob *dnsDiscoveryJob = nullptr;
    
    if (Smb4KSettings::useDnsServiceDiscovery())
    {
        dnsDiscoveryJob = new Smb4KDnsDiscoveryJob(this);
        dnsDiscoveryJob->setNetworkItem(networkItem);
        dnsDiscoveryJob->setProcess(LookupDomains);
    }
  
    //
    // Add the jobs to the subjobs
    //
    addSubjob(clientJob);
    
#ifdef USE_WS_DISCOVERY
    if (wsDiscoveryJob)
    {
        addSubjob(wsDiscoveryJob);
    }
#endif
    
    if (dnsDiscoveryJob)
    {
        addSubjob(dnsDiscoveryJob);
    }
    
    //
    // Start the jobs
    // 
    clientJob->start();
    
#ifdef USE_WS_DISCOVERY
    if (wsDiscoveryJob)
    {
        wsDiscoveryJob->start();
    }
#endif
    
    if (dnsDiscoveryJob)
    {
        dnsDiscoveryJob->start();
    }

    //
    // Clear the network item
    // 
    networkItem.clear();
}


void Smb4KClient::lookupDomainMembers(const WorkgroupPtr &workgroup)
{
    //
    // Emit the aboutToStart() signal
    // 
    emit aboutToStart(workgroup, LookupDomainMembers);
  
    //
    // Set the busy cursor
    //
    if (!hasSubjobs() && modifyCursor())
    {
        QApplication::setOverrideCursor(Qt::BusyCursor);
    }
  
    // 
    // Create the client job
    // 
    Smb4KClientJob *clientJob = new Smb4KClientJob(this);
    clientJob->setNetworkItem(workgroup);
    clientJob->setProcess(LookupDomainMembers);
    
#ifdef USE_WS_DISCOVERY
    //
    // Create the WS Discovery job, if desired
    // 
    Smb4KWsDiscoveryJob *wsDiscoveryJob = nullptr;
    
    if (Smb4KSettings::useWsDiscovery())
    {
        wsDiscoveryJob = new Smb4KWsDiscoveryJob(this);
        wsDiscoveryJob->setNetworkItem(workgroup);
        wsDiscoveryJob->setProcess(LookupDomainMembers);
    }
#endif
  
    //
    // Create the DNS-SD job, if desired
    // 
    Smb4KDnsDiscoveryJob *dnsDiscoveryJob = nullptr;
    
    if (Smb4KSettings::useDnsServiceDiscovery())
    {
        dnsDiscoveryJob = new Smb4KDnsDiscoveryJob(this);
        dnsDiscoveryJob->setNetworkItem(workgroup);
        dnsDiscoveryJob->setProcess(LookupDomainMembers);
    }
  
    //
    // Add the jobs to the subjobs
    //
    addSubjob(clientJob);
    
#ifdef USE_WS_DISCOVERY
    if (wsDiscoveryJob)
    {
        addSubjob(wsDiscoveryJob);
    }
#endif
    
    if (dnsDiscoveryJob)
    {
        addSubjob(dnsDiscoveryJob);
    }

    //
    // Start the job
    // 
    clientJob->start();
    
#ifdef USE_WS_DISCOVERY
    if (wsDiscoveryJob)
    {
        wsDiscoveryJob->start();
    }
#endif
    
    if (dnsDiscoveryJob)
    {
        dnsDiscoveryJob->start();
    }
}


void Smb4KClient::lookupShares(const HostPtr &host)
{
    //
    // Emit the aboutToStart() signal
    // 
    emit aboutToStart(host, LookupShares);
  
    // 
    // Create the job
    // 
    Smb4KClientJob *job = new Smb4KClientJob(this);
    job->setNetworkItem(host);
    job->setProcess(LookupShares);
  
    //
    // Set the busy cursor
    //
    if (!hasSubjobs() && modifyCursor())
    {
        QApplication::setOverrideCursor(Qt::BusyCursor);
    }

    //
    // Add the job to the subjobs
    //
    addSubjob(job);

    //
    // Start the job
    // 
    job->start();  
}


void Smb4KClient::lookupFiles(const NetworkItemPtr &item)
{
    //
    // Check that the network item has the correct type and process it.
    // 
    if (item->type() == Share || item->type() == Directory)
    {
        //
        // Emit the aboutToStart() signal
        // 
        emit aboutToStart(item, LookupFiles);
    
        // 
        // Create the job
        // 
        Smb4KClientJob *job = new Smb4KClientJob(this);
        job->setNetworkItem(item);
        job->setProcess(LookupFiles);
    
        //
        // Set the busy cursor
        //
        if (!hasSubjobs() && modifyCursor())
        {
            QApplication::setOverrideCursor(Qt::BusyCursor);
        }

        //
        // Add the job to the subjobs
        //
        addSubjob(job);

        //
        // Start the job
        // 
        job->start();
    }
}


void Smb4KClient::printFile(const SharePtr& share, const KFileItem& fileItem, int copies)
{
    //
    // Emit the aboutToStart() signal
    // 
    emit aboutToStart(share, PrintFile);
  
    // 
    // Create the job
    // 
    Smb4KClientJob *job = new Smb4KClientJob(this);
    job->setNetworkItem(share);
    job->setPrintFileItem(fileItem);
    job->setPrintCopies(copies);
    job->setProcess(PrintFile);
  
    //
    // Set the busy cursor
    //
    if (!hasSubjobs() && modifyCursor())
    {
        QApplication::setOverrideCursor(Qt::BusyCursor);
    }

    //
    // Add the job to the subjobs
    //
    addSubjob(job);

    //
    // Start the job
    // 
    job->start();
}


void Smb4KClient::search(const QString& item)
{
    //
    // Create empty basic network item
    // 
    NetworkItemPtr networkItem = NetworkItemPtr(new Smb4KBasicNetworkItem());
  
    //
    // Emit the aboutToStart() signal
    // 
    emit aboutToStart(networkItem, NetworkSearch);
  
    //
    // Before doing the search, lookup all domains, servers and shares in the 
    // network neighborhood.
    // 
    lookupDomains();
  
    while(isRunning())
    {
        QTest::qWait(50);
    }
  
    for (const WorkgroupPtr &workgroup : workgroupsList())
    {
        lookupDomainMembers(workgroup);
    
        while(isRunning())
        {
            QTest::qWait(50);
        }
    }
  
    for (const HostPtr &host : hostsList())
    {
        lookupShares(host);
    
        while(isRunning())
        {
            QTest::qWait(50);
        }
    }
  
    //
    // Do the actual search
    // 
    QList<SharePtr> results;
  
    for (const SharePtr &share : sharesList())
    {
        if (share->shareName().contains(item, Qt::CaseInsensitive))
        {
            results << share;
        }
    }
  
    //
    // Emit the search results
    // 
    emit searchResults(results);
  
    //
    // Emit the finished() signal
    // 
    emit finished(networkItem, NetworkSearch);
}



void Smb4KClient::openPreviewDialog(const SharePtr &share)
{
    //
    // Printer share check
    // 
    if (share->isPrinter())
    {        
        return;
    }
      
    //
    // 'homes' share check
    //
    if (share->isHomesShare())
    {
        Smb4KHomesSharesHandler::self()->specifyUser(share, true);
    }
    
    //
    // Start the preview dialog
    // 
    // First, check if a preview dialog has already been set up for this share 
    // and reuse it, if possible.
    // 
    QPointer<Smb4KPreviewDialog> dlg = 0;
  
    for (Smb4KPreviewDialog *p : d->previewDialogs)
    {
        if (share == p->share())
        {
            dlg = p;
        }
    }
  
    //
    // If there was no preview dialog present, create a new one
    // 
    if (!dlg)
    {
        dlg = new Smb4KPreviewDialog(share, QApplication::activeWindow());
        d->previewDialogs << dlg;
    
        //
        // Connections
        // 
        connect(dlg, SIGNAL(requestPreview(NetworkItemPtr)), this, SLOT(slotStartNetworkQuery(NetworkItemPtr)));
        connect(dlg, SIGNAL(aboutToClose(Smb4KPreviewDialog*)), this, SLOT(slotPreviewDialogClosed(Smb4KPreviewDialog*)));
        connect(dlg, SIGNAL(requestAbort()), this, SLOT(slotAbort()));
        connect(this, SIGNAL(files(QList<FilePtr>)), dlg, SLOT(slotPreviewResults(QList<FilePtr>)));
        connect(this, SIGNAL(aboutToStart(NetworkItemPtr,int)), dlg, SLOT(slotAboutToStart(NetworkItemPtr,int)));
        connect(this, SIGNAL(finished(NetworkItemPtr,int)), dlg, SLOT(slotFinished(NetworkItemPtr,int)));
    }
  
    //
    // Show the preview dialog
    // 
    if (!dlg->isVisible())
    {
        dlg->setVisible(true);
    }
}


void Smb4KClient::openPrintDialog(const SharePtr& share)
{
    //
    // Printer share check
    // 
    if (!share->isPrinter())
    {        
        return;
    }
  
    //
    // Start the print dialog
    // 
    // First, check if a print dialog has already been set up for this share 
    // and reuse it, if possible.
    // 
    QPointer<Smb4KPrintDialog> dlg = 0;
  
    for (Smb4KPrintDialog *p : d->printDialogs)
    {
        if (share == p->share())
        {
            dlg = p;
        }
    }
  
    //
    // If there was no print dialog present, create a new one
    // 
    if (!dlg)
    {
        Smb4KWalletManager::self()->readAuthInfo(share);
    
        dlg = new Smb4KPrintDialog(share, QApplication::activeWindow());
        d->printDialogs << dlg;
    
        connect(dlg, SIGNAL(printFile(SharePtr,KFileItem,int)), this, SLOT(slotStartPrinting(SharePtr,KFileItem,int)));
        connect(dlg, SIGNAL(aboutToClose(Smb4KPrintDialog*)), this, SLOT(slotPrintDialogClosed(Smb4KPrintDialog*)));
    }
  
    //
    // Show the preview dialog
    // 
    if (!dlg->isVisible())
    {
        dlg->setVisible(true);
    }
}


void Smb4KClient::processErrors(Smb4KClientBaseJob *job)
{
    switch (job->error())
    {
        case Smb4KClientJob::AccessDeniedError:
        {
            switch (job->networkItem()->type())
            {
                case Host:
                {
                    if (Smb4KWalletManager::self()->showPasswordDialog(job->networkItem()))
                    {
                        lookupShares(job->networkItem().staticCast<Smb4KHost>());
                    }
          
                    break;
                }
                case Share:
                {
                    if (Smb4KWalletManager::self()->showPasswordDialog(job->networkItem()))
                    {
                        if (job->process() == Smb4KGlobal::PrintFile)
                        {
                            Smb4KClientJob *clientJob = qobject_cast<Smb4KClientJob *>(job);
                            printFile(job->networkItem().staticCast<Smb4KShare>(), clientJob->printFileItem(), clientJob->printCopies());
                        }
                        else
                        {
                            lookupFiles(job->networkItem().staticCast<Smb4KShare>());
                        }
                    }
          
                    break;
                }
                case Directory:
                case File:
                {
                    FilePtr file = job->networkItem().staticCast<Smb4KFile>();
          
                    SharePtr share = SharePtr(new Smb4KShare());
                    share->setWorkgroupName(file->workgroupName());
                    share->setHostName(file->hostName());
                    share->setShareName(file->shareName());
                    share->setLogin(file->login());
                    share->setPassword(file->password());
          
                    if (Smb4KWalletManager::self()->showPasswordDialog(share))
                    {
                        file->setLogin(share->login());
                        file->setPassword(share->password());
            
                        lookupFiles(file);
                    }

                    break;
                }
                default:
                {
                    qDebug() << "Authentication error. URL:" << job->networkItem()->url();
                    break;
                }
            }
      
            break;
        }
        default:
        {
            Smb4KNotification::networkCommunicationFailed(job->errorText());
            break;
        }
    }
}


void Smb4KClient::processWorkgroups(Smb4KClientBaseJob *job)
{
    //
    // Collect the workgroups found while scanning
    // 
    for (const WorkgroupPtr &newWorkgroup : job->workgroups())
    {
        bool foundWorkgroup = false;
            
        for (const WorkgroupPtr &workgroup : d->tempWorkgroupList)
        {
            if (workgroup->workgroupName() == newWorkgroup->workgroupName())
            {
                foundWorkgroup = true;
                break;
            }
        }
            
        if (!foundWorkgroup)
        {
            d->tempWorkgroupList << newWorkgroup;
        }
    }
    
    //
    // When scanning finished, process the workgroups
    // 
    if (!isRunning())
    {
        // Remove obsolete workgroups and their members
        QListIterator<WorkgroupPtr> it(workgroupsList());
        
        while (it.hasNext())
        {
            WorkgroupPtr workgroup = it.next();
            
            bool foundWorkgroup = false;
            
            for (const WorkgroupPtr &w : qAsConst(d->tempWorkgroupList))
            {
                if (w->workgroupName() == workgroup->workgroupName())
                {
                    foundWorkgroup = true;
                    break;
                }
            }
            
            if (!foundWorkgroup)
            {
                QList<HostPtr> obsoleteHosts = workgroupMembers(workgroup);
                
                while (!obsoleteHosts.isEmpty())
                {
                    removeHost(obsoleteHosts.takeFirst());
                }
                
                removeWorkgroup(workgroup);
            }
        }
        
        // Add new workgroups and update existing ones
        for (const WorkgroupPtr &workgroup : qAsConst(d->tempWorkgroupList))
        {
            if (!findWorkgroup(workgroup->workgroupName()))
            {
                addWorkgroup(workgroup);
      
                // Since this is a new workgroup, no master browser is present.
                HostPtr masterBrowser = HostPtr(new Smb4KHost());
                masterBrowser->setWorkgroupName(workgroup->workgroupName());
                masterBrowser->setHostName(workgroup->masterBrowserName());
                masterBrowser->setIpAddress(workgroup->masterBrowserIpAddress());
                masterBrowser->setIsMasterBrowser(true);
      
                addHost(masterBrowser);
            }
            else
            {
                updateWorkgroup(workgroup);
      
                // Check if the master browser changed
                QList<HostPtr> members = workgroupMembers(workgroup);
      
                for (const HostPtr &host : qAsConst(members))
                {
                    if (workgroup->masterBrowserName() == host->hostName())
                    {
                        host->setIsMasterBrowser(true);
          
                        if (!host->hasIpAddress() && workgroup->hasMasterBrowserIpAddress())
                        {
                            host->setIpAddress(workgroup->masterBrowserIpAddress());
                        }
                    }
                    else
                    {
                        host->setIsMasterBrowser(false);
                    }
                }
            }
        }
        
        // Clear the temporary workgroup list
        while (!d->tempWorkgroupList.isEmpty())
        {
            d->tempWorkgroupList.takeFirst().clear();
        }
        
        emit workgroups();
    }
}


void Smb4KClient::processHosts(Smb4KClientBaseJob *job)
{
    //
    // Collect the hosts found while scanning
    // 
    for (const HostPtr &newHost : job->hosts())
    {
        bool foundHost = false;
            
        for (const HostPtr &host : d->tempHostList)
        {
            if (host->workgroupName() == newHost->workgroupName() && host->hostName() == newHost->hostName())
            {
                foundHost = true;
                break;
            }
        }
            
        if (!foundHost)
        {
            d->tempHostList << newHost;
        }
    }
    
    //
    // When scanning finished, process the hosts
    // 
    if (!isRunning())
    {
        // Get the workgroup pointer. Although several scans might have been
        // running, the workgroup should have been always the same.
        WorkgroupPtr workgroup = job->networkItem().staticCast<Smb4KWorkgroup>();
        
        // Remove obsolete workgroup/domain members
        QList<HostPtr> members = workgroupMembers(workgroup);
        QListIterator<HostPtr> it(members);
        
        while (it.hasNext())
        {
            HostPtr host = it.next();
            
            bool foundHost = false;
            
            for (const HostPtr &h : qAsConst(d->tempHostList))
            {
                if (h->workgroupName() == host->workgroupName() && h->hostName() == host->hostName())
                {
                    foundHost = true;
                    break;
                }
            }
            
            if (!foundHost)
            {
                QList<SharePtr> obsoleteShares = sharedResources(host);
                
                while (!obsoleteShares.isEmpty())
                {
                    removeShare(obsoleteShares.takeFirst());
                }
                
                removeHost(host);
            }
        }
        
        // Add new hosts and update existing ones
        for (const HostPtr &host : d->tempHostList)
        {
            if (host->hostName() == workgroup->masterBrowserName())
            {
                host->setIsMasterBrowser(true);
            }
            else
            {
                host->setIsMasterBrowser(false);
            }
      
            if (!findHost(host->hostName(), host->workgroupName()))
            {
                addHost(host);
            }
            else
            {
                updateHost(host);
            }            
        }
        
        // Clear the temporary host list
        while (!d->tempHostList.isEmpty())
        {
            d->tempHostList.takeFirst().clear();
        }
        
        emit hosts(workgroup);
    }
}


void Smb4KClient::processShares(Smb4KClientBaseJob *job)
{
    //
    // To look up the shared resources of a host, we only use
    // Samba's client library, so we do not have to wait until 
    // several jobs finished.
    // 
    // Get the host pointer
    // 
    HostPtr host = job->networkItem().staticCast<Smb4KHost>();

    //
    // Remove obsolete shares
    //
    QList<SharePtr> sharedRes = sharedResources(host);
    QListIterator<SharePtr> it(sharedRes);
    
    while (it.hasNext())
    {
        SharePtr share = it.next();
      
        bool foundShare = false;
      
        for (const SharePtr &s : job->shares())
        {
            if (s->workgroupName() == share->workgroupName() && s->url().matches(share->url(), QUrl::RemoveUserInfo|QUrl::RemovePort))
            {
                foundShare = true;
                break;
            }
        }
      
        if (!foundShare || (share->isHidden() && !Smb4KSettings::detectHiddenShares()) || (share->isPrinter() && !Smb4KSettings::detectPrinterShares()))
        {
            removeShare(share);
        }
    }
  
    //
    // Add new shares and update existing ones
    //
    for (const SharePtr &share : job->shares())
    {
        // Process only those shares that the user wants to see
        if (share->isHidden() && !Smb4KSettings::detectHiddenShares())
        {
            continue;
        }
      
        if (share->isPrinter() && !Smb4KSettings::detectPrinterShares())
        {
            continue;
        }
      
        // Add or update the shares
        if (!findShare(share->url(), share->workgroupName()))
        {
            addShare(share);
        }
        else
        {
            updateShare(share);
        }
    }
    
    emit shares(host);
}


void Smb4KClient::processFiles(Smb4KClientBaseJob *job)
{
    QList<FilePtr> list;
  
    for (const FilePtr &file : job->files())
    {
        if (file->isHidden() && !Smb4KSettings::previewHiddenItems())
        {
            continue;
        }
    
        list << file;
    }  
  
    emit files(list);
}


void Smb4KClient::slotStartJobs()
{
    //
    // Lookup domains as the first step
    // 
    lookupDomains();
}


void Smb4KClient::slotOnlineStateChanged(bool online)
{
    if (online)
    {
        slotStartJobs();
    }
    else
    {
        abort();
    }
}


void Smb4KClient::slotResult(KJob *job)
{
    //
    // Remove the job
    //
    removeSubjob(job);
    
    //
    // Get the client base job
    // 
    Smb4KClientBaseJob *clientBaseJob = qobject_cast<Smb4KClientBaseJob *>(job);
  
    //
    // Define a network item pointer and the process value for the 
    // finished() signal.
    // 
    NetworkItemPtr networkItem = clientBaseJob->networkItem();
    Smb4KGlobal::Process process = clientBaseJob->process();
  
    //
    // Get the result from the query and process it
    // 
    if (clientBaseJob->error() == 0)
    {
        switch (clientBaseJob->networkItem()->type())
        {
            case Network:
            {
                // Process the discovered workgroups
                processWorkgroups(clientBaseJob);
                break;
            }
            case Workgroup:
            {
                // Process the discovered workgroup members
                processHosts(clientBaseJob);
                break;
            }
            case Host:
            {
                // Process the discovered shares
                processShares(clientBaseJob);
                break;
            }
            case Share:
            case Directory:
            {
                // Process the discoveres files and directories
                processFiles(clientBaseJob);
                break;
            }
            default:
            {
                break;
            }
        }
    }
    else
    {
        processErrors(clientBaseJob);
    }

    //
    // Emit the finished signal when all subjobs finished
    // 
    if (!hasSubjobs())
    {
        emit finished(networkItem, process);
    }
  
    //
    // Clear the network item pointer
    // 
    networkItem.clear();
  
    //
    // Restore the cursor
    //
    if (!hasSubjobs() && modifyCursor())
    {
        QApplication::restoreOverrideCursor();
    }
}


void Smb4KClient::slotAboutToQuit()
{
    abort();
}


void Smb4KClient::slotStartNetworkQuery(NetworkItemPtr item)
{
    //
    // Look up files
    //
    lookupFiles(item);
}


void Smb4KClient::slotPreviewDialogClosed(Smb4KPreviewDialog *dialog)
{
    //
    // Remove the preview dialog from the list
    // 
    if (dialog)
    {
        // Find the dialog in the list and take it from the list.
        // It will automatically be deleted on close, so there is
        // no need to delete the dialog here.
        int i = d->previewDialogs.indexOf(dialog);
        d->previewDialogs.takeAt(i);
    }
}


void Smb4KClient::slotAbort()
{
    abort();
}


void Smb4KClient::slotStartPrinting(const SharePtr& printer, const KFileItem& fileItem, int copies)
{
    //
    // Start printing
    // 
    printFile(printer, fileItem, copies);
}


void Smb4KClient::slotPrintDialogClosed(Smb4KPrintDialog* dialog)
{
    //
    // Remove the print dialog from the list
    // 
    if (dialog)
    {
        // Find the dialog in the list and take it from the list.
        // It will automatically be deleted on close, so there is
        // no need to delete the dialog here.
        int i = d->printDialogs.indexOf(dialog);
        d->printDialogs.takeAt(i);
    }
}


