diff options
Diffstat (limited to 'examples/network')
118 files changed, 12742 insertions, 0 deletions
diff --git a/examples/network/README b/examples/network/README new file mode 100644 index 0000000..23721df --- /dev/null +++ b/examples/network/README @@ -0,0 +1,40 @@ +Qt is provided with an extensive set of network classes to support both +client-based and server side network programming. + +These examples demonstrate the fundamental aspects of network programming +with Qt. + + +The example launcher provided with Qt can be used to explore each of the +examples in this directory. + +Documentation for these examples can be found via the Tutorial and Examples +link in the main Qt documentation. + + +Finding the Qt Examples and Demos launcher +========================================== + +On Windows: + +The launcher can be accessed via the Windows Start menu. Select the menu +entry entitled "Qt Examples and Demos" entry in the submenu containing +the Qt tools. + +On Mac OS X: + +For the binary distribution, the qtdemo executable is installed in the +/Developer/Applications/Qt directory. For the source distribution, it is +installed alongside the other Qt tools on the path specified when Qt is +configured. + +On Unix/Linux: + +The qtdemo executable is installed alongside the other Qt tools on the path +specified when Qt is configured. + +On all platforms: + +The source code for the launcher can be found in the demos/qtdemo directory +in the Qt package. This example is built at the same time as the Qt libraries, +tools, examples, and demonstrations. diff --git a/examples/network/blockingfortuneclient/blockingclient.cpp b/examples/network/blockingfortuneclient/blockingclient.cpp new file mode 100644 index 0000000..207ff5f --- /dev/null +++ b/examples/network/blockingfortuneclient/blockingclient.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtNetwork> + +#include "blockingclient.h" + +BlockingClient::BlockingClient(QWidget *parent) + : QDialog(parent) +{ + hostLabel = new QLabel(tr("&Server name:")); + portLabel = new QLabel(tr("S&erver port:")); + + hostLineEdit = new QLineEdit("Localhost"); + portLineEdit = new QLineEdit; + portLineEdit->setValidator(new QIntValidator(1, 65535, this)); + + hostLabel->setBuddy(hostLineEdit); + portLabel->setBuddy(portLineEdit); + + statusLabel = new QLabel(tr("This examples requires that you run the " + "Fortune Server example as well.")); + + getFortuneButton = new QPushButton(tr("Get Fortune")); + getFortuneButton->setDefault(true); + getFortuneButton->setEnabled(false); + + quitButton = new QPushButton(tr("Quit")); + + buttonBox = new QDialogButtonBox; + buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole); + buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); + + connect(hostLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(enableGetFortuneButton())); + connect(portLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(enableGetFortuneButton())); + connect(getFortuneButton, SIGNAL(clicked()), + this, SLOT(requestNewFortune())); + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); +//! [0] + connect(&thread, SIGNAL(newFortune(const QString &)), + this, SLOT(showFortune(const QString &))); +//! [0] //! [1] + connect(&thread, SIGNAL(error(int, const QString &)), + this, SLOT(displayError(int, const QString &))); +//! [1] + + QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget(hostLabel, 0, 0); + mainLayout->addWidget(hostLineEdit, 0, 1); + mainLayout->addWidget(portLabel, 1, 0); + mainLayout->addWidget(portLineEdit, 1, 1); + mainLayout->addWidget(statusLabel, 2, 0, 1, 2); + mainLayout->addWidget(buttonBox, 3, 0, 1, 2); + setLayout(mainLayout); + + setWindowTitle(tr("Blocking Fortune Client")); + portLineEdit->setFocus(); +} + +//! [2] +void BlockingClient::requestNewFortune() +{ + getFortuneButton->setEnabled(false); + thread.requestNewFortune(hostLineEdit->text(), + portLineEdit->text().toInt()); +} +//! [2] + +//! [3] +void BlockingClient::showFortune(const QString &nextFortune) +{ + if (nextFortune == currentFortune) { + requestNewFortune(); + return; + } +//! [3] + +//! [4] + currentFortune = nextFortune; + statusLabel->setText(currentFortune); + getFortuneButton->setEnabled(true); +} +//! [4] + +void BlockingClient::displayError(int socketError, const QString &message) +{ + switch (socketError) { + case QAbstractSocket::HostNotFoundError: + QMessageBox::information(this, tr("Blocking Fortune Client"), + tr("The host was not found. Please check the " + "host and port settings.")); + break; + case QAbstractSocket::ConnectionRefusedError: + QMessageBox::information(this, tr("Blocking Fortune Client"), + tr("The connection was refused by the peer. " + "Make sure the fortune server is running, " + "and check that the host name and port " + "settings are correct.")); + break; + default: + QMessageBox::information(this, tr("Blocking Fortune Client"), + tr("The following error occurred: %1.") + .arg(message)); + } + + getFortuneButton->setEnabled(true); +} + +void BlockingClient::enableGetFortuneButton() +{ + getFortuneButton->setEnabled(!hostLineEdit->text().isEmpty() + && !portLineEdit->text().isEmpty()); +} diff --git a/examples/network/blockingfortuneclient/blockingclient.h b/examples/network/blockingfortuneclient/blockingclient.h new file mode 100644 index 0000000..2bc0b80 --- /dev/null +++ b/examples/network/blockingfortuneclient/blockingclient.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BLOCKINGCLIENT_H +#define BLOCKINGCLIENT_H + +#include <QDialog> + +#include "fortunethread.h" + +QT_BEGIN_NAMESPACE +class QDialogButtonBox; +class QLabel; +class QLineEdit; +class QPushButton; +QT_END_NAMESPACE + +//! [0] +class BlockingClient : public QDialog +{ + Q_OBJECT + +public: + BlockingClient(QWidget *parent = 0); + +private slots: + void requestNewFortune(); + void showFortune(const QString &fortune); + void displayError(int socketError, const QString &message); + void enableGetFortuneButton(); + +private: + QLabel *hostLabel; + QLabel *portLabel; + QLineEdit *hostLineEdit; + QLineEdit *portLineEdit; + QLabel *statusLabel; + QPushButton *getFortuneButton; + QPushButton *quitButton; + QDialogButtonBox *buttonBox; + + FortuneThread thread; + QString currentFortune; +}; +//! [0] + +#endif diff --git a/examples/network/blockingfortuneclient/blockingfortuneclient.pro b/examples/network/blockingfortuneclient/blockingfortuneclient.pro new file mode 100644 index 0000000..fa146fa --- /dev/null +++ b/examples/network/blockingfortuneclient/blockingfortuneclient.pro @@ -0,0 +1,12 @@ +HEADERS = blockingclient.h \ + fortunethread.h +SOURCES = blockingclient.cpp \ + main.cpp \ + fortunethread.cpp +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/blockingfortuneclient +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS blockingfortuneclient.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/network/blockingfortuneclient +INSTALLS += target sources diff --git a/examples/network/blockingfortuneclient/fortunethread.cpp b/examples/network/blockingfortuneclient/fortunethread.cpp new file mode 100644 index 0000000..af1c612 --- /dev/null +++ b/examples/network/blockingfortuneclient/fortunethread.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtNetwork> + +#include "fortunethread.h" + +FortuneThread::FortuneThread(QObject *parent) + : QThread(parent), quit(false) +{ +} + +//! [0] +FortuneThread::~FortuneThread() +{ + mutex.lock(); + quit = true; + cond.wakeOne(); + mutex.unlock(); + wait(); +} +//! [0] + +//! [1] //! [2] +void FortuneThread::requestNewFortune(const QString &hostName, quint16 port) +{ +//! [1] + QMutexLocker locker(&mutex); + this->hostName = hostName; + this->port = port; +//! [3] + if (!isRunning()) + start(); + else + cond.wakeOne(); +} +//! [2] //! [3] + +//! [4] +void FortuneThread::run() +{ + mutex.lock(); +//! [4] //! [5] + QString serverName = hostName; + quint16 serverPort = port; + mutex.unlock(); +//! [5] + +//! [6] + while (!quit) { +//! [7] + const int Timeout = 5 * 1000; + + QTcpSocket socket; + socket.connectToHost(serverName, serverPort); +//! [6] //! [8] + + if (!socket.waitForConnected(Timeout)) { + emit error(socket.error(), socket.errorString()); + return; + } +//! [8] //! [9] + + while (socket.bytesAvailable() < (int)sizeof(quint16)) { + if (!socket.waitForReadyRead(Timeout)) { + emit error(socket.error(), socket.errorString()); + return; + } +//! [9] //! [10] + } +//! [10] //! [11] + + quint16 blockSize; + QDataStream in(&socket); + in.setVersion(QDataStream::Qt_4_0); + in >> blockSize; +//! [11] //! [12] + + while (socket.bytesAvailable() < blockSize) { + if (!socket.waitForReadyRead(Timeout)) { + emit error(socket.error(), socket.errorString()); + return; + } +//! [12] //! [13] + } +//! [13] //! [14] + + mutex.lock(); + QString fortune; + in >> fortune; + emit newFortune(fortune); +//! [7] //! [14] //! [15] + + cond.wait(&mutex); + serverName = hostName; + serverPort = port; + mutex.unlock(); + } +//! [15] +} diff --git a/examples/network/blockingfortuneclient/fortunethread.h b/examples/network/blockingfortuneclient/fortunethread.h new file mode 100644 index 0000000..c2fbe6f --- /dev/null +++ b/examples/network/blockingfortuneclient/fortunethread.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORTUNETHREAD_H +#define FORTUNETHREAD_H + +#include <QThread> +#include <QMutex> +#include <QWaitCondition> + +//! [0] +class FortuneThread : public QThread +{ + Q_OBJECT + +public: + FortuneThread(QObject *parent = 0); + ~FortuneThread(); + + void requestNewFortune(const QString &hostName, quint16 port); + void run(); + +signals: + void newFortune(const QString &fortune); + void error(int socketError, const QString &message); + +private: + QString hostName; + quint16 port; + QMutex mutex; + QWaitCondition cond; + bool quit; +}; +//! [0] + +#endif diff --git a/examples/network/blockingfortuneclient/main.cpp b/examples/network/blockingfortuneclient/main.cpp new file mode 100644 index 0000000..98982c1 --- /dev/null +++ b/examples/network/blockingfortuneclient/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "blockingclient.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + BlockingClient client; + client.show(); + return client.exec(); +} diff --git a/examples/network/broadcastreceiver/broadcastreceiver.pro b/examples/network/broadcastreceiver/broadcastreceiver.pro new file mode 100644 index 0000000..ad04e2c --- /dev/null +++ b/examples/network/broadcastreceiver/broadcastreceiver.pro @@ -0,0 +1,10 @@ +HEADERS = receiver.h +SOURCES = receiver.cpp \ + main.cpp +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/broadcastreceiver +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS broadcastreceiver.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/network/broadcastreceiver +INSTALLS += target sources diff --git a/examples/network/broadcastreceiver/main.cpp b/examples/network/broadcastreceiver/main.cpp new file mode 100644 index 0000000..f815729 --- /dev/null +++ b/examples/network/broadcastreceiver/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "receiver.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Receiver receiver; + receiver.show(); + return receiver.exec(); +} diff --git a/examples/network/broadcastreceiver/receiver.cpp b/examples/network/broadcastreceiver/receiver.cpp new file mode 100644 index 0000000..ea9ab14 --- /dev/null +++ b/examples/network/broadcastreceiver/receiver.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtNetwork> + +#include "receiver.h" + +Receiver::Receiver(QWidget *parent) + : QDialog(parent) +{ + statusLabel = new QLabel(tr("Listening for broadcasted messages")); + quitButton = new QPushButton(tr("&Quit")); + +//! [0] + udpSocket = new QUdpSocket(this); + udpSocket->bind(45454); +//! [0] + +//! [1] + connect(udpSocket, SIGNAL(readyRead()), + this, SLOT(processPendingDatagrams())); +//! [1] + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); + + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(1); + buttonLayout->addWidget(quitButton); + buttonLayout->addStretch(1); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(statusLabel); + mainLayout->addLayout(buttonLayout); + setLayout(mainLayout); + + setWindowTitle(tr("Broadcast Receiver")); +} + +void Receiver::processPendingDatagrams() +{ +//! [2] + while (udpSocket->hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(udpSocket->pendingDatagramSize()); + udpSocket->readDatagram(datagram.data(), datagram.size()); + statusLabel->setText(tr("Received datagram: \"%1\"") + .arg(datagram.data())); + } +//! [2] +} diff --git a/examples/network/broadcastreceiver/receiver.h b/examples/network/broadcastreceiver/receiver.h new file mode 100644 index 0000000..9939cbe --- /dev/null +++ b/examples/network/broadcastreceiver/receiver.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RECEIVER_H +#define RECEIVER_H + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QLabel; +class QPushButton; +class QUdpSocket; +QT_END_NAMESPACE + +class Receiver : public QDialog +{ + Q_OBJECT + +public: + Receiver(QWidget *parent = 0); + +private slots: + void processPendingDatagrams(); + +private: + QLabel *statusLabel; + QPushButton *quitButton; + QUdpSocket *udpSocket; +}; + +#endif diff --git a/examples/network/broadcastsender/broadcastsender.pro b/examples/network/broadcastsender/broadcastsender.pro new file mode 100644 index 0000000..b300a50 --- /dev/null +++ b/examples/network/broadcastsender/broadcastsender.pro @@ -0,0 +1,10 @@ +HEADERS = sender.h +SOURCES = sender.cpp \ + main.cpp +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/broadcastsender +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS broadcastsender.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/network/broadcastsender +INSTALLS += target sources diff --git a/examples/network/broadcastsender/main.cpp b/examples/network/broadcastsender/main.cpp new file mode 100644 index 0000000..85613c5 --- /dev/null +++ b/examples/network/broadcastsender/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "sender.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Sender sender; + sender.show(); + return sender.exec(); +} diff --git a/examples/network/broadcastsender/sender.cpp b/examples/network/broadcastsender/sender.cpp new file mode 100644 index 0000000..a74beae --- /dev/null +++ b/examples/network/broadcastsender/sender.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtNetwork> + +#include "sender.h" + +Sender::Sender(QWidget *parent) + : QDialog(parent) +{ + statusLabel = new QLabel(tr("Ready to broadcast datagrams on port 45454")); + + startButton = new QPushButton(tr("&Start")); + quitButton = new QPushButton(tr("&Quit")); + + buttonBox = new QDialogButtonBox; + buttonBox->addButton(startButton, QDialogButtonBox::ActionRole); + buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); + + timer = new QTimer(this); +//! [0] + udpSocket = new QUdpSocket(this); +//! [0] + messageNo = 1; + + connect(startButton, SIGNAL(clicked()), this, SLOT(startBroadcasting())); + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); + connect(timer, SIGNAL(timeout()), this, SLOT(broadcastDatagram())); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(statusLabel); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("Broadcast Sender")); +} + +void Sender::startBroadcasting() +{ + startButton->setEnabled(false); + timer->start(1000); +} + +void Sender::broadcastDatagram() +{ + statusLabel->setText(tr("Now broadcasting datagram %1").arg(messageNo)); +//! [1] + QByteArray datagram = "Broadcast message " + QByteArray::number(messageNo); + udpSocket->writeDatagram(datagram.data(), datagram.size(), + QHostAddress::Broadcast, 45454); +//! [1] + ++messageNo; +} diff --git a/examples/network/broadcastsender/sender.h b/examples/network/broadcastsender/sender.h new file mode 100644 index 0000000..92265b8 --- /dev/null +++ b/examples/network/broadcastsender/sender.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SENDER_H +#define SENDER_H + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QDialogButtonBox; +class QLabel; +class QPushButton; +class QTimer; +class QUdpSocket; +QT_END_NAMESPACE + +class Sender : public QDialog +{ + Q_OBJECT + +public: + Sender(QWidget *parent = 0); + +private slots: + void startBroadcasting(); + void broadcastDatagram(); + +private: + QLabel *statusLabel; + QPushButton *startButton; + QPushButton *quitButton; + QDialogButtonBox *buttonBox; + QUdpSocket *udpSocket; + QTimer *timer; + int messageNo; +}; + +#endif diff --git a/examples/network/download/download.pro b/examples/network/download/download.pro new file mode 100644 index 0000000..254c356 --- /dev/null +++ b/examples/network/download/download.pro @@ -0,0 +1,19 @@ +###################################################################### +# Automatically generated by qmake (2.01a) fr. nov. 16 13:18:20 2007 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +QT = core network +CONFIG += console + +# Input +SOURCES += main.cpp + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/download +sources.files = $$SOURCES $$HEADERS $$FORMS $$RESOURCES *.pro *.png +sources.path = $$[QT_INSTALL_EXAMPLES]/network/download +INSTALLS += target sources diff --git a/examples/network/download/main.cpp b/examples/network/download/main.cpp new file mode 100644 index 0000000..e0158e7 --- /dev/null +++ b/examples/network/download/main.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QCoreApplication> +#include <QFile> +#include <QFileInfo> +#include <QList> +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QStringList> +#include <QTimer> +#include <QUrl> + +#include <stdio.h> + +class DownloadManager: public QObject +{ + Q_OBJECT + QNetworkAccessManager manager; + QList<QNetworkReply *> currentDownloads; + +public: + DownloadManager(); + void doDownload(const QUrl &url); + QString saveFileName(const QUrl &url); + bool saveToDisk(const QString &filename, QIODevice *data); + +public slots: + void execute(); + void downloadFinished(QNetworkReply *reply); +}; + +DownloadManager::DownloadManager() +{ + connect(&manager, SIGNAL(finished(QNetworkReply*)), + SLOT(downloadFinished(QNetworkReply*))); +} + +void DownloadManager::doDownload(const QUrl &url) +{ + QNetworkRequest request(url); + QNetworkReply *reply = manager.get(request); + + currentDownloads.append(reply); +} + +QString DownloadManager::saveFileName(const QUrl &url) +{ + QString path = url.path(); + QString basename = QFileInfo(path).fileName(); + + if (basename.isEmpty()) + basename = "download"; + + if (QFile::exists(basename)) { + // already exists, don't overwrite + int i = 0; + basename += '.'; + while (QFile::exists(basename + QString::number(i))) + ++i; + + basename += QString::number(i); + } + + return basename; +} + +bool DownloadManager::saveToDisk(const QString &filename, QIODevice *data) +{ + QFile file(filename); + if (!file.open(QIODevice::WriteOnly)) { + fprintf(stderr, "Could not open %s for writing: %s\n", + qPrintable(filename), + qPrintable(file.errorString())); + return false; + } + + file.write(data->readAll()); + file.close(); + + return true; +} + +void DownloadManager::execute() +{ + QStringList args = QCoreApplication::instance()->arguments(); + args.takeFirst(); // skip the first argument, which is the program's name + if (args.isEmpty()) { + printf("Qt Download example - downloads all URLs in parallel\n" + "Usage: download url1 [url2... urlN]\n" + "\n" + "Downloads the URLs passed in the command-line to the local directory\n" + "If the target file already exists, a .0, .1, .2, etc. is appended to\n" + "differentiate.\n"); + QCoreApplication::instance()->quit(); + return; + } + + foreach (QString arg, args) { + QUrl url = QUrl::fromEncoded(arg.toLocal8Bit()); + doDownload(url); + } +} + +void DownloadManager::downloadFinished(QNetworkReply *reply) +{ + QUrl url = reply->url(); + if (reply->error()) { + fprintf(stderr, "Download of %s failed: %s\n", + url.toEncoded().constData(), + qPrintable(reply->errorString())); + } else { + QString filename = saveFileName(url); + if (saveToDisk(filename, reply)) + printf("Download of %s succeded (saved to %s)\n", + url.toEncoded().constData(), qPrintable(filename)); + } + + currentDownloads.removeAll(reply); + reply->deleteLater(); + + if (currentDownloads.isEmpty()) + // all downloads finished + QCoreApplication::instance()->quit(); +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + DownloadManager manager; + QTimer::singleShot(0, &manager, SLOT(execute())); + + app.exec(); +} + +#include "main.moc" diff --git a/examples/network/downloadmanager/downloadmanager.cpp b/examples/network/downloadmanager/downloadmanager.cpp new file mode 100644 index 0000000..a4721a2 --- /dev/null +++ b/examples/network/downloadmanager/downloadmanager.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "downloadmanager.h" + +#include <QFileInfo> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QString> +#include <QStringList> +#include <QTimer> +#include <stdio.h> + +DownloadManager::DownloadManager(QObject *parent) + : QObject(parent), downloadedCount(0), totalCount(0) +{ +} + +void DownloadManager::append(const QStringList &urlList) +{ + foreach (QString url, urlList) + append(QUrl::fromEncoded(url.toLocal8Bit())); + + if (downloadQueue.isEmpty()) + QTimer::singleShot(0, this, SIGNAL(finished())); +} + +void DownloadManager::append(const QUrl &url) +{ + if (downloadQueue.isEmpty()) + QTimer::singleShot(0, this, SLOT(startNextDownload())); + + downloadQueue.enqueue(url); + ++totalCount; +} + +QString DownloadManager::saveFileName(const QUrl &url) +{ + QString path = url.path(); + QString basename = QFileInfo(path).fileName(); + + if (basename.isEmpty()) + basename = "download"; + + if (QFile::exists(basename)) { + // already exists, don't overwrite + int i = 0; + basename += '.'; + while (QFile::exists(basename + QString::number(i))) + ++i; + + basename += QString::number(i); + } + + return basename; +} + +void DownloadManager::startNextDownload() +{ + if (downloadQueue.isEmpty()) { + printf("%d/%d files downloaded successfully\n", downloadedCount, totalCount); + emit finished(); + return; + } + + QUrl url = downloadQueue.dequeue(); + + QString filename = saveFileName(url); + output.setFileName(filename); + if (!output.open(QIODevice::WriteOnly)) { + fprintf(stderr, "Problem opening save file '%s' for download '%s': %s\n", + qPrintable(filename), url.toEncoded().constData(), + qPrintable(output.errorString())); + + startNextDownload(); + return; // skip this download + } + + QNetworkRequest request(url); + currentDownload = manager.get(request); + connect(currentDownload, SIGNAL(downloadProgress(qint64,qint64)), + SLOT(downloadProgress(qint64,qint64))); + connect(currentDownload, SIGNAL(finished()), + SLOT(downloadFinished())); + connect(currentDownload, SIGNAL(readyRead()), + SLOT(downloadReadyRead())); + + // prepare the output + printf("Downloading %s...\n", url.toEncoded().constData()); + downloadTime.start(); +} + +void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + progressBar.setStatus(bytesReceived, bytesTotal); + + // calculate the download speed + double speed = bytesReceived * 1000.0 / downloadTime.elapsed(); + QString unit; + if (speed < 1024) { + unit = "bytes/sec"; + } else if (speed < 1024*1024) { + speed /= 1024; + unit = "kB/s"; + } else { + speed /= 1024*1024; + unit = "MB/s"; + } + + progressBar.setMessage(QString::fromLatin1("%1 %2") + .arg(speed, 3, 'f', 1).arg(unit)); + progressBar.update(); +} + +void DownloadManager::downloadFinished() +{ + progressBar.clear(); + output.close(); + + if (currentDownload->error()) { + // download failed + fprintf(stderr, "Failed: %s\n", qPrintable(currentDownload->errorString())); + } else { + printf("Succeeded.\n"); + ++downloadedCount; + } + + currentDownload->deleteLater(); + startNextDownload(); +} + +void DownloadManager::downloadReadyRead() +{ + output.write(currentDownload->readAll()); +} diff --git a/examples/network/downloadmanager/downloadmanager.h b/examples/network/downloadmanager/downloadmanager.h new file mode 100644 index 0000000..7ed80c4 --- /dev/null +++ b/examples/network/downloadmanager/downloadmanager.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DOWNLOADMANAGER_H +#define DOWNLOADMANAGER_H + +#include <QFile> +#include <QObject> +#include <QQueue> +#include <QTime> +#include <QUrl> +#include <QNetworkAccessManager> + +#include "textprogressbar.h" + +class DownloadManager: public QObject +{ + Q_OBJECT +public: + DownloadManager(QObject *parent = 0); + + void append(const QUrl &url); + void append(const QStringList &urlList); + QString saveFileName(const QUrl &url); + +signals: + void finished(); + +private slots: + void startNextDownload(); + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void downloadFinished(); + void downloadReadyRead(); + +private: + QNetworkAccessManager manager; + QQueue<QUrl> downloadQueue; + QNetworkReply *currentDownload; + QFile output; + QTime downloadTime; + TextProgressBar progressBar; + + int downloadedCount; + int totalCount; +}; + +#endif diff --git a/examples/network/downloadmanager/downloadmanager.pro b/examples/network/downloadmanager/downloadmanager.pro new file mode 100644 index 0000000..1b979ca --- /dev/null +++ b/examples/network/downloadmanager/downloadmanager.pro @@ -0,0 +1,20 @@ +###################################################################### +# Automatically generated by qmake (2.01a) fr. nov. 16 14:11:36 2007 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +QT = core network +CONFIG += console + +# Input +HEADERS += downloadmanager.h textprogressbar.h +SOURCES += downloadmanager.cpp main.cpp textprogressbar.cpp + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/downloadmanager +sources.files = $$SOURCES $$HEADERS $$FORMS $$RESOURCES *.pro *.png +sources.path = $$[QT_INSTALL_EXAMPLES]/network/downloadmanager +INSTALLS += target sources diff --git a/examples/network/downloadmanager/main.cpp b/examples/network/downloadmanager/main.cpp new file mode 100644 index 0000000..8a3fe24 --- /dev/null +++ b/examples/network/downloadmanager/main.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QCoreApplication> +#include <QStringList> +#include "downloadmanager.h" +#include <stdio.h> + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + QStringList arguments = app.arguments(); + arguments.takeFirst(); // remove the first argument, which is the program's name + + if (arguments.isEmpty()) { + printf("Qt Download example\n" + "Usage: downloadmanager url1 [url2... urlN]\n" + "\n" + "Downloads the URLs passed in the command-line to the local directory\n" + "If the target file already exists, a .0, .1, .2, etc. is appended to\n" + "differentiate.\n"); + return 0; + } + + DownloadManager manager; + manager.append(arguments); + + QObject::connect(&manager, SIGNAL(finished()), &app, SLOT(quit())); + app.exec(); +} diff --git a/examples/network/downloadmanager/textprogressbar.cpp b/examples/network/downloadmanager/textprogressbar.cpp new file mode 100644 index 0000000..e1fbf52 --- /dev/null +++ b/examples/network/downloadmanager/textprogressbar.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "textprogressbar.h" +#include <QByteArray> +#include <stdio.h> + +TextProgressBar::TextProgressBar() + : value(0), maximum(-1), iteration(0) +{ +} + +void TextProgressBar::clear() +{ + printf("\n"); + fflush(stdout); + + iteration = 0; + value = 0; + maximum = -1; +} + +void TextProgressBar::update() +{ + ++iteration; + + if (maximum > 0) { + // we know the maximum + // draw a progress bar + int percent = value * 100 / maximum; + int hashes = percent / 2; + + QByteArray progressbar(hashes, '#'); + if (percent % 2) + progressbar += '>'; + + printf("\r[%-50s] %3d%% %s ", + progressbar.constData(), + percent, + qPrintable(message)); + } else { + // we don't know the maximum, so we can't draw a progress bar + int center = (iteration % 48) + 1; // 50 spaces, minus 2 + QByteArray before(qMax(center - 2, 0), ' '); + QByteArray after(qMin(center + 2, 50), ' '); + + printf("\r[%s###%s] %s ", + before.constData(), after.constData(), qPrintable(message)); + } +} + +void TextProgressBar::setMessage(const QString &m) +{ + message = m; +} + +void TextProgressBar::setStatus(qint64 val, qint64 max) +{ + value = val; + maximum = max; +} diff --git a/examples/network/downloadmanager/textprogressbar.h b/examples/network/downloadmanager/textprogressbar.h new file mode 100644 index 0000000..34097d0 --- /dev/null +++ b/examples/network/downloadmanager/textprogressbar.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTPROGRESSBAR_H +#define TEXTPROGRESSBAR_H + +#include <QString> + +class TextProgressBar +{ +public: + TextProgressBar(); + + void clear(); + void update(); + void setMessage(const QString &message); + void setStatus(qint64 value, qint64 maximum); + +private: + QString message; + qint64 value; + qint64 maximum; + int iteration; +}; + +#endif diff --git a/examples/network/fortuneclient/client.cpp b/examples/network/fortuneclient/client.cpp new file mode 100644 index 0000000..4d65828 --- /dev/null +++ b/examples/network/fortuneclient/client.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtNetwork> + +#include "client.h" + +//! [0] +Client::Client(QWidget *parent) + : QDialog(parent) +{ +//! [0] + hostLabel = new QLabel(tr("&Server name:")); + portLabel = new QLabel(tr("S&erver port:")); + + hostLineEdit = new QLineEdit("Localhost"); + portLineEdit = new QLineEdit; + portLineEdit->setValidator(new QIntValidator(1, 65535, this)); + + hostLabel->setBuddy(hostLineEdit); + portLabel->setBuddy(portLineEdit); + + statusLabel = new QLabel(tr("This examples requires that you run the " + "Fortune Server example as well.")); + + getFortuneButton = new QPushButton(tr("Get Fortune")); + getFortuneButton->setDefault(true); + getFortuneButton->setEnabled(false); + + quitButton = new QPushButton(tr("Quit")); + + buttonBox = new QDialogButtonBox; + buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole); + buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); + +//! [1] + tcpSocket = new QTcpSocket(this); +//! [1] + + connect(hostLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(enableGetFortuneButton())); + connect(portLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(enableGetFortuneButton())); + connect(getFortuneButton, SIGNAL(clicked()), + this, SLOT(requestNewFortune())); + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); +//! [2] //! [3] + connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readFortune())); +//! [2] //! [4] + connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), +//! [3] + this, SLOT(displayError(QAbstractSocket::SocketError))); +//! [4] + + QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget(hostLabel, 0, 0); + mainLayout->addWidget(hostLineEdit, 0, 1); + mainLayout->addWidget(portLabel, 1, 0); + mainLayout->addWidget(portLineEdit, 1, 1); + mainLayout->addWidget(statusLabel, 2, 0, 1, 2); + mainLayout->addWidget(buttonBox, 3, 0, 1, 2); + setLayout(mainLayout); + + setWindowTitle(tr("Fortune Client")); + portLineEdit->setFocus(); +//! [5] +} +//! [5] + +//! [6] +void Client::requestNewFortune() +{ + getFortuneButton->setEnabled(false); + blockSize = 0; + tcpSocket->abort(); +//! [7] + tcpSocket->connectToHost(hostLineEdit->text(), + portLineEdit->text().toInt()); +//! [7] +} +//! [6] + +//! [8] +void Client::readFortune() +{ +//! [9] + QDataStream in(tcpSocket); + in.setVersion(QDataStream::Qt_4_0); + + if (blockSize == 0) { + if (tcpSocket->bytesAvailable() < (int)sizeof(quint16)) + return; +//! [8] + +//! [10] + in >> blockSize; + } + + if (tcpSocket->bytesAvailable() < blockSize) + return; +//! [10] //! [11] + + QString nextFortune; + in >> nextFortune; + + if (nextFortune == currentFortune) { + QTimer::singleShot(0, this, SLOT(requestNewFortune())); + return; + } +//! [11] + +//! [12] + currentFortune = nextFortune; +//! [9] + statusLabel->setText(currentFortune); + getFortuneButton->setEnabled(true); +} +//! [12] + +//! [13] +void Client::displayError(QAbstractSocket::SocketError socketError) +{ + switch (socketError) { + case QAbstractSocket::RemoteHostClosedError: + break; + case QAbstractSocket::HostNotFoundError: + QMessageBox::information(this, tr("Fortune Client"), + tr("The host was not found. Please check the " + "host name and port settings.")); + break; + case QAbstractSocket::ConnectionRefusedError: + QMessageBox::information(this, tr("Fortune Client"), + tr("The connection was refused by the peer. " + "Make sure the fortune server is running, " + "and check that the host name and port " + "settings are correct.")); + break; + default: + QMessageBox::information(this, tr("Fortune Client"), + tr("The following error occurred: %1.") + .arg(tcpSocket->errorString())); + } + + getFortuneButton->setEnabled(true); +} +//! [13] + +void Client::enableGetFortuneButton() +{ + getFortuneButton->setEnabled(!hostLineEdit->text().isEmpty() + && !portLineEdit->text().isEmpty()); +} diff --git a/examples/network/fortuneclient/client.h b/examples/network/fortuneclient/client.h new file mode 100644 index 0000000..b9fe7b1 --- /dev/null +++ b/examples/network/fortuneclient/client.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CLIENT_H +#define CLIENT_H + +#include <QDialog> +#include <QTcpSocket> + +QT_BEGIN_NAMESPACE +class QDialogButtonBox; +class QLabel; +class QLineEdit; +class QPushButton; +class QTcpSocket; +QT_END_NAMESPACE + +//! [0] +class Client : public QDialog +{ + Q_OBJECT + +public: + Client(QWidget *parent = 0); + +private slots: + void requestNewFortune(); + void readFortune(); + void displayError(QAbstractSocket::SocketError socketError); + void enableGetFortuneButton(); + +private: + QLabel *hostLabel; + QLabel *portLabel; + QLineEdit *hostLineEdit; + QLineEdit *portLineEdit; + QLabel *statusLabel; + QPushButton *getFortuneButton; + QPushButton *quitButton; + QDialogButtonBox *buttonBox; + + QTcpSocket *tcpSocket; + QString currentFortune; + quint16 blockSize; +}; +//! [0] + +#endif diff --git a/examples/network/fortuneclient/fortuneclient.pro b/examples/network/fortuneclient/fortuneclient.pro new file mode 100644 index 0000000..1c7b0a8 --- /dev/null +++ b/examples/network/fortuneclient/fortuneclient.pro @@ -0,0 +1,10 @@ +HEADERS = client.h +SOURCES = client.cpp \ + main.cpp +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/fortuneclient +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS fortuneclient.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/network/fortuneclient +INSTALLS += target sources diff --git a/examples/network/fortuneclient/main.cpp b/examples/network/fortuneclient/main.cpp new file mode 100644 index 0000000..11dcbc6 --- /dev/null +++ b/examples/network/fortuneclient/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "client.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Client client; + client.show(); + return client.exec(); +} diff --git a/examples/network/fortuneserver/fortuneserver.pro b/examples/network/fortuneserver/fortuneserver.pro new file mode 100644 index 0000000..e98385a --- /dev/null +++ b/examples/network/fortuneserver/fortuneserver.pro @@ -0,0 +1,10 @@ +HEADERS = server.h +SOURCES = server.cpp \ + main.cpp +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/fortuneserver +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS fortuneserver.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/network/fortuneserver +INSTALLS += target sources diff --git a/examples/network/fortuneserver/main.cpp b/examples/network/fortuneserver/main.cpp new file mode 100644 index 0000000..b505c44 --- /dev/null +++ b/examples/network/fortuneserver/main.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QtCore> + +#include <stdlib.h> + +#include "server.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Server server; + server.show(); + qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); + return server.exec(); +} diff --git a/examples/network/fortuneserver/server.cpp b/examples/network/fortuneserver/server.cpp new file mode 100644 index 0000000..09626a8 --- /dev/null +++ b/examples/network/fortuneserver/server.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtNetwork> + +#include <stdlib.h> + +#include "server.h" + +Server::Server(QWidget *parent) + : QDialog(parent) +{ + statusLabel = new QLabel; + quitButton = new QPushButton(tr("Quit")); + quitButton->setAutoDefault(false); + +//! [0] //! [1] + tcpServer = new QTcpServer(this); + if (!tcpServer->listen()) { + QMessageBox::critical(this, tr("Fortune Server"), + tr("Unable to start the server: %1.") + .arg(tcpServer->errorString())); + close(); + return; + } +//! [0] + + statusLabel->setText(tr("The server is running on port %1.\n" + "Run the Fortune Client example now.") + .arg(tcpServer->serverPort())); +//! [1] + +//! [2] + fortunes << tr("You've been leading a dog's life. Stay off the furniture.") + << tr("You've got to think about tomorrow.") + << tr("You will be surprised by a loud noise.") + << tr("You will feel hungry again in another hour.") + << tr("You might have mail.") + << tr("You cannot kill time without injuring eternity.") + << tr("Computers are not intelligent. They only think they are."); +//! [2] + + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); +//! [3] + connect(tcpServer, SIGNAL(newConnection()), this, SLOT(sendFortune())); +//! [3] + + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(1); + buttonLayout->addWidget(quitButton); + buttonLayout->addStretch(1); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(statusLabel); + mainLayout->addLayout(buttonLayout); + setLayout(mainLayout); + + setWindowTitle(tr("Fortune Server")); +} + +//! [4] +void Server::sendFortune() +{ +//! [5] + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_0); +//! [4] //! [6] + out << (quint16)0; + out << fortunes.at(qrand() % fortunes.size()); + out.device()->seek(0); + out << (quint16)(block.size() - sizeof(quint16)); +//! [6] //! [7] + + QTcpSocket *clientConnection = tcpServer->nextPendingConnection(); + connect(clientConnection, SIGNAL(disconnected()), + clientConnection, SLOT(deleteLater())); +//! [7] //! [8] + + clientConnection->write(block); + clientConnection->disconnectFromHost(); +//! [5] +} +//! [8] diff --git a/examples/network/fortuneserver/server.h b/examples/network/fortuneserver/server.h new file mode 100644 index 0000000..dcd64c7 --- /dev/null +++ b/examples/network/fortuneserver/server.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SERVER_H +#define SERVER_H + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QLabel; +class QPushButton; +class QTcpServer; +QT_END_NAMESPACE + +//! [0] +class Server : public QDialog +{ + Q_OBJECT + +public: + Server(QWidget *parent = 0); + +private slots: + void sendFortune(); + +private: + QLabel *statusLabel; + QPushButton *quitButton; + QTcpServer *tcpServer; + QStringList fortunes; +}; +//! [0] + +#endif diff --git a/examples/network/ftp/ftp.pro b/examples/network/ftp/ftp.pro new file mode 100644 index 0000000..cabc003 --- /dev/null +++ b/examples/network/ftp/ftp.pro @@ -0,0 +1,11 @@ +HEADERS = ftpwindow.h +SOURCES = ftpwindow.cpp \ + main.cpp +RESOURCES += ftp.qrc +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/ftp +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS ftp.pro images +sources.path = $$[QT_INSTALL_EXAMPLES]/network/ftp +INSTALLS += target sources diff --git a/examples/network/ftp/ftp.qrc b/examples/network/ftp/ftp.qrc new file mode 100644 index 0000000..b598ab8 --- /dev/null +++ b/examples/network/ftp/ftp.qrc @@ -0,0 +1,7 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/cdtoparent.png</file> + <file>images/dir.png</file> + <file>images/file.png</file> +</qresource> +</RCC> diff --git a/examples/network/ftp/ftpwindow.cpp b/examples/network/ftp/ftpwindow.cpp new file mode 100644 index 0000000..a05a5dd --- /dev/null +++ b/examples/network/ftp/ftpwindow.cpp @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtNetwork> + +#include "ftpwindow.h" + +FtpWindow::FtpWindow(QWidget *parent) + : QDialog(parent), ftp(0) +{ + ftpServerLabel = new QLabel(tr("Ftp &server:")); + ftpServerLineEdit = new QLineEdit("ftp.trolltech.com"); + ftpServerLabel->setBuddy(ftpServerLineEdit); + + statusLabel = new QLabel(tr("Please enter the name of an FTP server.")); + + fileList = new QTreeWidget; + fileList->setEnabled(false); + fileList->setRootIsDecorated(false); + fileList->setHeaderLabels(QStringList() << tr("Name") << tr("Size") << tr("Owner") << tr("Group") << tr("Time")); + fileList->header()->setStretchLastSection(false); + + connectButton = new QPushButton(tr("Connect")); + connectButton->setDefault(true); + + cdToParentButton = new QPushButton; + cdToParentButton->setIcon(QPixmap(":/images/cdtoparent.png")); + cdToParentButton->setEnabled(false); + + downloadButton = new QPushButton(tr("Download")); + downloadButton->setEnabled(false); + + quitButton = new QPushButton(tr("Quit")); + + buttonBox = new QDialogButtonBox; + buttonBox->addButton(downloadButton, QDialogButtonBox::ActionRole); + buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); + + progressDialog = new QProgressDialog(this); + + connect(fileList, SIGNAL(itemActivated(QTreeWidgetItem *, int)), + this, SLOT(processItem(QTreeWidgetItem *, int))); + connect(fileList, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(enableDownloadButton())); + connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelDownload())); + connect(connectButton, SIGNAL(clicked()), this, SLOT(connectOrDisconnect())); + connect(cdToParentButton, SIGNAL(clicked()), this, SLOT(cdToParent())); + connect(downloadButton, SIGNAL(clicked()), this, SLOT(downloadFile())); + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); + + QHBoxLayout *topLayout = new QHBoxLayout; + topLayout->addWidget(ftpServerLabel); + topLayout->addWidget(ftpServerLineEdit); + topLayout->addWidget(cdToParentButton); + topLayout->addWidget(connectButton); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(topLayout); + mainLayout->addWidget(fileList); + mainLayout->addWidget(statusLabel); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("FTP")); +} + +QSize FtpWindow::sizeHint() const +{ + return QSize(500, 300); +} + +//![0] +void FtpWindow::connectOrDisconnect() +{ + if (ftp) { + ftp->abort(); + ftp->deleteLater(); + ftp = 0; +//![0] + fileList->setEnabled(false); + cdToParentButton->setEnabled(false); + downloadButton->setEnabled(false); + connectButton->setEnabled(true); + connectButton->setText(tr("Connect")); +#ifndef QT_NO_CURSOR + setCursor(Qt::ArrowCursor); +#endif + return; + } + +#ifndef QT_NO_CURSOR + setCursor(Qt::WaitCursor); +#endif + +//![1] + ftp = new QFtp(this); + connect(ftp, SIGNAL(commandFinished(int, bool)), + this, SLOT(ftpCommandFinished(int, bool))); + connect(ftp, SIGNAL(listInfo(const QUrlInfo &)), + this, SLOT(addToList(const QUrlInfo &))); + connect(ftp, SIGNAL(dataTransferProgress(qint64, qint64)), + this, SLOT(updateDataTransferProgress(qint64, qint64))); + + fileList->clear(); + currentPath.clear(); + isDirectory.clear(); +//![1] + +//![2] + QUrl url(ftpServerLineEdit->text()); + if (!url.isValid() || url.scheme().toLower() != QLatin1String("ftp")) { + ftp->connectToHost(ftpServerLineEdit->text(), 21); + ftp->login(); + } else { + ftp->connectToHost(url.host(), url.port(21)); + + if (!url.userName().isEmpty()) + ftp->login(QUrl::fromPercentEncoding(url.userName().toLatin1()), url.password()); + else + ftp->login(); + if (!url.path().isEmpty()) + ftp->cd(url.path()); + } +//![2] + + fileList->setEnabled(true); + connectButton->setEnabled(false); + connectButton->setText(tr("Disconnect")); + statusLabel->setText(tr("Connecting to FTP server %1...") + .arg(ftpServerLineEdit->text())); +} + +//![3] +void FtpWindow::downloadFile() +{ + QString fileName = fileList->currentItem()->text(0); +//![3] +// + if (QFile::exists(fileName)) { + QMessageBox::information(this, tr("FTP"), + tr("There already exists a file called %1 in " + "the current directory.") + .arg(fileName)); + return; + } + +//![4] + file = new QFile(fileName); + if (!file->open(QIODevice::WriteOnly)) { + QMessageBox::information(this, tr("FTP"), + tr("Unable to save the file %1: %2.") + .arg(fileName).arg(file->errorString())); + delete file; + return; + } + + ftp->get(fileList->currentItem()->text(0), file); + + progressDialog->setLabelText(tr("Downloading %1...").arg(fileName)); + downloadButton->setEnabled(false); + progressDialog->exec(); +} +//![4] + +//![5] +void FtpWindow::cancelDownload() +{ + ftp->abort(); +} +//![5] + +//![6] +void FtpWindow::ftpCommandFinished(int, bool error) +{ +#ifndef QT_NO_CURSOR + setCursor(Qt::ArrowCursor); +#endif + + if (ftp->currentCommand() == QFtp::ConnectToHost) { + if (error) { + QMessageBox::information(this, tr("FTP"), + tr("Unable to connect to the FTP server " + "at %1. Please check that the host " + "name is correct.") + .arg(ftpServerLineEdit->text())); + connectOrDisconnect(); + return; + } + statusLabel->setText(tr("Logged onto %1.") + .arg(ftpServerLineEdit->text())); + fileList->setFocus(); + downloadButton->setDefault(true); + connectButton->setEnabled(true); + return; + } +//![6] + +//![7] + if (ftp->currentCommand() == QFtp::Login) + ftp->list(); +//![7] + +//![8] + if (ftp->currentCommand() == QFtp::Get) { + if (error) { + statusLabel->setText(tr("Canceled download of %1.") + .arg(file->fileName())); + file->close(); + file->remove(); + } else { + statusLabel->setText(tr("Downloaded %1 to current directory.") + .arg(file->fileName())); + file->close(); + } + delete file; + enableDownloadButton(); + progressDialog->hide(); +//![8] +//![9] + } else if (ftp->currentCommand() == QFtp::List) { + if (isDirectory.isEmpty()) { + fileList->addTopLevelItem(new QTreeWidgetItem(QStringList() << tr("<empty>"))); + fileList->setEnabled(false); + } + } +//![9] +} + +//![10] +void FtpWindow::addToList(const QUrlInfo &urlInfo) +{ + QTreeWidgetItem *item = new QTreeWidgetItem; + item->setText(0, urlInfo.name()); + item->setText(1, QString::number(urlInfo.size())); + item->setText(2, urlInfo.owner()); + item->setText(3, urlInfo.group()); + item->setText(4, urlInfo.lastModified().toString("MMM dd yyyy")); + + QPixmap pixmap(urlInfo.isDir() ? ":/images/dir.png" : ":/images/file.png"); + item->setIcon(0, pixmap); + + isDirectory[urlInfo.name()] = urlInfo.isDir(); + fileList->addTopLevelItem(item); + if (!fileList->currentItem()) { + fileList->setCurrentItem(fileList->topLevelItem(0)); + fileList->setEnabled(true); + } +} +//![10] + +//![11] +void FtpWindow::processItem(QTreeWidgetItem *item, int /*column*/) +{ + QString name = item->text(0); + if (isDirectory.value(name)) { + fileList->clear(); + isDirectory.clear(); + currentPath += "/" + name; + ftp->cd(name); + ftp->list(); + cdToParentButton->setEnabled(true); +#ifndef QT_NO_CURSOR + setCursor(Qt::WaitCursor); +#endif + return; + } +} +//![11] + +//![12] +void FtpWindow::cdToParent() +{ +#ifndef QT_NO_CURSOR + setCursor(Qt::WaitCursor); +#endif + fileList->clear(); + isDirectory.clear(); + currentPath = currentPath.left(currentPath.lastIndexOf('/')); + if (currentPath.isEmpty()) { + cdToParentButton->setEnabled(false); + ftp->cd("/"); + } else { + ftp->cd(currentPath); + } + ftp->list(); +} +//![12] + +//![13] +void FtpWindow::updateDataTransferProgress(qint64 readBytes, + qint64 totalBytes) +{ + progressDialog->setMaximum(totalBytes); + progressDialog->setValue(readBytes); +} +//![13] + +//![14] +void FtpWindow::enableDownloadButton() +{ + QTreeWidgetItem *current = fileList->currentItem(); + if (current) { + QString currentFile = current->text(0); + downloadButton->setEnabled(!isDirectory.value(currentFile)); + } else { + downloadButton->setEnabled(false); + } +} +//![14] + diff --git a/examples/network/ftp/ftpwindow.h b/examples/network/ftp/ftpwindow.h new file mode 100644 index 0000000..a3045fa --- /dev/null +++ b/examples/network/ftp/ftpwindow.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FTPWINDOW_H +#define FTPWINDOW_H + +#include <QDialog> +#include <QHash> + +QT_BEGIN_NAMESPACE +class QDialogButtonBox; +class QFile; +class QFtp; +class QLabel; +class QLineEdit; +class QTreeWidget; +class QTreeWidgetItem; +class QProgressDialog; +class QPushButton; +class QUrlInfo; +QT_END_NAMESPACE + +class FtpWindow : public QDialog +{ + Q_OBJECT + +public: + FtpWindow(QWidget *parent = 0); + QSize sizeHint() const; + +//![0] +private slots: + void connectOrDisconnect(); + void downloadFile(); + void cancelDownload(); + + void ftpCommandFinished(int commandId, bool error); + void addToList(const QUrlInfo &urlInfo); + void processItem(QTreeWidgetItem *item, int column); + void cdToParent(); + void updateDataTransferProgress(qint64 readBytes, + qint64 totalBytes); + void enableDownloadButton(); +//![0] + +private: + QLabel *ftpServerLabel; + QLineEdit *ftpServerLineEdit; + QLabel *statusLabel; + QTreeWidget *fileList; + QPushButton *cdToParentButton; + QPushButton *connectButton; + QPushButton *downloadButton; + QPushButton *quitButton; + QDialogButtonBox *buttonBox; + QProgressDialog *progressDialog; + +//![1] + QHash<QString, bool> isDirectory; + QString currentPath; + QFtp *ftp; + QFile *file; +//![1] +}; + +#endif diff --git a/examples/network/ftp/images/cdtoparent.png b/examples/network/ftp/images/cdtoparent.png Binary files differnew file mode 100644 index 0000000..24b6180 --- /dev/null +++ b/examples/network/ftp/images/cdtoparent.png diff --git a/examples/network/ftp/images/dir.png b/examples/network/ftp/images/dir.png Binary files differnew file mode 100644 index 0000000..0ce5ae7 --- /dev/null +++ b/examples/network/ftp/images/dir.png diff --git a/examples/network/ftp/images/file.png b/examples/network/ftp/images/file.png Binary files differnew file mode 100644 index 0000000..be6c530 --- /dev/null +++ b/examples/network/ftp/images/file.png diff --git a/examples/network/ftp/main.cpp b/examples/network/ftp/main.cpp new file mode 100644 index 0000000..b9ffd33 --- /dev/null +++ b/examples/network/ftp/main.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "ftpwindow.h" + +int main(int argc, char *argv[]) +{ + Q_INIT_RESOURCE(ftp); + + QApplication app(argc, argv); + FtpWindow ftpWin; + ftpWin.show(); + return ftpWin.exec(); +} diff --git a/examples/network/http/authenticationdialog.ui b/examples/network/http/authenticationdialog.ui new file mode 100644 index 0000000..82d908c --- /dev/null +++ b/examples/network/http/authenticationdialog.ui @@ -0,0 +1,129 @@ +<ui version="4.0" > + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>389</width> + <height>243</height> + </rect> + </property> + <property name="windowTitle" > + <string>Http authentication required</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="2" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>You need to supply a Username and a Password to access this site</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Username:</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLineEdit" name="userEdit" /> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Password:</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLineEdit" name="passwordEdit" /> + </item> + <item row="5" column="0" colspan="2" > + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>Site:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLabel" name="siteDescription" > + <property name="font" > + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text" > + <string>%1 at %2</string> + </property> + <property name="wordWrap" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="4" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/examples/network/http/http.pro b/examples/network/http/http.pro new file mode 100644 index 0000000..7f58d9f --- /dev/null +++ b/examples/network/http/http.pro @@ -0,0 +1,11 @@ +HEADERS += httpwindow.h +SOURCES += httpwindow.cpp \ + main.cpp +FORMS += authenticationdialog.ui +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/http +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS http.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/network/http +INSTALLS += target sources diff --git a/examples/network/http/httpwindow.cpp b/examples/network/http/httpwindow.cpp new file mode 100644 index 0000000..ebde770 --- /dev/null +++ b/examples/network/http/httpwindow.cpp @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtNetwork> + +#include "httpwindow.h" +#include "ui_authenticationdialog.h" + +HttpWindow::HttpWindow(QWidget *parent) + : QDialog(parent) +{ +#ifndef QT_NO_OPENSSL + urlLineEdit = new QLineEdit("https://"); +#else + urlLineEdit = new QLineEdit("http://"); +#endif + + urlLabel = new QLabel(tr("&URL:")); + urlLabel->setBuddy(urlLineEdit); + statusLabel = new QLabel(tr("Please enter the URL of a file you want to " + "download.")); + + downloadButton = new QPushButton(tr("Download")); + downloadButton->setDefault(true); + quitButton = new QPushButton(tr("Quit")); + quitButton->setAutoDefault(false); + + buttonBox = new QDialogButtonBox; + buttonBox->addButton(downloadButton, QDialogButtonBox::ActionRole); + buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); + + progressDialog = new QProgressDialog(this); + + http = new QHttp(this); + + connect(urlLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(enableDownloadButton())); + connect(http, SIGNAL(requestFinished(int, bool)), + this, SLOT(httpRequestFinished(int, bool))); + connect(http, SIGNAL(dataReadProgress(int, int)), + this, SLOT(updateDataReadProgress(int, int))); + connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)), + this, SLOT(readResponseHeader(const QHttpResponseHeader &))); + connect(http, SIGNAL(authenticationRequired(const QString &, quint16, QAuthenticator *)), + this, SLOT(slotAuthenticationRequired(const QString &, quint16, QAuthenticator *))); +#ifndef QT_NO_OPENSSL + connect(http, SIGNAL(sslErrors(const QList<QSslError> &)), + this, SLOT(sslErrors(const QList<QSslError> &))); +#endif + connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelDownload())); + connect(downloadButton, SIGNAL(clicked()), this, SLOT(downloadFile())); + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); + + QHBoxLayout *topLayout = new QHBoxLayout; + topLayout->addWidget(urlLabel); + topLayout->addWidget(urlLineEdit); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(topLayout); + mainLayout->addWidget(statusLabel); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("HTTP")); + urlLineEdit->setFocus(); +} + +void HttpWindow::downloadFile() +{ + QUrl url(urlLineEdit->text()); + QFileInfo fileInfo(url.path()); + QString fileName = fileInfo.fileName(); + if (fileName.isEmpty()) + fileName = "index.html"; + + if (QFile::exists(fileName)) { + if (QMessageBox::question(this, tr("HTTP"), + tr("There already exists a file called %1 in " + "the current directory. Overwrite?").arg(fileName), + QMessageBox::Ok|QMessageBox::Cancel, QMessageBox::Cancel) + == QMessageBox::Cancel) + return; + QFile::remove(fileName); + } + + file = new QFile(fileName); + if (!file->open(QIODevice::WriteOnly)) { + QMessageBox::information(this, tr("HTTP"), + tr("Unable to save the file %1: %2.") + .arg(fileName).arg(file->errorString())); + delete file; + file = 0; + return; + } + + QHttp::ConnectionMode mode = url.scheme().toLower() == "https" ? QHttp::ConnectionModeHttps : QHttp::ConnectionModeHttp; + http->setHost(url.host(), mode, url.port() == -1 ? 0 : url.port()); + + if (!url.userName().isEmpty()) + http->setUser(url.userName(), url.password()); + + httpRequestAborted = false; + QByteArray path = QUrl::toPercentEncoding(url.path(), "!$&'()*+,;=:@/"); + if (path.isEmpty()) + path = "/"; + httpGetId = http->get(path, file); + + progressDialog->setWindowTitle(tr("HTTP")); + progressDialog->setLabelText(tr("Downloading %1.").arg(fileName)); + downloadButton->setEnabled(false); +} + +void HttpWindow::cancelDownload() +{ + statusLabel->setText(tr("Download canceled.")); + httpRequestAborted = true; + http->abort(); + downloadButton->setEnabled(true); +} + +void HttpWindow::httpRequestFinished(int requestId, bool error) +{ + if (requestId != httpGetId) + return; + if (httpRequestAborted) { + if (file) { + file->close(); + file->remove(); + delete file; + file = 0; + } + + progressDialog->hide(); + return; + } + + if (requestId != httpGetId) + return; + + progressDialog->hide(); + file->close(); + + if (error) { + file->remove(); + QMessageBox::information(this, tr("HTTP"), + tr("Download failed: %1.") + .arg(http->errorString())); + } else { + QString fileName = QFileInfo(QUrl(urlLineEdit->text()).path()).fileName(); + statusLabel->setText(tr("Downloaded %1 to current directory.").arg(fileName)); + } + + downloadButton->setEnabled(true); + delete file; + file = 0; +} + +void HttpWindow::readResponseHeader(const QHttpResponseHeader &responseHeader) +{ + switch (responseHeader.statusCode()) { + case 200: // Ok + case 301: // Moved Permanently + case 302: // Found + case 303: // See Other + case 307: // Temporary Redirect + // these are not error conditions + break; + + default: + QMessageBox::information(this, tr("HTTP"), + tr("Download failed: %1.") + .arg(responseHeader.reasonPhrase())); + httpRequestAborted = true; + progressDialog->hide(); + http->abort(); + } +} + +void HttpWindow::updateDataReadProgress(int bytesRead, int totalBytes) +{ + if (httpRequestAborted) + return; + + progressDialog->setMaximum(totalBytes); + progressDialog->setValue(bytesRead); +} + +void HttpWindow::enableDownloadButton() +{ + downloadButton->setEnabled(!urlLineEdit->text().isEmpty()); +} + +void HttpWindow::slotAuthenticationRequired(const QString &hostName, quint16, QAuthenticator *authenticator) +{ + QDialog dlg; + Ui::Dialog ui; + ui.setupUi(&dlg); + dlg.adjustSize(); + ui.siteDescription->setText(tr("%1 at %2").arg(authenticator->realm()).arg(hostName)); + + if (dlg.exec() == QDialog::Accepted) { + authenticator->setUser(ui.userEdit->text()); + authenticator->setPassword(ui.passwordEdit->text()); + } +} + +#ifndef QT_NO_OPENSSL +void HttpWindow::sslErrors(const QList<QSslError> &errors) +{ + QString errorString; + foreach (const QSslError &error, errors) { + if (!errorString.isEmpty()) + errorString += ", "; + errorString += error.errorString(); + } + + if (QMessageBox::warning(this, tr("HTTP Example"), + tr("One or more SSL errors has occurred: %1").arg(errorString), + QMessageBox::Ignore | QMessageBox::Abort) == QMessageBox::Ignore) { + http->ignoreSslErrors(); + } +} +#endif diff --git a/examples/network/http/httpwindow.h b/examples/network/http/httpwindow.h new file mode 100644 index 0000000..f0fb504 --- /dev/null +++ b/examples/network/http/httpwindow.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HTTPWINDOW_H +#define HTTPWINDOW_H + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QDialogButtonBox; +class QFile; +class QHttp; +class QHttpResponseHeader; +class QLabel; +class QLineEdit; +class QProgressDialog; +class QPushButton; +class QSslError; +class QAuthenticator; +QT_END_NAMESPACE + +class HttpWindow : public QDialog +{ + Q_OBJECT + +public: + HttpWindow(QWidget *parent = 0); + +private slots: + void downloadFile(); + void cancelDownload(); + void httpRequestFinished(int requestId, bool error); + void readResponseHeader(const QHttpResponseHeader &responseHeader); + void updateDataReadProgress(int bytesRead, int totalBytes); + void enableDownloadButton(); + void slotAuthenticationRequired(const QString &, quint16, QAuthenticator *); +#ifndef QT_NO_OPENSSL + void sslErrors(const QList<QSslError> &errors); +#endif + +private: + QLabel *statusLabel; + QLabel *urlLabel; + QLineEdit *urlLineEdit; + QProgressDialog *progressDialog; + QPushButton *downloadButton; + QPushButton *quitButton; + QDialogButtonBox *buttonBox; + + QHttp *http; + QFile *file; + int httpGetId; + bool httpRequestAborted; +}; + +#endif diff --git a/examples/network/http/main.cpp b/examples/network/http/main.cpp new file mode 100644 index 0000000..dba4082 --- /dev/null +++ b/examples/network/http/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "httpwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + HttpWindow httpWin; + httpWin.show(); + return httpWin.exec(); +} diff --git a/examples/network/loopback/dialog.cpp b/examples/network/loopback/dialog.cpp new file mode 100644 index 0000000..06ec8dd --- /dev/null +++ b/examples/network/loopback/dialog.cpp @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtNetwork> + +#include "dialog.h" + +#if !defined(Q_OS_WINCE) +static const int TotalBytes = 50 * 1024 * 1024; +#else +static const int TotalBytes = 5 * 1024 * 1024; +#endif +static const int PayloadSize = 65536; + +Dialog::Dialog(QWidget *parent) + : QDialog(parent) +{ + clientProgressBar = new QProgressBar; + clientStatusLabel = new QLabel(tr("Client ready")); + serverProgressBar = new QProgressBar; + serverStatusLabel = new QLabel(tr("Server ready")); + + startButton = new QPushButton(tr("&Start")); + quitButton = new QPushButton(tr("&Quit")); + + buttonBox = new QDialogButtonBox; + buttonBox->addButton(startButton, QDialogButtonBox::ActionRole); + buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); + + connect(startButton, SIGNAL(clicked()), this, SLOT(start())); + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); + connect(&tcpServer, SIGNAL(newConnection()), + this, SLOT(acceptConnection())); + connect(&tcpClient, SIGNAL(connected()), this, SLOT(startTransfer())); + connect(&tcpClient, SIGNAL(bytesWritten(qint64)), + this, SLOT(updateClientProgress(qint64))); + connect(&tcpClient, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(displayError(QAbstractSocket::SocketError))); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(clientProgressBar); + mainLayout->addWidget(clientStatusLabel); + mainLayout->addWidget(serverProgressBar); + mainLayout->addWidget(serverStatusLabel); + mainLayout->addStretch(1); + mainLayout->addSpacing(10); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("Loopback")); +} + +void Dialog::start() +{ + startButton->setEnabled(false); + +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor(Qt::WaitCursor); +#endif + + bytesWritten = 0; + bytesReceived = 0; + + while (!tcpServer.isListening() && !tcpServer.listen()) { + QMessageBox::StandardButton ret = QMessageBox::critical(this, + tr("Loopback"), + tr("Unable to start the test: %1.") + .arg(tcpServer.errorString()), + QMessageBox::Retry + | QMessageBox::Cancel); + if (ret == QMessageBox::Cancel) + return; + } + + serverStatusLabel->setText(tr("Listening")); + clientStatusLabel->setText(tr("Connecting")); + tcpClient.connectToHost(QHostAddress::LocalHost, tcpServer.serverPort()); +} + +void Dialog::acceptConnection() +{ + tcpServerConnection = tcpServer.nextPendingConnection(); + connect(tcpServerConnection, SIGNAL(readyRead()), + this, SLOT(updateServerProgress())); + connect(tcpServerConnection, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(displayError(QAbstractSocket::SocketError))); + + serverStatusLabel->setText(tr("Accepted connection")); + tcpServer.close(); +} + +void Dialog::startTransfer() +{ + bytesToWrite = TotalBytes - (int)tcpClient.write(QByteArray(PayloadSize, '@')); + clientStatusLabel->setText(tr("Connected")); +} + +void Dialog::updateServerProgress() +{ + bytesReceived += (int)tcpServerConnection->bytesAvailable(); + tcpServerConnection->readAll(); + + serverProgressBar->setMaximum(TotalBytes); + serverProgressBar->setValue(bytesReceived); + serverStatusLabel->setText(tr("Received %1MB") + .arg(bytesReceived / (1024 * 1024))); + + if (bytesReceived == TotalBytes) { + tcpServerConnection->close(); + startButton->setEnabled(true); +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + } +} + +void Dialog::updateClientProgress(qint64 numBytes) +{ + bytesWritten += (int)numBytes; + if (bytesToWrite > 0) + bytesToWrite -= (int)tcpClient.write(QByteArray(qMin(bytesToWrite, PayloadSize), '@')); + + clientProgressBar->setMaximum(TotalBytes); + clientProgressBar->setValue(bytesWritten); + clientStatusLabel->setText(tr("Sent %1MB") + .arg(bytesWritten / (1024 * 1024))); +} + +void Dialog::displayError(QAbstractSocket::SocketError socketError) +{ + if (socketError == QTcpSocket::RemoteHostClosedError) + return; + + QMessageBox::information(this, tr("Network error"), + tr("The following error occurred: %1.") + .arg(tcpClient.errorString())); + + tcpClient.close(); + tcpServer.close(); + clientProgressBar->reset(); + serverProgressBar->reset(); + clientStatusLabel->setText(tr("Client ready")); + serverStatusLabel->setText(tr("Server ready")); + startButton->setEnabled(true); +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif +} diff --git a/examples/network/loopback/dialog.h b/examples/network/loopback/dialog.h new file mode 100644 index 0000000..cc15376 --- /dev/null +++ b/examples/network/loopback/dialog.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIALOG_H +#define DIALOG_H + +#include <QDialog> +#include <QTcpServer> +#include <QTcpSocket> + +QT_BEGIN_NAMESPACE +class QDialogButtonBox; +class QLabel; +class QProgressBar; +class QPushButton; +class QTcpServer; +class QTcpSocket; +QT_END_NAMESPACE + +class Dialog : public QDialog +{ + Q_OBJECT + +public: + Dialog(QWidget *parent = 0); + +public slots: + void start(); + void acceptConnection(); + void startTransfer(); + void updateServerProgress(); + void updateClientProgress(qint64 numBytes); + void displayError(QAbstractSocket::SocketError socketError); + +private: + QProgressBar *clientProgressBar; + QProgressBar *serverProgressBar; + QLabel *clientStatusLabel; + QLabel *serverStatusLabel; + QPushButton *startButton; + QPushButton *quitButton; + QDialogButtonBox *buttonBox; + + QTcpServer tcpServer; + QTcpSocket tcpClient; + QTcpSocket *tcpServerConnection; + int bytesToWrite; + int bytesWritten; + int bytesReceived; +}; + +#endif diff --git a/examples/network/loopback/loopback.pro b/examples/network/loopback/loopback.pro new file mode 100644 index 0000000..88b7a8b --- /dev/null +++ b/examples/network/loopback/loopback.pro @@ -0,0 +1,10 @@ +HEADERS = dialog.h +SOURCES = dialog.cpp \ + main.cpp +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/loopback +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS loopback.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/network/loopback +INSTALLS += target sources diff --git a/examples/network/loopback/main.cpp b/examples/network/loopback/main.cpp new file mode 100644 index 0000000..3e9e62c --- /dev/null +++ b/examples/network/loopback/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "dialog.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Dialog dialog; + dialog.show(); + return dialog.exec(); +} diff --git a/examples/network/network-chat/chatdialog.cpp b/examples/network/network-chat/chatdialog.cpp new file mode 100644 index 0000000..7e88e5b --- /dev/null +++ b/examples/network/network-chat/chatdialog.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +#include "chatdialog.h" + +ChatDialog::ChatDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + + lineEdit->setFocusPolicy(Qt::StrongFocus); + textEdit->setFocusPolicy(Qt::NoFocus); + textEdit->setReadOnly(true); + listWidget->setFocusPolicy(Qt::NoFocus); + + connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + connect(&client, SIGNAL(newMessage(const QString &, const QString &)), + this, SLOT(appendMessage(const QString &, const QString &))); + connect(&client, SIGNAL(newParticipant(const QString &)), + this, SLOT(newParticipant(const QString &))); + connect(&client, SIGNAL(participantLeft(const QString &)), + this, SLOT(participantLeft(const QString &))); + + myNickName = client.nickName(); + newParticipant(myNickName); + tableFormat.setBorder(0); + QTimer::singleShot(10 * 1000, this, SLOT(showInformation())); +} + +void ChatDialog::appendMessage(const QString &from, const QString &message) +{ + if (from.isEmpty() || message.isEmpty()) + return; + + QTextCursor cursor(textEdit->textCursor()); + cursor.movePosition(QTextCursor::End); + QTextTable *table = cursor.insertTable(1, 2, tableFormat); + table->cellAt(0, 0).firstCursorPosition().insertText("<" + from + "> "); + table->cellAt(0, 1).firstCursorPosition().insertText(message); + QScrollBar *bar = textEdit->verticalScrollBar(); + bar->setValue(bar->maximum()); +} + +void ChatDialog::returnPressed() +{ + QString text = lineEdit->text(); + if (text.isEmpty()) + return; + + if (text.startsWith(QChar('/'))) { + QColor color = textEdit->textColor(); + textEdit->setTextColor(Qt::red); + textEdit->append(tr("! Unknown command: %1") + .arg(text.left(text.indexOf(' ')))); + textEdit->setTextColor(color); + } else { + client.sendMessage(text); + appendMessage(myNickName, text); + } + + lineEdit->clear(); +} + +void ChatDialog::newParticipant(const QString &nick) +{ + if (nick.isEmpty()) + return; + + QColor color = textEdit->textColor(); + textEdit->setTextColor(Qt::gray); + textEdit->append(tr("* %1 has joined").arg(nick)); + textEdit->setTextColor(color); + listWidget->addItem(nick); +} + +void ChatDialog::participantLeft(const QString &nick) +{ + if (nick.isEmpty()) + return; + + QList<QListWidgetItem *> items = listWidget->findItems(nick, + Qt::MatchExactly); + if (items.isEmpty()) + return; + + delete items.at(0); + QColor color = textEdit->textColor(); + textEdit->setTextColor(Qt::gray); + textEdit->append(tr("* %1 has left").arg(nick)); + textEdit->setTextColor(color); +} + +void ChatDialog::showInformation() +{ + if (listWidget->count() == 1) { + QMessageBox::information(this, tr("Chat"), + tr("Launch several instances of this " + "program on your local network and " + "start chatting!")); + } +} diff --git a/examples/network/network-chat/chatdialog.h b/examples/network/network-chat/chatdialog.h new file mode 100644 index 0000000..552d794 --- /dev/null +++ b/examples/network/network-chat/chatdialog.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CHATDIALOG_H +#define CHATDIALOG_H + +#include "ui_chatdialog.h" +#include "client.h" + +class ChatDialog : public QDialog, private Ui::ChatDialog +{ + Q_OBJECT + +public: + ChatDialog(QWidget *parent = 0); + +public slots: + void appendMessage(const QString &from, const QString &message); + +private slots: + void returnPressed(); + void newParticipant(const QString &nick); + void participantLeft(const QString &nick); + void showInformation(); + +private: + Client client; + QString myNickName; + QTextTableFormat tableFormat; +}; + +#endif diff --git a/examples/network/network-chat/chatdialog.ui b/examples/network/network-chat/chatdialog.ui new file mode 100644 index 0000000..c85e0d0 --- /dev/null +++ b/examples/network/network-chat/chatdialog.ui @@ -0,0 +1,79 @@ +<ui version="4.0" > + <class>ChatDialog</class> + <widget class="QDialog" name="ChatDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>513</width> + <height>349</height> + </rect> + </property> + <property name="windowTitle" > + <string>Chat</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QTextEdit" name="textEdit" > + <property name="focusPolicy" > + <enum>Qt::NoFocus</enum> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="listWidget" > + <property name="maximumSize" > + <size> + <width>180</width> + <height>16777215</height> + </size> + </property> + <property name="focusPolicy" > + <enum>Qt::NoFocus</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="label" > + <property name="text" > + <string>Message:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit" /> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/examples/network/network-chat/client.cpp b/examples/network/network-chat/client.cpp new file mode 100644 index 0000000..d5931a6 --- /dev/null +++ b/examples/network/network-chat/client.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtNetwork> + +#include "client.h" +#include "connection.h" +#include "peermanager.h" + +Client::Client() +{ + peerManager = new PeerManager(this); + peerManager->setServerPort(server.serverPort()); + peerManager->startBroadcasting(); + + QObject::connect(peerManager, SIGNAL(newConnection(Connection *)), + this, SLOT(newConnection(Connection *))); + QObject::connect(&server, SIGNAL(newConnection(Connection *)), + this, SLOT(newConnection(Connection *))); +} + +void Client::sendMessage(const QString &message) +{ + if (message.isEmpty()) + return; + + QList<Connection *> connections = peers.values(); + foreach (Connection *connection, connections) + connection->sendMessage(message); +} + +QString Client::nickName() const +{ + return QString(peerManager->userName()) + "@" + QHostInfo::localHostName() + + ":" + QString::number(server.serverPort()); +} + +bool Client::hasConnection(const QHostAddress &senderIp, int senderPort) const +{ + if (senderPort == -1) + return peers.contains(senderIp); + + if (!peers.contains(senderIp)) + return false; + + QList<Connection *> connections = peers.values(senderIp); + foreach (Connection *connection, connections) { + if (connection->peerPort() == senderPort) + return true; + } + + return false; +} + +void Client::newConnection(Connection *connection) +{ + connection->setGreetingMessage(peerManager->userName()); + + connect(connection, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(connectionError(QAbstractSocket::SocketError))); + connect(connection, SIGNAL(disconnected()), this, SLOT(disconnected())); + connect(connection, SIGNAL(readyForUse()), this, SLOT(readyForUse())); +} + +void Client::readyForUse() +{ + Connection *connection = qobject_cast<Connection *>(sender()); + if (!connection || hasConnection(connection->peerAddress(), + connection->peerPort())) + return; + + connect(connection, SIGNAL(newMessage(const QString &, const QString &)), + this, SIGNAL(newMessage(const QString &, const QString &))); + + peers.insert(connection->peerAddress(), connection); + QString nick = connection->name(); + if (!nick.isEmpty()) + emit newParticipant(nick); +} + +void Client::disconnected() +{ + if (Connection *connection = qobject_cast<Connection *>(sender())) + removeConnection(connection); +} + +void Client::connectionError(QAbstractSocket::SocketError /* socketError */) +{ + if (Connection *connection = qobject_cast<Connection *>(sender())) + removeConnection(connection); +} + +void Client::removeConnection(Connection *connection) +{ + if (peers.contains(connection->peerAddress())) { + peers.remove(connection->peerAddress()); + QString nick = connection->name(); + if (!nick.isEmpty()) + emit participantLeft(nick); + } + connection->deleteLater(); +} diff --git a/examples/network/network-chat/client.h b/examples/network/network-chat/client.h new file mode 100644 index 0000000..307df46 --- /dev/null +++ b/examples/network/network-chat/client.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CLIENT_H +#define CLIENT_H + +#include <QAbstractSocket> +#include <QHash> +#include <QHostAddress> + +#include "server.h" + +class PeerManager; + +class Client : public QObject +{ + Q_OBJECT + +public: + Client(); + + void sendMessage(const QString &message); + QString nickName() const; + bool hasConnection(const QHostAddress &senderIp, int senderPort = -1) const; + +signals: + void newMessage(const QString &from, const QString &message); + void newParticipant(const QString &nick); + void participantLeft(const QString &nick); + +private slots: + void newConnection(Connection *connection); + void connectionError(QAbstractSocket::SocketError socketError); + void disconnected(); + void readyForUse(); + +private: + void removeConnection(Connection *connection); + + PeerManager *peerManager; + Server server; + QMultiHash<QHostAddress, Connection *> peers; +}; + +#endif diff --git a/examples/network/network-chat/connection.cpp b/examples/network/network-chat/connection.cpp new file mode 100644 index 0000000..116ca3a --- /dev/null +++ b/examples/network/network-chat/connection.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "connection.h" + +#include <QtNetwork> + +static const int TransferTimeout = 30 * 1000; +static const int PongTimeout = 60 * 1000; +static const int PingInterval = 5 * 1000; +static const char SeparatorToken = ' '; + +Connection::Connection(QObject *parent) + : QTcpSocket(parent) +{ + greetingMessage = tr("undefined"); + username = tr("unknown"); + state = WaitingForGreeting; + currentDataType = Undefined; + numBytesForCurrentDataType = -1; + transferTimerId = 0; + isGreetingMessageSent = false; + pingTimer.setInterval(PingInterval); + + QObject::connect(this, SIGNAL(readyRead()), this, SLOT(processReadyRead())); + QObject::connect(this, SIGNAL(disconnected()), &pingTimer, SLOT(stop())); + QObject::connect(&pingTimer, SIGNAL(timeout()), this, SLOT(sendPing())); + QObject::connect(this, SIGNAL(connected()), + this, SLOT(sendGreetingMessage())); +} + +QString Connection::name() const +{ + return username; +} + +void Connection::setGreetingMessage(const QString &message) +{ + greetingMessage = message; +} + +bool Connection::sendMessage(const QString &message) +{ + if (message.isEmpty()) + return false; + + QByteArray msg = message.toUtf8(); + QByteArray data = "MESSAGE " + QByteArray::number(msg.size()) + " " + msg; + return write(data) == data.size(); +} + +void Connection::timerEvent(QTimerEvent *timerEvent) +{ + if (timerEvent->timerId() == transferTimerId) { + abort(); + killTimer(transferTimerId); + transferTimerId = 0; + } +} + +void Connection::processReadyRead() +{ + if (state == WaitingForGreeting) { + if (!readProtocolHeader()) + return; + if (currentDataType != Greeting) { + abort(); + return; + } + state = ReadingGreeting; + } + + if (state == ReadingGreeting) { + if (!hasEnoughData()) + return; + + buffer = read(numBytesForCurrentDataType); + if (buffer.size() != numBytesForCurrentDataType) { + abort(); + return; + } + + username = QString(buffer) + "@" + peerAddress().toString() + ":" + + QString::number(peerPort()); + currentDataType = Undefined; + numBytesForCurrentDataType = 0; + buffer.clear(); + + if (!isValid()) { + abort(); + return; + } + + if (!isGreetingMessageSent) + sendGreetingMessage(); + + pingTimer.start(); + pongTime.start(); + state = ReadyForUse; + emit readyForUse(); + } + + do { + if (currentDataType == Undefined) { + if (!readProtocolHeader()) + return; + } + if (!hasEnoughData()) + return; + processData(); + } while (bytesAvailable() > 0); +} + +void Connection::sendPing() +{ + if (pongTime.elapsed() > PongTimeout) { + abort(); + return; + } + + write("PING 1 p"); +} + +void Connection::sendGreetingMessage() +{ + QByteArray greeting = greetingMessage.toUtf8(); + QByteArray data = "GREETING " + QByteArray::number(greeting.size()) + " " + greeting; + if (write(data) == data.size()) + isGreetingMessageSent = true; +} + +int Connection::readDataIntoBuffer(int maxSize) +{ + if (maxSize > MaxBufferSize) + return 0; + + int numBytesBeforeRead = buffer.size(); + if (numBytesBeforeRead == MaxBufferSize) { + abort(); + return 0; + } + + while (bytesAvailable() > 0 && buffer.size() < maxSize) { + buffer.append(read(1)); + if (buffer.endsWith(SeparatorToken)) + break; + } + return buffer.size() - numBytesBeforeRead; +} + +int Connection::dataLengthForCurrentDataType() +{ + if (bytesAvailable() <= 0 || readDataIntoBuffer() <= 0 + || !buffer.endsWith(SeparatorToken)) + return 0; + + buffer.chop(1); + int number = buffer.toInt(); + buffer.clear(); + return number; +} + +bool Connection::readProtocolHeader() +{ + if (transferTimerId) { + killTimer(transferTimerId); + transferTimerId = 0; + } + + if (readDataIntoBuffer() <= 0) { + transferTimerId = startTimer(TransferTimeout); + return false; + } + + if (buffer == "PING ") { + currentDataType = Ping; + } else if (buffer == "PONG ") { + currentDataType = Pong; + } else if (buffer == "MESSAGE ") { + currentDataType = PlainText; + } else if (buffer == "GREETING ") { + currentDataType = Greeting; + } else { + currentDataType = Undefined; + abort(); + return false; + } + + buffer.clear(); + numBytesForCurrentDataType = dataLengthForCurrentDataType(); + return true; +} + +bool Connection::hasEnoughData() +{ + if (transferTimerId) { + QObject::killTimer(transferTimerId); + transferTimerId = 0; + } + + if (numBytesForCurrentDataType <= 0) + numBytesForCurrentDataType = dataLengthForCurrentDataType(); + + if (bytesAvailable() < numBytesForCurrentDataType + || numBytesForCurrentDataType <= 0) { + transferTimerId = startTimer(TransferTimeout); + return false; + } + + return true; +} + +void Connection::processData() +{ + buffer = read(numBytesForCurrentDataType); + if (buffer.size() != numBytesForCurrentDataType) { + abort(); + return; + } + + switch (currentDataType) { + case PlainText: + emit newMessage(username, QString::fromUtf8(buffer)); + break; + case Ping: + write("PONG 1 p"); + break; + case Pong: + pongTime.restart(); + break; + default: + break; + } + + currentDataType = Undefined; + numBytesForCurrentDataType = 0; + buffer.clear(); +} diff --git a/examples/network/network-chat/connection.h b/examples/network/network-chat/connection.h new file mode 100644 index 0000000..0784154 --- /dev/null +++ b/examples/network/network-chat/connection.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONNECTION_H +#define CONNECTION_H + +#include <QHostAddress> +#include <QString> +#include <QTcpSocket> +#include <QTime> +#include <QTimer> + +static const int MaxBufferSize = 1024000; + +class Connection : public QTcpSocket +{ + Q_OBJECT + +public: + enum ConnectionState { + WaitingForGreeting, + ReadingGreeting, + ReadyForUse + }; + enum DataType { + PlainText, + Ping, + Pong, + Greeting, + Undefined + }; + + Connection(QObject *parent = 0); + + QString name() const; + void setGreetingMessage(const QString &message); + bool sendMessage(const QString &message); + +signals: + void readyForUse(); + void newMessage(const QString &from, const QString &message); + +protected: + void timerEvent(QTimerEvent *timerEvent); + +private slots: + void processReadyRead(); + void sendPing(); + void sendGreetingMessage(); + +private: + int readDataIntoBuffer(int maxSize = MaxBufferSize); + int dataLengthForCurrentDataType(); + bool readProtocolHeader(); + bool hasEnoughData(); + void processData(); + + QString greetingMessage; + QString username; + QTimer pingTimer; + QTime pongTime; + QByteArray buffer; + ConnectionState state; + DataType currentDataType; + int numBytesForCurrentDataType; + int transferTimerId; + bool isGreetingMessageSent; +}; + +#endif diff --git a/examples/network/network-chat/main.cpp b/examples/network/network-chat/main.cpp new file mode 100644 index 0000000..ffe28c9 --- /dev/null +++ b/examples/network/network-chat/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "chatdialog.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + ChatDialog dialog; + dialog.show(); + return app.exec(); +} diff --git a/examples/network/network-chat/network-chat.pro b/examples/network/network-chat/network-chat.pro new file mode 100644 index 0000000..5d5efea --- /dev/null +++ b/examples/network/network-chat/network-chat.pro @@ -0,0 +1,19 @@ +HEADERS = chatdialog.h \ + client.h \ + connection.h \ + peermanager.h \ + server.h +SOURCES = chatdialog.cpp \ + client.cpp \ + connection.cpp \ + main.cpp \ + peermanager.cpp \ + server.cpp +FORMS = chatdialog.ui +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/network-chat +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS network-chat.pro *.chat +sources.path = $$[QT_INSTALL_EXAMPLES]/network/network-chat +INSTALLS += target sources diff --git a/examples/network/network-chat/peermanager.cpp b/examples/network/network-chat/peermanager.cpp new file mode 100644 index 0000000..4ed4c5a --- /dev/null +++ b/examples/network/network-chat/peermanager.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtNetwork> + +#include "client.h" +#include "connection.h" +#include "peermanager.h" + +static const qint32 BroadcastInterval = 2000; +static const unsigned broadcastPort = 45000; + +PeerManager::PeerManager(Client *client) + : QObject(client) +{ + this->client = client; + + QStringList envVariables; + envVariables << "USERNAME.*" << "USER.*" << "USERDOMAIN.*" + << "HOSTNAME.*" << "DOMAINNAME.*"; + + QStringList environment = QProcess::systemEnvironment(); + foreach (QString string, envVariables) { + int index = environment.indexOf(QRegExp(string)); + if (index != -1) { + QStringList stringList = environment.at(index).split("="); + if (stringList.size() == 2) { + username = stringList.at(1).toUtf8(); + break; + } + } + } + + if (username.isEmpty()) + username = "unknown"; + + updateAddresses(); + serverPort = 0; + + broadcastSocket.bind(QHostAddress::Any, broadcastPort, QUdpSocket::ShareAddress + | QUdpSocket::ReuseAddressHint); + connect(&broadcastSocket, SIGNAL(readyRead()), + this, SLOT(readBroadcastDatagram())); + + broadcastTimer.setInterval(BroadcastInterval); + connect(&broadcastTimer, SIGNAL(timeout()), + this, SLOT(sendBroadcastDatagram())); +} + +void PeerManager::setServerPort(int port) +{ + serverPort = port; +} + +QByteArray PeerManager::userName() const +{ + return username; +} + +void PeerManager::startBroadcasting() +{ + broadcastTimer.start(); +} + +bool PeerManager::isLocalHostAddress(const QHostAddress &address) +{ + foreach (QHostAddress localAddress, ipAddresses) { + if (address == localAddress) + return true; + } + return false; +} + +void PeerManager::sendBroadcastDatagram() +{ + QByteArray datagram(username); + datagram.append('@'); + datagram.append(QByteArray::number(serverPort)); + + bool validBroadcastAddresses = true; + foreach (QHostAddress address, broadcastAddresses) { + if (broadcastSocket.writeDatagram(datagram, address, + broadcastPort) == -1) + validBroadcastAddresses = false; + } + + if (!validBroadcastAddresses) + updateAddresses(); +} + +void PeerManager::readBroadcastDatagram() +{ + while (broadcastSocket.hasPendingDatagrams()) { + QHostAddress senderIp; + quint16 senderPort; + QByteArray datagram; + datagram.resize(broadcastSocket.pendingDatagramSize()); + if (broadcastSocket.readDatagram(datagram.data(), datagram.size(), + &senderIp, &senderPort) == -1) + continue; + + QList<QByteArray> list = datagram.split('@'); + if (list.size() != 2) + continue; + + int senderServerPort = list.at(1).toInt(); + if (isLocalHostAddress(senderIp) && senderServerPort == serverPort) + continue; + + if (!client->hasConnection(senderIp)) { + Connection *connection = new Connection(this); + emit newConnection(connection); + connection->connectToHost(senderIp, senderServerPort); + } + } +} + +void PeerManager::updateAddresses() +{ + broadcastAddresses.clear(); + ipAddresses.clear(); + foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) { + foreach (QNetworkAddressEntry entry, interface.addressEntries()) { + QHostAddress broadcastAddress = entry.broadcast(); + if (broadcastAddress != QHostAddress::Null && + entry.ip() != QHostAddress::LocalHost) { + broadcastAddresses << broadcastAddress; + ipAddresses << entry.ip(); + } + } + } +} diff --git a/examples/network/network-chat/peermanager.h b/examples/network/network-chat/peermanager.h new file mode 100644 index 0000000..a729329 --- /dev/null +++ b/examples/network/network-chat/peermanager.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PEERMANAGER_H +#define PEERMANAGER_H + +#include <QByteArray> +#include <QList> +#include <QObject> +#include <QTimer> +#include <QUdpSocket> + +class Client; +class Connection; + +class PeerManager : public QObject +{ + Q_OBJECT + +public: + PeerManager(Client *client); + + void setServerPort(int port); + QByteArray userName() const; + void startBroadcasting(); + bool isLocalHostAddress(const QHostAddress &address); + +signals: + void newConnection(Connection *connection); + +private slots: + void sendBroadcastDatagram(); + void readBroadcastDatagram(); + +private: + void updateAddresses(); + + Client *client; + QList<QHostAddress> broadcastAddresses; + QList<QHostAddress> ipAddresses; + QUdpSocket broadcastSocket; + QTimer broadcastTimer; + QByteArray username; + int serverPort; +}; + +#endif diff --git a/examples/network/network-chat/server.cpp b/examples/network/network-chat/server.cpp new file mode 100644 index 0000000..26f1e70 --- /dev/null +++ b/examples/network/network-chat/server.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtNetwork> + +#include "connection.h" +#include "server.h" + +Server::Server(QObject *parent) + : QTcpServer(parent) +{ + listen(QHostAddress::Any); +} + +void Server::incomingConnection(int socketDescriptor) +{ + Connection *connection = new Connection(this); + connection->setSocketDescriptor(socketDescriptor); + emit newConnection(connection); +} diff --git a/examples/network/network-chat/server.h b/examples/network/network-chat/server.h new file mode 100644 index 0000000..d297693 --- /dev/null +++ b/examples/network/network-chat/server.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SERVER_H +#define SERVER_H + +#include <QTcpServer> + +class Connection; + +class Server : public QTcpServer +{ + Q_OBJECT + +public: + Server(QObject *parent = 0); + +signals: + void newConnection(Connection *connection); + +protected: + void incomingConnection(int socketDescriptor); +}; + +#endif diff --git a/examples/network/network.pro b/examples/network/network.pro new file mode 100644 index 0000000..13b3116 --- /dev/null +++ b/examples/network/network.pro @@ -0,0 +1,21 @@ +TEMPLATE = subdirs +SUBDIRS = blockingfortuneclient \ + broadcastreceiver \ + broadcastsender \ + network-chat \ + download \ + downloadmanager \ + fortuneclient \ + fortuneserver \ + ftp \ + http \ + loopback \ + threadedfortuneserver \ + torrent + +contains(QT_CONFIG, openssl):SUBDIRS += securesocketclient + +# install +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS network.pro README +sources.path = $$[QT_INSTALL_EXAMPLES]/network +INSTALLS += sources diff --git a/examples/network/securesocketclient/certificateinfo.cpp b/examples/network/securesocketclient/certificateinfo.cpp new file mode 100644 index 0000000..d9be2e3 --- /dev/null +++ b/examples/network/securesocketclient/certificateinfo.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "certificateinfo.h" +#include "ui_certificateinfo.h" + +CertificateInfo::CertificateInfo(QWidget *parent) + : QDialog(parent) +{ + form = new Ui_CertificateInfo; + form->setupUi(this); + + connect(form->certificationPathView, SIGNAL(currentRowChanged(int)), + this, SLOT(updateCertificateInfo(int))); +} + +CertificateInfo::~CertificateInfo() +{ + delete form; +} + +void CertificateInfo::setCertificateChain(const QList<QSslCertificate> &chain) +{ + this->chain = chain; + + form->certificationPathView->clear(); + + for (int i = 0; i < chain.size(); ++i) { + const QSslCertificate &cert = chain.at(i); + form->certificationPathView->addItem(tr("%1%2 (%3)").arg(!i ? QString() : tr("Issued by: ")) + .arg(cert.subjectInfo(QSslCertificate::Organization)) + .arg(cert.subjectInfo(QSslCertificate::CommonName))); + } + + form->certificationPathView->setCurrentRow(0); +} + +void CertificateInfo::updateCertificateInfo(int index) +{ + form->certificateInfoView->clear(); + if (index >= 0 && index < chain.size()) { + const QSslCertificate &cert = chain.at(index); + QStringList lines; + lines << tr("Organization: %1").arg(cert.subjectInfo(QSslCertificate::Organization)) + << tr("Subunit: %1").arg(cert.subjectInfo(QSslCertificate::OrganizationalUnitName)) + << tr("Country: %1").arg(cert.subjectInfo(QSslCertificate::CountryName)) + << tr("Locality: %1").arg(cert.subjectInfo(QSslCertificate::LocalityName)) + << tr("State/Province: %1").arg(cert.subjectInfo(QSslCertificate::StateOrProvinceName)) + << tr("Common Name: %1").arg(cert.subjectInfo(QSslCertificate::CommonName)) + << QString() + << tr("Issuer Organization: %1").arg(cert.issuerInfo(QSslCertificate::Organization)) + << tr("Issuer Unit Name: %1").arg(cert.issuerInfo(QSslCertificate::OrganizationalUnitName)) + << tr("Issuer Country: %1").arg(cert.issuerInfo(QSslCertificate::CountryName)) + << tr("Issuer Locality: %1").arg(cert.issuerInfo(QSslCertificate::LocalityName)) + << tr("Issuer State/Province: %1").arg(cert.issuerInfo(QSslCertificate::StateOrProvinceName)) + << tr("Issuer Common Name: %1").arg(cert.issuerInfo(QSslCertificate::CommonName)); + foreach (QString line, lines) + form->certificateInfoView->addItem(line); + } else { + form->certificateInfoView->clear(); + } +} diff --git a/examples/network/securesocketclient/certificateinfo.h b/examples/network/securesocketclient/certificateinfo.h new file mode 100644 index 0000000..dc6a200 --- /dev/null +++ b/examples/network/securesocketclient/certificateinfo.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CERTIFICATEINFO_H +#define CERTIFICATEINFO_H + +#include <QtGui/QDialog> +#include <QtNetwork/QSslCertificate> + +QT_BEGIN_NAMESPACE +class Ui_CertificateInfo; +QT_END_NAMESPACE + +class CertificateInfo : public QDialog +{ + Q_OBJECT +public: + CertificateInfo(QWidget *parent = 0); + ~CertificateInfo(); + + void setCertificateChain(const QList<QSslCertificate> &chain); + +private slots: + void updateCertificateInfo(int index); + +private: + Ui_CertificateInfo *form; + QList<QSslCertificate> chain; +}; + +#endif diff --git a/examples/network/securesocketclient/certificateinfo.ui b/examples/network/securesocketclient/certificateinfo.ui new file mode 100644 index 0000000..3761fe8 --- /dev/null +++ b/examples/network/securesocketclient/certificateinfo.ui @@ -0,0 +1,85 @@ +<ui version="4.0" > + <class>CertificateInfo</class> + <widget class="QDialog" name="CertificateInfo" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>397</height> + </rect> + </property> + <property name="windowTitle" > + <string>Display Certificate Information</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Certification Path</string> + </property> + <layout class="QHBoxLayout" > + <item> + <widget class="QListWidget" name="certificationPathView" /> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2" > + <property name="title" > + <string>Certificate Information</string> + </property> + <layout class="QHBoxLayout" > + <item> + <widget class="QListWidget" name="certificateInfoView" /> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="standardButtons" > + <set>QDialogButtonBox::Close</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>clicked(QAbstractButton*)</signal> + <receiver>CertificateInfo</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>343</x> + <y>374</y> + </hint> + <hint type="destinationlabel" > + <x>352</x> + <y>422</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/examples/network/securesocketclient/encrypted.png b/examples/network/securesocketclient/encrypted.png Binary files differnew file mode 100644 index 0000000..04a05c1 --- /dev/null +++ b/examples/network/securesocketclient/encrypted.png diff --git a/examples/network/securesocketclient/main.cpp b/examples/network/securesocketclient/main.cpp new file mode 100644 index 0000000..200aa26 --- /dev/null +++ b/examples/network/securesocketclient/main.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QMessageBox> + +#include "sslclient.h" + +int main(int argc, char **argv) +{ + Q_INIT_RESOURCE(securesocketclient); + + QApplication app(argc, argv); + + if (!QSslSocket::supportsSsl()) { + QMessageBox::information(0, "Secure Socket Client", + "This system does not support OpenSSL."); + return -1; + } + + SslClient client; + client.show(); + + return app.exec(); +} diff --git a/examples/network/securesocketclient/securesocketclient.pro b/examples/network/securesocketclient/securesocketclient.pro new file mode 100644 index 0000000..4d70a71 --- /dev/null +++ b/examples/network/securesocketclient/securesocketclient.pro @@ -0,0 +1,16 @@ +HEADERS += certificateinfo.h \ + sslclient.h +SOURCES += certificateinfo.cpp \ + main.cpp \ + sslclient.cpp +RESOURCES += securesocketclient.qrc +FORMS += certificateinfo.ui \ + sslclient.ui \ + sslerrors.ui +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/securesocketclient +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS *.pro *.png *.jpg images +sources.path = $$[QT_INSTALL_EXAMPLES]/network/securesocketclient +INSTALLS += target sources diff --git a/examples/network/securesocketclient/securesocketclient.qrc b/examples/network/securesocketclient/securesocketclient.qrc new file mode 100644 index 0000000..83b49c7 --- /dev/null +++ b/examples/network/securesocketclient/securesocketclient.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>encrypted.png</file> +</qresource> +</RCC> diff --git a/examples/network/securesocketclient/sslclient.cpp b/examples/network/securesocketclient/sslclient.cpp new file mode 100644 index 0000000..bf8443d --- /dev/null +++ b/examples/network/securesocketclient/sslclient.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "certificateinfo.h" +#include "sslclient.h" +#include "ui_sslclient.h" +#include "ui_sslerrors.h" + +#include <QtGui/QScrollBar> +#include <QtGui/QStyle> +#include <QtGui/QToolButton> +#include <QtNetwork/QSslCipher> + +SslClient::SslClient(QWidget *parent) + : QWidget(parent), socket(0), padLock(0), executingDialog(false) +{ + form = new Ui_Form; + form->setupUi(this); + form->hostNameEdit->setSelection(0, form->hostNameEdit->text().size()); + form->sessionOutput->setHtml(tr("<not connected>")); + + connect(form->hostNameEdit, SIGNAL(textChanged(QString)), + this, SLOT(updateEnabledState())); + connect(form->connectButton, SIGNAL(clicked()), + this, SLOT(secureConnect())); + connect(form->sendButton, SIGNAL(clicked()), + this, SLOT(sendData())); +} + +SslClient::~SslClient() +{ + delete form; +} + +void SslClient::updateEnabledState() +{ + bool unconnected = !socket || socket->state() == QAbstractSocket::UnconnectedState; + + form->hostNameEdit->setReadOnly(!unconnected); + form->hostNameEdit->setFocusPolicy(unconnected ? Qt::StrongFocus : Qt::NoFocus); + + form->hostNameLabel->setEnabled(unconnected); + form->portBox->setEnabled(unconnected); + form->portLabel->setEnabled(unconnected); + form->connectButton->setEnabled(unconnected && !form->hostNameEdit->text().isEmpty()); + + bool connected = socket && socket->state() == QAbstractSocket::ConnectedState; + form->sessionBox->setEnabled(connected); + form->sessionOutput->setEnabled(connected); + form->sessionInput->setEnabled(connected); + form->sessionInputLabel->setEnabled(connected); + form->sendButton->setEnabled(connected); +} + +void SslClient::secureConnect() +{ + if (!socket) { + socket = new QSslSocket(this); + connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(socketStateChanged(QAbstractSocket::SocketState))); + connect(socket, SIGNAL(encrypted()), + this, SLOT(socketEncrypted())); + connect(socket, SIGNAL(sslErrors(QList<QSslError>)), + this, SLOT(sslErrors(QList<QSslError>))); + connect(socket, SIGNAL(readyRead()), + this, SLOT(socketReadyRead())); + } + + socket->connectToHostEncrypted(form->hostNameEdit->text(), form->portBox->value()); + updateEnabledState(); +} + +void SslClient::socketStateChanged(QAbstractSocket::SocketState state) +{ + if (executingDialog) + return; + + updateEnabledState(); + if (state == QAbstractSocket::UnconnectedState) { + form->hostNameEdit->setPalette(QPalette()); + form->hostNameEdit->setFocus(); + form->cipherLabel->setText(tr("<none>")); + if (padLock) + padLock->hide(); + socket->deleteLater(); + socket = 0; + } +} + +void SslClient::socketEncrypted() +{ + if (!socket) + return; // might have disconnected already + + form->sessionOutput->clear(); + form->sessionInput->setFocus(); + + QPalette palette; + palette.setColor(QPalette::Base, QColor(255, 255, 192)); + form->hostNameEdit->setPalette(palette); + + QSslCipher ciph = socket->sessionCipher(); + QString cipher = QString("%1, %2 (%3/%4)").arg(ciph.authenticationMethod()) + .arg(ciph.name()).arg(ciph.usedBits()).arg(ciph.supportedBits());; + form->cipherLabel->setText(cipher); + + if (!padLock) { + padLock = new QToolButton; + padLock->setIcon(QIcon(":/encrypted.png")); + padLock->setCursor(Qt::ArrowCursor); + padLock->setToolTip(tr("Display encryption details.")); + + int extent = form->hostNameEdit->height() - 2; + padLock->resize(extent, extent); + padLock->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); + + QHBoxLayout *layout = new QHBoxLayout(form->hostNameEdit); + layout->setMargin(form->hostNameEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth)); + layout->setSpacing(0); + layout->addStretch(); + layout->addWidget(padLock); + + form->hostNameEdit->setLayout(layout); + + connect(padLock, SIGNAL(clicked()), + this, SLOT(displayCertificateInfo())); + } else { + padLock->show(); + } +} + +void SslClient::socketReadyRead() +{ + appendString(QString::fromUtf8(socket->readAll())); +} + +void SslClient::sendData() +{ + QString input = form->sessionInput->text(); + appendString(input + "\n"); + socket->write(input.toUtf8() + "\r\n"); + form->sessionInput->clear(); +} + +void SslClient::sslErrors(const QList<QSslError> &errors) +{ + QDialog errorDialog(this); + Ui_SslErrors ui; + ui.setupUi(&errorDialog); + connect(ui.certificateChainButton, SIGNAL(clicked()), + this, SLOT(displayCertificateInfo())); + + foreach (const QSslError &error, errors) + ui.sslErrorList->addItem(error.errorString()); + + executingDialog = true; + if (errorDialog.exec() == QDialog::Accepted) + socket->ignoreSslErrors(); + executingDialog = false; + + // did the socket state change? + if (socket->state() != QAbstractSocket::ConnectedState) + socketStateChanged(socket->state()); +} + +void SslClient::displayCertificateInfo() +{ + CertificateInfo *info = new CertificateInfo(this); + info->setCertificateChain(socket->peerCertificateChain()); + info->exec(); + info->deleteLater(); +} + +void SslClient::appendString(const QString &line) +{ + QTextCursor cursor(form->sessionOutput->textCursor()); + cursor.movePosition(QTextCursor::End); + cursor.insertText(line); + form->sessionOutput->verticalScrollBar()->setValue(form->sessionOutput->verticalScrollBar()->maximum()); +} diff --git a/examples/network/securesocketclient/sslclient.h b/examples/network/securesocketclient/sslclient.h new file mode 100644 index 0000000..ae208bb --- /dev/null +++ b/examples/network/securesocketclient/sslclient.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SSLCLIENT_H +#define SSLCLIENT_H + +#include <QtGui/QWidget> +#include <QtNetwork/QAbstractSocket> +#include <QtNetwork/QSslSocket> + +QT_BEGIN_NAMESPACE +class QSslSocket; +class QToolButton; +class Ui_Form; +QT_END_NAMESPACE + +class SslClient : public QWidget +{ + Q_OBJECT +public: + SslClient(QWidget *parent = 0); + ~SslClient(); + +private slots: + void updateEnabledState(); + void secureConnect(); + void socketStateChanged(QAbstractSocket::SocketState state); + void socketEncrypted(); + void socketReadyRead(); + void sendData(); + void sslErrors(const QList<QSslError> &errors); + void displayCertificateInfo(); + +private: + void appendString(const QString &line); + + QSslSocket *socket; + QToolButton *padLock; + Ui_Form *form; + bool executingDialog; +}; + +#endif diff --git a/examples/network/securesocketclient/sslclient.ui b/examples/network/securesocketclient/sslclient.ui new file mode 100644 index 0000000..5a24751 --- /dev/null +++ b/examples/network/securesocketclient/sslclient.ui @@ -0,0 +1,190 @@ +<ui version="4.0" > + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>343</width> + <height>320</height> + </rect> + </property> + <property name="windowTitle" > + <string>Secure Socket Client</string> + </property> + <layout class="QVBoxLayout" > + <item> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="hostNameLabel" > + <property name="text" > + <string>Host name:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="hostNameEdit" > + <property name="text" > + <string>imap.example.com</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="portLabel" > + <property name="text" > + <string>Port:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QSpinBox" name="portBox" > + <property name="minimum" > + <number>1</number> + </property> + <property name="maximum" > + <number>65535</number> + </property> + <property name="value" > + <number>993</number> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QPushButton" name="connectButton" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="text" > + <string>Connect to host</string> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="sessionBox" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="title" > + <string>Active session</string> + </property> + <layout class="QVBoxLayout" > + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QLabel" name="cipherText" > + <property name="text" > + <string>Cryptographic Cipher:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="cipherLabel" > + <property name="text" > + <string><none></string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QTextEdit" name="sessionOutput" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="focusPolicy" > + <enum>Qt::NoFocus</enum> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + <property name="html" > + <string><html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html></string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QLabel" name="sessionInputLabel" > + <property name="text" > + <string>Input:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="sessionInput" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="sendButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="focusPolicy" > + <enum>Qt::TabFocus</enum> + </property> + <property name="text" > + <string>&Send</string> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>hostNameEdit</sender> + <signal>returnPressed()</signal> + <receiver>connectButton</receiver> + <slot>animateClick()</slot> + <hints> + <hint type="sourcelabel" > + <x>126</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>142</x> + <y>78</y> + </hint> + </hints> + </connection> + <connection> + <sender>sessionInput</sender> + <signal>returnPressed()</signal> + <receiver>sendButton</receiver> + <slot>animateClick()</slot> + <hints> + <hint type="sourcelabel" > + <x>142</x> + <y>241</y> + </hint> + <hint type="destinationlabel" > + <x>297</x> + <y>234</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/examples/network/securesocketclient/sslerrors.ui b/examples/network/securesocketclient/sslerrors.ui new file mode 100644 index 0000000..4aac18c --- /dev/null +++ b/examples/network/securesocketclient/sslerrors.ui @@ -0,0 +1,110 @@ +<ui version="4.0" > + <class>SslErrors</class> + <widget class="QDialog" name="SslErrors" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>371</width> + <height>216</height> + </rect> + </property> + <property name="windowTitle" > + <string>Unable To Validate The Connection</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QLabel" name="label" > + <property name="text" > + <string><html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; color:#ff0000;">Warning</span><span style=" color:#ff0000;">:</span><span style=" color:#000000;"> One or more errors with this connection prevent validating the authenticity of the host you are connecting to. Please review the following list of errors, and click </span><span style=" color:#000000;">Ignore</span><span style=" color:#000000;"> to continue, or </span><span style=" color:#000000;">Cancel</span><span style=" color:#000000;"> to abort the connection.</span></p></body></html></string> + </property> + <property name="wordWrap" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="sslErrorList" /> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QPushButton" name="certificateChainButton" > + <property name="text" > + <string>View Certificate Chain</string> + </property> + <property name="autoDefault" > + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton" > + <property name="text" > + <string>Ignore</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_2" > + <property name="text" > + <string>Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>pushButton</sender> + <signal>clicked()</signal> + <receiver>SslErrors</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>235</x> + <y>185</y> + </hint> + <hint type="destinationlabel" > + <x>228</x> + <y>287</y> + </hint> + </hints> + </connection> + <connection> + <sender>pushButton_2</sender> + <signal>clicked()</signal> + <receiver>SslErrors</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>325</x> + <y>192</y> + </hint> + <hint type="destinationlabel" > + <x>333</x> + <y>231</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/examples/network/threadedfortuneserver/dialog.cpp b/examples/network/threadedfortuneserver/dialog.cpp new file mode 100644 index 0000000..4161c7f --- /dev/null +++ b/examples/network/threadedfortuneserver/dialog.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtNetwork> + +#include <stdlib.h> + +#include "dialog.h" +#include "fortuneserver.h" + +Dialog::Dialog(QWidget *parent) + : QDialog(parent) +{ + statusLabel = new QLabel; + quitButton = new QPushButton(tr("Quit")); + quitButton->setAutoDefault(false); + + if (!server.listen()) { + QMessageBox::critical(this, tr("Threaded Fortune Server"), + tr("Unable to start the server: %1.") + .arg(server.errorString())); + close(); + return; + } + + statusLabel->setText(tr("The server is running on port %1.\n" + "Run the Fortune Client example now.") + .arg(server.serverPort())); + + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); + + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(1); + buttonLayout->addWidget(quitButton); + buttonLayout->addStretch(1); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(statusLabel); + mainLayout->addLayout(buttonLayout); + setLayout(mainLayout); + + setWindowTitle(tr("Threaded Fortune Server")); +} diff --git a/examples/network/threadedfortuneserver/dialog.h b/examples/network/threadedfortuneserver/dialog.h new file mode 100644 index 0000000..5fe1c67 --- /dev/null +++ b/examples/network/threadedfortuneserver/dialog.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIALOG_H +#define DIALOG_H + +#include <QDialog> +#include "fortuneserver.h" + +QT_BEGIN_NAMESPACE +class QLabel; +class QPushButton; +QT_END_NAMESPACE + +class Dialog : public QDialog +{ + Q_OBJECT + +public: + Dialog(QWidget *parent = 0); + +private: + QLabel *statusLabel; + QPushButton *quitButton; + FortuneServer server; +}; + +#endif diff --git a/examples/network/threadedfortuneserver/fortuneserver.cpp b/examples/network/threadedfortuneserver/fortuneserver.cpp new file mode 100644 index 0000000..e23c899 --- /dev/null +++ b/examples/network/threadedfortuneserver/fortuneserver.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "fortuneserver.h" +#include "fortunethread.h" + +#include <stdlib.h> + +//! [0] +FortuneServer::FortuneServer(QObject *parent) + : QTcpServer(parent) +{ + fortunes << tr("You've been leading a dog's life. Stay off the furniture.") + << tr("You've got to think about tomorrow.") + << tr("You will be surprised by a loud noise.") + << tr("You will feel hungry again in another hour.") + << tr("You might have mail.") + << tr("You cannot kill time without injuring eternity.") + << tr("Computers are not intelligent. They only think they are."); +} +//! [0] + +//! [1] +void FortuneServer::incomingConnection(int socketDescriptor) +{ + QString fortune = fortunes.at(qrand() % fortunes.size()); + FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); +} +//! [1] diff --git a/examples/network/threadedfortuneserver/fortuneserver.h b/examples/network/threadedfortuneserver/fortuneserver.h new file mode 100644 index 0000000..2374f85 --- /dev/null +++ b/examples/network/threadedfortuneserver/fortuneserver.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORTUNESERVER_H +#define FORTUNESERVER_H + +#include <QStringList> +#include <QTcpServer> + +//! [0] +class FortuneServer : public QTcpServer +{ + Q_OBJECT + +public: + FortuneServer(QObject *parent = 0); + +protected: + void incomingConnection(int socketDescriptor); + +private: + QStringList fortunes; +}; +//! [0] + +#endif diff --git a/examples/network/threadedfortuneserver/fortunethread.cpp b/examples/network/threadedfortuneserver/fortunethread.cpp new file mode 100644 index 0000000..8c41076 --- /dev/null +++ b/examples/network/threadedfortuneserver/fortunethread.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "fortunethread.h" + +#include <QtNetwork> + +//! [0] +FortuneThread::FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent) + : QThread(parent), socketDescriptor(socketDescriptor), text(fortune) +{ +} +//! [0] + +//! [1] +void FortuneThread::run() +{ + QTcpSocket tcpSocket; +//! [1] //! [2] + if (!tcpSocket.setSocketDescriptor(socketDescriptor)) { + emit error(tcpSocket.error()); + return; + } +//! [2] //! [3] + + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_0); + out << (quint16)0; + out << text; + out.device()->seek(0); + out << (quint16)(block.size() - sizeof(quint16)); +//! [3] //! [4] + + tcpSocket.write(block); + tcpSocket.disconnectFromHost(); + tcpSocket.waitForDisconnected(); +} +//! [4] diff --git a/examples/network/threadedfortuneserver/fortunethread.h b/examples/network/threadedfortuneserver/fortunethread.h new file mode 100644 index 0000000..e534032 --- /dev/null +++ b/examples/network/threadedfortuneserver/fortunethread.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORTUNETHREAD_H +#define FORTUNETHREAD_H + +#include <QThread> +#include <QTcpSocket> + +//! [0] +class FortuneThread : public QThread +{ + Q_OBJECT + +public: + FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent); + + void run(); + +signals: + void error(QTcpSocket::SocketError socketError); + +private: + int socketDescriptor; + QString text; +}; +//! [0] + +#endif diff --git a/examples/network/threadedfortuneserver/main.cpp b/examples/network/threadedfortuneserver/main.cpp new file mode 100644 index 0000000..5187495 --- /dev/null +++ b/examples/network/threadedfortuneserver/main.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QtCore> + +#include <stdlib.h> + +#include "dialog.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Dialog dialog; + dialog.show(); + qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); + return dialog.exec(); +} diff --git a/examples/network/threadedfortuneserver/threadedfortuneserver.pro b/examples/network/threadedfortuneserver/threadedfortuneserver.pro new file mode 100644 index 0000000..0867dac --- /dev/null +++ b/examples/network/threadedfortuneserver/threadedfortuneserver.pro @@ -0,0 +1,14 @@ +HEADERS = dialog.h \ + fortuneserver.h \ + fortunethread.h +SOURCES = dialog.cpp \ + fortuneserver.cpp \ + fortunethread.cpp \ + main.cpp +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/threadedfortuneserver +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS threadedfortuneserver.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/network/threadedfortuneserver +INSTALLS += target sources diff --git a/examples/network/torrent/addtorrentdialog.cpp b/examples/network/torrent/addtorrentdialog.cpp new file mode 100644 index 0000000..fb43f59 --- /dev/null +++ b/examples/network/torrent/addtorrentdialog.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "addtorrentdialog.h" +#include "metainfo.h" + +#include <QFile> +#include <QFileDialog> +#include <QLineEdit> +#include <QMetaObject> + +static QString stringNumber(qint64 number) +{ + QString tmp; + if (number > (1024 * 1024 * 1024)) + tmp.sprintf("%.2fGB", number / (1024.0 * 1024.0 * 1024.0)); + else if (number > (1024 * 1024)) + tmp.sprintf("%.2fMB", number / (1024.0 * 1024.0)); + else if (number > (1024)) + tmp.sprintf("%.2fKB", number / (1024.0)); + else + tmp.sprintf("%d bytes", int(number)); + return tmp; +} + +AddTorrentDialog::AddTorrentDialog(QWidget *parent) + : QDialog(parent, Qt::Sheet) +{ + ui.setupUi(this); + + connect(ui.browseTorrents, SIGNAL(clicked()), + this, SLOT(selectTorrent())); + connect(ui.browseDestination, SIGNAL(clicked()), + this, SLOT(selectDestination())); + connect(ui.torrentFile, SIGNAL(textChanged(const QString &)), + this, SLOT(setTorrent(const QString &))); + + ui.destinationFolder->setText(destinationDirectory = QDir::current().path()); + ui.torrentFile->setFocus(); +} + +void AddTorrentDialog::selectTorrent() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Choose a torrent file"), + lastDirectory, + tr("Torrents (*.torrent);; All files (*.*)")); + if (fileName.isEmpty()) + return; + lastDirectory = QFileInfo(fileName).absolutePath(); + setTorrent(fileName); +} + +void AddTorrentDialog::selectDestination() +{ + QString dir = QFileDialog::getExistingDirectory(this, tr("Choose a destination directory"), + lastDestinationDirectory); + if (dir.isEmpty()) + return; + lastDestinationDirectory = destinationDirectory = dir; + ui.destinationFolder->setText(destinationDirectory); + enableOkButton(); +} + +void AddTorrentDialog::enableOkButton() +{ + ui.okButton->setEnabled(!ui.destinationFolder->text().isEmpty() + && !ui.torrentFile->text().isEmpty()); +} + +void AddTorrentDialog::setTorrent(const QString &torrentFile) +{ + if (torrentFile.isEmpty()) { + enableOkButton(); + return; + } + + ui.torrentFile->setText(torrentFile); + if (!torrentFile.isEmpty()) + lastDirectory = QFileInfo(torrentFile).absolutePath(); + + if (lastDestinationDirectory.isEmpty()) + lastDestinationDirectory = lastDirectory; + + MetaInfo metaInfo; + QFile torrent(torrentFile); + if (!torrent.open(QFile::ReadOnly) || !metaInfo.parse(torrent.readAll())) { + enableOkButton(); + return; + } + + ui.torrentFile->setText(torrentFile); + ui.announceUrl->setText(metaInfo.announceUrl()); + if (metaInfo.comment().isEmpty()) + ui.commentLabel->setText("<unknown>"); + else + ui.commentLabel->setText(metaInfo.comment()); + if (metaInfo.createdBy().isEmpty()) + ui.creatorLabel->setText("<unknown>"); + else + ui.creatorLabel->setText(metaInfo.createdBy()); + ui.sizeLabel->setText(stringNumber(metaInfo.totalSize())); + if (metaInfo.fileForm() == MetaInfo::SingleFileForm) { + ui.torrentContents->setHtml(metaInfo.singleFile().name); + } else { + QString html; + foreach (MetaInfoMultiFile file, metaInfo.multiFiles()) { + QString name = metaInfo.name(); + if (!name.isEmpty()) { + html += name; + if (!name.endsWith('/')) + html += '/'; + } + html += file.path + "<br>"; + } + ui.torrentContents->setHtml(html); + } + + QFileInfo info(torrentFile); + ui.destinationFolder->setText(info.absolutePath()); + + enableOkButton(); +} + +QString AddTorrentDialog::torrentFileName() const +{ + return ui.torrentFile->text(); +} + +QString AddTorrentDialog::destinationFolder() const +{ + return ui.destinationFolder->text(); +} diff --git a/examples/network/torrent/addtorrentdialog.h b/examples/network/torrent/addtorrentdialog.h new file mode 100644 index 0000000..89cb77c --- /dev/null +++ b/examples/network/torrent/addtorrentdialog.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ADDTORRENTDIALOG_H +#define ADDTORRENTDIALOG_H + +#include <QDialog> + +#include "ui_addtorrentform.h" + +class AddTorrentDialog : public QDialog +{ + Q_OBJECT + +public: + AddTorrentDialog(QWidget *parent = 0); + + QString torrentFileName() const; + QString destinationFolder() const; + +public slots: + void setTorrent(const QString &torrentFile); + +private slots: + void selectTorrent(); + void selectDestination(); + void enableOkButton(); + +private: + Ui_AddTorrentFile ui; + QString destinationDirectory; + QString lastDirectory; + QString lastDestinationDirectory; +}; + +#endif diff --git a/examples/network/torrent/bencodeparser.cpp b/examples/network/torrent/bencodeparser.cpp new file mode 100644 index 0000000..9311c0c --- /dev/null +++ b/examples/network/torrent/bencodeparser.cpp @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bencodeparser.h" + +#include <QList> +#include <QMetaType> + +BencodeParser::BencodeParser() +{ +} + +bool BencodeParser::parse(const QByteArray &content) +{ + if (content.isEmpty()) { + errString = QString("No content"); + return false; + } + + this->content = content; + index = 0; + infoStart = 0; + infoLength = 0; + return getDictionary(&dictionaryValue); +} + +QString BencodeParser::errorString() const +{ + return errString; +} + +QMap<QByteArray, QVariant> BencodeParser::dictionary() const +{ + return dictionaryValue; +} + +QByteArray BencodeParser::infoSection() const +{ + return content.mid(infoStart, infoLength); +} + +bool BencodeParser::getByteString(QByteArray *byteString) +{ + const int contentSize = content.size(); + int size = -1; + do { + char c = content.at(index); + if (c < '0' || c > '9') { + if (size == -1) + return false; + if (c != ':') { + errString = QString("Unexpected character at pos %1: %2") + .arg(index).arg(c); + return false; + } + ++index; + break; + } + if (size == -1) + size = 0; + size *= 10; + size += c - '0'; + } while (++index < contentSize); + + if (byteString) + *byteString = content.mid(index, size); + index += size; + return true; +} + +bool BencodeParser::getInteger(qint64 *integer) +{ + const int contentSize = content.size(); + if (content.at(index) != 'i') + return false; + + ++index; + qint64 num = -1; + bool negative = false; + + do { + char c = content.at(index); + if (c < '0' || c > '9') { + if (num == -1) { + if (c != '-' || negative) + return false; + negative = true; + continue; + } else { + if (c != 'e') { + errString = QString("Unexpected character at pos %1: %2") + .arg(index).arg(c); + return false; + } + ++index; + break; + } + } + if (num == -1) + num = 0; + num *= 10; + num += c - '0'; + } while (++index < contentSize); + + if (integer) + *integer = negative ? -num : num; + return true; +} + +bool BencodeParser::getList(QList<QVariant> *list) +{ + const int contentSize = content.size(); + if (content.at(index) != 'l') + return false; + + QList<QVariant> tmp; + ++index; + + do { + if (content.at(index) == 'e') { + ++index; + break; + } + + qint64 number; + QByteArray byteString; + QList<QVariant> tmpList; + QMap<QByteArray, QVariant> dictionary; + + if (getInteger(&number)) + tmp << number; + else if (getByteString(&byteString)) + tmp << byteString; + else if (getList(&tmpList)) + tmp << tmpList; + else if (getDictionary(&dictionary)) + tmp << qVariantFromValue<QMap<QByteArray, QVariant> >(dictionary); + else { + errString = QString("error at index %1").arg(index); + return false; + } + } while (index < contentSize); + + if (list) + *list = tmp; + return true; +} + +bool BencodeParser::getDictionary(QMap<QByteArray, QVariant> *dictionary) +{ + const int contentSize = content.size(); + if (content.at(index) != 'd') + return false; + + QMap<QByteArray, QVariant> tmp; + ++index; + + do { + if (content.at(index) == 'e') { + ++index; + break; + } + + QByteArray key; + if (!getByteString(&key)) + break; + + if (key == "info") + infoStart = index; + + qint64 number; + QByteArray byteString; + QList<QVariant> tmpList; + QMap<QByteArray, QVariant> dictionary; + + if (getInteger(&number)) + tmp.insert(key, number); + else if (getByteString(&byteString)) + tmp.insert(key, byteString); + else if (getList(&tmpList)) + tmp.insert(key, tmpList); + else if (getDictionary(&dictionary)) + tmp.insert(key, qVariantFromValue<QMap<QByteArray, QVariant> >(dictionary)); + else { + errString = QString("error at index %1").arg(index); + return false; + } + + if (key == "info") + infoLength = index - infoStart; + + } while (index < contentSize); + + if (dictionary) + *dictionary = tmp; + return true; +} diff --git a/examples/network/torrent/bencodeparser.h b/examples/network/torrent/bencodeparser.h new file mode 100644 index 0000000..b4fc74b --- /dev/null +++ b/examples/network/torrent/bencodeparser.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BENCODEPARSER_H +#define BENCODEPARSER_H + +#include <QByteArray> +#include <QMap> +#include <QString> +#include <QVariant> +#include <QList> + +typedef QMap<QByteArray,QVariant> Dictionary; +Q_DECLARE_METATYPE(Dictionary) + +class BencodeParser +{ +public: + BencodeParser(); + + bool parse(const QByteArray &content); + QString errorString() const; + + QMap<QByteArray, QVariant> dictionary() const; + QByteArray infoSection() const; + +private: + bool getByteString(QByteArray *byteString); + bool getInteger(qint64 *integer); + bool getList(QList<QVariant> *list); + bool getDictionary(QMap<QByteArray, QVariant> *dictionary); + + QMap<QByteArray, QVariant> dictionaryValue; + + QString errString; + QByteArray content; + int index; + + int infoStart; + int infoLength; +}; + +#endif diff --git a/examples/network/torrent/connectionmanager.cpp b/examples/network/torrent/connectionmanager.cpp new file mode 100644 index 0000000..3b78537 --- /dev/null +++ b/examples/network/torrent/connectionmanager.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "connectionmanager.h" + +#include <QByteArray> +#include <QDateTime> + +static const int MaxConnections = 250; + +Q_GLOBAL_STATIC(ConnectionManager, connectionManager) + +ConnectionManager *ConnectionManager::instance() +{ + return connectionManager(); +} + +bool ConnectionManager::canAddConnection() const +{ + return (connections.size() < MaxConnections); +} + +void ConnectionManager::addConnection(PeerWireClient *client) +{ + connections << client; +} + +void ConnectionManager::removeConnection(PeerWireClient *client) +{ + connections.remove(client); +} + +int ConnectionManager::maxConnections() const +{ + return MaxConnections; +} + +QByteArray ConnectionManager::clientId() const +{ + if (id.isEmpty()) { + // Generate peer id + int startupTime = int(QDateTime::currentDateTime().toTime_t()); + + QString s; + s.sprintf("-QT%04x-", (QT_VERSION % 0xffff00) >> 8); + id += s.toLatin1(); + id += QByteArray::number(startupTime, 10); + id += QByteArray(20 - id.size(), '-'); + } + return id; +} diff --git a/examples/network/torrent/connectionmanager.h b/examples/network/torrent/connectionmanager.h new file mode 100644 index 0000000..3b791e3 --- /dev/null +++ b/examples/network/torrent/connectionmanager.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +class PeerWireClient; + +#include <QByteArray> +#include <QSet> + +class ConnectionManager +{ +public: + static ConnectionManager *instance(); + + bool canAddConnection() const; + void addConnection(PeerWireClient *connection); + void removeConnection(PeerWireClient *connection); + int maxConnections() const; + QByteArray clientId() const; + + private: + QSet<PeerWireClient *> connections; + mutable QByteArray id; +}; + +#endif diff --git a/examples/network/torrent/filemanager.cpp b/examples/network/torrent/filemanager.cpp new file mode 100644 index 0000000..07d5cca --- /dev/null +++ b/examples/network/torrent/filemanager.cpp @@ -0,0 +1,447 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filemanager.h" +#include "metainfo.h" + +#include <QByteArray> +#include <QDir> +#include <QFile> +#include <QTimer> +#include <QTimerEvent> +#include <QCryptographicHash> + +FileManager::FileManager(QObject *parent) + : QThread(parent) +{ + quit = false; + totalLength = 0; + readId = 0; + startVerification = false; + wokeUp = false; + newFile = false; + numPieces = 0; + verifiedPieces.fill(false); +} + +FileManager::~FileManager() +{ + quit = true; + cond.wakeOne(); + wait(); + + foreach (QFile *file, files) { + file->close(); + delete file; + } +} + +int FileManager::read(int pieceIndex, int offset, int length) +{ + ReadRequest request; + request.pieceIndex = pieceIndex; + request.offset = offset; + request.length = length; + + QMutexLocker locker(&mutex); + request.id = readId++; + readRequests << request; + + if (!wokeUp) { + wokeUp = true; + QMetaObject::invokeMethod(this, "wakeUp", Qt::QueuedConnection); + } + + return request.id; +} + +void FileManager::write(int pieceIndex, int offset, const QByteArray &data) +{ + WriteRequest request; + request.pieceIndex = pieceIndex; + request.offset = offset; + request.data = data; + + QMutexLocker locker(&mutex); + writeRequests << request; + + if (!wokeUp) { + wokeUp = true; + QMetaObject::invokeMethod(this, "wakeUp", Qt::QueuedConnection); + } +} + +void FileManager::verifyPiece(int pieceIndex) +{ + QMutexLocker locker(&mutex); + pendingVerificationRequests << pieceIndex; + startVerification = true; + + if (!wokeUp) { + wokeUp = true; + QMetaObject::invokeMethod(this, "wakeUp", Qt::QueuedConnection); + } +} + +int FileManager::pieceLengthAt(int pieceIndex) const +{ + QMutexLocker locker(&mutex); + return (sha1s.size() == pieceIndex + 1) + ? (totalLength % pieceLength) : pieceLength; +} + +QBitArray FileManager::completedPieces() const +{ + QMutexLocker locker(&mutex); + return verifiedPieces; +} + +void FileManager::setCompletedPieces(const QBitArray &pieces) +{ + QMutexLocker locker(&mutex); + verifiedPieces = pieces; +} + +QString FileManager::errorString() const +{ + return errString; +} + +void FileManager::run() +{ + if (!generateFiles()) + return; + + do { + { + // Go to sleep if there's nothing to do. + QMutexLocker locker(&mutex); + if (!quit && readRequests.isEmpty() && writeRequests.isEmpty() && !startVerification) + cond.wait(&mutex); + } + + // Read pending read requests + mutex.lock(); + QList<ReadRequest> newReadRequests = readRequests; + readRequests.clear(); + mutex.unlock(); + while (!newReadRequests.isEmpty()) { + ReadRequest request = newReadRequests.takeFirst(); + QByteArray block = readBlock(request.pieceIndex, request.offset, request.length); + emit dataRead(request.id, request.pieceIndex, request.offset, block); + } + + // Write pending write requests + mutex.lock(); + QList<WriteRequest> newWriteRequests = writeRequests; + writeRequests.clear(); + while (!quit && !newWriteRequests.isEmpty()) { + WriteRequest request = newWriteRequests.takeFirst(); + writeBlock(request.pieceIndex, request.offset, request.data); + } + + // Process pending verification requests + if (startVerification) { + newPendingVerificationRequests = pendingVerificationRequests; + pendingVerificationRequests.clear(); + verifyFileContents(); + startVerification = false; + } + mutex.unlock(); + newPendingVerificationRequests.clear(); + + } while (!quit); + + // Write pending write requests + mutex.lock(); + QList<WriteRequest> newWriteRequests = writeRequests; + writeRequests.clear(); + mutex.unlock(); + while (!newWriteRequests.isEmpty()) { + WriteRequest request = newWriteRequests.takeFirst(); + writeBlock(request.pieceIndex, request.offset, request.data); + } +} + +void FileManager::startDataVerification() +{ + QMutexLocker locker(&mutex); + startVerification = true; + cond.wakeOne(); +} + +bool FileManager::generateFiles() +{ + numPieces = -1; + + // Set up the thread local data + if (metaInfo.fileForm() == MetaInfo::SingleFileForm) { + QMutexLocker locker(&mutex); + MetaInfoSingleFile singleFile = metaInfo.singleFile(); + + QString prefix; + if (!destinationPath.isEmpty()) { + prefix = destinationPath; + if (!prefix.endsWith("/")) + prefix += "/"; + QDir dir; + if (!dir.mkpath(prefix)) { + errString = tr("Failed to create directory %1").arg(prefix); + emit error(); + return false; + } + } + QFile *file = new QFile(prefix + singleFile.name); + if (!file->open(QFile::ReadWrite)) { + errString = tr("Failed to open/create file %1: %2") + .arg(file->fileName()).arg(file->errorString()); + emit error(); + return false; + } + + if (file->size() != singleFile.length) { + newFile = true; + if (!file->resize(singleFile.length)) { + errString = tr("Failed to resize file %1: %2") + .arg(file->fileName()).arg(file->errorString()); + emit error(); + return false; + } + } + fileSizes << file->size(); + files << file; + file->close(); + + pieceLength = singleFile.pieceLength; + totalLength = singleFile.length; + sha1s = singleFile.sha1Sums; + } else { + QMutexLocker locker(&mutex); + QDir dir; + QString prefix; + + if (!destinationPath.isEmpty()) { + prefix = destinationPath; + if (!prefix.endsWith("/")) + prefix += "/"; + } + if (!metaInfo.name().isEmpty()) { + prefix += metaInfo.name(); + if (!prefix.endsWith("/")) + prefix += "/"; + } + if (!dir.mkpath(prefix)) { + errString = tr("Failed to create directory %1").arg(prefix); + emit error(); + return false; + } + + foreach (const MetaInfoMultiFile &entry, metaInfo.multiFiles()) { + QString filePath = QFileInfo(prefix + entry.path).path(); + if (!QFile::exists(filePath)) { + if (!dir.mkpath(filePath)) { + errString = tr("Failed to create directory %1").arg(filePath); + emit error(); + return false; + } + } + + QFile *file = new QFile(prefix + entry.path); + if (!file->open(QFile::ReadWrite)) { + errString = tr("Failed to open/create file %1: %2") + .arg(file->fileName()).arg(file->errorString()); + emit error(); + return false; + } + + if (file->size() != entry.length) { + newFile = true; + if (!file->resize(entry.length)) { + errString = tr("Failed to resize file %1: %2") + .arg(file->fileName()).arg(file->errorString()); + emit error(); + return false; + } + } + fileSizes << file->size(); + files << file; + file->close(); + + totalLength += entry.length; + } + + sha1s = metaInfo.sha1Sums(); + pieceLength = metaInfo.pieceLength(); + } + numPieces = sha1s.size(); + return true; +} + +QByteArray FileManager::readBlock(int pieceIndex, int offset, int length) +{ + QByteArray block; + qint64 startReadIndex = (quint64(pieceIndex) * pieceLength) + offset; + qint64 currentIndex = 0; + + for (int i = 0; !quit && i < files.size() && length > 0; ++i) { + QFile *file = files[i]; + qint64 currentFileSize = fileSizes.at(i); + if ((currentIndex + currentFileSize) > startReadIndex) { + if (!file->isOpen()) { + if (!file->open(QFile::ReadWrite)) { + errString = tr("Failed to read from file %1: %2") + .arg(file->fileName()).arg(file->errorString()); + emit error(); + break; + } + } + + file->seek(startReadIndex - currentIndex); + QByteArray chunk = file->read(qMin<qint64>(length, currentFileSize - file->pos())); + file->close(); + + block += chunk; + length -= chunk.size(); + startReadIndex += chunk.size(); + if (length < 0) { + errString = tr("Failed to read from file %1 (read %3 bytes): %2") + .arg(file->fileName()).arg(file->errorString()).arg(length); + emit error(); + break; + } + } + currentIndex += currentFileSize; + } + return block; +} + +bool FileManager::writeBlock(int pieceIndex, int offset, const QByteArray &data) +{ + qint64 startWriteIndex = (qint64(pieceIndex) * pieceLength) + offset; + qint64 currentIndex = 0; + int bytesToWrite = data.size(); + int written = 0; + + for (int i = 0; !quit && i < files.size(); ++i) { + QFile *file = files[i]; + qint64 currentFileSize = fileSizes.at(i); + + if ((currentIndex + currentFileSize) > startWriteIndex) { + if (!file->isOpen()) { + if (!file->open(QFile::ReadWrite)) { + errString = tr("Failed to write to file %1: %2") + .arg(file->fileName()).arg(file->errorString()); + emit error(); + break; + } + } + + file->seek(startWriteIndex - currentIndex); + qint64 bytesWritten = file->write(data.constData() + written, + qMin<qint64>(bytesToWrite, currentFileSize - file->pos())); + file->close(); + + if (bytesWritten <= 0) { + errString = tr("Failed to write to file %1: %2") + .arg(file->fileName()).arg(file->errorString()); + emit error(); + return false; + } + + written += bytesWritten; + startWriteIndex += bytesWritten; + bytesToWrite -= bytesWritten; + if (bytesToWrite == 0) + break; + } + currentIndex += currentFileSize; + } + return true; +} + +void FileManager::verifyFileContents() +{ + // Verify all pieces the first time + if (newPendingVerificationRequests.isEmpty()) { + if (verifiedPieces.count(true) == 0) { + verifiedPieces.resize(sha1s.size()); + + int oldPercent = 0; + if (!newFile) { + int numPieces = sha1s.size(); + + for (int index = 0; index < numPieces; ++index) { + verifySinglePiece(index); + + int percent = ((index + 1) * 100) / numPieces; + if (oldPercent != percent) { + emit verificationProgress(percent); + oldPercent = percent; + } + } + } + } + emit verificationDone(); + return; + } + + // Verify all pending pieces + foreach (int index, newPendingVerificationRequests) + emit pieceVerified(index, verifySinglePiece(index)); +} + +bool FileManager::verifySinglePiece(int pieceIndex) +{ + QByteArray block = readBlock(pieceIndex, 0, pieceLength); + QByteArray sha1Sum = QCryptographicHash::hash(block, QCryptographicHash::Sha1); + + if (sha1Sum != sha1s.at(pieceIndex)) + return false; + verifiedPieces.setBit(pieceIndex); + return true; +} + +void FileManager::wakeUp() +{ + QMutexLocker locker(&mutex); + wokeUp = false; + cond.wakeOne(); +} diff --git a/examples/network/torrent/filemanager.h b/examples/network/torrent/filemanager.h new file mode 100644 index 0000000..05d1f49 --- /dev/null +++ b/examples/network/torrent/filemanager.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FILEMANAGER_H +#define FILEMANAGER_H + +#include <QBitArray> +#include <QList> +#include <QMutex> +#include <QThread> +#include <QWaitCondition> + +#include "metainfo.h" + +QT_BEGIN_NAMESPACE +class QByteArray; +class QFile; +class QTimerEvent; +QT_END_NAMESPACE + +class FileManager : public QThread +{ + Q_OBJECT + +public: + FileManager(QObject *parent = 0); + virtual ~FileManager(); + + inline void setMetaInfo(const MetaInfo &info) { metaInfo = info; } + inline void setDestinationFolder(const QString &directory) { destinationPath = directory; } + + int read(int pieceIndex, int offset, int length); + void write(int pieceIndex, int offset, const QByteArray &data); + void verifyPiece(int pieceIndex); + inline qint64 totalSize() const { return totalLength; } + + inline int pieceCount() const { return numPieces; } + int pieceLengthAt(int pieceIndex) const; + + QBitArray completedPieces() const; + void setCompletedPieces(const QBitArray &pieces); + + QString errorString() const; + +public slots: + void startDataVerification(); + +signals: + void dataRead(int id, int pieceIndex, int offset, const QByteArray &data); + void error(); + void verificationProgress(int percent); + void verificationDone(); + void pieceVerified(int pieceIndex, bool verified); + +protected: + void run(); + +private slots: + bool verifySinglePiece(int pieceIndex); + void wakeUp(); + +private: + bool generateFiles(); + QByteArray readBlock(int pieceIndex, int offset, int length); + bool writeBlock(int pieceIndex, int offset, const QByteArray &data); + void verifyFileContents(); + + struct WriteRequest { + int pieceIndex; + int offset; + QByteArray data; + }; + struct ReadRequest { + int pieceIndex; + int offset; + int length; + int id; + }; + + QString errString; + QString destinationPath; + MetaInfo metaInfo; + QList<QFile *> files; + QList<QByteArray> sha1s; + QBitArray verifiedPieces; + + bool newFile; + int pieceLength; + qint64 totalLength; + int numPieces; + int readId; + bool startVerification; + bool quit; + bool wokeUp; + + QList<WriteRequest> writeRequests; + QList<ReadRequest> readRequests; + QList<int> pendingVerificationRequests; + QList<int> newPendingVerificationRequests; + QList<qint64> fileSizes; + + mutable QMutex mutex; + mutable QWaitCondition cond; +}; + +#endif diff --git a/examples/network/torrent/forms/addtorrentform.ui b/examples/network/torrent/forms/addtorrentform.ui new file mode 100644 index 0000000..950bb67 --- /dev/null +++ b/examples/network/torrent/forms/addtorrentform.ui @@ -0,0 +1,266 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>AddTorrentFile</class> + <widget class="QDialog" name="AddTorrentFile" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>464</width> + <height>385</height> + </rect> + </property> + <property name="windowTitle" > + <string>Add a torrent</string> + </property> + <property name="sizeGripEnabled" > + <bool>false</bool> + </property> + <property name="modal" > + <bool>true</bool> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Select a torrent source</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="6" column="0" > + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>Destination:</string> + </property> + </widget> + </item> + <item row="0" column="1" colspan="2" > + <widget class="QLineEdit" name="torrentFile" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Tracker URL:</string> + </property> + </widget> + </item> + <item row="0" column="3" > + <widget class="QPushButton" name="browseTorrents" > + <property name="text" > + <string>Browse</string> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>File(s):</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Size:</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_6" > + <property name="text" > + <string>Creator:</string> + </property> + </widget> + </item> + <item row="5" column="1" colspan="3" > + <widget class="QTextEdit" name="torrentContents" > + <property name="focusPolicy" > + <enum>Qt::NoFocus</enum> + </property> + <property name="tabChangesFocus" > + <bool>true</bool> + </property> + <property name="lineWrapMode" > + <enum>QTextEdit::NoWrap</enum> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="6" column="1" colspan="2" > + <widget class="QLineEdit" name="destinationFolder" > + <property name="focusPolicy" > + <enum>Qt::StrongFocus</enum> + </property> + </widget> + </item> + <item row="1" column="1" colspan="3" > + <widget class="QLabel" name="announceUrl" > + <property name="text" > + <string><none></string> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Torrent file:</string> + </property> + </widget> + </item> + <item row="6" column="3" > + <widget class="QPushButton" name="browseDestination" > + <property name="text" > + <string>Browse</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_7" > + <property name="text" > + <string>Comment:</string> + </property> + </widget> + </item> + <item row="3" column="1" colspan="3" > + <widget class="QLabel" name="commentLabel" > + <property name="text" > + <string><none></string> + </property> + </widget> + </item> + <item row="2" column="1" colspan="3" > + <widget class="QLabel" name="creatorLabel" > + <property name="text" > + <string><none></string> + </property> + </widget> + </item> + <item row="4" column="1" colspan="3" > + <widget class="QLabel" name="sizeLabel" > + <property name="text" > + <string>0</string> + </property> + </widget> + </item> + </layout> + <widget class="QWidget" name="widget" > + <property name="geometry" > + <rect> + <x>10</x> + <y>40</y> + <width>364</width> + <height>33</height> + </rect> + </property> + </widget> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>131</width> + <height>31</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>&OK</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>&Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <pixmapfunction></pixmapfunction> + <tabstops> + <tabstop>torrentFile</tabstop> + <tabstop>browseTorrents</tabstop> + <tabstop>torrentContents</tabstop> + <tabstop>destinationFolder</tabstop> + <tabstop>browseDestination</tabstop> + <tabstop>okButton</tabstop> + <tabstop>cancelButton</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>okButton</sender> + <signal>clicked()</signal> + <receiver>AddTorrentFile</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>278</x> + <y>253</y> + </hint> + <hint type="destinationlabel" > + <x>96</x> + <y>254</y> + </hint> + </hints> + </connection> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>AddTorrentFile</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>369</x> + <y>253</y> + </hint> + <hint type="destinationlabel" > + <x>179</x> + <y>282</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/examples/network/torrent/icons.qrc b/examples/network/torrent/icons.qrc new file mode 100644 index 0000000..9541ef7 --- /dev/null +++ b/examples/network/torrent/icons.qrc @@ -0,0 +1,12 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/"> + <file>icons/peertopeer.png</file> + <file>icons/1uparrow.png</file> + <file>icons/1downarrow.png</file> + <file>icons/bottom.png</file> + <file>icons/player_pause.png</file> + <file>icons/player_play.png</file> + <file>icons/player_stop.png</file> + <file>icons/exit.png</file> +</qresource> +</RCC> diff --git a/examples/network/torrent/icons/1downarrow.png b/examples/network/torrent/icons/1downarrow.png Binary files differnew file mode 100644 index 0000000..08403b8 --- /dev/null +++ b/examples/network/torrent/icons/1downarrow.png diff --git a/examples/network/torrent/icons/1uparrow.png b/examples/network/torrent/icons/1uparrow.png Binary files differnew file mode 100644 index 0000000..f044811 --- /dev/null +++ b/examples/network/torrent/icons/1uparrow.png diff --git a/examples/network/torrent/icons/bottom.png b/examples/network/torrent/icons/bottom.png Binary files differnew file mode 100644 index 0000000..fe66b5d --- /dev/null +++ b/examples/network/torrent/icons/bottom.png diff --git a/examples/network/torrent/icons/edit_add.png b/examples/network/torrent/icons/edit_add.png Binary files differnew file mode 100644 index 0000000..85b022e --- /dev/null +++ b/examples/network/torrent/icons/edit_add.png diff --git a/examples/network/torrent/icons/edit_remove.png b/examples/network/torrent/icons/edit_remove.png Binary files differnew file mode 100644 index 0000000..93361f5 --- /dev/null +++ b/examples/network/torrent/icons/edit_remove.png diff --git a/examples/network/torrent/icons/exit.png b/examples/network/torrent/icons/exit.png Binary files differnew file mode 100644 index 0000000..2f7ff43 --- /dev/null +++ b/examples/network/torrent/icons/exit.png diff --git a/examples/network/torrent/icons/peertopeer.png b/examples/network/torrent/icons/peertopeer.png Binary files differnew file mode 100644 index 0000000..f4856dc --- /dev/null +++ b/examples/network/torrent/icons/peertopeer.png diff --git a/examples/network/torrent/icons/player_pause.png b/examples/network/torrent/icons/player_pause.png Binary files differnew file mode 100644 index 0000000..8c9bcc4 --- /dev/null +++ b/examples/network/torrent/icons/player_pause.png diff --git a/examples/network/torrent/icons/player_play.png b/examples/network/torrent/icons/player_play.png Binary files differnew file mode 100644 index 0000000..70daa33 --- /dev/null +++ b/examples/network/torrent/icons/player_play.png diff --git a/examples/network/torrent/icons/player_stop.png b/examples/network/torrent/icons/player_stop.png Binary files differnew file mode 100644 index 0000000..ce6585a --- /dev/null +++ b/examples/network/torrent/icons/player_stop.png diff --git a/examples/network/torrent/icons/stop.png b/examples/network/torrent/icons/stop.png Binary files differnew file mode 100644 index 0000000..52e593a --- /dev/null +++ b/examples/network/torrent/icons/stop.png diff --git a/examples/network/torrent/main.cpp b/examples/network/torrent/main.cpp new file mode 100644 index 0000000..3821254 --- /dev/null +++ b/examples/network/torrent/main.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QApplication> +#include <QtCore> + +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); + + Q_INIT_RESOURCE(icons); + + MainWindow window; + window.show(); + + return app.exec(); +} diff --git a/examples/network/torrent/mainwindow.cpp b/examples/network/torrent/mainwindow.cpp new file mode 100644 index 0000000..6ca3247 --- /dev/null +++ b/examples/network/torrent/mainwindow.cpp @@ -0,0 +1,713 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +#include "addtorrentdialog.h" +#include "mainwindow.h" +#include "ratecontroller.h" +#include "torrentclient.h" + +// TorrentView extends QTreeWidget to allow drag and drop. +class TorrentView : public QTreeWidget +{ + Q_OBJECT +public: + TorrentView(QWidget *parent = 0); + +signals: + void fileDropped(const QString &fileName); + +protected: + void dragMoveEvent(QDragMoveEvent *event); + void dropEvent(QDropEvent *event); +}; + +// TorrentViewDelegate is used to draw the progress bars. +class TorrentViewDelegate : public QItemDelegate +{ + Q_OBJECT +public: + inline TorrentViewDelegate(MainWindow *mainWindow) : QItemDelegate(mainWindow) {} + + inline void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index ) const + { + if (index.column() != 2) { + QItemDelegate::paint(painter, option, index); + return; + } + + // Set up a QStyleOptionProgressBar to precisely mimic the + // environment of a progress bar. + QStyleOptionProgressBar progressBarOption; + progressBarOption.state = QStyle::State_Enabled; + progressBarOption.direction = QApplication::layoutDirection(); + progressBarOption.rect = option.rect; + progressBarOption.fontMetrics = QApplication::fontMetrics(); + progressBarOption.minimum = 0; + progressBarOption.maximum = 100; + progressBarOption.textAlignment = Qt::AlignCenter; + progressBarOption.textVisible = true; + + // Set the progress and text values of the style option. + int progress = qobject_cast<MainWindow *>(parent())->clientForRow(index.row())->progress(); + progressBarOption.progress = progress < 0 ? 0 : progress; + progressBarOption.text = QString().sprintf("%d%%", progressBarOption.progress); + + // Draw the progress bar onto the view. + QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); + } +}; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), quitDialog(0), saveChanges(false) +{ + // Initialize some static strings + QStringList headers; + headers << tr("Torrent") << tr("Peers/Seeds") << tr("Progress") + << tr("Down rate") << tr("Up rate") << tr("Status"); + + // Main torrent list + torrentView = new TorrentView(this); + torrentView->setItemDelegate(new TorrentViewDelegate(this)); + torrentView->setHeaderLabels(headers); + torrentView->setSelectionBehavior(QAbstractItemView::SelectRows); + torrentView->setAlternatingRowColors(true); + torrentView->setRootIsDecorated(false); + setCentralWidget(torrentView); + + // Set header resize modes and initial section sizes + QFontMetrics fm = fontMetrics(); + QHeaderView *header = torrentView->header(); + header->resizeSection(0, fm.width("typical-name-for-a-torrent.torrent")); + header->resizeSection(1, fm.width(headers.at(1) + " ")); + header->resizeSection(2, fm.width(headers.at(2) + " ")); + header->resizeSection(3, qMax(fm.width(headers.at(3) + " "), fm.width(" 1234.0 KB/s "))); + header->resizeSection(4, qMax(fm.width(headers.at(4) + " "), fm.width(" 1234.0 KB/s "))); + header->resizeSection(5, qMax(fm.width(headers.at(5) + " "), fm.width(tr("Downloading") + " "))); + + // Create common actions + QAction *newTorrentAction = new QAction(QIcon(":/icons/bottom.png"), tr("Add &new torrent"), this); + pauseTorrentAction = new QAction(QIcon(":/icons/player_pause.png"), tr("&Pause torrent"), this); + removeTorrentAction = new QAction(QIcon(":/icons/player_stop.png"), tr("&Remove torrent"), this); + + // File menu + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + fileMenu->addAction(newTorrentAction); + fileMenu->addAction(pauseTorrentAction); + fileMenu->addAction(removeTorrentAction); + fileMenu->addSeparator(); + fileMenu->addAction(QIcon(":/icons/exit.png"), tr("E&xit"), this, SLOT(close())); + + // Help menu + QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); + helpMenu->addAction(tr("&About"), this, SLOT(about())); + helpMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt())); + + // Top toolbar + QToolBar *topBar = new QToolBar(tr("Tools")); + addToolBar(Qt::TopToolBarArea, topBar); + topBar->setMovable(false); + topBar->addAction(newTorrentAction); + topBar->addAction(removeTorrentAction); + topBar->addAction(pauseTorrentAction); + topBar->addSeparator(); + downActionTool = topBar->addAction(QIcon(tr(":/icons/1downarrow.png")), tr("Move down")); + upActionTool = topBar->addAction(QIcon(tr(":/icons/1uparrow.png")), tr("Move up")); + + // Bottom toolbar + QToolBar *bottomBar = new QToolBar(tr("Rate control")); + addToolBar(Qt::BottomToolBarArea, bottomBar); + bottomBar->setMovable(false); + downloadLimitSlider = new QSlider(Qt::Horizontal); + downloadLimitSlider->setRange(0, 1000); + bottomBar->addWidget(new QLabel(tr("Max download:"))); + bottomBar->addWidget(downloadLimitSlider); + bottomBar->addWidget((downloadLimitLabel = new QLabel(tr("0 KB/s")))); + downloadLimitLabel->setFixedSize(QSize(fm.width(tr("99999 KB/s")), fm.lineSpacing())); + bottomBar->addSeparator(); + uploadLimitSlider = new QSlider(Qt::Horizontal); + uploadLimitSlider->setRange(0, 1000); + bottomBar->addWidget(new QLabel(tr("Max upload:"))); + bottomBar->addWidget(uploadLimitSlider); + bottomBar->addWidget((uploadLimitLabel = new QLabel(tr("0 KB/s")))); + uploadLimitLabel->setFixedSize(QSize(fm.width(tr("99999 KB/s")), fm.lineSpacing())); + + // Set up connections + connect(torrentView, SIGNAL(itemSelectionChanged()), + this, SLOT(setActionsEnabled())); + connect(torrentView, SIGNAL(fileDropped(const QString &)), + this, SLOT(acceptFileDrop(const QString &))); + connect(uploadLimitSlider, SIGNAL(valueChanged(int)), + this, SLOT(setUploadLimit(int))); + connect(downloadLimitSlider, SIGNAL(valueChanged(int)), + this, SLOT(setDownloadLimit(int))); + connect(newTorrentAction, SIGNAL(triggered()), + this, SLOT(addTorrent())); + connect(pauseTorrentAction, SIGNAL(triggered()), + this, SLOT(pauseTorrent())); + connect(removeTorrentAction, SIGNAL(triggered()), + this, SLOT(removeTorrent())); + connect(upActionTool, SIGNAL(triggered(bool)), + this, SLOT(moveTorrentUp())); + connect(downActionTool, SIGNAL(triggered(bool)), + this, SLOT(moveTorrentDown())); + + // Load settings and start + setWindowTitle(tr("Torrent Client")); + setActionsEnabled(); + QMetaObject::invokeMethod(this, "loadSettings", Qt::QueuedConnection); +} + +QSize MainWindow::sizeHint() const +{ + const QHeaderView *header = torrentView->header(); + + // Add up the sizes of all header sections. The last section is + // stretched, so its size is relative to the size of the width; + // instead of counting it, we count the size of its largest value. + int width = fontMetrics().width(tr("Downloading") + " "); + for (int i = 0; i < header->count() - 1; ++i) + width += header->sectionSize(i); + + return QSize(width, QMainWindow::sizeHint().height()) + .expandedTo(QApplication::globalStrut()); +} + +const TorrentClient *MainWindow::clientForRow(int row) const +{ + // Return the client at the given row. + return jobs.at(row).client; +} + +int MainWindow::rowOfClient(TorrentClient *client) const +{ + // Return the row that displays this client's status, or -1 if the + // client is not known. + int row = 0; + foreach (Job job, jobs) { + if (job.client == client) + return row; + ++row; + } + return -1; +} + +void MainWindow::loadSettings() +{ + // Load base settings (last working directory, upload/download limits). + QSettings settings("Trolltech", "Torrent"); + lastDirectory = settings.value("LastDirectory").toString(); + if (lastDirectory.isEmpty()) + lastDirectory = QDir::currentPath(); + int up = settings.value("UploadLimit").toInt(); + int down = settings.value("DownloadLimit").toInt(); + uploadLimitSlider->setValue(up ? up : 170); + downloadLimitSlider->setValue(down ? down : 550); + + // Resume all previous downloads. + int size = settings.beginReadArray("Torrents"); + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + QByteArray resumeState = settings.value("resumeState").toByteArray(); + QString fileName = settings.value("sourceFileName").toString(); + QString dest = settings.value("destinationFolder").toString(); + + if (addTorrent(fileName, dest, resumeState)) { + TorrentClient *client = jobs.last().client; + client->setDownloadedBytes(settings.value("downloadedBytes").toLongLong()); + client->setUploadedBytes(settings.value("uploadedBytes").toLongLong()); + } + } +} + +bool MainWindow::addTorrent() +{ + // Show the file dialog, let the user select what torrent to start downloading. + QString fileName = QFileDialog::getOpenFileName(this, tr("Choose a torrent file"), + lastDirectory, + tr("Torrents (*.torrent);;" + " All files (*.*)")); + if (fileName.isEmpty()) + return false; + lastDirectory = QFileInfo(fileName).absolutePath(); + + // Show the "Add Torrent" dialog. + AddTorrentDialog *addTorrentDialog = new AddTorrentDialog(this); + addTorrentDialog->setTorrent(fileName); + addTorrentDialog->deleteLater(); + if (!addTorrentDialog->exec()) + return false; + + // Add the torrent to our list of downloads + addTorrent(fileName, addTorrentDialog->destinationFolder()); + if (!saveChanges) { + saveChanges = true; + QTimer::singleShot(1000, this, SLOT(saveSettings())); + } + return true; +} + +void MainWindow::removeTorrent() +{ + // Find the row of the current item, and find the torrent client + // for that row. + int row = torrentView->indexOfTopLevelItem(torrentView->currentItem()); + TorrentClient *client = jobs.at(row).client; + + // Stop the client. + client->disconnect(); + connect(client, SIGNAL(stopped()), this, SLOT(torrentStopped())); + client->stop(); + + // Remove the row from the view. + delete torrentView->takeTopLevelItem(row); + jobs.removeAt(row); + setActionsEnabled(); + + saveChanges = true; + saveSettings(); +} + +void MainWindow::torrentStopped() +{ + // Schedule the client for deletion. + TorrentClient *client = qobject_cast<TorrentClient *>(sender()); + client->deleteLater(); + + // If the quit dialog is shown, update its progress. + if (quitDialog) { + if (++jobsStopped == jobsToStop) + quitDialog->close(); + } +} + +void MainWindow::torrentError(TorrentClient::Error) +{ + // Delete the client. + TorrentClient *client = qobject_cast<TorrentClient *>(sender()); + int row = rowOfClient(client); + QString fileName = jobs.at(row).torrentFileName; + jobs.removeAt(row); + + // Display the warning. + QMessageBox::warning(this, tr("Error"), + tr("An error occurred while downloading %0: %1") + .arg(fileName) + .arg(client->errorString())); + + delete torrentView->takeTopLevelItem(row); + client->deleteLater(); +} + +bool MainWindow::addTorrent(const QString &fileName, const QString &destinationFolder, + const QByteArray &resumeState) +{ + // Check if the torrent is already being downloaded. + foreach (Job job, jobs) { + if (job.torrentFileName == fileName && job.destinationDirectory == destinationFolder) { + QMessageBox::warning(this, tr("Already downloading"), + tr("The torrent file %1 is " + "already being downloaded.").arg(fileName)); + return false; + } + } + + // Create a new torrent client and attempt to parse the torrent data. + TorrentClient *client = new TorrentClient(this); + if (!client->setTorrent(fileName)) { + QMessageBox::warning(this, tr("Error"), + tr("The torrent file %1 cannot not be opened/resumed.").arg(fileName)); + delete client; + return false; + } + client->setDestinationFolder(destinationFolder); + client->setDumpedState(resumeState); + + // Setup the client connections. + connect(client, SIGNAL(stateChanged(TorrentClient::State)), this, SLOT(updateState(TorrentClient::State))); + connect(client, SIGNAL(peerInfoUpdated()), this, SLOT(updatePeerInfo())); + connect(client, SIGNAL(progressUpdated(int)), this, SLOT(updateProgress(int))); + connect(client, SIGNAL(downloadRateUpdated(int)), this, SLOT(updateDownloadRate(int))); + connect(client, SIGNAL(uploadRateUpdated(int)), this, SLOT(updateUploadRate(int))); + connect(client, SIGNAL(stopped()), this, SLOT(torrentStopped())); + connect(client, SIGNAL(error(TorrentClient::Error)), this, SLOT(torrentError(TorrentClient::Error))); + + // Add the client to the list of downloading jobs. + Job job; + job.client = client; + job.torrentFileName = fileName; + job.destinationDirectory = destinationFolder; + jobs << job; + + // Create and add a row in the torrent view for this download. + QTreeWidgetItem *item = new QTreeWidgetItem(torrentView); + + QString baseFileName = QFileInfo(fileName).fileName(); + if (baseFileName.toLower().endsWith(".torrent")) + baseFileName.remove(baseFileName.size() - 8); + + item->setText(0, baseFileName); + item->setToolTip(0, tr("Torrent: %1<br>Destination: %2") + .arg(baseFileName).arg(destinationFolder)); + item->setText(1, tr("0/0")); + item->setText(2, "0"); + item->setText(3, "0.0 KB/s"); + item->setText(4, "0.0 KB/s"); + item->setText(5, tr("Idle")); + item->setFlags(item->flags() & ~Qt::ItemIsEditable); + item->setTextAlignment(1, Qt::AlignHCenter); + + if (!saveChanges) { + saveChanges = true; + QTimer::singleShot(5000, this, SLOT(saveSettings())); + } + client->start(); + return true; +} + +void MainWindow::saveSettings() +{ + if (!saveChanges) + return; + saveChanges = false; + + // Prepare and reset the settings + QSettings settings("Trolltech", "Torrent"); + settings.clear(); + + settings.setValue("LastDirectory", lastDirectory); + settings.setValue("UploadLimit", uploadLimitSlider->value()); + settings.setValue("DownloadLimit", downloadLimitSlider->value()); + + // Store data on all known torrents + settings.beginWriteArray("Torrents"); + for (int i = 0; i < jobs.size(); ++i) { + settings.setArrayIndex(i); + settings.setValue("sourceFileName", jobs.at(i).torrentFileName); + settings.setValue("destinationFolder", jobs.at(i).destinationDirectory); + settings.setValue("uploadedBytes", jobs.at(i).client->uploadedBytes()); + settings.setValue("downloadedBytes", jobs.at(i).client->downloadedBytes()); + settings.setValue("resumeState", jobs.at(i).client->dumpedState()); + } + settings.endArray(); + settings.sync(); +} + +void MainWindow::updateState(TorrentClient::State) +{ + // Update the state string whenever the client's state changes. + TorrentClient *client = qobject_cast<TorrentClient *>(sender()); + int row = rowOfClient(client); + QTreeWidgetItem *item = torrentView->topLevelItem(row); + if (item) { + item->setToolTip(0, tr("Torrent: %1<br>Destination: %2<br>State: %3") + .arg(jobs.at(row).torrentFileName) + .arg(jobs.at(row).destinationDirectory) + .arg(client->stateString())); + + item->setText(5, client->stateString()); + } + setActionsEnabled(); +} + +void MainWindow::updatePeerInfo() +{ + // Update the number of connected, visited, seed and leecher peers. + TorrentClient *client = qobject_cast<TorrentClient *>(sender()); + int row = rowOfClient(client); + + QTreeWidgetItem *item = torrentView->topLevelItem(row); + item->setText(1, tr("%1/%2").arg(client->connectedPeerCount()) + .arg(client->seedCount())); +} + +void MainWindow::updateProgress(int percent) +{ + TorrentClient *client = qobject_cast<TorrentClient *>(sender()); + int row = rowOfClient(client); + + // Update the progressbar. + QTreeWidgetItem *item = torrentView->topLevelItem(row); + if (item) + item->setText(2, QString::number(percent)); +} + +void MainWindow::setActionsEnabled() +{ + // Find the view item and client for the current row, and update + // the states of the actions. + QTreeWidgetItem *item = 0; + if (!torrentView->selectedItems().isEmpty()) + item = torrentView->selectedItems().first(); + TorrentClient *client = item ? jobs.at(torrentView->indexOfTopLevelItem(item)).client : 0; + bool pauseEnabled = client && ((client->state() == TorrentClient::Paused) + || (client->state() > TorrentClient::Preparing)); + + removeTorrentAction->setEnabled(item != 0); + pauseTorrentAction->setEnabled(item != 0 && pauseEnabled); + + if (client && client->state() == TorrentClient::Paused) { + pauseTorrentAction->setIcon(QIcon(":/icons/player_play.png")); + pauseTorrentAction->setText(tr("Resume torrent")); + } else { + pauseTorrentAction->setIcon(QIcon(":/icons/player_pause.png")); + pauseTorrentAction->setText(tr("Pause torrent")); + } + + int row = torrentView->indexOfTopLevelItem(item); + upActionTool->setEnabled(item && row != 0); + downActionTool->setEnabled(item && row != jobs.size() - 1); +} + +void MainWindow::updateDownloadRate(int bytesPerSecond) +{ + // Update the download rate. + TorrentClient *client = qobject_cast<TorrentClient *>(sender()); + int row = rowOfClient(client); + QString num; + num.sprintf("%.1f KB/s", bytesPerSecond / 1024.0); + torrentView->topLevelItem(row)->setText(3, num); + + if (!saveChanges) { + saveChanges = true; + QTimer::singleShot(5000, this, SLOT(saveSettings())); + } +} + +void MainWindow::updateUploadRate(int bytesPerSecond) +{ + // Update the upload rate. + TorrentClient *client = qobject_cast<TorrentClient *>(sender()); + int row = rowOfClient(client); + QString num; + num.sprintf("%.1f KB/s", bytesPerSecond / 1024.0); + torrentView->topLevelItem(row)->setText(4, num); + + if (!saveChanges) { + saveChanges = true; + QTimer::singleShot(5000, this, SLOT(saveSettings())); + } +} + +void MainWindow::pauseTorrent() +{ + // Pause or unpause the current torrent. + int row = torrentView->indexOfTopLevelItem(torrentView->currentItem()); + TorrentClient *client = jobs.at(row).client; + client->setPaused(client->state() != TorrentClient::Paused); + setActionsEnabled(); +} + +void MainWindow::moveTorrentUp() +{ + QTreeWidgetItem *item = torrentView->currentItem(); + int row = torrentView->indexOfTopLevelItem(item); + if (row == 0) + return; + + Job tmp = jobs.at(row - 1); + jobs[row - 1] = jobs[row]; + jobs[row] = tmp; + + QTreeWidgetItem *itemAbove = torrentView->takeTopLevelItem(row - 1); + torrentView->insertTopLevelItem(row, itemAbove); + setActionsEnabled(); +} + +void MainWindow::moveTorrentDown() +{ + QTreeWidgetItem *item = torrentView->currentItem(); + int row = torrentView->indexOfTopLevelItem(item); + if (row == jobs.size() - 1) + return; + + Job tmp = jobs.at(row + 1); + jobs[row + 1] = jobs[row]; + jobs[row] = tmp; + + QTreeWidgetItem *itemAbove = torrentView->takeTopLevelItem(row + 1); + torrentView->insertTopLevelItem(row, itemAbove); + setActionsEnabled(); +} + +static int rateFromValue(int value) +{ + int rate = 0; + if (value >= 0 && value < 250) { + rate = 1 + int(value * 0.124); + } else if (value < 500) { + rate = 32 + int((value - 250) * 0.384); + } else if (value < 750) { + rate = 128 + int((value - 500) * 1.536); + } else { + rate = 512 + int((value - 750) * 6.1445); + } + return rate; +} + +void MainWindow::setUploadLimit(int value) +{ + int rate = rateFromValue(value); + uploadLimitLabel->setText(tr("%1 KB/s").arg(QString().sprintf("%4d", rate))); + RateController::instance()->setUploadLimit(rate * 1024); +} + +void MainWindow::setDownloadLimit(int value) +{ + int rate = rateFromValue(value); + downloadLimitLabel->setText(tr("%1 KB/s").arg(QString().sprintf("%4d", rate))); + RateController::instance()->setDownloadLimit(rate * 1024); +} + +void MainWindow::about() +{ + QLabel *icon = new QLabel; + icon->setPixmap(QPixmap(":/icons/peertopeer.png")); + + QLabel *text = new QLabel; + text->setWordWrap(true); + text->setText("<p>The <b>Torrent Client</b> example demonstrates how to" + " write a complete peer-to-peer file sharing" + " application using Qt's network and thread classes.</p>" + "<p>This feature complete client implementation of" + " the BitTorrent protocol can efficiently" + " maintain several hundred network connections" + " simultaneously.</p>"); + + QPushButton *quitButton = new QPushButton("OK"); + + QHBoxLayout *topLayout = new QHBoxLayout; + topLayout->setMargin(10); + topLayout->setSpacing(10); + topLayout->addWidget(icon); + topLayout->addWidget(text); + + QHBoxLayout *bottomLayout = new QHBoxLayout; + bottomLayout->addStretch(); + bottomLayout->addWidget(quitButton); + bottomLayout->addStretch(); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(topLayout); + mainLayout->addLayout(bottomLayout); + + QDialog about(this); + about.setModal(true); + about.setWindowTitle(tr("About Torrent Client")); + about.setLayout(mainLayout); + + connect(quitButton, SIGNAL(clicked()), &about, SLOT(close())); + + about.exec(); +} + +void MainWindow::acceptFileDrop(const QString &fileName) +{ + // Create and show the "Add Torrent" dialog. + AddTorrentDialog *addTorrentDialog = new AddTorrentDialog; + lastDirectory = QFileInfo(fileName).absolutePath(); + addTorrentDialog->setTorrent(fileName); + addTorrentDialog->deleteLater(); + if (!addTorrentDialog->exec()) + return; + + // Add the torrent to our list of downloads. + addTorrent(fileName, addTorrentDialog->destinationFolder()); + saveSettings(); +} + +void MainWindow::closeEvent(QCloseEvent *) +{ + if (jobs.isEmpty()) + return; + + // Save upload / download numbers. + saveSettings(); + saveChanges = false; + + quitDialog = new QProgressDialog(tr("Disconnecting from trackers"), tr("Abort"), 0, jobsToStop, this); + + // Stop all clients, remove the rows from the view and wait for + // them to signal that they have stopped. + jobsToStop = 0; + jobsStopped = 0; + foreach (Job job, jobs) { + ++jobsToStop; + TorrentClient *client = job.client; + client->disconnect(); + connect(client, SIGNAL(stopped()), this, SLOT(torrentStopped())); + client->stop(); + delete torrentView->takeTopLevelItem(0); + } + + if (jobsToStop > jobsStopped) + quitDialog->exec(); + quitDialog->deleteLater(); + quitDialog = 0; +} + +TorrentView::TorrentView(QWidget *parent) + : QTreeWidget(parent) +{ + setAcceptDrops(true); +} + +void TorrentView::dragMoveEvent(QDragMoveEvent *event) +{ + // Accept file actions with a '.torrent' extension. + QUrl url(event->mimeData()->text()); + if (url.isValid() && url.scheme().toLower() == "file" + && url.path().toLower().endsWith(".torrent")) + event->acceptProposedAction(); +} + +void TorrentView::dropEvent(QDropEvent *event) +{ + // Accept drops if the file has a '.torrent' extension and it + // exists. + QString fileName = QUrl(event->mimeData()->text()).path(); + if (QFile::exists(fileName) && fileName.toLower().endsWith(".torrent")) + emit fileDropped(fileName); +} + +#include "mainwindow.moc" diff --git a/examples/network/torrent/mainwindow.h b/examples/network/torrent/mainwindow.h new file mode 100644 index 0000000..3249781 --- /dev/null +++ b/examples/network/torrent/mainwindow.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QList> +#include <QStringList> +#include <QMainWindow> + +#include "torrentclient.h" + +QT_BEGIN_NAMESPACE +class QAction; +class QCloseEvent; +class QLabel; +class QProgressDialog; +class QSlider; +QT_END_NAMESPACE +class TorrentView; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = 0); + + QSize sizeHint() const; + const TorrentClient *clientForRow(int row) const; + +protected: + void closeEvent(QCloseEvent *event); + +private slots: + void loadSettings(); + void saveSettings(); + + bool addTorrent(); + void removeTorrent(); + void pauseTorrent(); + void moveTorrentUp(); + void moveTorrentDown(); + + void torrentStopped(); + void torrentError(TorrentClient::Error error); + + void updateState(TorrentClient::State state); + void updatePeerInfo(); + void updateProgress(int percent); + void updateDownloadRate(int bytesPerSecond); + void updateUploadRate(int bytesPerSecond); + + void setUploadLimit(int bytes); + void setDownloadLimit(int bytes); + + void about(); + void setActionsEnabled(); + void acceptFileDrop(const QString &fileName); + +private: + int rowOfClient(TorrentClient *client) const; + bool addTorrent(const QString &fileName, const QString &destinationFolder, + const QByteArray &resumeState = QByteArray()); + + TorrentView *torrentView; + QAction *pauseTorrentAction; + QAction *removeTorrentAction; + QAction *upActionTool; + QAction *downActionTool; + QSlider *uploadLimitSlider; + QSlider *downloadLimitSlider; + QLabel *uploadLimitLabel; + QLabel *downloadLimitLabel; + + int uploadLimit; + int downloadLimit; + + struct Job { + TorrentClient *client; + QString torrentFileName; + QString destinationDirectory; + }; + QList<Job> jobs; + int jobsStopped; + int jobsToStop; + + QString lastDirectory; + QProgressDialog *quitDialog; + + bool saveChanges; +}; + +#endif diff --git a/examples/network/torrent/metainfo.cpp b/examples/network/torrent/metainfo.cpp new file mode 100644 index 0000000..03d824c --- /dev/null +++ b/examples/network/torrent/metainfo.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bencodeparser.h" +#include "metainfo.h" + +#include <QDateTime> +#include <QMetaType> +#include <QString> + +MetaInfo::MetaInfo() +{ + clear(); +} + +void MetaInfo::clear() +{ + errString = "Unknown error"; + content.clear(); + infoData.clear(); + metaInfoMultiFiles.clear(); + metaInfoAnnounce.clear(); + metaInfoAnnounceList.clear(); + metaInfoCreationDate = QDateTime(); + metaInfoComment.clear(); + metaInfoCreatedBy.clear(); + metaInfoName.clear(); + metaInfoPieceLength = 0; + metaInfoSha1Sums.clear(); +} + +bool MetaInfo::parse(const QByteArray &data) +{ + clear(); + content = data; + + BencodeParser parser; + if (!parser.parse(content)) { + errString = parser.errorString(); + return false; + } + + infoData = parser.infoSection(); + + QMap<QByteArray, QVariant> dict = parser.dictionary(); + if (!dict.contains("info")) + return false; + + QMap<QByteArray, QVariant> info = qVariantValue<Dictionary>(dict.value("info")); + + if (info.contains("files")) { + metaInfoFileForm = MultiFileForm; + + QList<QVariant> files = info.value("files").toList(); + + for (int i = 0; i < files.size(); ++i) { + QMap<QByteArray, QVariant> file = qVariantValue<Dictionary>(files.at(i)); + QList<QVariant> pathElements = file.value("path").toList(); + QByteArray path; + foreach (QVariant p, pathElements) { + if (!path.isEmpty()) + path += "/"; + path += p.toByteArray(); + } + + MetaInfoMultiFile multiFile; + multiFile.length = file.value("length").toLongLong(); + multiFile.path = QString::fromUtf8(path); + multiFile.md5sum = file.value("md5sum").toByteArray(); + metaInfoMultiFiles << multiFile; + } + + metaInfoName = QString::fromUtf8(info.value("name").toByteArray()); + metaInfoPieceLength = info.value("piece length").toInt(); + QByteArray pieces = info.value("pieces").toByteArray(); + for (int i = 0; i < pieces.size(); i += 20) + metaInfoSha1Sums << pieces.mid(i, 20); + } else if (info.contains("length")) { + metaInfoFileForm = SingleFileForm; + metaInfoSingleFile.length = info.value("length").toLongLong(); + metaInfoSingleFile.md5sum = info.value("md5sum").toByteArray(); + metaInfoSingleFile.name = QString::fromUtf8(info.value("name").toByteArray()); + metaInfoSingleFile.pieceLength = info.value("piece length").toInt(); + + QByteArray pieces = info.value("pieces").toByteArray(); + for (int i = 0; i < pieces.size(); i += 20) + metaInfoSingleFile.sha1Sums << pieces.mid(i, 20); + } + + metaInfoAnnounce = QString::fromUtf8(dict.value("announce").toByteArray()); + + if (dict.contains("announce-list")) { + // ### unimplemented + } + + if (dict.contains("creation date")) + metaInfoCreationDate.setTime_t(dict.value("creation date").toInt()); + if (dict.contains("comment")) + metaInfoComment = QString::fromUtf8(dict.value("comment").toByteArray()); + if (dict.contains("created by")) + metaInfoCreatedBy = QString::fromUtf8(dict.value("created by").toByteArray()); + + return true; +} + +QByteArray MetaInfo::infoValue() const +{ + return infoData; +} + +QString MetaInfo::errorString() const +{ + return errString; +} + +MetaInfo::FileForm MetaInfo::fileForm() const +{ + return metaInfoFileForm; +} + +QString MetaInfo::announceUrl() const +{ + return metaInfoAnnounce; +} + +QStringList MetaInfo::announceList() const +{ + return metaInfoAnnounceList; +} + +QDateTime MetaInfo::creationDate() const +{ + return metaInfoCreationDate; +} + +QString MetaInfo::comment() const +{ + return metaInfoComment; +} + +QString MetaInfo::createdBy() const +{ + return metaInfoCreatedBy; +} + +MetaInfoSingleFile MetaInfo::singleFile() const +{ + return metaInfoSingleFile; +} + +QList<MetaInfoMultiFile> MetaInfo::multiFiles() const +{ + return metaInfoMultiFiles; +} + +QString MetaInfo::name() const +{ + return metaInfoName; +} + +int MetaInfo::pieceLength() const +{ + return metaInfoPieceLength; +} + +QList<QByteArray> MetaInfo::sha1Sums() const +{ + return metaInfoSha1Sums; +} + +qint64 MetaInfo::totalSize() const +{ + if (fileForm() == SingleFileForm) + return singleFile().length; + + qint64 size = 0; + foreach (MetaInfoMultiFile file, multiFiles()) + size += file.length; + return size; +} diff --git a/examples/network/torrent/metainfo.h b/examples/network/torrent/metainfo.h new file mode 100644 index 0000000..de60b99 --- /dev/null +++ b/examples/network/torrent/metainfo.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef METAINFO_H +#define METAINFO_H + +#include <QByteArray> +#include <QDateTime> +#include <QList> +#include <QMap> +#include <QString> +#include <QStringList> +#include <QVariant> + +struct MetaInfoSingleFile +{ + qint64 length; + QByteArray md5sum; + QString name; + int pieceLength; + QList<QByteArray> sha1Sums; +}; + +struct MetaInfoMultiFile +{ + qint64 length; + QByteArray md5sum; + QString path; +}; + +class MetaInfo +{ +public: + enum FileForm { + SingleFileForm, + MultiFileForm + }; + + MetaInfo(); + void clear(); + + bool parse(const QByteArray &data); + QString errorString() const; + + QByteArray infoValue() const; + + FileForm fileForm() const; + QString announceUrl() const; + QStringList announceList() const; + QDateTime creationDate() const; + QString comment() const; + QString createdBy() const; + + // For single file form + MetaInfoSingleFile singleFile() const; + + // For multifile form + QList<MetaInfoMultiFile> multiFiles() const; + QString name() const; + int pieceLength() const; + QList<QByteArray> sha1Sums() const; + + // Total size + qint64 totalSize() const; + +private: + QString errString; + QByteArray content; + QByteArray infoData; + + FileForm metaInfoFileForm; + MetaInfoSingleFile metaInfoSingleFile; + QList<MetaInfoMultiFile> metaInfoMultiFiles; + QString metaInfoAnnounce; + QStringList metaInfoAnnounceList; + QDateTime metaInfoCreationDate; + QString metaInfoComment; + QString metaInfoCreatedBy; + QString metaInfoName; + int metaInfoPieceLength; + QList<QByteArray> metaInfoSha1Sums; +}; + +#endif diff --git a/examples/network/torrent/peerwireclient.cpp b/examples/network/torrent/peerwireclient.cpp new file mode 100644 index 0000000..e79184b --- /dev/null +++ b/examples/network/torrent/peerwireclient.cpp @@ -0,0 +1,665 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "peerwireclient.h" + +#include <QHostAddress> +#include <QTimerEvent> + +static const int PendingRequestTimeout = 60 * 1000; +static const int ClientTimeout = 120 * 1000; +static const int ConnectTimeout = 60 * 1000; +static const int KeepAliveInterval = 30 * 1000; +static const int RateControlTimerDelay = 2000; +static const int MinimalHeaderSize = 48; +static const int FullHeaderSize = 68; +static const char ProtocolId[] = "BitTorrent protocol"; +static const char ProtocolIdSize = 19; + +// Reads a 32bit unsigned int from data in network order. +static inline quint32 fromNetworkData(const char *data) +{ + const unsigned char *udata = (const unsigned char *)data; + return (quint32(udata[0]) << 24) + | (quint32(udata[1]) << 16) + | (quint32(udata[2]) << 8) + | (quint32(udata[3])); +} + +// Writes a 32bit unsigned int from num to data in network order. +static inline void toNetworkData(quint32 num, char *data) +{ + unsigned char *udata = (unsigned char *)data; + udata[3] = (num & 0xff); + udata[2] = (num & 0xff00) >> 8; + udata[1] = (num & 0xff0000) >> 16; + udata[0] = (num & 0xff000000) >> 24; +} + +// Constructs an unconnected PeerWire client and starts the connect timer. +PeerWireClient::PeerWireClient(const QByteArray &peerId, QObject *parent) + : QTcpSocket(parent), pendingBlockSizes(0), + pwState(ChokingPeer | ChokedByPeer), receivedHandShake(false), gotPeerId(false), + sentHandShake(false), nextPacketLength(-1), pendingRequestTimer(0), invalidateTimeout(false), + keepAliveTimer(0), torrentPeer(0) +{ + memset(uploadSpeedData, 0, sizeof(uploadSpeedData)); + memset(downloadSpeedData, 0, sizeof(downloadSpeedData)); + + transferSpeedTimer = startTimer(RateControlTimerDelay); + timeoutTimer = startTimer(ConnectTimeout); + peerIdString = peerId; + + connect(this, SIGNAL(readyRead()), this, SIGNAL(readyToTransfer())); + connect(this, SIGNAL(connected()), this, SIGNAL(readyToTransfer())); + + connect(&socket, SIGNAL(connected()), + this, SIGNAL(connected())); + connect(&socket, SIGNAL(readyRead()), + this, SIGNAL(readyRead())); + connect(&socket, SIGNAL(disconnected()), + this, SIGNAL(disconnected())); + connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SIGNAL(error(QAbstractSocket::SocketError))); + connect(&socket, SIGNAL(bytesWritten(qint64)), + this, SIGNAL(bytesWritten(qint64))); + connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(socketStateChanged(QAbstractSocket::SocketState))); + +} + +// Registers the peer ID and SHA1 sum of the torrent, and initiates +// the handshake. +void PeerWireClient::initialize(const QByteArray &infoHash, int pieceCount) +{ + this->infoHash = infoHash; + peerPieces.resize(pieceCount); + if (!sentHandShake) + sendHandShake(); +} + +void PeerWireClient::setPeer(TorrentPeer *peer) +{ + torrentPeer = peer; +} + +TorrentPeer *PeerWireClient::peer() const +{ + return torrentPeer; +} + +QBitArray PeerWireClient::availablePieces() const +{ + return peerPieces; +} + +QList<TorrentBlock> PeerWireClient::incomingBlocks() const +{ + return incoming; +} + +// Sends a "choke" message, asking the peer to stop requesting blocks. +void PeerWireClient::chokePeer() +{ + const char message[] = {0, 0, 0, 1, 0}; + write(message, sizeof(message)); + pwState |= ChokingPeer; + + // After receiving a choke message, the peer will assume all + // pending requests are lost. + pendingBlocks.clear(); + pendingBlockSizes = 0; +} + +// Sends an "unchoke" message, allowing the peer to start/resume +// requesting blocks. +void PeerWireClient::unchokePeer() +{ + const char message[] = {0, 0, 0, 1, 1}; + write(message, sizeof(message)); + pwState &= ~ChokingPeer; + + if (pendingRequestTimer) + killTimer(pendingRequestTimer); +} + +// Sends a "keep-alive" message to prevent the peer from closing +// the connection when there's no activity +void PeerWireClient::sendKeepAlive() +{ + const char message[] = {0, 0, 0, 0}; + write(message, sizeof(message)); +} + +// Sends an "interested" message, informing the peer that it has got +// pieces that we'd like to download. +void PeerWireClient::sendInterested() +{ + const char message[] = {0, 0, 0, 1, 2}; + write(message, sizeof(message)); + pwState |= InterestedInPeer; + + // After telling the peer that we're interested, we expect to get + // unchoked within a certain timeframe; otherwise we'll drop the + // connection. + if (pendingRequestTimer) + killTimer(pendingRequestTimer); + pendingRequestTimer = startTimer(PendingRequestTimeout); +} + +// Sends a "not interested" message, informing the peer that it does +// not have any pieces that we'd like to download. +void PeerWireClient::sendNotInterested() +{ + const char message[] = {0, 0, 0, 1, 3}; + write(message, sizeof(message)); + pwState &= ~InterestedInPeer; +} + +// Sends a piece notification / a "have" message, informing the peer +// that we have just downloaded a new piece. +void PeerWireClient::sendPieceNotification(int piece) +{ + if (!sentHandShake) + sendHandShake(); + + char message[] = {0, 0, 0, 5, 4, 0, 0, 0, 0}; + toNetworkData(piece, &message[5]); + write(message, sizeof(message)); +} + +// Sends the complete list of pieces that we have downloaded. +void PeerWireClient::sendPieceList(const QBitArray &bitField) +{ + // The bitfield message may only be sent immediately after the + // handshaking sequence is completed, and before any other + // messages are sent. + if (!sentHandShake) + sendHandShake(); + + // Don't send the bitfield if it's all zeros. + if (bitField.count(true) == 0) + return; + + int bitFieldSize = bitField.size(); + int size = (bitFieldSize + 7) / 8; + QByteArray bits(size, '\0'); + for (int i = 0; i < bitFieldSize; ++i) { + if (bitField.testBit(i)) { + quint32 byte = quint32(i) / 8; + quint32 bit = quint32(i) % 8; + bits[byte] = uchar(bits.at(byte)) | (1 << (7 - bit)); + } + } + + char message[] = {0, 0, 0, 1, 5}; + toNetworkData(bits.size() + 1, &message[0]); + write(message, sizeof(message)); + write(bits); +} + +// Sends a request for a block. +void PeerWireClient::requestBlock(int piece, int offset, int length) +{ + char message[] = {0, 0, 0, 1, 6}; + toNetworkData(13, &message[0]); + write(message, sizeof(message)); + + char numbers[4 * 3]; + toNetworkData(piece, &numbers[0]); + toNetworkData(offset, &numbers[4]); + toNetworkData(length, &numbers[8]); + write(numbers, sizeof(numbers)); + + incoming << TorrentBlock(piece, offset, length); + + // After requesting a block, we expect the block to be sent by the + // other peer within a certain number of seconds. Otherwise, we + // drop the connection. + if (pendingRequestTimer) + killTimer(pendingRequestTimer); + pendingRequestTimer = startTimer(PendingRequestTimeout); +} + +// Cancels a request for a block. +void PeerWireClient::cancelRequest(int piece, int offset, int length) +{ + char message[] = {0, 0, 0, 1, 8}; + toNetworkData(13, &message[0]); + write(message, sizeof(message)); + + char numbers[4 * 3]; + toNetworkData(piece, &numbers[0]); + toNetworkData(offset, &numbers[4]); + toNetworkData(length, &numbers[8]); + write(numbers, sizeof(numbers)); + + incoming.removeAll(TorrentBlock(piece, offset, length)); +} + +// Sends a block to the peer. +void PeerWireClient::sendBlock(int piece, int offset, const QByteArray &data) +{ + QByteArray block; + + char message[] = {0, 0, 0, 1, 7}; + toNetworkData(9 + data.size(), &message[0]); + block += QByteArray(message, sizeof(message)); + + char numbers[4 * 2]; + toNetworkData(piece, &numbers[0]); + toNetworkData(offset, &numbers[4]); + block += QByteArray(numbers, sizeof(numbers)); + block += data; + + BlockInfo blockInfo; + blockInfo.pieceIndex = piece; + blockInfo.offset = offset; + blockInfo.length = data.size(); + blockInfo.block = block; + + pendingBlocks << blockInfo; + pendingBlockSizes += block.size(); + + if (pendingBlockSizes > 32 * 16384) { + chokePeer(); + unchokePeer(); + return; + } + emit readyToTransfer(); +} + +// Attempts to write 'bytes' bytes to the socket from the buffer. +// This is used by RateController, which precisely controls how much +// each client can write. +qint64 PeerWireClient::writeToSocket(qint64 bytes) +{ + qint64 totalWritten = 0; + do { + if (outgoingBuffer.isEmpty() && !pendingBlocks.isEmpty()) { + BlockInfo block = pendingBlocks.takeFirst(); + pendingBlockSizes -= block.length; + outgoingBuffer += block.block; + } + qint64 written = socket.write(outgoingBuffer.constData(), + qMin<qint64>(bytes - totalWritten, outgoingBuffer.size())); + if (written <= 0) + return totalWritten ? totalWritten : written; + + totalWritten += written; + uploadSpeedData[0] += written; + outgoingBuffer.remove(0, written); + } while (totalWritten < bytes && (!outgoingBuffer.isEmpty() || !pendingBlocks.isEmpty())); + + return totalWritten; +} + +// Attempts to read at most 'bytes' bytes from the socket. +qint64 PeerWireClient::readFromSocket(qint64 bytes) +{ + char buffer[1024]; + qint64 totalRead = 0; + do { + qint64 bytesRead = socket.read(buffer, qMin<qint64>(sizeof(buffer), bytes - totalRead)); + if (bytesRead <= 0) + break; + qint64 oldSize = incomingBuffer.size(); + incomingBuffer.resize(oldSize + bytesRead); + memcpy(incomingBuffer.data() + oldSize, buffer, bytesRead); + + totalRead += bytesRead; + } while (totalRead < bytes); + + if (totalRead > 0) { + downloadSpeedData[0] += totalRead; + emit bytesReceived(totalRead); + processIncomingData(); + } + return totalRead; +} + +// Returns the average number of bytes per second this client is +// downloading. +qint64 PeerWireClient::downloadSpeed() const +{ + qint64 sum = 0; + for (unsigned int i = 0; i < sizeof(downloadSpeedData) / sizeof(qint64); ++i) + sum += downloadSpeedData[i]; + return sum / (8 * 2); +} + +// Returns the average number of bytes per second this client is +// uploading. +qint64 PeerWireClient::uploadSpeed() const +{ + qint64 sum = 0; + for (unsigned int i = 0; i < sizeof(uploadSpeedData) / sizeof(qint64); ++i) + sum += uploadSpeedData[i]; + return sum / (8 * 2); +} + +void PeerWireClient::setReadBufferSize(int size) +{ + socket.setReadBufferSize(size); +} + +bool PeerWireClient::canTransferMore() const +{ + return bytesAvailable() > 0 || socket.bytesAvailable() > 0 + || !outgoingBuffer.isEmpty() || !pendingBlocks.isEmpty(); +} + +void PeerWireClient::connectToHostImplementation(const QString &hostName, + quint16 port, OpenMode openMode) + +{ + setOpenMode(openMode); + socket.connectToHost(hostName, port, openMode); +} + +void PeerWireClient::diconnectFromHostImplementation() +{ + socket.disconnectFromHost(); +} + +void PeerWireClient::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == transferSpeedTimer) { + // Rotate the upload / download records. + for (int i = 6; i >= 0; --i) { + uploadSpeedData[i + 1] = uploadSpeedData[i]; + downloadSpeedData[i + 1] = downloadSpeedData[i]; + } + uploadSpeedData[0] = 0; + downloadSpeedData[0] = 0; + } else if (event->timerId() == timeoutTimer) { + // Disconnect if we timed out; otherwise the timeout is + // restarted. + if (invalidateTimeout) { + invalidateTimeout = false; + } else { + abort(); + emit infoHashReceived(QByteArray()); + } + } else if (event->timerId() == pendingRequestTimer) { + abort(); + } else if (event->timerId() == keepAliveTimer) { + sendKeepAlive(); + } + QTcpSocket::timerEvent(event); +} + +// Sends the handshake to the peer. +void PeerWireClient::sendHandShake() +{ + sentHandShake = true; + + // Restart the timeout + if (timeoutTimer) + killTimer(timeoutTimer); + timeoutTimer = startTimer(ClientTimeout); + + // Write the 68 byte PeerWire handshake. + write(&ProtocolIdSize, 1); + write(ProtocolId, ProtocolIdSize); + write(QByteArray(8, '\0')); + write(infoHash); + write(peerIdString); +} + +void PeerWireClient::processIncomingData() +{ + invalidateTimeout = true; + if (!receivedHandShake) { + // Check that we received enough data + if (bytesAvailable() < MinimalHeaderSize) + return; + + // Sanity check the protocol ID + QByteArray id = read(ProtocolIdSize + 1); + if (id.at(0) != ProtocolIdSize || !id.mid(1).startsWith(ProtocolId)) { + abort(); + return; + } + + // Discard 8 reserved bytes, then read the info hash and peer ID + (void) read(8); + + // Read infoHash + QByteArray peerInfoHash = read(20); + if (!infoHash.isEmpty() && peerInfoHash != infoHash) { + abort(); + return; + } + + emit infoHashReceived(peerInfoHash); + if (infoHash.isEmpty()) { + abort(); + return; + } + + // Send handshake + if (!sentHandShake) + sendHandShake(); + receivedHandShake = true; + } + + // Handle delayed peer id arrival + if (!gotPeerId) { + if (bytesAvailable() < 20) + return; + gotPeerId = true; + if (read(20) == peerIdString) { + // We connected to ourself + abort(); + return; + } + } + + // Initialize keep-alive timer + if (!keepAliveTimer) + keepAliveTimer = startTimer(KeepAliveInterval); + + do { + // Find the packet length + if (nextPacketLength == -1) { + if (bytesAvailable() < 4) + return; + + char tmp[4]; + read(tmp, sizeof(tmp)); + nextPacketLength = fromNetworkData(tmp); + + if (nextPacketLength < 0 || nextPacketLength > 200000) { + // Prevent DoS + abort(); + return; + } + } + + // KeepAlive + if (nextPacketLength == 0) { + nextPacketLength = -1; + continue; + } + + // Wait with parsing until the whole packet has been received + if (bytesAvailable() < nextPacketLength) + return; + + // Read the packet + QByteArray packet = read(nextPacketLength); + if (packet.size() != nextPacketLength) { + abort(); + return; + } + + switch (packet.at(0)) { + case ChokePacket: + // We have been choked. + pwState |= ChokedByPeer; + incoming.clear(); + if (pendingRequestTimer) + killTimer(pendingRequestTimer); + emit choked(); + break; + case UnchokePacket: + // We have been unchoked. + pwState &= ~ChokedByPeer; + emit unchoked(); + break; + case InterestedPacket: + // The peer is interested in downloading. + pwState |= PeerIsInterested; + emit interested(); + break; + case NotInterestedPacket: + // The peer is not interested in downloading. + pwState &= ~PeerIsInterested; + emit notInterested(); + break; + case HavePacket: { + // The peer has a new piece available. + quint32 index = fromNetworkData(&packet.data()[1]); + if (index < quint32(peerPieces.size())) { + // Only accept indexes within the valid range. + peerPieces.setBit(int(index)); + } + emit piecesAvailable(availablePieces()); + break; + } + case BitFieldPacket: + // The peer has the following pieces available. + for (int i = 1; i < packet.size(); ++i) { + for (int bit = 0; bit < 8; ++bit) { + if (packet.at(i) & (1 << (7 - bit))) { + int bitIndex = int(((i - 1) * 8) + bit); + if (bitIndex >= 0 && bitIndex < peerPieces.size()) { + // Occasionally, broken clients claim to have + // pieces whose index is outside the valid range. + // The most common mistake is the index == size + // case. + peerPieces.setBit(bitIndex); + } + } + } + } + emit piecesAvailable(availablePieces()); + break; + case RequestPacket: { + // The peer requests a block. + quint32 index = fromNetworkData(&packet.data()[1]); + quint32 begin = fromNetworkData(&packet.data()[5]); + quint32 length = fromNetworkData(&packet.data()[9]); + emit blockRequested(int(index), int(begin), int(length)); + break; + } + case PiecePacket: { + int index = int(fromNetworkData(&packet.data()[1])); + int begin = int(fromNetworkData(&packet.data()[5])); + + incoming.removeAll(TorrentBlock(index, begin, packet.size() - 9)); + + // The peer sends a block. + emit blockReceived(index, begin, packet.mid(9)); + + // Kill the pending block timer. + if (pendingRequestTimer) { + killTimer(pendingRequestTimer); + pendingRequestTimer = 0; + } + break; + } + case CancelPacket: { + // The peer cancels a block request. + quint32 index = fromNetworkData(&packet.data()[1]); + quint32 begin = fromNetworkData(&packet.data()[5]); + quint32 length = fromNetworkData(&packet.data()[9]); + for (int i = 0; i < pendingBlocks.size(); ++i) { + const BlockInfo &blockInfo = pendingBlocks.at(i); + if (blockInfo.pieceIndex == int(index) + && blockInfo.offset == int(begin) + && blockInfo.length == int(length)) { + pendingBlocks.removeAt(i); + break; + } + } + break; + } + default: + // Unsupported packet type; just ignore it. + break; + } + nextPacketLength = -1; + } while (bytesAvailable() > 0); +} + +void PeerWireClient::socketStateChanged(QAbstractSocket::SocketState state) +{ + setLocalAddress(socket.localAddress()); + setLocalPort(socket.localPort()); + setPeerName(socket.peerName()); + setPeerAddress(socket.peerAddress()); + setPeerPort(socket.peerPort()); + setSocketState(state); +} + +qint64 PeerWireClient::readData(char *data, qint64 size) +{ + int n = qMin<int>(size, incomingBuffer.size()); + memcpy(data, incomingBuffer.constData(), n); + incomingBuffer.remove(0, n); + return n; +} + +qint64 PeerWireClient::readLineData(char *data, qint64 maxlen) +{ + return QIODevice::readLineData(data, maxlen); +} + +qint64 PeerWireClient::writeData(const char *data, qint64 size) +{ + int oldSize = outgoingBuffer.size(); + outgoingBuffer.resize(oldSize + size); + memcpy(outgoingBuffer.data() + oldSize, data, size); + emit readyToTransfer(); + return size; +} diff --git a/examples/network/torrent/peerwireclient.h b/examples/network/torrent/peerwireclient.h new file mode 100644 index 0000000..a21faa2 --- /dev/null +++ b/examples/network/torrent/peerwireclient.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PEERWIRECLIENT_H +#define PEERWIRECLIENT_H + +#include <QBitArray> +#include <QList> +#include <QTcpSocket> + +QT_BEGIN_NAMESPACE +class QHostAddress; +class QTimerEvent; +QT_END_NAMESPACE +class TorrentPeer; + +struct TorrentBlock +{ + inline TorrentBlock(int p, int o, int l) + : pieceIndex(p), offset(o), length(l) + { + } + inline bool operator==(const TorrentBlock &other) const + { + return pieceIndex == other.pieceIndex + && offset == other.offset + && length == other.length; + } + + int pieceIndex; + int offset; + int length; +}; + +class PeerWireClient : public QTcpSocket +{ + Q_OBJECT + +public: + enum PeerWireStateFlag { + ChokingPeer = 0x1, + InterestedInPeer = 0x2, + ChokedByPeer = 0x4, + PeerIsInterested = 0x8 + }; + Q_DECLARE_FLAGS(PeerWireState, PeerWireStateFlag) + + PeerWireClient(const QByteArray &peerId, QObject *parent = 0); + void initialize(const QByteArray &infoHash, int pieceCount); + + void setPeer(TorrentPeer *peer); + TorrentPeer *peer() const; + + // State + inline PeerWireState peerWireState() const { return pwState; } + QBitArray availablePieces() const; + QList<TorrentBlock> incomingBlocks() const; + + // Protocol + void chokePeer(); + void unchokePeer(); + void sendInterested(); + void sendKeepAlive(); + void sendNotInterested(); + void sendPieceNotification(int piece); + void sendPieceList(const QBitArray &bitField); + void requestBlock(int piece, int offset, int length); + void cancelRequest(int piece, int offset, int length); + void sendBlock(int piece, int offset, const QByteArray &data); + + // Rate control + qint64 writeToSocket(qint64 bytes); + qint64 readFromSocket(qint64 bytes); + qint64 downloadSpeed() const; + qint64 uploadSpeed() const; + + bool canTransferMore() const; + qint64 bytesAvailable() const { return incomingBuffer.size() + QTcpSocket::bytesAvailable(); } + qint64 socketBytesAvailable() const { return socket.bytesAvailable(); } + qint64 socketBytesToWrite() const { return socket.bytesToWrite(); } + + void setReadBufferSize(int size); + +signals: + void infoHashReceived(const QByteArray &infoHash); + void readyToTransfer(); + + void choked(); + void unchoked(); + void interested(); + void notInterested(); + + void piecesAvailable(const QBitArray &pieces); + void blockRequested(int pieceIndex, int begin, int length); + void blockReceived(int pieceIndex, int begin, const QByteArray &data); + + void bytesReceived(qint64 size); + +protected slots: + void connectToHostImplementation(const QString &hostName, + quint16 port, OpenMode openMode = ReadWrite); + void diconnectFromHostImplementation(); + +protected: + void timerEvent(QTimerEvent *event); + + qint64 readData(char *data, qint64 maxlen); + qint64 readLineData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + +private slots: + void sendHandShake(); + void processIncomingData(); + void socketStateChanged(QAbstractSocket::SocketState state); + +private: + // Data waiting to be read/written + QByteArray incomingBuffer; + QByteArray outgoingBuffer; + + struct BlockInfo { + int pieceIndex; + int offset; + int length; + QByteArray block; + }; + QList<BlockInfo> pendingBlocks; + int pendingBlockSizes; + QList<TorrentBlock> incoming; + + enum PacketType { + ChokePacket = 0, + UnchokePacket = 1, + InterestedPacket = 2, + NotInterestedPacket = 3, + HavePacket = 4, + BitFieldPacket = 5, + RequestPacket = 6, + PiecePacket = 7, + CancelPacket = 8 + }; + + // State + PeerWireState pwState; + bool receivedHandShake; + bool gotPeerId; + bool sentHandShake; + int nextPacketLength; + + // Upload/download speed records + qint64 uploadSpeedData[8]; + qint64 downloadSpeedData[8]; + int transferSpeedTimer; + + // Timeout handling + int timeoutTimer; + int pendingRequestTimer; + bool invalidateTimeout; + int keepAliveTimer; + + // Checksum, peer ID and set of available pieces + QByteArray infoHash; + QByteArray peerIdString; + QBitArray peerPieces; + TorrentPeer *torrentPeer; + + QTcpSocket socket; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(PeerWireClient::PeerWireState) + +#endif diff --git a/examples/network/torrent/ratecontroller.cpp b/examples/network/torrent/ratecontroller.cpp new file mode 100644 index 0000000..243d897 --- /dev/null +++ b/examples/network/torrent/ratecontroller.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "peerwireclient.h" +#include "ratecontroller.h" + +#include <QtCore> + +Q_GLOBAL_STATIC(RateController, rateController) + +RateController *RateController::instance() +{ + return rateController(); +} + +void RateController::addSocket(PeerWireClient *socket) +{ + connect(socket, SIGNAL(readyToTransfer()), this, SLOT(scheduleTransfer())); + socket->setReadBufferSize(downLimit * 4); + sockets << socket; + scheduleTransfer(); +} + +void RateController::removeSocket(PeerWireClient *socket) +{ + disconnect(socket, SIGNAL(readyToTransfer()), this, SLOT(scheduleTransfer())); + socket->setReadBufferSize(0); + sockets.remove(socket); +} + +void RateController::setDownloadLimit(int bytesPerSecond) +{ + downLimit = bytesPerSecond; + foreach (PeerWireClient *socket, sockets) + socket->setReadBufferSize(downLimit * 4); +} + +void RateController::scheduleTransfer() +{ + if (transferScheduled) + return; + transferScheduled = true; + QTimer::singleShot(50, this, SLOT(transfer())); +} + +void RateController::transfer() +{ + transferScheduled = false; + if (sockets.isEmpty()) + return; + + int msecs = 1000; + if (!stopWatch.isNull()) + msecs = qMin(msecs, stopWatch.elapsed()); + + qint64 bytesToWrite = (upLimit * msecs) / 1000; + qint64 bytesToRead = (downLimit * msecs) / 1000; + if (bytesToWrite == 0 && bytesToRead == 0) { + scheduleTransfer(); + return; + } + + QSet<PeerWireClient *> pendingSockets; + foreach (PeerWireClient *client, sockets) { + if (client->canTransferMore()) + pendingSockets << client; + } + if (pendingSockets.isEmpty()) + return; + + stopWatch.start(); + + bool canTransferMore; + do { + canTransferMore = false; + qint64 writeChunk = qMax<qint64>(1, bytesToWrite / pendingSockets.size()); + qint64 readChunk = qMax<qint64>(1, bytesToRead / pendingSockets.size()); + + QSetIterator<PeerWireClient *> it(pendingSockets); + while (it.hasNext() && (bytesToWrite > 0 || bytesToRead > 0)) { + PeerWireClient *socket = it.next(); + if (socket->state() != QAbstractSocket::ConnectedState) { + pendingSockets.remove(socket); + continue; + } + + bool dataTransferred = false; + qint64 available = qMin<qint64>(socket->socketBytesAvailable(), readChunk); + if (available > 0) { + qint64 readBytes = socket->readFromSocket(qMin<qint64>(available, bytesToRead)); + if (readBytes > 0) { + bytesToRead -= readBytes; + dataTransferred = true; + } + } + + if (upLimit * 2 > socket->bytesToWrite()) { + qint64 chunkSize = qMin<qint64>(writeChunk, bytesToWrite); + qint64 toWrite = qMin(upLimit * 2 - socket->bytesToWrite(), chunkSize); + if (toWrite > 0) { + qint64 writtenBytes = socket->writeToSocket(toWrite); + if (writtenBytes > 0) { + bytesToWrite -= writtenBytes; + dataTransferred = true; + } + } + } + + if (dataTransferred && socket->canTransferMore()) + canTransferMore = true; + else + pendingSockets.remove(socket); + } + } while (canTransferMore && (bytesToWrite > 0 || bytesToRead > 0) && !pendingSockets.isEmpty()); + + if (canTransferMore || bytesToWrite == 0 || bytesToRead == 0) + scheduleTransfer(); +} diff --git a/examples/network/torrent/ratecontroller.h b/examples/network/torrent/ratecontroller.h new file mode 100644 index 0000000..fb22159 --- /dev/null +++ b/examples/network/torrent/ratecontroller.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RATECONTROLLER_H +#define RATECONTROLLER_H + +#include <QObject> +#include <QSet> +#include <QTime> + +class PeerWireClient; + +class RateController : public QObject +{ + Q_OBJECT + +public: + inline RateController(QObject *parent = 0) + : QObject(parent), transferScheduled(false) { } + static RateController *instance(); + + void addSocket(PeerWireClient *socket); + void removeSocket(PeerWireClient *socket); + + inline int uploadLimit() const { return upLimit; } + inline int downloadLimit() const { return downLimit; } + inline void setUploadLimit(int bytesPerSecond) { upLimit = bytesPerSecond; } + void setDownloadLimit(int bytesPerSecond); + +public slots: + void transfer(); + void scheduleTransfer(); + +private: + QTime stopWatch; + QSet<PeerWireClient *> sockets; + int upLimit; + int downLimit; + bool transferScheduled; +}; + +#endif diff --git a/examples/network/torrent/torrent.pro b/examples/network/torrent/torrent.pro new file mode 100644 index 0000000..10b2672 --- /dev/null +++ b/examples/network/torrent/torrent.pro @@ -0,0 +1,37 @@ +HEADERS += addtorrentdialog.h \ + bencodeparser.h \ + connectionmanager.h \ + mainwindow.h \ + metainfo.h \ + peerwireclient.h \ + ratecontroller.h \ + filemanager.h \ + torrentclient.h \ + torrentserver.h \ + trackerclient.h + +SOURCES += main.cpp \ + addtorrentdialog.cpp \ + bencodeparser.cpp \ + connectionmanager.cpp \ + mainwindow.cpp \ + metainfo.cpp \ + peerwireclient.cpp \ + ratecontroller.cpp \ + filemanager.cpp \ + torrentclient.cpp \ + torrentserver.cpp \ + trackerclient.cpp + +# Forms and resources +FORMS += forms/addtorrentform.ui +RESOURCES += icons.qrc + +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/torrent +sources.files = $$SOURCES $$HEADERS $$RESOURCES torrent.pro *.torrent +sources.files += icons forms 3rdparty +sources.path = $$[QT_INSTALL_EXAMPLES]/network/torrent +INSTALLS += target sources diff --git a/examples/network/torrent/torrentclient.cpp b/examples/network/torrent/torrentclient.cpp new file mode 100644 index 0000000..e8473bb --- /dev/null +++ b/examples/network/torrent/torrentclient.cpp @@ -0,0 +1,1529 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "connectionmanager.h" +#include "filemanager.h" +#include "metainfo.h" +#include "torrentclient.h" +#include "torrentserver.h" +#include "trackerclient.h" +#include "peerwireclient.h" +#include "ratecontroller.h" + +#include <QtCore> +#include <QNetworkInterface> + +// These constants could also be configurable by the user. +static const int ServerMinPort = 6881; +static const int ServerMaxPort = /* 6889 */ 7000; +static const int BlockSize = 16384; +static const int MaxBlocksInProgress = 5; +static const int MaxBlocksInMultiMode = 2; +static const int MaxConnectionPerPeer = 1; +static const int RateControlWindowLength = 10; +static const int RateControlTimerDelay = 1000; +static const int MinimumTimeBeforeRevisit = 30; +static const int MaxUploads = 4; +static const int UploadScheduleInterval = 10000; +static const int EndGamePieces = 5; + +class TorrentPiece { +public: + int index; + int length; + QBitArray completedBlocks; + QBitArray requestedBlocks; + bool inProgress; +}; + +class TorrentClientPrivate +{ +public: + TorrentClientPrivate(TorrentClient *qq); + + // State / error + void setError(TorrentClient::Error error); + void setState(TorrentClient::State state); + TorrentClient::Error error; + TorrentClient::State state; + QString errorString; + QString stateString; + + // Where to save data + QString destinationFolder; + MetaInfo metaInfo; + + // Announce tracker and file manager + QByteArray peerId; + QByteArray infoHash; + TrackerClient trackerClient; + FileManager fileManager; + + // Connections + QList<PeerWireClient *> connections; + QList<TorrentPeer *> peers; + bool schedulerCalled; + void callScheduler(); + bool connectingToClients; + void callPeerConnector(); + int uploadScheduleTimer; + + // Pieces + QMap<int, PeerWireClient *> readIds; + QMultiMap<PeerWireClient *, TorrentPiece *> payloads; + QMap<int, TorrentPiece *> pendingPieces; + QBitArray completedPieces; + QBitArray incompletePieces; + int pieceCount; + + // Progress + int lastProgressValue; + qint64 downloadedBytes; + qint64 uploadedBytes; + int downloadRate[RateControlWindowLength]; + int uploadRate[RateControlWindowLength]; + int transferRateTimer; + + TorrentClient *q; +}; + +TorrentClientPrivate::TorrentClientPrivate(TorrentClient *qq) + : trackerClient(qq), q(qq) +{ + error = TorrentClient::UnknownError; + state = TorrentClient::Idle; + errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unknown error"); + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Idle"); + schedulerCalled = false; + connectingToClients = false; + uploadScheduleTimer = 0; + lastProgressValue = -1; + pieceCount = 0; + downloadedBytes = 0; + uploadedBytes = 0; + memset(downloadRate, 0, sizeof(downloadRate)); + memset(uploadRate, 0, sizeof(uploadRate)); + transferRateTimer = 0; +} + +void TorrentClientPrivate::setError(TorrentClient::Error errorCode) +{ + this->error = errorCode; + switch (error) { + case TorrentClient::UnknownError: + errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unknown error"); + break; + case TorrentClient::TorrentParseError: + errorString = QT_TRANSLATE_NOOP(TorrentClient, "Invalid torrent data"); + break; + case TorrentClient::InvalidTrackerError: + errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unable to connect to tracker"); + break; + case TorrentClient::FileError: + errorString = QT_TRANSLATE_NOOP(TorrentClient, "File error"); + break; + case TorrentClient::ServerError: + errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unable to initialize server"); + break; + } + emit q->error(errorCode); +} + +void TorrentClientPrivate::setState(TorrentClient::State state) +{ + this->state = state; + switch (state) { + case TorrentClient::Idle: + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Idle"); + break; + case TorrentClient::Paused: + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Paused"); + break; + case TorrentClient::Stopping: + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Stopping"); + break; + case TorrentClient::Preparing: + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Preparing"); + break; + case TorrentClient::Searching: + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Searching"); + break; + case TorrentClient::Connecting: + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Connecting"); + break; + case TorrentClient::WarmingUp: + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Warming up"); + break; + case TorrentClient::Downloading: + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Downloading"); + break; + case TorrentClient::Endgame: + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Finishing"); + break; + case TorrentClient::Seeding: + stateString = QT_TRANSLATE_NOOP(TorrentClient, "Seeding"); + break; + } + emit q->stateChanged(state); +} + +void TorrentClientPrivate::callScheduler() +{ + if (!schedulerCalled) { + schedulerCalled = true; + QMetaObject::invokeMethod(q, "scheduleDownloads", Qt::QueuedConnection); + } +} + +void TorrentClientPrivate::callPeerConnector() +{ + if (!connectingToClients) { + connectingToClients = true; + QTimer::singleShot(10000, q, SLOT(connectToPeers())); + } +} + +TorrentClient::TorrentClient(QObject *parent) + : QObject(parent), d(new TorrentClientPrivate(this)) +{ + // Connect the file manager + connect(&d->fileManager, SIGNAL(dataRead(int, int, int, const QByteArray &)), + this, SLOT(sendToPeer(int, int, int, const QByteArray &))); + connect(&d->fileManager, SIGNAL(verificationProgress(int)), + this, SLOT(updateProgress(int))); + connect(&d->fileManager, SIGNAL(verificationDone()), + this, SLOT(fullVerificationDone())); + connect(&d->fileManager, SIGNAL(pieceVerified(int, bool)), + this, SLOT(pieceVerified(int, bool))); + connect(&d->fileManager, SIGNAL(error()), + this, SLOT(handleFileError())); + + // Connect the tracker client + connect(&d->trackerClient, SIGNAL(peerListUpdated(const QList<TorrentPeer> &)), + this, SLOT(addToPeerList(const QList<TorrentPeer> &))); + connect(&d->trackerClient, SIGNAL(stopped()), + this, SIGNAL(stopped())); +} + +TorrentClient::~TorrentClient() +{ + qDeleteAll(d->peers); + qDeleteAll(d->pendingPieces); + delete d; +} + +bool TorrentClient::setTorrent(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly) || !setTorrent(file.readAll())) { + d->setError(TorrentParseError); + return false; + } + return true; +} + +bool TorrentClient::setTorrent(const QByteArray &torrentData) +{ + if (!d->metaInfo.parse(torrentData)) { + d->setError(TorrentParseError); + return false; + } + + // Calculate SHA1 hash of the "info" section in the torrent + QByteArray infoValue = d->metaInfo.infoValue(); + d->infoHash = QCryptographicHash::hash(infoValue, QCryptographicHash::Sha1); + + return true; +} + +MetaInfo TorrentClient::metaInfo() const +{ + return d->metaInfo; +} + +void TorrentClient::setDestinationFolder(const QString &directory) +{ + d->destinationFolder = directory; +} + +QString TorrentClient::destinationFolder() const +{ + return d->destinationFolder; +} + +void TorrentClient::setDumpedState(const QByteArray &dumpedState) +{ + // Recover partially completed pieces + QDataStream stream(dumpedState); + + quint16 version = 0; + stream >> version; + if (version != 2) + return; + + stream >> d->completedPieces; + + while (!stream.atEnd()) { + int index; + int length; + QBitArray completed; + stream >> index >> length >> completed; + if (stream.status() != QDataStream::Ok) { + d->completedPieces.clear(); + break; + } + + TorrentPiece *piece = new TorrentPiece; + piece->index = index; + piece->length = length; + piece->completedBlocks = completed; + piece->requestedBlocks.resize(completed.size()); + piece->inProgress = false; + d->pendingPieces[index] = piece; + } +} + +QByteArray TorrentClient::dumpedState() const +{ + QByteArray partials; + QDataStream stream(&partials, QIODevice::WriteOnly); + + stream << quint16(2); + stream << d->completedPieces; + + // Save the state of all partially downloaded pieces into a format + // suitable for storing in settings. + QMap<int, TorrentPiece *>::ConstIterator it = d->pendingPieces.constBegin(); + while (it != d->pendingPieces.constEnd()) { + TorrentPiece *piece = it.value(); + if (blocksLeftForPiece(piece) > 0 && blocksLeftForPiece(piece) < piece->completedBlocks.size()) { + stream << piece->index; + stream << piece->length; + stream << piece->completedBlocks; + } + ++it; + } + + return partials; +} + +qint64 TorrentClient::progress() const +{ + return d->lastProgressValue; +} + +void TorrentClient::setDownloadedBytes(qint64 bytes) +{ + d->downloadedBytes = bytes; +} + +qint64 TorrentClient::downloadedBytes() const +{ + return d->downloadedBytes; +} + +void TorrentClient::setUploadedBytes(qint64 bytes) +{ + d->uploadedBytes = bytes; +} + +qint64 TorrentClient::uploadedBytes() const +{ + return d->uploadedBytes; +} + +int TorrentClient::connectedPeerCount() const +{ + int tmp = 0; + foreach (PeerWireClient *client, d->connections) { + if (client->state() == QAbstractSocket::ConnectedState) + ++tmp; + } + return tmp; +} + +int TorrentClient::seedCount() const +{ + int tmp = 0; + foreach (PeerWireClient *client, d->connections) { + if (client->availablePieces().count(true) == d->pieceCount) + ++tmp; + } + return tmp; +} + +TorrentClient::State TorrentClient::state() const +{ + return d->state; +} + +QString TorrentClient::stateString() const +{ + return d->stateString; +} + +TorrentClient::Error TorrentClient::error() const +{ + return d->error; +} + +QString TorrentClient::errorString() const +{ + return d->errorString; +} + +QByteArray TorrentClient::peerId() const +{ + return d->peerId; +} + +QByteArray TorrentClient::infoHash() const +{ + return d->infoHash; +} + +void TorrentClient::start() +{ + if (d->state != Idle) + return; + + TorrentServer::instance()->addClient(this); + + // Initialize the file manager + d->setState(Preparing); + d->fileManager.setMetaInfo(d->metaInfo); + d->fileManager.setDestinationFolder(d->destinationFolder); + d->fileManager.setCompletedPieces(d->completedPieces); + d->fileManager.start(QThread::LowestPriority); + d->fileManager.startDataVerification(); +} + +void TorrentClient::stop() +{ + if (d->state == Stopping) + return; + + TorrentServer::instance()->removeClient(this); + + // Update the state + State oldState = d->state; + d->setState(Stopping); + + // Stop the timer + if (d->transferRateTimer) { + killTimer(d->transferRateTimer); + d->transferRateTimer = 0; + } + + // Abort all existing connections + foreach (PeerWireClient *client, d->connections) { + RateController::instance()->removeSocket(client); + ConnectionManager::instance()->removeConnection(client); + client->abort(); + } + d->connections.clear(); + + // Perhaps stop the tracker + if (oldState > Preparing) { + d->trackerClient.stop(); + } else { + d->setState(Idle); + emit stopped(); + } +} + +void TorrentClient::setPaused(bool paused) +{ + if (paused) { + // Abort all connections, and set the max number of + // connections to 0. Keep the list of peers, so we can quickly + // resume later. + d->setState(Paused); + foreach (PeerWireClient *client, d->connections) + client->abort(); + d->connections.clear(); + TorrentServer::instance()->removeClient(this); + } else { + // Restore the max number of connections, and start the peer + // connector. We should also quickly start receiving incoming + // connections. + d->setState(d->completedPieces.count(true) == d->fileManager.pieceCount() + ? Seeding : Searching); + connectToPeers(); + TorrentServer::instance()->addClient(this); + } +} + +void TorrentClient::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == d->uploadScheduleTimer) { + // Update the state of who's choked and who's not + scheduleUploads(); + return; + } + + if (event->timerId() != d->transferRateTimer) { + QObject::timerEvent(event); + return; + } + + // Calculate average upload/download rate + qint64 uploadBytesPerSecond = 0; + qint64 downloadBytesPerSecond = 0; + for (int i = 0; i < RateControlWindowLength; ++i) { + uploadBytesPerSecond += d->uploadRate[i]; + downloadBytesPerSecond += d->downloadRate[i]; + } + uploadBytesPerSecond /= qint64(RateControlWindowLength); + downloadBytesPerSecond /= qint64(RateControlWindowLength); + for (int i = RateControlWindowLength - 2; i >= 0; --i) { + d->uploadRate[i + 1] = d->uploadRate[i]; + d->downloadRate[i + 1] = d->downloadRate[i]; + } + d->uploadRate[0] = 0; + d->downloadRate[0] = 0; + emit uploadRateUpdated(int(uploadBytesPerSecond)); + emit downloadRateUpdated(int(downloadBytesPerSecond)); + + // Stop the timer if there is no activity. + if (downloadBytesPerSecond == 0 && uploadBytesPerSecond == 0) { + killTimer(d->transferRateTimer); + d->transferRateTimer = 0; + } +} + +void TorrentClient::sendToPeer(int readId, int pieceIndex, int begin, const QByteArray &data) +{ + // Send the requested block to the peer if the client connection + // still exists; otherwise do nothing. This slot is called by the + // file manager after it has read a block of data. + PeerWireClient *client = d->readIds.value(readId); + if (client) { + if ((client->peerWireState() & PeerWireClient::ChokingPeer) == 0) + client->sendBlock(pieceIndex, begin, data); + } + d->readIds.remove(readId); +} + +void TorrentClient::fullVerificationDone() +{ + // Update our list of completed and incomplete pieces. + d->completedPieces = d->fileManager.completedPieces(); + d->incompletePieces.resize(d->completedPieces.size()); + d->pieceCount = d->completedPieces.size(); + for (int i = 0; i < d->fileManager.pieceCount(); ++i) { + if (!d->completedPieces.testBit(i)) + d->incompletePieces.setBit(i); + } + + updateProgress(); + + // If the checksums show that what the dumped state thought was + // partial was in fact complete, then we trust the checksums. + QMap<int, TorrentPiece *>::Iterator it = d->pendingPieces.begin(); + while (it != d->pendingPieces.end()) { + if (d->completedPieces.testBit(it.key())) + it = d->pendingPieces.erase(it); + else + ++it; + } + + d->uploadScheduleTimer = startTimer(UploadScheduleInterval); + + // Start the server + TorrentServer *server = TorrentServer::instance(); + if (!server->isListening()) { + // Set up the peer wire server + for (int i = ServerMinPort; i <= ServerMaxPort; ++i) { + if (server->listen(QHostAddress::Any, i)) + break; + } + if (!server->isListening()) { + d->setError(ServerError); + return; + } + } + + d->setState(d->completedPieces.count(true) == d->pieceCount ? Seeding : Searching); + + // Start the tracker client + d->trackerClient.start(d->metaInfo); +} + +void TorrentClient::pieceVerified(int pieceIndex, bool ok) +{ + TorrentPiece *piece = d->pendingPieces.value(pieceIndex); + + // Remove this piece from all payloads + QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.begin(); + while (it != d->payloads.end()) { + if (it.value()->index == pieceIndex) + it = d->payloads.erase(it); + else + ++it; + } + + if (!ok) { + // If a piece did not pass the SHA1 check, we'll simply clear + // its state, and the scheduler will re-request it + piece->inProgress = false; + piece->completedBlocks.fill(false); + piece->requestedBlocks.fill(false); + d->callScheduler(); + return; + } + + // Update the peer list so we know who's still interesting. + foreach (TorrentPeer *peer, d->peers) { + if (!peer->interesting) + continue; + bool interesting = false; + for (int i = 0; i < d->pieceCount; ++i) { + if (peer->pieces.testBit(i) && d->incompletePieces.testBit(i)) { + interesting = true; + break; + } + } + peer->interesting = interesting; + } + + // Delete the piece and update our structures. + delete piece; + d->pendingPieces.remove(pieceIndex); + d->completedPieces.setBit(pieceIndex); + d->incompletePieces.clearBit(pieceIndex); + + // Notify connected peers. + foreach (PeerWireClient *client, d->connections) { + if (client->state() == QAbstractSocket::ConnectedState + && !client->availablePieces().testBit(pieceIndex)) { + client->sendPieceNotification(pieceIndex); + } + } + + // Notify the tracker if we've entered Seeding status; otherwise + // call the scheduler. + int completed = d->completedPieces.count(true); + if (completed == d->pieceCount) { + if (d->state != Seeding) { + d->setState(Seeding); + d->trackerClient.startSeeding(); + } + } else { + if (completed == 1) + d->setState(Downloading); + else if (d->incompletePieces.count(true) < 5 && d->pendingPieces.size() > d->incompletePieces.count(true)) + d->setState(Endgame); + d->callScheduler(); + } + + updateProgress(); +} + +void TorrentClient::handleFileError() +{ + if (d->state == Paused) + return; + setPaused(true); + emit error(FileError); +} + +void TorrentClient::connectToPeers() +{ + d->connectingToClients = false; + + if (d->state == Stopping || d->state == Idle || d->state == Paused) + return; + + if (d->state == Searching) + d->setState(Connecting); + + // Find the list of peers we are not currently connected to, where + // the more interesting peers are listed more than once. + QList<TorrentPeer *> weighedPeers = weighedFreePeers(); + + // Start as many connections as we can + while (!weighedPeers.isEmpty() && ConnectionManager::instance()->canAddConnection() + && (qrand() % (ConnectionManager::instance()->maxConnections() / 2))) { + PeerWireClient *client = new PeerWireClient(ConnectionManager::instance()->clientId(), this); + RateController::instance()->addSocket(client); + ConnectionManager::instance()->addConnection(client); + + initializeConnection(client); + d->connections << client; + + // Pick a random peer from the list of weighed peers. + TorrentPeer *peer = weighedPeers.takeAt(qrand() % weighedPeers.size()); + weighedPeers.removeAll(peer); + peer->connectStart = QDateTime::currentDateTime().toTime_t(); + peer->lastVisited = peer->connectStart; + + // Connect to the peer. + client->setPeer(peer); + client->connectToHost(peer->address, peer->port); + } +} + +QList<TorrentPeer *> TorrentClient::weighedFreePeers() const +{ + QList<TorrentPeer *> weighedPeers; + + // Generate a list of peers that we want to connect to. + uint now = QDateTime::currentDateTime().toTime_t(); + QList<TorrentPeer *> freePeers; + QMap<QString, int> connectionsPerPeer; + foreach (TorrentPeer *peer, d->peers) { + bool busy = false; + foreach (PeerWireClient *client, d->connections) { + if (client->state() == PeerWireClient::ConnectedState + && client->peerAddress() == peer->address + && client->peerPort() == peer->port) { + if (++connectionsPerPeer[peer->address.toString()] >= MaxConnectionPerPeer) { + busy = true; + break; + } + } + } + if (!busy && (now - peer->lastVisited) > uint(MinimumTimeBeforeRevisit)) + freePeers << peer; + } + + // Nothing to connect to + if (freePeers.isEmpty()) + return weighedPeers; + + // Assign points based on connection speed and pieces available. + QList<QPair<int, TorrentPeer *> > points; + foreach (TorrentPeer *peer, freePeers) { + int tmp = 0; + if (peer->interesting) { + tmp += peer->numCompletedPieces; + if (d->state == Seeding) + tmp = d->pieceCount - tmp; + if (!peer->connectStart) // An unknown peer is as interesting as a seed + tmp += d->pieceCount; + + // 1/5 of the total score for each second below 5 it takes to + // connect. + if (peer->connectTime < 5) + tmp += (d->pieceCount / 10) * (5 - peer->connectTime); + } + points << QPair<int, TorrentPeer *>(tmp, peer); + } + qSort(points); + + // Minimize the list so the point difference is never more than 1. + typedef QPair<int,TorrentPeer*> PointPair; + QMultiMap<int, TorrentPeer *> pointMap; + int lowestScore = 0; + int lastIndex = 0; + foreach (PointPair point, points) { + if (point.first > lowestScore) { + lowestScore = point.first; + ++lastIndex; + } + pointMap.insert(lastIndex, point.second); + } + + // Now make up a list of peers where the ones with more points are + // listed many times. + QMultiMap<int, TorrentPeer *>::ConstIterator it = pointMap.constBegin(); + while (it != pointMap.constEnd()) { + for (int i = 0; i < it.key() + 1; ++i) + weighedPeers << it.value(); + ++it; + } + + return weighedPeers; +} + +void TorrentClient::setupIncomingConnection(PeerWireClient *client) +{ + // Connect signals + initializeConnection(client); + + // Initialize this client + RateController::instance()->addSocket(client); + d->connections << client; + + client->initialize(d->infoHash, d->pieceCount); + client->sendPieceList(d->completedPieces); + + emit peerInfoUpdated(); + + if (d->state == Searching || d->state == Connecting) { + int completed = d->completedPieces.count(true); + if (completed == 0) + d->setState(WarmingUp); + else if (d->incompletePieces.count(true) < 5 && d->pendingPieces.size() > d->incompletePieces.count(true)) + d->setState(Endgame); + } + + if (d->connections.isEmpty()) + scheduleUploads(); +} + +void TorrentClient::setupOutgoingConnection() +{ + PeerWireClient *client = qobject_cast<PeerWireClient *>(sender()); + + // Update connection statistics. + foreach (TorrentPeer *peer, d->peers) { + if (peer->port == client->peerPort() && peer->address == client->peerAddress()) { + peer->connectTime = peer->lastVisited - peer->connectStart; + break; + } + } + + // Send handshake and piece list + client->initialize(d->infoHash, d->pieceCount); + client->sendPieceList(d->completedPieces); + + emit peerInfoUpdated(); + + if (d->state == Searching || d->state == Connecting) { + int completed = d->completedPieces.count(true); + if (completed == 0) + d->setState(WarmingUp); + else if (d->incompletePieces.count(true) < 5 && d->pendingPieces.size() > d->incompletePieces.count(true)) + d->setState(Endgame); + } +} + +void TorrentClient::initializeConnection(PeerWireClient *client) +{ + connect(client, SIGNAL(connected()), + this, SLOT(setupOutgoingConnection())); + connect(client, SIGNAL(disconnected()), + this, SLOT(removeClient())); + connect(client, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(removeClient())); + connect(client, SIGNAL(piecesAvailable(const QBitArray &)), + this, SLOT(peerPiecesAvailable(const QBitArray &))); + connect(client, SIGNAL(blockRequested(int, int, int)), + this, SLOT(peerRequestsBlock(int, int, int))); + connect(client, SIGNAL(blockReceived(int, int, const QByteArray &)), + this, SLOT(blockReceived(int, int, const QByteArray &))); + connect(client, SIGNAL(choked()), + this, SLOT(peerChoked())); + connect(client, SIGNAL(unchoked()), + this, SLOT(peerUnchoked())); + connect(client, SIGNAL(bytesWritten(qint64)), + this, SLOT(peerWireBytesWritten(qint64))); + connect(client, SIGNAL(bytesReceived(qint64)), + this, SLOT(peerWireBytesReceived(qint64))); +} + +void TorrentClient::removeClient() +{ + PeerWireClient *client = static_cast<PeerWireClient *>(sender()); + + // Remove the host from our list of known peers if the connection + // failed. + if (client->peer() && client->error() == QAbstractSocket::ConnectionRefusedError) + d->peers.removeAll(client->peer()); + + // Remove the client from RateController and all structures. + RateController::instance()->removeSocket(client); + d->connections.removeAll(client); + QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(client); + while (it != d->payloads.end() && it.key() == client) { + TorrentPiece *piece = it.value(); + piece->inProgress = false; + piece->requestedBlocks.fill(false); + it = d->payloads.erase(it); + } + + // Remove pending read requests. + QMapIterator<int, PeerWireClient *> it2(d->readIds); + while (it2.findNext(client)) + d->readIds.remove(it2.key()); + + // Delete the client later. + disconnect(client, SIGNAL(disconnected()), this, SLOT(removeClient())); + client->deleteLater(); + ConnectionManager::instance()->removeConnection(client); + + emit peerInfoUpdated(); + d->callPeerConnector(); +} + +void TorrentClient::peerPiecesAvailable(const QBitArray &pieces) +{ + PeerWireClient *client = qobject_cast<PeerWireClient *>(sender()); + + // Find the peer in our list of announced peers. If it's there, + // then we can use the piece list into to gather statistics that + // help us decide what peers to connect to. + TorrentPeer *peer = 0; + QList<TorrentPeer *>::Iterator it = d->peers.begin(); + while (it != d->peers.end()) { + if ((*it)->address == client->peerAddress() && (*it)->port == client->peerPort()) { + peer = *it; + break; + } + ++it; + } + + // If the peer is a seed, and we are in seeding mode, then the + // peer is uninteresting. + if (pieces.count(true) == d->pieceCount) { + if (peer) + peer->seed = true; + emit peerInfoUpdated(); + if (d->state == Seeding) { + client->abort(); + return; + } else { + if (peer) + peer->interesting = true; + if ((client->peerWireState() & PeerWireClient::InterestedInPeer) == 0) + client->sendInterested(); + d->callScheduler(); + return; + } + } + + // Update our list of available pieces. + if (peer) { + peer->pieces = pieces; + peer->numCompletedPieces = pieces.count(true); + } + + // Check for interesting pieces, and tell the peer whether we are + // interested or not. + bool interested = false; + int piecesSize = pieces.size(); + for (int pieceIndex = 0; pieceIndex < piecesSize; ++pieceIndex) { + if (!pieces.testBit(pieceIndex)) + continue; + if (!d->completedPieces.testBit(pieceIndex)) { + interested = true; + if ((client->peerWireState() & PeerWireClient::InterestedInPeer) == 0) { + if (peer) + peer->interesting = true; + client->sendInterested(); + } + + QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(client); + int inProgress = 0; + while (it != d->payloads.end() && it.key() == client) { + if (it.value()->inProgress) + inProgress += it.value()->requestedBlocks.count(true); + ++it; + } + if (!inProgress) + d->callScheduler(); + break; + } + } + if (!interested && (client->peerWireState() & PeerWireClient::InterestedInPeer)) { + if (peer) + peer->interesting = false; + client->sendNotInterested(); + } +} + +void TorrentClient::peerRequestsBlock(int pieceIndex, int begin, int length) +{ + PeerWireClient *client = qobject_cast<PeerWireClient *>(sender()); + + // Silently ignore requests from choked peers + if (client->peerWireState() & PeerWireClient::ChokingPeer) + return; + + // Silently ignore requests for pieces we don't have. + if (!d->completedPieces.testBit(pieceIndex)) + return; + + // Request the block from the file manager + d->readIds.insert(d->fileManager.read(pieceIndex, begin, length), + qobject_cast<PeerWireClient *>(sender())); +} + +void TorrentClient::blockReceived(int pieceIndex, int begin, const QByteArray &data) +{ + PeerWireClient *client = qobject_cast<PeerWireClient *>(sender()); + if (data.size() == 0) { + client->abort(); + return; + } + + // Ignore it if we already have this block. + int blockBit = begin / BlockSize; + TorrentPiece *piece = d->pendingPieces.value(pieceIndex); + if (!piece || piece->completedBlocks.testBit(blockBit)) { + // Discard blocks that we already have, and fill up the pipeline. + requestMore(client); + return; + } + + // If we are in warmup or endgame mode, cancel all duplicate + // requests for this block. + if (d->state == WarmingUp || d->state == Endgame) { + QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.begin(); + while (it != d->payloads.end()) { + PeerWireClient *otherClient = it.key(); + if (otherClient != client && it.value()->index == pieceIndex) { + if (otherClient->incomingBlocks().contains(TorrentBlock(pieceIndex, begin, data.size()))) + it.key()->cancelRequest(pieceIndex, begin, data.size()); + } + ++it; + } + } + + if (d->state != Downloading && d->state != Endgame && d->completedPieces.count(true) > 0) + d->setState(Downloading); + + // Store this block + d->fileManager.write(pieceIndex, begin, data); + piece->completedBlocks.setBit(blockBit); + piece->requestedBlocks.clearBit(blockBit); + + if (blocksLeftForPiece(piece) == 0) { + // Ask the file manager to verify the newly downloaded piece + d->fileManager.verifyPiece(piece->index); + + // Remove this piece from all payloads + QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.begin(); + while (it != d->payloads.end()) { + if (!it.value() || it.value()->index == piece->index) + it = d->payloads.erase(it); + else + ++it; + } + } + + // Fill up the pipeline. + requestMore(client); +} + +void TorrentClient::peerWireBytesWritten(qint64 size) +{ + if (!d->transferRateTimer) + d->transferRateTimer = startTimer(RateControlTimerDelay); + + d->uploadRate[0] += size; + d->uploadedBytes += size; + emit dataSent(size); +} + +void TorrentClient::peerWireBytesReceived(qint64 size) +{ + if (!d->transferRateTimer) + d->transferRateTimer = startTimer(RateControlTimerDelay); + + d->downloadRate[0] += size; + d->downloadedBytes += size; + emit dataSent(size); +} + +int TorrentClient::blocksLeftForPiece(const TorrentPiece *piece) const +{ + int blocksLeft = 0; + int completedBlocksSize = piece->completedBlocks.size(); + for (int i = 0; i < completedBlocksSize; ++i) { + if (!piece->completedBlocks.testBit(i)) + ++blocksLeft; + } + return blocksLeft; +} + +void TorrentClient::scheduleUploads() +{ + // Generate a list of clients sorted by their transfer + // speeds. When leeching, we sort by download speed, and when + // seeding, we sort by upload speed. Seeds are left out; there's + // no use in unchoking them. + QList<PeerWireClient *> allClients = d->connections; + QMultiMap<int, PeerWireClient *> transferSpeeds; + foreach (PeerWireClient *client, allClients) { + if (client->state() == QAbstractSocket::ConnectedState + && client->availablePieces().count(true) != d->pieceCount) { + if (d->state == Seeding) { + transferSpeeds.insert(client->uploadSpeed(), client); + } else { + transferSpeeds.insert(client->downloadSpeed(), client); + } + } + } + + // Unchoke the top 'MaxUploads' downloaders (peers that we are + // uploading to) and choke all others. + int maxUploaders = MaxUploads; + QMapIterator<int, PeerWireClient *> it(transferSpeeds); + it.toBack(); + while (it.hasPrevious()) { + PeerWireClient *client = it.previous().value(); + bool interested = (client->peerWireState() & PeerWireClient::PeerIsInterested); + + if (maxUploaders) { + allClients.removeAll(client); + if (client->peerWireState() & PeerWireClient::ChokingPeer) + client->unchokePeer(); + --maxUploaders; + continue; + } + + if ((client->peerWireState() & PeerWireClient::ChokingPeer) == 0) { + if ((qrand() % 10) == 0) + client->abort(); + else + client->chokePeer(); + allClients.removeAll(client); + } + if (!interested) + allClients.removeAll(client); + } + + // Only interested peers are left in allClients. Unchoke one + // random peer to allow it to compete for a position among the + // downloaders. (This is known as an "optimistic unchoke".) + if (!allClients.isEmpty()) { + PeerWireClient *client = allClients[qrand() % allClients.size()]; + if (client->peerWireState() & PeerWireClient::ChokingPeer) + client->unchokePeer(); + } +} + +void TorrentClient::scheduleDownloads() +{ + d->schedulerCalled = false; + + if (d->state == Stopping || d->state == Paused || d->state == Idle) + return; + + // Check what each client is doing, and assign payloads to those + // who are either idle or done. + foreach (PeerWireClient *client, d->connections) + schedulePieceForClient(client); +} + +void TorrentClient::schedulePieceForClient(PeerWireClient *client) +{ + // Only schedule connected clients. + if (client->state() != QTcpSocket::ConnectedState) + return; + + // The peer has choked us; try again later. + if (client->peerWireState() & PeerWireClient::ChokedByPeer) + return; + + // Make a list of all the client's pending pieces, and count how + // many blocks have been requested. + QList<int> currentPieces; + bool somePiecesAreNotInProgress = false; + TorrentPiece *lastPendingPiece = 0; + QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(client); + while (it != d->payloads.end() && it.key() == client) { + lastPendingPiece = it.value(); + if (lastPendingPiece->inProgress) { + currentPieces << lastPendingPiece->index; + } else { + somePiecesAreNotInProgress = true; + } + ++it; + } + + // Skip clients that already have too many blocks in progress. + if (client->incomingBlocks().size() >= ((d->state == Endgame || d->state == WarmingUp) + ? MaxBlocksInMultiMode : MaxBlocksInProgress)) + return; + + // If all pieces are in progress, but we haven't filled up our + // block requesting quota, then we need to schedule another piece. + if (!somePiecesAreNotInProgress || client->incomingBlocks().size() > 0) + lastPendingPiece = 0; + TorrentPiece *piece = lastPendingPiece; + + // In warmup state, all clients request blocks from the same pieces. + if (d->state == WarmingUp && d->pendingPieces.size() >= 4) { + piece = d->payloads.value(client); + if (!piece) { + QList<TorrentPiece *> values = d->pendingPieces.values(); + piece = values.value(qrand() % values.size()); + piece->inProgress = true; + d->payloads.insert(client, piece); + } + if (piece->completedBlocks.count(false) == client->incomingBlocks().size()) + return; + } + + // If no pieces are currently in progress, schedule a new one. + if (!piece) { + // Build up a list of what pieces that we have not completed + // are available to this client. + QBitArray incompletePiecesAvailableToClient = d->incompletePieces; + + // Remove all pieces that are marked as being in progress + // already (i.e., pieces that this or other clients are + // already waiting for). A special rule applies to warmup and + // endgame mode; there, we allow several clients to request + // the same piece. In endgame mode, this only applies to + // clients that are currently uploading (more than 1.0KB/s). + if ((d->state == Endgame && client->uploadSpeed() < 1024) || d->state != WarmingUp) { + QMap<int, TorrentPiece *>::ConstIterator it = d->pendingPieces.constBegin(); + while (it != d->pendingPieces.constEnd()) { + if (it.value()->inProgress) + incompletePiecesAvailableToClient.clearBit(it.key()); + ++it; + } + } + + // Remove all pieces that the client cannot download. + incompletePiecesAvailableToClient &= client->availablePieces(); + + // Remove all pieces that this client has already requested. + foreach (int i, currentPieces) + incompletePiecesAvailableToClient.clearBit(i); + + // Only continue if more pieces can be scheduled. If no pieces + // are available and no blocks are in progress, just leave + // the connection idle; it might become interesting later. + if (incompletePiecesAvailableToClient.count(true) == 0) + return; + + // Check if any of the partially completed pieces can be + // recovered, and if so, pick a random one of them. + QList<TorrentPiece *> partialPieces; + QMap<int, TorrentPiece *>::ConstIterator it = d->pendingPieces.constBegin(); + while (it != d->pendingPieces.constEnd()) { + TorrentPiece *tmp = it.value(); + if (incompletePiecesAvailableToClient.testBit(it.key())) { + if (!tmp->inProgress || d->state == WarmingUp || d->state == Endgame) { + partialPieces << tmp; + break; + } + } + ++it; + } + if (!partialPieces.isEmpty()) + piece = partialPieces.value(qrand() % partialPieces.size()); + + if (!piece) { + // Pick a random piece 3 out of 4 times; otherwise, pick either + // one of the most common or the least common pieces available, + // depending on the state we're in. + int pieceIndex = 0; + if (d->state == WarmingUp || (qrand() & 4) == 0) { + int *occurrances = new int[d->pieceCount]; + memset(occurrances, 0, d->pieceCount * sizeof(int)); + + // Count how many of each piece are available. + foreach (PeerWireClient *peer, d->connections) { + QBitArray peerPieces = peer->availablePieces(); + int peerPiecesSize = peerPieces.size(); + for (int i = 0; i < peerPiecesSize; ++i) { + if (peerPieces.testBit(i)) + ++occurrances[i]; + } + } + + // Find the rarest or most common pieces. + int numOccurrances = d->state == WarmingUp ? 0 : 99999; + QList<int> piecesReadyForDownload; + for (int i = 0; i < d->pieceCount; ++i) { + if (d->state == WarmingUp) { + // Add common pieces + if (occurrances[i] >= numOccurrances + && incompletePiecesAvailableToClient.testBit(i)) { + if (occurrances[i] > numOccurrances) + piecesReadyForDownload.clear(); + piecesReadyForDownload.append(i); + numOccurrances = occurrances[i]; + } + } else { + // Add rare pieces + if (occurrances[i] <= numOccurrances + && incompletePiecesAvailableToClient.testBit(i)) { + if (occurrances[i] < numOccurrances) + piecesReadyForDownload.clear(); + piecesReadyForDownload.append(i); + numOccurrances = occurrances[i]; + } + } + } + + // Select one piece randomly + pieceIndex = piecesReadyForDownload.at(qrand() % piecesReadyForDownload.size()); + delete [] occurrances; + } else { + // Make up a list of available piece indices, and pick + // a random one. + QList<int> values; + int incompletePiecesAvailableToClientSize = incompletePiecesAvailableToClient.size(); + for (int i = 0; i < incompletePiecesAvailableToClientSize; ++i) { + if (incompletePiecesAvailableToClient.testBit(i)) + values << i; + } + pieceIndex = values.at(qrand() % values.size()); + } + + // Create a new TorrentPiece and fill in all initial + // properties. + piece = new TorrentPiece; + piece->index = pieceIndex; + piece->length = d->fileManager.pieceLengthAt(pieceIndex); + int numBlocks = piece->length / BlockSize; + if (piece->length % BlockSize) + ++numBlocks; + piece->completedBlocks.resize(numBlocks); + piece->requestedBlocks.resize(numBlocks); + d->pendingPieces.insert(pieceIndex, piece); + } + + piece->inProgress = true; + d->payloads.insert(client, piece); + } + + // Request more blocks from all pending pieces. + requestMore(client); +} + +void TorrentClient::requestMore(PeerWireClient *client) +{ + // Make a list of all pieces this client is currently waiting for, + // and count the number of blocks in progress. + QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(client); + int numBlocksInProgress = client->incomingBlocks().size(); + QList<TorrentPiece *> piecesInProgress; + while (it != d->payloads.end() && it.key() == client) { + TorrentPiece *piece = it.value(); + if (piece->inProgress || (d->state == WarmingUp || d->state == Endgame)) + piecesInProgress << piece; + ++it; + } + + // If no pieces are in progress, call the scheduler. + if (piecesInProgress.isEmpty() && d->incompletePieces.count(true)) { + d->callScheduler(); + return; + } + + // If too many pieces are in progress, there's nothing to do. + int maxInProgress = ((d->state == Endgame || d->state == WarmingUp) + ? MaxBlocksInMultiMode : MaxBlocksInProgress); + if (numBlocksInProgress == maxInProgress) + return; + + // Starting with the first piece that we're waiting for, request + // blocks until the quota is filled up. + foreach (TorrentPiece *piece, piecesInProgress) { + numBlocksInProgress += requestBlocks(client, piece, maxInProgress - numBlocksInProgress); + if (numBlocksInProgress == maxInProgress) + break; + } + + // If we still didn't fill up the quota, we need to schedule more + // pieces. + if (numBlocksInProgress < maxInProgress && d->state != WarmingUp) + d->callScheduler(); +} + +int TorrentClient::requestBlocks(PeerWireClient *client, TorrentPiece *piece, int maxBlocks) +{ + // Generate the list of incomplete blocks for this piece. + QVector<int> bits; + int completedBlocksSize = piece->completedBlocks.size(); + for (int i = 0; i < completedBlocksSize; ++i) { + if (!piece->completedBlocks.testBit(i) && !piece->requestedBlocks.testBit(i)) + bits << i; + } + + // Nothing more to request. + if (bits.size() == 0) { + if (d->state != WarmingUp && d->state != Endgame) + return 0; + bits.clear(); + for (int i = 0; i < completedBlocksSize; ++i) { + if (!piece->completedBlocks.testBit(i)) + bits << i; + } + } + + if (d->state == WarmingUp || d->state == Endgame) { + // By randomizing the list of blocks to request, we + // significantly speed up the warmup and endgame modes, where + // the same blocks are requested from multiple peers. The + // speedup comes from an increased chance of receiving + // different blocks from the different peers. + for (int i = 0; i < bits.size(); ++i) { + int a = qrand() % bits.size(); + int b = qrand() % bits.size(); + int tmp = bits[a]; + bits[a] = bits[b]; + bits[b] = tmp; + } + } + + // Request no more blocks than we've been asked to. + int blocksToRequest = qMin(maxBlocks, bits.size()); + + // Calculate the offset and size of each block, and send requests. + for (int i = 0; i < blocksToRequest; ++i) { + int blockSize = BlockSize; + if ((piece->length % BlockSize) && bits.at(i) == completedBlocksSize - 1) + blockSize = piece->length % BlockSize; + client->requestBlock(piece->index, bits.at(i) * BlockSize, blockSize); + piece->requestedBlocks.setBit(bits.at(i)); + } + + return blocksToRequest; +} + +void TorrentClient::peerChoked() +{ + PeerWireClient *client = qobject_cast<PeerWireClient *>(sender()); + if (!client) + return; + + // When the peer chokes us, we immediately forget about all blocks + // we've requested from it. We also remove the piece from out + // payload, making it available to other clients. + QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(client); + while (it != d->payloads.end() && it.key() == client) { + it.value()->inProgress = false; + it.value()->requestedBlocks.fill(false); + it = d->payloads.erase(it); + } +} + +void TorrentClient::peerUnchoked() +{ + PeerWireClient *client = qobject_cast<PeerWireClient *>(sender()); + if (!client) + return; + + // We got unchoked, which means we can request more blocks. + if (d->state != Seeding) + d->callScheduler(); +} + +void TorrentClient::addToPeerList(const QList<TorrentPeer> &peerList) +{ + // Add peers we don't already know of to our list of peers. + QList<QHostAddress> addresses = QNetworkInterface::allAddresses(); + foreach (TorrentPeer peer, peerList) { + if (addresses.contains(peer.address) + && peer.port == TorrentServer::instance()->serverPort()) { + // Skip our own server. + continue; + } + + bool known = false; + foreach (TorrentPeer *knownPeer, d->peers) { + if (knownPeer->port == peer.port + && knownPeer->address == peer.address) { + known = true; + break; + } + } + if (!known) { + TorrentPeer *newPeer = new TorrentPeer; + *newPeer = peer; + newPeer->interesting = true; + newPeer->seed = false; + newPeer->lastVisited = 0; + newPeer->connectStart = 0; + newPeer->connectTime = 999999; + newPeer->pieces.resize(d->pieceCount); + newPeer->numCompletedPieces = 0; + d->peers << newPeer; + } + } + + // If we've got more peers than we can connect to, we remove some + // of the peers that have no (or low) activity. + int maxPeers = ConnectionManager::instance()->maxConnections() * 3; + if (d->peers.size() > maxPeers) { + // Find what peers are currently connected & active + QSet<TorrentPeer *> activePeers; + foreach (TorrentPeer *peer, d->peers) { + foreach (PeerWireClient *client, d->connections) { + if (client->peer() == peer && (client->downloadSpeed() + client->uploadSpeed()) > 1024) + activePeers << peer; + } + } + + // Remove inactive peers from the peer list until we're below + // the max connections count. + QList<int> toRemove; + for (int i = 0; i < d->peers.size() && (d->peers.size() - toRemove.size()) > maxPeers; ++i) { + if (!activePeers.contains(d->peers.at(i))) + toRemove << i; + } + QListIterator<int> toRemoveIterator(toRemove); + toRemoveIterator.toBack(); + while (toRemoveIterator.hasPrevious()) + d->peers.removeAt(toRemoveIterator.previous()); + + // If we still have too many peers, remove the oldest ones. + while (d->peers.size() > maxPeers) + d->peers.takeFirst(); + } + + if (d->state != Paused && d->state != Stopping && d->state != Idle) { + if (d->state == Searching || d->state == WarmingUp) + connectToPeers(); + else + d->callPeerConnector(); + } +} + +void TorrentClient::trackerStopped() +{ + d->setState(Idle); + emit stopped(); +} + +void TorrentClient::updateProgress(int progress) +{ + if (progress == -1 && d->pieceCount > 0) { + int newProgress = (d->completedPieces.count(true) * 100) / d->pieceCount; + if (d->lastProgressValue != newProgress) { + d->lastProgressValue = newProgress; + emit progressUpdated(newProgress); + } + } else if (d->lastProgressValue != progress) { + d->lastProgressValue = progress; + emit progressUpdated(progress); + } +} diff --git a/examples/network/torrent/torrentclient.h b/examples/network/torrent/torrentclient.h new file mode 100644 index 0000000..6caaf38 --- /dev/null +++ b/examples/network/torrent/torrentclient.h @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TORRENTCLIENT_H +#define TORRENTCLIENT_H + +#include <QBitArray> +#include <QHostAddress> +#include <QList> + +class MetaInfo; +class PeerWireClient; +class TorrentClientPrivate; +class TorrentPeer; +class TorrentPiece; +QT_BEGIN_NAMESPACE +class QTimerEvent; +QT_END_NAMESPACE + +class TorrentPeer { +public: + QHostAddress address; + quint16 port; + QString id; + bool interesting; + bool seed; + uint lastVisited; + uint connectStart; + uint connectTime; + QBitArray pieces; + int numCompletedPieces; + + inline bool operator==(const TorrentPeer &other) + { + return port == other.port + && address == other.address + && id == other.id; + } +}; + +class TorrentClient : public QObject +{ + Q_OBJECT + +public: + enum State { + Idle, + Paused, + Stopping, + Preparing, + Searching, + Connecting, + WarmingUp, + Downloading, + Endgame, + Seeding + }; + enum Error { + UnknownError, + TorrentParseError, + InvalidTrackerError, + FileError, + ServerError + }; + + TorrentClient(QObject *parent = 0); + ~TorrentClient(); + + bool setTorrent(const QString &fileName); + bool setTorrent(const QByteArray &torrentData); + MetaInfo metaInfo() const; + + void setMaxConnections(int connections); + int maxConnections() const; + + void setDestinationFolder(const QString &directory); + QString destinationFolder() const; + + void setDumpedState(const QByteArray &dumpedState); + QByteArray dumpedState() const; + + // Progress and stats for download feedback. + qint64 progress() const; + void setDownloadedBytes(qint64 bytes); + qint64 downloadedBytes() const; + void setUploadedBytes(qint64 bytes); + qint64 uploadedBytes() const; + int connectedPeerCount() const; + int seedCount() const; + + // Accessors for the tracker + QByteArray peerId() const; + QByteArray infoHash() const; + quint16 serverPort() const; + + // State and error. + State state() const; + QString stateString() const; + Error error() const; + QString errorString() const; + +signals: + void stateChanged(TorrentClient::State state); + void error(TorrentClient::Error error); + + void downloadCompleted(); + void peerInfoUpdated(); + + void dataSent(int uploadedBytes); + void dataReceived(int downloadedBytes); + void progressUpdated(int percentProgress); + void downloadRateUpdated(int bytesPerSecond); + void uploadRateUpdated(int bytesPerSecond); + + void stopped(); + +public slots: + void start(); + void stop(); + void setPaused(bool paused); + void setupIncomingConnection(PeerWireClient *client); + +protected slots: + void timerEvent(QTimerEvent *event); + +private slots: + // File management + void sendToPeer(int readId, int pieceIndex, int begin, const QByteArray &data); + void fullVerificationDone(); + void pieceVerified(int pieceIndex, bool ok); + void handleFileError(); + + // Connection handling + void connectToPeers(); + QList<TorrentPeer *> weighedFreePeers() const; + void setupOutgoingConnection(); + void initializeConnection(PeerWireClient *client); + void removeClient(); + void peerPiecesAvailable(const QBitArray &pieces); + void peerRequestsBlock(int pieceIndex, int begin, int length); + void blockReceived(int pieceIndex, int begin, const QByteArray &data); + void peerWireBytesWritten(qint64 bytes); + void peerWireBytesReceived(qint64 bytes); + int blocksLeftForPiece(const TorrentPiece *piece) const; + + // Scheduling + void scheduleUploads(); + void scheduleDownloads(); + void schedulePieceForClient(PeerWireClient *client); + void requestMore(PeerWireClient *client); + int requestBlocks(PeerWireClient *client, TorrentPiece *piece, int maxBlocks); + void peerChoked(); + void peerUnchoked(); + + // Tracker handling + void addToPeerList(const QList<TorrentPeer> &peerList); + void trackerStopped(); + + // Progress + void updateProgress(int progress = -1); + +private: + TorrentClientPrivate *d; + friend class TorrentClientPrivate; +}; + +#endif diff --git a/examples/network/torrent/torrentserver.cpp b/examples/network/torrent/torrentserver.cpp new file mode 100644 index 0000000..3087d6a --- /dev/null +++ b/examples/network/torrent/torrentserver.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "connectionmanager.h" +#include "peerwireclient.h" +#include "ratecontroller.h" +#include "torrentclient.h" +#include "torrentserver.h" + +Q_GLOBAL_STATIC(TorrentServer, torrentServer) + +TorrentServer *TorrentServer::instance() +{ + return torrentServer(); +} + +void TorrentServer::addClient(TorrentClient *client) +{ + clients << client; +} + +void TorrentServer::removeClient(TorrentClient *client) +{ + clients.removeAll(client); +} + +void TorrentServer::incomingConnection(int socketDescriptor) +{ + PeerWireClient *client = + new PeerWireClient(ConnectionManager::instance()->clientId(), this); + + if (client->setSocketDescriptor(socketDescriptor)) { + if (ConnectionManager::instance()->canAddConnection() && !clients.isEmpty()) { + connect(client, SIGNAL(infoHashReceived(const QByteArray &)), + this, SLOT(processInfoHash(const QByteArray &))); + connect(client, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(removeClient())); + RateController::instance()->addSocket(client); + ConnectionManager::instance()->addConnection(client); + return; + } + } + client->abort(); + delete client; +} + +void TorrentServer::removeClient() +{ + PeerWireClient *peer = qobject_cast<PeerWireClient *>(sender()); + RateController::instance()->removeSocket(peer); + ConnectionManager::instance()->removeConnection(peer); + peer->deleteLater(); +} + +void TorrentServer::processInfoHash(const QByteArray &infoHash) +{ + PeerWireClient *peer = qobject_cast<PeerWireClient *>(sender()); + foreach (TorrentClient *client, clients) { + if (client->state() >= TorrentClient::Searching && client->infoHash() == infoHash) { + peer->disconnect(peer, 0, this, 0); + client->setupIncomingConnection(peer); + return; + } + } + removeClient(); +} diff --git a/examples/network/torrent/torrentserver.h b/examples/network/torrent/torrentserver.h new file mode 100644 index 0000000..6ae89e1 --- /dev/null +++ b/examples/network/torrent/torrentserver.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TORRENTSERVER_H +#define TORRENTSERVER_H + +#include <QList> +#include <QTcpServer> + +class TorrentClient; + +class TorrentServer : public QTcpServer +{ + Q_OBJECT + +public: + inline TorrentServer() {} + static TorrentServer *instance(); + + void addClient(TorrentClient *client); + void removeClient(TorrentClient *client); + +protected: + void incomingConnection(int socketDescriptor); + +private slots: + void removeClient(); + void processInfoHash(const QByteArray &infoHash); + +private: + QList<TorrentClient *> clients; +}; + +#endif diff --git a/examples/network/torrent/trackerclient.cpp b/examples/network/torrent/trackerclient.cpp new file mode 100644 index 0000000..bda8140 --- /dev/null +++ b/examples/network/torrent/trackerclient.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bencodeparser.h" +#include "connectionmanager.h" +#include "torrentclient.h" +#include "torrentserver.h" +#include "trackerclient.h" + +#include <QtCore> + +TrackerClient::TrackerClient(TorrentClient *downloader, QObject *parent) + : QObject(parent), torrentDownloader(downloader) +{ + length = 0; + requestInterval = 5 * 60; + requestIntervalTimer = -1; + firstTrackerRequest = true; + lastTrackerRequest = false; + firstSeeding = true; + + connect(&http, SIGNAL(done(bool)), this, SLOT(httpRequestDone(bool))); +} + +void TrackerClient::start(const MetaInfo &info) +{ + metaInfo = info; + QTimer::singleShot(0, this, SLOT(fetchPeerList())); + + if (metaInfo.fileForm() == MetaInfo::SingleFileForm) { + length = metaInfo.singleFile().length; + } else { + QList<MetaInfoMultiFile> files = metaInfo.multiFiles(); + for (int i = 0; i < files.size(); ++i) + length += files.at(i).length; + } +} + +void TrackerClient::startSeeding() +{ + firstSeeding = true; + fetchPeerList(); +} + +void TrackerClient::stop() +{ + lastTrackerRequest = true; + http.abort(); + fetchPeerList(); +} + +void TrackerClient::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == requestIntervalTimer) { + if (http.state() == QHttp::Unconnected) + fetchPeerList(); + } else { + QObject::timerEvent(event); + } +} + +void TrackerClient::fetchPeerList() +{ + // Prepare connection details + QString fullUrl = metaInfo.announceUrl(); + QUrl url(fullUrl); + QString passkey = "?"; + if (fullUrl.contains("?passkey")) { + passkey = metaInfo.announceUrl().mid(fullUrl.indexOf("?passkey"), -1); + passkey += "&"; + } + + // Percent encode the hash + QByteArray infoHash = torrentDownloader->infoHash(); + QString encodedSum; + for (int i = 0; i < infoHash.size(); ++i) { + encodedSum += '%'; + encodedSum += QString::number(infoHash[i], 16).right(2).rightJustified(2, '0'); + } + + bool seeding = (torrentDownloader->state() == TorrentClient::Seeding); + QByteArray query; + query += url.path().toLatin1(); + query += passkey; + query += "info_hash=" + encodedSum; + query += "&peer_id=" + ConnectionManager::instance()->clientId(); + query += "&port=" + QByteArray::number(TorrentServer::instance()->serverPort()); + query += "&compact=1"; + query += "&uploaded=" + QByteArray::number(torrentDownloader->uploadedBytes()); + + if (!firstSeeding) { + query += "&downloaded=0"; + query += "&left=0"; + } else { + query += "&downloaded=" + QByteArray::number( + torrentDownloader->downloadedBytes()); + int left = qMax<int>(0, metaInfo.totalSize() - torrentDownloader->downloadedBytes()); + query += "&left=" + QByteArray::number(seeding ? 0 : left); + } + + if (seeding && firstSeeding) { + query += "&event=completed"; + firstSeeding = false; + } else if (firstTrackerRequest) { + firstTrackerRequest = false; + query += "&event=started"; + } else if(lastTrackerRequest) { + query += "&event=stopped"; + } + + if (!trackerId.isEmpty()) + query += "&trackerid=" + trackerId; + + http.setHost(url.host(), url.port() == -1 ? 80 : url.port()); + if (!url.userName().isEmpty()) + http.setUser(url.userName(), url.password()); + http.get(query); +} + +void TrackerClient::httpRequestDone(bool error) +{ + if (lastTrackerRequest) { + emit stopped(); + return; + } + + if (error) { + emit connectionError(http.error()); + return; + } + + QByteArray response = http.readAll(); + http.abort(); + + BencodeParser parser; + if (!parser.parse(response)) { + qWarning("Error parsing bencode response from tracker: %s", + qPrintable(parser.errorString())); + http.abort(); + return; + } + + QMap<QByteArray, QVariant> dict = parser.dictionary(); + + if (dict.contains("failure reason")) { + // no other items are present + emit failure(QString::fromUtf8(dict.value("failure reason").toByteArray())); + return; + } + + if (dict.contains("warning message")) { + // continue processing + emit warning(QString::fromUtf8(dict.value("warning message").toByteArray())); + } + + if (dict.contains("tracker id")) { + // store it + trackerId = dict.value("tracker id").toByteArray(); + } + + if (dict.contains("interval")) { + // Mandatory item + if (requestIntervalTimer != -1) + killTimer(requestIntervalTimer); + requestIntervalTimer = startTimer(dict.value("interval").toInt() * 1000); + } + + if (dict.contains("peers")) { + // store it + peers.clear(); + QVariant peerEntry = dict.value("peers"); + if (peerEntry.type() == QVariant::List) { + QList<QVariant> peerTmp = peerEntry.toList(); + for (int i = 0; i < peerTmp.size(); ++i) { + TorrentPeer tmp; + QMap<QByteArray, QVariant> peer = qVariantValue<QMap<QByteArray, QVariant> >(peerTmp.at(i)); + tmp.id = QString::fromUtf8(peer.value("peer id").toByteArray()); + tmp.address.setAddress(QString::fromUtf8(peer.value("ip").toByteArray())); + tmp.port = peer.value("port").toInt(); + peers << tmp; + } + } else { + QByteArray peerTmp = peerEntry.toByteArray(); + for (int i = 0; i < peerTmp.size(); i += 6) { + TorrentPeer tmp; + uchar *data = (uchar *)peerTmp.constData() + i; + tmp.port = (int(data[4]) << 8) + data[5]; + uint ipAddress = 0; + ipAddress += uint(data[0]) << 24; + ipAddress += uint(data[1]) << 16; + ipAddress += uint(data[2]) << 8; + ipAddress += uint(data[3]); + tmp.address.setAddress(ipAddress); + peers << tmp; + } + } + emit peerListUpdated(peers); + } +} diff --git a/examples/network/torrent/trackerclient.h b/examples/network/torrent/trackerclient.h new file mode 100644 index 0000000..f1ce37db --- /dev/null +++ b/examples/network/torrent/trackerclient.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TRACKERCLIENT_H +#define TRACKERCLIENT_H + +#include <QByteArray> +#include <QList> +#include <QObject> +#include <QHostAddress> +#include <QHttp> + +#include "metainfo.h" +#include "torrentclient.h" + +class TorrentClient; + +class TrackerClient : public QObject +{ + Q_OBJECT + +public: + TrackerClient(TorrentClient *downloader, QObject *parent = 0); + + void start(const MetaInfo &info); + void stop(); + void startSeeding(); + +signals: + void connectionError(QHttp::Error error); + + void failure(const QString &reason); + void warning(const QString &message); + void peerListUpdated(const QList<TorrentPeer> &peerList); + + void uploadCountUpdated(qint64 newUploadCount); + void downloadCountUpdated(qint64 newDownloadCount); + + void stopped(); + +protected: + void timerEvent(QTimerEvent *event); + +private slots: + void fetchPeerList(); + void httpRequestDone(bool error); + +private: + TorrentClient *torrentDownloader; + + int requestInterval; + int requestIntervalTimer; + QHttp http; + MetaInfo metaInfo; + QByteArray trackerId; + QList<TorrentPeer> peers; + qint64 uploadedBytes; + qint64 downloadedBytes; + qint64 length; + + bool firstTrackerRequest; + bool lastTrackerRequest; + bool firstSeeding; +}; + +#endif |