From 0174723efbc1f612433d45c6916a2ad4596b96ba Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 15 Mar 2012 20:17:09 +0100 Subject: Connection settings support diff --git a/Swift/ChangeLog.md b/Swift/ChangeLog.md index ce52f1d..bef90f5 100644 --- a/Swift/ChangeLog.md +++ b/Swift/ChangeLog.md @@ -1,19 +1,20 @@ 2.0-beta2 --------- -- Enable auto-completion of nicknames that don't start with a letter -- Generate crash dumps on Windows -- Connection timeouts are now on each connection separately, instead of on the complete connection process -- Don't allow pressing `` in the roster to trigger logins -- Don't lose security labels when correcting a message -- Don't crash when completing user search without selection -- Always auto join MUC with a consistent nickname -- Renamed `swift` binary to `swift-im` on Linux -- Avoid woosh down and woosh up on Mac when opening chat windows -- Improved MUC invitation dialog -- Fixed problem with displaying group names containing '&' in the 'Edit' dialog -- Show stream encryption status in header -- Show extended certificate dialog when encryption errors occur -- Don't allow server command results to get interpreted as HTML +- Enable auto-completion of nicknames that don't start with a letter. +- Generate crash dumps on Windows. +- Connection timeouts are now on each connection separately, instead of on the complete connection process. +- Don't allow pressing `` in the roster to trigger logins. +- Don't lose security labels when correcting a message. +- Don't crash when completing user search without selection. +- Always auto join MUC with a consistent nickname. +- Renamed `swift` binary to `swift-im` on Linux. +- Avoid woosh down and woosh up on Mac when opening chat windows. +- Improved MUC invitation dialog. +- Fixed problem with displaying group names containing '&' in the 'Edit' dialog. +- Show stream encryption status in header. +- Show extended certificate dialog when encryption errors occur. +- Don't allow server command results to get interpreted as HTML. +- Additional connection settings (such as manual host setting, proxy configuration and BOSH) can now be specified through a new dialog on the login window. Thanks to Tobias Markmann. @@ -28,9 +29,9 @@ Thanks to Tobias Markmann. - Swift will now use appropriate algorithms when run on a Windows platform locked down in FIPS-140-2 mode. - Our TLS provider has been changed to the platform-provided one (Schannel) on Windows, allowing us to use certificates (both file and card-based) from the system store (CAPI). -- Added support for message receipts in one-to-one conversations (XEP-0184) +- Added support for message receipts in one-to-one conversations (XEP-0184). - Added support for several MUC operations (kicking, banning, invites, topic changes, room destruction, - changing participant affiliations, ...) + changing participant affiliations, ...). - It is now possible to resize the font in the chat window conversations. - Added support for message correction. Use 'up' to edit the previous message. - A list of recent chats is now kept in the 'Chats' tab of the main window. @@ -38,8 +39,8 @@ Thanks to Tobias Markmann. - Chat tabs with unread messages from several chats will now be a little more descriptive. - Use a bar for showing where the last read messages in a chat are. - Added support for SOCKS5 and HTTPConnect proxies. These settings are taken from the platform preferences. -- Retrieve incremental contact list on login on servers that support it (XEP-0237: Roster Versioning) -- Lots of bugfixes, performance fixes, and other changes +- Retrieve incremental contact list on login on servers that support it (XEP-0237: Roster Versioning). +- Lots of bugfixes, performance fixes, and other changes. Thanks to Tobias Markmann, Jan Kaluza, Thilo Cestonaro, Arnt Gulbrandsen, Vlad Voicu, Vitaly Takmazov, Yoann Blein, Catalin Badea, Pavol Babincak, Mateusz Piekos, Alexey Melnikov and Soren Dreijer. @@ -47,4 +48,4 @@ Yoann Blein, Catalin Badea, Pavol Babincak, Mateusz Piekos, Alexey Melnikov and 1.0 --- -- Initial release +- Initial release. diff --git a/Swift/Controllers/ConnectionSettings.h b/Swift/Controllers/ConnectionSettings.h new file mode 100644 index 0000000..c02c5d4 --- /dev/null +++ b/Swift/Controllers/ConnectionSettings.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include + +struct ConnectionSettings { + enum Method { + Automatic, + Manual, + BOSH + }; + enum ProxyType { + None, + System, + SOCKS5, + HTTPConnect + }; + + Method method; + struct { + bool useManualServer; + std::string manualServerHostname; + int manualServerPort; + ProxyType proxyType; + bool useManualProxy; + std::string manualProxyHostname; + int manualProxyPort; + } manualSettings; + struct { + std::string boshURI; + bool useManualProxy; + std::string manualProxyHostname; + int manualProxyPort; + } boshSettings; +}; diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 2c02ba8..f3a0226 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include #include #include #include "Swiften/Network/TimerFactory.h" @@ -145,6 +147,7 @@ MainController::MainController( bool loginAutomatically = settings_->getSetting(SettingConstants::LOGIN_AUTOMATICALLY); std::string cachedPassword; std::string cachedCertificate; + ClientOptions cachedOptions; bool eagle = settings_->getSetting(SettingConstants::FORGET_PASSWORDS); if (!eagle) { foreach (std::string profile, settings->getAvailableProfiles()) { @@ -152,10 +155,12 @@ MainController::MainController( std::string password = profileSettings.getStringSetting("pass"); std::string certificate = profileSettings.getStringSetting("certificate"); std::string jid = profileSettings.getStringSetting("jid"); - loginWindow_->addAvailableAccount(jid, password, certificate); + ClientOptions clientOptions = parseClientOptions(profileSettings.getStringSetting("options")); + loginWindow_->addAvailableAccount(jid, password, certificate, clientOptions); if (jid == selectedLoginJID) { cachedPassword = password; cachedCertificate = certificate; + cachedOptions = clientOptions; } } loginWindow_->selectUser(selectedLoginJID); @@ -163,7 +168,7 @@ MainController::MainController( } - loginWindow_->onLoginRequest.connect(boost::bind(&MainController::handleLoginRequest, this, _1, _2, _3, _4, _5, _6)); + loginWindow_->onLoginRequest.connect(boost::bind(&MainController::handleLoginRequest, this, _1, _2, _3, _4, _5, _6, _7)); loginWindow_->onPurgeSavedLoginRequest.connect(boost::bind(&MainController::handlePurgeSavedLoginRequest, this, _1)); loginWindow_->onCancelLoginRequest.connect(boost::bind(&MainController::handleCancelLoginRequest, this)); loginWindow_->onQuitRequest.connect(boost::bind(&MainController::handleQuitRequest, this)); @@ -180,7 +185,7 @@ MainController::MainController( if (loginAutomatically) { profileSettings_ = new ProfileSettingsProvider(selectedLoginJID, settings_); /* FIXME: deal with autologin with a cert*/ - handleLoginRequest(selectedLoginJID, cachedPassword, cachedCertificate, CertificateWithKey::ref(), true, true); + handleLoginRequest(selectedLoginJID, cachedPassword, cachedCertificate, CertificateWithKey::ref(), cachedOptions, true, true); } else { profileSettings_ = NULL; } @@ -445,7 +450,7 @@ void MainController::handleShowCertificateRequest() { rosterController_->getWindow()->openCertificateDialog(chain); } -void MainController::handleLoginRequest(const std::string &username, const std::string &password, const std::string& certificatePath, CertificateWithKey::ref certificate, bool remember, bool loginAutomatically) { +void MainController::handleLoginRequest(const std::string &username, const std::string &password, const std::string& certificatePath, CertificateWithKey::ref certificate, const ClientOptions& options, bool remember, bool loginAutomatically) { jid_ = JID(username); if (!jid_.isValid() || jid_.getNode().empty()) { loginWindow_->setMessage(QT_TRANSLATE_NOOP("", "User address invalid. User address should be of the form 'alice@wonderland.lit'")); @@ -458,13 +463,15 @@ void MainController::handleLoginRequest(const std::string &username, const std:: profileSettings_->storeString("jid", username); profileSettings_->storeString("certificate", certificatePath); profileSettings_->storeString("pass", (remember || loginAutomatically) ? password : ""); + profileSettings_->storeString("options", serializeClientOptions(options)); settings_->storeSetting(SettingConstants::LAST_LOGIN_JID, username); settings_->storeSetting(SettingConstants::LOGIN_AUTOMATICALLY, loginAutomatically); - loginWindow_->addAvailableAccount(profileSettings_->getStringSetting("jid"), profileSettings_->getStringSetting("pass"), profileSettings_->getStringSetting("certificate")); + loginWindow_->addAvailableAccount(profileSettings_->getStringSetting("jid"), profileSettings_->getStringSetting("pass"), profileSettings_->getStringSetting("certificate"), options); } password_ = password; certificate_ = certificate; + clientOptions_ = options; performLoginFromCachedCredentials(); } } @@ -524,7 +531,7 @@ void MainController::performLoginFromCachedCredentials() { if (rosterController_) { rosterController_->getWindow()->setConnecting(); } - ClientOptions clientOptions; + ClientOptions clientOptions = clientOptions_; bool eagle = settings_->getSetting(SettingConstants::FORGET_PASSWORDS); clientOptions.forgetPassword = eagle; clientOptions.useTLS = eagle ? ClientOptions::RequireTLS : ClientOptions::UseTLSWhenAvailable; @@ -731,4 +738,88 @@ void MainController::handleQuitRequest() { } } +#define SERIALIZE_BOOL(option) result += options.option ? "1" : "0"; result += ","; +#define SERIALIZE_INT(option) result += boost::lexical_cast(options.option); result += ","; +#define SERIALIZE_STRING(option) result += Base64::encode(createByteArray(options.option)); result += ","; +#define SERIALIZE_SAFE_STRING(option) result += safeByteArrayToString(Base64::encode(options.option)); result += ","; +#define SERIALIZE_URL(option) SERIALIZE_STRING(option.getScheme()) SERIALIZE_STRING(option.getHost()) SERIALIZE_INT(option.getPort()) SERIALIZE_STRING(option.getPath()) + +std::string MainController::serializeClientOptions(const ClientOptions& options) { + std::string result; + SERIALIZE_BOOL(useStreamCompression); + switch (options.useTLS) { + case ClientOptions::NeverUseTLS: result += "1";break; + case ClientOptions::UseTLSWhenAvailable: result += "2";break; + case ClientOptions::RequireTLS: result += "3";break; + } + result += ","; + SERIALIZE_BOOL(allowPLAINWithoutTLS); + SERIALIZE_BOOL(useStreamResumption); + SERIALIZE_BOOL(useAcks); + SERIALIZE_STRING(manualHostname); + SERIALIZE_INT(manualPort); + switch (options.proxyType) { + case ClientOptions::NoProxy: result += "1";break; + case ClientOptions::SystemConfiguredProxy: result += "2";break; + case ClientOptions::SOCKS5Proxy: result += "3";break; + case ClientOptions::HTTPConnectProxy: result += "4";break; + } + result += ","; + SERIALIZE_STRING(manualProxyHostname); + SERIALIZE_INT(manualProxyPort); + SERIALIZE_URL(boshURL); + SERIALIZE_URL(boshHTTPConnectProxyURL); + SERIALIZE_SAFE_STRING(boshHTTPConnectProxyAuthID); + SERIALIZE_SAFE_STRING(boshHTTPConnectProxyAuthPassword); + return result; +} + +#define CHECK_PARSE_LENGTH if (i >= segments.size()) {return result;} +#define PARSE_INT_RAW CHECK_PARSE_LENGTH intVal = 0; try {intVal = boost::lexical_cast(segments[i]);} catch(const boost::bad_lexical_cast&) {};i++; +#define PARSE_STRING_RAW CHECK_PARSE_LENGTH stringVal = byteArrayToString(Base64::decode(segments[i]));i++; + +#define PARSE_BOOL(option) PARSE_INT_RAW; result.option = (intVal == 1); +#define PARSE_INT(option) PARSE_INT_RAW; result.option = intVal; +#define PARSE_STRING(option) PARSE_STRING_RAW; result.option = stringVal; +#define PARSE_SAFE_STRING(option) PARSE_STRING_RAW; result.option = SafeString(createSafeByteArray(stringVal)); +#define PARSE_URL(option) {PARSE_STRING_RAW; std::string scheme = stringVal; PARSE_STRING_RAW; std::string host = stringVal; PARSE_INT_RAW; int port = intVal; PARSE_STRING_RAW; std::string path = stringVal; result.option = !scheme.empty() && !host.empty() ? URL(scheme, host, port, path) : URL();} + + +ClientOptions MainController::parseClientOptions(const std::string& optionString) { + ClientOptions result; + size_t i = 0; + int intVal = 0; + std::string stringVal; + std::vector segments = String::split(optionString, ','); + + PARSE_BOOL(useStreamCompression); + PARSE_INT_RAW; + switch (intVal) { + case 1: result.useTLS = ClientOptions::NeverUseTLS;break; + case 2: result.useTLS = ClientOptions::UseTLSWhenAvailable;break; + case 3: result.useTLS = ClientOptions::RequireTLS;break; + default:; + } + PARSE_BOOL(allowPLAINWithoutTLS); + PARSE_BOOL(useStreamResumption); + PARSE_BOOL(useAcks); + PARSE_STRING(manualHostname); + PARSE_INT(manualPort); + PARSE_INT_RAW; + switch (intVal) { + case 1: result.proxyType = ClientOptions::NoProxy;break; + case 2: result.proxyType = ClientOptions::SystemConfiguredProxy;break; + case 3: result.proxyType = ClientOptions::SOCKS5Proxy;break; + case 4: result.proxyType = ClientOptions::HTTPConnectProxy;break; + } + PARSE_STRING(manualProxyHostname); + PARSE_INT(manualProxyPort); + PARSE_URL(boshURL); + PARSE_URL(boshHTTPConnectProxyURL); + PARSE_SAFE_STRING(boshHTTPConnectProxyAuthID); + PARSE_SAFE_STRING(boshHTTPConnectProxyAuthPassword); + + return result; +} + } diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index 8f04f6c..6f7482e 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -93,7 +93,7 @@ namespace Swift { private: void resetClient(); void handleConnected(); - void handleLoginRequest(const std::string& username, const std::string& password, const std::string& certificatePath, CertificateWithKey::ref certificate, bool remember, bool loginAutomatically); + void handleLoginRequest(const std::string& username, const std::string& password, const std::string& certificatePath, CertificateWithKey::ref certificate, const ClientOptions& options, bool remember, bool loginAutomatically); void handleCancelLoginRequest(); void handleQuitRequest(); void handleChangeStatusRequest(StatusShow::Type show, const std::string &statusText); @@ -118,6 +118,8 @@ namespace Swift { void handleNotificationClicked(const JID& jid); void handleForceQuit(); void purgeCachedCredentials(); + std::string serializeClientOptions(const ClientOptions& options); + ClientOptions parseClientOptions(const std::string& optionString); private: EventLoop* eventLoop_; @@ -159,6 +161,7 @@ namespace Swift { std::string vCardPhotoHash_; std::string password_; CertificateWithKey::ref certificate_; + ClientOptions clientOptions_; boost::shared_ptr lastDisconnectError_; bool useDelayForLatency_; UserSearchController* userSearchControllerChat_; diff --git a/Swift/Controllers/UIInterfaces/LoginWindow.h b/Swift/Controllers/UIInterfaces/LoginWindow.h index f1c2ce1..dbc77c4 100644 --- a/Swift/Controllers/UIInterfaces/LoginWindow.h +++ b/Swift/Controllers/UIInterfaces/LoginWindow.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace Swift { class MainWindow; @@ -24,10 +25,10 @@ namespace Swift { virtual void setShowNotificationToggle(bool) = 0; virtual void setMessage(const std::string&) = 0; virtual void setIsLoggingIn(bool loggingIn) = 0; - virtual void addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate) = 0; + virtual void addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate, const ClientOptions& options) = 0; virtual void removeAvailableAccount(const std::string& jid) = 0; /** The certificate is what is used for login, the certificatePath is used for remembering paths to populate the loginwindow with*/ - boost::signal onLoginRequest; + boost::signal onLoginRequest; virtual void setLoginAutomatically(bool loginAutomatically) = 0; virtual void quit() = 0; /** Blocking request whether a cert should be permanently trusted.*/ diff --git a/Swift/QtUI/QtConnectionSettings.ui b/Swift/QtUI/QtConnectionSettings.ui new file mode 100644 index 0000000..4086cba --- /dev/null +++ b/Swift/QtUI/QtConnectionSettings.ui @@ -0,0 +1,535 @@ + + + QtConnectionSettings + + + + 0 + 0 + 406 + 434 + + + + Dialog + + + + + + + + Connection Method: + + + + + + + + 1 + 0 + + + + + Automatic + + + + + Manual + + + + + BOSH + + + + + + + + Qt::Horizontal + + + + 238 + 20 + + + + + + + + + + Qt::Horizontal + + + + + + + 0 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Secure connection: + + + + + + + + Never + + + + + Encrypt when possible + + + + + Always encrypt + + + + + + + + + + Allow Compression + + + + + + + Allow sending password over insecure connection + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 4 + + + + + + + + Manually select server + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 18 + 20 + + + + + + + + false + + + Hostname: + + + + + + + false + + + + 4 + 0 + + + + + + + + false + + + Port: + + + + + + + false + + + + 1 + 0 + + + + + + + + + + Connection Proxy + + + + + + + + Proxy type: + + + + + + + 1 + + + + None + + + + + Use system-configured proxy + + + + + SOCKS5 + + + + + HTTP Connect + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Override system-configured proxy + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 18 + 20 + + + + + + + + false + + + Hostname: + + + + + + + false + + + + 4 + 0 + + + + + + + + false + + + Port: + + + + + + + false + + + + 1 + 0 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 88 + + + + + + + + + + + + + + BOSH URI: + + + + + + + + 4 + 0 + + + + + + + + + + Manually select HTTP proxy + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 18 + 20 + + + + + + + + false + + + Hostname: + + + + + + + false + + + + 4 + 0 + + + + + + + + false + + + Port: + + + + + + + false + + + + 1 + 0 + + + + + + + + + + Qt::Vertical + + + + 20 + 80 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + QtConnectionSettings + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + QtConnectionSettings + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/Swift/QtUI/QtConnectionSettingsWindow.cpp b/Swift/QtUI/QtConnectionSettingsWindow.cpp new file mode 100644 index 0000000..324eb06 --- /dev/null +++ b/Swift/QtUI/QtConnectionSettingsWindow.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2012 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swift/QtUI/QtConnectionSettingsWindow.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Swift { + +QtConnectionSettingsWindow::QtConnectionSettingsWindow(const ClientOptions& options) : QDialog() { + ui.setupUi(this); + + connect(ui.connectionMethod, SIGNAL(currentIndexChanged(int)), ui.stackedWidget, SLOT(setCurrentIndex(int))); + + connect(ui.manual_manualHost, SIGNAL(toggled(bool)), ui.manual_manualHostNameLabel, SLOT(setEnabled(bool))); + connect(ui.manual_manualHost, SIGNAL(toggled(bool)), ui.manual_manualHostName, SLOT(setEnabled(bool))); + connect(ui.manual_manualHost, SIGNAL(toggled(bool)), ui.manual_manualHostPortLabel, SLOT(setEnabled(bool))); + connect(ui.manual_manualHost, SIGNAL(toggled(bool)), ui.manual_manualHostPort, SLOT(setEnabled(bool))); + + connect(ui.manual_manualProxy, SIGNAL(toggled(bool)), ui.manual_manualProxyHostLabel, SLOT(setEnabled(bool))); + connect(ui.manual_manualProxy, SIGNAL(toggled(bool)), ui.manual_manualProxyHost, SLOT(setEnabled(bool))); + connect(ui.manual_manualProxy, SIGNAL(toggled(bool)), ui.manual_manualProxyPortLabel, SLOT(setEnabled(bool))); + connect(ui.manual_manualProxy, SIGNAL(toggled(bool)), ui.manual_manualProxyPort, SLOT(setEnabled(bool))); + + connect(ui.bosh_manualProxy, SIGNAL(toggled(bool)), ui.bosh_manualProxyHostLabel, SLOT(setEnabled(bool))); + connect(ui.bosh_manualProxy, SIGNAL(toggled(bool)), ui.bosh_manualProxyHost, SLOT(setEnabled(bool))); + connect(ui.bosh_manualProxy, SIGNAL(toggled(bool)), ui.bosh_manualProxyPortLabel, SLOT(setEnabled(bool))); + connect(ui.bosh_manualProxy, SIGNAL(toggled(bool)), ui.bosh_manualProxyPort, SLOT(setEnabled(bool))); + + connect(ui.manual_proxyType, SIGNAL(currentIndexChanged(int)), SLOT(handleProxyTypeChanged(int))); + + ui.manual_useTLS->setCurrentIndex(2); + + ui.manual_proxyType->setCurrentIndex(0); + + ClientOptions defaults; + if (options.boshURL.empty()) { + bool isDefault = options.useStreamCompression == defaults.useStreamCompression && options.useTLS == defaults.useTLS && options.allowPLAINWithoutTLS == defaults.allowPLAINWithoutTLS && options.useStreamCompression == defaults.useStreamCompression && options.useAcks == defaults.useAcks && options.manualHostname == defaults.manualHostname && options.manualPort == defaults.manualPort && options.proxyType == defaults.proxyType && options.manualProxyHostname == defaults.manualProxyHostname && options.manualProxyPort == defaults.manualProxyPort; + if (!isDefault) { + ui.connectionMethod->setCurrentIndex(1); + ui.manual_useTLS->setCurrentIndex(options.useTLS); + ui.manual_allowPLAINWithoutTLS->setChecked(options.allowPLAINWithoutTLS); + ui.manual_allowCompression->setChecked(options.useStreamCompression); + if (!options.manualHostname.empty()) { + ui.manual_manualHost->setChecked(true); + ui.manual_manualHostName->setText(P2QSTRING(options.manualHostname)); + ui.manual_manualHostPort->setText(P2QSTRING(boost::lexical_cast(options.manualPort))); + } + ui.manual_proxyType->setCurrentIndex(options.proxyType); + if (!options.manualProxyHostname.empty()) { + ui.manual_manualProxy->setChecked(true); + ui.manual_manualProxyHost->setText(P2QSTRING(options.manualProxyHostname)); + ui.manual_manualHostPort->setText(P2QSTRING(boost::lexical_cast(options.manualProxyPort))); + } + } + } else { + ui.connectionMethod->setCurrentIndex(2); + ui.bosh_uri->setText(P2QSTRING(options.boshURL.toString())); + if (!options.boshHTTPConnectProxyURL.empty()) { + ui.bosh_manualProxy->setChecked(true); + ui.bosh_manualProxyHost->setText(P2QSTRING(options.boshHTTPConnectProxyURL.getHost())); + ui.bosh_manualProxyPort->setText(P2QSTRING(boost::lexical_cast(options.boshHTTPConnectProxyURL.getPort()))); + } + } +} + +void QtConnectionSettingsWindow::handleProxyTypeChanged(int index) { + bool proxySettingsVisible = index != NoProxy && index != SystemProxy; + ui.manual_manualProxy->setVisible(proxySettingsVisible); + ui.manual_manualProxyHostLabel->setVisible(proxySettingsVisible); + ui.manual_manualProxyHost->setVisible(proxySettingsVisible); + ui.manual_manualProxyPortLabel->setVisible(proxySettingsVisible); + ui.manual_manualProxyPort->setVisible(proxySettingsVisible); +} + +ClientOptions QtConnectionSettingsWindow::getOptions() { + ClientOptions options; + if (ui.connectionMethod->currentIndex() > 0) { + /* Not automatic */ + if (ui.connectionMethod->currentIndex() == 1) { + /* Manual */ + options.useTLS = static_cast(ui.manual_useTLS->currentIndex()); + options.useStreamCompression = ui.manual_allowCompression->isChecked(); + options.allowPLAINWithoutTLS = ui.manual_allowPLAINWithoutTLS->isChecked(); + if (ui.manual_manualHost->isChecked()) { + options.manualHostname = Q2PSTRING(ui.manual_manualHostName->text()); + try { + options.manualPort = boost::lexical_cast(Q2PSTRING(ui.manual_manualHostPort->text())); + } catch (const boost::bad_lexical_cast&) {} + } + options.proxyType = static_cast(ui.manual_proxyType->currentIndex()); + if (ui.manual_manualProxy->isChecked()) { + options.manualProxyHostname = Q2PSTRING(ui.manual_manualProxyHost->text()); + try { + options.manualProxyPort = boost::lexical_cast(Q2PSTRING(ui.manual_manualProxyPort->text())); + } catch (const boost::bad_lexical_cast&) {} + } + } + else { + /* BOSH */ + options.boshURL = URL(Q2PSTRING(ui.bosh_uri->text())); + if (ui.bosh_manualProxy->isChecked()) { + std::string host = Q2PSTRING(ui.bosh_manualProxyHost->text()); + int port = 80; + try { + port = boost::lexical_cast(Q2PSTRING(ui.bosh_manualProxyPort->text())); + } catch (const boost::bad_lexical_cast&) {} + options.boshHTTPConnectProxyURL = URL("http", host, port, ""); + } + } + } + return options; +} + +} diff --git a/Swift/QtUI/QtConnectionSettingsWindow.h b/Swift/QtUI/QtConnectionSettingsWindow.h new file mode 100644 index 0000000..2aed5d1 --- /dev/null +++ b/Swift/QtUI/QtConnectionSettingsWindow.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include + +#include "ui_QtConnectionSettings.h" + +#include + +namespace Swift { + class QtConnectionSettingsWindow : public QDialog { + Q_OBJECT + + public: + QtConnectionSettingsWindow(const ClientOptions& options); + + ClientOptions getOptions(); + + private slots: + void handleProxyTypeChanged(int); + + private: + enum { + NoProxy = 0, + SystemProxy = 1, + SOCKS5Proxy = 2, + HTTPProxy = 3 + }; + Ui::QtConnectionSettings ui; + }; +} diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp index 38ad25d..5caeb1f 100644 --- a/Swift/QtUI/QtLoginWindow.cpp +++ b/Swift/QtUI/QtLoginWindow.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef HAVE_SCHANNEL #include "CAPICertificateSelector.h" @@ -137,6 +138,13 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* set loginButton_->setDefault(true); layout->addWidget(loginButton_); + QLabel* connectionOptionsLabel = new QLabel(this); + connectionOptionsLabel->setText("Connection Options"); + connectionOptionsLabel->setTextFormat(Qt::RichText); + connectionOptionsLabel->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + layout->addWidget(connectionOptionsLabel); + connect(connectionOptionsLabel, SIGNAL(linkActivated(const QString&)), SLOT(handleOpenConnectionOptions())); + message_ = new QLabel(this); message_->setTextFormat(Qt::RichText); message_->setWordWrap(true); @@ -296,7 +304,7 @@ void QtLoginWindow::removeAvailableAccount(const std::string& jid) { } } -void QtLoginWindow::addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate) { +void QtLoginWindow::addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate, const ClientOptions& options) { QString username = P2QSTRING(defaultJID); int index = -1; for (int i = 0; i < usernames_.count(); i++) { @@ -308,11 +316,13 @@ void QtLoginWindow::addAvailableAccount(const std::string& defaultJID, const std usernames_.append(username); passwords_.append(P2QSTRING(defaultPassword)); certificateFiles_.append(P2QSTRING(defaultCertificate)); + options_.push_back(options); username_->addItem(username); } else { usernames_[index] = username; passwords_[index] = P2QSTRING(defaultPassword); certificateFiles_[index] = P2QSTRING(defaultCertificate); + options_[index] = options; } } @@ -323,6 +333,7 @@ void QtLoginWindow::handleUsernameTextChanged() { certificateFile_ = certificateFiles_[i]; password_->setText(passwords_[i]); remember_->setChecked(password_->text() != ""); + currentOptions_ = options_[i]; } } if (!certificateFile_.isEmpty()) { @@ -376,7 +387,7 @@ void QtLoginWindow::loginClicked() { certificate = boost::make_shared(certificateString, createSafeByteArray(Q2PSTRING(password_->text()))); #endif - onLoginRequest(Q2PSTRING(username_->currentText()), Q2PSTRING(password_->text()), certificateString, certificate, remember_->isChecked(), loginAutomatically_->isChecked()); + onLoginRequest(Q2PSTRING(username_->currentText()), Q2PSTRING(password_->text()), certificateString, certificate, currentOptions_, remember_->isChecked(), loginAutomatically_->isChecked()); if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { /* Mustn't remember logins */ username_->clearEditText(); password_->setText(""); @@ -528,4 +539,11 @@ bool QtLoginWindow::askUserToTrustCertificatePermanently(const std::string& mess } } +void QtLoginWindow::handleOpenConnectionOptions() { + QtConnectionSettingsWindow connectionSettings(currentOptions_); + if (connectionSettings.exec() == QDialog::Accepted) { + currentOptions_ = connectionSettings.getOptions(); + } +} + } diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h index 572902e..c1966d8 100644 --- a/Swift/QtUI/QtLoginWindow.h +++ b/Swift/QtUI/QtLoginWindow.h @@ -43,7 +43,7 @@ namespace Swift { virtual void loggedOut(); virtual void setShowNotificationToggle(bool); virtual void setMessage(const std::string& message); - virtual void addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate); + virtual void addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate, const ClientOptions& options); virtual void removeAvailableAccount(const std::string& jid); virtual void setLoginAutomatically(bool loginAutomatically); virtual void setIsLoggingIn(bool loggingIn); @@ -71,6 +71,7 @@ namespace Swift { void resizeEvent(QResizeEvent* event); void moveEvent(QMoveEvent* event); void handleSettingChanged(const std::string& settingPath); + void handleOpenConnectionOptions(); protected: bool eventFilter(QObject *obj, QEvent *event); @@ -81,6 +82,7 @@ namespace Swift { QStringList usernames_; QStringList passwords_; QStringList certificateFiles_; + std::vector options_; QComboBox* username_; QLineEdit* password_; QPushButton* loginButton_; @@ -102,5 +104,6 @@ namespace Swift { QAction* xmlConsoleAction_; QAction* fileTransferOverviewAction_; TimerFactory* timerFactory_; + ClientOptions currentOptions_; }; } diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 51873a0..fe186e5 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -116,6 +116,7 @@ sources = [ "QtLineEdit.cpp", "QtJoinMUCWindow.cpp", "QtInviteToChatWindow.cpp", + "QtConnectionSettingsWindow.cpp", "Roster/RosterModel.cpp", "Roster/QtTreeWidget.cpp", # "Roster/QtTreeWidgetItem.cpp", @@ -205,6 +206,7 @@ myenv.Uic4("QtBookmarkDetailWindow.ui") myenv.Uic4("QtAffiliationEditor.ui") myenv.Uic4("QtJoinMUCWindow.ui") myenv.Uic4("QtHistoryWindow.ui") +myenv.Uic4("QtConnectionSettings.ui") myenv.Qrc("DefaultTheme.qrc") myenv.Qrc("Swift.qrc") diff --git a/Swiften/Base/URL.h b/Swiften/Base/URL.h index 7a5aa59..94dc4cb 100644 --- a/Swiften/Base/URL.h +++ b/Swiften/Base/URL.h @@ -7,6 +7,7 @@ #pragma once #include +#include namespace Swift { @@ -16,6 +17,14 @@ class URL { URL() : scheme(""), user(""), password(""), host(""), port(-1), path(""), isEmpty(true) { } + URL(const std::string& urlString) { + host = urlString; + port = 80; + scheme = "http"; + isEmpty = false; + //FIXME + } + URL(const std::string& scheme, const std::string& host, int port, const std::string& path) : scheme(scheme), user(), password(), host(host), port(port), path(path), isEmpty(false) { } @@ -55,7 +64,27 @@ class URL { return path; } - + const std::string toString() const { + if (isEmpty) { + return ""; + } + std::string result = scheme + "://"; + if (!user.empty()) { + result += user; + if (!password.empty()) { + result += ":" + password; + } + result += "@"; + } + result += host; + if (port > 0) { + result += ":"; + result += boost::lexical_cast(port); + } + result += "/"; + result += path; + return result; + } private: std::string scheme; -- cgit v0.10.2-6-g49f6