1// vi: ts=8 sts=4 sw=4
2/*
3 This file is part of the KDE libraries
4 SPDX-FileCopyrightText: 1998 Pietro Iglio <iglio@fub.it>
5 SPDX-FileCopyrightText: 1999, 2000 Geert Jansen <jansen@kde.org>
6 SPDX-FileCopyrightText: 2004, 2005 Andrew Coles <andrew_coles@yahoo.co.uk>
7 SPDX-FileCopyrightText: 2007 Michaƫl Larouche <larouche@kde.org>
8 SPDX-FileCopyrightText: 2009 Christoph Feck <cfeck@kde.org>
9 SPDX-FileCopyrightText: 2015 Elvis Angelaccio <elvis.angelaccio@kde.org>
10
11 SPDX-License-Identifier: LGPL-2.0-only
12*/
13
14#include "knewpasswordwidget.h"
15#include "ui_knewpasswordwidget.h"
16
17class KNewPasswordWidgetPrivate
18{
19 Q_DECLARE_TR_FUNCTIONS(KNewPasswordWidget)
20
21public:
22 KNewPasswordWidgetPrivate(KNewPasswordWidget *parent)
23 : q(parent)
24 {
25 }
26
27 void init();
28 void passwordChanged();
29 void toggleEchoMode();
30 int effectivePasswordLength(QStringView password);
31 void updatePasswordStatus(KNewPasswordWidget::PasswordStatus status);
32
33 KNewPasswordWidget *const q;
34
35 KNewPasswordWidget::PasswordStatus passwordStatus = KNewPasswordWidget::WeakPassword;
36 int minimumPasswordLength = 0;
37 int passwordStrengthWarningLevel = 1;
38 int reasonablePasswordLength = 8;
39
40 QAction *toggleEchoModeAction = nullptr;
41 QColor backgroundWarningColor;
42 QColor defaultBackgroundColor;
43
44 Ui::KNewPasswordWidget ui;
45};
46
47void KNewPasswordWidgetPrivate::init()
48{
49 ui.setupUi(q);
50
51 const QString strengthBarWhatsThis(
52 tr(sourceText: "The password strength meter gives an indication of the security "
53 "of the password you have entered. To improve the strength of the password, try:"
54 "<ul><li>using a longer password;</li>"
55 "<li>using a mixture of upper- and lower-case letters;</li>"
56 "<li>using numbers or symbols, such as #, as well as letters.</li></ul>",
57 disambiguation: "@info:whatsthis"));
58 ui.labelStrengthMeter->setWhatsThis(strengthBarWhatsThis);
59 ui.strengthBar->setWhatsThis(strengthBarWhatsThis);
60
61 QObject::connect(sender: ui.linePassword, signal: &KPasswordLineEdit::echoModeChanged, context: q, slot: [this]() {
62 toggleEchoMode();
63 });
64
65 QObject::connect(sender: ui.linePassword, signal: &KPasswordLineEdit::passwordChanged, context: q, slot: [this]() {
66 passwordChanged();
67 });
68 QObject::connect(sender: ui.lineVerifyPassword, signal: &QLineEdit::textChanged, context: q, slot: [this]() {
69 passwordChanged();
70 });
71
72 defaultBackgroundColor = q->palette().color(cg: QPalette::Active, cr: QPalette::Base);
73 backgroundWarningColor = defaultBackgroundColor;
74
75 passwordChanged();
76}
77
78void KNewPasswordWidgetPrivate::passwordChanged()
79{
80 const QString password = ui.linePassword->password();
81 const QString verification = ui.lineVerifyPassword->text();
82 const bool match = (password == verification);
83 const bool partialMatch = password.startsWith(s: verification);
84 const int minPasswordLength = q->minimumPasswordLength();
85
86 QPalette palette = q->palette();
87 palette.setColor(acg: QPalette::Active, acr: QPalette::Base, acolor: (match || partialMatch) ? defaultBackgroundColor : backgroundWarningColor);
88 ui.lineVerifyPassword->setPalette(palette);
89
90 // Password strength calculator
91 int pwstrength =
92 (20 * ui.linePassword->password().length() + 80 * effectivePasswordLength(password: ui.linePassword->password())) / qMax(a: reasonablePasswordLength, b: 2);
93 ui.strengthBar->setValue(qBound(min: 0, val: pwstrength, max: 100));
94
95 // update the current password status
96 if (match || ui.lineVerifyPassword->isHidden()) {
97 if (!q->allowEmptyPasswords() && ui.linePassword->password().isEmpty()) {
98 updatePasswordStatus(status: KNewPasswordWidget::EmptyPasswordNotAllowed);
99 } else if (ui.linePassword->password().length() < minPasswordLength) {
100 updatePasswordStatus(status: KNewPasswordWidget::PasswordTooShort);
101 } else if (ui.strengthBar && ui.strengthBar->value() < passwordStrengthWarningLevel) {
102 updatePasswordStatus(status: KNewPasswordWidget::WeakPassword);
103 } else {
104 updatePasswordStatus(status: KNewPasswordWidget::StrongPassword);
105 }
106 } else {
107 updatePasswordStatus(status: KNewPasswordWidget::PasswordNotVerified);
108 }
109}
110
111void KNewPasswordWidgetPrivate::toggleEchoMode()
112{
113 if (ui.linePassword->lineEdit()->echoMode() == QLineEdit::Normal) {
114 ui.lineVerifyPassword->hide();
115 ui.labelVerifyPassword->hide();
116 } else if (ui.linePassword->lineEdit()->echoMode() == QLineEdit::Password) {
117 ui.lineVerifyPassword->show();
118 ui.labelVerifyPassword->show();
119 }
120 passwordChanged();
121}
122
123int KNewPasswordWidgetPrivate::effectivePasswordLength(QStringView password)
124{
125 enum Category {
126 Digit,
127 Upper,
128 Vowel,
129 Consonant,
130 Special,
131 };
132
133 Category previousCategory = Vowel;
134 static const QLatin1String vowels("aeiou");
135 int count = 0;
136
137 const int len = password.length();
138 for (int i = 0; i < len; ++i) {
139 const QChar currentChar = password.at(n: i);
140 if (!password.left(n: i).contains(c: currentChar)) {
141 Category currentCategory;
142 switch (currentChar.category()) {
143 case QChar::Letter_Uppercase:
144 currentCategory = Upper;
145 break;
146 case QChar::Letter_Lowercase:
147 if (vowels.contains(c: currentChar)) {
148 currentCategory = Vowel;
149 } else {
150 currentCategory = Consonant;
151 }
152 break;
153 case QChar::Number_DecimalDigit:
154 currentCategory = Digit;
155 break;
156 default:
157 currentCategory = Special;
158 break;
159 }
160 switch (currentCategory) {
161 case Vowel:
162 if (previousCategory != Consonant) {
163 ++count;
164 }
165 break;
166 case Consonant:
167 if (previousCategory != Vowel) {
168 ++count;
169 }
170 break;
171 default:
172 if (previousCategory != currentCategory) {
173 ++count;
174 }
175 break;
176 }
177 previousCategory = currentCategory;
178 }
179 }
180 return count;
181}
182
183void KNewPasswordWidgetPrivate::updatePasswordStatus(KNewPasswordWidget::PasswordStatus status)
184{
185 if (passwordStatus == status) {
186 return;
187 }
188
189 passwordStatus = status;
190 Q_EMIT q->passwordStatusChanged();
191}
192
193KNewPasswordWidget::KNewPasswordWidget(QWidget *parent)
194 : QWidget(parent)
195 , d(new KNewPasswordWidgetPrivate(this))
196{
197 d->init();
198}
199
200KNewPasswordWidget::~KNewPasswordWidget() = default;
201
202KNewPasswordWidget::PasswordStatus KNewPasswordWidget::passwordStatus() const
203{
204 return d->passwordStatus;
205}
206
207bool KNewPasswordWidget::allowEmptyPasswords() const
208{
209 return d->minimumPasswordLength == 0;
210}
211
212int KNewPasswordWidget::minimumPasswordLength() const
213{
214 return d->minimumPasswordLength;
215}
216
217int KNewPasswordWidget::maximumPasswordLength() const
218{
219 return d->ui.linePassword->lineEdit()->maxLength();
220}
221
222int KNewPasswordWidget::reasonablePasswordLength() const
223{
224 return d->reasonablePasswordLength;
225}
226
227int KNewPasswordWidget::passwordStrengthWarningLevel() const
228{
229 return d->passwordStrengthWarningLevel;
230}
231
232QColor KNewPasswordWidget::backgroundWarningColor() const
233{
234 return d->backgroundWarningColor;
235}
236
237bool KNewPasswordWidget::isPasswordStrengthMeterVisible() const
238{
239 return d->ui.labelStrengthMeter->isVisible() && d->ui.strengthBar->isVisible();
240}
241
242#if KWIDGETSADDONS_ENABLE_DEPRECATED_SINCE(6, 0)
243#pragma GCC diagnostic push
244#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
245bool KNewPasswordWidget::isRevealPasswordAvailable() const
246{
247 return d->ui.linePassword->isRevealPasswordAvailable();
248}
249#pragma GCC diagnostic pop
250#endif
251
252KPassword::RevealMode KNewPasswordWidget::revealPasswordMode() const
253{
254 return d->ui.linePassword->revealPasswordMode();
255}
256
257QString KNewPasswordWidget::password() const
258{
259 return d->ui.linePassword->password();
260}
261
262void KNewPasswordWidget::setAllowEmptyPasswords(bool allowed)
263{
264 setMinimumPasswordLength(allowed ? 0 : 1);
265 d->passwordChanged();
266}
267
268void KNewPasswordWidget::setMinimumPasswordLength(int minLength)
269{
270 d->minimumPasswordLength = minLength;
271 d->passwordChanged();
272}
273
274void KNewPasswordWidget::setMaximumPasswordLength(int maxLength)
275{
276 if (maxLength < minimumPasswordLength()) {
277 maxLength = minimumPasswordLength();
278 }
279
280 d->ui.linePassword->lineEdit()->setMaxLength(maxLength);
281 d->ui.lineVerifyPassword->setMaxLength(maxLength);
282}
283
284// reasonable password length code contributed by Steffen Mthing
285
286void KNewPasswordWidget::setReasonablePasswordLength(int reasonableLength)
287{
288 d->reasonablePasswordLength = qBound(min: 1, val: reasonableLength, max: maximumPasswordLength());
289}
290
291void KNewPasswordWidget::setPasswordStrengthWarningLevel(int warningLevel)
292{
293 d->passwordStrengthWarningLevel = qBound(min: 0, val: warningLevel, max: 99);
294}
295
296void KNewPasswordWidget::setBackgroundWarningColor(const QColor &color)
297{
298 d->backgroundWarningColor = color;
299 update();
300}
301
302void KNewPasswordWidget::setPasswordStrengthMeterVisible(bool visible)
303{
304 d->ui.labelStrengthMeter->setVisible(visible);
305 d->ui.strengthBar->setVisible(visible);
306}
307
308#if KWIDGETSADDONS_ENABLE_DEPRECATED_SINCE(6, 0)
309#pragma GCC diagnostic push
310#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
311void KNewPasswordWidget::setRevealPasswordAvailable(bool reveal)
312{
313 d->ui.linePassword->setRevealPasswordAvailable(reveal);
314}
315#pragma GCC diagnostic pop
316#endif
317
318void KNewPasswordWidget::setRevealPasswordMode(KPassword::RevealMode revealPasswordMode)
319{
320 d->ui.linePassword->setRevealPasswordMode(revealPasswordMode);
321}
322
323#include "moc_knewpasswordwidget.cpp"
324

source code of kwidgetsaddons/src/knewpasswordwidget.cpp