From a19e9744751d4278f56cb3b6ff885c0068f03a3d Mon Sep 17 00:00:00 2001 From: Alex Borisov Date: Sun, 27 Nov 2011 12:31:03 +0200 Subject: File downloading (no MD5 hash check yet). Various fixes --- client/Config.cpp | 33 +++++++++++--- client/Config.h | 28 +++++++----- client/DownloadClient.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++++++ client/DownloadClient.h | 95 +++++++++++++++++++++++++++++++++++++++ client/FileOpThread.cpp | 63 ++++++++++++++++++++++++++ client/FileOpThread.h | 25 +++++++++++ client/ProxyClientApp.cpp | 8 ++-- client/ProxyClientApp.h | 3 ++ client/SslClient.cpp | 31 ++++++++++++- client/SslClient.h | 19 ++++++-- client/UpdatedConfig.cpp | 12 +++++ client/UpdatedConfig.h | 5 +++ client/client.pro | 8 +++- 13 files changed, 411 insertions(+), 29 deletions(-) create mode 100644 client/DownloadClient.cpp create mode 100644 client/DownloadClient.h create mode 100644 client/FileOpThread.cpp create mode 100644 client/FileOpThread.h (limited to 'client') diff --git a/client/Config.cpp b/client/Config.cpp index ba62ee0..21b2c44 100644 --- a/client/Config.cpp +++ b/client/Config.cpp @@ -44,10 +44,11 @@ void Config::FirewallEntry::Parse(string entry) /* * FileEntry nested class section */ -void Config::FileEntry::Parse(string entry, ActionType action) +void Config::FileEntry::Parse(string entry, ActionType _action) { Logger::Trace("Parsing file action %s\n", entry.c_str()); + action = _action; size_t start = 0, end = 0; if (action == DeleteAction) { @@ -66,8 +67,6 @@ void Config::FileEntry::Parse(string entry, ActionType action) { Logger::Error("Unknown FileEntry action!\n"); } - - Logger::Debug("path: %s, md5: %s\n", path.c_str(), md5.c_str()); } @@ -238,6 +237,30 @@ unsigned Config::GetStaticProxyGuiLines() return maxLine; } +vector Config::GetDeleteList() +{ + vector delLst; + for (unsigned i = 0; i < fileActions.size(); i++) + { + if (fileActions[i].action == FileEntry::DeleteAction) + delLst.push_back(fileActions[i]); + } + + return delLst; +} + +vector Config::GetDownloadList() +{ + vector downloadLst; + for (unsigned i = 0; i < fileActions.size(); i++) + { + if (fileActions[i].action == FileEntry::DownloadAction) + downloadLst.push_back(fileActions[i]); + } + + return downloadLst; +} + void Config::ParseConfig(string data) { @@ -366,9 +389,7 @@ void Config::ParseFirewalls(string data) } void Config::ParseDeleteList(string data) -{ - Logger::Debug("Delete list:\n%s", data.c_str()); - +{ //delete all entries with DeleteAction for (unsigned i = 0; i < fileActions.size(); i++) { diff --git a/client/Config.h b/client/Config.h index 2392898..466f93a 100644 --- a/client/Config.h +++ b/client/Config.h @@ -86,7 +86,7 @@ public: /** * @brief Extract and set instance variables from config line */ - void Parse(string entry, ActionType action); + void Parse(string entry, ActionType _action); }; /** @@ -126,14 +126,6 @@ public: */ void AcquireConfig(); - /** - * @brief Check whether current confguration is valid
- * It may become invalid in case if server is not reachable. - * So every time you want to access Config members or methods you should check this value - * @return true if configuration is valid and false otherwise - */ - bool IsConfigValid(); - /** * @brief Get list of country names where at least one proxy available
* (generic proxy records only) @@ -200,9 +192,21 @@ public: unsigned GetStaticProxyGuiLines(); /** - * Static proxy speed value limit.
- * This value is used to determine speed label color. - * All speed value below this constant will be red, higher values will be green + * @brief Get list of files to be deleted + * @return List of FileEntry wrapper object that describe files that should be deleted + */ + vector GetDeleteList(); + + /** + * @brief Get list of files to be downloaded + * @return List of FileEntry wrapper object that describe files that should be downloaded + */ + vector GetDownloadList(); + + /** + * @brief Static proxy speed value limit.
+ * This value is used to determine speed label color. + * All speed value below this constant will be red, higher values will be green */ const unsigned StaticProxySpeedLow; protected: diff --git a/client/DownloadClient.cpp b/client/DownloadClient.cpp new file mode 100644 index 0000000..df4da25 --- /dev/null +++ b/client/DownloadClient.cpp @@ -0,0 +1,110 @@ + +#include +#include "DownloadClient.h" + + +DownloadClient::DownloadClient(): isDownloading(false), current(NULL), currentId(-1) +{ + connect(this, SIGNAL(ReplyRecieved(SslClient::RequestType&, QByteArray&)), + this, SLOT(FileDataRecieved(SslClient::RequestType&, QByteArray&))); +} + +DownloadClient::DownloadClient(QString addr): SslClient(addr), isDownloading(false), current(NULL), currentId(-1) +{ + DownloadClient(); +} + +void DownloadClient::Download(const string &filename) +{ + pair file(filename, 0); + vector > fileList; + fileList.push_back(file); + Download(fileList); +} + +void DownloadClient::Download(const pair &file) +{ + vector > fileList; + fileList.push_back(file); + Download(fileList); +} + +void DownloadClient::Download(const vector > &fileList) +{ + if (server.isEmpty()) + { + Logger::Error("Server address isn't set! Aborting.\n"); + return; + } + if (isDownloading) + { + Logger::Error("Previous downloading process wasn't finished! Aborting.\n"); + return; + } + isDownloading = true; + files = fileList; + DoDownload(); +} + +void DownloadClient::FileDataRecieved(SslClient::RequestType &type, QByteArray &data) +{ + if (type != RegularFile) + { + Logger::Error("Invalid reply recieved: %x\n", type); + return; + } + + if ((current != NULL) && (current->isOpen())) + { + Logger::Error("File is not open or invalid state\n"); + return; + } + + pair file = files[currentId]; + string filename = file.first; + if (data.size() == FRAGMENT_SIZE) + { + Logger::Debug("data:\n %s", data.constData()); + current->write(data); + SendFileRequest(filename); + } + else + { + if (data.size() != 0) + { + Logger::Debug("data:\n %s", data.constData()); + current->write(data); + } + current->close(); + delete current; + current = NULL; + + /** + * @todo md5 hash check + */ + + DoDownload(); + } +} + +void DownloadClient::DoDownload() +{ + if (currentId < (int)files.size()) + { + currentId++; + pair file = files[currentId]; + QString filename = QString::fromLocal8Bit(file.first.c_str()); + string md5 = file.second; + current = new QFile(filename); + current->open(QIODevice::ReadWrite); + + Logger::Info("Downloading file %s\n", file.first.c_str()); + SendFileRequest(file.first); + } + else + { + Logger::Info("All files have been downloaded."); + isDownloading = false; + emit downloadFinished(); + } +} diff --git a/client/DownloadClient.h b/client/DownloadClient.h new file mode 100644 index 0000000..5b75c2c --- /dev/null +++ b/client/DownloadClient.h @@ -0,0 +1,95 @@ + + +#ifndef DOWNLOAD_CLIENT_H +#define DOWNLOAD_CLIENT_H + +#include +#include +#include +#include "SslClient.h" + +using std::pair; +using std::string; +using std::vector; + +class QFile; + +class DownloadClient: public SslClient +{ +Q_OBJECT + +public: + /** + * @brief Initialize new DownloadClient instance + */ + DownloadClient(); + + /** + * @brief Initialize new DownloadClient instance + * @param addr server address or hostname to connect to + */ + DownloadClient(QString addr); + + /** + * @brief Download file from server + * @param file file path on client machine + */ + void Download(const string &file); + + /** + * @brief Download file from server + * @param file pair of file name and it's md5 hash value + */ + void Download(const pair &file); + + /** + * @brief Download list of files + * @param files associative array of file names and it's md5 hashes + * @note this is designated method among all + * @todo It seems that std::map isn't best choice for this purpose + * it's quite unsuitable to iterate through( + */ + void Download(const vector > &files); + +signals: + /** + * @brief This signal is emitted when all files have been downloaded + */ + void downloadFinished(); + +private slots: + void FileDataRecieved(SslClient::RequestType &type, QByteArray &confdata); + +private: + /** + * @brief size of file data fragment + */ + static const int FRAGMENT_SIZE = 4096; + + /** + * @brief indicates that program is downloading file(s) right now + */ + bool isDownloading; + + /** + * @brief name of file that is being downloaded + */ + QFile *current; + + /** + * @brief index of file that is being downloaded + */ + int currentId; + + /** + * @brief List of file to + */ + vector > files; + + /** + * @brief start file-by-file downloading process + */ + void DoDownload(); +}; + +#endif diff --git a/client/FileOpThread.cpp b/client/FileOpThread.cpp new file mode 100644 index 0000000..42cb51b --- /dev/null +++ b/client/FileOpThread.cpp @@ -0,0 +1,63 @@ + +#include +#include "DownloadClient.h" +#include "FileOpThread.h" +#include "UpdatedConfig.h" + +void FileOpThread::run() +{ + UpdatedConfig *cfg = UpdatedConfig::CurrentConfig(); + + /* delete files */ + vector toDelete = cfg->GetDeleteList(); + for (unsigned i = 0; i < toDelete.size(); i++) + { + Logger::Trace("Deleting file: \"%s\"\n", toDelete[i].path.c_str()); + + QString path = QString::fromLocal8Bit(toDelete[i].path.c_str()); + QFile file(path); + QFileInfo fileInfo(file); + if (fileInfo.exists()) + { + file.remove(); + } + else + { + Logger::Info("File \"%s\" has already been deleted\n", toDelete[i].path.c_str()); + } + } + + /* download files */ + vector > files; + vector downloadList = cfg->GetDownloadList(); + for (unsigned i = 0; i < downloadList.size(); i++) + { + QString path = QString::fromLocal8Bit(downloadList[i].path.c_str()); + QFileInfo fileInfo(path); + if (fileInfo.exists()) + { + Logger::Info("File \"%s\" already exists\n", downloadList[i].path.c_str()); + /** + * @todo Compute md5 hash and check if they are the same + */ + } + else + { + pair file = make_pair(downloadList[i].path, downloadList[i].md5); + files.push_back(file); + } + } + + if (files.size() != 0) + { + Logger::Debug("Setting server address to %s\n", cfg->GetServerAddr().c_str()); + + QString addr = QString::fromLocal8Bit(cfg->GetServerAddr().c_str()); + DownloadClient downloadClient(addr); + downloadClient.Download(files); + } + else + { + Logger::Trace("No files to download from server.\n"); + } +} \ No newline at end of file diff --git a/client/FileOpThread.h b/client/FileOpThread.h new file mode 100644 index 0000000..29731c9 --- /dev/null +++ b/client/FileOpThread.h @@ -0,0 +1,25 @@ + + +#ifndef FILE_OP_THREAD_H +#define FILE_OP_THREAD_H + +#include + +/** + * @brief Special thread to process all file operations + */ +class FileOpThread: public QThread +{ +public: + /** + * @brief Initialize new FIleOpThread instance + */ + FileOpThread() {} + + /** + * @brief Thread main loop + */ + void run(); +}; + +#endif \ No newline at end of file diff --git a/client/ProxyClientApp.cpp b/client/ProxyClientApp.cpp index 4b233e5..94d8a7c 100644 --- a/client/ProxyClientApp.cpp +++ b/client/ProxyClientApp.cpp @@ -5,6 +5,7 @@ #include "client.h" #include "Dialog.h" +#include "FileOpThread.h" #include "ProxyClientApp.h" #include "UpdatedConfig.h" @@ -84,6 +85,9 @@ void ProxyClientApp::showProxyDialog() void ProxyClientApp::configUpdated() { + FileOpThread fileOpThread; + fileOpThread.start(QThread::NormalPriority); + UpdatedConfig *cfg = UpdatedConfig::CurrentConfig(); QString msg = QString::fromLocal8Bit(cfg->ConfigLoadedMsg.c_str()); QMessageBox updatedMsg; @@ -92,10 +96,6 @@ void ProxyClientApp::configUpdated() updatedMsg.setStandardButtons(QMessageBox::Ok); updatedMsg.setIcon(QMessageBox::Information); updatedMsg.exec(); - - /* delete files */ - - /* download files */ } void ProxyClientApp::quitApp() diff --git a/client/ProxyClientApp.h b/client/ProxyClientApp.h index 83c7c31..5dd0a69 100644 --- a/client/ProxyClientApp.h +++ b/client/ProxyClientApp.h @@ -10,13 +10,16 @@ class ProxyClientApp: public QApplication { Q_OBJECT + public: ProxyClientApp(int &argc, char *argv[]); + private slots: void trayActivated(QSystemTrayIcon::ActivationReason reason); void showProxyDialog(); void configUpdated(); void quitApp(); + private: QSystemTrayIcon *trayIcon; }; diff --git a/client/SslClient.cpp b/client/SslClient.cpp index b3a2d15..b1841ed 100644 --- a/client/SslClient.cpp +++ b/client/SslClient.cpp @@ -1,4 +1,5 @@ +#include #include #include #include "SslClient.h" @@ -72,7 +73,8 @@ void SslClient::SendRequest(RequestType type) if (sslSocket.state() != QAbstractSocket::ConnectedState) { Logger::Trace("Connecting to server %s:%u\n", server.toLocal8Bit().data(), port); - sslSocket.connectToHostEncrypted(server, port); + //sslSocket.connectToHostEncrypted(server, port); + sslSocket.connectToHostEncrypted("127.0.0.1", port); } else { @@ -100,6 +102,33 @@ void SslClient::SendRequest(RequestType type) sslSocket.write(pkt); } +void SslClient::SendFileRequest(string filename) +{ + Logger::Trace("Sending request w code %x\n", RegularFile); + if (sslSocket.state() != QAbstractSocket::ConnectedState) + { + Logger::Trace("Connecting to server %s:%u\n", server.toLocal8Bit().data(), port); + sslSocket.connectToHostEncrypted(server, port); + } + else + { + Logger::Trace("Already connected to %s:%u\n", server.toLocal8Bit().data(), port); + } + + unsigned char rcode = 0x12; + const char *file = filename.c_str(); + size_t path_len = strlen(file); + char data[path_len + 5]; + data[0] = data[1] = 0x13; + data[2] = rcode; + strncpy(data + 3, file, path_len); + data[path_len - 1] = data[path_len - 2] = 0x14; + + QByteArray pkt(data); + sslSocket.write(pkt); +} + + /* * Signal handlers */ diff --git a/client/SslClient.h b/client/SslClient.h index 8172bad..a6b981c 100644 --- a/client/SslClient.h +++ b/client/SslClient.h @@ -88,13 +88,13 @@ public: }; /** - * @brief setup ssl socket ans it's type, certificates and key
+ * @brief Setup ssl socket ans it's type, certificates and key
* Default server address will be used: 127.0.0.1 */ SslClient(); /** - * @brief setup ssl socket ans it's type, certificates and key + * @brief Setup ssl socket and it's type, certificates and key * @param addr server address or hostname to connect to */ SslClient(QString addr); @@ -106,15 +106,22 @@ public: void SetServerAddr(QString addr); /** - * @brief Request generic proxy list + * @brief Send request to server * @param type type of request to send */ void SendRequest(RequestType type); + /** + * @brief Send file data request (RequestType::RegularFile) + * @param File name on client machine (will be sent to server) + */ + void SendFileRequest(string filename); + /** * @brief disconnect from server */ void Disconnect(); + signals: /** * @brief This signal is emited when data is recieved as a reply to @@ -128,6 +135,7 @@ signals: * @todo emit this signal on all SSL errors too */ void ConnectionError(); + private slots: void Connected(); void Disconnected(); @@ -135,9 +143,12 @@ private slots: void Error(QAbstractSocket::SocketError socketError); void PeerVerifyError(const QSslError &error); void SslErrors(const QList &errors); + +protected: + QString server; + private: QSslSocket sslSocket; - QString server; unsigned short port; }; diff --git a/client/UpdatedConfig.cpp b/client/UpdatedConfig.cpp index 6c69f12..a59bc67 100644 --- a/client/UpdatedConfig.cpp +++ b/client/UpdatedConfig.cpp @@ -12,6 +12,16 @@ UpdatedConfig *UpdatedConfig::CurrentConfig() return self; } +string UpdatedConfig::GetServerAddr() +{ + if (! configValid) + { + Logger::Error("No valid server records present!\n"); + return string(""); + } + return servers[activeSrvIndex].host; +} + UpdatedConfig::UpdatedConfig() { activeSrvIndex = 0; @@ -36,6 +46,7 @@ UpdatedConfig::UpdatedConfig() void UpdatedConfig::update() { Logger::Trace("Going to update configuration\n"); + configValid = false; if (retryFailed) { @@ -147,6 +158,7 @@ end: { Logger::Info("Config successfully updated!\n"); client->Disconnect(); + configValid = true; /* reset retry params and setup timer to fire on next planned update */ time = 0; diff --git a/client/UpdatedConfig.h b/client/UpdatedConfig.h index 5470d4d..2e4deac 100644 --- a/client/UpdatedConfig.h +++ b/client/UpdatedConfig.h @@ -26,6 +26,11 @@ public: */ static UpdatedConfig *CurrentConfig(); + /** + * @brief Get valid server address to communicate with + * @return address of server to communicate with ot empty string + */ + string GetServerAddr(); signals: /** * @brief Signal is emitted when client configuration is updated diff --git a/client/client.pro b/client/client.pro index 16de7f0..6520c5d 100644 --- a/client/client.pro +++ b/client/client.pro @@ -20,7 +20,9 @@ HEADERS += client.h \ Logger.h \ Config.h \ UpdatedConfig.h \ - SslClient.h + SslClient.h \ + FileOpThread.h \ + DownloadClient.h SOURCES += Dialog.cpp \ main.cpp \ @@ -29,7 +31,9 @@ SOURCES += Dialog.cpp \ Logger.cpp \ Config.cpp \ UpdatedConfig.cpp \ - SslClient.cpp + SslClient.cpp \ + FileOpThread.cpp \ + DownloadClient.cpp OTHER_FILES += -- cgit v1.2.3