X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fcoretransfer.cpp;h=60e3ac74cdec84d14e07218da50decb77320781d;hp=10d609bae490c5ac5fc72f51ca39c285a802abe1;hb=e3c42d072b2b8f39c8c9ea44dfc3bded87ae43b0;hpb=584085c95a70e921f401b387bb73e66e2d71a714 diff --git a/src/core/coretransfer.cpp b/src/core/coretransfer.cpp index 10d609ba..60e3ac74 100644 --- a/src/core/coretransfer.cpp +++ b/src/core/coretransfer.cpp @@ -18,24 +18,171 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include + +#include +#include + #include "coretransfer.h" +const qint64 chunkSize = 16 * 1024; + INIT_SYNCABLE_OBJECT(CoreTransfer) CoreTransfer::CoreTransfer(Direction direction, const QString &nick, const QString &fileName, const QHostAddress &address, quint16 port, quint64 fileSize, QObject *parent) - : Transfer(direction, nick, fileName, address, port, fileSize, parent) + : Transfer(direction, nick, fileName, address, port, fileSize, parent), + _socket(0), + _pos(0), + _reading(false) +{ + +} + + +void CoreTransfer::cleanUp() { + if (_socket) { + _socket->close(); + _socket->deleteLater(); + _socket = 0; + } + _buffer.clear(); + _reading = false; +} + + +void CoreTransfer::onSocketDisconnected() +{ + if (state() == Connecting || state() == Transferring) { + setError(tr("Socket closed while still transferring!")); + } + else + cleanUp(); +} + + +void CoreTransfer::onSocketError(QAbstractSocket::SocketError error) +{ + Q_UNUSED(error) + + if (state() == Connecting || state() == Transferring) { + setError(tr("DCC connection error: %1").arg(_socket->errorString())); + } } void CoreTransfer::requestAccepted(PeerPtr peer) { + if (_peer || !peer || state() != New) + return; // transfer was already accepted + + _peer = peer; + setState(Pending); + emit accepted(peer); + + // FIXME temporary until we have queueing + start(); } void CoreTransfer::requestRejected(PeerPtr peer) { + if (_peer || state() != New) + return; + + _peer = peer; + setState(Rejected); + emit rejected(peer); } + + +void CoreTransfer::start() +{ + if (!_peer || state() != Pending || direction() != Receive) + return; + + setupConnectionForReceive(); +} + + +void CoreTransfer::setupConnectionForReceive() +{ + if (port() == 0) { + setError(tr("Reverse DCC not supported yet!")); + return; + } + + setState(Connecting); + + _socket = new QTcpSocket(this); + connect(_socket, SIGNAL(connected()), SLOT(startReceiving())); + connect(_socket, SIGNAL(disconnected()), SLOT(onSocketDisconnected())); + connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(onSocketError(QAbstractSocket::SocketError))); + connect(_socket, SIGNAL(readyRead()), SLOT(onDataReceived())); + + _socket->connectToHost(address(), port()); +} + + +void CoreTransfer::startReceiving() +{ + setState(Transferring); +} + + +void CoreTransfer::onDataReceived() +{ + if (_reading) // since we're spinning the event loop, we may get another readyRead() and thus reentrancy + return; + _reading = true; + + while (_socket->bytesAvailable()) { + QByteArray data = _socket->read(chunkSize); + _pos += data.size(); + if (!relayData(data, true)) + return; + + QCoreApplication::processEvents(); // don't block the rest of the core/client communication + if (!_socket) // just in case something happened during spinning the event loop that killed our socket + return; + } + + // Send ack to sender. The DCC protocol only specifies 32 bit values, but modern clients (i.e. those who can send files + // larger than 4 GB) will ignore this anyway... + quint32 ack = qToBigEndian((quint32)_pos);// qDebug() << Q_FUNC_INFO << _pos; + _socket->write((char *)&ack, 4); + + if (_pos > fileSize()) { + qWarning() << "DCC Receive: Got more data than expected!"; + setError(tr("DCC Receive: Got more data than expected!")); + } + else if (_pos == fileSize()) { + qDebug() << "DCC Receive: Transfer finished"; + if (relayData(QByteArray(), false)) // empty buffer + setState(Completed); + } + + _reading = false; +} + + +bool CoreTransfer::relayData(const QByteArray &data, bool requireChunkSize) +{ + // safeguard against a disconnecting quasselclient + if (!_peer) { + setError(tr("DCC Receive: Quassel Client disconnected during transfer!")); + return false; + } + _buffer.append(data); + + // we only want to send data to the client once we have reached the chunksize + if (_buffer.size() > 0 && (_buffer.size() >= chunkSize || !requireChunkSize)) { + SYNC_OTHER(dataReceived, ARG(_peer), ARG(_buffer)); + _buffer.clear(); + } + + return true; +}