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

main
Adriaan de Groot 7 years ago
commit 96cb42414c

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

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

@ -30,6 +30,32 @@
#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
{
@ -41,7 +67,6 @@ Settings::instance()
return s_instance;
}
Settings::Settings( const QString& settingsFilePath,
bool debugMode,
QObject* parent )
@ -148,11 +173,9 @@ Settings::Settings( const QString& settingsFilePath,
}
}
m_brandingComponentName = QString::fromStdString( config[ "branding" ]
.as< std::string >() );
m_promptInstall = config[ "prompt-install" ].as< bool >();
m_doChroot = config[ "dont-chroot" ] ? !config[ "dont-chroot" ].as< bool >() : true;
m_brandingComponentName = requireString( config, "branding" );
m_promptInstall = requireBool( config, "prompt-install", false );
m_doChroot = requireBool( config, "dont-chroot", true );
}
catch ( YAML::Exception& e )
{
@ -175,7 +198,7 @@ Settings::modulesSearchPaths() const
}
QList<QMap<QString, QString> >
Settings::InstanceDescriptionList
Settings::customModuleInstances() const
{
return m_customModuleInstances;

@ -43,7 +43,9 @@ public:
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;
@ -60,7 +62,7 @@ private:
QStringList m_modulesSearchPaths;
QList< QMap< QString, QString > > m_customModuleInstances;
InstanceDescriptionList m_customModuleInstances;
QList< QPair< ModuleAction, QStringList > > m_modulesSequence;
QString m_brandingComponentName;

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

@ -20,6 +20,7 @@
#include "ViewManager.h"
#include "utils/Logger.h"
#include "viewpages/BlankViewStep.h"
#include "viewpages/ViewStep.h"
#include "ExecutionViewStep.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
ViewManager::viewSteps() const
{

@ -117,6 +117,12 @@ public slots:
*/
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:
void currentStepChanged();
void enlarge( QSize enlarge ) const; // See ViewStep::enlarge()

@ -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
ModuleManager::loadModules()
{
QTimer::singleShot( 0, this, [ this ]()
{
QList< QMap< QString, QString > > customInstances =
QStringList failedModules;
Settings::InstanceDescriptionList customInstances =
Settings::instance()->customModuleInstances();
const auto modulesSequence = Settings::instance()->modulesSequence();
@ -177,10 +198,9 @@ ModuleManager::loadModules()
if ( moduleEntrySplit.length() < 1 ||
moduleEntrySplit.length() > 2 )
{
cError() << "Wrong module entry format for module" << moduleEntry << '.';
cError() << "Calamares will now quit.";
qApp->exit( 1 );
return;
cError() << "Wrong module entry format for module" << moduleEntry;
failedModules.append( moduleEntry );
continue;
}
moduleName = moduleEntrySplit.first();
instanceId = moduleEntrySplit.last();
@ -191,37 +211,21 @@ ModuleManager::loadModules()
{
cError() << "Module" << moduleName << "not found in module search paths."
<< Logger::DebugList( m_paths );
cError() << "Calamares will now quit.";
qApp->exit( 1 );
return;
failedModules.append( moduleName );
continue;
}
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 ( 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
{
cError() << "Custom instance" << moduleEntry << "not found in custom instances section.";
cError() << "Calamares will now quit.";
qApp->exit( 1 );
return;
failedModules.append( moduleEntry );
continue;
}
}
@ -241,10 +245,9 @@ ModuleManager::loadModules()
m_loadedModulesByInstanceKey.value( instanceKey, nullptr );
if ( thisModule && !thisModule->isLoaded() )
{
cError() << "Module" << instanceKey << "exists but not loaded."
<< "\nCalamares will now quit.";
qApp->exit( 1 );
return;
cError() << "Module" << instanceKey << "exists but not loaded.";
failedModules.append( instanceKey );
continue;
}
if ( thisModule && thisModule->isLoaded() )
@ -260,8 +263,8 @@ ModuleManager::loadModules()
m_moduleDirectoriesByModuleName.value( moduleName ) );
if ( !thisModule )
{
cWarning() << "Module" << instanceKey << "cannot be created from descriptor.";
Q_ASSERT( thisModule );
cError() << "Module" << instanceKey << "cannot be created from descriptor" << configFileName;
failedModules.append( instanceKey );
continue;
}
// If it's a ViewModule, it also appends the ViewStep to the ViewManager.
@ -269,8 +272,8 @@ ModuleManager::loadModules()
m_loadedModulesByInstanceKey.insert( instanceKey, thisModule );
if ( !thisModule->isLoaded() )
{
cWarning() << "Module" << moduleName << "loading FAILED";
Q_ASSERT( thisModule->isLoaded() );
cError() << "Module" << instanceKey << "loading FAILED.";
failedModules.append( instanceKey );
continue;
}
}
@ -292,7 +295,13 @@ ModuleManager::loadModules()
}
}
}
emit modulesLoaded();
if ( !failedModules.isEmpty() )
{
ViewManager::instance()->onInitFailed( failedModules );
emit modulesFailed( failedModules );
}
else
emit modulesLoaded();
} );
}

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

@ -52,27 +52,32 @@ ViewModule::loadSelf()
PluginFactory* pf = qobject_cast< PluginFactory* >( m_loader->instance() );
if ( !pf )
{
cDebug() << Q_FUNC_INFO << "No factory:" << m_loader->errorString();
cWarning() << Q_FUNC_INFO << "No factory:" << m_loader->errorString();
return;
}
m_viewStep = pf->create< Calamares::ViewStep >();
if ( !m_viewStep )
{
cDebug() << Q_FUNC_INFO << "create() failed" << m_loader->errorString();
cWarning() << Q_FUNC_INFO << "create() failed" << m_loader->errorString();
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->setConfigurationMap( m_configurationMap );
ViewManager::instance()->addViewStep( m_viewStep );
m_loaded = true;
cDebug() << "ViewModule" << instanceKey() << "loading complete.";
}
else
cWarning() << Q_FUNC_INFO << "No view step was created";
}

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

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

@ -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

@ -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

@ -6,7 +6,7 @@ set( LIST_SKIPPED_MODULES "" )
if( BUILD_TESTING )
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} )
endif()

@ -21,43 +21,86 @@
* shipped with each module for correctness -- well, for parseability.
*/
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
#include <yaml-cpp/yaml.h>
#include <QFile>
#include <QByteArray>
using std::cerr;
static const char usage[] = "Usage: test_conf [-v] [-b] <file> ...\n";
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;
}
const char* filename = argv[optind];
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() )
{
// Special case: empty config files are valid,
// but aren't a map. For the example configs,
// this is still an error.
cerr << "WARNING:" << argv[1] << '\n';
cerr << "WARNING:" << filename << '\n';
cerr << "WARNING: empty YAML\n";
return 1;
}
if ( !doc.IsMap() )
{
cerr << "WARNING:" << argv[1] << '\n';
cerr << "WARNING:" << filename << '\n';
cerr << "WARNING: not-a-YAML-map\n";
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 )
{
cerr << "WARNING:" << argv[1] << '\n';
cerr << "WARNING:" << filename << '\n';
cerr << "WARNING: YAML parser error " << e.what() << '\n';
return 1;
}

Loading…
Cancel
Save