You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kftpgrabber/kftpgrabber/src/kftpsession.cpp

921 lines
26 KiB

/*
* This file is part of the KFTPGrabber project
*
* Copyright (C) 2003-2004 by the KFTPGrabber developers
* Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.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
* is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
* NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
* You must obey the GNU General Public License in all respects
* for all of the code used other than OpenSSL. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you
* do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source
* files in the program, then also delete it here.
*/
#include "kftpsession.h"
#include "kftpapi.h"
#include "browser/detailsview.h"
#include "browser/treeview.h"
#include "browser/view.h"
#include "kftpbookmarks.h"
#include "misc.h"
#include "widgets/systemtray.h"
#include "mainactions.h"
#include "misc/kftpconfig.h"
#include "misc/filter.h"
#include <ntqdir.h>
#include <ntqobjectlist.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <kdebug.h>
#include <kpassdlg.h>
#include <kstaticdeleter.h>
using namespace KFTPGrabberBase;
using namespace KFTPEngine;
using namespace KFTPCore::Filter;
namespace KFTPSession {
//////////////////////////////////////////////////////////////////
//////////////////////// Connection ///////////////////////
//////////////////////////////////////////////////////////////////
Connection::Connection(Session *session, bool primary)
: TQObject(session),
m_primary(primary),
m_busy(false),
m_aborting(false),
m_scanning(false)
{
// Create the actual connection client
m_client = new KFTPEngine::Thread();
connect(m_client->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), this, SLOT(slotEngineEvent(KFTPEngine::Event*)));
// If this is not a core session connection, connect
if (!primary) {
// Connect to the server
KURL url = session->getClient()->socket()->getCurrentUrl();
KFTPBookmarks::Manager::self()->setupClient(session->getSite(), m_client);
m_client->connect(url);
}
}
Connection::~Connection()
{
delete m_client;
}
bool Connection::isConnected()
{
return !static_cast<Session*>(parent())->isRemote() || m_client->socket()->isConnected();
}
void Connection::acquire(KFTPQueue::Transfer *transfer)
{
if (m_busy || !static_cast<Session*>(parent())->isRemote())
return;
m_curTransfer = transfer;
m_busy = true;
connect(transfer, SIGNAL(transferComplete(long)), this, SLOT(slotTransferCompleted()));
connect(transfer, SIGNAL(transferAbort(long)), this, SLOT(slotTransferCompleted()));
emit connectionAcquired();
}
void Connection::remove()
{
// Disconnect all signals from the transfer
if (m_curTransfer)
m_curTransfer->TQObject::disconnect(this);
m_curTransfer = 0L;
m_busy = false;
emit connectionRemoved();
emit static_cast<Session*>(parent())->freeConnectionAvailable();
}
void Connection::abort()
{
if (m_aborting || !m_client->socket()->isBusy())
return;
// Emit the signal before aborting
emit aborting();
// Abort transfer
m_aborting = true;
m_client->abort();
m_aborting = false;
}
void Connection::scanDirectory(KFTPQueue::Transfer *parent)
{
// Lock the connection and the transfer
acquire(parent);
parent->lock();
m_scanning = true;
if (isConnected())
m_client->scan(parent->getSourceUrl());
}
void Connection::addScannedDirectory(KFTPEngine::DirectoryTree *tree, KFTPQueue::Transfer *parent)
{
// Directories
DirectoryTree::DirIterator dirEnd = tree->directories()->end();
for (DirectoryTree::DirIterator i = tree->directories()->begin(); i != dirEnd; i++) {
KURL sourceUrlBase = parent->getSourceUrl();
KURL destUrlBase = parent->getDestUrl();
sourceUrlBase.addPath((*i)->info().filename());
destUrlBase.addPath((*i)->info().filename());
// Check if we should skip this entry
const ActionChain *actionChain = Filters::self()->process(sourceUrlBase, 0, true);
if (actionChain && actionChain->getAction(Action::Skip))
continue;
// Add directory transfer
KFTPQueue::TransferDir *transfer = new KFTPQueue::TransferDir(parent);
transfer->setSourceUrl(sourceUrlBase);
transfer->setDestUrl(destUrlBase);
transfer->setTransferType(parent->getTransferType());
transfer->setId(KFTPQueue::Manager::self()->nextTransferId());
emit KFTPQueue::Manager::self()->newTransfer(transfer);
addScannedDirectory(*i, transfer);
if (KFTPCore::Config::skipEmptyDirs() && !transfer->hasChildren())
KFTPQueue::Manager::self()->removeTransfer(transfer, false);
}
// Files
DirectoryTree::FileIterator fileEnd = tree->files()->end();
for (DirectoryTree::FileIterator i = tree->files()->begin(); i != fileEnd; i++) {
KURL sourceUrlBase = parent->getSourceUrl();
KURL destUrlBase = parent->getDestUrl();
sourceUrlBase.addPath((*i).filename());
destUrlBase.addPath((*i).filename());
// Check if we should skip this entry
const ActionChain *actionChain = Filters::self()->process(sourceUrlBase, (*i).size(), false);
if (actionChain && actionChain->getAction(Action::Skip))
continue;
// Add file transfer
KFTPQueue::TransferFile *transfer = new KFTPQueue::TransferFile(parent);
transfer->addSize((*i).size());
transfer->setSourceUrl(sourceUrlBase);
transfer->setDestUrl(destUrlBase);
transfer->setTransferType(parent->getTransferType());
transfer->setId(KFTPQueue::Manager::self()->nextTransferId());
emit KFTPQueue::Manager::self()->newTransfer(transfer);
}
}
void Connection::slotEngineEvent(KFTPEngine::Event *event)
{
switch (event->type()) {
case Event::EventDisconnect: {
emit connectionLost(this);
break;
}
case Event::EventConnect: {
emit connectionEstablished();
if (m_scanning) {
// Connected successfully, let's scan
m_client->scan(m_curTransfer->getSourceUrl());
}
break;
}
case Event::EventError: {
ErrorCode error = event->getParameter(0).asErrorCode();
if (m_scanning && (error == ConnectFailed || error == LoginFailed || error == OperationFailed)) {
// Scanning should be aborted, since there was an error
m_scanning = false;
m_curTransfer->unlock();
remove();
emit static_cast<Session*>(parent())->dirScanDone();
}
break;
}
case Event::EventScanComplete: {
if (m_scanning) {
// We have the listing
DirectoryTree *tree = static_cast<DirectoryTree*>(event->getParameter(0).asData());
addScannedDirectory(tree, m_curTransfer);
delete tree;
m_scanning = false;
m_curTransfer->unlock();
remove();
emit static_cast<Session*>(parent())->dirScanDone();
}
break;
}
default: break;
}
}
void Connection::slotTransferCompleted()
{
// Remove the lock
remove();
}
void Connection::reconnect()
{
if (!m_client->socket()->isConnected()) {
KFTPBookmarks::Manager::self()->setupClient(static_cast<Session*>(parent())->getSite(), m_client);
m_client->connect(m_client->socket()->getCurrentUrl());
}
}
////////////////////////////////////////////////////////
//////////////////// Session ////////////////////
////////////////////////////////////////////////////////
Session::Session(Side side)
: TQObject(),
m_side(side),
m_remote(false),
m_aborting(false),
m_registred(false),
m_site(0)
{
// Register this session
Manager::self()->registerSession(this);
}
Session::~Session()
{
}
KFTPEngine::Thread *Session::getClient()
{
// Return the first (core) connection's client
return m_connections.at(0)->getClient();
}
bool Session::isConnected()
{
// If there are no connections, just check if the session is remote
if (m_connections.count() == 0)
return !m_remote;
return m_connections.at(0)->isConnected();
}
void Session::slotClientEngineEvent(KFTPEngine::Event *event)
{
switch (event->type()) {
case Event::EventConnect: {
// ***************************************************************************
// ****************************** EventConnect *******************************
// ***************************************************************************
m_remote = true;
m_aborting = false;
m_lastUrl = getClient()->socket()->getCurrentUrl();
TQString siteName;
if (m_site)
siteName = m_site->getAttribute("name");
else
siteName = m_lastUrl.host();
Manager::self()->getTabs(m_side)->changeTab(m_fileView, siteName);
Manager::self()->getStatTabs()->changeTab(m_log, i18n("Log (%1)").arg(siteName));
Manager::self()->getStatTabs()->showPage(m_log);
Manager::self()->doEmitUpdate();
KURL homeUrl = getClient()->socket()->getCurrentUrl();
if (m_site && !m_site->getProperty("defremotepath").isEmpty())
homeUrl.setPath(m_site->getProperty("defremotepath"));
else
homeUrl.setPath(getClient()->socket()->getDefaultDirectory());
m_fileView->setHomeUrl(homeUrl);
m_fileView->goHome();
Session *opposite = Manager::self()->getActive(oppositeSide(m_side));
if (m_site && !opposite->isRemote()) {
TQString localPath = m_site->getProperty("deflocalpath");
if (!localPath.isEmpty())
opposite->getFileView()->openUrl(KURL(localPath));
}
break;
}
case Event::EventDisconnect: {
// ***************************************************************************
// **************************** EventDisconnect ******************************
// ***************************************************************************
m_remote = false;
m_aborting = false;
Manager::self()->getTabs(m_side)->changeTab(m_fileView, i18n("Local Session"));
Manager::self()->getStatTabs()->changeTab(m_log, "[" + i18n("Log") + "]");
Manager::self()->doEmitUpdate();
m_fileView->setHomeUrl(KURL(KFTPCore::Config::defLocalDir()));
m_fileView->goHome();
break;
}
case Event::EventCommand: m_log->ftpLog(1, event->getParameter(0).asString()); break;
case Event::EventMultiline: m_log->ftpLog(2, event->getParameter(0).asString()); break;
case Event::EventResponse: m_log->ftpLog(0, event->getParameter(0).asString()); break;
case Event::EventMessage: m_log->ftpLog(3, event->getParameter(0).asString()); break;
case Event::EventRetrySuccess: {
// ***************************************************************************
// ************************** EventRetrySuccess ******************************
// ***************************************************************************
if (KFTPCore::Config::showRetrySuccessBalloon()) {
KFTPWidgets::SystemTray::self()->showBalloon(i18n("Connection with %1 has been successfully established.").arg(getClient()->socket()->getCurrentUrl().host()));
}
break;
}
case Event::EventReloadNeeded: {
// We should only do refreshes if the queue is not being processed
if (KFTPQueue::Manager::self()->getNumRunning() == 0)
m_fileView->reload();
break;
}
case Event::EventPubkeyPassword: {
// A public-key authentication password was requested
TQString pass;
int ret = KPasswordDialog::getPassword(pass, i18n("Please provide your private key decryption password."));
if (ret == KPasswordDialog::Accepted) {
PubkeyWakeupEvent *event = new PubkeyWakeupEvent();
event->password = pass;
getClient()->wakeup(event);
} else {
getClient()->abort();
}
break;
}
default: break;
}
}
void Session::scanDirectory(KFTPQueue::Transfer *parent, Connection *connection)
{
// Go trough all files in path and add them as transfers that have parent as their parent
// transfer
KURL path = parent->getSourceUrl();
if (path.isLocalFile()) {
connect(new DirectoryScanner(parent), SIGNAL(completed()), this, SIGNAL(dirScanDone()));
} else if (m_remote) {
if (!connection) {
if (!isFreeConnection()) {
emit dirScanDone();
return;
}
// Assign a new connection (it might be unconnected!)
connection = assignConnection();
}
connection->scanDirectory(parent);
}
}
void Session::abort()
{
if (m_aborting)
return;
m_aborting = true;
emit aborting();
// Abort all connections
Connection *conn;
for (conn = m_connections.first(); conn; conn = m_connections.next()) {
conn->abort();
}
m_aborting = false;
}
void Session::reconnect(const KURL &url)
{
// Set the reconnect url
m_reconnectUrl = url;
if (m_remote && getClient()->socket()->isConnected()) {
abort();
connect(getClient()->eventHandler(), SIGNAL(disconnected()), this, SLOT(slotStartReconnect()));
getClient()->disconnect();
} else {
// The session is already disconnected, just call the slot
slotStartReconnect();
}
}
void Session::slotStartReconnect()
{
disconnect(getClient()->eventHandler(), SIGNAL(disconnected()), this, SLOT(slotStartReconnect()));
// Reconnect only if this is a remote url
if (!m_reconnectUrl.isLocalFile()) {
KFTPBookmarks::Manager::self()->setupClient(m_site, getClient());
getClient()->connect(m_reconnectUrl);
}
// Invalidate the url
m_reconnectUrl = KURL();
}
int Session::getMaxThreadCount()
{
// First get the global thread count
int count = KFTPCore::Config::threadCount();
if (!KFTPCore::Config::threadUsePrimary())
count++;
// Try to see if threads are disabled for this site
if (count > 1 && isRemote()) {
if (m_site && m_site->getIntProperty("disableThreads"))
return 1;
}
return count;
}
bool Session::isFreeConnection()
{
unsigned int max = getMaxThreadCount();
unsigned int free = 0;
if ((m_connections.count() < max && max > 1) || !isRemote())
return true;
Connection *conn;
for (conn = m_connections.first(); conn; conn = m_connections.next()) {
if (!conn->isBusy() && (!conn->isPrimary() || KFTPCore::Config::threadUsePrimary() || max == 1))
free++;
}
return free > 0;
}
Connection *Session::assignConnection()
{
unsigned int max = getMaxThreadCount();
if (m_connections.count() == 0) {
// We need a new core connection
Connection *conn = new Connection(this, true);
m_connections.append(conn);
Manager::self()->doEmitUpdate();
return conn;
} else {
// Find a free connection
Connection *conn;
for (conn = m_connections.first(); conn; conn = m_connections.next()) {
if (!conn->isBusy() && (!conn->isPrimary() || KFTPCore::Config::threadUsePrimary() || max == 1))
return conn;
}
// No free connection has been found, but we may be able to create
// another (if we are within limits)
if (m_connections.count() < max) {
conn = new Connection(this);
m_connections.append(conn);
Manager::self()->doEmitUpdate();
return conn;
}
}
return 0;
}
void Session::disconnectAllConnections()
{
// Abort any possible transfers first
abort();
// Now disconnect all connections
Connection *conn;
for (conn = m_connections.first(); conn; conn = m_connections.next()) {
if (conn->getClient()->socket()->isConnected()) {
conn->getClient()->disconnect();
}
}
}
////////////////////////////////////////////////////////
/////////////////////// Manager ////////////////////////
////////////////////////////////////////////////////////
Manager *Manager::m_self = 0;
Manager *Manager::self()
{
return m_self;
}
Manager::Manager(TQObject *parent, TQTabWidget *stat, KFTPTabWidget *left, KFTPTabWidget *right)
: TQObject(parent),
m_statTabs(stat),
m_leftTabs(left),
m_rightTabs(right),
m_active(0),
m_leftActive(0),
m_rightActive(0)
{
Manager::m_self = this;
// Connect some signals
connect(left, SIGNAL(currentChanged(TQWidget*)), this, SLOT(slotActiveChanged(TQWidget*)));
connect(right, SIGNAL(currentChanged(TQWidget*)), this, SLOT(slotActiveChanged(TQWidget*)));
connect(left, SIGNAL(closeRequest(TQWidget*)), this, SLOT(slotSessionCloseRequest(TQWidget*)));
connect(right, SIGNAL(closeRequest(TQWidget*)), this, SLOT(slotSessionCloseRequest(TQWidget*)));
}
void Manager::registerSession(Session *session)
{
m_active = session;
// Create some new stuff and assign it to the session
session->assignConnection();
session->m_fileView = new KFTPWidgets::Browser::View(0L, "", session->getClient(), session);
session->m_log = new KFTPWidgets::LogView();
// Install event filters
session->getFileView()->getDetailsView()->installEventFilter(this);
session->getFileView()->getTreeView()->installEventFilter(this);
session->getFileView()->m_toolBarFirst->installEventFilter(this);
session->getFileView()->m_toolBarSecond->installEventFilter(this);
connect(session->getFileView()->getDetailsView(), SIGNAL(clicked(TQListViewItem*)), this, SLOT(slotSwitchFocus()));
connect(session->getFileView()->getTreeView(), SIGNAL(clicked(TQListViewItem*)), this, SLOT(slotSwitchFocus()));
connect(session->getFileView()->m_toolBarFirst, SIGNAL(clicked(int)), this, SLOT(slotSwitchFocus()));
connect(session->getFileView()->m_toolBarSecond, SIGNAL(clicked(int)), this, SLOT(slotSwitchFocus()));
// Connect some signals
connect(session->getClient()->eventHandler(), SIGNAL(engineEvent(KFTPEngine::Event*)), session, SLOT(slotClientEngineEvent(KFTPEngine::Event*)));
// Assign GUI positions
m_statTabs->addTab(session->m_log, "[" + i18n("Log") + "]");
getTabs(session->m_side)->addTab(session->m_fileView, KFTPGrabberBase::loadSmallIcon("computer"), i18n("Session"));
// Actually add the session
m_sessionList.append(session);
session->m_registred = true;
}
KFTPWidgets::Browser::View *Manager::getActiveView()
{
if (m_active)
return m_active->getFileView();
return 0;
}
Session *Manager::getActiveSession()
{
return m_active;
}
bool Manager::eventFilter(TQObject *object, TQEvent *event)
{
if (event->type() == TQEvent::FocusIn || event->type() == TQEvent::MouseButtonPress) {
switchFocusToObject(object);
}
return false;
}
void Manager::slotSwitchFocus()
{
switchFocusToObject(TQObject::sender());
}
void Manager::switchFocusToObject(const TQObject *object)
{
if (!object)
return;
for (;;) {
if (object->isA("KFTPWidgets::Browser::View"))
break;
if (!(object = object->parent()))
break;
}
if (object) {
// We have the proper object
Session *session = find(static_cast<const KFTPWidgets::Browser::View*>(object));
if (session && session != m_active) {
m_active = session;
// Open the current session's log tab
if (session->isRemote())
m_statTabs->showPage(session->getLog());
}
}
}
void Manager::unregisterSession(Session *session)
{
// Destroy all objects related to the session and remove it
getTabs(session->m_side)->removePage(session->m_fileView);
m_statTabs->removePage(session->m_log);
if (session->getClient()->socket()->isConnected()) {
session->abort();
session->getClient()->disconnect();
}
// Delete objects
session->m_fileView->deleteLater();
session->m_log->deleteLater();
// Actually remove the session
m_sessionList.remove(session);
delete session;
// Emit update
emit update();
}
void Manager::doEmitUpdate()
{
emit update();
}
void Manager::disconnectAllSessions()
{
Session *i;
for (i = m_sessionList.first(); i; i = m_sessionList.next())
i->disconnectAllConnections();
}
Session *Manager::find(KFTPEngine::Thread *client)
{
Session *i;
for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
if (i->getClient() == client)
return i;
}
return 0L;
}
Session *Manager::find(KFTPWidgets::Browser::View *fileView)
{
Session *i;
for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
if (i->m_fileView == fileView)
return i;
}
return 0L;
}
Session *Manager::find(KFTPWidgets::LogView *log)
{
Session *i;
for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
if (i->m_log == log)
return i;
}
return 0L;
}
Session *Manager::find(const KURL &url, bool mustUnlock)
{
if (url.isLocalFile())
return find(true);
Session *i;
for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
KURL tmp = i->getClient()->socket()->getCurrentUrl();
tmp.setPath(url.path());
if (tmp == url && i->isRemote() && i->isConnected() && (!mustUnlock || i->isFreeConnection()))
return i;
}
return 0L;
}
Session *Manager::find(bool local)
{
Session *i;
for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
if (i->m_remote != local)
return i;
}
return 0L;
}
Session *Manager::findLast(const KURL &url, Side side)
{
if (url.isLocalFile())
return find(true);
Session *i;
for (i = m_sessionList.first(); i; i = m_sessionList.next()) {
KURL tmp = i->m_lastUrl;
tmp.setPath(url.path());
if (tmp == url && !i->isRemote() && (i->getSide() || side == IgnoreSide))
return i;
}
return 0L;
}
Session *Manager::spawnLocalSession(Side side, bool forceNew)
{
// Creates a new local session
Session *session = 0L;
if (forceNew || (session = find(true)) == 0L || (session->m_side != side && side != IgnoreSide)) {
side = side == IgnoreSide ? LeftSide : side;
session = new Session(side);
session->m_remote = false;
getTabs(side)->changeTab(session->m_fileView, i18n("Local Session"));
getStatTabs()->changeTab(session->m_log, "[" + i18n("Log") + "]");
}
setActive(session);
return session;
}
Session *Manager::spawnRemoteSession(Side side, const KURL &remoteUrl, KFTPBookmarks::Site *site, bool mustUnlock)
{
// Creates a new remote session and connects it to the correct server
Session *session;
if (remoteUrl.isLocalFile())
return spawnLocalSession(side);
if ((session = find(remoteUrl, mustUnlock)) == 0L || (session->m_side != side && side != IgnoreSide)) {
// Try to find the session that was last connected to this URL
if ((session = findLast(remoteUrl, side)) == 0L) {
// Attempt to reuse a local session if one exists one the right side
session = getActive(RightSide);
if (session->isRemote()) {
side = side == IgnoreSide ? RightSide : side;
session = new Session(side);
}
}
// Try to find the site by url if it is not set
if (!site)
site = KFTPBookmarks::Manager::self()->findSite(remoteUrl);
// Set properties
session->m_remote = true;
session->m_site = site;
m_active = session;
KFTPBookmarks::Manager::self()->setupClient(site, session->getClient());
session->getClient()->connect(remoteUrl);
}
return session;
}
void Manager::setActive(Session *session)
{
// Make a session active on its own side ;)
Session *oldActive = getActive(session->m_side);
oldActive ? oldActive->m_active = false : 0;
session->m_active = true;
switch (session->m_side) {
case LeftSide: m_leftActive = session; break;
case RightSide: m_rightActive = session; break;
case IgnoreSide: tqDebug("Invalid side specified!"); return;
}
// Refresh the GUI
getTabs(session->m_side)->showPage(session->m_fileView);
}
Session *Manager::getActive(Side side)
{
switch (side) {
case LeftSide: return m_leftActive;
case RightSide: return m_rightActive;
case IgnoreSide: tqDebug("Invalid side specified!"); break;
}
return NULL;
}
KFTPTabWidget *Manager::getTabs(Side side)
{
switch (side) {
case LeftSide: return m_leftTabs;
case RightSide: return m_rightTabs;
case IgnoreSide: tqDebug("Invalid side specified!"); break;
}
return NULL;
}
void Manager::slotActiveChanged(TQWidget *page)
{
Session *session = find(static_cast<KFTPWidgets::Browser::View*>(page));
setActive(session);
}
void Manager::slotSessionCloseRequest(TQWidget *page)
{
Session *session = find(static_cast<KFTPWidgets::Browser::View*>(page));
if (getTabs(session->m_side)->count() == 1) {
KMessageBox::error(0L, i18n("At least one session must remain open on each side."));
return;
}
if ((session->m_remote && session->getClient()->socket()->isBusy()) || !session->isFreeConnection()) {
KMessageBox::error(0L, i18n("Please finish all transfers before closing the session."));
return;
} else {
// Remove the session
if (session->getClient()->socket()->isConnected()) {
if (KMessageBox::questionYesNo(0L, i18n("This session is currently connected. Are you sure you wish to disconnect?"), i18n("Close Session")) == KMessageBox::No)
return;
}
unregisterSession(session);
}
}
}
#include "kftpsession.moc"