/* # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of # Freiburg # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # ----------------------------------------------------------------------------- # clientFileSendDialog.cpp # - filechooser and progress dialog # ----------------------------------------------------------------------------- */ #include "clientFileSendDialog.h" ClientFileSendDialog::ClientFileSendDialog(QWidget *parent) : QDialog(parent) { setupUi(this); _transferID = 0; _error = false; _isMulticast = false; _file = NULL; _socket = NULL; _clientNicklistDialog = new ClientNicklistDialog(this); // connect to D-Bus and get interface QDBusConnection dbus = QDBusConnection::sessionBus(); _ifaceDBus = new OrgOpenslxPvsInterface("org.openslx.pvs", "/", dbus, this); // get current users name from backend QDBusPendingReply reply = _ifaceDBus->chat_getNickname(); reply.waitForFinished(); if (reply.isValid()) _nickname = reply.value(); else _nickname = "unknown"; connect(this, SIGNAL(finished(int)), this, SLOT(deleteLater())); } ClientFileSendDialog::~ClientFileSendDialog() { qDebug("[%s] Deleted!", metaObject()->className()); } //////////////////////////////////////////////////////////////////////////////// // Public void ClientFileSendDialog::open() { // get nick of remote user int result = _clientNicklistDialog->exec(); if (result == 0) // User canceled { reject(); return; } if (_clientNicklistDialog->isSendToAll()) { sendToAll(); } else { open(_clientNicklistDialog->getNick()); } } void ClientFileSendDialog::open(QString nick) { QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::homePath(), ""); if (filename == "") { reject(); return; } open(nick, filename); } void ClientFileSendDialog::sendToAll() { QString filename = QFileDialog::getOpenFileName(this, tr("Send File"), QDir::homePath(), ""); if (filename == "") { reject(); return; } sendToAll(filename); } void ClientFileSendDialog::sendToAll(QString filename) { _isMulticast = true; connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferStarted(qulonglong)), SLOT(multicastTransferStarted(qulonglong))); connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferProgress(qulonglong,qulonglong,qulonglong)), SLOT(multicastTransferProgress(qulonglong, qulonglong, qulonglong))); connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferFinished(qulonglong)), SLOT(multicastTransferFinished(qulonglong))); connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferFailed(qulonglong, QString const&)), SLOT(multicastTransferFailed(qulonglong, QString const&))); filenameLabel->setText(filename); progressBar->setRange(0, 0); labelNick->setText(tr("all")); labelStatus->setText(tr("Waiting to start")); QString errorMessage("Backend error"); // We need to jump through a lot of hoops because this call is prone to time out, and // QtDBus does not support specifying timeouts in generated interfaces. QDBusMessage call = QDBusMessage::createMethodCall("org.openslx.pvs", "/", "org.openslx.pvs", "createMulticastTransfer"); call << filename; QDBusMessage reply = _ifaceDBus->connection().call(call, QDBus::Block, 5000); if (reply.type() == QDBusMessage::ErrorMessage) { QMessageBox::critical(this, tr("File Send error"), tr("Error communicating with backend: %1: %2").arg(reply.errorName()).arg(reply.errorMessage())); reject(); return; } else if (reply.type() == QDBusMessage::InvalidMessage) { QMessageBox::critical(this, tr("File Send error"), tr("Something went wrong while communicating with backend, but I don't know what.")); reject(); return; } else if (reply.type() == QDBusMessage::ReplyMessage) { QList args = reply.arguments(); bool created = args.at(0).toBool(); _transferID = args.at(1).toULongLong(); QString errorMessage = args.at(2).toString(); if (!created) { QMessageBox::critical(this, tr("File Send error"), tr("Could not create a multicast transfer: %1").arg(errorMessage)); reject(); return; } } connect(cancelButton, SIGNAL(clicked()), SLOT(canceled())); show(); } void ClientFileSendDialog::open(QString nick, QString filename) { // open file _file = new QFile(filename); _file->open(QIODevice::ReadOnly); _bytesToWrite = _file->size(); div = 1 + _bytesToWrite / 1000000000; // bc. progressBar supports only int // get host from backend QString host = ""; QDBusPendingReply reply = _ifaceDBus->getIpByNick(nick); reply.waitForFinished(); if (reply.isValid()) host = reply.value(); else // DBus Error, hostname qDebug("[%s] D-Bus ERROR, no hostname available!", metaObject()->className()); // gui filenameLabel->setText(filename); progressBar->setValue(0); progressBar->setMaximum(_bytesToWrite/div); labelNick->setText(nick); labelB->setText(formatSize(_bytesToWrite)); connect(cancelButton, SIGNAL(clicked()), this, SLOT(close())); // open socket _socket = new QTcpSocket(); _socket->connectToHost(host, 29481); qDebug("[%s] Remote host: %s", metaObject()->className(), qPrintable(host)); connect(_socket, SIGNAL(connected()), this, SLOT(sendHeader())); connect(_socket, SIGNAL(disconnected()), this, SLOT(close())); connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError))); } //////////////////////////////////////////////////////////////////////////////// // Private void ClientFileSendDialog::sendHeader() { QFileInfo info(_file->fileName()); QString size = QString::number(_bytesToWrite); QString header = _nickname + ";" + info.fileName() + ";" + size + "\n"; _socket->write(header.toLocal8Bit()); connect(_socket, SIGNAL(readyRead()), this, SLOT(receiveAck())); qDebug("[%s] Sending header...", metaObject()->className()); } void ClientFileSendDialog::receiveAck() { QString ack = QString::fromUtf8(_socket->readLine()); if (ack != "ok\n") { _error = true; _reason = tr("Receiver declined"); qDebug("[%s] Received nack!", metaObject()->className()); close(); return; } qDebug("[%s] Received ack.", metaObject()->className()); disconnect(_socket, SIGNAL(readyRead()), this, SLOT(receiveAck())); connect(_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(sendFile())); show(); qDebug("[%s] Sending file...", metaObject()->className()); sendFile(); } void ClientFileSendDialog::sendFile() { if (_bytesToWrite == 0) { qDebug("[%s] Transfer completed.", metaObject()->className()); close(); // finished } else { qint64 bytesWritten = _socket->write(_file->read(1024)); // data left _bytesToWrite -= bytesWritten; progressBar->setValue(progressBar->value() + bytesWritten/div); labelA->setText(formatSize(progressBar->value()*div)); } } void ClientFileSendDialog::close() { if (!_isMulticast) { if (_file && _file->isOpen()) { _file->close(); qDebug("[%s] File closed.", metaObject()->className()); } if (_socket && _socket->isOpen()) { disconnect(_socket, SIGNAL(readyRead()), this, SLOT(receiveAck())); disconnect(_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(sendFile())); disconnect(_socket, SIGNAL(disconnected()), this, SLOT(close())); disconnect(_socket, SIGNAL(connected()), this, SLOT(sendHeader())); disconnect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError))); _socket->disconnectFromHost(); qDebug("[%s] Connection closed.", metaObject()->className()); } } disconnect(cancelButton, SIGNAL(clicked()), this, SLOT(canceled())); if (!_error) { accept(); QMessageBox::information(0, tr("PVS - File Transfer"), tr("File Transfer complete.")); } else { reject(); QMessageBox::warning(0, tr("PVS - File Transfer"), tr("File Transfer canceled: %1").arg(_reason)); } } void ClientFileSendDialog::canceled() { if(_isMulticast) { _ifaceDBus->cancelOutgoingMulticastTransfer(_transferID); } _error = true; _reason = tr("You clicked 'Cancel'"); close(); } void ClientFileSendDialog::error(QAbstractSocket::SocketError error) { _error = true; _reason = tr("Socket Error"); qDebug("[%s] Socket error: %i", metaObject()->className(), error); close(); } QString ClientFileSendDialog::formatSize(qint64 size) { if (size >= 1000000000) // GB return QString("%1GB").arg((qreal)size / 1000000000, 0, 'f',1); else if (size >= 1000000) // MB return QString("%1MB").arg((qreal)size / 1000000, 0, 'f',1); else if (size >= 1000) // KB return QString("%1KB").arg((qreal)size / 1000, 0, 'f',1); else // B return QString("%1B").arg((qreal)size, 0, 'f',1); } void ClientFileSendDialog::multicastTransferStarted(qulonglong transferID) { qDebug() << "multicastTransferStarted(" << transferID << ")"; if (transferID != _transferID) { return; } labelStatus->setText("Started"); } void ClientFileSendDialog::multicastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of) { qDebug() << "multicastTransferProgress(" << transferID << bytes << of << ")"; if (transferID != _transferID) { return; } if(bytes < of) { labelStatus->setText("Transferring"); progressBar->setRange(0, of); progressBar->setValue(bytes); } else { labelStatus->setText("Waiting to finish"); progressBar->setRange(0, 0); } labelA->setText(formatSize(bytes)); labelB->setText(formatSize(of)); } void ClientFileSendDialog::multicastTransferFinished(quint64 transferID) { qDebug() << "multicastTransferFinished(" << transferID << ")"; qDebug("[%s] MulticastTransfer finished", metaObject()->className()); close(); } void ClientFileSendDialog::multicastTransferFailed(quint64 transferID, QString const& reason) { qDebug() << "multicastTransferFailed(" << transferID << reason << ")"; _error = true; _reason = reason; close(); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ClientNicklistDialog::ClientNicklistDialog(QWidget *parent) : QDialog(parent) { setupUi(this); // connect to D-Bus and get interface QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject("/nicklist", this); dbus.registerService("org.openslx.pvsgui"); _ifaceDBus = new OrgOpenslxPvsInterface("org.openslx.pvs", "/", dbus, this); // get available nicknames QDBusPendingReply reply = _ifaceDBus->chat_getNicknames(); reply.waitForFinished(); QStringList nicknames = reply.value(); if (!reply.isValid() || nicknames.isEmpty()) // DBus Error, nicknames qDebug("[%s] D-Bus ERROR, no nicknames available!", metaObject()->className()); listWidget->addItems(nicknames); listWidget->setCurrentRow(0); connect(sendToAllCheckBox, SIGNAL(stateChanged(int)), SLOT(sendToAllStateChanged(int))); sendToAllCheckBox->setCheckState(Qt::Unchecked); _isSendToAll = false; } void ClientNicklistDialog::sendToAllStateChanged(int state) { if (state) { listWidget->setEnabled(false); _isSendToAll = true; } else { listWidget->setEnabled(true); _isSendToAll = false; } } ClientNicklistDialog::~ClientNicklistDialog() { } QString ClientNicklistDialog::getNick() { return listWidget->currentItem()->text(); }