source: calamares/trunk/fuentes/src/modules/users/UsersPage.cpp @ 7538

Last change on this file since 7538 was 7538, checked in by kbut, 13 months ago

sync with github

File size: 14.0 KB
Line 
1/* === This file is part of Calamares - <https://github.com/calamares> ===
2 *
3 *   Copyright 2014-2017, Teo Mrnjavac <teo@kde.org>
4 *   Copyright 2017, Adriaan de Groot <groot@kde.org>
5 *
6 *   Portions from the Manjaro Installation Framework
7 *   by Roland Singer <roland@manjaro.org>
8 *   Copyright (C) 2007 Free Software Foundation, Inc.
9 *
10 *   Calamares is free software: you can redistribute it and/or modify
11 *   it under the terms of the GNU General Public License as published by
12 *   the Free Software Foundation, either version 3 of the License, or
13 *   (at your option) any later version.
14 *
15 *   Calamares is distributed in the hope that it will be useful,
16 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 *   GNU General Public License for more details.
19 *
20 *   You should have received a copy of the GNU General Public License
21 *   along with Calamares. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include "UsersPage.h"
25#include "ui_page_usersetup.h"
26#include "CreateUserJob.h"
27#include "SetPasswordJob.h"
28#include "SetHostNameJob.h"
29#include "JobQueue.h"
30#include "GlobalStorage.h"
31#include "utils/Logger.h"
32#include "utils/CalamaresUtilsGui.h"
33#include "utils/Retranslator.h"
34
35#include <QBoxLayout>
36#include <QLabel>
37#include <QLineEdit>
38#include <QRegExp>
39#include <QRegExpValidator>
40
41/** Add an error message and pixmap to a label. */
42static inline void
43labelError( QLabel* pix, QLabel* label, const QString& message )
44{
45    label->setText( message );
46    pix->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::No, CalamaresUtils::Original, label->size() ) );
47}
48
49/** Clear error, indicate OK on a label. */
50static inline void
51labelOk( QLabel* pix, QLabel* label )
52{
53    label->clear();
54    pix->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes, CalamaresUtils::Original, label->size() ) );
55}
56
57UsersPage::UsersPage( QWidget* parent )
58    : QWidget( parent )
59    , ui( new Ui::Page_UserSetup )
60    , m_readyFullName( false )
61    , m_readyUsername( false )
62    , m_readyHostname( false )
63    , m_readyPassword( false )
64    , m_readyRootPassword( false )
65    , m_writeRootPassword( true )
66{
67    ui->setupUi( this );
68
69    // Connect signals and slots
70    connect( ui->textBoxFullName, &QLineEdit::textEdited,
71             this, &UsersPage::onFullNameTextEdited );
72    connect( ui->textBoxUsername, &QLineEdit::textEdited,
73             this, &UsersPage::onUsernameTextEdited );
74    connect( ui->textBoxHostname, &QLineEdit::textEdited,
75             this, &UsersPage::onHostnameTextEdited );
76    connect( ui->textBoxUserPassword, &QLineEdit::textChanged,
77             this, &UsersPage::onPasswordTextChanged );
78    connect( ui->textBoxUserVerifiedPassword, &QLineEdit::textChanged,
79             this, &UsersPage::onPasswordTextChanged );
80    connect( ui->textBoxRootPassword, &QLineEdit::textChanged,
81             this, &UsersPage::onRootPasswordTextChanged );
82    connect( ui->textBoxVerifiedRootPassword, &QLineEdit::textChanged,
83             this, &UsersPage::onRootPasswordTextChanged );
84    connect( ui->checkBoxReusePassword, &QCheckBox::stateChanged,
85             this, [this]( int checked )
86    {
87        ui->labelChooseRootPassword->setVisible( !checked );
88        ui->labelExtraRootPassword->setVisible( !checked );
89        ui->labelRootPassword->setVisible( !checked );
90        ui->labelRootPasswordError->setVisible( !checked );
91        ui->textBoxRootPassword->setVisible( !checked );
92        ui->textBoxVerifiedRootPassword->setVisible( !checked );
93        checkReady( isReady() );
94    } );
95
96    m_customUsername = false;
97    m_customHostname = false;
98
99    setWriteRootPassword( true );
100    ui->checkBoxReusePassword->setChecked( true );
101
102    CALAMARES_RETRANSLATE( ui->retranslateUi( this ); )
103}
104
105
106UsersPage::~UsersPage()
107{
108    delete ui;
109}
110
111
112bool
113UsersPage::isReady()
114{
115    bool readyFields = m_readyFullName &&
116                       m_readyHostname &&
117                       m_readyPassword &&
118                       m_readyUsername;
119    if ( !m_writeRootPassword || ui->checkBoxReusePassword->isChecked() )
120        return readyFields;
121
122    return readyFields && m_readyRootPassword;
123}
124
125
126QList< Calamares::job_ptr >
127UsersPage::createJobs( const QStringList& defaultGroupsList )
128{
129    QList< Calamares::job_ptr > list;
130    if ( !isReady() )
131        return list;
132
133    Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
134
135    Calamares::Job* j;
136    j = new CreateUserJob( ui->textBoxUsername->text(),
137                           ui->textBoxFullName->text().isEmpty() ?
138                               ui->textBoxUsername->text() :
139                               ui->textBoxFullName->text(),
140                           ui->checkBoxAutoLogin->isChecked(),
141                           defaultGroupsList );
142    list.append( Calamares::job_ptr( j ) );
143
144    j = new SetPasswordJob( ui->textBoxUsername->text(),
145                            ui->textBoxUserPassword->text() );
146    list.append( Calamares::job_ptr( j ) );
147
148    if ( m_writeRootPassword )
149    {
150        gs->insert( "reuseRootPassword", ui->checkBoxReusePassword->isChecked() );
151        if ( ui->checkBoxReusePassword->isChecked() )
152            j = new SetPasswordJob( "root",
153                                    ui->textBoxUserPassword->text() );
154        else
155            j = new SetPasswordJob( "root",
156                                    ui->textBoxRootPassword->text() );
157        list.append( Calamares::job_ptr( j ) );
158    }
159    else
160    {
161        j = new SetPasswordJob( "root",
162                                "" ); //explicitly disable root password
163        list.append( Calamares::job_ptr( j ) );
164    }
165
166    j = new SetHostNameJob( ui->textBoxHostname->text() );
167    list.append( Calamares::job_ptr( j ) );
168
169    gs->insert( "hostname", ui->textBoxHostname->text() );
170    if ( ui->checkBoxAutoLogin->isChecked() )
171        gs->insert( "autologinUser", ui->textBoxUsername->text() );
172
173    gs->insert( "username", ui->textBoxUsername->text() );
174    gs->insert( "password", CalamaresUtils::obscure( ui->textBoxUserPassword->text() ) );
175
176    return list;
177}
178
179
180void
181UsersPage::onActivate()
182{
183    ui->textBoxFullName->setFocus();
184}
185
186
187void
188UsersPage::setWriteRootPassword( bool write )
189{
190    ui->checkBoxReusePassword->setVisible( write );
191    m_writeRootPassword = write;
192}
193
194
195void
196UsersPage::onFullNameTextEdited( const QString& textRef )
197{
198    if ( textRef.isEmpty() )
199    {
200        ui->labelFullNameError->clear();
201        ui->labelFullName->clear();
202        if ( !m_customUsername )
203            ui->textBoxUsername->clear();
204        if ( !m_customHostname )
205            ui->textBoxHostname->clear();
206        m_readyFullName = false;
207    }
208    else
209    {
210        ui->labelFullName->setPixmap( CalamaresUtils::defaultPixmap( CalamaresUtils::Yes,
211                                                                     CalamaresUtils::Original,
212                                                                     ui->labelFullName->size() ) );
213        m_readyFullName = true;
214        fillSuggestions();
215    }
216    checkReady( isReady() );
217}
218
219
220void
221UsersPage::fillSuggestions()
222{
223    QString fullName = ui->textBoxFullName->text();
224    QRegExp rx( "[^a-zA-Z0-9 ]", Qt::CaseInsensitive );
225    QString cleanName = CalamaresUtils::removeDiacritics( fullName )
226                        .toLower().replace( rx, " " ).simplified();
227    QStringList cleanParts = cleanName.split( ' ' );
228
229    if ( !m_customUsername )
230    {
231        if ( !cleanParts.isEmpty() && !cleanParts.first().isEmpty() )
232        {
233            QString usernameSuggestion = cleanParts.first();
234            for ( int i = 1; i < cleanParts.length(); ++i )
235            {
236                if ( !cleanParts.value( i ).isEmpty() )
237                    usernameSuggestion.append( cleanParts.value( i ).at( 0 ) );
238            }
239            if ( USERNAME_RX.indexIn( usernameSuggestion ) != -1 )
240            {
241                ui->textBoxUsername->setText( usernameSuggestion );
242                validateUsernameText( usernameSuggestion );
243                m_customUsername = false;
244            }
245        }
246    }
247
248    if ( !m_customHostname )
249    {
250        if ( !cleanParts.isEmpty() && !cleanParts.first().isEmpty() )
251        {
252            QString hostnameSuggestion = QString( "%1-pc" ).arg( cleanParts.first() );
253            if ( HOSTNAME_RX.indexIn( hostnameSuggestion ) != -1 )
254            {
255                ui->textBoxHostname->setText( hostnameSuggestion );
256                validateHostnameText( hostnameSuggestion );
257                m_customHostname = false;
258            }
259        }
260    }
261}
262
263
264void
265UsersPage::onUsernameTextEdited( const QString& textRef )
266{
267    m_customUsername = true;
268    validateUsernameText( textRef );
269}
270
271
272void
273UsersPage::validateUsernameText( const QString& textRef )
274{
275    QString text( textRef );
276    QRegExp rx( USERNAME_RX );
277    QRegExpValidator val( rx );
278    int pos = -1;
279
280    if ( text.isEmpty() )
281    {
282        ui->labelUsernameError->clear();
283        ui->labelUsername->clear();
284        m_readyUsername = false;
285    }
286    else if ( text.length() > USERNAME_MAX_LENGTH )
287    {
288        labelError( ui->labelUsername, ui->labelUsernameError,
289                    tr( "Your username is too long." ) );
290        m_readyUsername = false;
291    }
292    else if ( val.validate( text, pos ) == QValidator::Invalid )
293    {
294        labelError( ui->labelUsername, ui->labelUsernameError,
295                    tr( "Your username contains invalid characters. Only lowercase letters and numbers are allowed." ) );
296        m_readyUsername = false;
297    }
298    else
299    {
300        labelOk( ui->labelUsername, ui->labelUsernameError );
301        m_readyUsername = true;
302    }
303
304    emit checkReady( isReady() );
305}
306
307
308void
309UsersPage::onHostnameTextEdited( const QString& textRef )
310{
311    m_customHostname = true;
312    validateHostnameText( textRef );
313}
314
315
316void
317UsersPage::validateHostnameText( const QString& textRef )
318{
319    QString text = textRef;
320    QRegExp rx( HOSTNAME_RX );
321    QRegExpValidator val( rx );
322    int pos = -1;
323
324    if ( text.isEmpty() )
325    {
326        ui->labelHostnameError->clear();
327        ui->labelHostname->clear();
328        m_readyHostname= false;
329    }
330    else if ( text.length() < HOSTNAME_MIN_LENGTH )
331    {
332        labelError( ui->labelHostname, ui->labelHostnameError,
333                    tr( "Your hostname is too short." ) );
334        m_readyHostname = false;
335    }
336    else if ( text.length() > HOSTNAME_MAX_LENGTH )
337    {
338        labelError( ui->labelHostname, ui->labelHostnameError,
339                    tr( "Your hostname is too long." ) );
340        m_readyHostname = false;
341    }
342    else if ( val.validate( text, pos ) == QValidator::Invalid )
343    {
344        labelError( ui->labelHostname, ui->labelHostnameError,
345                    tr( "Your hostname contains invalid characters. Only letters, numbers and dashes are allowed." ) );
346        m_readyHostname = false;
347    }
348    else
349    {
350        labelOk( ui->labelHostname, ui->labelHostnameError );
351        m_readyHostname = true;
352    }
353
354    emit checkReady( isReady() );
355}
356
357void
358UsersPage::onPasswordTextChanged( const QString& )
359{
360    QString pw1 = ui->textBoxUserPassword->text();
361    QString pw2 = ui->textBoxUserVerifiedPassword->text();
362
363    // TODO: 3.3: remove empty-check and leave it to passwordRequirements
364    if ( pw1.isEmpty() && pw2.isEmpty() )
365    {
366        ui->labelUserPasswordError->clear();
367        ui->labelUserPassword->clear();
368        m_readyPassword = false;
369    }
370    else if ( pw1 != pw2 )
371    {
372        labelError( ui->labelUserPassword, ui->labelUserPasswordError,
373                    tr( "Your passwords do not match!" ) );
374        m_readyPassword = false;
375    }
376    else
377    {
378        bool ok = true;
379        for ( auto pc : m_passwordChecks )
380        {
381            QString s = pc.filter( pw1 );
382            if ( !s.isEmpty() )
383            {
384                labelError( ui->labelUserPassword, ui->labelUserPasswordError, s );
385                ok = false;
386                m_readyPassword = false;
387                break;
388            }
389        }
390
391        if ( ok )
392        {
393            labelOk( ui->labelUserPassword, ui->labelUserPasswordError );
394            m_readyPassword = true;
395        }
396    }
397
398    emit checkReady( isReady() );
399}
400
401void
402UsersPage::onRootPasswordTextChanged( const QString& )
403{
404    QString pw1 = ui->textBoxRootPassword->text();
405    QString pw2 = ui->textBoxVerifiedRootPassword->text();
406
407    // TODO: 3.3: remove empty-check and leave it to passwordRequirements
408    if ( pw1.isEmpty() && pw2.isEmpty() )
409    {
410        ui->labelRootPasswordError->clear();
411        ui->labelRootPassword->clear();
412        m_readyRootPassword = false;
413    }
414    else if ( pw1 != pw2 )
415    {
416        labelError( ui->labelRootPassword, ui->labelRootPasswordError,
417                    tr( "Your passwords do not match!" ) );
418        m_readyRootPassword = false;
419    }
420    else
421    {
422        bool ok = true;
423        for ( auto pc : m_passwordChecks )
424        {
425            QString s = pc.filter( pw1 );
426            if ( !s.isEmpty() )
427            {
428                labelError( ui->labelRootPassword, ui->labelRootPasswordError, s );
429                ok = false;
430                m_readyRootPassword = false;
431                break;
432            }
433        }
434
435        if ( ok )
436        {
437            labelOk( ui->labelRootPassword, ui->labelRootPasswordError );
438            m_readyRootPassword = true;
439        }
440    }
441
442    emit checkReady( isReady() );
443}
444
445
446void
447UsersPage::setAutologinDefault( bool checked )
448{
449    ui->checkBoxAutoLogin->setChecked( checked );
450    emit checkReady( isReady() );
451}
452
453void
454UsersPage::setReusePasswordDefault( bool checked )
455{
456    ui->checkBoxReusePassword->setChecked( checked );
457    emit checkReady( isReady() );
458}
459
460void
461UsersPage::addPasswordCheck( const QString& key, const QVariant& value )
462{
463    if ( key == "minLength" )
464    {
465        add_check_minLength( m_passwordChecks, value );
466    }
467    else if ( key == "maxLength" )
468    {
469        add_check_maxLength( m_passwordChecks, value );
470    }
471#ifdef CHECK_PWQUALITY
472    else if ( key == "libpwquality" )
473    {
474        add_check_libpwquality( m_passwordChecks, value );
475    }
476#endif
477    else
478        cWarning() << "Unknown password-check key" << key;
479}
Note: See TracBrowser for help on using the repository browser.