diff --git a/.gitignore b/.gitignore index 4bdaf615..6fa84a3b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ config.h /src/gui/ui_* src/gui/gui.pro.user src/gui/.qmake.stash +src/gui/.rnd src/setup/win32/synergy.suo diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp index 637b108c..43141dc2 100644 --- a/src/gui/src/PluginManager.cpp +++ b/src/gui/src/PluginManager.cpp @@ -17,6 +17,7 @@ #include "PluginManager.h" +#include "CommandProcess.h" #include "DataDownloader.h" #include "QUtility.h" #include "ProcessorArch.h" @@ -28,6 +29,7 @@ #include static const char kGetPluginDirArg[] = "--get-plugin-dir"; +static const char kGetProfileDirArg[] = "--get-profile-dir"; static QString kPluginsBaseUrl = "http://synergy-project.org/files/plugins/"; static const char kWinProcessorArch32[] = "Windows-x86"; @@ -35,8 +37,17 @@ static const char kWinProcessorArch64[] = "Windows-x64"; static const char kMacProcessorArch[] = "MacOSX-i386"; static const char kLinuxProcessorArch32[] = "Linux-i686"; static const char kLinuxProcessorArch64[] = "Linux-x86_64"; +// TODO: use live url +static QString kOpenSSLBaseUrl = "http://ws2/public/openssl/"; +static QString kCertificateLifetime = "365"; +static QString kCertificateSubjectInfo = "/CN=Synergy"; +static QString kCertificateFilename = "Synergy.pem"; +static QString kUnixOpenSSLCommand = "openssl"; + #if defined(Q_OS_WIN) static const char kWinPluginExt[] = ".dll"; +static const char kWinOpenSSLBinary[] = "openssl.exe"; + #elif defined(Q_OS_MAC) static const char kMacPluginPrefix[] = "lib"; static const char kMacPluginExt[] = ".dylib"; @@ -50,6 +61,17 @@ PluginManager::PluginManager(QStringList pluginList) : m_DownloadIndex(-1), m_pPluginDownloader(NULL) { + QStringList args1(kGetPluginDirArg); + m_PluginDir = getDirViaSyntool(args1); + if (m_PluginDir.isEmpty()) { + emit error(tr("Failed to get plugin directory.")); + } + + QStringList args2(kGetProfileDirArg); + m_ProfileDir = getDirViaSyntool(args2); + if (m_ProfileDir.isEmpty()) { + emit error(tr("Failed to get profile directory.")); + } } PluginManager::~PluginManager() @@ -77,6 +99,9 @@ void PluginManager::downloadPlugins() if (m_DownloadIndex < m_PluginList.size()) { QUrl url; QString pluginUrl = getPluginUrl(m_PluginList.at(m_DownloadIndex)); + if (pluginUrl.isEmpty()) { + return; + } url.setUrl(pluginUrl); if (m_pPluginDownloader == NULL) { @@ -87,25 +112,64 @@ void PluginManager::downloadPlugins() } } -void PluginManager::savePlugin() +void PluginManager::saveOpenSSLBinary() { - QString pluginDir = getPluginDir(); - if (pluginDir.isEmpty()) { + QDir dir(m_ProfileDir); + if (!dir.exists()) { + dir.mkpath("."); + } + + QString filename = m_ProfileDir; +#if defined(Q_OS_WIN) + filename.append("\\").append(kWinOpenSSLBinary); +#endif + + + QFile file(filename); + if (!file.open(QIODevice::WriteOnly)) { + emit error( + tr("Failed to download OpenSSl to location: %1") + .arg(m_ProfileDir)); return; } - QString filename = pluginDir; + file.write(m_pPluginDownloader->data()); + file.close(); + + emit openSSLBinaryReady(); +} + +void PluginManager::generateCertificate() +{ + connect( + this, + SIGNAL(openSSLBinaryReady()), + this, + SLOT(doGenerateCertificate())); + + downloadOpenSSLBinary(); +} + +void PluginManager::savePlugin() +{ + // create the path if not exist + QDir dir(m_PluginDir); + if (!dir.exists()) { + dir.mkpath("."); + } + + QString filename = m_PluginDir; QString pluginName = m_PluginList.at(m_DownloadIndex); pluginName = getPluginOSSpecificName(pluginName); filename.append(QDir::separator()).append(pluginName); QFile file(filename); if (!file.open(QIODevice::WriteOnly)) { - QMessageBox::warning( - (QWidget*)parent(), "Synergy", - tr("Failed to download plugin %1 to location: %2") - .arg(m_PluginList.at(m_DownloadIndex)) - .arg(pluginDir)); + emit error( + tr("Failed to download plugin %1 to location: %2") + .arg(m_PluginList.at(m_DownloadIndex)) + .arg(m_PluginDir)); + return; } @@ -114,13 +178,12 @@ void PluginManager::savePlugin() } -QString PluginManager::getPluginDir() +QString PluginManager::getDirViaSyntool(QStringList& args) { QString program(QCoreApplication::applicationDirPath() + "/syntool"); QProcess process; process.setReadChannel(QProcess::StandardOutput); - QStringList args(kGetPluginDirArg); process.start(program, args); bool success = process.waitForStarted(); @@ -143,20 +206,14 @@ QString PluginManager::getPluginDir() { QMessageBox::critical( (QWidget*)parent(), tr("Synergy"), - tr("An error occured while trying to get " - "plugin directory from syntool. Code: %1\nError: %2") + tr("An error occured while calling syntool " + "with the first arg %1. Code: %2\nError: %3") + .arg(args.at(0)) .arg(process.exitCode()) .arg(error.isEmpty() ? "Unknown" : error)); return ""; } - // create the path if not exist - // TODO: synergy folder should be hidden - QDir dir(out); - if (!dir.exists()) { - dir.mkpath("."); - } - return out; } @@ -182,9 +239,9 @@ QString PluginManager::getPluginUrl(const QString& pluginName) result.append(kLinuxProcessorArch64); } else { - QMessageBox::critical( - (QWidget*)parent(), tr("Synergy"), - tr("Failed to detect system architecture.")); + emit error( + tr("Failed to get the url of plugin %1 .") + .arg(pluginName)); return ""; } result.append("/"); @@ -193,6 +250,16 @@ QString PluginManager::getPluginUrl(const QString& pluginName) return result; } +QString PluginManager::getOpenSSLBinaryUrl() +{ + QString result; +#if defined(Q_OS_WIN) + result = kOpenSSLBaseUrl.append(kWinOpenSSLBinary); +#endif + + return result; +} + QString PluginManager::getPluginOSSpecificName(const QString& pluginName) { QString result = pluginName; @@ -205,3 +272,93 @@ QString PluginManager::getPluginOSSpecificName(const QString& pluginName) #endif return result; } + +bool PluginManager::checkOpenSSLBinary() +{ + bool exist = false; +#if defined(Q_OS_WIN) + QString openSSLFilename = m_ProfileDir; + openSSLFilename.append("\\").append(kWinOpenSSLBinary); + QDir dir(openSSLFilename); + if (dir.exists()) { + exist = true; + } +#else + // assume OpenSSL is always installed on both Mac and Linux + exist = true; +#endif + + return exist; +} + +void PluginManager::downloadOpenSSLBinary() +{ + if (checkOpenSSLBinary()) { + emit openSSLBinaryReady(); + return; + } + + QUrl url; + QString pluginUrl = getOpenSSLBinaryUrl(); + url.setUrl(pluginUrl); + + disconnect( + m_pPluginDownloader, + SIGNAL(isComplete()), + this, + SLOT(downloadPlugins())); + connect( + m_pPluginDownloader, + SIGNAL(isComplete()), + this, + SLOT(saveOpenSSLBinary())); + + m_pPluginDownloader->download(url); +} + +void PluginManager::doGenerateCertificate() +{ + QString openSSLFilename = m_ProfileDir; +#if defined(Q_OS_WIN) + openSSLFilename.append("\\").append(kWinOpenSSLBinary); +#else + openSSLFilename = kUnixOpenSSLCommand; +#endif + + QStringList arguments; + + // self signed certificate + arguments.append("req"); + arguments.append("-x509"); + arguments.append("-nodes"); + + // valide duration + arguments.append("-days"); + arguments.append(kCertificateLifetime); + + // subject information + arguments.append("-subj"); + + QString info(kCertificateSubjectInfo); + arguments.append(info); + + // private key + arguments.append("-newkey"); + arguments.append("rsa:1024"); + + // key output filename + arguments.append("-keyout"); + QString filename = m_ProfileDir; + filename.append(QDir::separator()).append(kCertificateFilename); + arguments.append(filename); + + // certificate output filename + arguments.append("-out"); + arguments.append(filename); + + // update command and arguments + CommandProcess commandProcess(openSSLFilename, arguments); + commandProcess.run(); + + emit generateCertificateFinished(); +} diff --git a/src/gui/src/PluginManager.h b/src/gui/src/PluginManager.h index 07038db2..26623ad5 100644 --- a/src/gui/src/PluginManager.h +++ b/src/gui/src/PluginManager.h @@ -36,19 +36,30 @@ public: public slots: void downloadPlugins(); + void saveOpenSSLBinary(); + void generateCertificate(); + void doGenerateCertificate(); private: void savePlugin(); - QString getPluginDir(); + QString getDirViaSyntool(QStringList& args); QString getPluginUrl(const QString& pluginName); + QString getOpenSSLBinaryUrl(); QString getPluginOSSpecificName(const QString& pluginName); + bool checkOpenSSLBinary(); + void downloadOpenSSLBinary(); signals: + void error(QString e); void downloadNext(); void downloadFinished(); + void openSSLBinaryReady(); + void generateCertificateFinished(); private: QStringList m_PluginList; + QString m_PluginDir; + QString m_ProfileDir; int m_DownloadIndex; DataDownloader* m_pPluginDownloader; }; diff --git a/src/gui/src/PluginWizardPage.cpp b/src/gui/src/PluginWizardPage.cpp index 4007cd56..b3008894 100644 --- a/src/gui/src/PluginWizardPage.cpp +++ b/src/gui/src/PluginWizardPage.cpp @@ -43,15 +43,21 @@ void PluginWizardPage::changeEvent(QEvent *e) } } +void PluginWizardPage::showError(QString error) +{ + updateStatus(error); + stopSpinning(); + m_Finished = true; + emit completeChanged(); +} + void PluginWizardPage::queryPluginDone() { QStringList pluginList = m_pWebClient->getPluginList(); if (pluginList.isEmpty()) { - if (!m_pWebClient->getLastError().isEmpty()) { - updateStatus(m_pWebClient->getLastError()); - m_Finished = true; - emit completeChanged(); - } + updateStatus("No plugin available."); + m_Finished = true; + emit completeChanged(); } else { downloadPlugins(); @@ -77,6 +83,26 @@ void PluginWizardPage::finished() emit completeChanged(); } +void PluginWizardPage::generateCertificate() +{ + connect(m_pPluginManager, + SIGNAL(generateCertificateFinished()), + this, + SLOT(finished())); + + connect(m_pPluginManager, + SIGNAL(generateCertificateFinished()), + m_pPluginManagerThread, + SLOT(quit())); + + updateStatus(tr("Generating certificate...")); + + QMetaObject::invokeMethod( + m_pPluginManager, + "generateCertificate", + Qt::QueuedConnection); +} + void PluginWizardPage::updateStatus(QString info) { m_pLabelStatus->setText(info); @@ -86,7 +112,12 @@ void PluginWizardPage::downloadPlugins() { QStringList pluginList = m_pWebClient->getPluginList(); m_pPluginManager = new PluginManager(pluginList); - QThread* thread = new QThread; + m_pPluginManagerThread = new QThread; + + connect(m_pPluginManager, + SIGNAL(error(QString)), + this, + SLOT(showError(QString))); connect(m_pPluginManager, SIGNAL(downloadNext()), @@ -96,16 +127,16 @@ void PluginWizardPage::downloadPlugins() connect(m_pPluginManager, SIGNAL(downloadFinished()), this, - SLOT(finished())); + SLOT(generateCertificate())); connect(m_pPluginManager, - SIGNAL(downloadFinished()), - thread, + SIGNAL(error(QString)), + m_pPluginManagerThread, SLOT(quit())); - connect(thread, + connect(m_pPluginManagerThread, SIGNAL(finished()), - thread, + m_pPluginManagerThread, SLOT(deleteLater())); updateStatus( @@ -113,7 +144,13 @@ void PluginWizardPage::downloadPlugins() .arg(pluginList.at(0)) .arg(pluginList.size())); - QMetaObject::invokeMethod(m_pPluginManager, "downloadPlugins", Qt::QueuedConnection); + m_pPluginManager->moveToThread(m_pPluginManagerThread); + m_pPluginManagerThread->start(); + + QMetaObject::invokeMethod( + m_pPluginManager, + "downloadPlugins", + Qt::QueuedConnection); } void PluginWizardPage::stopSpinning() @@ -147,6 +184,12 @@ void PluginWizardPage::initializePage() m_pWebClient->setPassword(m_Password); QThread* thread = new QThread; + + connect(m_pWebClient, + SIGNAL(error(QString)), + this, + SLOT(showError(QString))); + connect(m_pWebClient, SIGNAL(queryPluginDone()), this, @@ -157,6 +200,11 @@ void PluginWizardPage::initializePage() thread, SLOT(quit())); + connect(m_pWebClient, + SIGNAL(error(QString)), + thread, + SLOT(quit())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); m_pWebClient->moveToThread(thread); diff --git a/src/gui/src/PluginWizardPage.h b/src/gui/src/PluginWizardPage.h index 49ff4944..994679c5 100644 --- a/src/gui/src/PluginWizardPage.h +++ b/src/gui/src/PluginWizardPage.h @@ -26,9 +26,11 @@ protected: void changeEvent(QEvent *e); protected slots: + void showError(QString error); void queryPluginDone(); void updateDownloadStatus(); void finished(); + void generateCertificate(); private: void updateStatus(QString info); @@ -41,5 +43,6 @@ private: QString m_Password; WebClient* m_pWebClient; PluginManager* m_pPluginManager; + QThread* m_pPluginManagerThread; }; #endif // PLUGINWIZARDPAGE_H diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index 252ab052..0175abdf 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -98,10 +98,9 @@ void WebClient::queryPluginList() } catch (std::exception& e) { - m_Error = tr("An error occured while trying to query the " - "plugin list. Please contact the help desk, and provide " - "the following details.\n\n%1").arg(e.what()); - emit queryPluginDone(); + emit error(tr("An error occured while trying to query the " + "plugin list. Please contact the help desk, and " + "provide the following details.\n\n%1").arg(e.what())); return; } @@ -113,15 +112,13 @@ void WebClient::queryPluginList() if (editionRegex.exactMatch(responseJson)) { QString e = editionRegex.cap(1); m_PluginList = e.split(","); - m_Error.clear(); emit queryPluginDone(); return; } } else if (boolString == "false") { - m_Error = tr("Get plugin list failed, invalid user email " - "or password."); - emit queryPluginDone(); + emit error(tr("Get plugin list failed, invalid user email " + "or password.")); return; } } @@ -130,17 +127,15 @@ void WebClient::queryPluginList() if (errorRegex.exactMatch(responseJson)) { // replace "\n" with real new lines. - QString error = errorRegex.cap(1).replace("\\n", "\n"); - m_Error = tr("Get plugin list failed, an error occurred." - "\n\n%1").arg(error); - emit queryPluginDone(); + QString e = errorRegex.cap(1).replace("\\n", "\n"); + emit error(tr("Get plugin list failed, an error occurred." + "\n\n%1").arg(e)); return; } } - m_Error = tr("Get plugin list failed, an error occurred.\n\n" - "Server response:\n\n%1").arg(responseJson); - emit queryPluginDone(); + emit error(tr("Get plugin list failed, an error occurred.\n\n" + "Server response:\n\n%1").arg(responseJson)); return; } diff --git a/src/gui/src/WebClient.h b/src/gui/src/WebClient.h index d806d9f8..e39fa552 100644 --- a/src/gui/src/WebClient.h +++ b/src/gui/src/WebClient.h @@ -38,12 +38,12 @@ public: void setEmail(QString& e) { m_Email = e; } void setPassword(QString& p) { m_Password = p; } QStringList& getPluginList() { return m_PluginList; } - QString& getLastError() { return m_Error; } public slots: void queryPluginList(); signals: + void error(QString e); void queryPluginDone(); private: @@ -55,7 +55,6 @@ private: QString m_Email; QString m_Password; QStringList m_PluginList; - QString m_Error; }; #endif // WEBCLIENT_H diff --git a/src/lib/arch/IArchFile.h b/src/lib/arch/IArchFile.h index 517fbf99..7790487e 100644 --- a/src/lib/arch/IArchFile.h +++ b/src/lib/arch/IArchFile.h @@ -68,6 +68,12 @@ public: */ virtual std::string getPluginDirectory() = 0; + //! Get local profile directory + /*! + Returns the local profile directory. + */ + virtual std::string getProfileDirectory() = 0; + //! Concatenate path components /*! Concatenate pathname components with a directory separator diff --git a/src/lib/arch/unix/ArchFileUnix.cpp b/src/lib/arch/unix/ArchFileUnix.cpp index ff6ed0c4..21ffdea6 100644 --- a/src/lib/arch/unix/ArchFileUnix.cpp +++ b/src/lib/arch/unix/ArchFileUnix.cpp @@ -108,9 +108,19 @@ std::string ArchFileUnix::getPluginDirectory() { #if WINAPI_XWINDOWS - return getUserDirectory().append("/.synergy/plugins"); + return getProfileDirectory().append("/plugins"); #else - return getUserDirectory().append("/Libraries/Synergy/Plugins"); + return getProfileDirectory().append("/Plugins"); +#endif +} + +std::string +ArchFileUnix::getProfileDirectory() +{ +#if WINAPI_XWINDOWS + return getUserDirectory().append("/.synergy"); +#else + return getUserDirectory().append("/Libraries/Synergy"); #endif } diff --git a/src/lib/arch/unix/ArchFileUnix.h b/src/lib/arch/unix/ArchFileUnix.h index 2818d26b..0428b75e 100644 --- a/src/lib/arch/unix/ArchFileUnix.h +++ b/src/lib/arch/unix/ArchFileUnix.h @@ -35,6 +35,7 @@ public: virtual std::string getInstalledDirectory(); virtual std::string getLogDirectory(); virtual std::string getPluginDirectory(); + virtual std::string getProfileDirectory(); virtual std::string concatPath(const std::string& prefix, const std::string& suffix); }; diff --git a/src/lib/arch/win32/ArchFileWindows.cpp b/src/lib/arch/win32/ArchFileWindows.cpp index ec1fabe8..278a18f9 100644 --- a/src/lib/arch/win32/ArchFileWindows.cpp +++ b/src/lib/arch/win32/ArchFileWindows.cpp @@ -142,8 +142,24 @@ ArchFileWindows::getLogDirectory() std::string ArchFileWindows::getPluginDirectory() { - std::string dir = getUserDirectory(); - dir.append("\\Synergy\\Plugins"); + std::string dir = getProfileDirectory(); + dir.append("\\Plugins"); + return dir; +} + +std::string +ArchFileWindows::getProfileDirectory() +{ + TCHAR result[MAX_PATH]; + std::string dir; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, result))) { + dir = result; + } + else { + dir = getUserDirectory(); + } + + dir.append("\\Synergy"); return dir; } diff --git a/src/lib/arch/win32/ArchFileWindows.h b/src/lib/arch/win32/ArchFileWindows.h index c4745073..aa99b7fd 100644 --- a/src/lib/arch/win32/ArchFileWindows.h +++ b/src/lib/arch/win32/ArchFileWindows.h @@ -35,6 +35,7 @@ public: virtual std::string getInstalledDirectory(); virtual std::string getLogDirectory(); virtual std::string getPluginDirectory(); + virtual std::string getProfileDirectory(); virtual std::string concatPath(const std::string& prefix, const std::string& suffix); }; diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 77d301cd..a1c6187e 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -177,6 +177,10 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) args.m_getPluginDir = true; return true; } + if (isArg(i, argc, argv, NULL, "--get-profile-dir", 0)) { + args.m_getProfileDir = true; + return true; + } else { return false; } diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index a7d4a88b..02701578 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -76,6 +76,9 @@ ToolApp::run(int argc, char** argv) else if (m_args.m_getPluginDir) { std::cout << ARCH->getPluginDirectory() << std::endl; } + else if (m_args.m_getProfileDir) { + std::cout << ARCH->getProfileDirectory() << std::endl; + } else { throw XSynergy("Nothing to do"); } diff --git a/src/lib/synergy/ToolArgs.cpp b/src/lib/synergy/ToolArgs.cpp index 6155a9e8..d7eea498 100644 --- a/src/lib/synergy/ToolArgs.cpp +++ b/src/lib/synergy/ToolArgs.cpp @@ -21,6 +21,7 @@ ToolArgs::ToolArgs() : m_printActiveDesktopName(false), m_loginAuthenticate(false), m_getPluginList(false), - m_getPluginDir(false) + m_getPluginDir(false), + m_getProfileDir(false) { } diff --git a/src/lib/synergy/ToolArgs.h b/src/lib/synergy/ToolArgs.h index c820f3f5..1ad05513 100644 --- a/src/lib/synergy/ToolArgs.h +++ b/src/lib/synergy/ToolArgs.h @@ -28,4 +28,5 @@ public: bool m_loginAuthenticate; bool m_getPluginList; bool m_getPluginDir; + bool m_getProfileDir; };