32 Commits

Author SHA1 Message Date
Adriaan de Groot
f01b4ed2f8 docs: drop mention of IRC, prefer Matrix for communication 2023-08-27 22:47:08 +02:00
Adriaan de Groot
ea762b0945 Merge pull request #26 from nmschulte/nms/fix-username-prompt
add usermod command about default username
2023-08-06 15:24:10 +02:00
Nathan Schulte
cae8acb4a4 custom username also changes the fullname/comment 2023-08-03 14:52:59 -05:00
Nathan Schulte
ae204d0108 change username before password 2023-07-19 20:49:48 -05:00
Nathan Schulte
a2fae600c4 add usermod command about default username
cmdUsermod supports changing the default username

in case any extra initial state needs updated, cmdUsermod can be amended to run additional commands
2023-07-11 01:29:32 -05:00
Adriaan de Groot
349acad491 Merge pull request #23 from Undef-a/calamares
mobile: Reflow confirm page to fit smaller screens
2023-07-01 22:58:31 +02:00
Adriaan de Groot
9b4c4876bc Merge pull request #24 from ollieparanoid/6-char-pass
mobile: change min pass length to 6 digits
2023-07-01 22:58:18 +02:00
Oliver Smith
cc02baed9a mobile: change min pass length to 6 digits
Require at least 6 characters instead of 8 and mention that digits can
also be used. Most users set something like a 6 digit number on their
lockscreen, it is even what we have by default in the postmarketOS
images when you don't use the installer. So requiring 8 in the installer
does not make sense.

Fixes: https://gitlab.com/postmarketOS/postmarketos-ondev/-/issues/62
2023-06-05 17:49:42 +02:00
undef
dfab089938 mobile: Reflow confirm page to fit smaller screens
When testing on the Librem5 this page hangs slightly off the screen,
causing the "Install" button to be only just selectable on the bottom
edge.

This slightly reflows the page such that on these smaller screens the
button is still in a selectable location.
2023-05-20 09:48:22 +00:00
Adriaan de Groot
2dd9a7ba8a Merge pull request #22 from nmschulte/nms/username-prompt-origin
[mobile] add username prompt, reservedUsernames config, minor cleanup
2023-02-24 22:34:35 +01:00
Nathan Schulte
82f31f3cd9 use reservedUsernames config 2023-02-20 01:24:40 -06:00
Nathan Schulte
c5b01a574d add reservedUsernames config 2023-02-20 01:24:40 -06:00
Nathan Schulte
bc4789d57f add username input field to user_pass module 2023-02-20 01:24:40 -06:00
Nathan Schulte
0962b98494 add username validation routine to mobile module 2022-09-22 00:55:24 -05:00
Nathan Schulte
108476c025 fix typo and cleanup code style 2022-09-22 00:55:24 -05:00
Adriaan de Groot
04a1bc9e2c Merge pull request #21 from demmm/calamares
[KaOS Branding] add navigation & sidebar examples
2022-08-07 14:19:14 +02:00
demmm
bff5e485f4 [KaOS Branding] add navigation & sidebar examples
both set to QML
navigation set vertical, progress set horizontal
include some updates
2022-08-04 18:55:01 +02:00
Adriaan de Groot
3c838436c2 [mobile] Increase timeout for PartitionJob: 10 min 2022-07-16 08:01:53 +02:00
Adriaan de Groot
644c9cf4f3 [unpackfsc] Improve tar support
Enable support for tar option in unpackfsc module for all compression algorithms
2022-07-16 08:00:33 +02:00
Oliver Smith
dcaa378ddd [mobile] Increase timeout for PartitionJob: 10 min
Increase the PartitionJob's timeout from 2 min to 10 min, as there was
an report of hitting the timeout with the PinePhone Pro's 128 GiB eMMC.

Related: https://gitlab.com/postmarketOS/pmaports/-/merge_requests/3280#note_1021536268
2022-07-11 08:25:55 +02:00
sravanpannala
14fd23dcef remove z option from tar so that it works with all 2022-07-06 17:56:28 -04:00
Adriaan de Groot
f4bc7052e0 [unpackfsc] Add (untested) tarball support 2022-05-17 12:30:28 +02:00
Adriaan de Groot
4f0f48d99d [unpackfsc] Unsquash is implemented 2022-05-17 12:21:52 +02:00
Adriaan de Groot
1344880f2e [unpackfsc] Add fsarchiver "fs" mode
- Add "restfs" suppotr for fsarchiver
- Apply coding style from Calamares
2022-05-17 12:12:30 +02:00
Adriaan de Groot
5704e146a0 Changes: pre-release housekeeping 2021-11-16 13:10:36 +01:00
Adriaan de Groot
85eb434b57 Merge branch 'fsarchiver' into calamares 2021-11-16 13:07:14 +01:00
Adriaan de Groot
4b2ea61aa3 [unpackfs] Document that fsarchiver uses savedir/restdir 2021-11-16 13:04:54 +01:00
Adriaan de Groot
39d25eef1c [unpackfsc] Reduce number of status updates in fsarchiver 2021-11-16 13:00:00 +01:00
Adriaan de Groot
e82d32fe66 [unpackfsc] Use restdir instead of restfs 2021-11-16 12:58:17 +01:00
Adriaan de Groot
a50ab49c22 [unpackfsc] Add test-configs for fsarchiver-unpack 2021-11-16 12:52:34 +01:00
Adriaan de Groot
a66ab99ce8 [unpackfsc] Report fsarchiver progress, too 2021-11-16 12:24:33 +01:00
Adriaan de Groot
c3170a9dfb Changes: post-release housekeeping 2021-11-16 12:12:21 +01:00
38 changed files with 934 additions and 136 deletions

25
CHANGES
View File

@@ -6,6 +6,31 @@ This is the changelog for Calamares-Extensions. For each release, the major
changes and contributors are listed. Note that Calamares-Extensions does not changes and contributors are listed. Note that Calamares-Extensions does not
have a historical changelog -- this log starts with version 1.0.0. have a historical changelog -- this log starts with version 1.0.0.
# 1.2.1 (2021-11-16)
The 1.2.0 release had no release-notes for that version, and failed to
credit Anke and Otus.
Changes and new modules in this release:
- *unpackfsc* can use `fsarchiver` and unpack that, instead of squashfs;
a distro might choose one tool or the other. Currently, only *savedir* /
*restdir* mode (i.e. directories, not block-devices) are supported.
# 1.2.0 (2021-11-16)
This release contains contributions from (alphabetically by first name):
- Anke Boersma
- Otus9051
Changes and new modules in this release:
- *refind* is a new module that installs the rEFInd bootloader. It can
be used instead of the *bootloader* module from core Calamares. (Thanks Anke)
- *unpackfsc* is a new module that uses `unsquashfs` directly. This may
be faster or more convenient than the *unpackfs* module from core Calamares.
The configuration file supports only one entry, but is otherwise easy to
adapt from an existing `unpackfs.conf`.
# 1.1.2 (2021-05-14) # 1.1.2 (2021-05-14)
This release contains contributions from (alphabetically by first name): This release contains contributions from (alphabetically by first name):

View File

@@ -40,7 +40,7 @@
# #
cmake_minimum_required(VERSION 3.3 FATAL_ERROR) cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
set( CALAMARES_EXTENSIONS_VERSION 1.2.0 ) set( CALAMARES_EXTENSIONS_VERSION 1.2.1 )
include( ${CMAKE_CURRENT_LIST_DIR}/CMakeModules/ExtendedVersion.cmake ) include( ${CMAKE_CURRENT_LIST_DIR}/CMakeModules/ExtendedVersion.cmake )
if ( CMAKE_SCRIPT_MODE_FILE ) if ( CMAKE_SCRIPT_MODE_FILE )

View File

@@ -29,15 +29,12 @@ rules of decent behavior in both communities are pretty much the same).
GitHub Issues are **one** place for discussing Calamares and its extensions if there are concrete GitHub Issues are **one** place for discussing Calamares and its extensions if there are concrete
problems or a new feature to discuss. problems or a new feature to discuss.
Issues are not a help channel.
Visit Matrix for help with configuration or compilation.
Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/) Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/)
room, `#calamares:kde.org`. The conversation is bridged with IRC room, `#calamares:kde.org`. Responsiveness is best during the day
on [Libera.Chat](https://libera.chat/). in Europe, but feel free to idle.
Responsiveness is best during the day
in Europe, but feel free to idle. If you use IRC, **DO NOT** ask-and-leave. Keep
that chat window open because it can easily take a few hours for
someone to notice a message.
Matrix is persistent, and we'll see your message eventually. Matrix is persistent, and we'll see your message eventually.
* [![Join us on Matrix](https://img.shields.io/badge/Matrix-%23calamares:kde.org-blue)](https://webchat.kde.org/#/room/%23calamares:kde.org) * [![Join us on Matrix](https://img.shields.io/badge/Matrix-%23calamares:kde.org-blue)](https://webchat.kde.org/#/room/%23calamares:kde.org)
* [![Chat on IRC](https://img.shields.io/badge/IRC-Libera.Chat%20%23calamares-green)](https://kiwiirc.com/client/irc.libera.chat/#calamares)

View File

@@ -40,7 +40,10 @@ and documentation for the framework that Calamares ships with.
(probably moreso than the default slideshow). (probably moreso than the default slideshow).
- [`kaos_branding/`](branding/kaos_branding/branding.desc) - [`kaos_branding/`](branding/kaos_branding/branding.desc)
is a copy of the KaOS branding component, which is a copy of the KaOS branding component, which
has translations and a bunch of fancy graphics. has translations and a bunch of fancy graphics for the
slideshow. Plus it includes examples of using different
QML options for a vertical navigation bar and horizontal
sidebar.
- [`samegame/` ](branding/default/branding.desc) - [`samegame/` ](branding/default/branding.desc)
is a copy of the Qt Company "Same Game" QML demo. It is a copy of the Qt Company "Same Game" QML demo. It
shows that **any** QML can be used for branding purposes. shows that **any** QML can be used for branding purposes.
@@ -217,15 +220,12 @@ The API is loosely documented in the
GitHub Issues are **one** place for discussing Calamares (and Calamares Extensions) GitHub Issues are **one** place for discussing Calamares (and Calamares Extensions)
if there are concrete if there are concrete
problems or a new feature to discuss. problems or a new feature to discuss.
Issues are not a help channel.
Visit Matrix for help with configuration or compilation.
Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/) Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/)
room, `#calamares:kde.org`. The conversation is bridged with IRC room, `#calamares:kde.org`. Responsiveness is best during the day
on [Libera.Chat](https://libera.chat/). in Europe, but feel free to idle.
Responsiveness is best during the day
in Europe, but feel free to idle. If you use IRC, **DO NOT** ask-and-leave. Keep
that chat window open because it can easily take a few hours for
someone to notice a message.
Matrix is persistent, and we'll see your message eventually. Matrix is persistent, and we'll see your message eventually.
* [![Join us on Matrix](https://img.shields.io/badge/Matrix-%23calamares:kde.org-blue)](https://webchat.kde.org/#/room/%23calamares:kde.org) * [![Join us on Matrix](https://img.shields.io/badge/Matrix-%23calamares:kde.org-blue)](https://webchat.kde.org/#/room/%23calamares:kde.org)
* [![Chat on IRC](https://img.shields.io/badge/IRC-Libera.Chat%20%23calamares-green)](https://kiwiirc.com/client/irc.libera.chat/#calamares)

View File

@@ -0,0 +1,101 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2020 2022 Anke Boersma <demm@kaosx.us>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
import io.calamares.core 1.0
import io.calamares.ui 1.0
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Window 2.3
ApplicationWindow {
id: about
visible: true
width: 760
height: 400
title: qsTr("About Calamares")
property var appName: "Calamares"
property var appVersion: "3.3 RC"
Rectangle {
id: textArea
anchors.fill: parent
color: "#f2f2f2"
Column {
id: column
anchors.centerIn: parent
Rectangle {
width: 560
height: 250
radius: 10
border.width: 0
Text {
width: 400
height: 250
anchors.centerIn: parent
text: qsTr("<h1>%1</h1><br/>
<strong>%2<br/>
for %3</strong><br/><br/>
Copyright 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>
Copyright 2017-2022 Adriaan de Groot &lt;groot@kde.org&gt;<br/>
Thanks to <a href='https://calamares.io/team/'>the Calamares team</a>
and the <a href=\"https://www.transifex.com/kaos/kaos/\">KaOS
translators team</a>.<br/><br/>
<a href='https://calamares.io/'>Calamares</a>
development is sponsored by <br/>
<a href='http://www.blue-systems.com/'>Blue Systems</a> -
Liberating Software." )
.arg(appName)
.arg(appVersion)
.arg(Branding.string(Branding.VersionedName))
onLinkActivated: Qt.openUrlExternally(link)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
font.pointSize: 10
anchors.verticalCenterOffset: 10
anchors.horizontalCenterOffset: 40
wrapMode: Text.WordWrap
}
Image {
id: image
x: 8
y: 12
height: 100
fillMode: Image.PreserveAspectFit
source: "squid.png"
}
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
icon.name: "window-close"
text: qsTr("Close")
hoverEnabled: true
onClicked: about.close();
}
}
}

View File

@@ -1,22 +1,32 @@
--- ---
componentName: kaos componentName: kaos
welcomeStyleCalamares: false
# Should the welcome image (productWelcome, below) be scaled # Should the welcome image (productWelcome, below) be scaled
# up beyond its natural size? # up beyond its natural size?
welcomeExpandingLogo: true welcomeExpandingLogo: true
windowExpanding: normal
windowSize: 920px,630px
windowPlacement: center
sidebar: qml,bottom
navigation: qml,right
strings: strings:
productName: KaOS productName: KaOS
shortProductName: KaOS shortProductName: KaOS
version: 2018.03 version: 2022.08
shortVersion: KaOS shortVersion: KaOS
versionedName: KaOS 2018.03 versionedName: KaOS 2022.08
shortVersionedName: KaOS 2018.03 shortVersionedName: KaOS 2018.03
bootloaderEntryName: KaOS bootloaderEntryName: KaOS
productUrl: https://kaosx.us/ productUrl: https://kaosx.us/
supportUrl: https://kaosx.us/docs/ supportUrl: https://kaosx.us/docs/
knownIssuesUrl: https://kaosx.us/pages/download/#known-issues knownIssuesUrl: https://kaosx.us/pages/download/#known-issues
releaseNotesUrl: https://kaosx.us/pages/release_notes releaseNotesUrl: https://kaosx.us/pages/release_notes
donateUrl: https://kaosx.us/about/donors
images: images:
productLogo: "kaos.png" productLogo: "kaos.png"
@@ -27,6 +37,7 @@ slideshow: "show.qml"
slideshowAPI: 1 slideshowAPI: 1
style: style:
sidebarBackground: "#bdc3c7" SidebarBackground: "#bdc3c7"
sidebarText: "#1F1F1F" SidebarText: "#1F1F1F"
sidebarTextSelect: "#3498DB" SidebarTextCurrent: "#3498DB"
SidebarBackgroundCurrent: "#eff0f1"

View File

@@ -0,0 +1,224 @@
/* Sample of QML navigation.
SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
SPDX-FileCopyrightText: 2021 - 2022 Anke Boersma <demm@kaosx.us>
SPDX-License-Identifier: GPL-3.0-or-later
This navigation panel is for a "vertical" layout, with
mouse areas for next and previous and it includes the logo
plus About & Debug buttons.
*/
import io.calamares.ui 1.0
import io.calamares.core 1.0
import QtQuick 2.3
import QtQuick.Controls 2.10
import QtQuick.Layouts 1.3
Rectangle {
id: navigationBar;
color: Branding.styleString( Branding.SidebarBackground );
height: parent.height;
width:64;
ColumnLayout {
id: buttonBar
anchors.fill: parent;
spacing: 1
Image {
Layout.topMargin: 1;
Layout.bottomMargin:parent.height / 7;
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
id: logo;
width: 62;
height: width; // square
source: "file:/" + Branding.imagePath(Branding.ProductLogo);
sourceSize.width: width;
sourceSize.height: height;
}
Rectangle {
id: backArea
Layout.fillWidth: true;
Layout.preferredHeight: parent.height / 7;
color: mouseBack.containsMouse ? "#e6e9ea" : "#d9dcde";
enabled: ViewManager.backEnabled;
visible: ViewManager.backAndNextVisible;
MouseArea {
id: mouseBack
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Back")
color: Branding.styleString( !backArea.enabled ? Branding.SidebarBackground : (mouseBack.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText ));
font.pointSize : 8
}
Image {
source: "pan-start-symbolic.svg"
anchors.centerIn: parent
anchors.verticalCenterOffset : 18
fillMode: Image.PreserveAspectFit
height: 32
opacity: backArea.enabled ? 1 : 0.2
}
onClicked: { ViewManager.back(); }
}
}
Rectangle {
id: nextArea
Layout.preferredHeight: parent.height / 7;
Layout.fillWidth: true
color: mouseNext.containsMouse ? "#f4f5f6" : "#e6e9ea";
enabled: ViewManager.nextEnabled;
visible: ViewManager.backAndNextVisible;
MouseArea {
id: mouseNext
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Next")
color: Branding.styleString( !nextArea.enabled ? Branding.SidebarBackground : (mouseNext.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText ));
font.pointSize : 8
}
Image {
source: "pan-end-symbolic.svg"
anchors.centerIn: parent
anchors.verticalCenterOffset : 18
fillMode: Image.PreserveAspectFit
height: 32
opacity: nextArea.enabled ? 1 : 0.2
}
onClicked: { ViewManager.next(); }
}
}
Rectangle {
id: cancelArea
height: parent.height / 7;
Layout.fillWidth: true
color: mouseCancel.containsMouse ? "#e6e9ea" : "#d9dcde";
/*
* The ViewManager has settings -- user-controlled via the
* branding component, and party based on program state --
* whether the quit button should be enabled and visible.
*
* QML navigation *should* follow this pattern, but can also
* add other qualifications. For instance, you may have a
* "finished" module that handles quit in its own way, and
* want to hide the quit button then. The ViewManager has a
* current step and a total count, so compare them:
*
* visible: ViewManager.quitVisible && ( ViewManager.currentStepIndex < ViewManager.rowCount()-1);
*/
enabled: ViewManager.quitEnabled;
visible: ViewManager.quitVisible && ( ViewManager.currentStepIndex < ViewManager.rowCount()-1);
ToolTip {
width: 59
visible: mouseCancel.containsMouse
timeout: 5000
delay: 1000
text: ViewManager.quitTooltip;
}
MouseArea {
id: mouseCancel
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Cancel")
color: Branding.styleString( !cancelArea.enabled ? Branding.SidebarBackground : (mouseCancel.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText ));
font.pointSize : 8
}
Image {
source: "draw-rectangle.svg"
anchors.centerIn: parent
anchors.verticalCenterOffset : 18
fillMode: Image.PreserveAspectFit
height: 9
opacity: cancelArea.enabled ? 1 : 0.2
}
onClicked: { ViewManager.quit(); }
}
}
Item {
Layout.fillHeight: true;
}
Rectangle {
id: debugArea
Layout.fillWidth: true;
height: 35
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
color: Branding.styleString( mouseAreaDebug.containsMouse ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground);
visible: debug.enabled
MouseArea {
id: mouseAreaDebug
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("Debug")
color: Branding.styleString( mouseAreaDebug.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarBackground );
font.pointSize : 8
}
onClicked: debug.toggle()
}
}
Rectangle {
id: aboutArea
Layout.fillWidth: true;
height: 35
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
color: Branding.styleString( mouseAreaAbout.containsMouse ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground);
visible: true
MouseArea {
id: mouseAreaAbout
anchors.fill: parent;
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
Text {
anchors.centerIn: parent
text: qsTr("About")
ToolTip {
visible: mouseAreaAbout.containsMouse
delay: 1000
text: qsTr("Info about Calamares")
}
color: Branding.styleString( mouseAreaAbout.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarBackgroundCurrent );
font.pointSize : 8
}
property variant window;
onClicked: {
var component = Qt.createComponent("about.qml");
window = component.createObject();
window.show();
}
}
}
}
}

View File

@@ -0,0 +1,72 @@
/* Sample of QML progress tree.
SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
SPDX-FileCopyrightText: 2021 - 2022 Anke Boersma <demm@kaosx.us>
SPDX-License-Identifier: GPL-3.0-or-later
The progress tree (actually a list) is "horizontal" in this example,
with the steps going to the right.
*/
import io.calamares.ui 1.0
import io.calamares.core 1.0
import QtQuick 2.3
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.15
Rectangle {
id: sideBar;
color: Branding.styleString( Branding.SidebarBackground );
height: 48;
width: parent.width
RowLayout {
anchors.fill: parent;
spacing: 2;
Item {
Layout.fillHeight: true;
}
Repeater {
model: ViewManager
Rectangle {
Layout.leftMargin: 0;
Layout.fillWidth: true;
Layout.alignment: Qt.AlignTop;
height: 42;
radius: 0;
color: Branding.styleString( index == ViewManager.currentStepIndex ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground );
Text {
anchors.verticalCenter: parent.verticalCenter;
anchors.horizontalCenter: parent.horizontalCenter
x: parent.x + 12;
color: Branding.styleString( index == ViewManager.currentStepIndex ? Branding.SidebarTextCurrent : Branding.SidebarText );
text: display;
font.pointSize : index == ViewManager.currentStepIndex ? 10 : 9
}
Rectangle {
height: 2
width: 800
anchors.bottom: parent.bottom;
border.color: Branding.styleString(ViewManager.currentStepIndex === index ? Branding.SidebarTextCurrent : (ViewManager.currentStepIndex >= index ? Branding.SidebarTextCurrent : Branding.SidebarBackgroundCurrent))
border.width: 3
Image {
source: "pan-up-symbolic.svg"
id: image
anchors.verticalCenter: parent.verticalCenter;
anchors.verticalCenterOffset : -3
x: parent.x + 35;
fillMode: Image.PreserveAspectFit
height: 32
visible: index == ViewManager.currentStepIndex ? true : false
}
}
}
}
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"><path d="m2 1034.36v16h16v-16z" fill="#566060" transform="translate(1-1031.36)"/></svg>

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

@@ -0,0 +1,15 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height="16" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" version="1.1" width="16" xmlns="http://www.w3.org/2000/svg" enable-background="new">
<metadata id="metadata90"/>
<defs id="defs7386">
<linearGradient id="linearGradient5606" osb:paint="solid">
<stop id="stop5608"/>
</linearGradient>
<filter inkscape:collect="always" color-interpolation-filters="sRGB" id="filter7554">
<feBlend inkscape:collect="always" id="feBlend7556" in2="BackgroundImage" mode="darken"/>
</filter>
</defs>
<g inkscape:groupmode="layer" id="layer12" inkscape:label="actions" transform="translate(-445.0002,-129)">
<path inkscape:connector-curvature="0" d="m 451.0002,142 5,-5 -5,-5 z" id="path6412" sodipodi:nodetypes="cccc" fill="#555555"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 896 B

View File

@@ -0,0 +1,15 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height="16" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" version="1.1" width="16" xmlns="http://www.w3.org/2000/svg" enable-background="new">
<metadata id="metadata90"/>
<defs id="defs7386">
<linearGradient id="linearGradient5606" osb:paint="solid">
<stop id="stop5608"/>
</linearGradient>
<filter inkscape:collect="always" color-interpolation-filters="sRGB" id="filter7554">
<feBlend inkscape:collect="always" id="feBlend7556" in2="BackgroundImage" mode="darken"/>
</filter>
</defs>
<g inkscape:groupmode="layer" id="layer12" inkscape:label="actions" transform="translate(-425.0002,-129)">
<path inkscape:connector-curvature="0" d="m 435.0002,142 -5,-5 5,-5 z" id="path6400-8" sodipodi:nodetypes="cccc" fill="#555555"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 898 B

View File

@@ -0,0 +1,15 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height="16" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" version="1.1" width="16" xmlns="http://www.w3.org/2000/svg" enable-background="new">
<metadata id="metadata90"/>
<defs id="defs7386">
<linearGradient id="linearGradient5606" osb:paint="solid">
<stop id="stop5608"/>
</linearGradient>
<filter inkscape:collect="always" color-interpolation-filters="sRGB" id="filter7554">
<feBlend inkscape:collect="always" id="feBlend7556" in2="BackgroundImage" mode="darken"/>
</filter>
</defs>
<g inkscape:groupmode="layer" id="layer12" inkscape:label="actions" transform="translate(-465.0002,-129.00001)">
<path inkscape:connector-curvature="0" d="m 478.0002,139 -5,-5 -5,5 z" id="path6418" sodipodi:nodetypes="cccc" fill="#3498DB"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 902 B

View File

@@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2015, Teo Mrnjavac <teo@kde.org> * Copyright 2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2015-2018, Anke Boersma <demm@kaosx.us>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -24,13 +25,12 @@ Presentation
id: presentation id: presentation
Timer { Timer {
id: advanceTimer
interval: 5000 interval: 5000
running: false running: false
repeat: true repeat: true
onTriggered: presentation.goToNextSlide() onTriggered: presentation.goToNextSlide()
} }
Slide { Slide {
anchors.fill: parent anchors.fill: parent
@@ -38,7 +38,7 @@ Presentation
id: background id: background
source: "1.svg" source: "1.svg"
anchors.fill: parent anchors.fill: parent
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
anchors.verticalCenterOffset: 0 anchors.verticalCenterOffset: 0
@@ -96,15 +96,15 @@ Presentation
anchors.horizontalCenterOffset: -100 anchors.horizontalCenterOffset: -100
font.pixelSize: parent.width *.015 font.pixelSize: parent.width *.015
color: 'white' color: 'white'
text: qsTr("The default Office Suite is Calligra.<br/>"+ text: qsTr("The default Office Suite is LibreOffice.<br/>"+
"LibreOffice is available in the repositories. <br/>") "Calligra is available in the repositories. <br/>")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: 450 width: 450
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
} }
} }
} }
Slide { Slide {
anchors.fill: parent anchors.fill: parent
@@ -120,7 +120,7 @@ Presentation
font.pixelSize: parent.width *.015 font.pixelSize: parent.width *.015
color: 'white' color: 'white'
text: qsTr("Qt/KDE specific internet applications include the <br/>"+ text: qsTr("Qt/KDE specific internet applications include the <br/>"+
"Qupzilla web-browser and kde-telepathy for <br/>"+ "Falkon web-browser and kde-telepathy for <br/>"+
"chat and Instant Messaging. <br/>") "chat and Instant Messaging. <br/>")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: 450 width: 450
@@ -128,7 +128,7 @@ Presentation
} }
} }
} }
Slide { Slide {
anchors.fill: parent anchors.fill: parent
@@ -150,7 +150,7 @@ Presentation
} }
} }
} }
Slide { Slide {
anchors.fill: parent anchors.fill: parent
@@ -172,6 +172,4 @@ Presentation
} }
} }
} }
Component.onCompleted: advanceTimer.running = true
} }

View File

@@ -30,6 +30,13 @@ Config::setConfigurationMap( const QVariantMap& cfgMap )
m_device = getString( cfgMap, "device", "(unknown)" ); m_device = getString( cfgMap, "device", "(unknown)" );
m_userInterface = getString( cfgMap, "userInterface", "(unknown)" ); m_userInterface = getString( cfgMap, "userInterface", "(unknown)" );
m_version = getString( cfgMap, "version", "(unknown)" ); m_version = getString( cfgMap, "version", "(unknown)" );
m_reservedUsernames = getStringList( cfgMap, "reservedUsernames", QStringList { "adm", "at ", "bin", "colord",
"cron", "cyrus", "daemon", "ftp", "games", "geoclue", "guest", "halt", "lightdm", "lp", "mail", "man",
"messagebus", "news", "nobody", "ntp", "operator", "polkitd", "postmaster", "pulse", "root", "shutdown",
"smmsp", "squid", "sshd", "sync", "uucp", "vpopmail", "xfs" } );
// ensure m_cmdUsermod matches m_username
m_username = getString( cfgMap, "username", "user" ); m_username = getString( cfgMap, "username", "user" );
m_userPasswordNumeric = getBool( cfgMap, "userPasswordNumeric", true ); m_userPasswordNumeric = getBool( cfgMap, "userPasswordNumeric", true );
@@ -54,6 +61,8 @@ Config::setConfigurationMap( const QVariantMap& cfgMap )
m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" ); m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" );
m_cmdPasswd = getString( cfgMap, "cmdPasswd", "passwd" ); m_cmdPasswd = getString( cfgMap, "cmdPasswd", "passwd" );
m_cmdUsermod = getString( cfgMap, "cmdUsermod", "xargs -I{} -n1 usermod -m -d /home/{} -l {} -c {} user");
m_cmdSshdEnable = getString( cfgMap, "cmdSshdEnable", "systemctl enable sshd.service" ); m_cmdSshdEnable = getString( cfgMap, "cmdSshdEnable", "systemctl enable sshd.service" );
m_cmdSshdDisable = getString( cfgMap, "cmdSshdDisable", "systemctl disable sshd.service" ); m_cmdSshdDisable = getString( cfgMap, "cmdSshdDisable", "systemctl disable sshd.service" );
m_cmdSshdUseradd = getString( cfgMap, "cmdSshdUseradd", "useradd -G wheel -m" ); m_cmdSshdUseradd = getString( cfgMap, "cmdSshdUseradd", "useradd -G wheel -m" );
@@ -68,6 +77,7 @@ Config::createJobs()
/* Put users job in queue (should run after unpackfs) */ /* Put users job in queue (should run after unpackfs) */
Calamares::Job* j = new UsersJob( m_featureSshd, Calamares::Job* j = new UsersJob( m_featureSshd,
m_cmdPasswd, m_cmdPasswd,
m_cmdUsermod,
cmdSshd, cmdSshd,
m_cmdSshdUseradd, m_cmdSshdUseradd,
m_isSshEnabled, m_isSshEnabled,
@@ -132,6 +142,13 @@ Config::runPartitionJobThenLeave( bool b )
} }
} }
void
Config::setUsername( const QString& username )
{
m_username = username;
emit usernameChanged( m_username );
}
void void
Config::setUserPassword( const QString& userPassword ) Config::setUserPassword( const QString& userPassword )
{ {

View File

@@ -20,8 +20,11 @@ class Config : public QObject
Q_PROPERTY( QString userInterface READ userInterface CONSTANT FINAL ) Q_PROPERTY( QString userInterface READ userInterface CONSTANT FINAL )
Q_PROPERTY( QString version READ version CONSTANT FINAL ) Q_PROPERTY( QString version READ version CONSTANT FINAL )
/* reserved usernames (user_pass, ssh_credentials )*/
Q_PROPERTY( QStringList reservedUsernames READ reservedUsernames CONSTANT FINAL )
/* default user */ /* default user */
Q_PROPERTY( QString username READ username CONSTANT FINAL ) Q_PROPERTY( QString username READ username WRITE setUsername NOTIFY usernameChanged )
Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged ) Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged )
Q_PROPERTY( bool userPasswordNumeric READ userPasswordNumeric CONSTANT FINAL ) Q_PROPERTY( bool userPasswordNumeric READ userPasswordNumeric CONSTANT FINAL )
@@ -72,13 +75,17 @@ public:
QString userInterface() const { return m_userInterface; } QString userInterface() const { return m_userInterface; }
QString version() const { return m_version; } QString version() const { return m_version; }
/* default user */ /* reserved usernames (user_pass, ssh_credentials) */
QStringList reservedUsernames() const { return m_reservedUsernames; };
/* user */
QString username() const { return m_username; } QString username() const { return m_username; }
QString userPassword() const { return m_userPassword; } QString userPassword() const { return m_userPassword; }
void setUsername( const QString& username );
void setUserPassword( const QString& userPassword ); void setUserPassword( const QString& userPassword );
bool userPasswordNumeric() const { return m_userPasswordNumeric; } bool userPasswordNumeric() const { return m_userPasswordNumeric; }
/* ssh server + credetials */ /* ssh server + credentials */
bool featureSshd() { return m_featureSshd; } bool featureSshd() { return m_featureSshd; }
QString sshdUsername() const { return m_sshdUsername; } QString sshdUsername() const { return m_sshdUsername; }
QString sshdPassword() const { return m_sshdPassword; } QString sshdPassword() const { return m_sshdPassword; }
@@ -120,6 +127,7 @@ public:
/* users job */ /* users job */
QString cmdPasswd() const { return m_cmdPasswd; } QString cmdPasswd() const { return m_cmdPasswd; }
QString cmdUsermod() const { return m_cmdUsermod; }
QString cmdSshdEnable() const { return m_cmdSshdEnable; } QString cmdSshdEnable() const { return m_cmdSshdEnable; }
QString cmdSshdDisable() const { return m_cmdSshdDisable; } QString cmdSshdDisable() const { return m_cmdSshdDisable; }
QString cmdSshdUseradd() const { return m_cmdSshdUseradd; } QString cmdSshdUseradd() const { return m_cmdSshdUseradd; }
@@ -135,6 +143,9 @@ private:
QString m_userInterface; QString m_userInterface;
QString m_version; QString m_version;
/* reserved usernames (user_pass, ssh_credentials) */
QStringList m_reservedUsernames;
/* default user */ /* default user */
QString m_username; QString m_username;
QString m_userPassword; QString m_userPassword;
@@ -172,6 +183,7 @@ private:
/* users job */ /* users job */
QString m_cmdPasswd; QString m_cmdPasswd;
QString m_cmdUsermod;
QString m_cmdSshdEnable; QString m_cmdSshdEnable;
QString m_cmdSshdDisable; QString m_cmdSshdDisable;
QString m_cmdSshdUseradd; QString m_cmdSshdUseradd;
@@ -181,6 +193,7 @@ signals:
/* default user */ /* default user */
void userPasswordChanged( QString userPassword ); void userPasswordChanged( QString userPassword );
void usernameChanged( QString username );
/* ssh server + credentials */ /* ssh server + credentials */
void sshdUsernameChanged( QString sshdUsername ); void sshdUsernameChanged( QString sshdUsername );

View File

@@ -118,7 +118,7 @@ PartitionJob::exec()
const QString pathRoot = "/"; const QString pathRoot = "/";
ProcessResult res ProcessResult res
= System::runCommand( System::RunLocation::RunInHost, args, pathRoot, stdInput, chrono::seconds( 120 ) ); = System::runCommand( System::RunLocation::RunInHost, args, pathRoot, stdInput, chrono::seconds( 600 ) );
if ( res.getExitCode() ) if ( res.getExitCode() )
{ {
return JobResult::error( "Command failed:<br><br>" return JobResult::error( "Command failed:<br><br>"

View File

@@ -14,6 +14,7 @@
UsersJob::UsersJob( bool featureSshd, UsersJob::UsersJob( bool featureSshd,
const QString& cmdPasswd, const QString& cmdPasswd,
const QString& cmdUsermod,
const QString& cmdSshd, const QString& cmdSshd,
const QString& cmdSshdUseradd, const QString& cmdSshdUseradd,
bool isSshEnabled, bool isSshEnabled,
@@ -24,6 +25,7 @@ UsersJob::UsersJob( bool featureSshd,
: Calamares::Job() : Calamares::Job()
, m_featureSshd( featureSshd ) , m_featureSshd( featureSshd )
, m_cmdPasswd( cmdPasswd ) , m_cmdPasswd( cmdPasswd )
, m_cmdUsermod( cmdUsermod )
, m_cmdSshd( cmdSshd ) , m_cmdSshd( cmdSshd )
, m_cmdSshdUseradd( cmdSshdUseradd ) , m_cmdSshdUseradd( cmdSshdUseradd )
, m_isSshEnabled( isSshEnabled ) , m_isSshEnabled( isSshEnabled )
@@ -49,9 +51,11 @@ UsersJob::exec()
using namespace std; using namespace std;
QList< QPair< QStringList, QString > > commands = { QList< QPair< QStringList, QString > > commands = {
{ { "sh", "-c", m_cmdPasswd + " " + m_username }, m_password + "\n" + m_password + "\n" }, { { "sh", "-c", m_cmdUsermod }, m_username + "\n" }
}; };
commands.append( { { "sh", "-c", m_cmdPasswd + " " + m_username }, m_password + "\n" + m_password + "\n" } );
if ( m_featureSshd ) if ( m_featureSshd )
{ {
commands.append( { { "sh", "-c", m_cmdSshd }, QString() } ); commands.append( { { "sh", "-c", m_cmdSshd }, QString() } );

View File

@@ -10,6 +10,7 @@ class UsersJob : public Calamares::Job
public: public:
UsersJob( bool featureSshd, UsersJob( bool featureSshd,
const QString& cmdPasswd, const QString& cmdPasswd,
const QString& cmdUsermod,
const QString& cmdSshd, const QString& cmdSshd,
const QString& cmdSshdUseradd, const QString& cmdSshdUseradd,
bool isSshEnabled, bool isSshEnabled,
@@ -26,6 +27,7 @@ public:
private: private:
bool m_featureSshd; bool m_featureSshd;
QString m_cmdPasswd; QString m_cmdPasswd;
QString m_cmdUsermod;
QString m_cmdSshd; QString m_cmdSshd;
QString m_cmdSshdUseradd; QString m_cmdSshdUseradd;
bool m_isSshEnabled; bool m_isSshEnabled;

View File

@@ -22,13 +22,13 @@ Item {
id: mainText id: mainText
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 30 anchors.topMargin: 25
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: (function() { text: (function() {
var ret = "Once you hit 'install', the installation will begin." + var ret = "Once you hit 'install', the installation will begin." +
" It will typically take a few minutes. Do not power off the" + " It will typically take a few minutes. Do not power off the" +
" device until it is done.<br><br>"; " device until it is done.<br>";
if (config.installFromExternalToInternal) { if (config.installFromExternalToInternal) {
ret += "<b>After the installation, your device will shutdown" + ret += "<b>After the installation, your device will shutdown" +
@@ -44,14 +44,14 @@ Item {
return ret; return ret;
}()) }())
width: 500 width: 550
} }
Button { Button {
id: firstButton id: firstButton
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom anchors.top: mainText.bottom
anchors.topMargin: 40 anchors.topMargin: 20
width: 500 width: 500
text: qsTr("Install") text: qsTr("Install")

View File

@@ -26,8 +26,45 @@ bogus: true
# version: "(unknown)" # version: "(unknown)"
## Default username (for which the password will be set) ## Default username (for which the password will be set)
## Ensure also cmdUsermod command matches the default user, so it can be changed if desired.
# username: "user" # username: "user"
## reserved usernames (for user_pass username prompt and ssh_credentials)
# reservedUsernames:
# - adm
# - at
# - bin
# - colord
# - cron
# - cyrus
# - daemon
# - ftp
# - games
# - geoclue
# - guest
# - halt
# - lightdm
# - lp
# - mail
# - man
# - messagebus
# - news
# - nobody
# - ntp
# - operator
# - polkitd
# - postmaster
# - pulse
# - root
# - shutdown
# - smmsp
# - squid
# - sshd
# - sync
# - uucp
# - vpopmail
# - xfs
####### #######
### Target device information ### Target device information
####### #######
@@ -118,6 +155,10 @@ bogus: true
### Commands running in the target OS (chroot) ### Commands running in the target OS (chroot)
####### #######
## Change the username for the default user
## Stdin: username with \n
# cmdUsermod: "xargs -I{} -n1 usermod -m -d /home/{} -l {} -c {} user"
## Set the password for default user and sshd user ## Set the password for default user and sshd user
## Arguments: <username> ## Arguments: <username>
## Stdin: password twice, each time with \n ## Stdin: password twice, each time with \n

View File

@@ -253,7 +253,7 @@ Page
return true; return true;
} }
/* Input validation: user-screens (user_pass, ssh_credentials) */ /* Input validation: user-screens (fde_pass, user_pass, ssh_credentials) */
function validatePin(userPin, userPinRepeat, errorText) { function validatePin(userPin, userPinRepeat, errorText) {
var pin = userPin.text; var pin = userPin.text;
var repeat = userPinRepeat.text; var repeat = userPinRepeat.text;
@@ -278,47 +278,12 @@ Page
return validationFailureClear(errorText); return validationFailureClear(errorText);
} }
function validateSshdUsername(username, errorText) { function validateUsername(username, errorText, extraReservedUsernames = []) {
var name = username.text; var name = username.text;
var reserved = [ /* FIXME: make configurable */ var reserved = config.reservedUsernames.concat(extraReservedUsernames);
config.username,
"adm",
"at ",
"bin",
"colord",
"cron",
"cyrus",
"daemon",
"ftp",
"games",
"geoclue",
"guest",
"halt",
"lightdm",
"lp",
"mail",
"man",
"messagebus",
"news",
"nobody",
"ntp",
"operator",
"polkitd",
"postmaster",
"pulse",
"root",
"shutdown",
"smmsp",
"squid",
"sshd",
"sync",
"uucp",
"vpopmail",
"xfs",
]
/* Validate characters */ /* Validate characters */
for (var i=0; i<name.length; i++) { for (var i = 0; i < name.length; i++) {
if (i) { if (i) {
if (!name[i].match(/^[a-z0-9_-]$/)) if (!name[i].match(/^[a-z0-9_-]$/))
return validationFailure(errorText, return validationFailure(errorText,
@@ -335,16 +300,20 @@ Page
} }
/* Validate against reserved usernames */ /* Validate against reserved usernames */
for (var i=0;i<reserved.length;i++) { for (var i = 0; i < reserved.length; i++) {
if (name == reserved[i]) if (name == reserved[i])
return validationFailure(errorText, "Username '" + return validationFailure(errorText, "Username '" +
reserved[i] + reserved[i] +
"' is reserved.") "' is reserved.");
} }
/* Passed */ /* Passed */
return validationFailureClear(errorText); return validationFailureClear(errorText);
} }
function validateSshdUsername(username, errorText) {
return validateUsername(username, errorText, [config.username]);
}
function validateSshdPassword(password, passwordRepeat, errorText) { function validateSshdPassword(password, passwordRepeat, errorText) {
var pass = password.text; var pass = password.text;
var repeat = passwordRepeat.text; var repeat = passwordRepeat.text;
@@ -352,10 +321,10 @@ Page
if (pass == "") if (pass == "")
return validationFailure(errorText); return validationFailure(errorText);
if (pass.length < 8) if (pass.length < 6)
return validationFailure(errorText, return validationFailure(errorText,
"Too short: needs at least 8" + "Too short: needs at least 6" +
" characters."); " digits/characters.");
if (repeat == "") if (repeat == "")
return validationFailure(errorText); return validationFailure(errorText);
@@ -365,8 +334,6 @@ Page
return validationFailureClear(errorText); return validationFailureClear(errorText);
} }
/* Input validation: fde_pass */
function check_chars(input) { function check_chars(input) {
for (var i = 0; i < input.length; i++) { for (var i = 0; i < input.length; i++) {
if (allowed_chars.indexOf(input[i]) == -1) if (allowed_chars.indexOf(input[i]) == -1)
@@ -407,10 +374,10 @@ Page
"\n" + "\n" +
allowed_chars_multiline()); allowed_chars_multiline());
if (pass.length < 8) if (pass.length < 6)
return validationFailure(errorText, return validationFailure(errorText,
"Too short: needs at least 8" + "Too short: needs at least 6" +
" characters."); " digits/characters.");
if (repeat == "") if (repeat == "")
return validationFailure(errorText); return validationFailure(errorText);

View File

@@ -7,7 +7,7 @@
<file>install_target.qml</file> <!-- install from external to internal? --> <file>install_target.qml</file> <!-- install from external to internal? -->
<file>install_target_confirm.qml</file> <!-- overwrite internal storage? --> <file>install_target_confirm.qml</file> <!-- overwrite internal storage? -->
<file>user_pass.qml</file> <!-- default user: password --> <file>user_pass.qml</file> <!-- default user: username, password -->
<file>ssh_confirm.qml</file> <!-- sshd: enable or not? --> <file>ssh_confirm.qml</file> <!-- sshd: enable or not? -->
<file>ssh_credentials.qml</file> <!-- sshd user: username, password --> <file>ssh_credentials.qml</file> <!-- sshd user: username, password -->
<file>fs_selection.qml</file> <!-- filesystem selection --> <file>fs_selection.qml</file> <!-- filesystem selection -->

View File

@@ -12,16 +12,18 @@ import QtQuick.Window 2.3
import QtQuick.VirtualKeyboard 2.1 import QtQuick.VirtualKeyboard 2.1
Item { Item {
property var placeholder: (config.userPasswordNumeric property var passPlaceholder: (config.userPasswordNumeric
? "PIN" ? "PIN"
: "Password") : "Password")
property var hints: (config.userPasswordNumeric property var hints: (config.userPasswordNumeric
? Qt.ImhDigitsOnly ? Qt.ImhDigitsOnly
: Qt.ImhPreferLowercase) : Qt.ImhPreferLowercase)
property var validateFunc: (config.userPasswordNumeric property var validatePassFunc: (config.userPasswordNumeric
? validatePin ? validatePin
: validatePassword); : validatePassword);
property var validateNameFunc: validateUsername;
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
@@ -30,12 +32,38 @@ Item {
height: parent.height height: parent.height
Text { Text {
id: description id: usernameDescription
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 30 anchors.topMargin: 30
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: (function() {
return "Set the username of your user. The default" +
" username is \"" + config.username + "\".";
}())
width: 500
}
TextField {
id: username
anchors.top: usernameDescription.bottom
placeholderText: qsTr("Username")
onTextChanged: validateNameFunc(username, errorText)
text: config.username
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 50
width: 500
}
Text {
id: userPassDescription
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: username.bottom
anchors.topMargin: 30
wrapMode: Text.WordWrap
text: (function() { text: (function() {
if (config.userPasswordNumeric) { if (config.userPasswordNumeric) {
return "Set the numeric password of your user. The" + return "Set the numeric password of your user. The" +
@@ -53,10 +81,10 @@ Item {
TextField { TextField {
id: userPass id: userPass
anchors.top: description.bottom anchors.top: userPassDescription.bottom
placeholderText: qsTr(placeholder) placeholderText: qsTr(passPlaceholder)
echoMode: TextInput.Password echoMode: TextInput.Password
onTextChanged: validateFunc(userPass, userPassRepeat, errorText) onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText)
text: config.userPassword text: config.userPassword
/* Let the virtual keyboard change to digits only */ /* Let the virtual keyboard change to digits only */
@@ -75,10 +103,10 @@ Item {
TextField { TextField {
id: userPassRepeat id: userPassRepeat
anchors.top: userPass.bottom anchors.top: userPass.bottom
placeholderText: qsTr(placeholder + " (repeat)") placeholderText: qsTr(passPlaceholder + " (repeat)")
inputMethodHints: hints inputMethodHints: hints
echoMode: TextInput.Password echoMode: TextInput.Password
onTextChanged: validateFunc(userPass, userPassRepeat, errorText) onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText)
text: config.userPassword text: config.userPassword
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
@@ -105,8 +133,9 @@ Item {
text: qsTr("Continue") text: qsTr("Continue")
onClicked: { onClicked: {
if (validateFunc(userPass, userPassRepeat, errorText)) { if (validatePassFunc(userPass, userPassRepeat, errorText) && validateNameFunc(username, errorText)) {
config.userPassword = userPass.text; config.userPassword = userPass.text;
config.username = username.text;
navNext(); navNext();
} }
} }

View File

@@ -38,7 +38,7 @@ Page
anchors.topMargin: 150 anchors.topMargin: 150
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: "Formatting and mounting target partition. This may" + text: "Formatting and mounting target partition. This may" +
" take up to two minutes, please be patient." " take up to ten minutes, please be patient."
width: 500 width: 500
} }
} }

View File

@@ -9,6 +9,7 @@ calamares_add_plugin( unpackfsc
# The workers for differently-packed filesystems # The workers for differently-packed filesystems
Runners.cpp Runners.cpp
FSArchiverRunner.cpp FSArchiverRunner.cpp
TarballRunner.cpp
UnsquashRunner.cpp UnsquashRunner.cpp
SHARED_LIB SHARED_LIB
) )

View File

@@ -14,11 +14,40 @@
#include <QProcess> #include <QProcess>
static constexpr const int chunk_size = 137;
static const QString&
toolName()
{
static const QString name = QStringLiteral( "fsarchiver" );
return name;
}
void
FSArchiverRunner::fsarchiverProgress( QString line )
{
m_since++;
// Typical line of output is this:
// -[00][ 99%][REGFILEM] /boot/thing
// 5 9 ^21
if ( m_since >= chunk_size && line.length() > 21 && line[ 5 ] == '[' && line[ 9 ] == '%' )
{
m_since = 0;
double p = double( line.mid( 6, 3 ).toInt() ) / 100.0;
const QString filename = line.mid( 22 );
Q_EMIT progress( p, filename );
}
}
Calamares::JobResult Calamares::JobResult
FSArchiverRunner::run() FSArchiverRunner::checkPrerequisites( QString& fsarchiverExecutable ) const
{ {
const QString toolName = QStringLiteral( "fsarchiver" ); if ( !checkToolExists( toolName(), fsarchiverExecutable ) )
{
return Calamares::JobResult::internalError(
tr( "Missing tools" ),
tr( "The <i>%1</i> tool is not installed on the system." ).arg( toolName() ),
Calamares::JobResult::MissingRequirements );
}
if ( !checkSourceExists() ) if ( !checkSourceExists() )
{ {
@@ -27,28 +56,62 @@ FSArchiverRunner::run()
tr( "The source archive <i>%1</i> does not exist." ).arg( m_source ), tr( "The source archive <i>%1</i> does not exist." ).arg( m_source ),
Calamares::JobResult::InvalidConfiguration ); Calamares::JobResult::InvalidConfiguration );
} }
return Calamares::JobResult::ok();
}
QString fsarchiverExecutable; Calamares::JobResult
if ( !checkToolExists( toolName, fsarchiverExecutable ) ) FSArchiverRunner::checkDestination( QString& destinationPath ) const
{
destinationPath = CalamaresUtils::System::instance()->targetPath( m_destination );
if ( destinationPath.isEmpty() )
{ {
return Calamares::JobResult::internalError( return Calamares::JobResult::internalError(
tr( "Missing tools" ), tr( "Invalid fsarchiver configuration" ),
tr( "The <i>%1</i> tool is not installed on the system." ).arg( toolName ), tr( "No destination could be found for <i>%1</i>." ).arg( m_destination ),
Calamares::JobResult::MissingRequirements ); Calamares::JobResult::InvalidConfiguration );
} }
Calamares::Utils::Runner r( { fsarchiverExecutable, return Calamares::JobResult::ok();
QStringLiteral( "-v" ),
QStringLiteral( "restfs" ),
m_source,
QStringLiteral( "id=0,dest=%1" ).arg( m_destination ) } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
connect( &r, &decltype( r )::output, this, &FSArchiverRunner::fsarchiverProgress );
return r.run().explainProcess( toolName, std::chrono::seconds( 0 ) );
} }
void Calamares::JobResult
FSArchiverRunner::fsarchiverProgress( QString line ) FSArchiverDirRunner::run()
{ {
cDebug() << Logger::SubEntry << line; QString fsarchiverExecutable;
if ( auto res = checkPrerequisites( fsarchiverExecutable ); !res )
{
return res;
}
QString destinationPath;
if ( auto res = checkDestination( destinationPath ); !res )
{
return res;
}
Calamares::Utils::Runner r(
{ fsarchiverExecutable, QStringLiteral( "-v" ), QStringLiteral( "restdir" ), m_source, destinationPath } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
connect( &r, &decltype( r )::output, this, &FSArchiverDirRunner::fsarchiverProgress );
return r.run().explainProcess( toolName(), std::chrono::seconds( 0 ) );
}
Calamares::JobResult
FSArchiverFSRunner::run()
{
QString fsarchiverExecutable;
if ( auto res = checkPrerequisites( fsarchiverExecutable ); !res )
{
return res;
}
QString destinationPath;
if ( auto res = checkDestination( destinationPath ); !res )
{
return res;
}
Calamares::Utils::Runner r(
{ fsarchiverExecutable, QStringLiteral( "-v" ), QStringLiteral( "restfs" ), m_source, destinationPath } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
connect( &r, &decltype( r )::output, this, &FSArchiverFSRunner::fsarchiverProgress );
return r.run().explainProcess( toolName(), std::chrono::seconds( 0 ) );
} }

View File

@@ -12,16 +12,48 @@
#include "Runners.h" #include "Runners.h"
/** @brief Base class for runners of FSArchiver
*
*/
class FSArchiverRunner : public Runner class FSArchiverRunner : public Runner
{ {
Q_OBJECT Q_OBJECT
public: public:
using Runner::Runner; using Runner::Runner;
Calamares::JobResult run() override;
protected Q_SLOTS: protected Q_SLOTS:
void fsarchiverProgress( QString line ); void fsarchiverProgress( QString line );
protected:
/** @brief Checks prerequisites, sets full path of fsarchiver in @p executable
*/
Calamares::JobResult checkPrerequisites( QString& executable ) const;
Calamares::JobResult checkDestination( QString& destinationPath ) const;
int m_since = 0;
}; };
/** @brief Running FSArchiver in **dir** mode
*
*/
class FSArchiverDirRunner : public FSArchiverRunner
{
public:
using FSArchiverRunner::FSArchiverRunner;
Calamares::JobResult run() override;
};
/** @brief Running FSArchiver in **dir** mode
*
*/
class FSArchiverFSRunner : public FSArchiverRunner
{
public:
using FSArchiverRunner::FSArchiverRunner;
Calamares::JobResult run() override;
};
#endif #endif

View File

@@ -21,7 +21,7 @@ Runner::Runner( const QString& source, const QString& destination )
{ {
} }
Runner::~Runner() {} Runner::~Runner() { }
bool bool
Runner::checkSourceExists() const Runner::checkSourceExists() const

View File

@@ -0,0 +1,86 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "TarballRunner.h"
#include <utils/Logger.h>
#include <utils/Runner.h>
#include <utils/String.h>
#include <QString>
static constexpr const int chunk_size = 107;
Calamares::JobResult
TarballRunner::run()
{
if ( !checkSourceExists() )
{
return Calamares::JobResult::internalError(
tr( "Invalid tarball configuration" ),
tr( "The source archive <i>%1</i> does not exist." ).arg( m_source ),
Calamares::JobResult::InvalidConfiguration );
}
const QString toolName = QStringLiteral( "tar" );
QString tarExecutable;
if ( !checkToolExists( toolName, tarExecutable ) )
{
return Calamares::JobResult::internalError(
tr( "Missing tools" ),
tr( "The <i>%1</i> tool is not installed on the system." ).arg( toolName ),
Calamares::JobResult::MissingRequirements );
}
const QString destinationPath = CalamaresUtils::System::instance()->targetPath( m_destination );
if ( destinationPath.isEmpty() )
{
return Calamares::JobResult::internalError(
tr( "Invalid tarball configuration" ),
tr( "No destination could be found for <i>%1</i>." ).arg( m_destination ),
Calamares::JobResult::InvalidConfiguration );
}
// Get the stats (number of inodes) from the FS
{
m_total = 0;
Calamares::Utils::Runner r( { tarExecutable, QStringLiteral( "-tf" ), m_source } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
QObject::connect( &r, &decltype( r )::output, [ & ]( QString line ) { m_total++; } );
/* ignored */ r.run();
}
if ( m_total <= 0 )
{
cWarning() << "No stats could be obtained from" << tarExecutable << "-tf" << m_source;
}
// Now do the actual unpack
{
m_processed = 0;
m_since = 0;
Calamares::Utils::Runner r(
{ tarExecutable, QStringLiteral( "-xpvf" ), m_source, QStringLiteral( "-C" ), destinationPath } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
connect( &r, &decltype( r )::output, this, &TarballRunner::tarballProgress );
return r.run().explainProcess( toolName, std::chrono::seconds( 0 ) );
}
}
void
TarballRunner::tarballProgress( QString line )
{
m_processed++;
m_since++;
if ( m_since > chunk_size )
{
m_since = 0;
double p = m_total > 0 ? ( double( m_processed ) / double( m_total ) ) : 0.5;
Q_EMIT progress( p, tr( "Tarball extract file %1" ).arg( line ) );
}
}

View File

@@ -0,0 +1,35 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef UNPACKFSC_TARBALLRUNNER_H
#define UNPACKFSC_TARBALLRUNNER_H
#include "Runners.h"
/** @brief Use (GNU) tar for extracting a filesystem
*
*/
class TarballRunner : public Runner
{
public:
using Runner::Runner;
Calamares::JobResult run() override;
protected Q_SLOTS:
void tarballProgress( QString line );
private:
// Progress reporting
int m_total = 0;
int m_processed = 0;
int m_since = 0;
};
#endif

View File

@@ -10,6 +10,7 @@
#include "UnpackFSCJob.h" #include "UnpackFSCJob.h"
#include "FSArchiverRunner.h" #include "FSArchiverRunner.h"
#include "TarballRunner.h"
#include "UnsquashRunner.h" #include "UnsquashRunner.h"
#include <utils/Logger.h> #include <utils/Logger.h>
@@ -30,9 +31,15 @@ typeNames()
{ "fsarchiver", T::FSArchive }, { "fsarchiver", T::FSArchive },
{ "fsarchive", T::FSArchive }, { "fsarchive", T::FSArchive },
{ "fsa", T::FSArchive }, { "fsa", T::FSArchive },
{ "fsa-dir", T::FSArchive },
{ "fsa-block", T::FSArchiveFS },
{ "fsa-fs", T::FSArchiveFS },
{ "squashfs", T::Squashfs }, { "squashfs", T::Squashfs },
{ "squash", T::Squashfs }, { "squash", T::Squashfs },
{ "unsquash", T::Squashfs }, { "unsquash", T::Squashfs },
{ "tar", T::Tarball },
{ "tarball", T::Tarball },
{ "tgz", T::Tarball },
}; };
// clang-format on // clang-format on
return names; return names;
@@ -43,7 +50,7 @@ UnpackFSCJob::UnpackFSCJob( QObject* parent )
{ {
} }
UnpackFSCJob::~UnpackFSCJob() {} UnpackFSCJob::~UnpackFSCJob() { }
QString QString
UnpackFSCJob::prettyName() const UnpackFSCJob::prettyName() const
@@ -64,21 +71,30 @@ UnpackFSCJob::exec()
switch ( m_type ) switch ( m_type )
{ {
case Type::FSArchive: case Type::FSArchive:
r = std::make_unique< FSArchiverRunner >( m_source, m_destination ); r = std::make_unique< FSArchiverDirRunner >( m_source, m_destination );
break;
case Type::FSArchiveFS:
r = std::make_unique< FSArchiverFSRunner >( m_source, m_destination );
break; break;
case Type::Squashfs: case Type::Squashfs:
r = std::make_unique< UnsquashRunner >( m_source, m_destination ); r = std::make_unique< UnsquashRunner >( m_source, m_destination );
break; break;
case Type::Tarball:
r = std::make_unique< TarballRunner >( m_source, m_destination );
break;
case Type::None: case Type::None:
default: default:
cDebug() << "Nothing to do."; cDebug() << "Nothing to do.";
return Calamares::JobResult::ok(); return Calamares::JobResult::ok();
} }
connect( r.get(), &Runner::progress, [=]( qreal percent, const QString& message ) { connect( r.get(),
m_progressMessage = message; &Runner::progress,
Q_EMIT progress( percent ); [ = ]( qreal percent, const QString& message )
} ); {
m_progressMessage = message;
Q_EMIT progress( percent );
} );
return r->run(); return r->run();
} }

View File

@@ -23,7 +23,9 @@ public:
{ {
None, /// << Invalid None, /// << Invalid
FSArchive, FSArchive,
FSArchiveFS,
Squashfs, Squashfs,
Tarball,
}; };
explicit UnpackFSCJob( QObject* parent = nullptr ); explicit UnpackFSCJob( QObject* parent = nullptr );

View File

@@ -52,12 +52,15 @@ UnsquashRunner::run()
m_inodes = -1; m_inodes = -1;
Calamares::Utils::Runner r( { unsquashExecutable, QStringLiteral( "-s" ), m_source } ); Calamares::Utils::Runner r( { unsquashExecutable, QStringLiteral( "-s" ), m_source } );
r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing(); r.setLocation( Calamares::Utils::RunLocation::RunInHost ).enableOutputProcessing();
QObject::connect( &r, &decltype( r )::output, [&]( QString line ) { QObject::connect( &r,
if ( line.startsWith( "Number of inodes " ) ) &decltype( r )::output,
{ [ & ]( QString line )
m_inodes = line.split( ' ', SplitSkipEmptyParts ).last().toInt(); {
} if ( line.startsWith( "Number of inodes " ) )
} ); {
m_inodes = line.split( ' ', SplitSkipEmptyParts ).last().toInt();
}
} );
/* ignored */ r.run(); /* ignored */ r.run();
} }
if ( m_inodes <= 0 ) if ( m_inodes <= 0 )

View File

@@ -14,7 +14,6 @@
/** @brief Use Unsquash for extracting a filesystem /** @brief Use Unsquash for extracting a filesystem
* *
* NOTE: not implemented
*/ */
class UnsquashRunner : public Runner class UnsquashRunner : public Runner
{ {

View File

@@ -0,0 +1,2 @@
---
rootMountPoint: /tmp/fstest

View File

@@ -0,0 +1,4 @@
---
source: /tmp/src.fsa
sourcefs: fsarchive
destination: "/calasrc"

View File

@@ -2,7 +2,7 @@
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
# #
# Unpack a filesystem. Supported ways to "pack" the filesystem are: # Unpack a filesystem. Supported ways to "pack" the filesystem are:
# - fsarchiver # - fsarchiver in *savedir/restdir* mode (directories, not block devices)
# - squashfs # - squashfs
# #
# Configuration: # Configuration:
@@ -21,11 +21,19 @@
# - *sourcefs* the type of the source files; valid entries are # - *sourcefs* the type of the source files; valid entries are
# - `none` (this entry is ignored; kind of useless) # - `none` (this entry is ignored; kind of useless)
# - `fsarchiver` # - `fsarchiver`
# Aliases of this are `fsarchive`, `fsa` and `fsa-dir`. Uses
# fsarchiver in "restdir" mode.
# - `fsarchiver-block`
# Aliases of this are `fsa-block` and `fsa-fs`. Uses fsarchiver
# in "restfs" mode.
# - `squashfs` # - `squashfs`
# Aliases of this are `squash` and `unsquash`.
# - `tar`
# - *destination* path relative to rootMountPoint (so in the target # - *destination* path relative to rootMountPoint (so in the target
# system) where this filesystem is unpacked. It may be an # system) where this filesystem is unpacked. It may be an
# empty string, which effectively is / (the root) of the target # empty string, which effectively is / (the root) of the target
# system. # system.
#
source: /data/rootfs.fsa source: /data/rootfs.fsa
sourcefs: fsarchiver sourcefs: fsarchiver
destination: "/" destination: "/"