commit 40a3dd027f51effaed89cf66e661b30cb3f14719 Author: Oliver Groß Date: Mon Apr 20 13:29:36 2009 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..79830f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +Makefile +Doxyfile +*.kdevelop +*.kdevelop.pcs +*.kdevses +*~ +mumodel +build +ui diff --git a/cconnectiondialog.cpp b/cconnectiondialog.cpp new file mode 100644 index 0000000..1681829 --- /dev/null +++ b/cconnectiondialog.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "cconnectiondialog.h" +#include +#include +#include +#include +#include +#include +#include + +namespace qftrans { + CConnectionDialog::CConnectionDialog(QWidget * parent, Qt::WindowFlags f) : QDialog(parent, f) { + setupUi(); + } + + CConnectionDialog::~CConnectionDialog() { + } + + bool CConnectionDialog::execute(bool isServer) { + m_HostEdit->setEnabled(!isServer); + m_HostLabel->setEnabled(!isServer); + + if (m_HostEdit->text().isEmpty()) + m_HostEdit->setText("localhost"); + return exec(); + } + + QString CConnectionDialog::host() { return m_HostEdit->text(); } + quint16 CConnectionDialog::port() { return (quint16)m_PortEdit->value(); } + + inline void CConnectionDialog::setupUi() { + m_HostEdit = new QLineEdit(this); + m_HostLabel = new QLabel(tr("&Host"), this); + m_HostLabel->setBuddy(m_HostEdit); + + m_PortEdit = new QSpinBox(this); + m_PortEdit->setMinimum(0); + m_PortEdit->setMaximum(65535); + m_PortEdit->setValue(8899); + m_PortLabel = new QLabel(tr("&Port"), this); + m_PortLabel->setBuddy(m_PortLabel); + + QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); + + QFormLayout * formLayout = new QFormLayout(); + formLayout->addRow(m_HostLabel, m_HostEdit); + formLayout->addRow(m_PortLabel, m_PortEdit); + + QVBoxLayout * mainLayout = new QVBoxLayout(); + mainLayout->addLayout(formLayout); + mainLayout->addWidget(buttonBox); + + setLayout(mainLayout); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(checkHost())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + } + + void CConnectionDialog::checkHost() { + if (m_HostEdit->isEnabled()) { + if (m_HostEdit->text().isEmpty()) + QMessageBox::warning(this, tr("Invalid host"), tr("You have to specify a host name/address.")); + else + accept(); + } + else + accept(); + } +} diff --git a/cconnectiondialog.h b/cconnectiondialog.h new file mode 100644 index 0000000..f1b05ab --- /dev/null +++ b/cconnectiondialog.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef QFTRANSCCONNECTIONDIALOG_H +#define QFTRANSCCONNECTIONDIALOG_H + +#include + +class QLineEdit; +class QSpinBox; +class QLabel; + +namespace qftrans { + /** + @author Oliver Groß + */ + class CConnectionDialog : public QDialog { + Q_OBJECT + public: + CConnectionDialog(QWidget * parent = 0, Qt::WindowFlags f = 0); + ~CConnectionDialog(); + + bool execute(bool isServer); + + QString host(); + quint16 port(); + private: + QLabel * m_HostLabel; + QLabel * m_PortLabel; + QLineEdit * m_HostEdit; + QSpinBox * m_PortEdit; + + inline void setupUi(); + private slots: + void checkHost(); + }; +} + +#endif diff --git a/common.h b/common.h new file mode 100644 index 0000000..dc638fc --- /dev/null +++ b/common.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include + +#ifndef QFTRANS_COMMON_H +#define QFTRANS_COMMON_H + +#define RAWPACKET_SIZE 9 +#define DATA_BLOCK_SIZE 256 +#define DESCRIPTOR_STRING "QFTH" + +namespace qftrans { + enum TransferStatus {TS_WAITING = 0, TS_ANNOUNCING = 1, TS_TRANSFERING = 2, TS_CLOSING = 3, TS_FINISHED = 4}; + enum HeaderType {HT_ID = 0, HT_SIZE = 1, HT_NAME = 2, HT_DATA = 3, HT_CLOSE = 4, HT_ACK = 5, HT_CANCEL = 6}; + + struct TransferData { + QString fileDir; + QString fileName; + TransferStatus status; + bool localFile; + qint64 size; + qint64 transfered; + HeaderType lastHeader; + }; + + struct TransferHeader { + char descriptor[5]; + quint8 type; + quint32 id; + quint32 length; + TransferHeader() { + descriptor[0] = 'Q'; + descriptor[1] = 'F'; + descriptor[2] = 'T'; + descriptor[3] = 'H'; + descriptor[4] = 0; + } + }; + + typedef QList QTransferDataList; + typedef QHash QTransferDataHash; + typedef QHash QTransferIdHash; +} + +#endif diff --git a/crecievethread.cpp b/crecievethread.cpp new file mode 100644 index 0000000..f2ac6bb --- /dev/null +++ b/crecievethread.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "crecievethread.h" +#include +#include +#include +#include +#include + +namespace qftrans { + CRecieveThread::CRecieveThread(QString & destinationDir, QTcpSocket * socket, QObject * parent) : + QThread(parent), m_Socket(socket), m_DestinationDir(destinationDir) + { + } + + CRecieveThread::~CRecieveThread() { + } + + void CRecieveThread::setDestinationDir(QString & value) { + m_DestinationDir = value; + } + + // TODO removeDownload(...) + void CRecieveThread::removeDownload(TransferData * /*data*/) { + + } + + void CRecieveThread::readData() { + QDataStream in(m_Socket); + in.setVersion(QDataStream::Qt_4_0); + + if (m_Socket->bytesAvailable() >= (qint64)(sizeof(TransferHeader))) { + in.readRawData((char *)(&m_LastHeader), sizeof(TransferHeader)); + m_LastHeaderValid = (qstrcmp(m_LastHeader.descriptor, DESCRIPTOR_STRING) == 0); + + qDebug() << "got header" << m_LastHeader.type; + if (m_LastHeaderValid) + qDebug() << "valid"; + else + qDebug() << "invalid"; + qDebug() << "id" << m_LastHeader.id; + qDebug() << "length" << m_LastHeader.length; + } + else + m_LastHeaderValid = false; + + TransferData * target = NULL; + + while (m_LastHeaderValid) { + qDebug() << "available Bytes" << m_Socket->bytesAvailable(); + if (m_Socket->bytesAvailable() >= m_LastHeader.length) { + //look for existing id + if (m_LastHeader.type == HT_ACK) { + qDebug() << "got HT_ACK .. requesting to start upload"; + emit uploadStartRequest(m_LastHeader.id); + } + else { + if (m_DataDict.contains(m_LastHeader.id)) { + if (m_LastHeader.type == HT_CLOSE) { + //finished or canceled by peer + qDebug() << "download closed py peer"; + emit downloadRemoved(m_DataDict.take(m_LastHeader.id)); + } + else + //assign target + target = m_DataDict[m_LastHeader.id]; + } + //id not in dict + else if (m_LastHeader.type == HT_ID) { + //new file announced + qDebug() << "download announced"; + TransferData * newFile = new TransferData; + m_DataDict.insert(m_LastHeader.id, newFile); + newFile->transfered = 0; + newFile->size = 0; + newFile->localFile = false; + newFile->status = TS_WAITING; + newFile->fileDir = m_DestinationDir; + } + + //check valid target and data available + if (target && m_LastHeader.length) { + switch (m_LastHeader.type) { + case HT_ID: + target->status = TS_WAITING; + QFile::remove(target->fileDir + QDir::separator() + target->fileName); + emit downloadUpdated(target); + break; + case HT_SIZE: + qDebug() << "reading size.."; + in >> target->size; + qDebug() << "got size:" << target->size; + break; + case HT_NAME: + qDebug() << "reading filename.."; + in >> target->fileName; + qDebug() << "got filename:" << target->fileName; + qDebug() << "requesting to send HT_ACK"; + emit downloadAcknowledged(m_LastHeader.id); + break; + case HT_DATA: { +// qDebug() << "got data" << target->transfered << '/' << target->size; + if (target->status == TS_WAITING) { + target->status = TS_TRANSFERING; + emit downloadAdded(target); + } + + char * buffer = new char[m_LastHeader.length]; + in.readRawData(buffer, m_LastHeader.length); + +// QFile file(target->fileDir + QDir::separator() + target->fileName); +// file.open(QIODevice::WriteOnly | QIODevice::Append); +// QDataStream out(&file); +// out.writeRawData((const char *)(buffer), m_LastHeader.length); + target->transfered += m_LastHeader.length; + delete[] buffer; + + emit downloadUpdated(target); + } + break; + default: + in.skipRawData(m_LastHeader.length); + break; + } + } + } + } + + if (m_Socket->bytesAvailable() >= (qint64)(sizeof(TransferHeader))) { + in.readRawData((char *)(&m_LastHeader), sizeof(TransferHeader)); + m_LastHeaderValid = (qstrcmp(m_LastHeader.descriptor, DESCRIPTOR_STRING) == 0); + qDebug() << "got header" << m_LastHeader.type; + if (m_LastHeaderValid) + qDebug() << "valid"; + else + qDebug() << "invalid"; + qDebug() << "id" << m_LastHeader.id; + qDebug() << "length" << m_LastHeader.length; + } + else + m_LastHeaderValid = false; + } + } + + + void CRecieveThread::run() { + qDebug() << "start reciever thread"; + connect(m_Socket, SIGNAL(readyRead()), this, SLOT(readData())); + exec(); + disconnect(m_Socket, SIGNAL(readyRead()), this, SLOT(readData())); + } +} diff --git a/crecievethread.h b/crecievethread.h new file mode 100644 index 0000000..aa943a4 --- /dev/null +++ b/crecievethread.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef QFTRANS_CRECIEVETHREAD_H +#define QFTRANS_CRECIEVETHREAD_H + +#include +#include "common.h" + +class QTcpSocket; + +namespace qftrans { + /** + @author Oliver Groß + */ + class CRecieveThread : public QThread { + Q_OBJECT + public: + CRecieveThread(QString & destinationDir, QTcpSocket * socket, QObject * parent = 0); + ~CRecieveThread(); + signals: + void downloadAdded(TransferData * data); + void downloadRemoved(TransferData * data); + void downloadUpdated(TransferData * transfer); + + void downloadAcknowledged(quint32 id); + void uploadStartRequest(quint32 id); + public slots: + void setDestinationDir(QString & value); + + void removeDownload(TransferData * data); + protected slots: + void readData(); + protected: + QTcpSocket * m_Socket; + + QString m_DestinationDir; + + QTransferDataHash m_DataDict; + + TransferHeader m_LastHeader; + bool m_LastHeaderValid; + + void run(); + }; +} + +#endif diff --git a/csendthread.cpp b/csendthread.cpp new file mode 100644 index 0000000..3464e58 --- /dev/null +++ b/csendthread.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "csenderthread.h" +#include +#include +#include +#include + +namespace qftrans { + CSenderThread::CSenderThread(QTcpSocket * socket, QObject * parent) : QThread(parent), m_Socket(socket), m_IdCounter(0) { + } + + CSenderThread::~CSenderThread() { + } + + void CSenderThread::addUpload(TransferData * upload) { + if (!m_DataDict.contains(upload)) { + qDebug() << "adding upload"; + m_DataDict.insert(upload, m_IdCounter); + m_IdCounter++; + } + } + + void CSenderThread::removeUpload(TransferData * transfer) { + transfer->status = TS_CLOSING; + if (m_Socket->bytesToWrite() == 0) { + qDebug() << "executing upload"; + writeData(); + } + } + + void CSenderThread::startUpload(TransferData * upload) { + if (m_DataDict.contains(upload) && upload->status == TS_WAITING) { + qDebug() << "scheduling upload for announce"; + upload->status = TS_ANNOUNCING; + if (m_Socket->bytesToWrite() == 0) { + qDebug() << "executing upload"; + writeData(); + } + } + } + + void CSenderThread::startUploadData(quint32 id) { + TransferData * transfer = m_DataDict.key(id); + if (transfer) { + qDebug() << "scheduling upload for transfer"; + transfer->status = TS_TRANSFERING; + if (m_Socket->bytesToWrite() == 0) { + qDebug() << "executing upload"; + writeData(); + } + } + } + + void CSenderThread::sendAcknowledge(quint32 id) { + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_0); + + TransferHeader header; +// header.descriptor = DESCRIPTOR_STRING; + + header.type = HT_ACK; + header.id = id; + header.length = 0; + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + qDebug() << "sending HT_ACK" << id; + + m_Socket->write(block); + } + + void CSendThread::writeData() { + if (m_DataDict.isEmpty()) + exit(); + + QByteArray block; + QDataStream out(&block, QIODevice::ReadWrite); + out.setVersion(QDataStream::Qt_4_0); + + QTransferDataList transfers = m_DataDict.keys(); + QTransferDataList::iterator i; + for (i = transfers.begin(); i != transfers.end(); ++i) { + block.clear(); + + TransferHeader header; +// header.descriptor = DESCRIPTOR_STRING; + switch ((*i)->status) { + case TS_ANNOUNCING: { + qDebug() << "announcing" << m_DataDict[*i]; + if ((*i)->transfered) + (*i)->transfered = 0; + + // ID header with no data + header.type = HT_ID; + header.id = m_DataDict[*i]; + header.length = 0; + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + //size header with size + header.type = HT_SIZE; + header.length = sizeof(qint64); + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + qDebug() << "sending size" << (*i)->size; + out << (*i)->size; + + //name header with file name + + qint64 oldPosition = block.size(); + + //fake write filename + out << (*i)->fileName; + out.device()->seek(oldPosition); + + header.type = HT_NAME; + header.length = quint32(block.size() - oldPosition); + +// qDebug() << "pos" << out.device()->pos() << block.size(); + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); +// qDebug() << "pos post header" << out.device()->pos() << block.size(); + +// qint64 oldPosition = block.size() - sizeof(quint32); + + qDebug() << "sending filename" << (*i)->fileName; + out << (*i)->fileName; +// qDebug() << "pos post filename" << out.device()->pos() << block.size(); +// out.device()->seek(oldPosition); +// qDebug() << "pos post seek" << out.device()->pos(); + + +/* qDebug() << "filename data length" << header.length; + out << header.length; + out.device()->seek(oldPosition); + out >> header.length; + qDebug() << "written length" << header.length; + out.device()->seek(block.size()); + qDebug() << "pos post length correct" << out.device()->pos() << block.size();*/ + + (*i)->status = TS_WAITING; + } + break; + case TS_TRANSFERING: { +// qDebug() << "transfering" << m_DataDict[*i]; + header.type = HT_DATA; + header.id = m_DataDict[*i]; + if ((*i)->transfered + DATA_BLOCK_SIZE < (*i)->size) + header.length = DATA_BLOCK_SIZE; + else + header.length = (*i)->size - (*i)->transfered; + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + char * buffer = new char[header.length]; + + QFile file((*i)->fileDir + (*i)->fileName); + file.open(QIODevice::ReadOnly); + QDataStream in(&file); + in.readRawData(buffer, header.length); + + out.writeRawData((const char *)(buffer), header.length); + (*i)->transfered += header.length; + delete[] buffer; + + if ((*i)->transfered >= (*i)->size) + (*i)->status = TS_CLOSING; + + emit uploadUpdated(*i); + } + break; + case TS_CLOSING: + header.type = HT_CLOSE; + header.id = m_DataDict[*i]; + header.length = 0; + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + (*i)->status = TS_FINISHED; + + m_DataDict.remove(*i); + break; + default: + break; + } + if (!block.isEmpty()) + m_Socket->write(block); + } + } + + void CSendThread::run() { + qDebug() << "start sender thread"; + connect(m_Socket, SIGNAL(bytesWritten(qint64)), this, SLOT(writeData())); + exec(); + disconnect(m_Socket, SIGNAL(bytesWritten(qint64)), this, SLOT(writeData())); + } + +} diff --git a/csendthread.h b/csendthread.h new file mode 100644 index 0000000..c3d19f8 --- /dev/null +++ b/csendthread.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef QFTRANS_CSENDERTHREAD_H +#define QFTRANS_CSENDERTHREAD_H + +#include +#include "common.h" + +class QTcpSocket; + +namespace qftrans { + + /** + @author Oliver Groß + */ + class CSendThread : public QThread + { + Q_OBJECT + public: + CSendThread(QTcpSocket * socket, QObject * parent = 0); + ~CSendThread(); + signals: + void uploadUpdated(TransferData * transfer); + public slots: + void addUpload(TransferData * transfer); + void removeUpload(TransferData * transfer); + void startUpload(TransferData * transfer); + void startUploadData(quint32 id); + + void sendAcknowledge(quint32 id); + protected slots: + void writeData(); + protected: + QTcpSocket * m_Socket; + + quint32 m_IdCounter; + QTransferIdHash m_DataDict; + + void run(); + }; + +} + +#endif diff --git a/csocketmanager.cpp b/csocketmanager.cpp new file mode 100644 index 0000000..3b2e0d3 --- /dev/null +++ b/csocketmanager.cpp @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "csocketmanager.h" +#include "cworker.h" +#include +#include + +namespace qftrans { + CSocketManager::CSocketManager(QObject * parent) : QObject(parent), + m_Server(NULL), m_Socket(NULL), m_Worker(NULL) { + } + + CSocketManager::~CSocketManager() { + if (m_Socket) + delete m_Socket; + } + + void CSocketManager::setup(bool server, quint16 port, QString & host) { + m_Host = host; + m_Port = port; + + if (server) { + m_Server = new QTcpServer(this); //TODO proper setup server + connect(m_Server, SIGNAL(newConnection()), this, SLOT(handleServerConnected())); + + m_Server->listen(QHostAddress(host), port); + } + else { + m_Socket = new QTcpSocket(); //TODO proper setup client + connect(m_Socket, SIGNAL(connected()), this, SLOT(handleClientConnected())); + connect(m_Socket, SIGNAL(disconnected()), this, SLOT(handleDisconnected())); + + m_Socket->connectToHost(host, port); + } + } + + void CSocketManager::handleServerConnected() { + qDebug() << "srv conn... thread id" << QThread::currentThreadId(); + + m_Socket = m_Server->nextPendingConnection(); + connect(m_Socket, SIGNAL(disconnected()), this, SLOT(handleDisconnected())); + + m_Worker = new CWorker(m_Socket, m_Socket); +// emit workerReady(m_Worker); + + emit connectionChanged(true); + + m_Server->close(); + } + + void CSocketManager::handleClientConnected() { + qDebug() << "clt conn... thread id" << QThread::currentThreadId(); + + m_Worker = new CWorker(m_Socket, m_Socket); + + emit connectionChanged(true); + } + + void CSocketManager::handleDisconnected() { + qDebug() << "clt/srv disconn.. thread id" << QThread::currentThreadId(); +/* m_Worker->deleteLater(); + m_Worker = NULL;*/ + +// if (m_ActAsServer) { +// m_Socket->deleteLater(); +// m_Socket = NULL; +// +// m_Server->listen(, 8899); +// emit connectionChanged(false); +// } +// else { + emit connectionChanged(false); + + if (m_Server) { + m_Socket->deleteLater(); + m_Socket = NULL; + + m_Worker = NULL; + + m_Server->listen(QHostAddress(m_Host), m_Port); + } + else + QThread::currentThread()->quit(); +// } + } +/* + void CSocketManager::connectToPeer() { + if (!m_Server && m_Socket) + m_Socket->connectToHost(host, port); + }*/ + +// void CSocketManager::disconnectFromPeer() { +// if (m_Socket) +// m_Socket->disconnectFromHost(); +// } +} diff --git a/csocketmanager.h b/csocketmanager.h new file mode 100644 index 0000000..c971608 --- /dev/null +++ b/csocketmanager.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef QFTRANSCSOCKETMANAGER_H +#define QFTRANSCSOCKETMANAGER_H + +#include + +class QTcpSocket; +class QTcpServer; + +namespace qftrans { + class CWorker; + + /** + @author Oliver Groß + */ + class CSocketManager : public QObject { + Q_OBJECT + public: + CSocketManager(QObject * parent = 0); + ~CSocketManager(); + + CWorker * worker() { return m_Worker; } + public slots: + void setup(bool server, quint16 port, QString & host); +/* void connectToPeer();*/ +// void disconnectFromPeer(); + signals: + void connectionChanged(bool connected); + protected: +// bool m_ActAsServer; + + QString m_Host; + quint16 m_Port; + + QTcpServer * m_Server; + QTcpSocket * m_Socket; + CWorker * m_Worker; + protected slots: + void handleServerConnected(); + void handleClientConnected(); + void handleDisconnected(); + }; +} + +#endif diff --git a/ctransfermanager.cpp b/ctransfermanager.cpp new file mode 100644 index 0000000..2319222 --- /dev/null +++ b/ctransfermanager.cpp @@ -0,0 +1,291 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "ctransfermanager.h" +#include "ctransferthread.h" +#include "csocketmanager.h" +#include "qtransferlistmodel.h" +#include "cworker.h" +#include "cconnectiondialog.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace qftrans { + CTransferManager::CTransferManager(QWidget * parent) : QMainWindow(parent), + m_ActAsServer(false), + m_Connected(false), + m_DestinationDir(QDir::homePath()), + m_TransferThread(NULL), + m_SelectedTransfer(NULL) + { + setupUi(); + } + + // TODO ~CTransferManager() + CTransferManager::~CTransferManager() { + cleanupThread(); + } + + void CTransferManager::openFile() { + QStringList files = QFileDialog::getOpenFileNames(this, + tr("Select one or more files to open"), + QDir::homePath()); + + TransferData * newUpload = NULL; + QFileInfo fileInfo; + + foreach (QString i, files) { + fileInfo.setFile(i); + newUpload = new TransferData; + newUpload->fileDir = fileInfo.absolutePath(); + newUpload->fileName = fileInfo.fileName(); + newUpload->size = fileInfo.size(); + newUpload->transfered = 0; + newUpload->status = TS_WAITING; + newUpload->lastHeader = HT_ID; + newUpload->localFile = true; + + addTransfer(newUpload); +// emit uploadAddRequested(newUpload); + } + } + + void CTransferManager::setDestinationDir() { + QString selectedDir = QFileDialog::getExistingDirectory(this, tr("Select a directory to save downloaded files"), m_DestinationDir); + if (!selectedDir.isEmpty()) { + m_DestinationDir = selectedDir; + + emit destinationDirChanged(m_DestinationDir); + } + } + + void CTransferManager::requestServerThread() { + if (!m_ConnectionDialog->execute(true)) + return; + + createThread(); + + m_ActAsServer = true; + m_TransferThread->start(); + } + + void CTransferManager::requestClientThread() { + if (!m_ConnectionDialog->execute(false)) + return; + + createThread(); + + m_ActAsServer = false; + m_TransferThread->start(); + } + + void CTransferManager::setupConnection() { + connect(m_TransferThread->socketManager(), SIGNAL(connectionChanged(bool)), this, SLOT(updateConnectionState(bool))); + + connect(this, SIGNAL(setupConnectionRequested(bool, quint16, QString &)), + m_TransferThread->socketManager(), SLOT(setup(bool, quint16, QString &))); + + QString host = m_ActAsServer ? "0.0.0.0" : m_ConnectionDialog->host(); + + emit setupConnectionRequested(m_ActAsServer, m_ConnectionDialog->port(), host); + } + + void CTransferManager::cleanupThread() { + if (m_TransferThread) { + m_TransferThread->quit(); + m_TransferThread->deleteLater(); + } + m_TransferThread = NULL; + statusBar()->showMessage(tr("connected")); + } + + void CTransferManager::restartSelected() { + emit uploadAddRequested(m_SelectedTransfer); + emit uploadRestartRequested(m_SelectedTransfer); + } + + void CTransferManager::cancelSelected() { + if (m_SelectedTransfer->localFile) + emit uploadRemoveRequested(m_SelectedTransfer); + else + emit downloadRemoveRequested(m_SelectedTransfer); + } + + inline void CTransferManager::setupUi() { + setWindowTitle(tr("QFTrans")); + + QMenu * currentMenu; + QToolBar * currentToolBar = addToolBar("Main"); + + currentMenu = menuBar()->addMenu(tr("&File")); + m_FileOpen = currentMenu->addAction(tr("&Open..."), this, SLOT(openFile())); + currentMenu->addAction(tr("Set destination directory..."), this, SLOT(setDestinationDir())); + + currentToolBar->addActions(currentMenu->actions()); + + currentMenu->addSeparator(); + currentMenu->addAction(tr("&Quit"), this, SLOT(close())); + + currentToolBar->addSeparator(); + + currentMenu = menuBar()->addMenu(tr("&Connection")); + m_ConnectionAsServer = currentMenu->addAction(tr("Act as &server"), this, SLOT(requestServerThread())); + m_ConnectionAsClient = currentMenu->addAction(tr("Act as &client"), this, SLOT(requestClientThread())); + currentMenu->addSeparator(); + m_ConnectionDisconnect = currentMenu->addAction(tr("&Disconnect"), this, SLOT(cleanupThread())); + + currentToolBar->addActions(currentMenu->actions()); + currentToolBar->addSeparator(); + + currentMenu = menuBar()->addMenu(tr("&Transfer")); + m_TransferRestartAction = currentMenu->addAction(tr("(Re)&Start"), this, SLOT(restartSelected())); + m_TransferCancelAction = currentMenu->addAction(tr("&Cancel"), this, SLOT(cancelSelected())); + + m_TransferRestartAction->setEnabled(false); + m_TransferCancelAction->setEnabled(false); + + currentToolBar->addActions(currentMenu->actions()); + + currentMenu = menuBar()->addMenu(tr("&Help")); + currentMenu->addAction(tr("&About"), this, SLOT(about())); + currentMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt())); + + m_TransferView = new QListView(this); + setCentralWidget(m_TransferView); + + QTransferListModel * listModel = new QTransferListModel(&m_Data); + + connect(this, SIGNAL(transferRemoved(TransferData *)), listModel, SIGNAL(layoutChanged())); + connect(this, SIGNAL(transferAdded(TransferData *)), listModel, SIGNAL(layoutChanged())); + + m_TransferView->setModel(listModel); + + connect(m_TransferView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(handleSelectionChanged(const QItemSelection &))); + + updateConnectionState(false); + + m_ConnectionDialog = new CConnectionDialog(this); + +} + + void CTransferManager::updateConnectionState(bool connected) { + m_Connected = connected; + if (connected) { + connectToWorker(m_TransferThread->socketManager()->worker()); + statusBar()->showMessage(tr("connected")); + } + else + statusBar()->showMessage(m_TransferThread ? tr("waiting") : tr("disconnected")); + + m_FileOpen->setEnabled(connected); + m_ConnectionAsServer->setDisabled(connected); + m_ConnectionAsClient->setDisabled(connected); + m_ConnectionDisconnect->setEnabled(connected); + } + + // TODO cleanup() + void CTransferManager::cleanup() { + } + + void CTransferManager::connectToWorker(CWorker * worker) { + worker->setDestinationDir(m_DestinationDir); + + connect(this, SIGNAL(destinationDirChanged(QString &)), worker, SLOT(setDestinationDir(QString &))); + + connect(worker, SIGNAL(downloadAdded(TransferData *)), this, SLOT(addTransfer(TransferData *))); + + QTransferListModel * model = dynamic_cast(m_TransferView->model()); + + connect(worker, SIGNAL(downloadUpdated(TransferData *)), model, SLOT(updateEntry(TransferData *))); + connect(worker, SIGNAL(uploadUpdated(TransferData *)), model, SLOT(updateEntry(TransferData *))); + + connect(this, SIGNAL(uploadAddRequested(TransferData *)), worker, SLOT(addUpload(TransferData *))); + connect(this, SIGNAL(uploadRestartRequested(TransferData *)), worker, SLOT(startUpload(TransferData *))); + connect(this, SIGNAL(uploadRemoveRequested(TransferData *)), worker, SLOT(removeUpload(TransferData *))); + + connect(this, SIGNAL(downloadRemoveRequested(TransferData *)), worker, SLOT(removeDownload(TransferData *))); + } + + void CTransferManager::handleSelectionChanged(const QItemSelection & selected) { + QModelIndexList selectedIndexes = selected.indexes(); + if (selectedIndexes.isEmpty()) + m_SelectedTransfer = NULL; + else + m_SelectedTransfer = static_cast(selectedIndexes[0].internalPointer()); + + m_TransferRestartAction->setEnabled(m_SelectedTransfer && m_SelectedTransfer->localFile); + m_TransferCancelAction->setEnabled(m_SelectedTransfer && m_SelectedTransfer->status != TS_FINISHED); + } + + void CTransferManager::addTransfer(TransferData * transfer) { + m_Data << transfer; + emit transferAdded(transfer); + } + + // TODO proper handle removing transfer + void CTransferManager::removeTransfer(TransferData * transfer) { + int index = m_Data.indexOf(transfer); + if (index != -1) { + m_Data.removeAt(index); + + emit transferRemoved(transfer); + } + delete transfer; + } + + inline void CTransferManager::createThread() { + cleanupThread(); + + qDebug() << "main thread" << QThread::currentThreadId(); + m_TransferThread = new CTransferThread(); + + connect(m_TransferThread, SIGNAL(terminated()), this, SLOT(cleanupThread())); +// connect(m_TransferThread, SIGNAL(finished()), m_TransferThread, SLOT(deleteLater())); + + connect(m_TransferThread, SIGNAL(socketManagerReady()), this, SLOT(setupConnection())); + } + + void CTransferManager::updateStatusBar(const QString & message) { + if (message.isEmpty()) { + if (m_Connected) + statusBar()->showMessage(tr("connected")); + else + statusBar()->showMessage(m_TransferThread ? tr("waiting") : tr("disconnected")); + } + } + + void CTransferManager::about() { + QMessageBox::about(this, tr("About"), tr("This is a still unfancy \"peer-to-peer\"-like file transfer tool.")); + } +} diff --git a/ctransfermanager.h b/ctransfermanager.h new file mode 100644 index 0000000..64c3190 --- /dev/null +++ b/ctransfermanager.h @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef QFTRANS_CTRANSFERMANAGER_H +#define QFTRANS_CTRANSFERMANAGER_H + +#include +#include "common.h" +#include +// #include + +class QListView; + +namespace qftrans { + class CTransferThread; + class CWorker; + class CConnectionDialog; + + /** + @author Oliver Groß + */ + class CTransferManager : public QMainWindow { + Q_OBJECT + public: + CTransferManager(QWidget * parent = 0); + ~CTransferManager(); + public slots: + void openFile(); + void setDestinationDir(); + + void requestServerThread(); + void requestClientThread(); + + void cleanupThread(); + + void restartSelected(); + void cancelSelected(); + signals: + void transferAdded(TransferData * transfer); + void transferRemoved(TransferData * transfer); + + void uploadAddRequested(TransferData * transfer); + void uploadRemoveRequested(TransferData * transfer); + void uploadRestartRequested(TransferData * transfer); + + void downloadRemoveRequested(TransferData * transfer); + + void destinationDirChanged(QString & value); + + void setupConnectionRequested(bool server, quint16 port, QString & host); + private: + CConnectionDialog * m_ConnectionDialog; + + QAction * m_FileOpen; + QAction * m_ConnectionAsServer; + QAction * m_ConnectionAsClient; + QAction * m_ConnectionDisconnect; + QAction * m_TransferRestartAction; + QAction * m_TransferCancelAction; + + bool m_ActAsServer; + bool m_Connected; + + QString m_DestinationDir; + + QTransferDataList m_Data; + + CTransferThread * m_TransferThread; + + QListView * m_TransferView; + + TransferData * m_SelectedTransfer; + + void setupUi(); + + void removeFileTransfer(TransferData * transfer); + void cleanup(); + + inline void createThread(); + private slots: + void setupConnection(); + void connectToWorker(CWorker * worker); + void updateConnectionState(bool connected); + void handleSelectionChanged(const QItemSelection & selected); + + void addTransfer(TransferData * transfer); + void removeTransfer(TransferData * transfer); + + void updateStatusBar(const QString & message); + void about(); + }; +} + +#endif diff --git a/ctransferthread.cpp b/ctransferthread.cpp new file mode 100644 index 0000000..054fef6 --- /dev/null +++ b/ctransferthread.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "ctransferthread.h" +#include "csocketmanager.h" +#include + +namespace qftrans { + CTransferThread::CTransferThread(QObject * parent) : QThread(parent), + m_SocketManager(NULL) { + } + + CTransferThread::~CTransferThread() { + if (m_SocketManager) + m_SocketManager->deleteLater(); +/* if (m_Socket) + delete m_Socket; + + if (m_Worker) + delete m_Worker; + + if (m_Server) + delete m_Server;*/ + } + + void CTransferThread::run() { + qDebug() << "start transfer thread" << currentThreadId(); + + m_SocketManager = new CSocketManager(); + emit socketManagerReady(); + + exec(); + + qDebug() << "exit transfer thread" << currentThreadId(); + + if (m_SocketManager) { + delete m_SocketManager; +// m_SocketManager->deleteLater(); + m_SocketManager = NULL; + } + +/* if (m_Worker) { + delete m_Worker; + m_Worker = NULL; + } + + if (m_Server) { + delete m_Server; + m_Server = NULL; + } + + if (m_Socket) { + delete m_Socket; + m_Socket = NULL; + }*/ + +// emit connectionChanged(false); + } +} diff --git a/ctransferthread.h b/ctransferthread.h new file mode 100644 index 0000000..0d5fc63 --- /dev/null +++ b/ctransferthread.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef QFTRANS_CTRANSFERTHREAD_H +#define QFTRANS_CTRANSFERTHREAD_H + +#include +#include "common.h" + +namespace qftrans { + class CSocketManager; + + /** + @author Oliver Groß + */ + class CTransferThread : public QThread { + Q_OBJECT + public: + CTransferThread(QObject * parent = 0); + + ~CTransferThread(); + +// CWorker * worker() { return m_Worker; } + CSocketManager * socketManager() { return m_SocketManager; } + signals: + void socketManagerReady(); + protected: + CSocketManager * m_SocketManager; + + void run(); + }; +} + +#endif diff --git a/cworker.cpp b/cworker.cpp new file mode 100644 index 0000000..1481bf2 --- /dev/null +++ b/cworker.cpp @@ -0,0 +1,425 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "cworker.h" +#include +#include +#include +#include +#include + +namespace qftrans { + CWorker::CWorker(QTcpSocket * socket, QObject * parent) : QObject(parent), m_Socket(socket), m_IdCounter(0) { + connect(m_Socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError))); + connect(m_Socket, SIGNAL(bytesWritten(qint64)), this, SLOT(writeData()), Qt::QueuedConnection); + connect(m_Socket, SIGNAL(readyRead()), this, SLOT(readData()), Qt::QueuedConnection); + qDebug() << "worker created"; + } + + CWorker::~CWorker() { + qDebug() << "worker removed"; + } + + //TODO addUpload + void CWorker::addUpload(TransferData * transfer) { + if (!m_UploadDict.contains(transfer)) { + qDebug() << "adding upload"; + transfer->status = TS_WAITING; + m_UploadDict.insert(transfer, m_IdCounter); + m_IdCounter++; + } + } + //TODO removeUpload + void CWorker::removeUpload(TransferData * transfer) { + if (m_UploadDict.contains(transfer) && transfer->status != TS_WAITING) { + transfer->status = TS_CLOSING; + if (m_Socket->bytesToWrite() == 0) { + qDebug() << "executing upload"; + writeData(); + } + } + } + //TODO startUpload + void CWorker::startUpload(TransferData * transfer) { + if (m_UploadDict.contains(transfer) && transfer->status == TS_WAITING) { + qDebug() << "scheduling upload for announce"; + transfer->status = TS_ANNOUNCING; + if (m_Socket->bytesToWrite() == 0) { + qDebug() << "executing upload"; + writeData(); + } + } + } + + void CWorker::startUploadData(quint32 id) { + TransferData * transfer = m_UploadDict.key(id); + if (transfer) { + qDebug() << "scheduling upload for transfer"; + transfer->status = TS_TRANSFERING; + if (m_Socket->bytesToWrite() == 0) { + qDebug() << "executing upload"; + writeData(); + } + } + } + + void CWorker::sendAcknowledge(quint32 id) { + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_0); + + TransferHeader header; +// header.descriptor = DESCRIPTOR_STRING; + + header.type = HT_ACK; + header.id = id; + header.length = 0; + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + qDebug() << "sending HT_ACK" << id; + + m_Socket->write(block); + qDebug() << "sent HT_ACK" << id; + } + + //TODO CTransferThread::removeDownload + void CWorker::removeDownload(TransferData * /*transfer*/) { + + } + + void CWorker::setDestinationDir(QString & value) { + m_DestinationDir = value; + } + + void CWorker::writeData() { + if (m_UploadDict.isEmpty()) { + qDebug() << "no more uploads."; + return; + } + + QByteArray block; + QDataStream out(&block, QIODevice::ReadWrite); + out.setVersion(QDataStream::Qt_4_0); + + QTransferDataList transfers = m_UploadDict.keys(); + QTransferDataList::iterator i; + for (i = transfers.begin(); i != transfers.end(); ++i) { + block.clear(); + + TransferHeader header; + switch ((*i)->status) { + case TS_ANNOUNCING: { + qDebug() << "announcing" << m_UploadDict[*i]; + if ((*i)->transfered) + (*i)->transfered = 0; + + // ID header with no data + header.type = HT_ID; + header.id = m_UploadDict[*i]; + header.length = 0; + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + //size header with size + header.type = HT_SIZE; + header.length = sizeof(qint64); + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + qDebug() << "sending size" << (*i)->size; + out << (*i)->size; + + //name header with file name + + qint64 oldPosition = block.size(); + + //fake write filename + out << (*i)->fileName; + out.device()->seek(oldPosition); + + header.type = HT_NAME; + header.length = quint32(block.size() - oldPosition); + +// qDebug() << "pos" << out.device()->pos() << block.size(); + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); +// qDebug() << "pos post header" << out.device()->pos() << block.size(); + +// qint64 oldPosition = block.size() - sizeof(quint32); + + qDebug() << "sending filename" << (*i)->fileName; + out << (*i)->fileName; +// qDebug() << "pos post filename" << out.device()->pos() << block.size(); +// out.device()->seek(oldPosition); +// qDebug() << "pos post seek" << out.device()->pos(); + + +/* qDebug() << "filename data length" << header.length; + out << header.length; + out.device()->seek(oldPosition); + out >> header.length; + qDebug() << "written length" << header.length; + out.device()->seek(block.size()); + qDebug() << "pos post length correct" << out.device()->pos() << block.size();*/ + + (*i)->status = TS_WAITING; + } + break; + case TS_TRANSFERING: { +// qDebug() << "transfering" << m_DataDict[*i]; + header.type = HT_DATA; + header.id = m_UploadDict[*i]; + if ((*i)->transfered + DATA_BLOCK_SIZE < (*i)->size) + header.length = DATA_BLOCK_SIZE; + else + header.length =(*i)->size - (*i)->transfered; + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + char * buffer = new char[header.length]; + + QFile file((*i)->fileDir + QDir::separator() + (*i)->fileName); + file.open(QIODevice::ReadOnly); + QDataStream in(&file); + if (in.skipRawData((*i)->transfered) == -1) { + qDebug() << "filename" << file.fileName(); + qDebug() << "skip error"; + qDebug() << "file size" << (*i)->size; + qDebug() << "transfered bytes" << (*i)->transfered; + } +// in.device()->seek((*i)->transfered); + if (in.readRawData(buffer, header.length) == -1) { + qDebug() << "Read error"; + qDebug() << "buffer size" << header.length; + qDebug() << "file size" << (*i)->size; + qDebug() << "transfered bytes" << (*i)->transfered; + } + + out.writeRawData((const char *)(buffer), header.length); + (*i)->transfered += header.length; + delete[] buffer; + + if ((*i)->transfered >= (*i)->size) + (*i)->status = TS_CLOSING; + + if ((*i)->transfered > (*i)->size) { + qDebug() << "overread?"; + qDebug() << "file size" << (*i)->size; + qDebug() << "transfered bytes" << (*i)->transfered; + } + + emit uploadUpdated(*i); + } + break; + case TS_CLOSING: + header.type = HT_CLOSE; + header.id = m_UploadDict[*i]; + header.length = 0; + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + (*i)->status = TS_FINISHED; + + m_UploadDict.remove(*i); + break; + default: + break; + } + if (!block.isEmpty()) + m_Socket->write(block); + } + } + + void CWorker::readHeader(QDataStream & in) { + if (m_Socket->bytesAvailable() >= (qint64)(sizeof(TransferHeader))) { + in.readRawData((char *)(&m_LastHeader), sizeof(TransferHeader)); + m_LastHeaderValid = (qstrcmp(m_LastHeader.descriptor, DESCRIPTOR_STRING) == 0); + + if (!m_LastHeaderValid) { + qDebug() << "got invalid header data"; + qDebug() << "available Bytes" << m_Socket->bytesAvailable(); + } + } + else + m_LastHeaderValid = false; + } + + void CWorker::readData() { + QDataStream in(m_Socket); + in.setVersion(QDataStream::Qt_4_0); + + if (!m_LastHeaderValid) + readHeader(in); + + TransferData * target = NULL; + + while (m_LastHeaderValid && m_Socket->bytesAvailable() >= m_LastHeader.length) { +// qDebug() << "available Bytes" << m_Socket->bytesAvailable() << "length" << m_LastHeader.length; + + //look for existing id + if (m_LastHeader.type == HT_ACK) { + qDebug() << "got HT_ACK .. scheduling upload to start"; + startUploadData(m_LastHeader.id); + } + else { + if (m_DownloadDict.contains(m_LastHeader.id)) { + if (m_LastHeader.type == HT_CLOSE) { + //finished or canceled by peer + + qDebug() << "download closed py peer"; + TransferData * closed = m_DownloadDict.take(m_LastHeader.id); + closed->status = TS_FINISHED; + emit downloadRemoved(closed); + } + else + //assign target + target = m_DownloadDict[m_LastHeader.id]; + } + //id not in dict + else if (m_LastHeader.type == HT_ID) { + //new file announced + qDebug() << "download announced"; + TransferData * newFile = new TransferData; + m_DownloadDict.insert(m_LastHeader.id, newFile); + newFile->transfered = 0; + newFile->size = 0; + newFile->localFile = false; + newFile->status = TS_WAITING; + newFile->fileDir = m_DestinationDir; + } + + //check valid target and data available + if (target && m_LastHeader.length) { + switch (m_LastHeader.type) { + case HT_ID: + target->status = TS_WAITING; +// QFile::remove(target->fileDir + QDir::separator() + target->fileName); + emit downloadUpdated(target); + break; + case HT_SIZE: + qDebug() << "reading size.."; + in >> target->size; + qDebug() << "got size:" << target->size; + break; + case HT_NAME: + qDebug() << "reading filename.."; + in >> target->fileName; + qDebug() << "got filename:" << target->fileName; + qDebug() << "trying to send HT_ACK"; + + //TODO better do this async + sendAcknowledge(m_LastHeader.id); + break; + case HT_DATA: { +// qDebug() << "got data" << target->transfered << '/' << target->size; + QFile file(target->fileDir + QDir::separator() + target->fileName); + if (target->status == TS_WAITING) { + target->status = TS_TRANSFERING; + emit downloadAdded(target); + file.open(QIODevice::WriteOnly | QIODevice::Truncate); + } + else if (target->status == TS_TRANSFERING) + file.open(QIODevice::WriteOnly | QIODevice::Append); + else { + in.skipRawData(m_LastHeader.length); + qDebug() << "transfer not ready to recieve data. status is" << target->status; + qDebug() << "skipping" << m_LastHeader.length << "bytes of data"; + break; + } + + char * buffer = new char[m_LastHeader.length]; + if (in.readRawData(buffer, m_LastHeader.length) == -1) { + qDebug() << "error reading from socket"; + qDebug() << "length" << m_LastHeader.length; + qDebug() << "available Bytes" << m_Socket->bytesAvailable(); + } + + QDataStream out(&file); + out.writeRawData((const char *)(buffer), m_LastHeader.length); + target->transfered += m_LastHeader.length; + delete[] buffer; + + emit downloadUpdated(target); + } + break; + default: + in.skipRawData(m_LastHeader.length); + qDebug() << "unknown header"; + qDebug() << "skipping" << m_LastHeader.length << "bytes of data"; + break; + } + } + } + + readHeader(in); + } + } + + void CWorker::writePing() { + qDebug() << "write ping... thread id" << QThread::currentThreadId(); + if (m_IdCounter > 10) + return; + + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_0); + + TransferHeader header; + + header.type = HT_ACK; + header.id = m_IdCounter++; + header.length = 0; + + out.writeRawData((const char *)(&header), sizeof(TransferHeader)); + + qDebug() << "sending Ping..." << header.id; + if (m_Socket->write(block) == -1) + qDebug() << "error"; + else + qDebug() << "done"; + } + + void CWorker::readPing() { + qDebug() << "read ping... thread id" << QThread::currentThreadId(); + + QDataStream in(m_Socket); + in.setVersion(QDataStream::Qt_4_0); + + do { + if (m_Socket->bytesAvailable() >= (qint64)(sizeof(TransferHeader))) { + in.readRawData((char *)(&m_LastHeader), sizeof(TransferHeader)); + m_LastHeaderValid = (qstrcmp(m_LastHeader.descriptor, DESCRIPTOR_STRING) == 0); + + if (m_LastHeaderValid) + qDebug() << "got valid ping" << m_LastHeader.id; + else + qDebug() << "got invalid ping" << m_LastHeader.id; + + writePing(); + } + else + m_LastHeaderValid = false; + } + while (m_LastHeaderValid); + } + + void CWorker::error(QAbstractSocket::SocketError) { + qDebug() << m_Socket->errorString(); + } +} diff --git a/cworker.h b/cworker.h new file mode 100644 index 0000000..6557462 --- /dev/null +++ b/cworker.h @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef QFTRANSCWORKER_H +#define QFTRANSCWORKER_H + +#include +#include +#include +#include "common.h" + +namespace qftrans { + /** + @author Oliver Groß + */ + class CWorker : public QObject { + Q_OBJECT + public: + CWorker(QTcpSocket * socket, QObject * parent = 0); + ~CWorker(); + signals: + void uploadAdded(TransferData * transfer); + void uploadRemoved(TransferData * transfer); + void uploadUpdated(TransferData * transfer); + + void downloadAdded(TransferData * transfer); + void downloadRemoved(TransferData * transfer); + void downloadUpdated(TransferData * transfer); + public slots: + void addUpload(TransferData * transfer); + void removeUpload(TransferData * transfer); + void startUpload(TransferData * transfer); + + void removeDownload(TransferData * data); + + void setDestinationDir(QString & value); + void writePing(); + protected slots: + void writeData(); + void readData(); + + void readPing(); + protected: + void readHeader(QDataStream & in); + + void startUploadData(quint32 id); + void sendAcknowledge(quint32 id); + + QTcpSocket * m_Socket; + QString m_DestinationDir; + + quint32 m_IdCounter; + QTransferIdHash m_UploadDict; + + QTransferDataHash m_DownloadDict; + + TransferHeader m_LastHeader; + bool m_LastHeaderValid; + private slots: + void error(QAbstractSocket::SocketError socketError); + }; +} + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a849944 --- /dev/null +++ b/main.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include +#include +#include "ctransfermanager.h" +#include "common.h" +//#include "constants.h" + +using namespace qftrans; + +int main(int argc, char * argv[]) +{ + QApplication app(argc, argv); + qRegisterMetaType("QString&"); + +/* QString locale = QLocale::system().name(); + QTranslator translator; + translator.load(QString(UI_PATH_TRANSLATIONS "qcross_") + locale); + app.installTranslator(&translator);*/ +// app.setQuitOnLastWindowClosed(false); + +// { +// QDir workdir(UI_PATH_WORK); +// if (!workdir.exists()) { +// workdir.cdUp(); +// workdir.mkdir(UI_DIR_WORK); +// } +// } + + CTransferManager mainwin; + mainwin.show(); + return app.exec(); +} diff --git a/qftrans.pro b/qftrans.pro new file mode 100644 index 0000000..b980236 --- /dev/null +++ b/qftrans.pro @@ -0,0 +1,22 @@ +SOURCES += main.cpp \ +ctransfermanager.cpp \ +qtransferlistmodel.cpp \ + ctransferthread.cpp \ + cworker.cpp \ + csocketmanager.cpp \ + cconnectiondialog.cpp +HEADERS += ctransfermanager.h \ +qtransferlistmodel.h \ +common.h \ + ctransferthread.h \ + cworker.h \ + csocketmanager.h \ + cconnectiondialog.h +TEMPLATE = app + +QT += network + +CONFIG -= release + +CONFIG += debug + diff --git a/qtransferlistmodel.cpp b/qtransferlistmodel.cpp new file mode 100644 index 0000000..4db6e50 --- /dev/null +++ b/qtransferlistmodel.cpp @@ -0,0 +1,111 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "qtransferlistmodel.h" + +namespace qftrans { + QTransferListModel::QTransferListModel(QTransferDataList * data, QObject * parent) : QAbstractListModel(parent), m_Data(data) { + } + + QTransferListModel::~QTransferListModel() { + } + + int QTransferListModel::rowCount(const QModelIndex & /*parent*/) const { + if (m_Data) + return m_Data->size(); + else + return 0; + } + + QVariant QTransferListModel::data(const QModelIndex & index, int role) const { + if (role != Qt::DisplayRole) + return QVariant(); + + TransferData * data = static_cast(index.internalPointer()); + + if (!data) + return QVariant(); + + QString status; + + switch (data->status) { + case TS_WAITING: + status = tr("waiting"); + break; + case TS_ANNOUNCING: + status = tr("announcing to peer"); + break; + case TS_TRANSFERING: + status = data->localFile ? tr("Sending") : tr("Recieving"); + break; + case TS_CLOSING: + status = tr("closing down"); + break; + case TS_FINISHED: + status = (data->transfered == data->size) ? tr("finished") : tr("canceled"); + break; + default: + status = tr("unknown status"); + break; + } + + QString size; + unsigned int fancySize = data->size; + unsigned int siCount = 0; + + while (fancySize > 1000) { + fancySize /= 1000; + siCount++; + } + + size = QString::number(fancySize); + + switch (siCount) { + case 0: + break; + case 1: + size += 'K'; + break; + case 2: + size += 'M'; + break; + case 3: + size += 'G'; + break; + default: + size += "*10^" + QString::number(siCount); + break; + } + + size += 'B'; + + return (data->localFile ? "out #" : "in #") + QString::number(index.row()) + " [" + status + "] (" + + QString::number((unsigned int)((100.0 * data->transfered) / data->size)) + "%) " + + data->fileName + " (size: " + size + ")"; + } + + QModelIndex QTransferListModel::index (int row, int column, const QModelIndex & /*parent*/) const { + return createIndex(row, column, m_Data->at(row)); + } + + void QTransferListModel::updateEntry(TransferData * /*entry*/) { + // TODO proper tranfer entry update + emit layoutChanged(); + } +} diff --git a/qtransferlistmodel.h b/qtransferlistmodel.h new file mode 100644 index 0000000..6425f15 --- /dev/null +++ b/qtransferlistmodel.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2008 by Oliver Groß * + * z.o.gross@gmx.de * + * * + * 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 WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef QFTRANS_QTRANSFERLISTMODEL_H +#define QFTRANS_QTRANSFERLISTMODEL_H + +#include +#include "common.h" + +namespace qftrans { + /** + @author Oliver Groß + */ + class QTransferListModel : public QAbstractListModel { + Q_OBJECT + public: + QTransferListModel(QTransferDataList * data, QObject * parent = 0); + ~QTransferListModel(); + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + QModelIndex index (int row, int column = 0, const QModelIndex & parent = QModelIndex()) const; + public slots: + void updateEntry(TransferData * entry); + private: + QTransferDataList * m_Data; + }; +} + +#endif