1 /*
2     Copyright © 2015-2019 by The qTox Project Contributors
3 
4     This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6     qTox is libre software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     qTox is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with qTox.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 
21 #include "loginscreen.h"
22 #include "ui_loginscreen.h"
23 #include "src/persistence/profile.h"
24 #include "src/persistence/profilelocker.h"
25 #include "src/persistence/settings.h"
26 #include "src/widget/form/setpassworddialog.h"
27 #include "src/widget/style.h"
28 #include "src/widget/tool/profileimporter.h"
29 #include "src/widget/translator.h"
30 #include <QDebug>
31 #include <QDialog>
32 #include <QMessageBox>
33 #include <QToolButton>
34 
LoginScreen(const QString & initialProfileName,QWidget * parent)35 LoginScreen::LoginScreen(const QString& initialProfileName, QWidget* parent)
36     : QDialog(parent)
37     , ui(new Ui::LoginScreen)
38     , quitShortcut{QKeySequence(Qt::CTRL + Qt::Key_Q), this}
39 {
40     ui->setupUi(this);
41 
42     // permanently disables maximize button https://github.com/qTox/qTox/issues/1973
43     this->setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);
44     this->setFixedSize(this->size());
45 
46     connect(&quitShortcut, &QShortcut::activated, this, &LoginScreen::close);
47     connect(ui->newProfilePgbtn, &QPushButton::clicked, this, &LoginScreen::onNewProfilePageClicked);
48     connect(ui->loginPgbtn, &QPushButton::clicked, this, &LoginScreen::onLoginPageClicked);
49     connect(ui->createAccountButton, &QPushButton::clicked, this, &LoginScreen::onCreateNewProfile);
50     connect(ui->newUsername, &QLineEdit::returnPressed, this, &LoginScreen::onCreateNewProfile);
51     connect(ui->newPass, &QLineEdit::returnPressed, this, &LoginScreen::onCreateNewProfile);
52     connect(ui->newPassConfirm, &QLineEdit::returnPressed, this, &LoginScreen::onCreateNewProfile);
53     connect(ui->loginButton, &QPushButton::clicked, this, &LoginScreen::onLogin);
54     connect(ui->loginUsernames, &QComboBox::currentTextChanged, this,
55             &LoginScreen::onLoginUsernameSelected);
56     connect(ui->loginPassword, &QLineEdit::returnPressed, this, &LoginScreen::onLogin);
57     connect(ui->newPass, &QLineEdit::textChanged, this, &LoginScreen::onPasswordEdited);
58     connect(ui->newPassConfirm, &QLineEdit::textChanged, this, &LoginScreen::onPasswordEdited);
59     connect(ui->autoLoginCB, &QCheckBox::stateChanged, this, &LoginScreen::onAutoLoginCheckboxChanged);
60     connect(ui->importButton, &QPushButton::clicked, this, &LoginScreen::onImportProfile);
61 
62     reset(initialProfileName);
63     this->setStyleSheet(Style::getStylesheet("loginScreen/loginScreen.css"));
64 
65     retranslateUi();
66     Translator::registerHandler(std::bind(&LoginScreen::retranslateUi, this), this);
67 }
68 
~LoginScreen()69 LoginScreen::~LoginScreen()
70 {
71     Translator::unregister(this);
72     delete ui;
73 }
74 
75 /**
76  * @brief Resets the UI, clears all fields.
77  */
reset(const QString & initialProfileName)78 void LoginScreen::reset(const QString& initialProfileName)
79 {
80     ui->newUsername->clear();
81     ui->newPass->clear();
82     ui->newPassConfirm->clear();
83     ui->loginPassword->clear();
84     ui->loginUsernames->clear();
85 
86     QStringList allProfileNames = Profile::getAllProfileNames();
87 
88     if (allProfileNames.isEmpty()) {
89         ui->stackedWidget->setCurrentIndex(0);
90         ui->newUsername->setFocus();
91     } else {
92         for (const QString& profileName : allProfileNames) {
93             ui->loginUsernames->addItem(profileName);
94         }
95 
96         ui->loginUsernames->setCurrentText(initialProfileName);
97         ui->stackedWidget->setCurrentIndex(1);
98         ui->loginPassword->setFocus();
99     }
100 }
101 
onProfileLoaded()102 void LoginScreen::onProfileLoaded()
103 {
104     done(QDialog::Accepted);
105 }
106 
onProfileLoadFailed()107 void LoginScreen::onProfileLoadFailed()
108 {
109     QMessageBox::critical(this, tr("Couldn't load this profile"), tr("Wrong password."));
110     ui->loginPassword->setFocus();
111     ui->loginPassword->selectAll();
112 }
113 
onAutoLoginChanged(bool state)114 void LoginScreen::onAutoLoginChanged(bool state)
115 {
116     ui->autoLoginCB->setChecked(state);
117 }
118 
event(QEvent * event)119 bool LoginScreen::event(QEvent* event)
120 {
121     switch (event->type()) {
122 #ifdef Q_OS_MAC
123     case QEvent::WindowActivate:
124     case QEvent::WindowStateChange:
125         emit windowStateChanged(windowState());
126         break;
127 #endif
128     default:
129         break;
130     }
131 
132     return QWidget::event(event);
133 }
134 
onNewProfilePageClicked()135 void LoginScreen::onNewProfilePageClicked()
136 {
137     ui->stackedWidget->setCurrentIndex(0);
138 }
139 
onLoginPageClicked()140 void LoginScreen::onLoginPageClicked()
141 {
142     ui->stackedWidget->setCurrentIndex(1);
143 }
144 
onCreateNewProfile()145 void LoginScreen::onCreateNewProfile()
146 {
147     QString name = ui->newUsername->text();
148     QString pass = ui->newPass->text();
149 
150     if (name.isEmpty()) {
151         QMessageBox::critical(this, tr("Couldn't create a new profile"),
152                               tr("The username must not be empty."));
153         return;
154     }
155 
156     if (pass.size() != 0 && pass.size() < 6) {
157         QMessageBox::critical(this, tr("Couldn't create a new profile"),
158                               tr("The password must be at least 6 characters long."));
159         return;
160     }
161 
162     if (ui->newPassConfirm->text() != pass) {
163         QMessageBox::critical(this, tr("Couldn't create a new profile"),
164                               tr("The passwords you've entered are different.\nPlease make sure to "
165                                  "enter same password twice."));
166         return;
167     }
168 
169     if (Profile::exists(name)) {
170         QMessageBox::critical(this, tr("Couldn't create a new profile"),
171                               tr("A profile with this name already exists."));
172         return;
173     }
174 
175     emit createNewProfile(name, pass);
176 }
177 
onLoginUsernameSelected(const QString & name)178 void LoginScreen::onLoginUsernameSelected(const QString& name)
179 {
180     if (name.isEmpty())
181         return;
182 
183     ui->loginPassword->clear();
184     if (Profile::isEncrypted(name)) {
185         ui->loginPasswordLabel->show();
186         ui->loginPassword->show();
187         // there is no way to do autologin if profile is encrypted, and
188         // visible option confuses users into thinking that it is possible,
189         // thus hide it
190         ui->autoLoginCB->hide();
191     } else {
192         ui->loginPasswordLabel->hide();
193         ui->loginPassword->hide();
194         ui->autoLoginCB->show();
195         ui->autoLoginCB->setToolTip(
196             tr("Password protected profiles can't be automatically loaded."));
197     }
198 }
199 
onLogin()200 void LoginScreen::onLogin()
201 {
202     QString name = ui->loginUsernames->currentText();
203     QString pass = ui->loginPassword->text();
204 
205     // name can be empty when there are no profiles
206     if (name.isEmpty()) {
207         QMessageBox::critical(this, tr("Couldn't load profile"),
208                               tr("There is no selected profile.\n\n"
209                                  "You may want to create one."));
210         return;
211     }
212 
213     if (!ProfileLocker::isLockable(name)) {
214         QMessageBox::critical(this, tr("Couldn't load this profile"),
215                               tr("This profile is already in use."));
216         return;
217     }
218 
219     emit loadProfile(name, pass);
220 }
221 
onPasswordEdited()222 void LoginScreen::onPasswordEdited()
223 {
224     ui->passStrengthMeter->setValue(SetPasswordDialog::getPasswordStrength(ui->newPass->text()));
225 }
226 
onAutoLoginCheckboxChanged(int state)227 void LoginScreen::onAutoLoginCheckboxChanged(int state)
228 {
229     auto cstate = static_cast<Qt::CheckState>(state);
230     emit autoLoginChanged(cstate == Qt::CheckState::Checked);
231 }
232 
retranslateUi()233 void LoginScreen::retranslateUi()
234 {
235     ui->retranslateUi(this);
236 }
237 
onImportProfile()238 void LoginScreen::onImportProfile()
239 {
240     ProfileImporter pi(this);
241     if (pi.importProfile()) {
242         reset();
243     }
244 }
245