summaryrefslogtreecommitdiffstats
path: root/kftpgrabber/src/kftpqueue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kftpgrabber/src/kftpqueue.cpp')
-rw-r--r--kftpgrabber/src/kftpqueue.cpp834
1 files changed, 834 insertions, 0 deletions
diff --git a/kftpgrabber/src/kftpqueue.cpp b/kftpgrabber/src/kftpqueue.cpp
new file mode 100644
index 0000000..0186c4e
--- /dev/null
+++ b/kftpgrabber/src/kftpqueue.cpp
@@ -0,0 +1,834 @@
+/*
+ * 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 <math.h>
+
+#include "kftpqueue.h"
+#include "kftpbookmarks.h"
+#include "widgets/systemtray.h"
+#include "kftpqueueprocessor.h"
+#include "kftpsession.h"
+
+#include "misc/config.h"
+#include "misc/filter.h"
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kio/renamedlg.h>
+#include <kdiskfreesp.h>
+#include <kfileitem.h>
+#include <kopenwith.h>
+#include <kstaticdeleter.h>
+#include <kservice.h>
+#include <kuserprofile.h>
+#include <kstandarddirs.h>
+#include <krun.h>
+#include <kmdcodec.h>
+
+#include <qapplication.h>
+#include <qregexp.h>
+#include <qobjectlist.h>
+#include <qfile.h>
+
+using namespace KFTPEngine;
+using namespace KFTPCore::Filter;
+
+namespace KFTPQueue {
+
+OpenedFile::OpenedFile(TransferFile *transfer)
+ : m_source(transfer->getSourceUrl()),
+ m_dest(transfer->getDestUrl()),
+ m_hash(QString::null)
+{
+ // Calculate the file's MD5 hash
+ QFile file(m_dest.path());
+ if (!file.open(IO_ReadOnly)) {
+ return;
+ }
+
+ KMD5 context;
+ if (context.update(file))
+ m_hash = QString(context.hexDigest());
+ file.close();
+}
+
+bool OpenedFile::hasChanged()
+{
+ // Compare the file's MD5 hash with stored value
+ QFile file(m_dest.path());
+ if (!file.open(IO_ReadOnly)) {
+ return false;
+ }
+
+ QString tmp = QString::null;
+ KMD5 context;
+ if (context.update(file))
+ tmp = QString(context.hexDigest());
+ file.close();
+
+ return tmp != m_hash;
+}
+
+UserDialogRequest::UserDialogRequest(TransferFile *transfer, filesize_t srcSize, time_t srcTime,
+ filesize_t dstSize, time_t dstTime)
+ : m_transfer(transfer),
+ m_srcSize(srcSize),
+ m_srcTime(srcTime),
+ m_dstSize(dstSize),
+ m_dstTime(dstTime)
+{
+}
+
+void UserDialogRequest::sendResponse(FileExistsWakeupEvent *event)
+{
+ m_transfer->wakeup(event);
+ delete this;
+}
+
+Manager *Manager::m_self = 0;
+static KStaticDeleter<Manager> staticManagerDeleter;
+
+Manager *Manager::self()
+{
+ if (!m_self) {
+ staticManagerDeleter.setObject(m_self, new Manager());
+ }
+
+ return m_self;
+}
+
+Manager::Manager()
+ : m_topLevel(new QueueObject(this, QueueObject::Toplevel)),
+ m_processingQueue(false),
+ m_feDialogOpen(false),
+ m_defaultFeAction(FE_DISABLE_ACT)
+{
+ m_topLevel->setId(0);
+
+ m_lastQID = 1;
+ m_curDownSpeed = 0;
+ m_curUpSpeed = 0;
+
+ m_emitUpdate = true;
+
+ // Create the queue processor object
+ m_queueProc = new KFTPQueueProcessor(this);
+
+ connect(m_queueProc, SIGNAL(queueComplete()), this, SLOT(slotQueueProcessingComplete()));
+ connect(m_queueProc, SIGNAL(queueAborted()), this, SLOT(slotQueueProcessingAborted()));
+
+ // Create the queue converter object
+ m_converter = new KFTPQueueConverter(this);
+}
+
+Manager::~Manager()
+{
+ if (m_self == this)
+ staticManagerDeleter.setObject(m_self, 0, false);
+}
+
+void Manager::stopAllTransfers()
+{
+ if (isProcessing()) {
+ abort();
+ } else {
+ QueueObject *i;
+ QPtrList<QueueObject> sites = topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ if (i->isRunning()) {
+ i->abort();
+ } else {
+ QueueObject *t;
+ QPtrList<QueueObject> list = i->getChildrenList();
+
+ for (t = list.first(); t; t = list.next()) {
+ if (t->isRunning())
+ t->abort();
+ }
+ }
+ }
+ }
+}
+
+Transfer *Manager::findTransfer(long id)
+{
+ // First try the cache
+ QueueObject *object = m_queueObjectCache[QString::number(id)];
+
+ if (!object) {
+ object = m_topLevel->findChildObject(id);
+ m_queueObjectCache.insert(QString::number(id), object);
+ }
+
+ return static_cast<Transfer*>(object);
+}
+
+Site *Manager::findSite(KURL url, bool noCreate)
+{
+ // Reset path
+ url.setPath("/");
+
+ if (url.isLocalFile())
+ return NULL;
+
+ // Find the appropriate site and if one doesn't exist create a new one
+ QueueObject *i;
+ QPtrList<QueueObject> sites = topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ if (i->getType() == QueueObject::Site) {
+ Site *site = static_cast<Site*>(i);
+
+ if (site->getUrl() == url)
+ return site;
+ }
+ }
+
+ // The site doesn't exist, let's create one
+ if (!noCreate) {
+ Site *site = new Site(topLevelObject(), url);
+ site->setId(m_lastQID++);
+ emit newSite(site);
+
+ return site;
+ }
+
+ return 0;
+}
+
+void Manager::insertTransfer(Transfer *transfer)
+{
+ // Set id
+ transfer->setId(m_lastQID++);
+
+ // Reparent transfer
+ filesize_t size = transfer->getSize();
+ transfer->addSize(-size);
+
+ if (transfer->hasParentObject())
+ transfer->parentObject()->delChildObject(transfer);
+
+ if (transfer->parent())
+ transfer->parent()->removeChild(transfer);
+
+ Site *site = 0;
+
+ switch (transfer->getTransferType()) {
+ case Download: site = findSite(transfer->getSourceUrl()); break;
+ case Upload: site = findSite(transfer->getDestUrl()); break;
+ case FXP: site = findSite(transfer->getSourceUrl()); break;
+ }
+
+ site->insertChild(transfer);
+ site->addChildObject(transfer);
+ transfer->addSize(size);
+
+ emit newTransfer(transfer);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+void Manager::insertTransfer(KURLDrag *drag)
+{
+ // Decode the drag
+ KIO::MetaData p_meta;
+ KURL::List p_urls;
+ KURLDrag::decode(drag, p_urls, p_meta);
+
+ // TODO make support for local drops - eg. from konqueror, where
+ // we get no meta data, so we must get the file info ourselves and
+ // reject remote urls (or show a dialog to ask the user if he
+ // wants to connect to the remote site)
+
+ // Now we should add transfers for all URLs
+ Transfer *lastTransfer = 0L;
+ KURL::List::iterator end(p_urls.end());
+
+ for (KURL::List::iterator i(p_urls.begin()); i != end; ++i) {
+ QString p_data = p_meta[(*i).htmlURL().local8Bit()];
+ QChar type = p_data.at(0);
+ filesize_t size = p_data.section(':', 1, 1).toULongLong();
+ KURL sourceUrl = (*i);
+ KURL destinationUrl = KURL(p_meta["DestURL"]);
+ destinationUrl.addPath(sourceUrl.fileName());
+
+ // Skip where both files are local
+ if (sourceUrl.isLocalFile() && destinationUrl.isLocalFile())
+ continue;
+
+ lastTransfer = spawnTransfer(sourceUrl, destinationUrl, size, type == 'D', true, true, 0L, true);
+ }
+
+ // Execute the transfer if set in configuration
+ if (!KFTPCore::Config::queueOnDND() && lastTransfer)
+ static_cast<KFTPQueue::Site*>(lastTransfer->parentObject())->delayedExecute();
+}
+
+Transfer *Manager::spawnTransfer(KURL sourceUrl, KURL destinationUrl, filesize_t size, bool dir, bool ignoreSkip,
+ bool insertToQueue, QObject *parent, bool noScan)
+{
+ const ActionChain *actionChain = Filters::self()->process(sourceUrl, size, dir);
+
+ if (!ignoreSkip && (actionChain && actionChain->getAction(Action::Skip)))
+ return 0;
+
+ // Determine transfer type
+ TransferType type;
+
+ if (sourceUrl.isLocalFile())
+ type = Upload;
+ else if (destinationUrl.isLocalFile())
+ type = Download;
+ else
+ type = FXP;
+
+ // Should we lowercase the destination path ?
+ if (actionChain && actionChain->getAction(Action::Lowercase))
+ destinationUrl.setPath(destinationUrl.directory() + "/" + destinationUrl.fileName().lower());
+
+ // Reset a possible preconfigured default action
+ setDefaultFileExistsAction();
+
+ if (!parent)
+ parent = this;
+
+ Transfer *transfer = 0L;
+
+ if (dir)
+ transfer = new TransferDir(parent);
+ else {
+ transfer = new TransferFile(parent);
+ transfer->addSize(size);
+ }
+
+ transfer->setSourceUrl(sourceUrl);
+ transfer->setDestUrl(destinationUrl);
+ transfer->setTransferType(type);
+
+ if (insertToQueue) {
+ insertTransfer(transfer);
+ } else {
+ transfer->setId(m_lastQID++);
+ emit newTransfer(transfer);
+ }
+
+ if (dir && !noScan) {
+ // This is a directory, we should scan the directory and add all files/dirs found
+ // as parent of current object
+ KFTPSession::Session *session = KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, sourceUrl, 0, true);
+ session->scanDirectory(transfer);
+ }
+
+ return transfer;
+}
+
+void Manager::removeTransfer(Transfer *transfer, bool abortSession)
+{
+ if (!transfer)
+ return;
+
+ transfer->abort();
+ long id = transfer->getId();
+ long sid = transfer->parentObject()->getId();
+
+ // Remove transfer from cache
+ m_queueObjectCache.remove(QString::number(id));
+
+ // Should the site be removed as well ?
+ QueueObject *site = 0;
+ if (transfer->parentObject()->getType() == QueueObject::Site && transfer->parentObject()->getChildrenList().count() == 1)
+ site = transfer->parentObject();
+
+ // Signal destruction & delete transfer
+ transfer->faceDestruction(abortSession);
+ delete transfer;
+
+ if (site) {
+ delete site;
+ emit siteRemoved(sid);
+ }
+
+ emit transferRemoved(id);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+void Manager::revalidateTransfer(Transfer *transfer)
+{
+ QueueObject *i = transfer;
+
+ while (i) {
+ if (i->parentObject() == topLevelObject())
+ break;
+
+ i = i->parentObject();
+ }
+
+ // We have the site
+ Site *curSite = static_cast<Site*>(i);
+ Site *site = 0;
+
+ switch (transfer->getTransferType()) {
+ case Download: site = findSite(transfer->getSourceUrl()); break;
+ case Upload: site = findSite(transfer->getDestUrl()); break;
+ case FXP: site = findSite(transfer->getSourceUrl()); break;
+ }
+
+ // If the sites don't match, reparent transfer
+ if (site != curSite) {
+ transfer->parentObject()->delChildObject(transfer);
+ transfer->parent()->removeChild(transfer);
+
+ site->insertChild(transfer);
+ site->addChildObject(transfer);
+
+ emit transferRemoved(transfer->getId());
+ emit newTransfer(transfer);
+
+ if (curSite->getChildrenList().count() == 0) {
+ emit siteRemoved(curSite->getId());
+ curSite->deleteLater();
+ }
+ }
+}
+
+void Manager::removeFailedTransfer(FailedTransfer *transfer)
+{
+ // Remove the transfer and signal removal
+ m_failedTransfers.remove(transfer);
+ emit failedTransferRemoved(transfer->getTransfer()->getId());
+
+ delete transfer;
+}
+
+void Manager::clearFailedTransferList()
+{
+ // Clear the failed transfers list
+ FailedTransfer *transfer;
+ QPtrListIterator<KFTPQueue::FailedTransfer> i(m_failedTransfers);
+
+ while ((transfer = i.current()) != 0) {
+ ++i;
+ removeFailedTransfer(transfer);
+ }
+}
+
+void Manager::moveTransferUp(QueueObject *object)
+{
+ object->parentObject()->moveChildUp(object);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+void Manager::moveTransferDown(QueueObject *object)
+{
+ object->parentObject()->moveChildDown(object);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+void Manager::moveTransferTop(QueueObject *object)
+{
+ object->parentObject()->moveChildTop(object);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+void Manager::moveTransferBottom(QueueObject *object)
+{
+ object->parentObject()->moveChildBottom(object);
+
+ if (m_emitUpdate)
+ emit queueUpdate();
+}
+
+bool Manager::canBeMovedUp(QueueObject *object)
+{
+ return object ? object->parentObject()->canMoveChildUp(object) : false;
+}
+
+bool Manager::canBeMovedDown(QueueObject *object)
+{
+ return object ? object->parentObject()->canMoveChildDown(object) : false;
+}
+
+void Manager::doEmitUpdate()
+{
+ m_curDownSpeed = 0;
+ m_curUpSpeed = 0;
+
+ topLevelObject()->removeMarkedTransfers();
+
+ // Get download/upload speeds
+ QueueObject *i;
+ QPtrList<QueueObject> sites = topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ QueueObject *t;
+ QPtrList<QueueObject> list = i->getChildrenList();
+
+ for (t = list.first(); t; t = list.next()) {
+ KFTPQueue::Transfer *tmp = static_cast<Transfer*>(t);
+
+ switch (tmp->getTransferType()) {
+ case Download: m_curDownSpeed += tmp->getSpeed(); break;
+ case Upload: m_curUpSpeed += tmp->getSpeed(); break;
+ case FXP: {
+ m_curDownSpeed += tmp->getSpeed();
+ m_curUpSpeed += tmp->getSpeed();
+ break;
+ }
+ }
+ }
+ }
+
+ // Emit global update to all GUI objects
+ emit queueUpdate();
+}
+
+void Manager::start()
+{
+ if (m_processingQueue)
+ return;
+
+ m_processingQueue = true;
+
+ // Now, go trough all queued files and execute them - try to do as little server connects
+ // as possible
+ m_queueProc->startProcessing();
+}
+
+void Manager::abort()
+{
+ m_processingQueue = false;
+
+ // Stop further queue processing
+ m_queueProc->stopProcessing();
+
+ emit queueUpdate();
+}
+
+void Manager::slotQueueProcessingComplete()
+{
+ m_processingQueue = false;
+
+ // Queue processing is now complete
+ if (KFTPCore::Config::showBalloons())
+ KFTPWidgets::SystemTray::self()->showBalloon(i18n("All queued transfers have been completed."));
+
+ emit queueUpdate();
+}
+
+void Manager::slotQueueProcessingAborted()
+{
+ m_processingQueue = false;
+}
+
+void Manager::clearQueue()
+{
+ QueueObject *i;
+ QPtrList<QueueObject> sites = topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ QueueObject *t;
+ QPtrList<QueueObject> list = i->getChildrenList();
+
+ for (t = list.first(); t; t = list.next())
+ removeTransfer(static_cast<Transfer*>(t));
+ }
+}
+
+int Manager::getTransferPercentage()
+{
+ return 0;
+}
+
+int Manager::getNumRunning(bool onlyDirs)
+{
+ int running = 0;
+
+ QueueObject *i;
+ QPtrList<QueueObject> sites = topLevelObject()->getChildrenList();
+
+ for (i = sites.first(); i; i = sites.next()) {
+ QueueObject *t;
+ QPtrList<QueueObject> list = i->getChildrenList();
+
+ for (t = list.first(); t; t = list.next()) {
+ if (t->isRunning() && (!onlyDirs || t->isDir()))
+ running++;
+ }
+
+ if (i->isRunning())
+ running++;
+ }
+
+ return running;
+}
+
+int Manager::getNumRunning(const KURL &remoteUrl)
+{
+ int running = 0;
+ Site *site = findSite(remoteUrl, true);
+
+ if (site) {
+ QueueObject *i;
+ QPtrList<QueueObject> transfers = site->getChildrenList();
+
+ for (i = transfers.first(); i; i = transfers.next()) {
+ if (i->isRunning())
+ running++;
+ }
+ }
+
+ return running;
+}
+
+KFTPEngine::FileExistsWakeupEvent *Manager::fileExistsAction(TransferFile *transfer,
+ QValueList<KFTPEngine::DirectoryEntry> stat)
+{
+ FileExistsWakeupEvent *event = new FileExistsWakeupEvent();
+ FileExistsActions *fa = NULL;
+ FEAction action;
+
+ filesize_t srcSize = 0;
+ time_t srcTime = 0;
+
+ filesize_t dstSize = 0;
+ time_t dstTime = 0;
+
+ // Check if there is a default action set
+ action = getDefaultFileExistsAction();
+
+ if (action == FE_DISABLE_ACT) {
+ switch (transfer->getTransferType()) {
+ case KFTPQueue::Download: {
+ KFileItem info(KFileItem::Unknown, KFileItem::Unknown, transfer->getDestUrl());
+ dstSize = info.size();
+ dstTime = info.time(KIO::UDS_MODIFICATION_TIME);
+
+ srcSize = stat[0].size();
+ srcTime = stat[0].time();
+
+ fa = KFTPCore::Config::self()->dActions();
+ break;
+ }
+ case KFTPQueue::Upload: {
+ KFileItem info(KFileItem::Unknown, KFileItem::Unknown, transfer->getSourceUrl());
+ srcSize = info.size();
+ srcTime = info.time(KIO::UDS_MODIFICATION_TIME);
+
+ dstSize = stat[0].size();
+ dstTime = stat[0].time();
+
+ fa = KFTPCore::Config::self()->uActions();
+ break;
+ }
+ case KFTPQueue::FXP: {
+ srcSize = stat[0].size();
+ srcTime = stat[0].time();
+
+ dstSize = stat[1].size();
+ dstTime = stat[1].time();
+
+ fa = KFTPCore::Config::self()->fActions();
+ break;
+ }
+ }
+
+ // Now that we have all data, get the action and do it
+ action = fa->getActionForSituation(srcSize, srcTime, dstSize, dstTime);
+ }
+
+ switch (action) {
+ default:
+ case FE_SKIP_ACT: event->action = FileExistsWakeupEvent::Skip; break;
+ case FE_OVERWRITE_ACT: event->action = FileExistsWakeupEvent::Overwrite; break;
+ case FE_RESUME_ACT: event->action = FileExistsWakeupEvent::Resume; break;
+ case FE_RENAME_ACT:
+ case FE_USER_ACT: {
+ appendUserDialogRequest(new UserDialogRequest(transfer, srcSize, srcTime, dstSize, dstTime));
+
+ // Event shall be deferred
+ delete event;
+ event = 0;
+ }
+ }
+
+ return event;
+}
+
+void Manager::appendUserDialogRequest(UserDialogRequest *request)
+{
+ m_userDialogRequests.append(request);
+
+ if (m_userDialogRequests.count() == 1) {
+ processUserDialogRequest();
+ }
+}
+
+void Manager::processUserDialogRequest()
+{
+ UserDialogRequest *request = m_userDialogRequests.getFirst();
+ if (!request)
+ return;
+
+ FEAction action = getDefaultFileExistsAction();
+ FileExistsWakeupEvent *event = new FileExistsWakeupEvent();
+
+ if (action == FE_DISABLE_ACT || action == FE_USER_ACT) {
+ // A dialog really needs to be displayed
+ TransferFile *transfer = request->getTransfer();
+
+ QString newDestPath;
+ KIO::RenameDlg_Result r = KIO::open_RenameDlg(
+ i18n("File Exists"),
+ transfer->getSourceUrl().prettyURL(),
+ transfer->getDestUrl().prettyURL(),
+ (KIO::RenameDlg_Mode) (KIO::M_OVERWRITE | KIO::M_RESUME | KIO::M_SKIP | KIO::M_MULTI),
+ newDestPath,
+ request->sourceSize(),
+ request->destinationSize(),
+ request->sourceTime(),
+ request->destinationTime()
+ );
+
+ switch (r) {
+ case KIO::R_RENAME: {
+ KURL url = transfer->getDestUrl();
+ url.setPath(newDestPath);
+ transfer->setDestUrl(url);
+
+ event->action = FileExistsWakeupEvent::Rename;
+ event->newFileName = newDestPath;
+ break;
+ }
+ case KIO::R_CANCEL: {
+ // Abort queue processing
+ abort();
+ transfer->abort();
+
+ // An event is not required, since we will not be recalling the process
+ delete event;
+ event = 0;
+ break;
+ }
+ case KIO::R_AUTO_SKIP: setDefaultFileExistsAction(FE_SKIP_ACT);
+ case KIO::R_SKIP: event->action = FileExistsWakeupEvent::Skip; break;
+ case KIO::R_RESUME_ALL: setDefaultFileExistsAction(FE_RESUME_ACT);
+ case KIO::R_RESUME: event->action = FileExistsWakeupEvent::Resume; break;
+ case KIO::R_OVERWRITE_ALL: setDefaultFileExistsAction(FE_OVERWRITE_ACT);
+ default: event->action = FileExistsWakeupEvent::Overwrite; break;
+ }
+ } else {
+ switch (action) {
+ default:
+ case FE_SKIP_ACT: event->action = FileExistsWakeupEvent::Skip; break;
+ case FE_OVERWRITE_ACT: event->action = FileExistsWakeupEvent::Overwrite; break;
+ case FE_RESUME_ACT: event->action = FileExistsWakeupEvent::Resume; break;
+ }
+ }
+
+ // Send a response to this request
+ request->sendResponse(event);
+
+ m_userDialogRequests.removeFirst();
+
+ if (!m_userDialogRequests.isEmpty())
+ processUserDialogRequest();
+}
+
+void Manager::openAfterTransfer(TransferFile *transfer)
+{
+ QString mimeType = KMimeType::findByURL(transfer->getDestUrl(), 0, true, true)->name();
+ KService::Ptr offer = KServiceTypeProfile::preferredService(mimeType, "Application");
+
+ if (!offer) {
+ KOpenWithDlg dialog(KURL::List(transfer->getDestUrl()));
+
+ if (dialog.exec() == QDialog::Accepted) {
+ offer = dialog.service();
+
+ if (!offer)
+ offer = new KService("", dialog.text(), "");
+ } else {
+ return;
+ }
+ }
+
+ QStringList params = KRun::processDesktopExec(*offer, KURL::List(transfer->getDestUrl()), false);
+ KProcess *p = new KProcess(this);
+ *p << params;
+
+ connect(p, SIGNAL(processExited(KProcess*)), this, SLOT(slotEditProcessTerminated(KProcess*)));
+
+ p->start();
+
+ // Save the process
+ m_editProcessList.insert(p->pid(), OpenedFile(transfer));
+}
+
+void Manager::slotEditProcessTerminated(KProcess *p)
+{
+ // A process has terminated, we should reupload
+ OpenedFile file = m_editProcessList[p->pid()];
+
+ // Only upload a file if it has been changed
+ if (file.hasChanged()) {
+ TransferFile *transfer = new TransferFile(KFTPQueue::Manager::self());
+ transfer->setSourceUrl(file.destination());
+ transfer->setDestUrl(file.source());
+ transfer->setTransferType(KFTPQueue::Upload);
+ transfer->addSize(KFileItem(KFileItem::Unknown, KFileItem::Unknown, file.destination()).size());
+ insertTransfer(transfer);
+
+ // Execute the transfer
+ transfer->delayedExecute();
+ }
+
+ // Cleanup
+ m_editProcessList.remove(p->pid());
+ p->deleteLater();
+}
+
+}
+
+#include "kftpqueue.moc"