summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThanos Doukoudakis <thanos.doukoudakis@isode.com>2018-07-12 22:58:02 (GMT)
committerKevin Smith <kevin.smith@isode.com>2018-07-18 17:11:33 (GMT)
commit0d5cd98d372c6b9235b55cd8d16e93647c9d017f (patch)
tree9f8ade0f540e35c49e1dd390bc3efe56de0b6b25
parent0d3c4298f0818cb377fcfc8409e89fe6327ab036 (diff)
downloadswift-0d5cd98d372c6b9235b55cd8d16e93647c9d017f.zip
swift-0d5cd98d372c6b9235b55cd8d16e93647c9d017f.tar.bz2
Add support for multiple accounts
Added support for multiple accounts and a list of servers where the user can switch between the accounts. Future patches will make the list widget to use server avatars with user status. Upon startup the client will reconnect with all previous accounts. If the user log outs with any of the accounts then it will not login automatically for future sessions, the credential though will be available to the user. Upon upgrading from previous versions, the client will migrate the account that was previously marked to auto-login to the new configuration and enable it. After the migration the autologin setting will be set to false. Some of the settings and command line arguments have been made obsolete due to these changes and removed, including SSO support, which will be re-introduced in a future patch. Test-Information: Tested the changes in Windows and Linux, tested adding and removing accounts, and switching between them. Tested the new configuration for accounts, the upgrade behaviour when an account is marked/not marked to autojoin, and the migration to the new configuration. Verified that the auto-login setting is set to false after the migration, and that the migrated account can be disabled (currently only by signing out). Change-Id: I63662f80e006112fde6f418f9743e2b420e81870
-rw-r--r--Swift/Controllers/MainController.cpp93
-rw-r--r--Swift/Controllers/MainController.h3
-rw-r--r--Swift/Controllers/SettingConstants.cpp3
-rw-r--r--Swift/Controllers/SettingConstants.h11
-rw-r--r--Swift/QtUI/QtLoginWindow.h4
-rw-r--r--Swift/QtUI/QtSingleWindow.cpp71
-rw-r--r--Swift/QtUI/QtSingleWindow.h16
-rw-r--r--Swift/QtUI/QtSwift.cpp190
-rw-r--r--Swift/QtUI/QtSwift.h14
-rw-r--r--Swift/QtUI/QtUIFactory.cpp13
-rw-r--r--Swift/QtUI/QtUIFactory.h4
11 files changed, 241 insertions, 181 deletions
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index f678c0d..91140c2 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -173,36 +173,6 @@ MainController::MainController(
xmppURIController_ = new XMPPURIController(uriHandler_, uiEventStream_);
- std::string selectedLoginJID = settings_->getSetting(SettingConstants::LAST_LOGIN_JID);
- bool loginAutomatically = settings_->getSetting(SettingConstants::LOGIN_AUTOMATICALLY);
- std::string cachedPassword;
- std::string cachedCertificate;
- ClientOptions cachedOptions;
- bool eagle = settings_->getSetting(SettingConstants::FORGET_PASSWORDS);
- if (!eagle) {
- for (auto&& profile : settings->getAvailableProfiles()) {
- ProfileSettingsProvider profileSettings(profile, settings);
- std::string password = profileSettings.getStringSetting("pass");
- std::string certificate = profileSettings.getStringSetting("certificate");
- std::string jid = profileSettings.getStringSetting("jid");
- ClientOptions clientOptions = parseClientOptions(profileSettings.getStringSetting("options"));
-
-#ifdef SWIFTEN_PLATFORM_WIN32
- clientOptions.singleSignOn = settings_->getSetting(SettingConstants::SINGLE_SIGN_ON);
-#endif
-
- loginWindow_->addAvailableAccount(jid, password, certificate, clientOptions);
- if (jid == selectedLoginJID) {
- cachedPassword = password;
- cachedCertificate = certificate;
- cachedOptions = clientOptions;
- }
- }
- loginWindow_->selectUser(selectedLoginJID);
- loginWindow_->setLoginAutomatically(loginAutomatically);
- }
-
-
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));
@@ -217,13 +187,6 @@ MainController::MainController(
settings_->onSettingChanged.connect(boost::bind(&MainController::handleSettingChanged, this, _1));
- if (loginAutomatically) {
- profileSettings_ = new ProfileSettingsProvider(selectedLoginJID, settings_);
- /* FIXME: deal with autologin with a cert*/
- handleLoginRequest(selectedLoginJID, cachedPassword, cachedCertificate, CertificateWithKey::ref(), cachedOptions, true, true);
- } else {
- profileSettings_ = nullptr;
- }
}
MainController::~MainController() {
@@ -559,8 +522,7 @@ void MainController::handleLoginRequest(const std::string &username, const std::
profileSettings_->storeString("pass", (remember || loginAutomatically) ? password : "");
std::string optionString = serializeClientOptions(options);
profileSettings_->storeString("options", optionString);
- settings_->storeSetting(SettingConstants::LAST_LOGIN_JID, username);
- settings_->storeSetting(SettingConstants::LOGIN_AUTOMATICALLY, loginAutomatically);
+ profileSettings_->storeInt("enabled", 1);
loginWindow_->addAvailableAccount(profileSettings_->getStringSetting("jid"), profileSettings_->getStringSetting("pass"), profileSettings_->getStringSetting("certificate"), options);
}
@@ -760,6 +722,7 @@ void MainController::signOut() {
if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) {
purgeCachedCredentials();
}
+ profileSettings_->storeInt("enabled", 0);
eventController_->clear();
logout();
loginWindow_->loggedOut();
@@ -864,6 +827,8 @@ void MainController::handleQuitRequest() {
}
}
+//FIXME: Switch all this to boost::serialise
+
#define SERIALIZE_BOOL(option) result += options.option ? "1" : "0"; result += ",";
#define SERIALIZE_INT(option) result += boost::lexical_cast<std::string>(options.option); result += ",";
#define SERIALIZE_STRING(option) result += Base64::encode(createByteArray(options.option)); result += ",";
@@ -898,55 +863,7 @@ std::string MainController::serializeClientOptions(const ClientOptions& options)
SERIALIZE_SAFE_STRING(boshHTTPConnectProxyAuthID);
SERIALIZE_SAFE_STRING(boshHTTPConnectProxyAuthPassword);
SERIALIZE_BOOL(tlsOptions.schannelTLS1_0Workaround);
- return result;
-}
-
-#define CHECK_PARSE_LENGTH if (i >= segments.size()) {return result;}
-#define PARSE_INT_RAW(defaultValue) CHECK_PARSE_LENGTH intVal = defaultValue; try {intVal = boost::lexical_cast<int>(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, defaultValue) PARSE_INT_RAW(defaultValue); result.option = (intVal == 1);
-#define PARSE_INT(option, defaultValue) PARSE_INT_RAW(defaultValue); 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; result.option = URL::fromString(stringVal);}
-
-
-ClientOptions MainController::parseClientOptions(const std::string& optionString) {
- ClientOptions result;
- size_t i = 0;
- int intVal = 0;
- std::string stringVal;
- std::vector<std::string> segments = String::split(optionString, ',');
-
- PARSE_BOOL(useStreamCompression, 1);
- PARSE_INT_RAW(-1);
- 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, 0);
- PARSE_BOOL(useStreamResumption, 0);
- PARSE_BOOL(useAcks, 1);
- PARSE_STRING(manualHostname);
- PARSE_INT(manualPort, -1);
- PARSE_INT_RAW(-1);
- 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, -1);
- PARSE_URL(boshURL);
- PARSE_URL(boshHTTPConnectProxyURL);
- PARSE_SAFE_STRING(boshHTTPConnectProxyAuthID);
- PARSE_SAFE_STRING(boshHTTPConnectProxyAuthPassword);
- PARSE_BOOL(tlsOptions.schannelTLS1_0Workaround, false);
-
+ SERIALIZE_BOOL(singleSignOn);
return result;
}
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 8b62415..7a06a0b 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -132,7 +132,6 @@ namespace Swift {
void handleForceQuit();
void purgeCachedCredentials();
std::string serializeClientOptions(const ClientOptions& options);
- ClientOptions parseClientOptions(const std::string& optionString);
private:
EventLoop* eventLoop_;
@@ -146,7 +145,7 @@ namespace Swift {
bool clientInitialized_;
std::shared_ptr<Client> client_;
SettingsProvider *settings_;
- ProfileSettingsProvider* profileSettings_;
+ ProfileSettingsProvider* profileSettings_ = nullptr;
Dock* dock_;
URIHandler* uriHandler_;
IdleDetector* idleDetector_;
diff --git a/Swift/Controllers/SettingConstants.cpp b/Swift/Controllers/SettingConstants.cpp
index 1191c28..8336200 100644
--- a/Swift/Controllers/SettingConstants.cpp
+++ b/Swift/Controllers/SettingConstants.cpp
@@ -14,8 +14,6 @@ const SettingsProvider::Setting<bool> SettingConstants::SHOW_NOTIFICATIONS = Set
const SettingsProvider::Setting<bool> SettingConstants::REQUEST_DELIVERYRECEIPTS = SettingsProvider::Setting<bool>("requestDeliveryReceipts", false);
const SettingsProvider::Setting<bool> SettingConstants::FORGET_PASSWORDS = SettingsProvider::Setting<bool>("forgetPasswords", false);
const SettingsProvider::Setting<bool> SettingConstants::REMEMBER_RECENT_CHATS = SettingsProvider::Setting<bool>("rememberRecentChats", true);
-const SettingsProvider::Setting<std::string> SettingConstants::LAST_LOGIN_JID = SettingsProvider::Setting<std::string>("lastLoginJID", "");
-const SettingsProvider::Setting<bool> SettingConstants::LOGIN_AUTOMATICALLY = SettingsProvider::Setting<bool>("loginAutomatically", false);
const SettingsProvider::Setting<bool> SettingConstants::SHOW_OFFLINE("showOffline", false);
const SettingsProvider::Setting<std::string> SettingConstants::EXPANDED_ROSTER_GROUPS("GroupExpandiness", "");
const SettingsProvider::Setting<bool> SettingConstants::PLAY_SOUNDS("playSounds", true);
@@ -23,7 +21,6 @@ const SettingsProvider::Setting<std::string> SettingConstants::HIGHLIGHT_RULES("
const SettingsProvider::Setting<std::string> SettingConstants::HIGHLIGHT_RULES_V2("highlightRulesV2", "@");
const SettingsProvider::Setting<std::string> SettingConstants::INVITE_AUTO_ACCEPT_MODE("inviteAutoAcceptMode", "presence");
const SettingsProvider::Setting<bool> SettingConstants::DISCONNECT_ON_CARD_REMOVAL("disconnectOnCardRemoval", true);
-const SettingsProvider::Setting<bool> SettingConstants::SINGLE_SIGN_ON("singleSignOn", false);
const SettingsProvider::Setting<bool> SettingConstants::MUC_MARKING_ELISION("mucMarkingElision", true);
const SettingsProvider::Setting<bool> SettingConstants::FUTURE("future", false);
diff --git a/Swift/Controllers/SettingConstants.h b/Swift/Controllers/SettingConstants.h
index f82dfb5..68c22b7 100644
--- a/Swift/Controllers/SettingConstants.h
+++ b/Swift/Controllers/SettingConstants.h
@@ -34,8 +34,6 @@ namespace Swift {
static const SettingsProvider::Setting<bool> REQUEST_DELIVERYRECEIPTS;
static const SettingsProvider::Setting<bool> FORGET_PASSWORDS;
static const SettingsProvider::Setting<bool> REMEMBER_RECENT_CHATS;
- static const SettingsProvider::Setting<std::string> LAST_LOGIN_JID;
- static const SettingsProvider::Setting<bool> LOGIN_AUTOMATICALLY;
/**
* The #SHOW_OFFLINE setting specifies whether or not to show offline contacts in the
* roster.
@@ -85,15 +83,6 @@ namespace Swift {
*/
static const SettingsProvider::Setting<bool> DISCONNECT_ON_CARD_REMOVAL;
/**
- * The #SINGLE_SIGN_ON setting
- * specifies whether to log in using Single Sign On.
- * This is currently supported on Windows.
- *
- * If set true Swift will use GSSAPI authentication to
- * log in the user; else not.
- */
- static const SettingsProvider::Setting<bool> SINGLE_SIGN_ON;
- /**
* The #MUC_MARKING_ELISION setting
* specifies whether or not messages with the default muc
* marking display their marking, and whether unmarked messages
diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h
index 6920903..d3c2601 100644
--- a/Swift/QtUI/QtLoginWindow.h
+++ b/Swift/QtUI/QtLoginWindow.h
@@ -59,8 +59,10 @@ namespace Swift {
signals:
void geometryChanged();
- private slots:
+ public slots:
void loginClicked();
+
+ private slots:
void handleCertficateChecked(bool);
void handleQuit();
void handleShowXMLConsole();
diff --git a/Swift/QtUI/QtSingleWindow.cpp b/Swift/QtUI/QtSingleWindow.cpp
index d53f247..6881c4f 100644
--- a/Swift/QtUI/QtSingleWindow.cpp
+++ b/Swift/QtUI/QtSingleWindow.cpp
@@ -6,9 +6,13 @@
#include <Swift/QtUI/QtSingleWindow.h>
+#include <QPushButton>
+#include <QVBoxLayout>
+
#include <Swiften/Base/Platform.h>
#include <Swift/QtUI/QtChatTabs.h>
+#include <Swift/QtUI/QtLoginWindow.h>
#include <Swift/QtUI/QtSettingsProvider.h>
namespace Swift {
@@ -24,6 +28,25 @@ QtSingleWindow::QtSingleWindow(QtSettingsProvider* settings) : QSplitter() {
}
connect(this, SIGNAL(splitterMoved(int, int)), this, SLOT(handleSplitterMoved(/*int, int*/)));
setChildrenCollapsible(false);
+
+ auto left = new QWidget(this);
+ list_ = new QListWidget(left);
+ auto addButton = new QPushButton("+", left);
+ QVBoxLayout* leftLayout = new QVBoxLayout();
+ leftLayout->addWidget(list_);
+ leftLayout->addWidget(addButton);
+ left->setLayout(leftLayout);
+ QSplitter::addWidget(left);
+ loginWindows_ = new QStackedWidget(this);
+ QSplitter::addWidget(loginWindows_);
+ tabs_ = new QStackedWidget(this);
+ QSplitter::addWidget(tabs_);
+ restoreSplitters();
+ setStretchFactor(0, 0);
+ setStretchFactor(1, 0);
+ setStretchFactor(2, 1);
+ connect(list_, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(handleListItemClicked(QListWidgetItem*)));
+ connect(addButton, SIGNAL(clicked()), this, SIGNAL(wantsToAddAccount()));
#ifdef SWIFTEN_PLATFORM_MACOSX
setHandleWidth(0);
#endif
@@ -33,14 +56,6 @@ QtSingleWindow::~QtSingleWindow() {
}
-void QtSingleWindow::addWidget(QWidget* widget) {
- QtChatTabs* tabs = dynamic_cast<QtChatTabs*>(widget);
- if (tabs) {
- connect(tabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
- }
- QSplitter::addWidget(widget);
-}
-
void QtSingleWindow::handleTabsTitleChanged(const QString& title) {
setWindowTitle(title);
}
@@ -55,25 +70,18 @@ void QtSingleWindow::handleSplitterMoved() {
}
void QtSingleWindow::restoreSplitters() {
- QList<QVariant> variantValues = settings_->getQSettings()->value(SINGLE_WINDOW_SPLITS).toList();
- QList<int> intValues;
- for (auto&& value : variantValues) {
- intValues.append(value.toInt());
- }
- setSizes(intValues);
-}
-
-void QtSingleWindow::insertAtFront(QWidget* widget) {
- insertWidget(0, widget);
auto splitsVariant = settings_->getQSettings()->value(SINGLE_WINDOW_SPLITS);
if (splitsVariant.isValid()) {
- restoreSplitters();
+ auto variantValues = splitsVariant.toList();
+ QList<int> intValues;
+ for (auto&& value : variantValues) {
+ intValues.append(value.toInt());
+ }
+ setSizes(intValues);
}
else {
handleSplitterMoved();
}
- setStretchFactor(0, 0);
- setStretchFactor(1, 1);
}
void QtSingleWindow::handleGeometryChanged() {
@@ -89,4 +97,25 @@ void QtSingleWindow::moveEvent(QMoveEvent*) {
handleGeometryChanged();
}
+void QtSingleWindow::addAccount(QtLoginWindow* loginWindow, QtChatTabs* tabs) {
+ if (!loginWindows_->count()) {
+ connect(tabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
+ }
+ loginWindows_->addWidget(loginWindow);
+ tabs_->addWidget(tabs);
+ list_->addItem(QString("Account %1").arg(loginWindows_->count()));
+}
+
+void QtSingleWindow::handleListItemClicked(QListWidgetItem* /*item*/) {
+ //FIXME: Should use a full model/view and do this properly (and render pretty things ourselves too)
+ auto currentTabs = tabs_->widget(tabs_->currentIndex());
+ disconnect(currentTabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
+ loginWindows_->setCurrentIndex(list_->currentRow());
+ tabs_->setCurrentIndex(list_->currentRow());
+ currentTabs = tabs_->widget(tabs_->currentIndex());
+ connect(currentTabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
+ //TODO change the title of the window.
+ handleTabsTitleChanged(QString("Swift"));
+}
+
}
diff --git a/Swift/QtUI/QtSingleWindow.h b/Swift/QtUI/QtSingleWindow.h
index c6f22cf..9a7e475 100644
--- a/Swift/QtUI/QtSingleWindow.h
+++ b/Swift/QtUI/QtSingleWindow.h
@@ -6,9 +6,14 @@
#pragma once
+#include <QListWidget>
#include <QSplitter>
+#include <QStackedWidget>
+
namespace Swift {
+ class QtChatTabs;
+ class QtLoginWindow;
class QtSettingsProvider;
class QtSingleWindow : public QSplitter {
@@ -16,14 +21,18 @@ namespace Swift {
public:
QtSingleWindow(QtSettingsProvider* settings);
virtual ~QtSingleWindow();
- void insertAtFront(QWidget* widget);
- void addWidget(QWidget* widget);
+ void addAccount(QtLoginWindow* widget, QtChatTabs* tabs);
+
+ signals:
+ void wantsToAddAccount();
+
protected:
void resizeEvent(QResizeEvent*);
void moveEvent(QMoveEvent*);
private slots:
void handleSplitterMoved();
void handleTabsTitleChanged(const QString& title);
+ void handleListItemClicked(QListWidgetItem*);
private:
void handleGeometryChanged();
void restoreSplitters();
@@ -31,6 +40,9 @@ namespace Swift {
private:
QtSettingsProvider* settings_;
+ QListWidget* list_;
+ QStackedWidget* loginWindows_;
+ QStackedWidget* tabs_;
};
}
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index 89dfa01..28c7044 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -22,8 +22,10 @@
#include <Swiften/Base/Path.h>
#include <Swiften/Base/Paths.h>
#include <Swiften/Base/Platform.h>
+#include <Swiften/Base/String.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/Presence.h>
+#include <Swiften/StringCodecs/Base64.h>
#include <Swiften/TLS/TLSContextFactory.h>
#include <SwifTools/Application/PlatformApplicationPathProvider.h>
@@ -92,8 +94,6 @@ po::options_description QtSwift::getOptionsDescription() {
("version", "Show version information")
("no-tabs", "Don't manage chat windows in tabs (unsupported)")
("latency-debug", "Use latency debugging (unsupported)")
- ("multi-account", po::value<int>()->default_value(1), "Number of accounts to open windows for (unsupported)")
- ("start-minimized", "Don't show the login/roster window at startup")
("enable-jid-adhocs", "Enable AdHoc commands to custom JIDs.")
#if QT_VERSION >= 0x040800
("language", po::value<std::string>(), "Use a specific language, instead of the system-wide one")
@@ -167,19 +167,11 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
networkFactories_.getTLSContextFactory()->setDisconnectOnCardRemoval(settingsHierarchy_->getSetting(SettingConstants::DISCONNECT_ON_CARD_REMOVAL));
- std::map<std::string, std::string> emoticons;
- loadEmoticonsFile(":/emoticons/emoticons.txt", emoticons);
- loadEmoticonsFile(P2QSTRING(pathToString(Paths::getExecutablePath() / "emoticons.txt")), emoticons);
+ loadEmoticonsFile(":/emoticons/emoticons.txt", emoticons_);
+ loadEmoticonsFile(P2QSTRING(pathToString(Paths::getExecutablePath() / "emoticons.txt")), emoticons_);
splitter_ = new QtSingleWindow(qtSettings_);
-
- int numberOfAccounts = 1;
- try {
- numberOfAccounts = options["multi-account"].as<int>();
- } catch (...) {
- /* This seems to fail on a Mac when the .app is launched directly (the usual path).*/
- numberOfAccounts = 1;
- }
+ connect(splitter_, SIGNAL(wantsToAddAccount()), this, SLOT(handleWantsToAddAccount()));
if (options.count("debug")) {
Log::setLogLevel(Swift::Log::debug);
@@ -202,6 +194,8 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
SWIFT_LOG(error) << "Error while retrieving the specified log file name from the command line" << std::endl;
}
}
+ //TODO this old option can be purged
+ useDelayForLatency_ = options.count("latency-debug") > 0;
// Load fonts
std::vector<std::string> fontNames = {
@@ -240,13 +234,10 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
#ifdef SWIFTEN_PLATFORM_WINDOWS
QFont::insertSubstitution(QApplication::font().family(), "Segoe UI Emoji");
#endif
- bool enableAdHocCommandOnJID = options.count("enable-jid-adhocs") > 0;
- tabs_ = new QtChatTabs(settingsHierarchy_, true);
- bool startMinimized = options.count("start-minimized") > 0;
+ enableAdHocCommandOnJID_ = options.count("enable-jid-adhocs") > 0;
applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME);
storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir(), networkFactories_.getCryptoProvider());
certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory(), networkFactories_.getCryptoProvider());
- chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierarchy_, qtSettings_, tabs_, ":/themes/Default/", emoticons);
soundPlayer_ = new QtSoundPlayer(applicationPathProvider_);
// Ugly, because the dock depends on the tray, but the temporary
@@ -298,32 +289,8 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
}
});
}
-
- for (int i = 0; i < numberOfAccounts; i++) {
- if (i > 0) {
- // Don't add the first tray (see note above)
- systemTrays_.push_back(new QtSystemTray());
- }
- QtUIFactory* uiFactory = new QtUIFactory(settingsHierarchy_, qtSettings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, networkFactories_.getTimerFactory(), statusCache_, autoUpdater_, startMinimized, !emoticons.empty(), enableAdHocCommandOnJID);
- uiFactories_.push_back(uiFactory);
- MainController* mainController = new MainController(
- &clientMainThreadCaller_,
- &networkFactories_,
- uiFactory,
- settingsHierarchy_,
- systemTrays_[i],
- soundPlayer_,
- storagesFactory_,
- certificateStorageFactory_,
- dock_,
- notifier_,
- uriHandler_,
- &idleDetector_,
- emoticons,
- options.count("latency-debug") > 0);
- mainControllers_.push_back(mainController);
- }
-
+ migrateLastLoginAccount();
+ restoreAccounts();
connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(handleAboutToQuit()));
}
@@ -339,8 +306,6 @@ QtSwift::~QtSwift() {
for (auto* tray : systemTrays_) {
delete tray;
}
- delete tabs_;
- delete chatWindowFactory_;
delete splitter_;
delete settingsHierarchy_;
delete qtSettings_;
@@ -379,4 +344,139 @@ void QtSwift::handleAutoUpdaterStateChanged(AutoUpdater::State updatedState) {
}
}
+void QtSwift::handleWantsToAddAccount() {
+ auto loginWindow = addAccount();
+ if (!settingsHierarchy_->getSetting(SettingConstants::FORGET_PASSWORDS)) {
+ for (const auto& profile : settingsHierarchy_->getAvailableProfiles()) {
+ ProfileSettingsProvider profileSettings(profile, settingsHierarchy_);
+ if (profileSettings.getIntSetting("enabled", 0)) {
+ // No point showing accounts that're already logged in
+ continue;
+ }
+ const auto& password = profileSettings.getStringSetting("pass");
+ const auto& certificate = profileSettings.getStringSetting("certificate");
+ const auto& jid = profileSettings.getStringSetting("jid");
+ const auto& clientOptions = parseClientOptions(profileSettings.getStringSetting("options"));
+ loginWindow->addAvailableAccount(jid, password, certificate, clientOptions);
+ }
+ }
+}
+
+void QtSwift::restoreAccounts() {
+ if (!settingsHierarchy_->getSetting(SettingConstants::FORGET_PASSWORDS)) {
+ for (const auto& profile : settingsHierarchy_->getAvailableProfiles()) {
+ ProfileSettingsProvider profileSettings(profile, settingsHierarchy_);
+ if (!profileSettings.getIntSetting("enabled", 0)) {
+ continue;
+ }
+ const auto& jid = profileSettings.getStringSetting("jid");
+ const auto& password = profileSettings.getStringSetting("pass");
+ const auto& certificate = profileSettings.getStringSetting("certificate");
+ const auto& clientOptions = parseClientOptions(profileSettings.getStringSetting("options"));
+ auto loginWindow = addAccount();
+ loginWindow->addAvailableAccount(jid, password, certificate, clientOptions);
+ loginWindow->loginClicked();
+ }
+ }
+}
+
+void QtSwift::migrateLastLoginAccount() {
+ const SettingsProvider::Setting<bool> loginAutomatically = SettingsProvider::Setting<bool>("loginAutomatically", false);
+ if (settingsHierarchy_->getSetting(loginAutomatically)) {
+ auto selectedLoginJID = settingsHierarchy_->getSetting(SettingsProvider::Setting<std::string>("lastLoginJID", ""));
+ for (const auto& profile : settingsHierarchy_->getAvailableProfiles()) {
+ ProfileSettingsProvider profileSettings(profile, settingsHierarchy_);
+ if (profileSettings.getStringSetting("jid") == selectedLoginJID) {
+ profileSettings.storeInt("enabled", 1);
+ break;
+ }
+ }
+ settingsHierarchy_->storeSetting(loginAutomatically, false);
+ }
+}
+
+QtLoginWindow* QtSwift::addAccount() {
+ if (uiFactories_.size() > 0) {
+ // Don't add the first tray (see note above)
+ systemTrays_.push_back(new QtSystemTray());
+ }
+ auto tabs = new QtChatTabs(settingsHierarchy_, true);
+ QtUIFactory* uiFactory = new QtUIFactory(settingsHierarchy_, qtSettings_, tabs, splitter_, systemTrays_[systemTrays_.size() - 1], networkFactories_.getTimerFactory(), statusCache_, autoUpdater_, emoticons_, enableAdHocCommandOnJID_);
+ uiFactories_.push_back(uiFactory);
+ MainController* mainController = new MainController(
+ &clientMainThreadCaller_,
+ &networkFactories_,
+ uiFactory,
+ settingsHierarchy_,
+ systemTrays_[systemTrays_.size() - 1],
+ soundPlayer_,
+ storagesFactory_,
+ certificateStorageFactory_,
+ dock_,
+ notifier_,
+ uriHandler_,
+ &idleDetector_,
+ emoticons_,
+ useDelayForLatency_);
+ mainControllers_.push_back(mainController);
+
+ //FIXME - mainController has already created the window, so we can pass null here and get the old one
+ auto loginWindow = uiFactory->createLoginWindow(nullptr);
+
+ return dynamic_cast<QtLoginWindow*>(loginWindow);
+}
+
+//FIXME: Switch all this to boost::serialise
+
+#define CHECK_PARSE_LENGTH if (i >= segments.size()) {return result;}
+#define PARSE_INT_RAW(defaultValue) CHECK_PARSE_LENGTH intVal = defaultValue; try {intVal = boost::lexical_cast<int>(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, defaultValue) PARSE_INT_RAW(defaultValue); result.option = (intVal == 1);
+#define PARSE_INT(option, defaultValue) PARSE_INT_RAW(defaultValue); 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; result.option = URL::fromString(stringVal);}
+
+
+ClientOptions QtSwift::parseClientOptions(const std::string& optionString) {
+ ClientOptions result;
+ size_t i = 0;
+ int intVal = 0;
+ std::string stringVal;
+ std::vector<std::string> segments = String::split(optionString, ',');
+
+ PARSE_BOOL(useStreamCompression, 1);
+ PARSE_INT_RAW(-1);
+ 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, 0);
+ PARSE_BOOL(useStreamResumption, 0);
+ PARSE_BOOL(useAcks, 1);
+ PARSE_STRING(manualHostname);
+ PARSE_INT(manualPort, -1);
+ PARSE_INT_RAW(-1);
+ 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, -1);
+ PARSE_URL(boshURL);
+ PARSE_URL(boshHTTPConnectProxyURL);
+ PARSE_SAFE_STRING(boshHTTPConnectProxyAuthID);
+ PARSE_SAFE_STRING(boshHTTPConnectProxyAuthPassword);
+ PARSE_BOOL(tlsOptions.schannelTLS1_0Workaround, false);
+ PARSE_BOOL(singleSignOn, false);
+
+ return result;
+}
+
+
}
diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h
index f35d2cc..d876cd8 100644
--- a/Swift/QtUI/QtSwift.h
+++ b/Swift/QtUI/QtSwift.h
@@ -6,12 +6,14 @@
#pragma once
+#include <map>
#include <string>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <Swiften/Base/Platform.h>
+#include <Swiften/Client/ClientOptions.h>
#include <Swiften/EventLoop/Qt/QtEventLoop.h>
#include <Swiften/Network/BoostNetworkFactories.h>
#include <Swiften/TLS/PlatformTLSFactories.h>
@@ -68,11 +70,19 @@ namespace Swift {
private slots:
void handleAboutToQuit();
void handleAutoUpdaterStateChanged(AutoUpdater::State updatedState);
+ void handleWantsToAddAccount();
private:
XMLSettingsProvider* loadSettingsFile(const QString& fileName);
void loadEmoticonsFile(const QString& fileName, std::map<std::string, std::string>& emoticons);
static const std::string& updateChannelToFeed(const std::string& channel);
+ QtLoginWindow* addAccount();
+ ClientOptions parseClientOptions(const std::string& optionString);
+ void restoreAccounts();
+ /**
+ * Upgrades the config from pre-multi-account to post-multi-account format (added in 5.0).
+ */
+ void migrateLastLoginAccount();
private:
QtEventLoop clientMainThreadCaller_;
@@ -89,7 +99,6 @@ namespace Swift {
QtSoundPlayer* soundPlayer_;
Dock* dock_;
URIHandler* uriHandler_;
- QtChatTabs* tabs_;
ApplicationPathProvider* applicationPathProvider_;
StoragesFactory* storagesFactory_;
CertificateStorageFactory* certificateStorageFactory_;
@@ -98,6 +107,9 @@ namespace Swift {
StatusCache* statusCache_;
PlatformIdleQuerier idleQuerier_;
ActualIdleDetector idleDetector_;
+ std::map<std::string, std::string> emoticons_;
+ bool enableAdHocCommandOnJID_ = false;
+ bool useDelayForLatency_;
#if defined(SWIFTEN_PLATFORM_MACOSX)
CocoaApplication cocoaApplication_;
CocoaApplicationActivateHelper cocoaApplicationActivateHelper_;
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index 0b19f63..2762d68 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -40,7 +40,9 @@
namespace Swift {
-QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, StatusCache* statusCache, AutoUpdater* autoUpdater, bool startMinimized, bool emoticonsExist, bool enableAdHocCommandOnJID) : settings_(settings), qtOnlySettings_(qtOnlySettings), tabs_(tabs), netbookSplitter_(netbookSplitter), systemTray_(systemTray), chatWindowFactory_(chatWindowFactory), timerFactory_(timerFactory), lastMainWindow_(nullptr), loginWindow_(nullptr), statusCache_(statusCache), autoUpdater_(autoUpdater), startMinimized_(startMinimized), emoticonsExist_(emoticonsExist), enableAdHocCommandOnJID_(enableAdHocCommandOnJID) {
+QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, TimerFactory* timerFactory, StatusCache* statusCache, AutoUpdater* autoUpdater, std::map<std::string, std::string>& emoticons, bool enableAdHocCommandOnJID) : settings_(settings), qtOnlySettings_(qtOnlySettings), tabs_(tabs), netbookSplitter_(netbookSplitter), systemTray_(systemTray), timerFactory_(timerFactory), lastMainWindow_(nullptr), loginWindow_(nullptr), statusCache_(statusCache), autoUpdater_(autoUpdater), emoticons_(emoticons), enableAdHocCommandOnJID_(enableAdHocCommandOnJID) {
+ emoticonsExist_ = !emoticons_.empty();
+ chatWindowFactory_ = new QtChatWindowFactory(netbookSplitter_, settings, qtOnlySettings, tabs_, ":/themes/Default/", emoticons_);
chatFontSize_ = settings_->getSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE);
historyFontSize_ = settings_->getSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE);
}
@@ -50,6 +52,7 @@ QtUIFactory::~QtUIFactory() {
for (auto chat : chatWindows_) {
SWIFT_LOG_ASSERT(chat.isNull(), debug) << "QtUIFactory has active chat windows and has not been reset properly" << std::endl;
}
+ delete chatWindowFactory_;
}
XMLConsoleWidget* QtUIFactory::createXMLConsoleWidget() {
@@ -91,12 +94,12 @@ MainWindow* QtUIFactory::createMainWindow(Chattables& chattables, UIEventStream*
}
LoginWindow* QtUIFactory::createLoginWindow(UIEventStream* eventStream) {
+ if (loginWindow_) {
+ return loginWindow_;
+ }
loginWindow_ = new QtLoginWindow(eventStream, settings_, timerFactory_, autoUpdater_);
- netbookSplitter_->insertAtFront(loginWindow_);
+ netbookSplitter_->addAccount(loginWindow_, tabs_);
connect(systemTray_, SIGNAL(clicked()), loginWindow_, SLOT(toggleBringToFront()));
- if (startMinimized_) {
- loginWindow_->hide();
- }
return loginWindow_;
}
diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h
index 4013668..ad8dc1f 100644
--- a/Swift/QtUI/QtUIFactory.h
+++ b/Swift/QtUI/QtUIFactory.h
@@ -35,7 +35,7 @@ namespace Swift {
class QtUIFactory : public QObject, public UIFactory {
Q_OBJECT
public:
- QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, StatusCache* statusCache, AutoUpdater* autoUpdater, bool startMinimized, bool emoticonsExist, bool enableAdHocCommandOnJID);
+ QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, TimerFactory* timerFactory, StatusCache* statusCache, AutoUpdater* autoUpdater, std::map<std::string, std::string>& emoticons, bool enableAdHocCommandOnJID);
~QtUIFactory();
virtual XMLConsoleWidget* createXMLConsoleWidget();
virtual HistoryWindow* createHistoryWindow(UIEventStream*);
@@ -75,10 +75,10 @@ namespace Swift {
StatusCache* statusCache_;
AutoUpdater* autoUpdater_;
std::vector<QPointer<QtChatWindow> > chatWindows_;
- bool startMinimized_;
int chatFontSize_;
int historyFontSize_;
bool emoticonsExist_;
+ std::map<std::string, std::string>& emoticons_;
bool enableAdHocCommandOnJID_;
};
}