diff --git a/src/dcc/transfer.cpp b/src/dcc/transfer.cpp index 937689ce..00ec8c4d 100644 --- a/src/dcc/transfer.cpp +++ b/src/dcc/transfer.cpp @@ -1,442 +1,423 @@ /* 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. */ /* Copyright (C) 2002-2004 Dario Abatianni Copyright (C) 2004-2007 Shintaro Matsuoka Copyright (C) 2004,2005 John Tapsell Copyright (C) 2009 Bernd Buschinski */ #include "transfer.h" #include "application.h" #include "connectionmanager.h" #include "notificationhandler.h" #include "preferences.h" #include #include -#ifdef HAVE_BYTESWAP_H -# include -#elif defined(HAVE_SYS_ENDIAN_H) -# include -# define bswap_32(X) bswap32(X) -#else -# if (defined(_MSC_VER) && (_MSC_VER > 1298)) -# include -# define bswap_32(X) _byteswap_ulong(X) -# else -# define bswap_32(X) ( (((X)&0x000000FF)<<24) | (((X)&0xFF000000) >> 24) | (((X)&0x0000FF00) << 8) | (((X) &0x00FF0000) >> 8)) -# endif -#endif - #include namespace Konversation { namespace DCC { Transfer::Transfer(Type dccType, QObject *parent) : QObject(parent) { qDebug(); m_type = dccType; m_status = Configuring; m_ownPort = 0; m_fileSize = 0; m_resumed = false; m_reverse = false; m_connectionId = -1; // Not configured m_timeLeft = Transfer::NotInTransfer; m_transferringPosition = 0; m_transferStartPosition = 0; m_averageSpeed = 0.0; m_currentSpeed = 0.0; m_bufferSize = Preferences::self()->dccBufferSize(); m_buffer = new char[m_bufferSize]; connect(&m_loggerTimer, &QTimer::timeout, this, &Transfer::logTransfer); m_timeOffer = QDateTime::currentDateTime(); } Transfer::~Transfer() { qDebug(); } void Transfer::setConnectionId(int id) { if (getStatus() == Configuring || getStatus() == Queued) { m_connectionId = id; } } void Transfer::setPartnerNick(const QString &nick) { if (getStatus() == Configuring || getStatus() == Queued) { m_partnerNick = nick; } } bool Transfer::queue() { qDebug(); if (getStatus() != Configuring) { return false; } if (m_fileName.isEmpty()) { return false; } if (m_connectionId == -1 || m_partnerNick.isEmpty()) { return false; } setStatus(Queued); return true; } void Transfer::runFile() { if (getType() == Transfer::Send || getStatus() == Transfer::Done) { new KRun(getFileURL(), 0); } } void Transfer::startTransferLogger() { m_timeTransferStarted = QDateTime::currentDateTime(); m_loggerBaseTime.start(); m_loggerTimer.start(100); } void Transfer::finishTransferLogger() { if (m_timeTransferFinished.isNull()) { m_timeTransferFinished = QDateTime::currentDateTime(); } m_loggerTimer.stop(); updateTransferMeters(); } // called by m_loggerTimer void Transfer::logTransfer() { m_transferLogTime.append(m_loggerBaseTime.elapsed()); m_transferLogPosition.append(m_transferringPosition); updateTransferMeters(); } void Transfer::cleanUp() { qDebug(); delete[] m_buffer; m_buffer = 0; m_loggerTimer.stop(); } void Transfer::removedFromView() { emit removed(this); } // just for convenience void Transfer::failed(const QString &errorMessage) { cleanUp(); Application *konv_app = Application::instance(); Server *server = konv_app->getConnectionManager()->getServerByConnectionId(m_connectionId); if (server) { qDebug() << "notification:" << errorMessage; konv_app->notificationHandler()->dccError(server->getStatusView(), errorMessage); } setStatus(Failed, errorMessage); emit done(this); } void Transfer::setStatus(Status status, const QString &statusDetail) { bool changed = (status != m_status); Status oldStatus = m_status; m_status = status; m_statusDetail = statusDetail; if (changed) { emit statusChanged(this, m_status, oldStatus); } if (m_status == Done) { Application *konv_app = Application::instance(); Server *server = konv_app->getConnectionManager()->getServerByConnectionId(m_connectionId); if (server) { qDebug() << "notification:" << m_fileName; konv_app->notificationHandler()->dccTransferDone(server->getStatusView(), m_fileName, this); } } } void Transfer::updateTransferMeters() { const int timeToCalc = 5; if (getStatus() == Transferring) { // update CurrentSpeed // remove too old data QList::iterator itTime = m_transferLogTime.begin(); QList::iterator itPos = m_transferLogPosition.begin(); while (itTime != m_transferLogTime.end() && (m_transferLogTime.last() - (*itTime) > timeToCalc * 1000)) { itTime = m_transferLogTime.erase(itTime); itPos = m_transferLogPosition.erase(itPos); } // shift the base of the time (m_transferLoggerBaseTime) // reason: QTime can't handle a time longer than 24 hours int shiftOffset = m_loggerBaseTime.restart(); itTime = m_transferLogTime.begin(); for (; itTime != m_transferLogTime.end(); ++itTime) { (*itTime) = (*itTime) - shiftOffset; } // The logTimer is 100ms, as 200ms is below 1sec we get "undefined" speed if (m_transferLogTime.count() >= 2 && m_timeTransferStarted.secsTo(QDateTime::currentDateTime()) > 0) { // FIXME: precision of average speed is too bad m_averageSpeed = (double)(m_transferringPosition - m_transferStartPosition) / (double)m_timeTransferStarted.secsTo(QDateTime::currentDateTime()); m_currentSpeed = (double)(m_transferLogPosition.last() - m_transferLogPosition.front()) / (double)(m_transferLogTime.last() - m_transferLogTime.front()) * 1000; } else // avoid zero devision { m_averageSpeed = Transfer::Calculating; m_currentSpeed = Transfer::Calculating; } // update the remaining time if (m_transferringPosition == (KIO::fileoffset_t)m_fileSize) { m_timeLeft = 0; } else if (m_currentSpeed <= 0) { m_timeLeft = Transfer::InfiniteValue; } else { m_timeLeft = (int)((double)(m_fileSize - m_transferringPosition) / m_currentSpeed); } } else if (m_status >= Done) { if (m_timeTransferStarted.secsTo(m_timeTransferFinished) > 1) { m_averageSpeed = (double)(m_transferringPosition - m_transferStartPosition) / (double)m_timeTransferStarted.secsTo(m_timeTransferFinished); } else { m_averageSpeed = Transfer::InfiniteValue; } m_currentSpeed = 0; if (m_status == Done) { m_timeLeft = 0; } else { m_timeLeft = Transfer::NotInTransfer; } } else { m_averageSpeed = 0; m_currentSpeed = 0; m_timeLeft = Transfer::NotInTransfer; } } QString Transfer::sanitizeFileName(const QString &fileName) { QString fileNameTmp = QFileInfo(fileName).fileName(); if (fileNameTmp.startsWith('.')) { fileNameTmp.replace(0, 1, '_'); // Don't create hidden files } if (fileNameTmp.isEmpty()) { fileNameTmp = "unnamed"; } return fileNameTmp; } - quint32 Transfer::intel(quint32 value) - { - return bswap_32(value); - } - Transfer::Type Transfer::getType() const { return m_type; } Transfer::Status Transfer::getStatus() const { return m_status; } const QString &Transfer::getStatusDetail() const { return m_statusDetail; } QDateTime Transfer::getTimeOffer() const { return m_timeOffer; } int Transfer::getConnectionId() const { return m_connectionId; } QString Transfer::getOwnIp() const { return m_ownIp; } quint16 Transfer::getOwnPort() const { return m_ownPort; } QString Transfer::getPartnerNick() const { return m_partnerNick; } QString Transfer::getPartnerIp() const { return m_partnerIp; } quint16 Transfer::getPartnerPort() const { return m_partnerPort; } QString Transfer::getFileName() const { return m_fileName; } KIO::filesize_t Transfer::getFileSize() const { return m_fileSize; } KIO::fileoffset_t Transfer::getTransferringPosition() const { return m_transferringPosition; } KIO::fileoffset_t Transfer::getTransferStartPosition() const { return m_transferStartPosition; } QUrl Transfer::getFileURL() const { return m_fileURL; } bool Transfer::isResumed() const { return m_resumed; } bool Transfer::isReverse() const { return m_reverse; } QString Transfer::getReverseToken() const { return m_reverseToken; } transferspeed_t Transfer::getAverageSpeed() const { return m_averageSpeed; } transferspeed_t Transfer::getCurrentSpeed() const { return m_currentSpeed; } int Transfer::getTimeLeft() const { return m_timeLeft; } int Transfer::getProgress() const { if (getFileSize() == 0) { return 0; } else { return (int)(((double)getTransferringPosition() / (double)getFileSize()) * 100.0); } } QDateTime Transfer::getTimeTransferStarted() const { return m_timeTransferStarted; } QDateTime Transfer::getTimeTransferFinished() const { return m_timeTransferFinished; } QString Transfer::transferFileName(const QString & fileName) { if (fileName.contains(' ') && !(fileName.startsWith('\"') && fileName.endsWith('\"'))) { return '\"'+fileName+'\"'; } return fileName; } } } diff --git a/src/dcc/transfer.h b/src/dcc/transfer.h index bd658316..6edeac2d 100644 --- a/src/dcc/transfer.h +++ b/src/dcc/transfer.h @@ -1,198 +1,197 @@ /* This class represents a DCC transfer. */ /* 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. */ /* Copyright (C) 2002-2004 Dario Abatianni Copyright (C) 2004-2007 Shintaro Matsuoka Copyright (C) 2004,2005 John Tapsell Copyright (C) 2009 Bernd Buschinski */ #ifndef TRANSFER_H #define TRANSFER_H #include #include #include #include #include namespace Konversation { namespace DCC { typedef qreal transferspeed_t; class Transfer : public QObject { Q_OBJECT public: enum Type { Receive = 1, Send = 1 << 1 }; enum Status { Configuring = 0, // Not queud yet (this means that user can't see the item at this time) Queued, // Newly added DCC, waiting user's response Preparing, // Opening KIO to write received data WaitingRemote, // Waiting for remote host's response Connecting, // RECV: trying to connect to the server Transferring, Done, Failed, Aborted }; enum UnavailableStatus { Calculating = -1, NotInTransfer = -2, InfiniteValue = -3 }; Transfer(Type dccType, QObject *parent); virtual ~Transfer(); Type getType() const; Status getStatus() const; const QString & getStatusDetail() const; QDateTime getTimeOffer() const; int getConnectionId() const; QString getOwnIp() const; quint16 getOwnPort() const; QString getPartnerNick() const; QString getPartnerIp() const; quint16 getPartnerPort() const; QString getFileName() const; KIO::filesize_t getFileSize() const; KIO::fileoffset_t getTransferringPosition() const; KIO::fileoffset_t getTransferStartPosition() const; QUrl getFileURL() const; bool isResumed() const; bool isReverse() const; QString getReverseToken() const; transferspeed_t getAverageSpeed() const; transferspeed_t getCurrentSpeed() const; int getTimeLeft() const; int getProgress() const; QDateTime getTimeTransferStarted() const; QDateTime getTimeTransferFinished() const; // common settings for DccTransferRecv / DccTransferSend // REQUIRED void setConnectionId(int connectionId); // REQUIRED void setPartnerNick(const QString &nick); void removedFromView(); Q_SIGNALS: void transferStarted(Konversation::DCC::Transfer *item); //done is when the transfer is done, it will not get deleted after emiting this signal void done(Konversation::DCC::Transfer *item); void statusChanged(Konversation::DCC::Transfer *item, int newStatus, int oldStatus); //removed is when the transfer is removed from all visible views and ready to get deleted void removed(Konversation::DCC::Transfer *item); public Q_SLOTS: virtual bool queue(); virtual void start() {} virtual void abort() {} void runFile(); protected: virtual void cleanUp(); void failed(const QString &errorMessage = QString()); /** * setStatus behavior changed: * Now make sure to run functions that change transfer information before setStatus. * For example cleanUp(); * * If you call setStatus(..) and change the "Started at:"-time afterwards, * the transferpanel won't notice it */ void setStatus(Status status, const QString &statusDetail = QString()); void startTransferLogger(); void finishTransferLogger(); static QString transferFileName(const QString &fileName); static QString sanitizeFileName(const QString &fileName); - static quint32 intel(quint32 value); protected Q_SLOTS: void logTransfer(); protected: // transfer information Type m_type; Status m_status; QString m_statusDetail; bool m_resumed; bool m_reverse; QString m_reverseToken; KIO::fileoffset_t m_transferringPosition; KIO::fileoffset_t m_transferStartPosition; // we'll communicate with the partner via this server int m_connectionId; QString m_partnerNick; QString m_partnerIp; // null when unknown quint16 m_partnerPort; QString m_ownIp; quint16 m_ownPort; unsigned long m_bufferSize; char *m_buffer; /** * The filename. Clean filename without any "../" or extra " */ QString m_fileName; /** The file size of the complete file sending/recieving. */ KIO::filesize_t m_fileSize; /** * If we are sending a file, this is the url of the file we are sending. * If we are recieving a file, this is the url of the file we are saving * to in the end (Temporararily it will be filename+".part" ). */ QUrl m_fileURL; private: void updateTransferMeters(); private: QDateTime m_timeOffer; QDateTime m_timeTransferStarted; //QDateTime m_timeLastActive; QDateTime m_timeTransferFinished; QTimer m_loggerTimer; QTime m_loggerBaseTime; // for calculating CPS QList m_transferLogTime; QList m_transferLogPosition; transferspeed_t m_averageSpeed; transferspeed_t m_currentSpeed; int m_timeLeft; }; } } #endif // TRANSFER_H diff --git a/src/dcc/transferrecv.cpp b/src/dcc/transferrecv.cpp index 4244dd31..1efcfe5e 100644 --- a/src/dcc/transferrecv.cpp +++ b/src/dcc/transferrecv.cpp @@ -1,958 +1,958 @@ /* receive a file on DCC protocol begin: Mit Aug 7 2002 copyright: (C) 2002 by Dario Abatianni email: eisfuchs@tigress.com */ /* Copyright (C) 2004-2007 Shintaro Matsuoka Copyright (C) 2004,2005 John Tapsell Copyright (C) 2009 Michael Kreitzer Copyright (C) 2009 Bernd Buschinski */ /* 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. */ #include "transferrecv.h" #include "dcccommon.h" #include "transfermanager.h" #include "application.h" #include "connectionmanager.h" #include "server.h" #include "upnprouter.h" #include #include #include #include #include #include #include /* *flow chart* TransferRecv() start() : called from TransferPanel when user pushes the accept button | \ | requestResume() : called when user chooses to resume in ResumeDialog. it emits the signal ResumeRequest() | | startResume() : called by "Server" | | connectToSender() connectionSuccess() : called by recvSocket */ namespace Konversation { namespace DCC { TransferRecv::TransferRecv(QObject *parent) : Transfer(Transfer::Receive, parent) { qDebug(); m_serverSocket = 0; m_recvSocket = 0; m_writeCacheHandler = 0; m_connectionTimer = new QTimer(this); m_connectionTimer->setSingleShot(true); connect(m_connectionTimer, &QTimer::timeout, this, &TransferRecv::connectionTimeout); //timer hasn't started yet. qtimer will be deleted automatically when 'this' object is deleted } TransferRecv::~TransferRecv() { qDebug(); cleanUp(); } void TransferRecv::cleanUp() { qDebug(); stopConnectionTimer(); disconnect(m_connectionTimer, 0, 0, 0); finishTransferLogger(); if (m_serverSocket) { m_serverSocket->close(); m_serverSocket = 0; if (m_reverse && Preferences::self()->dccUPnP()) { UPnP::UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router) { router->undoForward(m_ownPort, QAbstractSocket::TcpSocket); } } } if (m_recvSocket) { disconnect(m_recvSocket, 0, 0, 0); m_recvSocket->close(); m_recvSocket = 0; // the instance will be deleted automatically by its parent } if (m_writeCacheHandler) { m_writeCacheHandler->closeNow(); m_writeCacheHandler->deleteLater(); m_writeCacheHandler = 0; } Transfer::cleanUp(); } void TransferRecv::setPartnerIp(const QString &ip) { if (getStatus() == Configuring) { m_partnerIp = ip; } } void TransferRecv::setPartnerPort(quint16 port) { if (getStatus() == Configuring) { m_partnerPort = port; } } void TransferRecv::setFileSize(quint64 fileSize) { if (getStatus() == Configuring) { m_fileSize = fileSize; } } void TransferRecv::setFileName(const QString &fileName) { if (getStatus() == Configuring) { m_fileName = fileName; m_saveFileName = m_fileName; } } void TransferRecv::setFileURL(const QUrl &url) { if (getStatus() == Preparing || getStatus() == Configuring || getStatus() == Queued) { m_fileURL = url; m_saveFileName = url.fileName(); } } void TransferRecv::setReverse(bool reverse, const QString &reverseToken) { if (getStatus() == Configuring) { m_reverse = reverse; if (reverse) { m_partnerPort = 0; m_reverseToken = reverseToken; } } } bool TransferRecv::queue() { qDebug(); if (getStatus() != Configuring) { return false; } if (m_partnerIp.isEmpty()) { return false; } if (m_ownIp.isEmpty()) { m_ownIp = DccCommon::getOwnIp(Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId)); } if (!KAuthorized::authorizeKAction("allow_downloading")) { //note we have this after the initialisations so that item looks okay //Do not have the rights to send the file. Shouldn't have gotten this far anyway failed(i18n("The admin has restricted the right to receive files")); return false; } // check if the sender IP is valid if (m_partnerIp == "0.0.0.0") { failed(i18n("Invalid sender address (%1)", m_partnerIp)); return false; } // TODO: should we support it? if (m_fileSize == 0) { failed(i18n("Unsupported negotiation (filesize=0)")); return false; } if (m_fileName.isEmpty()) { m_fileName = "unnamed_file_" + QDateTime::currentDateTime().toString(Qt::ISODate).remove(':'); m_saveFileName = m_fileName; } if (m_fileURL.isEmpty()) { // determine default incoming file URL // set default folder if (!Preferences::self()->dccPath().isEmpty()) { m_fileURL = Preferences::self()->dccPath(); } else { m_fileURL.setPath(KUser(KUser::UseRealUserID).homeDir()); // default folder is *not* specified } //buschinski TODO CHECK ME // add a slash if there is none //m_fileURL.adjustPath(KUrl::AddTrailingSlash); // Append folder with partner's name if wanted if (Preferences::self()->dccCreateFolder()) { m_fileURL = m_fileURL.adjusted(QUrl::StripTrailingSlash); m_fileURL.setPath(m_fileURL.path() + QDir::separator() + m_partnerNick); } // Just incase anyone tries to do anything nasty QString fileNameSanitized = sanitizeFileName(m_saveFileName); // Append partner's name to file name if wanted if (Preferences::self()->dccAddPartner()) { m_fileURL = m_fileURL.adjusted(QUrl::StripTrailingSlash); m_fileURL.setPath(m_fileURL.path() + QDir::separator() + m_partnerNick + '.' + fileNameSanitized); } else { m_fileURL = m_fileURL.adjusted(QUrl::StripTrailingSlash); m_fileURL.setPath(m_fileURL.path() + QDir::separator() + fileNameSanitized); } } return Transfer::queue(); } void TransferRecv::abort() // public slot { qDebug(); if (getStatus() == Transfer::Queued) { Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (server) { server->dccRejectSend(m_partnerNick, transferFileName(m_fileName)); } } if(m_writeCacheHandler) { m_writeCacheHandler->write(true); // flush } cleanUp(); setStatus(Aborted); emit done(this); } void TransferRecv::start() // public slot { qDebug() << "[BEGIN]"; if (getStatus() != Queued) { return; } setStatus(Preparing); prepareLocalKio(false, false); qDebug() << "[END]"; } void TransferRecv::prepareLocalKio(bool overwrite, bool resume, KIO::fileoffset_t startPosition) { qDebug() << "URL: " << m_fileURL << endl << "Overwrite: " << overwrite << endl << "Resume: " << resume << " (Position: " << startPosition << ")"; m_resumed = resume; m_transferringPosition = startPosition; if (!createDirs(KIO::upUrl(m_fileURL))) { askAndPrepareLocalKio(i18n("Cannot create the folder or destination is not writable.
" "Folder: %1
", KIO::upUrl(m_fileURL).toString()), ResumeDialog::RA_Rename | ResumeDialog::RA_Cancel | ResumeDialog::RA_OverwriteDefaultPath, ResumeDialog::RA_Rename); return; } if (Application::instance()->getDccTransferManager()->isLocalFileInWritingProcess(m_fileURL)) { askAndPrepareLocalKio(i18n("The file is used by another transfer.
" "%1
", m_fileURL.toString()), ResumeDialog::RA_Rename | ResumeDialog::RA_Cancel, ResumeDialog::RA_Rename); return; } KIO::JobFlags flags; if(overwrite) { flags |= KIO::Overwrite; } if(m_resumed) { flags |= KIO::Resume; } //for now, maybe later flags |= KIO::HideProgressInfo; KIO::TransferJob *transferJob = KIO::put(m_fileURL, -1, flags); if (!transferJob) { qDebug() << "KIO::put() returned NULL. what happened?"; failed(i18n("Could not create a KIO instance")); return; } transferJob->setAutoDelete(true); connect(transferJob, &KIO::TransferJob::canResume, this, &TransferRecv::slotLocalCanResume); connect(transferJob, &KIO::TransferJob::result, this, &TransferRecv::slotLocalGotResult); connect(transferJob, &KIO::TransferJob::dataReq, this, &TransferRecv::slotLocalReady); } void TransferRecv::askAndPrepareLocalKio(const QString &message, int enabledActions, ResumeDialog::ReceiveAction defaultAction, KIO::fileoffset_t startPosition) { switch (ResumeDialog::ask(this, message, enabledActions, defaultAction)) { case ResumeDialog::RA_Resume: prepareLocalKio(false, true, startPosition); break; case ResumeDialog::RA_Overwrite: prepareLocalKio(true, false); break; case ResumeDialog::RA_Rename: prepareLocalKio(false, false); break; case ResumeDialog::RA_Cancel: default: setStatus(Queued); } } bool TransferRecv::createDirs(const QUrl &dirURL) const { QUrl kurl(dirURL); //First we split directories until we reach to the top, //since we need to create directories one by one QList dirList; while (kurl != KIO::upUrl(kurl)) { dirList.prepend(kurl); kurl = KIO::upUrl(kurl); } //Now we create the directories QList::ConstIterator it; for (it=dirList.constBegin(); it != dirList.constEnd(); ++it) { KIO::StatJob* statJob = KIO::stat(*it, KIO::StatJob::SourceSide, 0); statJob->exec(); if (statJob->error()) { KIO::MkdirJob* job = KIO::mkdir(*it, -1); if (!job->exec()) { return false; } } } #ifndef Q_OS_WIN QFileInfo dirInfo(dirURL.toLocalFile()); if (!dirInfo.isWritable()) { return false; } #else //!TODO find equivalent windows solution //from 4.7 QFile Doc: // File permissions are handled differently on Linux/Mac OS X and Windows. // In a non writable directory on Linux, files cannot be created. // This is not always the case on Windows, where, for instance, // the 'My Documents' directory usually is not writable, but it is still // possible to create files in it. #endif return true; } void TransferRecv::slotLocalCanResume(KIO::Job *job, KIO::filesize_t size) { qDebug() << "[BEGIN]" << endl << "size: " << size; KIO::TransferJob* transferJob = dynamic_cast(job); if (!transferJob) { qDebug() << "not a TransferJob? returning"; return; } if (size != 0) { disconnect(transferJob, 0, 0, 0); if (Preferences::self()->dccAutoResume()) { prepareLocalKio(false, true, size); } else { askAndPrepareLocalKio(i18np( "A partial file exists:
" "%2
" "Size of the partial file: 1 byte.
", "A partial file exists:
" "%2
" "Size of the partial file: %1 bytes.
", size, m_fileURL.toString()), ResumeDialog::RA_Resume | ResumeDialog::RA_Overwrite | ResumeDialog::RA_Rename | ResumeDialog::RA_Cancel, ResumeDialog::RA_Resume, size); } transferJob->putOnHold(); } qDebug() << "[END]"; } void TransferRecv::slotLocalGotResult(KJob *job) { qDebug() << "[BEGIN]"; KIO::TransferJob* transferJob = static_cast(job); disconnect(transferJob, 0, 0, 0); switch (transferJob->error()) { case 0: // no error qDebug() << "job->error() returned 0." << endl << "Why was I called in spite of no error?"; break; case KIO::ERR_FILE_ALREADY_EXIST: askAndPrepareLocalKio(i18nc("%1=fileName, %2=local filesize, %3=sender filesize", "The file already exists.
" "%1 (%2)
" "Sender reports file size of %3
", m_fileURL.toString(), KIO::convertSize(QFileInfo(m_fileURL.path()).size()), KIO::convertSize(m_fileSize)), ResumeDialog::RA_Overwrite | ResumeDialog::RA_Rename | ResumeDialog::RA_Cancel, ResumeDialog::RA_Overwrite); break; default: askAndPrepareLocalKio(i18n("Could not open the file.
" "Error: %1

" "%2
", transferJob->error(), m_fileURL.toString()), ResumeDialog::RA_Rename | ResumeDialog::RA_Cancel, ResumeDialog::RA_Rename); } qDebug() << "[END]"; } void TransferRecv::slotLocalReady(KIO::Job *job) { qDebug(); KIO::TransferJob* transferJob = static_cast(job); disconnect(transferJob, 0, 0, 0); // WriteCacheHandler will control the job after this m_writeCacheHandler = new TransferRecvWriteCacheHandler(transferJob); connect(m_writeCacheHandler, &TransferRecvWriteCacheHandler::done, this, &TransferRecv::slotLocalWriteDone); connect(m_writeCacheHandler, &TransferRecvWriteCacheHandler::gotError, this, &TransferRecv::slotLocalGotWriteError); if (!m_resumed) { connectWithSender(); } else { requestResume(); } } void TransferRecv::connectWithSender() { if (m_reverse) { if (!startListeningForSender()) { return; } Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (!server) { failed(i18n("Could not send Reverse DCC SEND acknowledgement to the partner via the IRC server.")); return; } m_ownIp = DccCommon::getOwnIp(server); m_ownPort = m_serverSocket->serverPort(); if (Preferences::self()->dccUPnP()) { UPnP::UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router && router->forward(QHostAddress(server->getOwnIpByNetworkInterface()), m_ownPort, QAbstractSocket::TcpSocket)) { connect(router, &UPnP::UPnPRouter::forwardComplete, this, &TransferRecv::sendReverseAck); } else { sendReverseAck(true, 0); // Try anyways on error } } else { sendReverseAck(false, 0); } } else { connectToSendServer(); } } void TransferRecv::sendReverseAck(bool error, quint16 port) { Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (!server) { failed(i18n("Could not send Reverse DCC SEND acknowledgement to the partner via the IRC server.")); return; } qDebug(); if (Preferences::self()->dccUPnP() && this->sender()) { if (port != m_ownPort) return; // Somebody elses forward succeeded disconnect (this->sender(), SIGNAL(forwardComplete(bool,quint16)), this, SLOT(sendRequest(bool,quint16))); if (error) { server->appendMessageToFrontmost(i18nc("Universal Plug and Play", "UPnP"), i18n("Failed to forward port %1. Sending DCC request to remote user regardless.", QString::number(m_ownPort)), QHash(), false); } } setStatus(WaitingRemote, i18n("Waiting for connection")); server->dccReverseSendAck(m_partnerNick, transferFileName(m_fileName), DccCommon::textIpToNumericalIp(m_ownIp), m_ownPort, m_fileSize, m_reverseToken); } void TransferRecv::requestResume() { qDebug(); setStatus(WaitingRemote, i18n("Waiting for remote host's acceptance")); startConnectionTimer(30); qDebug() << "Requesting resume for " << m_partnerNick << " file " << m_fileName << " partner " << m_partnerPort; Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (!server) { qDebug() << "Could not retrieve the instance of Server. Connection id: " << m_connectionId; failed(i18n("Could not send DCC RECV resume request to the partner via the IRC server.")); return; } if (m_reverse) { server->dccPassiveResumeGetRequest(m_partnerNick, transferFileName(m_fileName), m_partnerPort, m_transferringPosition, m_reverseToken); } else { server->dccResumeGetRequest(m_partnerNick, transferFileName(m_fileName), m_partnerPort, m_transferringPosition); } } // public slot void TransferRecv::startResume(quint64 position) { qDebug() << "Position:" << position; stopConnectionTimer(); if ((quint64)m_transferringPosition != position) { qDebug() << "remote responded with an unexpected position"<< endl << "expected: " << m_transferringPosition << endl << "remote response: " << position; failed(i18n("Unexpected response from remote host")); return; } connectWithSender(); } void TransferRecv::connectToSendServer() { qDebug(); // connect to sender setStatus(Connecting); startConnectionTimer(30); m_recvSocket = new QTcpSocket(this); connect(m_recvSocket, &QTcpSocket::connected, this, &TransferRecv::startReceiving); connect(m_recvSocket, static_cast(&QTcpSocket::error), this, &TransferRecv::connectionFailed); qDebug() << "Attempting to connect to " << m_partnerIp << ":" << m_partnerPort; m_recvSocket->connectToHost(m_partnerIp, m_partnerPort); } bool TransferRecv::startListeningForSender() { // Set up server socket QString failedReason; if (Preferences::self()->dccSpecificSendPorts()) { m_serverSocket = DccCommon::createServerSocketAndListen(this, &failedReason, Preferences::self()->dccSendPortsFirst(), Preferences::self()->dccSendPortsLast()); } else { m_serverSocket = DccCommon::createServerSocketAndListen(this, &failedReason); } if (!m_serverSocket) { failed(failedReason); return false; } connect(m_serverSocket, &QTcpServer::newConnection, this, &TransferRecv::slotServerSocketReadyAccept); startConnectionTimer(30); return true; } void TransferRecv::slotServerSocketReadyAccept() { //reverse dcc m_recvSocket = m_serverSocket->nextPendingConnection(); if (!m_recvSocket) { failed(i18n("Could not accept the connection (socket error).")); return; } connect(m_recvSocket, static_cast(&QTcpSocket::error), this, &TransferRecv::connectionFailed); // we don't need ServerSocket anymore m_serverSocket->close(); m_serverSocket = 0; // Will be deleted by parent if (Preferences::self()->dccUPnP()) { UPnP::UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router) { router->undoForward(m_ownPort, QAbstractSocket::TcpSocket); } } startReceiving(); } void TransferRecv::startReceiving() { qDebug(); stopConnectionTimer(); connect(m_recvSocket, &QTcpSocket::readyRead, this, &TransferRecv::readData); m_transferStartPosition = m_transferringPosition; //we don't need the original filename anymore, overwrite it to display the correct one in transfermanager/panel m_fileName = m_saveFileName; m_ownPort = m_recvSocket->localPort(); startTransferLogger(); // initialize CPS counter, ETA counter, etc... setStatus(Transferring); } // slot void TransferRecv::connectionFailed(QAbstractSocket::SocketError errorCode) { qDebug() << "Code = " << errorCode << ", string = " << m_recvSocket->errorString(); failed(m_recvSocket->errorString()); } void TransferRecv::readData() // slot { //qDebug(); qint64 actual = m_recvSocket->read(m_buffer, m_bufferSize); if (actual > 0) { //actual is the size we read in, and is guaranteed to be less than m_bufferSize m_transferringPosition += actual; m_writeCacheHandler->append(m_buffer, actual); m_writeCacheHandler->write(false); //in case we could not read all the data, leftover data could get lost if (m_recvSocket->bytesAvailable() > 0) { readData(); } else { sendAck(); } } } void TransferRecv::sendAck() // slot { //qDebug() << m_transferringPosition << "/" << (KIO::fileoffset_t)m_fileSize; //It is bound to be 32bit according to dcc specs, -> 4GB limit. //But luckily no client ever reads this value, //except for old mIRC versions, but they couldn't send or receive files over 4GB anyway. //Note: The resume and filesize are set via dcc send command and can be over 4GB - quint32 pos = intel((quint32)m_transferringPosition); + quint32 pos = htonl((quint32)m_transferringPosition); m_recvSocket->write((char*)&pos, 4); if (m_transferringPosition == (KIO::fileoffset_t)m_fileSize) { qDebug() << "Sent final ACK."; disconnect(m_recvSocket, 0, 0, 0); m_writeCacheHandler->close(); // WriteCacheHandler will send the signal done() } else if (m_transferringPosition > (KIO::fileoffset_t)m_fileSize) { qDebug() << "The remote host sent larger data than expected: " << m_transferringPosition; failed(i18n("Transfer error")); } } void TransferRecv::slotLocalWriteDone() // <-WriteCacheHandler::done() { qDebug(); cleanUp(); setStatus(Done); emit done(this); } // <- WriteCacheHandler::gotError() void TransferRecv::slotLocalGotWriteError(const QString &errorString) { qDebug(); failed(i18n("KIO error: %1", errorString)); } void TransferRecv::startConnectionTimer(int secs) { qDebug(); m_connectionTimer->start(secs * 1000); } void TransferRecv::stopConnectionTimer() { if (m_connectionTimer->isActive()) { m_connectionTimer->stop(); qDebug(); } } void TransferRecv::connectionTimeout() // slot { qDebug(); failed(i18n("Timed out")); } // WriteCacheHandler TransferRecvWriteCacheHandler::TransferRecvWriteCacheHandler(KIO::TransferJob *transferJob) : m_transferJob(transferJob) { m_writeReady = true; m_cacheStream = 0; connect(m_transferJob, &KIO::TransferJob::dataReq, this, &TransferRecvWriteCacheHandler::slotKIODataReq); connect(m_transferJob, &KIO::TransferJob::result, this, &TransferRecvWriteCacheHandler::slotKIOResult); m_transferJob->setAsyncDataEnabled(m_writeAsyncMode = true); } TransferRecvWriteCacheHandler::~TransferRecvWriteCacheHandler() { closeNow(); } // public void TransferRecvWriteCacheHandler::append(char *data, int size) { // sendAsyncData() and dataReq() cost a lot of time, so we should pack some caches. static const int maxWritePacketSize = 1 * 1024 * 1024; // 1meg if (m_cacheList.isEmpty() || m_cacheList.back().size() + size > maxWritePacketSize) { m_cacheList.append(QByteArray()); delete m_cacheStream; m_cacheStream = new QDataStream(&m_cacheList.back(), QIODevice::WriteOnly); } m_cacheStream->writeRawData(data, size); } // public bool TransferRecvWriteCacheHandler::write(bool force) { // force == false: return without doing anything when the whole cache size is smaller than maxWritePacketSize if (m_cacheList.isEmpty() || !m_transferJob || !m_writeReady || !m_writeAsyncMode) { return false; } if (!force && m_cacheList.count() < 2) { return false; } // do write m_writeReady = false; m_transferJob->sendAsyncData(m_cacheList.front()); //qDebug() << "wrote " << m_cacheList.front().size() << " bytes."; m_cacheList.pop_front(); return true; } void TransferRecvWriteCacheHandler::close() // public { qDebug(); write(true); // write once if kio is ready to write m_transferJob->setAsyncDataEnabled(m_writeAsyncMode = false); qDebug() << "switched to synchronized mode."; qDebug() << "flushing... (remaining caches: " << m_cacheList.count() << ")"; } void TransferRecvWriteCacheHandler::closeNow() // public { write(true); // flush if (m_transferJob) { m_transferJob->kill(); m_transferJob = 0; } m_cacheList.clear(); delete m_cacheStream; m_cacheStream = 0; } void TransferRecvWriteCacheHandler::slotKIODataReq(KIO::Job *job, QByteArray &data) { Q_UNUSED(job); // We are in writeAsyncMode if there is more data to be read in from dcc if (m_writeAsyncMode) { m_writeReady = true; } else { // No more data left to read from incoming dcctransfer if (!m_cacheList.isEmpty()) { // once we write everything in cache, the file is complete. // This function will be called once more after this last data is written. data = m_cacheList.front(); qDebug() << "will write " << m_cacheList.front().size() << " bytes."; m_cacheList.pop_front(); } else { // finally, no data left to write or read. qDebug() << "flushing done."; m_transferJob = 0; emit done(); // -> TransferRecv::slotLocalWriteDone() } } } void TransferRecvWriteCacheHandler::slotKIOResult(KJob *job) { Q_ASSERT(m_transferJob); disconnect(m_transferJob, 0, 0, 0); m_transferJob = 0; if (job->error()) { QString errorString = job->errorString(); closeNow(); emit gotError(errorString); // -> TransferRecv::slotLocalGotWriteError() } } } } diff --git a/src/dcc/transfersend.cpp b/src/dcc/transfersend.cpp index cc3629a3..eae8e71a 100644 --- a/src/dcc/transfersend.cpp +++ b/src/dcc/transfersend.cpp @@ -1,640 +1,640 @@ /* send a file on DCC protocol begin: Mit Aug 7 2002 copyright: (C) 2002 by Dario Abatianni email: eisfuchs@tigress.com */ /* Copyright (C) 2004-2007 Shintaro Matsuoka Copyright (C) 2004,2005 John Tapsell Copyright (C) 2009 Michael Kreitzer Copyright (C) 2009 Bernd Buschinski */ /* 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. */ #include "transfersend.h" #include "dcccommon.h" #include "transfermanager.h" #include "application.h" #include "connectionmanager.h" #include "server.h" #include "upnprouter.h" #include #include #include #include #include #include // TODO: remove the dependence #include #include using namespace Konversation::UPnP; namespace Konversation { namespace DCC { TransferSend::TransferSend(QObject *parent) : Transfer(Transfer::Send, parent) { qDebug(); m_serverSocket = 0; m_sendSocket = 0; m_tmpFile = 0; m_connectionTimer = new QTimer(this); m_connectionTimer->setSingleShot(true); connect(m_connectionTimer, &QTimer::timeout, this, &TransferSend::slotConnectionTimeout); // set defualt values m_reverse = Preferences::self()->dccPassiveSend(); } TransferSend::~TransferSend() { cleanUp(); } void TransferSend::cleanUp() { qDebug(); stopConnectionTimer(); disconnect(m_connectionTimer, 0, 0, 0); finishTransferLogger(); if (m_tmpFile) { delete m_tmpFile; m_tmpFile = 0; } m_file.close(); if (m_sendSocket) { disconnect(m_sendSocket, 0, 0, 0); m_sendSocket->close(); m_sendSocket = 0; // the instance will be deleted automatically by its parent } if (m_serverSocket) { m_serverSocket->close(); m_serverSocket = 0; // the instance will be deleted automatically by its parent if (Preferences::self()->dccUPnP()) { UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router) { router->undoForward(m_ownPort, QAbstractSocket::TcpSocket); } } } Transfer::cleanUp(); } void TransferSend::setFileURL(const QUrl &url) { if (getStatus() == Configuring) { m_fileURL = url; } } void TransferSend::setFileName(const QString &fileName) { if (getStatus() == Configuring) { m_fileName = fileName; } } void TransferSend::setOwnIp(const QString &ownIp) { if (getStatus() == Configuring) { m_ownIp = ownIp; } } void TransferSend::setFileSize(KIO::filesize_t fileSize) { if (getStatus() == Configuring) { m_fileSize = fileSize; } } void TransferSend::setReverse(bool reverse) { if (getStatus() == Configuring) { m_reverse = reverse; } } bool TransferSend::queue() { qDebug(); if (getStatus() != Configuring) { return false; } if (m_ownIp.isEmpty()) { m_ownIp = DccCommon::getOwnIp(Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId)); } if (!KAuthorized::authorizeKAction("allow_downloading")) { //Do not have the rights to send the file. Shouldn't have gotten this far anyway //Note this is after the initialisation so the view looks correct still failed(i18n("The admin has restricted the right to send files")); return false; } if (m_fileName.isEmpty()) { m_fileName = sanitizeFileName(m_fileURL.fileName()); } if (Preferences::self()->dccIPv4Fallback()) { m_ownIp = DCC::DccCommon::ipv6FallbackAddress(m_ownIp); } m_fastSend = Preferences::self()->dccFastSend(); qDebug() << "Fast DCC send: " << m_fastSend; //Check the file exists KIO::StatJob* statJob = KIO::stat(m_fileURL, KIO::StatJob::SourceSide, 0); statJob->exec(); if (statJob->error()) { failed(i18n("The url \"%1\" does not exist", m_fileURL.toString())); return false; } //Some protocols, like http, maybe not return a filename, and altFileName may be empty, So prompt the user for one. if (m_fileName.isEmpty()) { bool pressedOk; m_fileName = QInputDialog::getText(0, i18n("Enter Filename"), i18n("The file that you are sending to %1 does not have a filename.
Please enter a filename to be presented to the receiver, or cancel the dcc transfer
", getPartnerNick()), QLineEdit::EchoMode::Normal, i18n("unknown"), &pressedOk); if (!pressedOk) { failed(i18n("No filename was given")); return false; } } //FIXME: if "\\\"" works well on other IRC clients, replace "\"" with "\\\"" m_fileName.replace('\"', '_'); if (Preferences::self()->dccSpaceToUnderscore()) { m_fileName.replace(' ', '_'); } if (!m_fileURL.isLocalFile()) { m_tmpFile = new QTemporaryFile(); m_tmpFile->open(); // create the file, and thus create m_tmpFile.fileName m_tmpFile->close(); // no need to keep the file open, it isn't deleted until the destructor is called QUrl tmpUrl = QUrl::fromLocalFile(m_tmpFile->fileName()); KIO::FileCopyJob *fileCopyJob = KIO::file_copy(m_fileURL, tmpUrl, -1, KIO::Overwrite); connect(fileCopyJob, &KIO::FileCopyJob::result, this, &TransferSend::slotLocalCopyReady); fileCopyJob->start(); setStatus(Preparing); return false; // not ready to send yet } slotLocalCopyReady(0); return true; } void TransferSend::slotLocalCopyReady(KJob *job) { QString fn = m_fileURL.toDisplayString(); bool remoteFile = job != 0; int error = job ? job->error() : 0; qDebug() << "m_tmpFile: " << fn << "error: " << error << "remote file: " << remoteFile; if (error) { failed(i18n("Could not retrieve \"%1\"", fn)); return; } if (remoteFile) { m_file.setFileName(m_tmpFile->fileName()); } else { m_file.setFileName(m_fileURL.toLocalFile()); } if (m_fileSize == 0) { m_fileSize = m_file.size(); qDebug() << "filesize 0, new filesize: " << m_fileSize; if (m_fileSize == 0) { failed(i18n("Unable to send a 0 byte file.")); return; } } setStatus(Queued); if (remoteFile) { start(); // addDccSend would have done it for us if this was a local file } } void TransferSend::reject() { qDebug(); failed(i18n("DCC SEND request was rejected")); } void TransferSend::abort() // public slot { qDebug(); cleanUp(); setStatus(Aborted); emit done(this); } void TransferSend::start() // public slot { qDebug(); if (getStatus() != Queued) { return; } // common procedure Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (!server) { qDebug() << "could not retrieve the instance of Server. Connection id: " << m_connectionId; failed(i18n("Could not send a DCC SEND request to the partner via the IRC server.")); return; } if (!m_reverse) { // Normal DCC SEND qDebug() << "normal DCC SEND"; // Set up server socket QString failedReason; if (Preferences::self()->dccSpecificSendPorts()) { m_serverSocket = DccCommon::createServerSocketAndListen(this, &failedReason, Preferences::self()->dccSendPortsFirst(), Preferences::self()->dccSendPortsLast()); } else { m_serverSocket = DccCommon::createServerSocketAndListen(this, &failedReason); } if (!m_serverSocket) { failed(failedReason); return; } connect(m_serverSocket, &QTcpServer::newConnection, this, &TransferSend::acceptClient); // Get own port number m_ownPort = m_serverSocket->serverPort(); qDebug() << "Own Address=" << m_ownIp << ":" << m_ownPort; if (Preferences::self()->dccUPnP()) { UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router && router->forward(QHostAddress(server->getOwnIpByNetworkInterface()), m_ownPort, QAbstractSocket::TcpSocket)) { connect(router, &UPnPRouter::forwardComplete, this, &TransferSend::sendRequest); } else { sendRequest(true, 0); // Just try w/o UPnP on failure } } else { sendRequest(false, 0); } } else { // Passive DCC SEND qDebug() << "Passive DCC SEND"; int tokenNumber = Application::instance()->getDccTransferManager()->generateReverseTokenNumber(); // TODO: should we append a letter "T" to this token? m_reverseToken = QString::number(tokenNumber); qDebug() << "Passive DCC key(token): " << m_reverseToken; startConnectionTimer(Preferences::self()->dccSendTimeout()); server->dccPassiveSendRequest(m_partnerNick, transferFileName(m_fileName), DccCommon::textIpToNumericalIp(m_ownIp), m_fileSize, m_reverseToken); } setStatus(WaitingRemote, i18n("Awaiting acceptance by remote user...")); } void TransferSend::sendRequest(bool error, quint16 port) { Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(m_connectionId); if (!server) { qDebug() << "could not retrieve the instance of Server. Connection id: " << m_connectionId; failed(i18n("Could not send a DCC SEND request to the partner via the IRC server.")); return; } if (Preferences::self()->dccUPnP() && this->sender()) { if (port != m_ownPort) return; // Somebody elses forward succeeded disconnect(this->sender(), SIGNAL(forwardComplete(bool,quint16)), this, SLOT(sendRequest(bool,quint16))); if (error) { server->appendMessageToFrontmost(i18nc("Universal Plug and Play", "UPnP"), i18n("Failed to forward port %1. Sending DCC request to remote user regardless.", QString::number(m_ownPort)), QHash(), false); } } startConnectionTimer(Preferences::self()->dccSendTimeout()); server->dccSendRequest(m_partnerNick, transferFileName(m_fileName), DccCommon::textIpToNumericalIp(m_ownIp), m_ownPort, m_fileSize); } void TransferSend::connectToReceiver(const QString &partnerHost, quint16 partnerPort) { qDebug() << "host:" << partnerHost << "port:" << partnerPort; // Reverse DCC startConnectionTimer(Preferences::self()->dccSendTimeout()); m_partnerIp = partnerHost; m_partnerPort = partnerPort; m_sendSocket = new QTcpSocket(this); connect(m_sendSocket, &QTcpSocket::connected, this, &TransferSend::startSending); connect(m_sendSocket, static_cast(&QTcpSocket::error), this, &TransferSend::slotGotSocketError); setStatus(Connecting); m_sendSocket->connectToHost(partnerHost, partnerPort); } // public bool TransferSend::setResume(quint64 position) { qDebug() << "Position=" << position; if (getStatus() > WaitingRemote) { return false; } if (position >= m_fileSize) { return false; } m_resumed = true; m_transferringPosition = position; return true; } void TransferSend::acceptClient() // slot { // Normal DCC qDebug(); stopConnectionTimer(); m_sendSocket = m_serverSocket->nextPendingConnection(); if (!m_sendSocket) { failed(i18n("Could not accept the connection (socket error).")); return; } connect(m_sendSocket, static_cast(&QTcpSocket::error), this, &TransferSend::slotGotSocketError); // we don't need ServerSocket anymore m_serverSocket->close(); m_serverSocket = 0; // the instance will be deleted automatically by its parent if (Preferences::self()->dccUPnP()) { UPnPRouter *router = Application::instance()->getDccTransferManager()->getUPnPRouter(); if (router) { router->undoForward(m_ownPort, QAbstractSocket::TcpSocket); } } startSending(); } void TransferSend::startSending() { stopConnectionTimer(); connect(m_sendSocket, &QTcpSocket::bytesWritten, this, &TransferSend::bytesWritten); connect(m_sendSocket, &QTcpSocket::readyRead, this, &TransferSend::getAck); m_partnerIp = m_sendSocket->peerAddress().toString(); m_partnerPort = m_sendSocket->peerPort(); m_ownPort = m_sendSocket->localPort(); if (m_file.open(QIODevice::ReadOnly)) { // seek to file position to make resume work m_file.seek(m_transferringPosition); m_transferStartPosition = m_transferringPosition; writeData(); startTransferLogger(); // initialize CPS counter, ETA counter, etc... setStatus(Transferring); } else { failed(getQFileErrorString(m_file.error())); } } void TransferSend::bytesWritten(qint64 bytes) { if (bytes > 0) { m_transferringPosition += bytes; if ((KIO::fileoffset_t)m_fileSize <= m_transferringPosition) { Q_ASSERT((KIO::fileoffset_t)m_fileSize == m_transferringPosition); qDebug() << "Done."; } } if (m_sendSocket) { if (m_fastSend && m_sendSocket->bytesToWrite() <= (qint64)m_bufferSize) { writeData(); } else if (!m_fastSend && bytes == 0) { writeData(); } } } void TransferSend::writeData() // slot { //qDebug(); qint64 actual = m_file.read(m_buffer, m_bufferSize); if (actual > 0) { m_sendSocket->write(m_buffer, actual); } } void TransferSend::getAck() // slot { //qDebug(); if (m_transferringPosition < (KIO::fileoffset_t)m_fileSize) { //don't write data directly, in case we get spammed with ACK we try so send too fast bytesWritten(0); } quint32 pos; while (m_sendSocket->bytesAvailable() >= 4) { m_sendSocket->read((char*)&pos, 4); - pos = intel(pos); + pos = ntohl(pos); //qDebug() << pos << "/" << m_fileSize; if (pos == m_fileSize) { qDebug() << "Received final ACK."; cleanUp(); setStatus(Done); emit done(this); break; // for safe } } } void TransferSend::slotGotSocketError(QAbstractSocket::SocketError errorCode) { stopConnectionTimer(); qDebug() << "code = " << errorCode << " string = " << m_sendSocket->errorString(); failed(i18n("Socket error: %1", m_sendSocket->errorString())); } void TransferSend::startConnectionTimer(int secs) { qDebug(); //start also restarts, no need for us to double check it m_connectionTimer->start(secs * 1000); } void TransferSend::stopConnectionTimer() { if (m_connectionTimer->isActive()) { qDebug() << "stop"; m_connectionTimer->stop(); } } void TransferSend::slotConnectionTimeout() // slot { qDebug(); failed(i18n("Timed out")); } // protected, static QString TransferSend::getQFileErrorString(int code) { QString errorString; switch(code) { case QFile::NoError: errorString = i18n("The operation was successful. Should never happen in an error dialog."); break; case QFile::ReadError: errorString = i18n("Could not read from file \"%1\".", m_fileName); break; case QFile::WriteError: errorString = i18n("Could not write to file \"%1\".", m_fileName); break; case QFile::FatalError: errorString = i18n("A fatal unrecoverable error occurred."); break; case QFile::OpenError: errorString = i18n("Could not open file \"%1\".", m_fileName); break; // Same case value? Damn! // case IO_ConnectError: // errorString="Could not connect to the device."; // break; case QFile::AbortError: errorString = i18n("The operation was unexpectedly aborted."); break; case QFile::TimeOutError: errorString = i18n("The operation timed out."); break; case QFile::UnspecifiedError: errorString = i18n("An unspecified error happened on close."); break; default: errorString = i18n("Unknown error. Code %1",code); break; } return errorString; } } }