Merge remote-tracking branch 'origin/improve-settings-sanitization'

This commit is contained in:
Adriaan de Groot 2018-06-14 07:34:53 -04:00
commit 96cb42414c
16 changed files with 374 additions and 59 deletions

View File

@ -335,6 +335,8 @@ CalamaresApplication::initView()
connect( m_moduleManager, &Calamares::ModuleManager::modulesLoaded, connect( m_moduleManager, &Calamares::ModuleManager::modulesLoaded,
this, &CalamaresApplication::initViewSteps ); this, &CalamaresApplication::initViewSteps );
connect( m_moduleManager, &Calamares::ModuleManager::modulesFailed,
this, &CalamaresApplication::initFailed );
m_moduleManager->loadModules(); m_moduleManager->loadModules();
@ -356,6 +358,12 @@ CalamaresApplication::initViewSteps()
cDebug() << "STARTUP: Window now visible and ProgressTreeView populated"; cDebug() << "STARTUP: Window now visible and ProgressTreeView populated";
} }
void
CalamaresApplication::initFailed(const QStringList& l)
{
cError() << "STARTUP: failed modules are" << l;
m_mainwindow->show();
}
void void
CalamaresApplication::initJobQueue() CalamaresApplication::initJobQueue()

View File

@ -70,6 +70,7 @@ public:
private slots: private slots:
void initView(); void initView();
void initViewSteps(); void initViewSteps();
void initFailed( const QStringList& l );
private: private:
void initQmlPath(); void initQmlPath();

View File

@ -30,6 +30,32 @@
#include <yaml-cpp/yaml.h> #include <yaml-cpp/yaml.h>
/** Helper function to grab a QString out of the config, and to warn if not present. */
static QString
requireString( const YAML::Node& config, const char* key )
{
if ( config[ key ] )
return QString::fromStdString( config[ key ].as< std::string >() );
else
{
cWarning() << "Required settings.conf key" << key << "is missing.";
return QString();
}
}
/** Helper function to grab a bool out of the config, and to warn if not present. */
static bool
requireBool( const YAML::Node& config, const char* key, bool d )
{
if ( config[ key ] )
return config[ key ].as< bool >();
else
{
cWarning() << "Required settings.conf key" << key << "is missing.";
return d;
}
}
namespace Calamares namespace Calamares
{ {
@ -41,7 +67,6 @@ Settings::instance()
return s_instance; return s_instance;
} }
Settings::Settings( const QString& settingsFilePath, Settings::Settings( const QString& settingsFilePath,
bool debugMode, bool debugMode,
QObject* parent ) QObject* parent )
@ -148,11 +173,9 @@ Settings::Settings( const QString& settingsFilePath,
} }
} }
m_brandingComponentName = QString::fromStdString( config[ "branding" ] m_brandingComponentName = requireString( config, "branding" );
.as< std::string >() ); m_promptInstall = requireBool( config, "prompt-install", false );
m_promptInstall = config[ "prompt-install" ].as< bool >(); m_doChroot = requireBool( config, "dont-chroot", true );
m_doChroot = config[ "dont-chroot" ] ? !config[ "dont-chroot" ].as< bool >() : true;
} }
catch ( YAML::Exception& e ) catch ( YAML::Exception& e )
{ {
@ -175,7 +198,7 @@ Settings::modulesSearchPaths() const
} }
QList<QMap<QString, QString> > Settings::InstanceDescriptionList
Settings::customModuleInstances() const Settings::customModuleInstances() const
{ {
return m_customModuleInstances; return m_customModuleInstances;

View File

@ -43,7 +43,9 @@ public:
QStringList modulesSearchPaths() const; QStringList modulesSearchPaths() const;
QList< QMap< QString, QString > > customModuleInstances() const; using InstanceDescription = QMap< QString, QString >;
using InstanceDescriptionList = QList< InstanceDescription >;
InstanceDescriptionList customModuleInstances() const;
QList< QPair< ModuleAction, QStringList > > modulesSequence() const; QList< QPair< ModuleAction, QStringList > > modulesSequence() const;
@ -60,7 +62,7 @@ private:
QStringList m_modulesSearchPaths; QStringList m_modulesSearchPaths;
QList< QMap< QString, QString > > m_customModuleInstances; InstanceDescriptionList m_customModuleInstances;
QList< QPair< ModuleAction, QStringList > > m_modulesSequence; QList< QPair< ModuleAction, QStringList > > m_modulesSequence;
QString m_brandingComponentName; QString m_brandingComponentName;

View File

@ -15,6 +15,7 @@ set( calamaresui_SOURCES
utils/qjsonitem.cpp utils/qjsonitem.cpp
viewpages/AbstractPage.cpp viewpages/AbstractPage.cpp
viewpages/BlankViewStep.cpp
viewpages/ViewStep.cpp viewpages/ViewStep.cpp
widgets/ClickableLabel.cpp widgets/ClickableLabel.cpp

View File

@ -20,6 +20,7 @@
#include "ViewManager.h" #include "ViewManager.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "viewpages/BlankViewStep.h"
#include "viewpages/ViewStep.h" #include "viewpages/ViewStep.h"
#include "ExecutionViewStep.h" #include "ExecutionViewStep.h"
#include "JobQueue.h" #include "JobQueue.h"
@ -172,6 +173,27 @@ ViewManager::onInstallationFailed( const QString& message, const QString& detail
} }
void
ViewManager::onInitFailed( const QStringList& modules)
{
QString title( tr( "Calamares Initialization Failed" ) );
QString description( tr( "%1 can not be installed. Calamares was unable to load all of the configured modules. This is a problem with the way Calamares is being used by the distribution." ) );
QString detailString;
if ( modules.count() > 0 )
{
description.append( tr( "<br/>The following modules could not be loaded:" ) );
QStringList details;
details << QLatin1Literal("<ul>");
for( const auto& m : modules )
details << QLatin1Literal("<li>") << m << QLatin1Literal("</li>");
details << QLatin1Literal("</ul>");
detailString = details.join( QString() );
}
insertViewStep( 0, new BlankViewStep( title, description.arg( *Calamares::Branding::ShortProductName ), detailString ) );
}
ViewStepList ViewStepList
ViewManager::viewSteps() const ViewManager::viewSteps() const
{ {

View File

@ -117,6 +117,12 @@ public slots:
*/ */
void onInstallationFailed( const QString& message, const QString& details ); void onInstallationFailed( const QString& message, const QString& details );
/** @brief Replaces the stack with a view step stating that initialization failed.
*
* @param modules a list of failed modules.
*/
void onInitFailed( const QStringList& modules );
signals: signals:
void currentStepChanged(); void currentStepChanged();
void enlarge( QSize enlarge ) const; // See ViewStep::enlarge() void enlarge( QSize enlarge ) const; // See ViewStep::enlarge()

View File

@ -154,12 +154,33 @@ ModuleManager::moduleInstance( const QString& instanceKey )
} }
/**
* @brief Search a list of instance descriptions for one matching @p module and @p id
*
* @return -1 on failure, otherwise index of the instance that matches.
*/
static int findCustomInstance( const Settings::InstanceDescriptionList& customInstances,
const QString& module,
const QString& id )
{
for ( int i = 0; i < customInstances.count(); ++i )
{
const auto& thisInstance = customInstances[ i ];
if ( thisInstance.value( "module" ) == module &&
thisInstance.value( "id" ) == id )
return i;
}
return -1;
}
void void
ModuleManager::loadModules() ModuleManager::loadModules()
{ {
QTimer::singleShot( 0, this, [ this ]() QTimer::singleShot( 0, this, [ this ]()
{ {
QList< QMap< QString, QString > > customInstances = QStringList failedModules;
Settings::InstanceDescriptionList customInstances =
Settings::instance()->customModuleInstances(); Settings::instance()->customModuleInstances();
const auto modulesSequence = Settings::instance()->modulesSequence(); const auto modulesSequence = Settings::instance()->modulesSequence();
@ -177,10 +198,9 @@ ModuleManager::loadModules()
if ( moduleEntrySplit.length() < 1 || if ( moduleEntrySplit.length() < 1 ||
moduleEntrySplit.length() > 2 ) moduleEntrySplit.length() > 2 )
{ {
cError() << "Wrong module entry format for module" << moduleEntry << '.'; cError() << "Wrong module entry format for module" << moduleEntry;
cError() << "Calamares will now quit."; failedModules.append( moduleEntry );
qApp->exit( 1 ); continue;
return;
} }
moduleName = moduleEntrySplit.first(); moduleName = moduleEntrySplit.first();
instanceId = moduleEntrySplit.last(); instanceId = moduleEntrySplit.last();
@ -191,37 +211,21 @@ ModuleManager::loadModules()
{ {
cError() << "Module" << moduleName << "not found in module search paths." cError() << "Module" << moduleName << "not found in module search paths."
<< Logger::DebugList( m_paths ); << Logger::DebugList( m_paths );
cError() << "Calamares will now quit."; failedModules.append( moduleName );
qApp->exit( 1 ); continue;
return;
} }
auto findCustomInstance =
[ customInstances ]( const QString& module,
const QString& id) -> int
{
for ( int i = 0; i < customInstances.count(); ++i )
{
auto thisInstance = customInstances[ i ];
if ( thisInstance.value( "module" ) == module &&
thisInstance.value( "id" ) == id )
return i;
}
return -1;
};
if ( moduleName != instanceId ) //means this is a custom instance if ( moduleName != instanceId ) //means this is a custom instance
{ {
if ( findCustomInstance( moduleName, instanceId ) > -1 ) if ( int found = findCustomInstance( customInstances, moduleName, instanceId ) > -1 )
{ {
configFileName = customInstances[ findCustomInstance( moduleName, instanceId ) ].value( "config" ); configFileName = customInstances[ found ].value( "config" );
} }
else //ought to be a custom instance, but cannot find instance entry else //ought to be a custom instance, but cannot find instance entry
{ {
cError() << "Custom instance" << moduleEntry << "not found in custom instances section."; cError() << "Custom instance" << moduleEntry << "not found in custom instances section.";
cError() << "Calamares will now quit."; failedModules.append( moduleEntry );
qApp->exit( 1 ); continue;
return;
} }
} }
@ -241,10 +245,9 @@ ModuleManager::loadModules()
m_loadedModulesByInstanceKey.value( instanceKey, nullptr ); m_loadedModulesByInstanceKey.value( instanceKey, nullptr );
if ( thisModule && !thisModule->isLoaded() ) if ( thisModule && !thisModule->isLoaded() )
{ {
cError() << "Module" << instanceKey << "exists but not loaded." cError() << "Module" << instanceKey << "exists but not loaded.";
<< "\nCalamares will now quit."; failedModules.append( instanceKey );
qApp->exit( 1 ); continue;
return;
} }
if ( thisModule && thisModule->isLoaded() ) if ( thisModule && thisModule->isLoaded() )
@ -260,8 +263,8 @@ ModuleManager::loadModules()
m_moduleDirectoriesByModuleName.value( moduleName ) ); m_moduleDirectoriesByModuleName.value( moduleName ) );
if ( !thisModule ) if ( !thisModule )
{ {
cWarning() << "Module" << instanceKey << "cannot be created from descriptor."; cError() << "Module" << instanceKey << "cannot be created from descriptor" << configFileName;
Q_ASSERT( thisModule ); failedModules.append( instanceKey );
continue; continue;
} }
// If it's a ViewModule, it also appends the ViewStep to the ViewManager. // If it's a ViewModule, it also appends the ViewStep to the ViewManager.
@ -269,8 +272,8 @@ ModuleManager::loadModules()
m_loadedModulesByInstanceKey.insert( instanceKey, thisModule ); m_loadedModulesByInstanceKey.insert( instanceKey, thisModule );
if ( !thisModule->isLoaded() ) if ( !thisModule->isLoaded() )
{ {
cWarning() << "Module" << moduleName << "loading FAILED"; cError() << "Module" << instanceKey << "loading FAILED.";
Q_ASSERT( thisModule->isLoaded() ); failedModules.append( instanceKey );
continue; continue;
} }
} }
@ -292,6 +295,12 @@ ModuleManager::loadModules()
} }
} }
} }
if ( !failedModules.isEmpty() )
{
ViewManager::instance()->onInitFailed( failedModules );
emit modulesFailed( failedModules );
}
else
emit modulesLoaded(); emit modulesLoaded();
} ); } );
} }

View File

@ -82,7 +82,8 @@ public:
signals: signals:
void initDone(); void initDone();
void modulesLoaded(); void modulesLoaded(); /// All of the modules were loaded successfully
void modulesFailed( QStringList ); /// .. or not
private slots: private slots:
void doInit(); void doInit();

View File

@ -52,27 +52,32 @@ ViewModule::loadSelf()
PluginFactory* pf = qobject_cast< PluginFactory* >( m_loader->instance() ); PluginFactory* pf = qobject_cast< PluginFactory* >( m_loader->instance() );
if ( !pf ) if ( !pf )
{ {
cDebug() << Q_FUNC_INFO << "No factory:" << m_loader->errorString(); cWarning() << Q_FUNC_INFO << "No factory:" << m_loader->errorString();
return; return;
} }
m_viewStep = pf->create< Calamares::ViewStep >(); m_viewStep = pf->create< Calamares::ViewStep >();
if ( !m_viewStep ) if ( !m_viewStep )
{ {
cDebug() << Q_FUNC_INFO << "create() failed" << m_loader->errorString(); cWarning() << Q_FUNC_INFO << "create() failed" << m_loader->errorString();
return; return;
} }
// cDebug() << "ViewModule loading self for instance" << instanceKey() }
// << "\nViewModule at address" << this
// << "\nCalamares::PluginFactory at address" << pf
// << "\nViewStep at address" << m_viewStep;
// TODO: allow internal view steps to be created here; they would
// have to be linked into the main application somehow.
// If any method created the view step, use it now.
if ( m_viewStep )
{
m_viewStep->setModuleInstanceKey( instanceKey() ); m_viewStep->setModuleInstanceKey( instanceKey() );
m_viewStep->setConfigurationMap( m_configurationMap ); m_viewStep->setConfigurationMap( m_configurationMap );
ViewManager::instance()->addViewStep( m_viewStep ); ViewManager::instance()->addViewStep( m_viewStep );
m_loaded = true; m_loaded = true;
cDebug() << "ViewModule" << instanceKey() << "loading complete."; cDebug() << "ViewModule" << instanceKey() << "loading complete.";
} }
else
cWarning() << Q_FUNC_INFO << "No view step was created";
} }

View File

@ -226,10 +226,20 @@ defaultFont()
} }
QFont
largeFont()
{
QFont f;
f.setPointSize( defaultFontSize() + 4 );
return f;
}
void void
setDefaultFontSize( int points ) setDefaultFontSize( int points )
{ {
s_defaultFontSize = points; s_defaultFontSize = points;
s_defaultFontHeight = 0; // Recalculate on next call to defaultFontHeight()
} }

View File

@ -115,6 +115,7 @@ UIDLLEXPORT void setDefaultFontSize( int points );
UIDLLEXPORT int defaultFontSize(); // in points UIDLLEXPORT int defaultFontSize(); // in points
UIDLLEXPORT int defaultFontHeight(); // in pixels, DPI-specific UIDLLEXPORT int defaultFontHeight(); // in pixels, DPI-specific
UIDLLEXPORT QFont defaultFont(); UIDLLEXPORT QFont defaultFont();
UIDLLEXPORT QFont largeFont();
UIDLLEXPORT QSize defaultIconSize(); UIDLLEXPORT QSize defaultIconSize();
/** /**

View File

@ -0,0 +1,118 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "BlankViewStep.h"
#include "utils/CalamaresUtilsGui.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
namespace Calamares
{
BlankViewStep::BlankViewStep( const QString& title, const QString& description, const QString& details, QObject* parent)
: Calamares::ViewStep( parent )
, m_widget( new QWidget() )
{
QBoxLayout* layout = new QVBoxLayout();
constexpr int const marginWidth = 10;
constexpr int const spacingHeight = 10;
auto* label = new QLabel( title );
label->setAlignment( Qt::AlignHCenter );
label->setFont( CalamaresUtils::largeFont() );
layout->addWidget( label );
label = new QLabel( description );
label->setWordWrap( true );
label->setMargin( marginWidth );
layout->addSpacing( spacingHeight );
layout->addWidget( label );
if ( !details.isEmpty() )
{
label = new QLabel( details );
label->setMargin( marginWidth );
layout->addSpacing( spacingHeight );
layout->addWidget( label );
}
layout->addStretch( 1 ); // Push the rest to the top
m_widget->setLayout( layout );
}
BlankViewStep::~BlankViewStep()
{
}
QString
BlankViewStep::prettyName() const
{
return tr( "Blank Page" );
}
void
BlankViewStep::back()
{
}
void
BlankViewStep::next()
{
}
bool
BlankViewStep::isBackEnabled() const
{
return false;
}
bool
BlankViewStep::isNextEnabled() const
{
return false;
}
bool
BlankViewStep::isAtBeginning() const
{
return true;
}
bool
BlankViewStep::isAtEnd() const
{
return false;
}
QWidget*
BlankViewStep::widget()
{
return m_widget;
}
Calamares::JobList
BlankViewStep::jobs() const
{
return JobList();
}
} // namespace

View File

@ -0,0 +1,65 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BLANKVIEWSTEP_H
#define BLANKVIEWSTEP_H
#include <QObject>
#include <utils/PluginFactory.h>
#include <viewpages/ViewStep.h>
class QWidget;
namespace Calamares
{
/** @brief A "blank" view step, used for error and status reporting
*
* This view step never allows navigation (forward or back); it's a trap.
* It displays a title and explanation, and optional details.
*/
class BlankViewStep : public Calamares::ViewStep
{
Q_OBJECT
public:
explicit BlankViewStep( const QString& title, const QString& description, const QString& details = QString(), QObject* parent = nullptr );
virtual ~BlankViewStep() override;
QString prettyName() const override;
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;
bool isAtBeginning() const override;
bool isAtEnd() const override;
Calamares::JobList jobs() const override;
private:
QWidget* m_widget;
};
} // namespace
#endif // BLANKVIEWSTEP_H

View File

@ -6,7 +6,7 @@ set( LIST_SKIPPED_MODULES "" )
if( BUILD_TESTING ) if( BUILD_TESTING )
add_executable( test_conf test_conf.cpp ) add_executable( test_conf test_conf.cpp )
target_link_libraries( test_conf ${YAMLCPP_LIBRARY} ) target_link_libraries( test_conf ${YAMLCPP_LIBRARY} Qt5::Core )
target_include_directories( test_conf PUBLIC ${YAMLCPP_INCLUDE_DIR} ) target_include_directories( test_conf PUBLIC ${YAMLCPP_INCLUDE_DIR} )
endif() endif()

View File

@ -21,43 +21,86 @@
* shipped with each module for correctness -- well, for parseability. * shipped with each module for correctness -- well, for parseability.
*/ */
#include <unistd.h>
#include <stdlib.h>
#include <iostream> #include <iostream>
#include <yaml-cpp/yaml.h> #include <yaml-cpp/yaml.h>
#include <QFile>
#include <QByteArray>
using std::cerr; using std::cerr;
static const char usage[] = "Usage: test_conf [-v] [-b] <file> ...\n";
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
if (argc != 2) bool verbose = false;
bool bytes = false;
int opt;
while ((opt = getopt(argc, argv, "vb")) != -1) {
switch (opt) {
case 'v':
verbose = true;
break;
case 'b':
bytes = true;
break;
default: /* '?' */
cerr << usage;
return 1;
}
}
if ( optind >= argc )
{ {
cerr << "Usage: test_conf <file.conf>\n"; cerr << usage;
return 1; return 1;
} }
const char* filename = argv[optind];
try try
{ {
YAML::Node doc = YAML::LoadFile( argv[1] ); YAML::Node doc;
if ( bytes )
{
QFile f( filename );
if ( f.open( QFile::ReadOnly | QFile::Text ) )
doc = YAML::Load( f.readAll().constData() );
}
else
doc = YAML::LoadFile( filename );
if ( doc.IsNull() ) if ( doc.IsNull() )
{ {
// Special case: empty config files are valid, // Special case: empty config files are valid,
// but aren't a map. For the example configs, // but aren't a map. For the example configs,
// this is still an error. // this is still an error.
cerr << "WARNING:" << argv[1] << '\n'; cerr << "WARNING:" << filename << '\n';
cerr << "WARNING: empty YAML\n"; cerr << "WARNING: empty YAML\n";
return 1; return 1;
} }
if ( !doc.IsMap() ) if ( !doc.IsMap() )
{ {
cerr << "WARNING:" << argv[1] << '\n'; cerr << "WARNING:" << filename << '\n';
cerr << "WARNING: not-a-YAML-map\n"; cerr << "WARNING: not-a-YAML-map\n";
return 1; return 1;
} }
if ( verbose )
{
cerr << "Keys:\n";
for ( auto i = doc.begin(); i != doc.end(); ++i )
cerr << i->first.as<std::string>() << '\n';
}
} }
catch ( YAML::Exception& e ) catch ( YAML::Exception& e )
{ {
cerr << "WARNING:" << argv[1] << '\n'; cerr << "WARNING:" << filename << '\n';
cerr << "WARNING: YAML parser error " << e.what() << '\n'; cerr << "WARNING: YAML parser error " << e.what() << '\n';
return 1; return 1;
} }