OSDN Git Service

Initial commit
authorRafael Costa Rega <rcostarega@gmail.com>
Mon, 25 May 2020 23:55:25 +0000 (20:55 -0300)
committerRafael Costa Rega <rcostarega@gmail.com>
Mon, 25 May 2020 23:55:25 +0000 (20:55 -0300)
290 files changed:
CHANGELOG [new file with mode: 0644]
COPYING [new file with mode: 0644]
Cnchi/1.png [new file with mode: 0644]
Cnchi/1.xcf [new file with mode: 0644]
Cnchi/101_gnome.gschema.override [new file with mode: 0644]
Cnchi/10_antergos [new file with mode: 0755]
Cnchi/2.png [new file with mode: 0644]
Cnchi/2.xcf [new file with mode: 0644]
Cnchi/20-intel.conf [new file with mode: 0644]
Cnchi/3.png [new file with mode: 0644]
Cnchi/4-RebornMaintenance.png [new file with mode: 0644]
Cnchi/4.png [new file with mode: 0644]
Cnchi/4.xcf [new file with mode: 0644]
Cnchi/95_cinnamon.gschema.override [new file with mode: 0644]
Cnchi/Border.png [new file with mode: 0644]
Cnchi/adwaita-day.jpg [new file with mode: 0644]
Cnchi/antergos-icon.png [new file with mode: 0755]
Cnchi/antergos-logo-mini2.png [new file with mode: 0644]
Cnchi/antergos-logo-mini2.xcf [new file with mode: 0644]
Cnchi/archlinux.svg [new file with mode: 0644]
Cnchi/ask.py [new file with mode: 0644]
Cnchi/auto_partition.py [new file with mode: 0644]
Cnchi/bashrc [new file with mode: 0644]
Cnchi/check.py [new file with mode: 0644]
Cnchi/cinnamon.sh [new file with mode: 0644]
Cnchi/cnchi-start.sh [new file with mode: 0755]
Cnchi/cnchi.png [new file with mode: 0755]
Cnchi/cnchi.py [new file with mode: 0755]
Cnchi/desktop.py [new file with mode: 0644]
Cnchi/desktop_info.py [new file with mode: 0644]
Cnchi/encfs.py [new file with mode: 0644]
Cnchi/features.py [new file with mode: 0644]
Cnchi/features_info.py [new file with mode: 0755]
Cnchi/geoip.py [new file with mode: 0644]
Cnchi/grub2.py [new file with mode: 0644]
Cnchi/gufw.desktop [new file with mode: 0644]
Cnchi/gufw.png [new file with mode: 0644]
Cnchi/gufw.svg [new file with mode: 0644]
Cnchi/hexagon erased.xcf [new file with mode: 0644]
Cnchi/info.py [new file with mode: 0755]
Cnchi/install.py [new file with mode: 0755]
Cnchi/lightdm-webkit2-greeter.conf [new file with mode: 0644]
Cnchi/logging_utils.py [new file with mode: 0755]
Cnchi/main_window.py [new file with mode: 0644]
Cnchi/metalink.py [new file with mode: 0755]
Cnchi/mirrors.py [new file with mode: 0755]
Cnchi/pac.py [new file with mode: 0755]
Cnchi/packages.xml [new file with mode: 0755]
Cnchi/pacman.conf [new file with mode: 0755]
Cnchi/pacman.tmpl [new file with mode: 0755]
Cnchi/pacman2.conf [new file with mode: 0644]
Cnchi/pacman2.tmpl [new file with mode: 0644]
Cnchi/post_install.py [new file with mode: 0755]
Cnchi/postinstall.sh [new file with mode: 0755]
Cnchi/rank_mirrors.py [new file with mode: 0755]
Cnchi/reborn-mirrorlist [new file with mode: 0644]
Cnchi/reborn-mirrorlist2 [new file with mode: 0644]
Cnchi/rebornos-icon-new.png [new file with mode: 0644]
Cnchi/rebornos-icon.png [new file with mode: 0644]
Cnchi/rebornos-install.desktop [new file with mode: 0644]
Cnchi/rebornos-logo-mini2.png [new file with mode: 0644]
Cnchi/rebornos-logo-mini2.xcf [new file with mode: 0644]
Cnchi/refresh-keys.desktop [new file with mode: 0644]
Cnchi/refresh-keys.sh [new file with mode: 0755]
Cnchi/sddm.conf [new file with mode: 0644]
Cnchi/select_packages.py [new file with mode: 0755]
Cnchi/show_message.py [new file with mode: 0644]
Cnchi/slides.py [new file with mode: 0755]
Cnchi/systemd_boot.py [new file with mode: 0644]
Cnchi/timezone.py [new file with mode: 0644]
Cnchi/update_db.py [new file with mode: 0755]
Cnchi/updating.sh [new file with mode: 0755]
Cnchi/welcome.py [new file with mode: 0644]
HELP_ME.sh [new file with mode: 0755]
README.md [new file with mode: 0644]
TEST_FILE.sh [new file with mode: 0644]
airootfs/README.md [new file with mode: 0644]
airootfs/etc/arch-release [new file with mode: 0644]
airootfs/etc/fstab [new file with mode: 0644]
airootfs/etc/hostname [new file with mode: 0644]
airootfs/etc/lightdm/lightdm.conf [new file with mode: 0644]
airootfs/etc/locale.conf [new file with mode: 0644]
airootfs/etc/locale.gen [new file with mode: 0644]
airootfs/etc/lsb-release [new file with mode: 0644]
airootfs/etc/machine-id [new file with mode: 0644]
airootfs/etc/modprobe.d/blacklist.conf [new file with mode: 0644]
airootfs/etc/os-release [new file with mode: 0644]
airootfs/etc/pacman.conf [new file with mode: 0644]
airootfs/etc/pam.d/su [new file with mode: 0644]
airootfs/etc/reborn-mirrorlist [new file with mode: 0644]
airootfs/etc/sddm.conf [new file with mode: 0644]
airootfs/etc/shells [new file with mode: 0644]
airootfs/etc/skel/.bash_profile2 [new file with mode: 0644]
airootfs/etc/skel/.bashrc [new file with mode: 0644]
airootfs/etc/skel/.config/autostart/refresh.desktop [new file with mode: 0644]
airootfs/etc/skel/.dmrc [new file with mode: 0644]
airootfs/etc/skel/.inputrc [new file with mode: 0644]
airootfs/etc/skel/.xinitrc2 [new file with mode: 0644]
airootfs/etc/skel/.xsession2 [new file with mode: 0644]
airootfs/etc/sudoers.d/g_wheel [new file with mode: 0644]
airootfs/etc/systemd/scripts/choose-mirror [new file with mode: 0755]
airootfs/etc/systemd/system/choose-mirror.service [new file with mode: 0644]
airootfs/etc/systemd/system/etc-pacman.d-gnupg.mount [new file with mode: 0644]
airootfs/etc/systemd/system/getty@tty1.service.d/autologin.conf [new file with mode: 0644]
airootfs/etc/systemd/system/internet.service [new file with mode: 0644]
airootfs/etc/systemd/system/lightdm.service [new file with mode: 0644]
airootfs/etc/systemd/system/pacman-init.service [new file with mode: 0644]
airootfs/etc/udev/rules.d/81-dhcpcd.rules [new file with mode: 0644]
airootfs/etc/xdg/autostart/cnchi.desktop [new file with mode: 0755]
airootfs/etc/xdg/autostart/internet.desktop [new file with mode: 0644]
airootfs/root/.automated_script.sh [new file with mode: 0755]
airootfs/root/.zlogin [new file with mode: 0644]
airootfs/root/customize_airootfs.sh [new file with mode: 0755]
airootfs/root/install.txt [new file with mode: 0644]
airootfs/usr/bin/auto-partition.sh [new file with mode: 0644]
airootfs/usr/bin/cnchi [new file with mode: 0755]
airootfs/usr/bin/cnchi-git [new file with mode: 0644]
airootfs/usr/bin/cnchi-rank.sh [new file with mode: 0755]
airootfs/usr/bin/cnchi-start.sh [new file with mode: 0755]
airootfs/usr/bin/install-antergos.sh [new file with mode: 0644]
airootfs/usr/bin/internet.sh [new file with mode: 0755]
airootfs/usr/bin/pacman-boot.sh [new file with mode: 0644]
airootfs/usr/share/applications/antergos-install.desktop [new file with mode: 0644]
airootfs/usr/share/applications/cnchi.desktop [new file with mode: 0755]
airootfs/usr/share/applications/cnchi.png [new file with mode: 0644]
airootfs/usr/share/applications/deepin-root-filemanager.desktop [new file with mode: 0644]
airootfs/usr/share/applications/flatpak.desktop [new file with mode: 0644]
airootfs/usr/share/backgrounds/abstract1-reborn2.png [new file with mode: 0644]
airootfs/usr/share/backgrounds/japanese-lake.jpg [new file with mode: 0644]
airootfs/usr/share/locale/af-ZA/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/af/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ar-SA/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ar/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ast/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/az-AZ/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/az/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/az@latin/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/az_IR/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/be/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/bg/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/bn-BD/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/bs-BA/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/bs/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ca/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ca@valencia/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/cs-CZ/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/cs/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/da/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/de-AT/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/de/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/el/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/en/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/en_GB/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/eo/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/es-VE/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/es/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/es_419/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/es_AR/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/es_CL/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/es_MX/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/et/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/eu/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/fa/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/fa_IR/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/fi/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/fr-BE/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/fr-CA/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/fr-FR/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/fr/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/gl/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/gu/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/he/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/hi/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/hr/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/hu/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/id/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/it-IT/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/it/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/it_CH/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ja/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ka/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ko/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ky/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/lt/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/mk/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/mr/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ms/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ms_MY/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/nb/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/nl/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/pa/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/pl-PL/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/pl/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/pt/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/pt_BR/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/pt_PT/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ro-RO/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ro/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ru/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/sk/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/sl-SI/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/sl/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/sr-RS/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/sr/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/sr@latin/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/sv/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/szl/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ta/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/ta_IN/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/tg/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/tl_PH/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/tr-TR/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/tr/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/uk/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/vi-VN/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/vi/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/wa/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/xh/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/zh/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/zh_CN.GB2312/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/zh_CN/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/zh_TW/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/locale/zu/LC_MESSAGES/cnchi.mo [new file with mode: 0644]
airootfs/usr/share/pacman/keyrings/rebornos-revoked [new file with mode: 0644]
airootfs/usr/share/pacman/keyrings/rebornos-trusted [new file with mode: 0644]
airootfs/usr/share/pacman/keyrings/rebornos.gpg [new file with mode: 0644]
airootfs/usr/share/pixmaps/cnchi.png [new file with mode: 0644]
build.sh [new file with mode: 0755]
clean.sh [new file with mode: 0755]
config [new file with mode: 0644]
efiboot/loader/entries/archiso-x86_64-cd.conf [new file with mode: 0644]
efiboot/loader/entries/archiso-x86_64-usb.conf [new file with mode: 0644]
efiboot/loader/entries/nvidia-fallback.conf [new file with mode: 0644]
efiboot/loader/entries/uefi-shell-v1-x86_64.conf [new file with mode: 0644]
efiboot/loader/entries/uefi-shell-v2-x86_64.conf [new file with mode: 0644]
efiboot/loader/loader.conf [new file with mode: 0644]
git-v2.sh [new file with mode: 0755]
gsettings/101_reborn_budgie.gschema.override [new file with mode: 0755]
gsettings/95_reborn_deepin.gschema.override [new file with mode: 0644]
images/apricity.png [new file with mode: 0644]
images/cinnamon.png [new file with mode: 0644]
images/deepin.png [new file with mode: 0644]
images/desktop-environment-apricity.svg [new file with mode: 0644]
images/desktop-environment-budgie.svg [new file with mode: 0644]
images/desktop-environment-i3.svg [new file with mode: 0644]
images/desktop-environment-pantheon.svg [new file with mode: 0644]
images/desktop-environment-windows.svg [new file with mode: 0644]
images/enlightenment.png [new file with mode: 0644]
images/i3.png [new file with mode: 0644]
images/kde.png [new file with mode: 0644]
images/lxqt.png [new file with mode: 0644]
images/openbox.png [new file with mode: 0644]
images/pantheon.png [new file with mode: 0644]
images/windows.png [new file with mode: 0644]
images/xfce.png [new file with mode: 0644]
isolinux/isolinux.cfg [new file with mode: 0644]
mkinitcpio.conf [new file with mode: 0644]
packages.both [new file with mode: 0755]
packages2.both [new file with mode: 0755]
pacman-init.service [new file with mode: 0644]
pacman.conf [new file with mode: 0644]
rebornos.gpg [new file with mode: 0644]
run.sh [new file with mode: 0755]
scripts/antergos-mirrorlist [new file with mode: 0644]
scripts/conky-start.desktop [new file with mode: 0644]
scripts/deepin-fix.service [new file with mode: 0755]
scripts/deepin-fix.sh [new file with mode: 0755]
scripts/flatpak.desktop [new file with mode: 0644]
scripts/flatpak.sh [new file with mode: 0755]
scripts/mate-panel.desktop [new file with mode: 0644]
scripts/obmenu-gen.desktop [new file with mode: 0644]
scripts/openbox-config.sh [new file with mode: 0755]
scripts/pkcon.sh [new file with mode: 0755]
scripts/pkcon2.sh [new file with mode: 0755]
scripts/plymouth-reborn.desktop [new file with mode: 0644]
scripts/plymouth.sh [new file with mode: 0755]
scripts/reflector-antergos [new file with mode: 0755]
scripts/tint2-start.desktop [new file with mode: 0644]
scripts/update.desktop [new file with mode: 0644]
set_password [new file with mode: 0644]
syslinux/archiso.cfg [new file with mode: 0644]
syslinux/archiso_head.cfg [new file with mode: 0644]
syslinux/archiso_pxe.cfg [new file with mode: 0644]
syslinux/archiso_sys.cfg [new file with mode: 0644]
syslinux/archiso_tail.cfg [new file with mode: 0644]
syslinux/splash(copy).png [new file with mode: 0644]
syslinux/splash.png [new file with mode: 0644]
syslinux/syslinux.cfg [new file with mode: 0644]
test-cnchi.sh [new file with mode: 0755]
translations.sh [new file with mode: 0755]

diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644 (file)
index 0000000..3e91409
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,130 @@
+python-parted and python-pyparted compiled and required by the installer.
+
+
+Important module for cnchi and not existing in the Arch Linux repository:
+
+python-pyparted
+
+For prevent module not found when "import parted", is compiled and present in RebornOS repo.
+
+
+
+Gnome extensions used (are installed from the RebornOS repository):
+
+gnome-shell-extension-appindicator  (KStatusNotifierItem/AppIndicator Support)
+gnome-shell-extension-dash-to-panel
+
+
+Visual theme used:
+
+yaru
+papirus icon theme
+
+The RebornOS live installer can be used as the original Arch Linux installation ISO
+for system recovery, as it comes with arch-install-scripts installed.
+
+===========================================================================
+
+2020.04.27
+
+Previously, cnchi was downloaded from the Antergos (read-only) Gitlab repository.
+Now, cnchi is downloaded from our repository.
+Location: https://repo.rebornos.org/RebornOS/sources/cnchi/
+
+
+===========================================================================
+
+2020.04.29
+
+Change load.conf (live efi boot) from:
+
+timeout 3
+default archiso-x86_64
+
+to:
+
+timeout 5
+default archiso-x86_64.conf
+
+Live image now starts correctly in EFI mode
+
+Change the Wallpaper
+
+Change to a new location/subgroup:
+
+https://gitlab.com/reborn-os-team/rebornos-cnchi/cnchi-gnome-based
+
+
+=========================================================================
+
+2020.05.07
+
+Installation options (file selection) exist in:
+
+desktop_info.py
+feature_info.py
+
+
+Nixnote2 (abandoned maintenance, and malfunctioning) was changed to qownnotes
+
+
+Fixed Uncomplicated Firewall (gufw) shortcut to display properly on different desktops
+
+
+File /etc/pacman.d/reborn-mirrorlist change:
+
+chmod 644 /etc/pacman.d/reborn-mirrorlist
+
+
+=========================================================================
+
+2020.05.22
+
+In file "desktop.py" change in line 65:
+
+self.desktop_choice = 'deepin'
+
+...to:
+
+self.desktop_choice = 'base'
+
+file 172:
+
+        # Set Gnome as default
+        self.select_default_row(desktop_info.NAMES["gnome"])
+        
+...changed to:
+
+        # Set base as default
+        self.select_default_row(desktop_info.NAMES["base"])
+
+...so that the default selection of the desktop at the time of installation goes
+from Deepin to base (base is chosen only because it is the first option
+existing in the list).
+
+
+
+In file "desktop_info.py" change the Deepin description from:
+
+    'deepin': _("Deepin desktop is a lightweight, elegant desktop environment that "
+                "has been commented as a mix between Windows and Macs by many of its' "
+                "users. It was originally created for the Linux Deepin distribution. "
+                "Now, DDE will support most Linux operating systems such as Arch "
+                "Linux, Ubuntu, Fedora, openSUSE etc."),
+                
+...to:
+
+   'deepin': _("IMPORTANT: Keep in mind that the Deepin desktop can often be unstable. "
+                "This does not depend on us, but on the developers of Deepin who "
+                "usually upload BETA versions of the desktop or some components in the "
+                "stable repositories of Arch Linux."),
+
+
+Added file "auto_partition.py" in Cnchi folder with changes to original one,
+and modified build.sh to make this change when compiling.
+Here we changed the references from AntergosRoot to RebornOSRoot, etc.
+To return to the previous condition, remove the modification in build.sh.
+
+
+
+
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program 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.
+
+    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/Cnchi/1.png b/Cnchi/1.png
new file mode 100644 (file)
index 0000000..01e71b4
Binary files /dev/null and b/Cnchi/1.png differ
diff --git a/Cnchi/1.xcf b/Cnchi/1.xcf
new file mode 100644 (file)
index 0000000..5fbc173
Binary files /dev/null and b/Cnchi/1.xcf differ
diff --git a/Cnchi/101_gnome.gschema.override b/Cnchi/101_gnome.gschema.override
new file mode 100644 (file)
index 0000000..d2072c4
--- /dev/null
@@ -0,0 +1,41 @@
+[org.gnome.desktop.interface]
+clock-format='24h'
+clock-show-date=true
+clock-show-seconds=false
+gtk-theme='Yaru'
+icon-theme='Papirus'
+
+[org.gnome.shell.extensions.user-theme]
+name='Yaru'
+
+[org.gnome.desktop.peripherals.touchpad]
+natural-scroll=true
+tap-to-click=true
+
+[org.gnome.desktop.datetime]
+automatic-timezone=true
+
+[org.gnome.desktop.wm.preferences]
+button-layout='appmenu:minimize,maximize,close'
+theme='Yaru'
+
+[org.gnome.nautilus.preferences]
+show-create-link=true
+
+[org.gnome.desktop.background]
+picture-uri='file:///usr/share/backgrounds/gnome/adwaita-day.jpg'
+
+[org.gnome.shell]
+enabled-extensions=['dash-to-panel@jderose9.github.com', 'drive-menu@gnome-shell-extensions.gcampax.github.com', 'appindicatorsupport@rgcjonas.gmail.com',  'screenshot-window-sizer@gnome-shell-extensions.gcampax.github.com', 'user-theme@gnome-shell-extensions.gcampax.github.com', 'pamac-updates@manjaro.org']
+always-show-log-out=true
+
+[org.gnome.settings-daemon.plugins.xsettings]
+antialiasing='rgba'
+
+[org.gnome.mutter]
+center-new-windows = true
+dynamic-workspaces = true
+
+[org.gnome.shell.extensions.activities-config]
+activities-config-button-no-text=true
+
diff --git a/Cnchi/10_antergos b/Cnchi/10_antergos
new file mode 100755 (executable)
index 0000000..cd3626b
--- /dev/null
@@ -0,0 +1,324 @@
+#! /bin/sh
+set -e
+
+# grub-mkconfig helper script.
+# Copyright (C) 2006,2007,2008,2009,2010  Free Software Foundation, Inc.
+#
+# GRUB 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.
+#
+# GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+
+# Disable 10_linux
+
+if [ -f /etc/grub.d/10_linux ]; then
+    chmod -x /etc/grub.d/10_linux
+fi
+
+# Disable 10_archlinux
+if [ -f /etc/grub.d/10_archlinux ]; then
+    chmod -x /etc/grub.d/10_archlinux
+fi
+
+prefix="/usr"
+exec_prefix="/usr"
+datarootdir="/usr/share"
+
+. "${datarootdir}/grub/grub-mkconfig_lib"
+
+export TEXTDOMAIN=grub
+export TEXTDOMAINDIR="${datarootdir}/locale"
+
+CLASS="--class gnu-linux --class gnu --class os"
+
+if [ "${GRUB_DISTRIBUTOR}" = "Antergos" ]; then
+  CLASS="--class arch ${CLASS}"
+fi
+
+if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then
+  OS=Linux
+else
+  OS="RebornOS"
+  CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}"
+fi
+
+# loop-AES arranges things so that /dev/loop/X can be our root device, but
+# the initrds that Linux uses don't like that.
+case ${GRUB_DEVICE} in
+  /dev/loop/*|/dev/loop[0-9])
+    GRUB_DEVICE=`losetup ${GRUB_DEVICE} | sed -e "s/^[^(]*(\([^)]\+\)).*/\1/"`
+  ;;
+esac
+
+if [ "x${GRUB_DEVICE_UUID}" = "x" ] || [ "x${GRUB_DISABLE_LINUX_UUID}" = "xtrue" ] \
+    || ! test -e "/dev/disk/by-uuid/${GRUB_DEVICE_UUID}" \
+    || uses_abstraction "${GRUB_DEVICE}" lvm; then
+  LINUX_ROOT_DEVICE=${GRUB_DEVICE}
+else
+  LINUX_ROOT_DEVICE=UUID=${GRUB_DEVICE_UUID}
+fi
+
+case x"$GRUB_FS" in
+    xbtrfs)
+       rootsubvol="`make_system_path_relative_to_its_root /`"
+       rootsubvol="${rootsubvol#/}"
+       if [ "x${rootsubvol}" != x ]; then
+           GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}"
+       fi;;
+    xzfs)
+       rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`
+       bootfs="`make_system_path_relative_to_its_root / | sed -e "s,@$,,"`"
+       LINUX_ROOT_DEVICE="ZFS=${rpool}${bootfs}"
+       ;;
+esac
+
+intel_ucode=
+if test -e "/boot/intel-ucode.img" ; then
+    gettext_printf "Found Intel Microcode image\n" >&2
+    intel_ucode="$(make_system_path_relative_to_its_root /boot/intel-ucode.img)"
+fi
+
+title_correction_code=
+
+linux_entry ()
+{
+  os="$1"
+  version="$2"
+  type="$3"
+  args="$4"
+
+  if [ "$os" == "Antergos Linux" ] || [ "$os" == "Arch Linux" ] || [ "$os" == "Manjaro Linux" ] || [ "$os" == "RebornOS" ]; then
+      is_arch=True
+  fi
+
+  if [ -z "$boot_device_id" ]; then
+      boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")"
+  fi
+  if [ x$type != xsimple ] && [ "$is_arch" != True ]; then
+      case $type in
+         recovery)
+             title="$(gettext_printf "%s, with Linux %s (recovery mode)" "${os}" "${version}")" ;;
+         fallback)
+          title="$(gettext_printf "RebornOS, with Linux %s (fallback initramfs)" "${os}" "${version}")" ;;
+         *)
+             title="$(gettext_printf "RebornOS, with %s Kernel" "${os}" "${version}")" ;;
+      esac
+      if [ x"$title" = x"$GRUB_ACTUAL_DEFAULT" ] || [ x"Previous Linux versions>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then
+         replacement_title="$(echo "Advanced options for RebornOS" | sed 's,>,>>,g')>$(echo "$title" | sed 's,>,>>,g')"
+         quoted="$(echo "$GRUB_ACTUAL_DEFAULT" | grub_quote)"
+         title_correction_code="${title_correction_code}if [ \"x\$default\" = '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;"
+         grub_warn "$(gettext_printf "Please don't use old title \`%s' for GRUB_DEFAULT, use \`%s' (for versions before 2.00) or \`%s' (for 2.00 or later)" "$GRUB_ACTUAL_DEFAULT" "$replacement_title" "gnulinux-advanced-$boot_device_id>gnulinux-$version-$type-$boot_device_id")"
+      fi
+      echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
+  else
+       if [ "$is_arch" ]; then
+               if [ x$type != xfallback ]; then
+               if [ "$version" == "linux" ]; then
+                title="$(gettext_printf "%s" "${os}")"
+               elif [ "$version" == "linux-lts" ]; then
+                title="$(gettext_printf "RebornOS LTS Kernel" "${os}")"
+               else
+                title="$(gettext_printf "RebornOS, with %s Kernel" "${os}" "${version}")"
+               fi
+        else
+                       if [ "$version" == "linux" ]; then
+                title="$(gettext_printf "RebornOS - Fallback" "${os}")"
+               elif [ "$version" == "linux-lts" ]; then
+                title="$(gettext_printf "RebornOS LTS Kernel - Fallback" "${os}")"
+               else
+                title="$(gettext_printf "RebornOS, with %s Kernel - Fallback" "${os}" "${version}")"
+               fi
+        fi
+
+               os="$title"
+       fi
+       echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
+  fi
+  if [ x$type != xrecovery ] && [ x$type != xfallback ] ; then
+      save_default_entry | grub_add_tab
+  fi
+
+  # Use ELILO's generic "efifb" when it's known to be available.
+  # FIXME: We need an interface to select vesafb in case efifb can't be used.
+  if [ "x$GRUB_GFXPAYLOAD_LINUX" = x ]; then
+      echo "   load_video" | sed "s/^/$submenu_indentation/"
+      if grep -qx "CONFIG_FB_EFI=y" "${config}" 2> /dev/null \
+         && grep -qx "CONFIG_VT_HW_CONSOLE_BINDING=y" "${config}" 2> /dev/null; then
+         echo "        set gfxpayload=keep" | sed "s/^/$submenu_indentation/"
+      fi
+  else
+      if [ "x$GRUB_GFXPAYLOAD_LINUX" != xtext ]; then
+         echo "        load_video" | sed "s/^/$submenu_indentation/"
+      fi
+      echo "   set gfxpayload=$GRUB_GFXPAYLOAD_LINUX" | sed "s/^/$submenu_indentation/"
+  fi
+
+  echo "       insmod gzio" | sed "s/^/$submenu_indentation/"
+
+  if [ x$dirname = x/ ]; then
+    if [ -z "${prepare_root_cache}" ]; then
+      prepare_root_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE} | grub_add_tab)"
+    fi
+    printf '%s\n' "${prepare_root_cache}" | sed "s/^/$submenu_indentation/"
+  else
+    if [ -z "${prepare_boot_cache}" ]; then
+      prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | grub_add_tab)"
+    fi
+    printf '%s\n' "${prepare_boot_cache}" | sed "s/^/$submenu_indentation/"
+  fi
+  message="$(gettext_printf "Loading  %s kernel ..." ${version})"
+  sed "s/^/$submenu_indentation/" << EOF
+       echo    '$(echo "$message" | grub_quote)'
+       linux   ${rel_dirname}/${basename} root=${linux_root_device_thisversion} rw ${args}
+EOF
+       if test -n "${initrd}" ; then
+               if [ "$is_arch" ] && [ x$type = xfallback ] ; then
+                       initrd="initramfs-${version}-fallback.img"
+               fi
+    # TRANSLATORS: ramdisk isn't identifier. Should be translated.
+       message="$(gettext_printf "Loading initial ramdisk ...")"
+       sed "s/^/$submenu_indentation/" << EOF
+       echo    '$(echo "$message" | grub_quote)'
+       initrd  ${intel_ucode} ${rel_dirname}/${initrd}
+EOF
+       fi
+       sed "s/^/$submenu_indentation/" << EOF
+}
+EOF
+}
+
+machine=`uname -m`
+case "x$machine" in
+    xi?86 | xx86_64)
+       list=`for i in /boot/vmlinuz-* /vmlinuz-* /boot/kernel-* ; do
+                  if grub_file_is_not_garbage "$i" ; then echo -n "$i " ; fi
+              done` ;;
+    *)
+       list=`for i in /boot/vmlinuz-* /boot/vmlinux-* /vmlinuz-* /vmlinux-* /boot/kernel-* ; do
+                  if grub_file_is_not_garbage "$i" ; then echo -n "$i " ; fi
+            done` ;;
+esac
+
+case "$machine" in
+    i?86) GENKERNEL_ARCH="x86" ;;
+    mips|mips64) GENKERNEL_ARCH="mips" ;;
+    mipsel|mips64el) GENKERNEL_ARCH="mipsel" ;;
+    arm*) GENKERNEL_ARCH="arm" ;;
+    *) GENKERNEL_ARCH="$machine" ;;
+esac
+
+prepare_boot_cache=
+prepare_root_cache=
+boot_device_id=
+title_correction_code=
+
+# Extra indentation to add to menu entries in a submenu. We're not in a submenu
+# yet, so it's empty. In a submenu it will be equal to '\t' (one tab).
+submenu_indentation=""
+
+is_top_level=true
+while [ "x$list" != "x" ] ; do
+  if [ $(echo $list | grep -q '[0-9]') ]; then
+    linux=`version_find_latest $list`
+  else
+    artmp=($list)
+    linux=${artmp[0]}
+  fi
+
+  gettext_printf "Found linux image: %s\n" "$linux" >&2
+  basename=`basename $linux`
+  dirname=`dirname $linux`
+  rel_dirname=`make_system_path_relative_to_its_root $dirname`
+  version=`echo $basename | sed -e "s,vmlinuz-,,g"`
+  alt_version=`echo $version | sed -e "s,\.old$,,g"`
+  linux_root_device_thisversion="${LINUX_ROOT_DEVICE}"
+
+  initrd=
+  for i in "initrd.img-${version}" "initrd-${version}.img" "initrd-${version}.gz" \
+          "initrd-${version}" "initramfs-${version}.img" \
+          "initrd.img-${alt_version}" "initrd-${alt_version}.img" \
+          "initrd-${alt_version}" "initramfs-${alt_version}.img" \
+           "initrd-linux-${version}" "initramfs-linux-${version}.img" \
+           "initrd-linux-${alt_version}" "initramfs-linux-${alt_version}.img" \
+          "initramfs-genkernel-${version}" \
+          "initramfs-genkernel-${alt_version}" \
+          "initramfs-genkernel-${GENKERNEL_ARCH}-${version}" \
+          "initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}"; do
+    if test -e "${dirname}/${i}" ; then
+      initrd="$i"
+      break
+    fi
+  done
+
+  config=
+  for i in "${dirname}/config-${version}" "${dirname}/config-${alt_version}" "/etc/kernels/kernel-config-${version}" ; do
+    if test -e "${i}" ; then
+      config="${i}"
+      break
+    fi
+  done
+
+  initramfs=
+  if test -n "${config}" ; then
+      initramfs=`grep CONFIG_INITRAMFS_SOURCE= "${config}" | cut -f2 -d= | tr -d \"`
+  fi
+
+  if test -n "${initrd}" ; then
+    gettext_printf "Found initrd image: %s\n" "${dirname}/${initrd}" >&2
+  elif test -z "${initramfs}" ; then
+    # "UUID=" and "ZFS=" magic is parsed by initrd or initramfs.  Since there's
+    # no initrd or builtin initramfs, it can't work here.
+    linux_root_device_thisversion=${GRUB_DEVICE}
+  fi
+
+  # GRUB_DISABLE_SUBMENU=y is the documented value to disable the submenu, testing for true is for backwards compatibility with Antergos
+  # see https://wiki.archlinux.org/index.php/GRUB/Tips_and_tricks#Disable_submenu
+  if [ "x$is_top_level" = xtrue ] && [ "x${GRUB_DISABLE_SUBMENU}" != xy ] && [ "x${GRUB_DISABLE_SUBMENU}" != xtrue ]; then
+    linux_entry "${OS}" "${version}" simple \
+    "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
+
+    submenu_indentation="$grub_tab"
+
+    if [ -z "$boot_device_id" ]; then
+       boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")"
+    fi
+    # TRANSLATORS: %s is replaced with an OS name
+    echo "submenu '$(gettext_printf "Advanced options for %s" "RebornOS" | grub_quote)' \$menuentry_id_option 'gnulinux-advanced-$boot_device_id' {"
+    is_top_level=false
+  fi
+
+  linux_entry "${OS}" "${version}" advanced \
+              "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
+  if test -e "${dirname}/initramfs-${version}-fallback.img" ; then
+      initrd="initramfs-${version}-fallback.img"
+
+      if test -n "${initrd}" ; then
+        gettext_printf "Found fallback initramfs image: %s\n" "${dirname}/${initrd}" >&2
+      fi
+
+      linux_entry "${OS}" "${version}" fallback \
+      "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"
+  fi
+  if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then
+    linux_entry "${OS}" "${version}" recovery \
+                "single ${GRUB_CMDLINE_LINUX}"
+  fi
+
+  list=`echo $list | tr ' ' '\n' | grep -vx $linux | tr '\n' ' '`
+done
+
+# If at least one kernel was found, then we need to
+# add a closing '}' for the submenu command.
+if [ x"$is_top_level" != xtrue ]; then
+  echo '}'
+fi
+
+echo "$title_correction_code"
diff --git a/Cnchi/2.png b/Cnchi/2.png
new file mode 100644 (file)
index 0000000..7ce1094
Binary files /dev/null and b/Cnchi/2.png differ
diff --git a/Cnchi/2.xcf b/Cnchi/2.xcf
new file mode 100644 (file)
index 0000000..64a22c8
Binary files /dev/null and b/Cnchi/2.xcf differ
diff --git a/Cnchi/20-intel.conf b/Cnchi/20-intel.conf
new file mode 100644 (file)
index 0000000..27c23c7
--- /dev/null
@@ -0,0 +1,5 @@
+Section "Device"
+   Identifier  "Intel Graphics"
+   Driver      "intel"
+   Option      "AccelMethod" "uxa"
+EndSection
diff --git a/Cnchi/3.png b/Cnchi/3.png
new file mode 100644 (file)
index 0000000..c0e510a
Binary files /dev/null and b/Cnchi/3.png differ
diff --git a/Cnchi/4-RebornMaintenance.png b/Cnchi/4-RebornMaintenance.png
new file mode 100644 (file)
index 0000000..f33ce7f
Binary files /dev/null and b/Cnchi/4-RebornMaintenance.png differ
diff --git a/Cnchi/4.png b/Cnchi/4.png
new file mode 100644 (file)
index 0000000..5ae810a
Binary files /dev/null and b/Cnchi/4.png differ
diff --git a/Cnchi/4.xcf b/Cnchi/4.xcf
new file mode 100644 (file)
index 0000000..302f500
Binary files /dev/null and b/Cnchi/4.xcf differ
diff --git a/Cnchi/95_cinnamon.gschema.override b/Cnchi/95_cinnamon.gschema.override
new file mode 100644 (file)
index 0000000..ce9b699
--- /dev/null
@@ -0,0 +1,11 @@
+[org.cinnamon.desktop.interface]
+keyboard-layout-show-flags=true
+gtk-theme = 'Mint-Y'
+icon-theme = 'Mint-Y'
+cursor-theme = 'Adwaita'
+clock-use-24h=true
+clock-show-date=false
+
+[org.cinnamon.desktop.wm.preferences]
+theme = 'Mint-Y'
+
diff --git a/Cnchi/Border.png b/Cnchi/Border.png
new file mode 100644 (file)
index 0000000..8d6e86f
Binary files /dev/null and b/Cnchi/Border.png differ
diff --git a/Cnchi/adwaita-day.jpg b/Cnchi/adwaita-day.jpg
new file mode 100644 (file)
index 0000000..3068e13
Binary files /dev/null and b/Cnchi/adwaita-day.jpg differ
diff --git a/Cnchi/antergos-icon.png b/Cnchi/antergos-icon.png
new file mode 100755 (executable)
index 0000000..181725b
Binary files /dev/null and b/Cnchi/antergos-icon.png differ
diff --git a/Cnchi/antergos-logo-mini2.png b/Cnchi/antergos-logo-mini2.png
new file mode 100644 (file)
index 0000000..f2bd332
Binary files /dev/null and b/Cnchi/antergos-logo-mini2.png differ
diff --git a/Cnchi/antergos-logo-mini2.xcf b/Cnchi/antergos-logo-mini2.xcf
new file mode 100644 (file)
index 0000000..0958148
Binary files /dev/null and b/Cnchi/antergos-logo-mini2.xcf differ
diff --git a/Cnchi/archlinux.svg b/Cnchi/archlinux.svg
new file mode 100644 (file)
index 0000000..31daadb
Binary files /dev/null and b/Cnchi/archlinux.svg differ
diff --git a/Cnchi/ask.py b/Cnchi/ask.py
new file mode 100644 (file)
index 0000000..e7a21e3
--- /dev/null
@@ -0,0 +1,520 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# ask.py
+#
+# Copyright © 2013-2019 RebornOS
+#
+# This file is part of Cnchi.
+#
+# Cnchi 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.
+#
+# Cnchi 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.
+#
+# The following additional terms are in effect as per Section 7 of the license:
+#
+# The preservation of all legal notices and author attributions in
+# the material or in the Appropriate Legal Notices displayed
+# by works containing it is required.
+#
+# You should have received a copy of the GNU General Public License
+# along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+
+""" Asks which type of installation the user wants to perform """
+
+import os
+import logging
+import subprocess
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+import bootinfo
+from pages.gtkbasebox import GtkBaseBox
+import misc.extra as misc
+from browser_window import BrowserWindow
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+
+def check_alongside_disk_layout():
+    """ Alongside can only work if user has followed the recommended
+        BIOS-Based Disk-Partition Configurations shown in
+        http://technet.microsoft.com/en-us/library/dd744364(v=ws.10).aspx """
+
+    # TODO: Add more scenarios where alongside could work
+
+    partitions = misc.get_partitions()
+    # logging.debug(partitions)
+    extended = False
+    for partition in partitions:
+        if misc.is_partition_extended(partition):
+            extended = True
+
+    if extended:
+        return False
+
+    # We just seek for sda partitions
+    partitions_sda = []
+    for partition in partitions:
+        if "sda" in partition:
+            partitions_sda.append(partition)
+
+    # There's no extended partition, so all partitions must be primary
+    if len(partitions_sda) < 4:
+        return True
+
+    return False
+
+
+def load_zfs():
+    """ Load ZFS kernel module """
+    cmd = ["modprobe", "zfs"]
+    try:
+        with misc.raised_privileges():
+            subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+        logging.debug("ZFS kernel module loaded successfully.")
+    except subprocess.CalledProcessError as err:
+        error_msg = err.output.decode().rstrip()
+        logging.warning("%s", error_msg)
+        return False
+    return True
+
+
+class InstallationAsk(GtkBaseBox):
+    """ Asks user which type of installation wants to perform """
+    def __init__(self, params, prev_page="mirrors", next_page=None):
+        super().__init__(self, params, "ask", prev_page, next_page)
+
+        data_dir = self.settings.get("data")
+
+        partitioner_dir = os.path.join(
+            data_dir,
+            "images",
+            "partitioner",
+            "small")
+
+        image = self.gui.get_object("automatic_image")
+        path = os.path.join(partitioner_dir, "automatic.png")
+        image.set_from_file(path)
+
+        # image = self.gui.get_object("alongside_image")
+        # path = os.path.join(partitioner_dir, "alongside.png")
+        # image.set_from_file(path)
+
+        image = self.gui.get_object("advanced_image")
+        path = os.path.join(partitioner_dir, "advanced.png")
+        image.set_from_file(path)
+
+        self.other_oses = []
+
+        # DISABLE ALONGSIDE INSTALLATION. IT'S NOT READY YET
+        # enable_alongside = self.check_alongside()
+        enable_alongside = False
+        self.settings.set('enable_alongside', enable_alongside)
+
+        #if enable_alongside:
+        #    msg = "Cnchi will enable the 'alongside' installation mode."
+        #else:
+        #    msg = "Cnchi will NOT enable the 'alongside' installation mode."
+        #logging.debug(msg)
+
+        # By default, select automatic installation
+        self.next_page = "installation_automatic"
+        self.settings.set("partition_mode", "automatic")
+
+        self.is_zfs_available = load_zfs()
+
+        self.enable_automatic_options(True)
+
+        btn_label = _(
+            "I need help with an RebornOS / Windows(tm) dual boot setup!")
+        btn = Gtk.Button.new_with_label(btn_label)
+        btn.connect(
+            'clicked', self.alongside_wiki_button_clicked)
+        ask_box = self.gui.get_object("ask")
+        ask_box.pack_start(btn, True, False, 0)
+
+        self.browser = None
+
+    def alongside_wiki_button_clicked(self, _widget, _data=None):
+        """ Shows dual installation wiki page in a browser window  """
+        try:
+            self.browser = BrowserWindow("RebornOS Wiki - Dual Boot")
+            url = ("https://sourceforge.net/p/rebornos/wiki/Dual%20Boot%20RebornOS%20%26%20Windows%20UEFI%20%28Expanded%29%20%E2%80%93%20by%20linuxhelmet/")
+            self.browser.load_url(url)
+        except Exception as err:
+            logging.warning("Could not show RebornOS wiki: %s", err)
+
+    def check_alongside(self):
+        """ Check if alongside installation type must be enabled.
+            Alongside only works when Windows is installed on sda """
+
+        enable_alongside = False
+
+        # FIXME: Alongside does not work in UEFI systems
+        if os.path.exists("/sys/firmware/efi"):
+            msg = "The 'alongside' installation mode does not work in UEFI systems"
+            logging.debug(msg)
+            enable_alongside = False
+        else:
+            oses = bootinfo.get_os_dict()
+            self.other_oses = []
+            for key in oses:
+                # We only check the first hard disk
+                non_valid = ["unknown", "Swap",
+                             "Data or Swap", self.other_oses]
+                if "sda" in key and oses[key] not in non_valid:
+                    self.other_oses.append(oses[key])
+
+            if self.other_oses:
+                for detected_os in self.other_oses:
+                    if "windows" in detected_os.lower():
+                        logging.debug("Windows(tm) OS detected.")
+                        enable_alongside = True
+                if not enable_alongside:
+                    logging.debug("Windows(tm) OS not detected.")
+                    enable_alongside = False
+            else:
+                logging.debug("Can't detect any OS in device sda.")
+                enable_alongside = False
+
+            if not check_alongside_disk_layout():
+                msg = "Unsuported disk layout for the 'alongside' installation mode"
+                logging.debug(msg)
+                enable_alongside = False
+
+        return enable_alongside
+
+    def enable_automatic_options(self, status):
+        """ Enables or disables automatic installation options """
+        names = [
+            "encrypt_checkbutton", "encrypt_label",
+            "lvm_checkbutton", "lvm_label",
+            "home_checkbutton", "home_label"]
+
+        for name in names:
+            obj = self.gui.get_object(name)
+            obj.set_sensitive(status)
+
+        names = ["zfs_checkbutton", "zfs_label"]
+        for name in names:
+            obj = self.gui.get_object(name)
+            obj.set_sensitive(status and self.is_zfs_available)
+
+    def prepare(self, direction):
+        """ Prepares screen """
+        # Read options and set widgets accordingly
+        widgets_settings = {
+            ('use_luks', 'encrypt_checkbutton'), ('use_lvm', 'lvm_checkbutton'),
+            ('use_zfs', 'zfs_checkbutton'), ('use_home', 'home_checkbutton')}
+
+        for (setting_name, widget_id) in widgets_settings:
+            widget = self.gui.get_object(widget_id)
+            setting_value = self.settings.get(setting_name)
+            widget.set_active(setting_value)
+
+        self.translate_ui()
+        self.show_all()
+
+        if not self.settings.get('enable_alongside'):
+            self.hide_option("alongside")
+
+        self.forward_button.set_sensitive(True)
+
+    def hide_option(self, option):
+        """ Hides widgets """
+        widgets = []
+        if option == "alongside":
+            widgets = [
+                "alongside_radiobutton",
+                "alongside_description",
+                "alongside_image"]
+
+        for name in widgets:
+            widget = self.gui.get_object(name)
+            if widget is not None:
+                widget.hide()
+
+    def get_os_list_str(self):
+        """ Get string with the detected os names """
+        os_str = ""
+        len_other_oses = len(self.other_oses)
+        if len_other_oses > 0:
+            if len_other_oses > 1:
+                if len_other_oses == 2:
+                    os_str = _(" and ").join(self.other_oses)
+                else:
+                    os_str = ", ".join(self.other_oses)
+            else:
+                os_str = self.other_oses[0]
+
+        # Truncate string if it's too large
+        if len(os_str) > 40:
+            os_str = os_str[:40] + "..."
+
+        return os_str
+
+    def translate_ui(self):
+        """ Translates screen before showing it """
+        self.header.set_subtitle(_("Installation Type"))
+
+        self.forward_button.set_always_show_image(True)
+        self.forward_button.set_sensitive(True)
+
+        # description_style = '<span style="italic">{0}</span>'
+        # bold_style = '<span weight="bold">{0}</span>'
+
+        oses_str = self.get_os_list_str()
+
+        max_width_chars = 80
+
+        # Automatic Install
+        radio = self.gui.get_object("automatic_radiobutton")
+        if oses_str:
+            txt = _("Replace {0} with RebornOS").format(oses_str)
+        else:
+            txt = _("Erase disk and install RebornOS")
+        radio.set_label(txt)
+        radio.set_name('auto_radio_btn')
+
+        label = self.gui.get_object("automatic_description")
+        txt = _("Warning: This will erase ALL data on your disk.")
+        # txt = description_style.format(txt)
+        label.set_text(txt)
+        label.set_name("automatic_desc")
+        label.set_hexpand(False)
+        label.set_line_wrap(True)
+        label.set_max_width_chars(max_width_chars)
+
+        button = self.gui.get_object("encrypt_checkbutton")
+        txt = _("Encrypt this installation for increased security.")
+        button.set_label(txt)
+        button.set_name("enc_btn")
+        button.set_hexpand(False)
+        # button.set_line_wrap(True)
+        # button.set_max_width_chars(max_width_chars)
+
+        label = self.gui.get_object("encrypt_label")
+        txt = _("You will be asked to create an encryption password in the "
+                "next step.")
+        # txt = description_style.format(txt)
+        label.set_text(txt)
+        label.set_name("enc_label")
+        label.set_hexpand(False)
+        label.set_line_wrap(True)
+        label.set_max_width_chars(max_width_chars)
+
+        button = self.gui.get_object("lvm_checkbutton")
+        txt = _("Use LVM with this installation.")
+        button.set_label(txt)
+        button.set_name("lvm_btn")
+        button.set_hexpand(False)
+        # button.set_line_wrap(True)
+        # button.set_max_width_chars(max_width_chars)
+
+        label = self.gui.get_object("lvm_label")
+        txt = _("This will setup LVM and allow you to easily manage "
+                "partitions and create snapshots.")
+        # txt = description_style.format(txt)
+        label.set_text(txt)
+        label.set_name("lvm_label")
+        label.set_hexpand(False)
+        label.set_line_wrap(True)
+        label.set_max_width_chars(max_width_chars)
+
+        button = self.gui.get_object("zfs_checkbutton")
+        txt = _("Use ZFS with this installation.")
+        button.set_label(txt)
+        button.set_name("zfs_btn")
+        button.set_hexpand(False)
+        # button.set_line_wrap(True)
+        # button.set_max_width_chars(max_width_chars)
+
+        label = self.gui.get_object("zfs_label")
+        txt = _("This will setup ZFS on your drive(s).")
+        # txt = description_style.format(txt)
+        label.set_text(txt)
+        label.set_name("zfs_label")
+        label.set_hexpand(False)
+        label.set_line_wrap(True)
+        label.set_max_width_chars(max_width_chars)
+
+        button = self.gui.get_object("home_checkbutton")
+        txt = _("Set your Home in a different partition/volume")
+        button.set_label(txt)
+        button.set_name("home_btn")
+        button.set_hexpand(False)
+        # button.set_line_wrap(True)
+        # button.set_max_width_chars(max_width_chars)
+
+        label = self.gui.get_object("home_label")
+        txt = _("This will setup your /home directory in a different "
+                "partition or volume.")
+        # txt = description_style.format(txt)
+        label.set_text(txt)
+        label.set_name("home_label")
+        label.set_hexpand(False)
+        label.set_line_wrap(True)
+        label.set_max_width_chars(max_width_chars)
+
+        # Alongside Install (For now, only works with Windows)
+        # if len(oses_str) > 0:
+        #     txt = _("Install Antergos alongside {0}").format(oses_str)
+        #     radio = self.gui.get_object("alongside_radiobutton")
+        #     radio.set_label(txt)
+        #
+        #     label = self.gui.get_object("alongside_description")
+        #     txt = _("Installs Antergos without removing {0}").format(oses_str)
+        #     txt = description_style.format(txt)
+        #     label.set_markup(txt)
+        #     label.set_line_wrap(True)
+        #
+        #     intro_txt = _("This computer has {0} installed.").format(oses_str)
+        #     intro_txt = intro_txt + "\n" + _("What do you want to do?")
+        # else:
+        intro_txt = _("How would you like to proceed?")
+
+        intro_label = self.gui.get_object("introduction")
+        # intro_txt = bold_style.format(intro_txt)
+        intro_label.set_text(intro_txt)
+        intro_label.set_name("intro_label")
+        intro_label.set_hexpand(False)
+        intro_label.set_line_wrap(True)
+        intro_label.set_max_width_chars(max_width_chars)
+
+        # Advanced Install
+        radio = self.gui.get_object("advanced_radiobutton")
+        radio.set_label(
+            _("Choose exactly where RebornOS should be installed."))
+        radio.set_name("advanced_radio_btn")
+
+        label = self.gui.get_object("advanced_description")
+        txt = _("Edit partition table and choose mount points.")
+        # txt = description_style.format(txt)
+        label.set_text(txt)
+        label.set_name("adv_desc_label")
+        label.set_hexpand(False)
+        label.set_line_wrap(True)
+        label.set_max_width_chars(max_width_chars)
+
+    def store_values(self):
+        """ Store selected values """
+        check = self.gui.get_object("encrypt_checkbutton")
+        use_luks = check.get_active()
+
+        check = self.gui.get_object("lvm_checkbutton")
+        use_lvm = check.get_active()
+
+        check = self.gui.get_object("zfs_checkbutton")
+        use_zfs = check.get_active()
+
+        check = self.gui.get_object("home_checkbutton")
+        use_home = check.get_active()
+
+        self.settings.set('use_lvm', use_lvm)
+        self.settings.set('use_luks', use_luks)
+        self.settings.set('use_luks_in_root', True)
+        self.settings.set('luks_root_volume', 'cryptAntergos')
+        self.settings.set('use_zfs', use_zfs)
+        self.settings.set('use_home', use_home)
+
+        if not self.settings.get('use_zfs'):
+            if self.settings.get('use_luks'):
+                logging.info(
+                    "RebornOS installation will be encrypted using LUKS")
+            if self.settings.get('use_lvm'):
+                logging.info("RebornOS will be installed using LVM volumes")
+                if self.settings.get('use_home'):
+                    logging.info(
+                        "RebornOS will be installed using a separate /home volume.")
+            elif self.settings.get('use_home'):
+                logging.info(
+                    "RebornOS will be installed using a separate /home partition.")
+        else:
+            logging.info("RebornOS will be installed using ZFS")
+            if self.settings.get('use_luks'):
+                logging.info("RebornOS ZFS installation will be encrypted")
+            if self.settings.get('use_home'):
+                logging.info(
+                    "RebornOS will be installed using a separate /home volume.")
+
+        if self.next_page == "installation_alongside":
+            self.settings.set('partition_mode', 'alongside')
+        elif self.next_page == "installation_advanced":
+            self.settings.set('partition_mode', 'advanced')
+        elif self.next_page == "installation_automatic":
+            self.settings.set('partition_mode', 'automatic')
+        elif self.next_page == "installation_zfs":
+            self.settings.set('partition_mode', 'zfs')
+
+        # Get sure other modules will know if zfs is activated or not
+        self.settings.set("zfs", use_zfs)
+
+        return True
+
+    def get_next_page(self):
+        """ Returns which page should be the next one """
+        return self.next_page
+
+    def automatic_radiobutton_toggled(self, widget):
+        """ Automatic selected, enable all options """
+        if widget.get_active():
+            check = self.gui.get_object("zfs_checkbutton")
+            if check.get_active():
+                self.next_page = "installation_zfs"
+            else:
+                self.next_page = "installation_automatic"
+            # Enable all options
+            self.enable_automatic_options(True)
+
+    def automatic_lvm_toggled(self, widget):
+        """ Enables / disables LVM installation """
+        if widget.get_active():
+            self.next_page = "installation_automatic"
+            # Disable ZFS if using LVM
+            check = self.gui.get_object("zfs_checkbutton")
+            if check.get_active():
+                check.set_active(False)
+
+    def automatic_zfs_toggled(self, widget):
+        """ Enables / disables ZFS installation """
+        if widget.get_active():
+            self.next_page = "installation_zfs"
+            # Disable LVM if using ZFS
+            check = self.gui.get_object("lvm_checkbutton")
+            if check.get_active():
+                check.set_active(False)
+        else:
+            self.next_page = "installation_automatic"
+
+    def alongside_radiobutton_toggled(self, widget):
+        """ Alongside selected, disable all automatic options """
+        if widget.get_active():
+            self.next_page = "installation_alongside"
+            self.enable_automatic_options(False)
+
+    def advanced_radiobutton_toggled(self, widget):
+        """ Advanced selected, disable all automatic options """
+        if widget.get_active():
+            self.next_page = "installation_advanced"
+            self.enable_automatic_options(False)
+
+
+if __name__ == '__main__':
+    from test_screen import _, run
+
+    run('InstallationAsk')
diff --git a/Cnchi/auto_partition.py b/Cnchi/auto_partition.py
new file mode 100644 (file)
index 0000000..cedc193
--- /dev/null
@@ -0,0 +1,807 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# auto_partition.py
+#
+# Copyright © 2013-2018 Antergos
+#
+# Modify by Rafael from RebornOS in 2020
+#
+# This file is part of Cnchi.
+#
+# Cnchi 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.
+#
+# Cnchi 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.
+#
+# The following additional terms are in effect as per Section 7 of the license:
+#
+# The preservation of all legal notices and author attributions in
+# the material or in the Appropriate Legal Notices displayed
+# by works containing it is required.
+#
+# You should have received a copy of the GNU General Public License
+# along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+""" AutoPartition module, used by automatic installation """
+
+import os
+import logging
+import math
+
+from misc.extra import InstallError
+from misc.run_cmd import call
+import misc.events as events
+import parted3.fs_module as fs
+
+from installation import luks
+from installation import mount
+from installation import wrapper
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+# NOTE: Exceptions in this file
+# On a warning situation, Cnchi should try to continue, so we need to catch
+# the exception here. If we don't catch the exception here, it will be caught
+# in process.py and managed as a fatal error. On the other hand, if we want
+# to clarify the exception message we can catch it here and then raise an
+# InstallError exception.
+
+# Partition sizes are in MiB
+MAX_ROOT_SIZE = 30000
+
+# KDE (with all features) needs 8 GB for its files (including pacman cache xz files).
+# Vbox, by default, creates disks of 8GB. We should limit to this so vbox installations do not fail
+# (if installing kde and not enough free space is available is their fault, not ours)
+MIN_ROOT_SIZE = 8000
+
+
+class AutoPartition():
+    """ Class used by the automatic installation method """
+
+    LUKS_KEY_FILES = [".keyfile-root", ".keyfile-home"]
+
+    def __init__(self, dest_dir, auto_device, settings, callback_queue):
+        """ Class initialization """
+
+        self.dest_dir = dest_dir
+        self.auto_device = auto_device
+        self.temp = settings.get('temp')
+
+        # Use LUKS encryption
+        self.luks = settings.get("use_luks")
+        self.luks_password = settings.get("luks_root_password")
+
+        # Use LVM
+        self.lvm = settings.get("use_lvm")
+
+        # Make home a different partition or if using LVM, a different volume
+        self.home = settings.get("use_home")
+
+        self.bootloader = settings.get("bootloader").lower()
+
+        # Will use these queue to show progress info to the user
+        self.events = events.Events(callback_queue)
+
+        if os.path.exists("/sys/firmware/efi"):
+            # If UEFI use GPT
+            self.uefi = True
+            self.gpt = True
+        else:
+            # If BIOS, use MBR
+            self.uefi = False
+            self.gpt = False
+
+    def mkfs(self, device, fs_type, mount_point, label_name, fs_options="", btrfs_devices=""):
+        """ We have two main cases: "swap" and everything else. """
+        logging.debug("Will format device %s as %s", device, fs_type)
+        if fs_type == "swap":
+            err_msg = "Can't activate swap in {0}".format(device)
+            swap_devices = call(["swapon", "-s"], msg=err_msg)
+            if device in swap_devices:
+                call(["swapoff", device], msg=err_msg)
+            cmd = ["mkswap", "-L", label_name, device]
+            call(cmd, msg=err_msg)
+            cmd = ["swapon", device]
+            call(cmd, msg=err_msg)
+        else:
+            mkfs = {
+                "xfs": "mkfs.xfs {0} -L {1} -f {2}".format(
+                    fs_options, label_name, device),
+                "jfs": "yes | mkfs.jfs {0} -L {1} {2}".format(
+                    fs_options, label_name, device),
+                "reiserfs": "yes | mkreiserfs {0} -l {1} {2}".format(
+                    fs_options, label_name, device),
+                "ext2": "mkfs.ext2 -q {0} -F -L {1} {2}".format(
+                    fs_options, label_name, device),
+                "ext3": "mkfs.ext3 -q {0} -F -L {1} {2}".format(
+                    fs_options, label_name, device),
+                "ext4": "mkfs.ext4 -q {0} -F -L {1} {2}".format(
+                    fs_options, label_name, device),
+                "btrfs": "mkfs.btrfs {0} -L {1} {2}".format(
+                    fs_options, label_name, btrfs_devices),
+                "nilfs2": "mkfs.nilfs2 {0} -L {1} {2}".format(
+                    fs_options, label_name, device),
+                "ntfs-3g": "mkfs.ntfs {0} -f -L {1} {2}".format(
+                    fs_options, label_name, device),
+                "vfat": "mkfs.vfat {0} -F 32 -n {1} {2}".format(
+                    fs_options, label_name, device),
+                "fat32": "mkfs.vfat {0} -F 32 -n {1} {2}".format(
+                    fs_options, label_name, device),
+                "f2fs": "mkfs.f2fs {0} -l {1} {2}".format(
+                    fs_options, label_name, device)}
+
+            # Make sure the fs type is one we can handle
+            if fs_type not in mkfs.keys():
+                txt = _("Unknown filesystem type {0}").format(fs_type)
+                raise InstallError(txt)
+
+            command = mkfs[fs_type]
+
+            err_msg = "Can't create filesystem {0}".format(fs_type)
+            call(command.split(), msg=err_msg, fatal=True)
+
+            # Flush filesystem buffers
+            call(["sync"])
+
+            # Create our mount directory
+            path = self.dest_dir + mount_point
+            os.makedirs(path, mode=0o755, exist_ok=True)
+
+            # Mount our new filesystem
+
+            mopts = "rw,relatime"
+            if fs_type == "ext4":
+                mopts = "rw,relatime,data=ordered"
+            elif fs_type == "btrfs":
+                mopts = 'rw,relatime,space_cache,autodefrag,inode_cache'
+
+            err_msg = "Error trying to mount {0} in {1}".format(device, path)
+            cmd = ["mount", "-t", fs_type, "-o", mopts, device, path]
+            call(cmd, msg=err_msg, fatal=True)
+
+            # Change permission of base directories to avoid btrfs issues
+            if mount_point == "/tmp":
+                mode = 0o1777
+            elif mount_point == "/root":
+                mode = 0o750
+            else:
+                mode = 0o755
+            os.chmod(path, mode)
+
+        fs_uuid = fs.get_uuid(device)
+        fs_label = fs.get_label(device)
+        msg = "Device details: %s UUID=%s LABEL=%s"
+        logging.debug(msg, device, fs_uuid, fs_label)
+
+    @staticmethod
+    def get_partition_path(device, part_num):
+        """ This is awful and prone to fail. We should do some
+            type of test here """
+
+        # Remove /dev/
+        path = device.replace('/dev/', '')
+        partials = [
+            'rd/', 'ida/', 'cciss/', 'sx8/', 'mapper/', 'mmcblk', 'md', 'nvme']
+        found = [p for p in partials if path.startswith(p)]
+        if found:
+            return "{0}p{1}".format(device, part_num)
+        return "{0}{1}".format(device, part_num)
+
+    def get_devices(self):
+        """ Set (and return) all partitions on the device """
+        devices = {}
+        device = self.auto_device
+        logging.debug(device)
+
+        # device is of type /dev/sdX or /dev/hdX or /dev/mmcblkX
+
+        if self.gpt:
+            if not self.uefi:
+                # Skip BIOS Boot Partition
+                # We'll never get here as we use UEFI+GPT or BIOS+MBR
+                part_num = 2
+            else:
+                part_num = 1
+
+            if self.bootloader == "grub2":
+                devices['efi'] = self.get_partition_path(device, part_num)
+                part_num += 1
+
+            devices['boot'] = self.get_partition_path(device, part_num)
+            part_num += 1
+            devices['root'] = self.get_partition_path(device, part_num)
+            part_num += 1
+            if self.home:
+                devices['home'] = self.get_partition_path(device, part_num)
+                part_num += 1
+            devices['swap'] = self.get_partition_path(device, part_num)
+        else:
+            devices['boot'] = self.get_partition_path(device, 1)
+            devices['root'] = self.get_partition_path(device, 2)
+            if self.home:
+                devices['home'] = self.get_partition_path(device, 3)
+            devices['swap'] = self.get_partition_path(device, 5)
+
+        if self.luks:
+            if self.lvm:
+                # LUKS and LVM
+                devices['luks_root'] = devices['root']
+                devices['lvm'] = "/dev/mapper/cryptRebornOS"
+            else:
+                # LUKS and no LVM
+                devices['luks_root'] = devices['root']
+                devices['root'] = "/dev/mapper/cryptRebornOS"
+                if self.home:
+                    # In this case we'll have two LUKS devices, one for root
+                    # and the other one for /home
+                    devices['luks_home'] = devices['home']
+                    devices['home'] = "/dev/mapper/cryptRebornOSHome"
+        elif self.lvm:
+            # No LUKS but using LVM
+            devices['lvm'] = devices['root']
+
+        if self.lvm:
+            devices['root'] = "/dev/RebornOSVG/RebornOSRoot"
+            devices['swap'] = "/dev/RebornOSVG/RebornOSSwap"
+            if self.home:
+                devices['home'] = "/dev/RebornOSVG/RebornOSHome"
+
+        return devices
+
+    def get_mount_devices(self):
+        """ Specify for each mount point which device we must mount there """
+
+        devices = self.get_devices()
+        mount_devices = {}
+
+        if self.gpt and self.bootloader == "grub2":
+            mount_devices['/boot/efi'] = devices['efi']
+
+        mount_devices['/boot'] = devices['boot']
+        mount_devices['/'] = devices['root']
+
+        if self.home:
+            mount_devices['/home'] = devices['home']
+
+        if self.luks:
+            mount_devices['/'] = devices['luks_root']
+            if self.home and not self.lvm:
+                mount_devices['/home'] = devices['luks_home']
+
+        mount_devices['swap'] = devices['swap']
+
+        for mount_device in mount_devices:
+            logging.debug(
+                "%s assigned to be mounted in %s",
+                mount_devices[mount_device],
+                mount_device)
+
+        return mount_devices
+
+    def get_fs_devices(self):
+        """ Return which filesystem is in a selected device """
+
+        devices = self.get_devices()
+
+        fs_devices = {}
+
+        if self.gpt:
+            if self.bootloader == "grub2":
+                fs_devices[devices['efi']] = "vfat"
+                fs_devices[devices['boot']] = "ext4"
+            elif self.bootloader in ["systemd-boot", "refind"]:
+                fs_devices[devices['boot']] = "vfat"
+        else:
+            if self.uefi:
+                fs_devices[devices['boot']] = "vfat"
+            else:
+                fs_devices[devices['boot']] = "ext4"
+
+        fs_devices[devices['swap']] = "swap"
+        fs_devices[devices['root']] = "ext4"
+
+        if self.home:
+            fs_devices[devices['home']] = "ext4"
+
+        if self.luks:
+            fs_devices[devices['luks_root']] = "ext4"
+            if self.home:
+                if self.lvm:
+                    # luks, lvm, home
+                    fs_devices[devices['home']] = "ext4"
+                else:
+                    # luks, home
+                    fs_devices[devices['luks_home']] = "ext4"
+
+        for device in fs_devices:
+            logging.debug("Device %s will have a %s filesystem",
+                          device, fs_devices[device])
+
+        return fs_devices
+
+    def get_part_sizes(self, disk_size, start_part_sizes=1):
+        """ Returns a dict with all partition sizes """
+        part_sizes = {'disk': disk_size, 'boot': 512, 'efi': 0}
+
+        if self.gpt and self.bootloader == "grub2":
+            part_sizes['efi'] = 512
+
+        cmd = ["grep", "MemTotal", "/proc/meminfo"]
+        mem_total = call(cmd)
+        mem_total = int(mem_total.split()[1])
+        mem = mem_total / 1024
+
+        # Suggested swap sizes from Anaconda installer
+        if mem < 2048:
+            part_sizes['swap'] = 2 * mem
+        elif 2048 <= mem < 8192:
+            part_sizes['swap'] = mem
+        elif 8192 <= mem < 65536:
+            part_sizes['swap'] = mem // 2
+        else:
+            part_sizes['swap'] = 4096
+
+        # Max swap size is 10% of all available disk size
+        max_swap = disk_size * 0.1
+        if part_sizes['swap'] > max_swap:
+            part_sizes['swap'] = max_swap
+
+        part_sizes['swap'] = math.ceil(part_sizes['swap'])
+
+        other_than_root_size = start_part_sizes + \
+            part_sizes['efi'] + part_sizes['boot'] + part_sizes['swap']
+        part_sizes['root'] = disk_size - other_than_root_size
+
+        if self.home:
+            # Decide how much we leave to root and how much we leave to /home
+            # Question: why 5?
+            new_root_part_size = part_sizes['root'] // 5
+            if new_root_part_size > MAX_ROOT_SIZE:
+                new_root_part_size = MAX_ROOT_SIZE
+            elif new_root_part_size < MIN_ROOT_SIZE:
+                new_root_part_size = MIN_ROOT_SIZE
+
+            if new_root_part_size >= part_sizes['root']:
+                # new_root_part_size can't be bigger than part_sizes['root'] !
+                # this could happen if new_root_part_size == MIN_ROOT_SIZE but
+                # our harddisk is smaller (detected using vbox)
+                # Should we fail here or install without a separated /home partition?
+                logging.warning(
+                    "There's not enough free space to have a separate /home partition")
+                self.home = False
+                part_sizes['home'] = 0
+            else:
+                part_sizes['home'] = part_sizes['root'] - new_root_part_size
+                part_sizes['root'] = new_root_part_size
+        else:
+            part_sizes['home'] = 0
+
+        part_sizes['lvm_pv'] = part_sizes['swap'] + \
+            part_sizes['root'] + part_sizes['home']
+
+        for part in part_sizes:
+            part_sizes[part] = int(part_sizes[part])
+
+        return part_sizes
+
+    def log_part_sizes(self, part_sizes):
+        """ Log partition sizes for debugging purposes """
+        logging.debug("Total disk size: %dMiB", part_sizes['disk'])
+        if self.gpt and self.bootloader == "grub2":
+            logging.debug(
+                "EFI System Partition (ESP) size: %dMiB", part_sizes['efi'])
+        logging.debug("Boot partition size: %dMiB", part_sizes['boot'])
+
+        if self.lvm:
+            logging.debug("LVM physical volume size: %dMiB",
+                          part_sizes['lvm_pv'])
+
+        logging.debug("Swap partition size: %dMiB", part_sizes['swap'])
+        logging.debug("Root partition size: %dMiB", part_sizes['root'])
+
+        if self.home:
+            logging.debug("Home partition size: %dMiB", part_sizes['home'])
+
+    def get_disk_size(self):
+        """ Gets disk size in MiB """
+        # Partition sizes are expressed in MiB
+        # Get just the disk size in MiB
+        device = self.auto_device
+        device_name = os.path.split(device)[1]
+        size_path = os.path.join("/sys/block", device_name, 'size')
+        base_path = os.path.split(size_path)[0]
+        disk_size = 0
+        if os.path.exists(size_path):
+            logical_path = os.path.join(base_path, "queue/logical_block_size")
+            with open(logical_path, 'r') as logical_file:
+                logical_block_size = int(logical_file.read())
+            with open(size_path, 'r') as size_file:
+                size = int(size_file.read())
+            disk_size = ((logical_block_size * (size - 68)) / 1024) / 1024
+        else:
+            logging.error("Cannot detect %s device size", device)
+            txt = _("Setup cannot detect size of your device, please use advanced "
+                    "installation routine for partitioning and mounting devices.")
+            raise InstallError(txt)
+        return disk_size
+
+    def run_gpt(self, part_sizes):
+        """ Auto partition using a GPT table """
+        # Our computed sizes are all in mebibytes (MiB) i.e. powers of 1024, not metric megabytes.
+        # These are 'M' in sgdisk and 'MiB' in parted.
+        # If you use 'M' in parted you'll get MB instead of MiB, and you're gonna have a bad time.
+        device = self.auto_device
+
+        # Clean partition table to avoid issues!
+        wrapper.sgdisk("zap-all", device)
+
+        # Clear all magic strings/signatures - mdadm, lvm, partition tables etc.
+        wrapper.run_dd("/dev/zero", device)
+        wrapper.wipefs(device)
+
+        # Create fresh GPT
+        wrapper.sgdisk("clear", device)
+        wrapper.parted_mklabel(device, "gpt")
+
+        # Inform the kernel of the partition change.
+        # Needed if the hard disk had a MBR partition table.
+        err_msg = "Error informing the kernel of the partition change."
+        call(["partprobe", device], msg=err_msg, fatal=True)
+
+        part_num = 1
+
+        if not self.uefi:
+            # We don't allow BIOS+GPT right now, so this code will be never executed
+            # We leave here just for future reference
+            # Create BIOS Boot Partition
+            # GPT GUID: 21686148-6449-6E6F-744E-656564454649
+            # This partition is not required if the system is UEFI based,
+            # as there is no such embedding of the second-stage code in that case
+            wrapper.sgdisk_new(device, part_num, "BIOS_BOOT", 2, "EF02")
+            part_num += 1
+
+        if self.bootloader == "grub2":
+            # Create EFI System Partition (ESP)
+            # GPT GUID: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
+            wrapper.sgdisk_new(
+                device, part_num, "UEFI_SYSTEM", part_sizes['efi'], "EF00")
+            part_num += 1
+
+        # Create Boot partition
+        if self.bootloader in ["systemd-boot", "refind"]:
+            wrapper.sgdisk_new(
+                device, part_num, "REBORNOS_BOOT", part_sizes['boot'], "EF00")
+        else:
+            wrapper.sgdisk_new(
+                device, part_num, "REBORNOS_BOOT", part_sizes['boot'], "8300")
+        part_num += 1
+
+        if self.lvm:
+            # Create partition for lvm
+            # (will store root, swap and home (if desired) logical volumes)
+            wrapper.sgdisk_new(
+                device, part_num, "REBORNOS_LVM", part_sizes['lvm_pv'], "8E00")
+            part_num += 1
+        else:
+            wrapper.sgdisk_new(
+                device, part_num, "REBORNOS_ROOT", part_sizes['root'], "8300")
+            part_num += 1
+            if self.home:
+                wrapper.sgdisk_new(
+                    device, part_num, "REBORNOS_HOME", part_sizes['home'], "8302")
+                part_num += 1
+            wrapper.sgdisk_new(
+                device, part_num, "REBORNOS_SWAP", 0, "8200")
+
+        output = call(["sgdisk", "--print", device])
+        logging.debug(output)
+
+    def run_mbr(self, part_sizes):
+        """ DOS MBR partition table """
+        # Our computed sizes are all in mebibytes (MiB) i.e. powers of 1024, not metric megabytes.
+        # These are 'M' in sgdisk and 'MiB' in parted.
+        # If you use 'M' in parted you'll get MB instead of MiB, and you're gonna have a bad time.
+        device = self.auto_device
+
+        # Start at sector 1 for 4k drive compatibility and correct alignment
+        # Clean partitiontable to avoid issues!
+        wrapper.run_dd("/dev/zero", device)
+        wrapper.wipefs(device)
+
+        # Create DOS MBR
+        wrapper.parted_mklabel(device, "msdos")
+
+        # Create boot partition (all sizes are in MiB)
+        # if start is -1 wrapper.parted_mkpart assumes that our partition
+        # starts at 1 (first partition in disk)
+        start = -1
+        end = part_sizes['boot']
+        wrapper.parted_mkpart(device, "primary", start, end)
+
+        # Set boot partition as bootable
+        wrapper.parted_set(device, "1", "boot", "on")
+
+        if self.lvm:
+            # Create partition for lvm (will store root, home (if desired),
+            # and swap logical volumes)
+            start = end
+            # end = start + part_sizes['lvm_pv']
+            end = "-1s"
+            wrapper.parted_mkpart(device, "primary", start, end)
+
+            # Set lvm flag
+            wrapper.parted_set(device, "2", "lvm", "on")
+        else:
+            # Create root partition
+            start = end
+            end = start + part_sizes['root']
+            wrapper.parted_mkpart(device, "primary", start, end)
+
+            if self.home:
+                # Create home partition
+                start = end
+                end = start + part_sizes['home']
+                wrapper.parted_mkpart(device, "primary", start, end)
+
+            # Create an extended partition where we will put our swap partition
+            start = end
+            # end = start + part_sizes['swap']
+            end = "-1s"
+            wrapper.parted_mkpart(device, "extended", start, end)
+
+            # Now create a logical swap partition
+            start += 1
+            end = "-1s"
+            wrapper.parted_mkpart(
+                device, "logical", start, end, "linux-swap")
+
+    def run_lvm(self, devices, part_sizes, start_part_sizes, disk_size):
+        """ Create lvm volumes """
+        logging.debug("Cnchi will setup LVM on device %s", devices['lvm'])
+
+        err_msg = "Error creating LVM physical volume in device {0}"
+        err_msg = err_msg.format(devices['lvm'])
+        cmd = ["pvcreate", "-f", "-y", devices['lvm']]
+        call(cmd, msg=err_msg, fatal=True)
+
+        err_msg = "Error creating LVM volume group in device {0}"
+        err_msg = err_msg.format(devices['lvm'])
+        cmd = ["vgcreate", "-f", "-y", "RebornOSVG", devices['lvm']]
+        call(cmd, msg=err_msg, fatal=True)
+
+        # Fix issue 180
+        # Check space we have now for creating logical volumes
+        cmd = ["vgdisplay", "-c", "RebornOSVG"]
+        vg_info = call(cmd, fatal=True)
+        # Get column number 12: Size of volume group in kilobytes
+        vg_size = int(vg_info.split(":")[11]) / 1024
+        if part_sizes['lvm_pv'] > vg_size:
+            logging.debug(
+                "Real RebornOSVG volume group size: %d MiB", vg_size)
+            logging.debug("Reajusting logical volume sizes")
+            diff_size = part_sizes['lvm_pv'] - vg_size
+            part_sizes = self.get_part_sizes(
+                disk_size - diff_size, start_part_sizes)
+            self.log_part_sizes(part_sizes)
+
+        # Create LVM volumes
+        err_msg = "Error creating LVM logical volume"
+
+        size = str(int(part_sizes['root']))
+        cmd = ["lvcreate", "--name", "RebornOSRoot", "--size", size, "RebornOSVG"]
+        call(cmd, msg=err_msg, fatal=True)
+
+        if not self.home:
+            # Use the remainig space for our swap volume
+            cmd = ["lvcreate", "--name", "RebornOSSwap", "--extents", "100%FREE", "RebornOSVG"]
+            call(cmd, msg=err_msg, fatal=True)
+        else:
+            size = str(int(part_sizes['swap']))
+            cmd = ["lvcreate", "--name", "RebornOSSwap", "--size", size, "RebornOSVG"]
+            call(cmd, msg=err_msg, fatal=True)
+            # Use the remaining space for our home volume
+            cmd = ["lvcreate", "--name", "RebornOSHome", "--extents", "100%FREE", "RebornOSVG"]
+            call(cmd, msg=err_msg, fatal=True)
+
+    def create_filesystems(self, devices):
+        """ Create filesystems in newly created partitions """
+        mount_points = {
+            'efi': '/boot/efi', 'boot': '/boot', 'root': '/', 'home': '/home', 'swap': ''}
+
+        labels = {
+            'efi': 'UEFI_SYSTEM', 'boot': 'RebornOSBoot', 'root': 'RebornOSRoot',
+            'home': 'RebornOSHome', 'swap': 'RebornOSSwap'}
+
+        fs_devices = self.get_fs_devices()
+
+        # Note: Make sure the "root" partition is defined first!
+        self.mkfs(devices['root'], fs_devices[devices['root']],
+                  mount_points['root'], labels['root'])
+        self.mkfs(devices['swap'], fs_devices[devices['swap']],
+                  mount_points['swap'], labels['swap'])
+
+        # NOTE: This will be formated in ext4 (bios or gpt+grub2) or
+        # fat32 (gpt+systemd-boot or refind bootloaders)
+        self.mkfs(devices['boot'], fs_devices[devices['boot']],
+                  mount_points['boot'], labels['boot'])
+
+        # NOTE: Make sure the "boot" partition is defined before the "efi" one!
+        if self.gpt and self.bootloader == "grub2":
+            # Format EFI System Partition (ESP) with vfat (fat32)
+            self.mkfs(devices['efi'], fs_devices[devices['efi']],
+                      mount_points['efi'], labels['efi'])
+
+        if self.home:
+            self.mkfs(devices['home'], fs_devices[devices['home']],
+                      mount_points['home'], labels['home'])
+
+    def copy_luks_keyfiles(self):
+        """ Copy root keyfile to boot partition and home keyfile to root partition
+            user will choose what to do with it
+            THIS IS NONSENSE (BIG SECURITY HOLE), BUT WE TRUST THE USER TO FIX THIS
+            User shouldn't store the keyfiles unencrypted unless the medium itself
+            is reasonably safe (boot partition is not) """
+
+        key_files = AutoPartition.LUKS_KEY_FILES
+        key_file = os.path.join(self.temp, key_files[0])
+        os.chmod(key_file, 0o400)
+        boot_path = os.path.join(self.dest_dir, "boot")
+        cmd = ['mv', key_file, boot_path]
+        call(cmd, msg="Can't copy root LUKS keyfile to the installation device.")
+        if self.home and not self.lvm:
+            key_file = os.path.join(self.temp, key_files[1])
+            os.chmod(key_files, 0o400)
+            luks_dir = os.path.join(self.dest_dir, 'etc/luks-keys')
+            os.makedirs(luks_dir, mode=0o755, exist_ok=True)
+            cmd = ['mv', key_file, luks_dir]
+            call(cmd, msg="Can't copy home LUKS keyfile to the installation device.")
+
+    @staticmethod
+    def remove_lvm(device):
+        """ Remove all previous LVM volumes
+        (it may have been left created due to a previous failed installation) """
+
+        err_msg = "Can't delete existent LVM volumes in device {0}".format(device)
+
+        cmd = ["/usr/bin/lvs", "-o", "lv_name,vg_name,devices", "--noheadings"]
+        lvolumes = call(cmd, msg=err_msg)
+        if lvolumes:
+            lvolumes = lvolumes.split("\n")
+            for lvolume in lvolumes:
+                if lvolume:
+                    (lvolume, vgroup, ldevice) = lvolume.split()
+                    if device in ldevice:
+                        lvdev = "/dev/" + vgroup + "/" + lvolume
+                        call(["/usr/bin/wipefs", "-a", lvdev], msg=err_msg)
+                        call(["/usr/bin/lvremove", "-f", lvdev], msg=err_msg)
+
+        cmd = ["/usr/bin/vgs", "-o", "vg_name,devices", "--noheadings"]
+        vgnames = call(cmd, msg=err_msg)
+        if vgnames:
+            vgnames = vgnames.split("\n")
+            for vgname in vgnames:
+                (vgname, vgdevice) = vgname.split()
+                if vgname and device in vgdevice:
+                    call(["/usr/bin/vgremove", "-f", vgname], msg=err_msg)
+
+        cmd = ["/usr/bin/pvs", "-o", "pv_name", "--noheadings"]
+        pvolumes = call(cmd, msg=err_msg)
+        if pvolumes:
+            pvolumes = pvolumes.split("\n")
+            for pvolume in pvolumes:
+                pvolume = pvolume.strip()
+                if device in pvolume:
+                    cmd = ["/usr/bin/pvremove", "-ff", "-y", pvolume]
+                    call(cmd, msg=err_msg)
+    @staticmethod
+    def printk(enable):
+        """ Enables / disables printing kernel messages to console """
+        with open("/proc/sys/kernel/printk", "w") as fpk:
+            if enable:
+                fpk.write("4")
+            else:
+                fpk.write("0")
+
+    def log_devices(self, devices):
+        """ Log all devices for debugging purposes """
+        if self.gpt and self.bootloader == "grub2":
+            logging.debug("EFI: %s", devices['efi'])
+        logging.debug("Boot: %s", devices['boot'])
+        logging.debug("Root: %s", devices['root'])
+        if self.home:
+            logging.debug("Home: %s", devices['home'])
+        logging.debug("Swap: %s", devices['swap'])
+
+    def run(self):
+        """ Main method. Runs auto partition sequence """
+        disk_size = self.get_disk_size()
+        device = self.auto_device
+        start_part_sizes = 1
+
+        part_sizes = self.get_part_sizes(disk_size, start_part_sizes)
+        self.log_part_sizes(part_sizes)
+
+        # Disable swap and unmount all partitions inside dest_dir
+        mount.unmount_all_in_directory(self.dest_dir)
+        # Disable swap and unmount all partitions of device
+        mount.unmount_all_in_device(device)
+        # Remove lvm in destination device
+        self.remove_lvm(device)
+        # Close luks devices in destination device
+        luks.close_antergos_devices()
+
+        self.printk(False)
+        if self.gpt:
+            self.run_gpt(part_sizes)
+        else:
+            self.run_mbr(part_sizes)
+        self.printk(True)
+
+        # Wait until /dev initialized correct devices
+        call(["udevadm", "settle"])
+
+        devices = self.get_devices()
+
+        self.log_devices(devices)
+
+        if self.luks:
+            luks_options = {'password': self.luks_password,
+                            'key': AutoPartition.LUKS_KEY_FILES[0]}
+            luks.setup(devices['luks_root'], 'cryptRebornOS', luks_options)
+            if self.home and not self.lvm:
+                luks_options = {'password': self.luks_password,
+                                'key': AutoPartition.LUKS_KEY_FILES[1]}
+                luks.setup(devices['luks_home'], 'cryptRebornOSHome', luks_options)
+
+        if self.lvm:
+            self.run_lvm(devices, part_sizes, start_part_sizes, disk_size)
+
+        # We have all partitions and volumes created. Let's create its filesystems with mkfs.
+        self.create_filesystems(devices)
+
+        # NOTE: encrypted and/or lvm2 hooks will be added to mkinitcpio.conf
+        #       in mkinitcpio.py, if necessary.
+        # NOTE: /etc/default/grub, /etc/stab and /etc/crypttab will be modified
+        #       in post_install.py, if necessary.
+
+        if self.luks and self.luks_password == "":
+            self.copy_luks_keyfiles()
+
+
+def test_module():
+    """ Test autopartition module """
+    import gettext
+
+    _ = gettext.gettext
+
+    os.makedirs("/var/log/cnchi")
+    logging.basicConfig(
+        filename="/var/log/cnchi/cnchi-autopartition.log",
+        level=logging.DEBUG)
+
+    settings = {
+        'use_luks': True,
+        'luks_password': "luks",
+        'use_lvm': True,
+        'use_home': True,
+        'bootloader': "grub2"}
+
+    AutoPartition(
+        dest_dir="/install",
+        auto_device="/dev/sdb",
+        settings=settings,
+        callback_queue=None).run()
+
+if __name__ == '__main__':
+    test_module()
diff --git a/Cnchi/bashrc b/Cnchi/bashrc
new file mode 100644 (file)
index 0000000..490e3e3
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# ~/.bashrc
+#
+
+# If not running interactively, don't do anything
+[[ $- != *i* ]] && return
+
+alias ls='ls --color=auto'
+PS1='[\u@\H \W]\$ '
+
+alias pacrepo='sudo reflector -l 20 -f 10 --save /etc/pacman.d/mirrorlist'
+alias pacman='sudo pacman'
+alias journalctl='sudo journalctl'
+alias pacu='sudo pacman -Syu --noconfirm'
+alias auru='yaourt -Syua --noconfirm'
+alias systemctl='sudo systemctl'
+alias se='ls /usr/bin | grep'
+alias update-grub=' grub-mkconfig -o /boot/grub/grub.cfg'
+
+export EDITOR=nano
+export QT_STYLE_OVERRIDE=gtk
+export QT_SELECT=qt5
+
+if [[ $LANG = '' ]]; then
+       export LANG=en_US.UTF-8
+fi
+
+neofetch
+
+path() ( IFS=: ; printf '%s\n' $PATH ; )
diff --git a/Cnchi/check.py b/Cnchi/check.py
new file mode 100644 (file)
index 0000000..4b9f2b0
--- /dev/null
@@ -0,0 +1,306 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  check.py
+#
+#  Copyright © 2013-2019 RebornOS
+#
+#  This file is part of Cnchi.
+#
+#  Cnchi 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.
+#
+#  Cnchi 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.
+#
+#  The following additional terms are in effect as per Section 7 of the license:
+#
+#  The preservation of all legal notices and author attributions in
+#  the material or in the Appropriate Legal Notices displayed
+#  by works containing it is required.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+
+""" Check screen (detects if Reborn prerequisites are meet) """
+
+import dbus
+import logging
+import os
+import subprocess
+
+from gi.repository import GLib
+
+import info
+
+import misc.extra as misc
+from misc.run_cmd import call
+from pages.gtkbasebox import GtkBaseBox
+
+import show_message as show
+
+# Constants
+NM = 'org.freedesktop.NetworkManager'
+NM_STATE_CONNECTED_GLOBAL = 70
+UPOWER = 'org.freedesktop.UPower'
+UPOWER_PATH = '/org/freedesktop/UPower'
+MIN_ROOT_SIZE = 8000000000
+
+class Check(GtkBaseBox):
+    """ Check class """
+
+    def __init__(self, params, prev_page="language", next_page="location"):
+        """ Init class ui """
+        super().__init__(self, params, "check", prev_page, next_page)
+
+        self.remove_timer = False
+
+        self.prepare_power_source = None
+        self.prepare_network_connection = None
+        self.prepare_enough_space = None
+        self.timeout_id = None
+        self.prepare_best_results = None
+        self.updated = None
+        self.packaging_issues = None
+        self.remote_version = None
+
+        self.label_space = self.gui.get_object("label_space")
+
+        if 'checks_are_optional' in params:
+            self.checks_are_optional = params['checks_are_optional']
+        else:
+            self.checks_are_optional = False
+
+    def translate_ui(self):
+        """ Translates all ui elements """
+        txt = _("System Check")
+        self.header.set_subtitle(txt)
+
+        self.updated = self.gui.get_object("updated")
+        txt = _("Cnchi is up to date")
+        self.updated.set_property("label", txt)
+
+        self.prepare_enough_space = self.gui.get_object("prepare_enough_space")
+        txt = _("has at least {0}GB available storage space. (*)")
+        txt = txt.format(MIN_ROOT_SIZE / 1000000000)
+        self.prepare_enough_space.set_property("label", txt)
+
+        txt = _("This highly depends on which desktop environment you choose, "
+                "so you might need more space.")
+        txt = "(*) <i>{0}</i>".format(txt)
+        self.label_space.set_markup(txt)
+        self.label_space.set_hexpand(False)
+        self.label_space.set_line_wrap(True)
+        self.label_space.set_max_width_chars(80)
+
+        self.prepare_power_source = self.gui.get_object("prepare_power_source")
+        txt = _("is plugged in to a power source")
+        self.prepare_power_source.set_property("label", txt)
+
+        self.prepare_network_connection = self.gui.get_object(
+            "prepare_network_connection")
+        txt = _("is connected to the Internet")
+        self.prepare_network_connection.set_property("label", txt)
+
+        self.packaging_issues = self.gui.get_object("packaging_issues")
+        txt = _(
+            "There are no temporary packaging issues that would interfere with installation.")
+        self.packaging_issues.set_property("label", txt)
+
+        self.prepare_best_results = self.gui.get_object("prepare_best_results")
+        txt = _("For best results, please ensure that this computer:")
+        txt = '<span weight="bold" size="large">{0}</span>'.format(txt)
+        self.prepare_best_results.set_markup(txt)
+        self.prepare_best_results.set_hexpand(False)
+        self.prepare_best_results.set_line_wrap(True)
+        self.prepare_best_results.set_max_width_chars(80)
+
+    def check_all(self):
+        """ Check that all requirements are meet """
+        if os.path.exists("/tmp/.cnchi_partitioning_completed"):
+            msg = "You must reboot before retrying again."
+            logging.error(msg)
+            msg = _("You must reboot before retrying again.")
+            show.fatal_error(self.main_window, msg)
+            return False
+
+        has_internet = misc.has_connection()
+        self.prepare_network_connection.set_state(has_internet)
+
+        on_power = not self.on_battery()
+        self.prepare_power_source.set_state(on_power)
+
+        space = self.has_enough_space()
+        self.prepare_enough_space.set_state(space)
+
+        packaging_issues = os.path.exists('/tmp/.packaging_issue')
+        self.packaging_issues.set_state(not packaging_issues)
+
+        if has_internet:
+            updated = self.is_updated()
+        else:
+            updated = False
+
+        self.updated.set_state(updated)
+
+        if self.checks_are_optional:
+            return True
+
+        if has_internet and space and not packaging_issues:
+            return True
+
+        return False
+
+    def on_battery(self):
+        """ Checks if we are on battery power """
+        if self.has_battery():
+            bus = dbus.SystemBus()
+            upower = bus.get_object(UPOWER, UPOWER_PATH)
+            result = misc.get_prop(upower, UPOWER_PATH, 'OnBattery')
+            if result is None:
+                # Cannot read property, something is wrong.
+                logging.warning("Cannot read %s/%s dbus property",
+                                UPOWER_PATH, 'OnBattery')
+                # We will assume we are connected to a power supply
+                result = False
+            return result
+
+        return False
+
+    def has_battery(self):
+        """ Checks if latptop is connected to a power supply """
+        # UPower doesn't seem to have an interface for this.
+        path = '/sys/class/power_supply'
+        if os.path.exists(path):
+            for folder in os.listdir(path):
+                type_path = os.path.join(path, folder, 'type')
+                if os.path.exists(type_path):
+                    with open(type_path) as power_file:
+                        if power_file.read().startswith('Battery'):
+                            self.settings.set('laptop', 'True')
+                            return True
+        return False
+
+    @staticmethod
+    def has_enough_space():
+        """ Check that we have a disk or partition with enough space """
+
+        output = call(cmd=["lsblk", "-lnb"], debug=False).split("\n")
+
+        max_size = 0
+
+        for item in output:
+            col = item.split()
+            if len(col) >= 5:
+                if col[5] == "disk" or col[5] == "part":
+                    size = int(col[3])
+                    if size > max_size:
+                        max_size = size
+
+        return max_size >= MIN_ROOT_SIZE
+
+    def is_updated(self):
+        """ Checks that we're running the latest stable cnchi version """
+        remote_version = info.CNCHI_VERSION
+        local_version = info.CNCHI_VERSION
+
+        if remote_version:
+            return self.compare_versions(remote_version, local_version)
+        else:
+            return False
+
+    def compare_versions(self, remote, local):
+        """ Compares Cnchi versions (local vs remote) and returns true
+            if local is at least as new as remote """
+        
+        remote = remote.split('.')
+        local = local.split('.')
+
+        for i, remote_val in enumerate(remote):
+            remote[i] = info.CNCHI_VERSION
+
+        for i, local_val in enumerate(local):
+            local[i] = info.CNCHI_VERSION
+
+        if remote[0] < local[0]:
+            return True
+        
+        if remote[0] > local[0]:
+            return False
+
+        if remote[1] < local[1]:
+            return True        
+        
+        if remote[1] > local[1]:
+            return False
+        
+        if remote[2] >  local[2]:
+            return False
+        
+        return True
+
+    def get_cnchi_version_in_repo(self):
+        """ Checks cnchi version in the RebornOS repository """
+        if not self.remote_version:
+            try:
+                cmd = ["pacman", "-Ss", "cnchi"]
+                line = subprocess.check_output(cmd).decode().split()
+                version = line[1]
+                if '-' in version:
+                    version = version.split('-')[0]
+                logging.debug(
+                    'Cnchi version in the repository is: CNCHI_VERSION', version)
+            except subprocess.CalledProcessError as err:
+                logging.debug(err)
+                version = None
+            return version
+        else:
+            return self.remote_version
+
+    def on_timer(self):
+        """ If all requirements are meet, enable forward button """
+        if not self.remove_timer:
+            self.forward_button.set_sensitive(self.check_all())
+        return not self.remove_timer
+
+    def store_values(self):
+        """ Continue """
+        # Remove timer
+        self.remove_timer = True
+
+        logging.info("We have Internet connection.")
+        logging.info("We're connected to a power source.")
+        logging.info("We have enough disk space.")
+
+        # Enable forward button
+        self.forward_button.set_sensitive(True)
+        return True
+
+    def prepare(self, direction):
+        """ Load screen """
+        self.translate_ui()
+        self.show_all()
+
+        self.forward_button.set_sensitive(self.check_all())
+
+        # Set timer
+        self.timeout_id = GLib.timeout_add(5000, self.on_timer)
+
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+if __name__ == '__main__':
+    from test_screen import _, run
+
+    run('Check')
+
diff --git a/Cnchi/cinnamon.sh b/Cnchi/cinnamon.sh
new file mode 100644 (file)
index 0000000..4199aa6
--- /dev/null
@@ -0,0 +1,75 @@
+sudo wget https://cinnamon-spices.linuxmint.com/files/applets/slingshot@jfarthing84.zip --directory-prefix=/usr/share/cinnamon/applets/
+sudo unzip -d /usr/share/cinnamon/applets/ /usr/share/cinnamon/applets/slingshot@jfarthing84.zip ##unzip##
+
+sudo wget https://cinnamon-spices.linuxmint.com/files/extensions/transparent-panels@germanfr.zip --directory-prefix=/$HOME/.local/share/cinnamon/extensions/
+sudo unzip -d /$HOME/.local/share/cinnamon/extensions/ /$HOME/.local/share/cinnamon/extensions/transparent-panels@germanfr.zip
+
+git clone https://github.com/fsvh/plank-themes.git
+cd plank-themes
+yes | ./install.sh
+cd ..
+rm -rf plank-themes
+
+git clone https://github.com/RebornOS/elementary-theme.git
+cd elementary-theme
+sudo mv elementary.zip /usr/share/themes/
+sudo unzip -d /usr/share/themes/ /usr/share/themes/elementary.zip
+sudo rm /usr/share/themes/elementary.zip
+sudo rm /$HOME/.local/share/cinnamon/extensions/transparent-panels@germanfr/settings-schema.json
+sudo mv settings-schema.json /$HOME/.local/share/cinnamon/extensions/transparent-panels@germanfr/
+cd ..
+rm -rf elementary-theme
+
+dconf write /org/cinnamon/desktop/wm/preferences/theme "'MacOS-Sierra'"
+dconf write /org/cinnamon/theme/name "'macOS-Sierra'"
+dconf write /org/cinnamon/desktop/interface/gtk-theme "'elementary'"
+dconf write /org/cinnamon/desktop/interface/icon-theme "'elementary'" ##elementary-icon-theme##
+dconf write /org/cinnamon/desktop/interface/gtk-decoration-layout "'close:menu,maximize'"
+
+dconf write /org/cinnamon/alttab-switcher-style "'coverflow'"
+
+donf write /org/cinnamon/hotcorner-layout "['expo:true:50', 'expo:false:50', 'scale:false:0', 'desktop:false:0']"
+
+dconf write /org/cinnamon/enabled-extensions "['transparent-panels@germanfr']"
+
+dconf write /org/cinnamon/enabled-applets "['panel1:right:0:systray@cinnamon.org:0', 'panel1:center:0:calendar@cinnamon.org:12', 'panel1:left:1:slingshot@jfarthing84:14', 'panel1:right:3:user@cinnamon.org:15', 'panel1:right:1:sound@cinnamon.org:21', 'panel1:right:2:power@cinnamon.org:22']"
+
+dconf write /org/cinnamon/panel-launchers "['DEPRECATED']"  ##plank##
+
+dconf write /org/cinnamon/panels-autohide "['1:intel']"
+
+dconf write /org/cinnamon/panels-enabled "['1:0:top']"
+
+dconf write /org/cinnamon/panels-height "['1:25']"
+
+dconf write /org/cinnamon/show-media-keys-osd "'medium'"
+
+dconf write /org/cinnamon/workspace-osd-duration "400"
+
+dconf write /org/cinnamon/workspace-osd-x "50"
+
+dconf write /org/cinnamon/workspace-osd-y "50"
+
+dconf write /org/cinnamon/desktop-effects-maximize-effect "'scale'"
+
+dconf write /org/cinnamon/desktop-effects-maximize-time "200"
+
+dconf write /org/cinnamon/desktop-effects-unmaximize-effect "'scale'"
+
+dconf write /org/cinnamon/desktop-effects-unmaximize-time "200"
+
+dconf write /net/launchpad/plank/enabled-docks "['dock1']"
+
+dconf write /net/launchpad/plank/docks/dock1/dock-items "['gnome-terminal.dockitem', 'nautilus.dockitem', 'pamac-manager.dockitem', 'pragha.dockitem', 'reborn-updates.dockitem']"
+
+dconf write /net/launchpad/plank/docks/dock1/hide-mode "'intelligent'"
+
+dconf write /net/launchpad/plank/docks/dock1/current-workspace-only true
+
+dconf write /net/launchpad/plank/docks/dock1/icon-size "40"
+
+dconf write /net/launchpad/plank/docks/dock1/theme "'Gtk+'"
+
+dconf write /net/launchpad/plank/docks/dock1/zoom-enabled true
+
+dconf write /net/launchpad/plank/docks/dock1/zoom-percent "110"
diff --git a/Cnchi/cnchi-start.sh b/Cnchi/cnchi-start.sh
new file mode 100755 (executable)
index 0000000..3a51aa0
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+sudo chmod 644 /etc/pacman.conf
+sudo -E /usr/bin/python -Wall /usr/share/cnchi/src/cnchi.py -dvz --no-check --packagelist /usr/share/cnchi/data/packages.xml
diff --git a/Cnchi/cnchi.png b/Cnchi/cnchi.png
new file mode 100755 (executable)
index 0000000..181725b
Binary files /dev/null and b/Cnchi/cnchi.png differ
diff --git a/Cnchi/cnchi.py b/Cnchi/cnchi.py
new file mode 100755 (executable)
index 0000000..392245d
--- /dev/null
@@ -0,0 +1,597 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  cnchi.py
+#
+#  Copyright © 2013-2019 RebornOS
+#
+#  This file is part of Cnchi.
+#
+#  Cnchi 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.
+#
+#  Cnchi 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.
+#
+#  The following additional terms are in effect as per Section 7 of the license:
+#
+#  The preservation of all legal notices and author attributions in
+#  the material or in the Appropriate Legal Notices displayed
+#  by works containing it is required.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+""" Main Cnchi (Antergos Installer) module """
+
+import os
+import sys
+import shutil
+import logging
+import logging.handlers
+import gettext
+import locale
+import gi
+import pwd
+
+CNCHI_PATH = "/usr/share/cnchi"
+sys.path.append(CNCHI_PATH)
+sys.path.append(os.path.join(CNCHI_PATH, "src"))
+sys.path.append(os.path.join(CNCHI_PATH, "src/download"))
+sys.path.append(os.path.join(CNCHI_PATH, "src/hardware"))
+sys.path.append(os.path.join(CNCHI_PATH, "src/installation"))
+sys.path.append(os.path.join(CNCHI_PATH, "src/misc"))
+sys.path.append(os.path.join(CNCHI_PATH, "src/pacman"))
+sys.path.append(os.path.join(CNCHI_PATH, "src/pages"))
+sys.path.append(os.path.join(CNCHI_PATH, "src/pages/dialogs"))
+sys.path.append(os.path.join(CNCHI_PATH, "src/parted3"))
+
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gio, Gtk, GObject
+
+import misc.extra as misc
+from misc.run_cmd import call
+import show_message as show
+import info
+
+from logging_utils import ContextFilter
+import logging_color
+
+#try:
+#    from bugsnag.handlers import BugsnagHandler
+#    import bugsnag
+#    BUGSNAG_ERROR = None
+#except ImportError as err:
+#    BUGSNAG_ERROR = str(err)
+
+BUGSNAG_ERROR = "Bugsnag disabled. Makes requests raise several exceptions. Need to check what is wrong"
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+
+class CnchiApp(Gtk.Application):
+    """ Main Cnchi App class """
+
+    TEMP_FOLDER = '/var/tmp/cnchi'
+
+    def __init__(self, cmd_line):
+        """ Constructor. Call base class """
+        Gtk.Application.__init__(self,
+                                 application_id="com.antergos.cnchi",
+                                 flags=Gio.ApplicationFlags.FLAGS_NONE)
+        self.tmp_running = os.path.join(CnchiApp.TEMP_FOLDER, ".setup-running")
+        self.cmd_line = cmd_line
+
+    def do_activate(self):
+        """ Override the 'activate' signal of GLib.Application.
+            Shows the default first window of the application (like a new document).
+            This corresponds to the application being launched by the desktop environment. """
+        try:
+            import main_window
+        except ImportError as err:
+            msg = "Cannot create Cnchi main window: {0}".format(err)
+            logging.error(msg)
+            sys.exit(1)
+
+        # Check if we have administrative privileges
+        if os.getuid() != 0:
+            msg = _('This installer must be run with administrative privileges, '
+                    'and cannot continue without them.')
+            show.error(None, msg)
+            return
+
+        # Check if we're already running
+        if self.already_running():
+            msg = _("You cannot run two instances of this installer.\n\n"
+                    "If you are sure that the installer is not already running\n"
+                    "you can run this installer using the --force option\n"
+                    "or you can manually delete the offending file.\n\n"
+                    "Offending file: '{0}'").format(self.tmp_running)
+            show.error(None, msg)
+            return
+
+        window = main_window.MainWindow(self, self.cmd_line)
+        self.add_window(window)
+        window.show()
+
+        try:
+            with misc.raised_privileges():
+                with open(self.tmp_running, 'w') as tmp_file:
+                    txt = "Cnchi {0}\n{1}\n".format(info.CNCHI_VERSION, os.getpid())
+                    tmp_file.write(txt)
+        except PermissionError as err:
+            logging.error(err)
+            show.error(None, err)
+
+        # This is unnecessary as show_all is called in MainWindow
+        # window.show_all()
+
+        # def do_startup(self):
+        # """ Override the 'startup' signal of GLib.Application. """
+        # Gtk.Application.do_startup(self)
+
+        # Application main menu (we don't need one atm)
+        # Leaving this here for future reference
+        # menu = Gio.Menu()
+        # menu.append("About", "win.about")
+        # menu.append("Quit", "app.quit")
+        # self.set_app_menu(menu)
+
+    def already_running(self):
+        """ Check if we're already running """
+        if os.path.exists(self.tmp_running):
+            logging.debug("File %s already exists.", self.tmp_running)
+            with open(self.tmp_running) as setup:
+                lines = setup.readlines()
+            if len(lines) >= 2:
+                try:
+                    pid = int(lines[1].strip('\n'))
+                except ValueError as err:
+                    logging.debug(err)
+                    logging.debug("Cannot read PID value.")
+                    return True
+            else:
+                logging.debug("Cannot read PID value.")
+                return True
+
+            if misc.check_pid(pid):
+                logging.info("Cnchi with pid '%d' already running.", pid)
+                return True
+
+            # Cnchi with pid 'pid' is no longer running, we can safely
+            # remove the offending file and continue.
+            os.remove(self.tmp_running)
+        return False
+
+class CnchiInit():
+    """ Initializes Cnchi """
+
+    # Useful vars for gettext (translations)
+    APP_NAME = "cnchi"
+    LOCALE_DIR = "/usr/share/locale"
+
+    # At least this GTK version is needed
+    GTK_VERSION_NEEDED = "3.18.0"
+
+    LOG_FOLDER = '/var/log/cnchi'
+    TEMP_FOLDER = '/var/tmp/cnchi'
+
+    def __init__(self):
+        """ This function initialises Cnchi """
+
+        # Sets SIGTERM handler, so Cnchi can clean up before exiting
+        # signal.signal(signal.SIGTERM, sigterm_handler)
+
+        # Create Cnchi's temporary folder
+        with misc.raised_privileges():
+            os.makedirs(CnchiInit.TEMP_FOLDER, mode=0o755, exist_ok=True)
+
+        # Configures gettext to be able to translate messages, using _()
+        self.setup_gettext()
+
+        # Command line options
+        self.cmd_line = self.parse_options()
+
+        if self.cmd_line.version:
+            print(_("Cnchi (RebornOS Installer) version {0}").format(
+                info.CNCHI_VERSION))
+            sys.exit(0)
+
+        if self.cmd_line.force:
+            misc.remove_temp_files(CnchiInit.TEMP_FOLDER)
+
+        # Drop root privileges
+        misc.drop_privileges()
+
+        # Setup our logging framework
+        self.setup_logging()
+
+        # Enables needed repositories only if it's not enabled
+        self.enable_repositories()
+
+        # Check that all repositories are present in pacman.conf file
+        if not self.check_pacman_conf("/etc/pacman.conf"):
+            sys.exit(1)
+
+        # Check Cnchi is correctly installed
+        if not self.check_for_files():
+            sys.exit(1)
+
+        # Check installed GTK version
+        if not self.check_gtk_version():
+            sys.exit(1)
+
+        # Check installed pyalpm and libalpm versions
+        if not self.check_pyalpm_version():
+            sys.exit(1)
+
+        # Check ISO version where Cnchi is running from
+        if not self.check_iso_version():
+            sys.exit(1)
+
+        # Disable suspend to RAM
+        self.disable_suspend()
+
+        # Init PyObject Threads
+        self.threads_init()
+
+    @staticmethod
+    def check_pacman_conf(path):
+        """ Check that pacman.conf has the correct options """
+        """ Remove antergos repo, and add Reborn-OS repo (Rafael) """
+
+        with open(path, 'rt') as pacman:
+            lines = pacman.readlines()
+
+        repos = [
+            "[Reborn-OS]", "[core]", "[extra]", "[community]", "[multilib]"]
+
+        for line in lines:
+            line = line.strip('\n')
+            if line in repos:
+                repos.remove(line)
+        if repos:
+            for repo in repos:
+                logging.error("Repository %s not in pacman.conf file", repo)
+            return False
+
+        logging.debug("All repositories are present in pacman.conf file")
+        return True
+
+    def setup_logging(self):
+        """ Configure our logger """
+
+        with misc.raised_privileges():
+            os.makedirs(CnchiInit.LOG_FOLDER, mode=0o755, exist_ok=True)
+
+        logger = logging.getLogger()
+
+        logger.handlers = []
+
+        if self.cmd_line.debug:
+            log_level = logging.DEBUG
+        else:
+            log_level = logging.INFO
+
+        logger.setLevel(log_level)
+
+        context_filter = ContextFilter()
+        logger.addFilter(context_filter.filter)
+
+        datefmt = "%Y-%m-%d %H:%M:%S"
+
+        fmt = "%(asctime)s [%(levelname)-7s] %(message)s  (%(filename)s:%(lineno)d)"
+        formatter = logging.Formatter(fmt, datefmt)
+
+        color_fmt = (
+            "%(asctime)s [%(levelname)-18s] %(message)s  "
+            "($BOLD%(filename)s$RESET:%(lineno)d)")
+        color_formatter = logging_color.ColoredFormatter(color_fmt, datefmt)
+
+        # File logger
+        log_path = os.path.join(CnchiInit.LOG_FOLDER, 'cnchi.log')
+        try:
+            with misc.raised_privileges():
+                file_handler = logging.FileHandler(log_path, mode='w')
+            file_handler.setLevel(log_level)
+            file_handler.setFormatter(formatter)
+            logger.addHandler(file_handler)
+        except PermissionError as permission_error:
+            print("Can't open ", log_path, " : ", permission_error)
+
+        # Stdout logger
+        if self.cmd_line.verbose:
+            # Show log messages to stdout in color (color_formatter)
+            stream_handler = logging.StreamHandler()
+            stream_handler.setLevel(log_level)
+            stream_handler.setFormatter(color_formatter)
+            logger.addHandler(stream_handler)
+
+        if not BUGSNAG_ERROR:
+            # Bugsnag logger
+            bugsnag_api = context_filter.api_key
+            if bugsnag_api is not None:
+                bugsnag.configure(
+                    api_key=bugsnag_api,
+                    app_version=info.CNCHI_VERSION,
+                    project_root='/usr/share/cnchi/cnchi',
+                    release_stage=info.CNCHI_RELEASE_STAGE)
+                bugsnag_handler = BugsnagHandler(api_key=bugsnag_api)
+                bugsnag_handler.setLevel(logging.WARNING)
+                bugsnag_handler.setFormatter(formatter)
+                bugsnag_handler.addFilter(context_filter.filter)
+                bugsnag.before_notify(
+                    context_filter.bugsnag_before_notify_callback)
+                logger.addHandler(bugsnag_handler)
+                logging.info(
+                    "Sending Cnchi log messages to bugsnag server (using python-bugsnag).")
+            else:
+                logging.warning(
+                    "Cannot read the bugsnag api key, logging to bugsnag is not possible.")
+        else:
+            logging.warning(BUGSNAG_ERROR)
+
+    @staticmethod
+    def check_gtk_version():
+        """ Check GTK version """
+        # Check desired GTK Version
+        major_needed = int(CnchiInit.GTK_VERSION_NEEDED.split(".")[0])
+        minor_needed = int(CnchiInit.GTK_VERSION_NEEDED.split(".")[1])
+        micro_needed = int(CnchiInit.GTK_VERSION_NEEDED.split(".")[2])
+
+        # Check system GTK Version
+        major = Gtk.get_major_version()
+        minor = Gtk.get_minor_version()
+        micro = Gtk.get_micro_version()
+
+        # Cnchi will be called from our liveCD that already
+        # has the latest GTK version. This is here just to
+        # help testing Cnchi in our environment.
+        wrong_gtk_version = False
+        if major_needed > major:
+            wrong_gtk_version = True
+        if major_needed == major and minor_needed > minor:
+            wrong_gtk_version = True
+        if major_needed == major and minor_needed == minor and micro_needed > micro:
+            wrong_gtk_version = True
+
+        if wrong_gtk_version:
+            text = "Detected GTK version {0}.{1}.{2} but version >= {3} is needed."
+            text = text.format(major, minor, micro,
+                               CnchiInit.GTK_VERSION_NEEDED)
+            try:
+                show.error(None, text)
+            except ImportError as import_error:
+                logging.error(import_error)
+            return False
+        logging.info("Using GTK v%d.%d.%d", major, minor, micro)
+
+        return True
+
+    @staticmethod
+    def check_pyalpm_version():
+        """ Checks python alpm binding and alpm library versions """
+        try:
+            import pyalpm
+
+            txt = "Using pyalpm v{0} as interface to libalpm v{1}"
+            txt = txt.format(pyalpm.version(), pyalpm.alpmversion())
+            logging.info(txt)
+        except (NameError, ImportError) as err:
+            try:
+                show.error(None, err)
+                logging.error(err)
+            except ImportError as import_error:
+                logging.error(import_error)
+            return False
+
+        return True
+
+    def check_iso_version(self):
+        """ Hostname contains the ISO version """
+        from socket import gethostname
+        hostname = gethostname()
+        # antergos-year.month-iso
+        prefix = "ant-"
+        suffix = "-min"
+        if hostname.startswith(prefix) or hostname.endswith(suffix):
+            # We're running form the ISO, register which version.
+            if suffix in hostname:
+                version = hostname[len(prefix):-len(suffix)]
+            else:
+                version = hostname[len(prefix):]
+            logging.debug("Running from ISO version %s", version)
+            # Delete user's chromium cache (just in case)
+            cache_dir = "/home/antergos/.cache/chromium"
+            if os.path.exists(cache_dir):
+                shutil.rmtree(path=cache_dir, ignore_errors=True)
+                logging.debug("User's chromium cache deleted")
+            # If we're running from sonar iso force a11y parameter to true
+            if hostname.endswith("sonar"):
+                self.cmd_line.a11y = True
+        else:
+            logging.warning("Not running from ISO")
+        return True
+
+    @staticmethod
+    def parse_options():
+        """ argparse http://docs.python.org/3/howto/argparse.html """
+
+        import argparse
+
+        desc = _("Cnchi v{0} - RebornOS Installer").format(info.CNCHI_VERSION)
+        parser = argparse.ArgumentParser(description=desc)
+
+        parser.add_argument(
+            "-a", "--a11y", help=_("Set accessibility feature on by default"),
+            action="store_true")
+        parser.add_argument(
+            "-c", "--cache", help=_("Use pre-downloaded xz packages when possible"),
+            nargs='?')
+        parser.add_argument(
+            "-d", "--debug", help=_("Sets Cnchi log level to 'debug'"),
+            action="store_true")
+        parser.add_argument(
+            "-e", "--environment", help=_("Sets the Desktop Environment that will be installed"),
+            nargs='?')
+        parser.add_argument(
+            "-f", "--force", help=_("Runs cnchi even when another instance is running"),
+            action="store_true")
+        parser.add_argument(
+            "-n", "--no-check", help=_("Makes checks optional in check screen"),
+            action="store_true")
+        parser.add_argument(
+            "-p", "--packagelist", help=_("Install packages referenced by a local XML file"),
+            nargs='?')
+        parser.add_argument(
+            "-s", "--logserver", help=_("Log server (deprecated, always uses bugsnag)"),
+            nargs='?')
+        parser.add_argument(
+            "-t", "--no-tryit", help=_("Disables first screen's 'try it' option"),
+            action="store_true")
+        parser.add_argument(
+            "-v", "--verbose", help=_("Show logging messages to stdout"),
+            action="store_true")
+        parser.add_argument(
+            "-V", "--version", help=_("Show Cnchi version and quit"),
+            action="store_true")
+        parser.add_argument(
+            "-z", "--hidden", help=_("Show options in development (use at your own risk!)"),
+            action="store_true")
+
+        return parser.parse_args()
+
+    @staticmethod
+    def threads_init():
+        """
+        For applications that wish to use Python threads to interact with the GNOME platform,
+        GObject.threads_init() must be called prior to running or creating threads and starting
+        main loops (see notes below for PyGObject 3.10 and greater). Generally, this should be done
+        in the first stages of an applications main entry point or right after importing GObject.
+        For multi-threaded GUI applications Gdk.threads_init() must also be called prior to running
+        Gtk.main() or Gio/Gtk.Application.run().
+        """
+        minor = Gtk.get_minor_version()
+        micro = Gtk.get_micro_version()
+
+        if minor == 10 and micro < 2:
+            # Unfortunately these versions of PyGObject suffer a bug
+            # which require a workaround to get threading working properly.
+            # Workaround: Force GIL creation
+            import threading
+            threading.Thread(target=lambda: None).start()
+
+        # Since version 3.10.2, calling threads_init is no longer needed.
+        # See: https://wiki.gnome.org/PyGObject/Threading
+        if minor < 10 or (minor == 10 and micro < 2):
+            GObject.threads_init()
+            # Gdk.threads_init()
+
+    @staticmethod
+    def setup_gettext():
+        """ This allows to translate all py texts (not the glade ones) """
+
+        gettext.textdomain(CnchiInit.APP_NAME)
+        gettext.bindtextdomain(CnchiInit.APP_NAME, CnchiInit.LOCALE_DIR)
+
+        locale_code, _encoding = locale.getdefaultlocale()
+        lang = gettext.translation(
+            CnchiInit.APP_NAME, CnchiInit.LOCALE_DIR, [locale_code], None, True)
+        lang.install()
+
+
+    @staticmethod
+    def check_for_files():
+        """ Check for some necessary files. Cnchi can't run without them """
+        paths = [
+            "/usr/share/cnchi",
+            "/usr/share/cnchi/ui",
+            "/usr/share/cnchi/data",
+            "/usr/share/cnchi/data/locale"]
+
+        for path in paths:
+            if not os.path.exists(path):
+                print(_("Cnchi files not found. Please, install Cnchi using pacman"))
+                return False
+
+        return True
+
+    @staticmethod
+    def enable_repositories():
+        """ Enable needed repositories in /etc/pacman.conf (just in case) """
+        """ Remove antergos repo, and add Reborn-OS repo (Rafael) """
+
+        repositories = ['Reborn-OS', 'core', 'extra', 'community', 'multilib']
+
+        # Read pacman.conf file
+        with open("/etc/pacman.conf", 'rt') as pconf:
+            lines = pconf.readlines()
+
+        # For each repository, check if it is enabled or not
+        enabled = {}
+        for repo in repositories:
+            enabled[repo] = False
+            for line in lines:
+                if line.startswith('[' + repo + ']'):
+                    enabled[repo] = True
+                    break
+
+        # For each repository, add it's definition if it is not enabled
+        # Remove antergos repo definition, and add Reborn-OS repo definition (Rafael)
+        for repo in repositories:
+            if not enabled[repo]:
+                logging.debug("Adding %s repository to /etc/pacman.conf", repo)
+                with misc.raised_privileges():
+                    with open("/etc/pacman.conf", 'at') as pconf:
+                        pconf.write("[{}]\n".format(repo))
+                        if repo == 'Reborn-OS':
+                            pconf.write("SigLevel = Optional TrustAll\n")
+                            pconf.write("Include = /etc/pacman.d/reborn-mirrorlist\n\n")
+                        else:
+                            pconf.write("Include = /etc/pacman.d/mirrorlist\n\n")
+
+    def disable_suspend(self):
+        """ Disable gnome settings suspend to ram """
+        """ Change logging warning. Original: 'User "antergos" does not exist'  """
+        try:
+            pwd.getpwnam('antergos')
+            schema = 'org.gnome.settings-daemon.plugins.power'
+            keys = ['sleep-inactive-ac-type', 'sleep-inactive-battery-type']
+            value = 'nothing'
+            for key in keys:
+                self.gsettings_set('antergos', schema, key, value)
+        except KeyError:
+            logging.warning('User Rebornos exist')
+
+    @staticmethod
+    def gsettings_set(user, schema, key, value):
+        """ Set a gnome setting """
+        cmd = [
+            'runuser',
+            '-l', user,
+            '-c', "dbus-launch gsettings set " + schema + " " + key + " " + value]
+
+        logging.debug("Running set on gsettings: %s", ''.join(str(e) + ' ' for e in cmd))
+        with misc.raised_privileges():
+            return call(cmd)
+
+def main():
+    """ Main function. Initializes Cnchi and creates it as a GTK App """
+    # Init cnchi
+    cnchi_init = CnchiInit()
+    # Create Gtk Application
+    my_app = CnchiApp(cnchi_init.cmd_line)
+    status = my_app.run(None)
+    sys.exit(status)
+
+if __name__ == '__main__':
+    main()
diff --git a/Cnchi/desktop.py b/Cnchi/desktop.py
new file mode 100644 (file)
index 0000000..d7ec33c
--- /dev/null
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  desktop.py
+#
+#  Copyright © 2013-2019 RebornOS
+#
+#  This file is part of Cnchi.
+#
+#  Cnchi 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.
+#
+#  Cnchi 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.
+#
+#  The following additional terms are in effect as per Section 7 of the license:
+#
+#  The preservation of all legal notices and author attributions in
+#  the material or in the Appropriate Legal Notices displayed
+#  by works containing it is required.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+
+""" Desktop screen """
+
+import os
+import logging
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, GdkPixbuf
+
+import desktop_info
+from pages.gtkbasebox import GtkBaseBox
+import misc.extra as misc
+
+CLASS_NAME = "DesktopAsk"
+
+class DesktopAsk(GtkBaseBox):
+    """ Class to show the Desktop screen """
+
+    def __init__(self, params, prev_page="keymap", next_page="features"):
+        super().__init__(self, params, "desktop", prev_page, next_page)
+
+        data_dir = self.settings.get('data')
+        self.desktops_dir = os.path.join(data_dir, "images", "desktops")
+
+        self.desktop_info = self.gui.get_object("desktop_info")
+
+        self.desktop_image = None
+        self.icon_desktop_image = None
+
+        # Set up list box
+        self.listbox = self.gui.get_object("listbox_desktop")
+        self.listbox.connect("row-selected", self.on_listbox_row_selected)
+        self.listbox.set_selection_mode(Gtk.SelectionMode.BROWSE)
+        self.listbox.set_sort_func(self.listbox_sort_by_name, None)
+
+        self.desktop_choice = 'base'
+
+        self.enabled_desktops = self.settings.get("desktops")
+
+        self.set_desktop_list()
+
+    def translate_ui(self, desktop, set_header=True):
+        """ Translates all ui elements """
+        label = self.gui.get_object("desktop_info")
+        txt = "<span weight='bold'>{0}</span>\n".format(
+            desktop_info.NAMES[desktop])
+        description = desktop_info.DESCRIPTIONS[desktop]
+        txt = txt + _(description)
+        label.set_markup(txt)
+
+        # This sets the desktop's image
+        path = os.path.join(self.desktops_dir, desktop + ".png")
+        if self.desktop_image is None:
+            self.desktop_image = Gtk.Image.new_from_file(path)
+            overlay = self.gui.get_object("image_overlay")
+            overlay.add(self.desktop_image)
+        else:
+            self.desktop_image.set_from_file(path)
+
+        # and this sets the icon
+        filename = "desktop-environment-" + desktop.lower() + ".svg"
+        icon_path = os.path.join(
+            desktop_info.DESKTOP_ICONS_PATH, "scalable", filename)
+        icon_exists = os.path.exists(icon_path)
+
+        if self.icon_desktop_image is None:
+            if icon_exists:
+                pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
+                    icon_path, 48, 48)
+                self.icon_desktop_image = Gtk.Image.new_from_pixbuf(pixbuf)
+            else:
+                filename = desktop.lower() + ".png"
+                icon_path = os.path.join(
+                    desktop_info.DESKTOP_ICONS_PATH, "48x48", filename)
+                icon_exists = os.path.exists(icon_path)
+                if icon_exists:
+                    self.icon_desktop_image = Gtk.Image.new_from_file(
+                        icon_path)
+                else:
+                    self.icon_desktop_image = Gtk.Image.new_from_icon_name(
+                        "image-missing",
+                        Gtk.IconSize.DIALOG)
+
+            overlay = self.gui.get_object("image_overlay")
+            overlay.add_overlay(self.icon_desktop_image)
+        else:
+            if icon_exists:
+                pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
+                    icon_path, 48, 48)
+                self.icon_desktop_image.set_from_pixbuf(pixbuf)
+            else:
+                filename = desktop.lower() + ".png"
+                icon_path = os.path.join(
+                    desktop_info.DESKTOP_ICONS_PATH, "48x48", filename)
+                icon_exists = os.path.exists(icon_path)
+                if icon_exists:
+                    self.icon_desktop_image.set_from_file(icon_path)
+                else:
+                    self.icon_desktop_image.set_from_icon_name(
+                        "image-missing", Gtk.IconSize.DIALOG)
+
+        if set_header:
+            # set header text
+            txt = _("Choose Your Desktop")
+            self.header.set_subtitle(txt)
+
+    def prepare(self, direction):
+        """ Prepare screen """
+        self.translate_ui(self.desktop_choice)
+        self.show_all()
+
+    def set_desktop_list(self):
+        """ Set desktop list in the ListBox """
+        for desktop in sorted(desktop_info.NAMES):
+            if desktop in self.enabled_desktops:
+                box = Gtk.HBox()
+
+                filename = "desktop-environment-" + desktop.lower() + ".svg"
+                icon_path = os.path.join(
+                    desktop_info.DESKTOP_ICONS_PATH, "scalable", filename)
+                if os.path.exists(icon_path):
+                    pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
+                        icon_path, 24, 24)
+                    image = Gtk.Image.new_from_pixbuf(pixbuf)
+                else:
+                    filename = desktop.lower() + ".png"
+                    icon_path = os.path.join(
+                        desktop_info.DESKTOP_ICONS_PATH, "24x24", filename)
+                    if os.path.exists(icon_path):
+                        image = Gtk.Image.new_from_file(icon_path)
+                    else:
+                        image = Gtk.Image.new_from_icon_name(
+                            "image-missing",
+                            Gtk.IconSize.LARGE_TOOLBAR)
+                box.pack_start(image, False, False, 2)
+
+                label = Gtk.Label()
+                label.set_markup(desktop_info.NAMES[desktop])
+                box.pack_start(label, False, False, 2)
+
+                self.listbox.add(box)
+
+        # Set base as default
+        self.select_default_row(desktop_info.NAMES["base"])
+
+    @staticmethod
+    def listbox_sort_by_name(row1, row2, _user_data):
+        """ Sort function for listbox
+            Returns : < 0 if row1 should be before row2, 0 if they are equal and > 0 otherwise
+            WARNING: IF LAYOUT IS CHANGED IN fill_listbox THEN THIS SHOULD BE
+            CHANGED ACCORDINGLY. """
+        box1 = row1.get_child()
+        label1 = box1.get_children()[1]
+
+        box2 = row2.get_child()
+        label2 = box2.get_children()[1]
+
+        text = [label1.get_text(), label2.get_text()]
+        # sorted_text = misc.sort_list(text, self.settings.get("locale"))
+        sorted_text = misc.sort_list(text)
+
+        # If strings are already well sorted return < 0
+        if text[0] == sorted_text[0]:
+            return -1
+
+        # Strings must be swaped, return > 0
+        return 1
+
+    def select_default_row(self, desktop_name):
+        """ Selects default row
+            WARNING: IF LAYOUT IS CHANGED IN desktop.ui THEN THIS SHOULD BE
+            CHANGED ACCORDINGLY. """
+        for listbox_row in self.listbox.get_children():
+            for vbox in listbox_row.get_children():
+                label = vbox.get_children()[1]
+                if desktop_name == label.get_text():
+                    self.listbox.select_row(listbox_row)
+                    return
+
+    def set_desktop(self, desktop):
+        """ Show desktop info """
+        for key in desktop_info.NAMES:
+            if desktop_info.NAMES[key] == desktop:
+                self.desktop_choice = key
+                self.translate_ui(self.desktop_choice, set_header=False)
+                return
+
+    def on_listbox_row_selected(self, _listbox, listbox_row):
+        """ Someone selected a different row of the listbox
+            WARNING: IF LAYOUT IS CHANGED IN desktop.ui THEN THIS SHOULD BE
+            CHANGED ACCORDINGLY. """
+        if listbox_row is not None:
+            for vbox in listbox_row:
+                label = vbox.get_children()[1]
+                desktop = label.get_text()
+                self.set_desktop(desktop)
+
+    def store_values(self):
+        """ Store desktop """
+        self.settings.set('desktop', self.desktop_choice.lower())
+        logging.info(
+            "Cnchi will install RebornOS with the '%s' desktop",
+            self.desktop_choice.lower())
+        return True
+
+    @staticmethod
+    def scroll_to_cell(treeview, path):
+        """ Scrolls treeview to show the desired cell """
+        treeview.scroll_to_cell(path)
+        return False
+
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+if __name__ == '__main__':
+    from test_screen import _, run
+    run('DesktopAsk')
diff --git a/Cnchi/desktop_info.py b/Cnchi/desktop_info.py
new file mode 100644 (file)
index 0000000..e74b5e0
--- /dev/null
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  desktop_info.py
+#
+#  Copyright © 2013-2019 RebornOS
+#
+#  This file is part of Cnchi.
+#
+#  Cnchi 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.
+#
+#  Cnchi 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.
+#
+#  The following additional terms are in effect as per Section 7 of the license:
+#
+#  The preservation of all legal notices and author attributions in
+#  the material or in the Appropriate Legal Notices displayed
+#  by works containing it is required.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+
+""" Desktop Environments information """
+
+# Disabled desktops(in DESKTOPS_DEV = DESKTOPS : "enlightenment", "windows", "apricity"
+# Enabled desktops
+
+DESKTOPS = ["base", "cinnamon", "deepin",
+            "gnome", "kde", "mate", "openbox", "xfce"]
+
+DESKTOPS_DEV = DESKTOPS + ["budgie", "i3", "lxqt", "pantheon"]
+
+DESKTOPS_A11Y = ["gnome", "mate", "apricity"]
+
+DESKTOP_ICONS_PATH = "/usr/share/cnchi/data/icons"
+
+'''
+MENU - Size appropriate for menus (16px).
+SMALL_TOOLBAR - Size appropriate for small toolbars (16px).
+LARGE_TOOLBAR - Size appropriate for large toolbars (24px)
+BUTTON - Size appropriate for buttons (16px)
+DND - Size appropriate for drag and drop (32px)
+DIALOG - Size appropriate for dialogs (48px)
+'''
+
+# Descriptive names
+NAMES = {
+    'apricity': "Original Apricity Experience",
+    'base': "Base",
+    'cinnamon': "Cinnamon",
+    'deepin': "Deepin",
+    'pantheon': "Pantheon",
+   'windows':"Windows Interface",
+    'gnome': "GNOME",
+    'kde': "KDE",
+    'mate': "MATE",
+    'openbox': "Openbox",
+    'xfce': "Xfce",
+    'budgie': "Budgie",
+    'enlightenment': "Enlightenment",
+    'i3': "i3",
+    'lxqt': "LXQt",
+}
+
+LIBS = {
+    'gtk': ["apricity", "cinnamon", "deepin", "pantheon", "gnome", "mate", "openbox", "xfce", "budgie", "enlightenment", "i3", "windows"],
+    'qt': ["kde", "lxqt"]
+}
+
+ALL_FEATURES = ["a11y", "aur", "bluetooth", "broadcom", "maintenance", "cups", "chromium", "email", "dropbox", "firefox", "firefox-developer-edition", "google-chrome", "firewire", "opera", "hardinfo", "hunspell", "vivaldi", "games", "graphics", "gtk-play", "hardinfo", "qt-play", "movie", "graphic_drivers", "lamp", "lts", "freeoffice", "wps-office", "libreoffice", "redshift", "power", "sshd", "spotify", "visual", "vlc", "nautilus", "nemo", "qownnotes", "wallpapers", "wine"]
+
+# Not all desktops have all features
+EXCLUDED_FEATURES = {
+    'base': ["bluetooth", "chromium", "maintenance", "dropbox", "email", "firefox", "firefox-developer-edition", "google-chrome", "firewire", "opera", "vivaldi", "games", "graphic_drivers", "graphics", "hardinfo", "hunspell", "freeoffice", "wps-office", "libreoffice", "visual", "vlc", "nautilus", "nemo", "qownnotes", "qt-play", "movie", "gtk-play", "qt-play", "power", "redshift", "spotify", "wallpapers", "wps-office", "libreoffice", "freeoffice"],
+    'apricity': ["lamp", "visual", "nautilus", "qt-play"],
+    'cinnamon': ["lamp", "visual", "nemo", "qt-play"],
+    'deepin': ["lamp", "visual", "qt-play"],
+    'pantheon': ["lamp", "visual", "qt-play", "nemo"],
+   'windows': ["lamp", "visual", "qt-play", "nemo"],
+    'gnome': ["lamp", "visual", "nautilus", "qt-play"],
+    'kde': ["lamp", "visual", "gtk-play"],
+    'mate': ["lamp", "visual", "qt-play"],
+    'openbox': ["lamp", "qt-play"],
+    'xfce': ["lamp", "visual", "qt-play"],
+    'budgie': ["lamp", "visual", "qt-play"],
+    'enlightenment': ["lamp", "visual", "qt-play"],
+    'i3': ["lamp", "qt-play"],
+    'lxqt': ["lamp", "visual", "gtk-play"]
+}
+
+# Session names for lightDM setup (/usr/share/xsessions)
+SESSIONS = {
+    'apricity' : 'gnome',
+    'cinnamon': 'cinnamon',
+    'deepin': 'deepin',
+    'pantheon': 'pantheon',
+    'gnome': 'gnome',
+    'kde': 'plasma',
+    'mate': 'mate',
+    'openbox': 'openbox',
+    'xfce': 'xfce',
+    'budgie': 'budgie-desktop',
+    'enlightenment': 'enlightenment',
+    'i3': 'i3',
+    'lxqt': 'lxsession',
+   'windows': 'windows'
+}
+
+
+# See http://docs.python.org/2/library/gettext.html "22.1.3.4. Deferred translations"
+def _(message):
+    return message
+
+
+DESCRIPTIONS = {
+    'base': _("This option will install RebornOS as command-line only system, "
+              "without any type of graphical interface. After the installation you can "
+              "customize RebornOS by installing packages with the command-line package manager."),
+   'apricity': _("Apricity OS is a now discontinued Linux distro in the Arch Linux family that simply  "
+                        "offered a highly customized GNOME dekstop experience that combined beauty with "
+                        "funcionality. With this option, the original Apricity look and feel is finally revivied! Experience "
+                         "it now on RebornOS."),
+    'cinnamon': _("Cinnamon is a Linux desktop which provides advanced, "
+                  "innovative features and a traditional desktop user experience. "
+                  "Cinnamon aims to make users feel at home by providing them with "
+                  "an easy-to-use and comfortable desktop experience."),
+    'deepin': _("IMPORTANT: Keep in mind that the Deepin desktop can often be unstable. "
+                "This does not depend on us, but on the developers of Deepin who "
+                "usually upload BETA versions of the desktop or some components in the "
+                "stable repositories of Arch Linux."),
+    'pantheon': _("Pantheon is the desktop environment that Elementary OS runs on. "
+                  "While true Pantheon is too unstable for RebornOS to offer, we "
+                  "have tried to offer the next best thing. By selecting Pantheon, "
+                  "you will get Elementary OS's good looks on a highly customized desktop "
+                  "running Cinnamon underneath. Through this, you will be able to run "
+                  "the majority of Elementary OS's applications and experience their "
+                  "stunning style and theming - all in a stable system."), 
+    'gnome': _("GNOME 3 is an easy and elegant way to use your "
+               "computer. It features the Activities Overview which "
+               "is an easy way to access all your basic tasks."),
+
+    'kde': _("If you are looking for a familiar working environment, KDE's "
+             "Plasma Desktop offers all the tools required for a modern desktop "
+             "computing experience so you can be productive right from the start."),
+
+    'mate': _("MATE is an intuitive, attractive, and lightweight desktop "
+              "environment which provides a more traditional desktop "
+              "experience. Accelerated compositing is supported, but not "
+              "required to run MATE making it suitable for lower-end hardware."),
+
+    'openbox': _("Not actually a desktop environment, Openbox is a highly "
+                 "configurable window manager. It is known for its "
+                 "minimalistic appearance and its flexibility. It is the most "
+                 "lightweight graphical option offered by RebornOS. Please "
+                 "Note: Openbox is not recommended for users who are new to Linux."),
+
+    'xfce': _("Xfce is a lightweight desktop environment. It aims to "
+              "be fast and low on system resources, while remaining visually "
+              "appealing and user friendly. It suitable for use on older "
+              "computers and those with lower-end hardware specifications. "),
+
+    'budgie': _("Budgie is the flagship desktop of Solus and is a Solus project. "
+                "It focuses on simplicity and elegance. Written from scratch with "
+                "integration in mind, the Budgie desktop tightly integrates with "
+                "the GNOME stack, but offers an alternative desktop experience."),
+
+    'enlightenment': _("Enlightenment is not just a window manager for Linux/X11 "
+                       "and others, but also a whole suite of libraries to help "
+                       "you create beautiful user interfaces with much less work"),
+
+    'i3': _("i3 is a tiling window manager primarily targeted at advanced "
+            "users and developers."),
+
+    'lxqt': _("LXQt is the next-generation of LXDE, the Lightweight Desktop "
+              "Environment. It is lightweight, modular, blazing-fast, and "
+              "user-friendly."),
+   
+   'windows': _("While I am sure you have all heard of Windows, this option "
+                           "does NOT truly offer you straight up Windows. This is Linux after all, " 
+                           "not Microsoft. However, what this option DOES allow you to experience, "
+                           "is a Windows-like desktop running Cinnamon underneath, made to look "
+                           "and act like the Windows 10 you are already familiar with. Made with Linux "
+                          "newbies specifically in mind, this option should hopefully ensure you have "
+                          "an easy, hassle free transition to Linux.")
+}
+
+# Delete previous _() dummy declaration
+del _
diff --git a/Cnchi/encfs.py b/Cnchi/encfs.py
new file mode 100644 (file)
index 0000000..ce836f5
--- /dev/null
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  encfs.py
+#
+#  Copyright © 2013-2019 RebornOS
+#
+#  This file is part of Cnchi.
+#
+#  Cnchi 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.
+#
+#  Cnchi 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.
+#
+#  The following additional terms are in effect as per Section 7 of the license:
+#
+#  The preservation of all legal notices and author attributions in
+#  the material or in the Appropriate Legal Notices displayed
+#  by works containing it is required.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+
+""" Configures Antergos to encrypt user's home with encFS """
+
+import os
+import shutil
+import subprocess
+import logging
+
+import misc.extra as misc
+
+# TODO: This is unfinished and untested
+
+
+@misc.raise_privileges
+def backup_conf_files(dest_dir):
+    """ Copy encfs setup files """
+    conf_files = [
+        "etc/security/pam_encfs.conf",
+        "etc/security/pam_env.conf",
+        "etc/fuse.conf",
+        "etc/pam.d/system-login",
+        "etc/pam.d/system-auth"]
+    for conf_file in conf_files:
+        path = os.path.join(dest_dir, conf_file)
+        if os.path.exists(path):
+            shutil.copy(path, path + ".cnchi")
+    os.system("sync")
+
+
+@misc.raise_privileges
+def setup_conf_files(dest_dir):
+    """ Setup encfs """
+    path = os.path.join(dest_dir, "etc/security/pam_encfs.conf")
+    with open(path, 'w') as pam_encfs:
+        pam_encfs.write("# File created by Cnchi (RebornOS Installer)\n\n")
+        pam_encfs.write(
+            "# If this is specified program will attempt to drop permissions "
+            "before running encfs.\n")
+        pam_encfs.write("drop_permissions\n\n")
+        pam_encfs.write(
+            "# This specifies which options to pass to encfs for every user.\n")
+        pam_encfs.write(
+            "# You can find encfs options by running encfs without any arguments\n")
+        pam_encfs.write("encfs_default --idle=1\n\n")
+        pam_encfs.write("# Same for fuse\n")
+        pam_encfs.write("# you can find fuse options with encfs -H\n")
+        pam_encfs.write("fuse_default allow_root,nonempty\n\n")
+        pam_encfs.write("# Added by Cnchi - RebornOS Installer\n")
+        # USERNAME SOURCE TARGET_PATH ENCFS_Options FUSE_Options
+        pam_encfs.write("-\t/home/.encfs\t-\t-v\t-\n")
+
+    path = os.path.join(dest_dir, "etc/security/pam_env.conf")
+    with open(path, 'a') as pam_env:
+        pam_env.write("\n# Added by Cnchi - RebornOS Installer\n")
+        pam_env.write(
+            "# Set the ICEAUTHORITY file location to allow GNOME to start on encfs $HOME\n")
+        pam_env.write("ICEAUTHORITY DEFAULT=/tmp/.ICEauthority_@{PAM_USER}\n")
+
+    path = os.path.join(dest_dir, "etc/fuse.conf")
+    with open(path, 'a') as fuse_conf:
+        fuse_conf.write("\n# Added by Cnchi - RebornOS Installer\n")
+        fuse_conf.write("user_allow_other\n")
+
+    path = os.path.join(dest_dir, "etc/pam.d/system-login")
+    with open(path, 'a') as system_login:
+        system_login.write("\n# Added by Cnchi - RebornOS Installer\n")
+        system_login.write("session required\tpam_encfs.so\n")
+        system_login.write("session optional\tpam_mount.so\n")
+
+    path = os.path.join(dest_dir, "etc/pam.d/system-auth")
+    with open(path, "a") as system_auth:
+        system_auth.write("\n# Added by Cnchi - RebornOS Installer\n")
+        system_auth.write("auth sufficient\tpam_encfs.so\n")
+        system_auth.write("auth optional\tpam_mount.so\n")
+
+
+@misc.raise_privileges
+def setup(username, dest_dir, password):
+    """ Encrypt user's home folder """
+    # encfs and pam_mount packages are needed
+    # and pam_encfs from AUR, too.
+    # Reference: https://wiki.debian.org/TransparentEncryptionForHomeFolder
+
+    try:
+        backup_conf_files(dest_dir)
+        setup_conf_files(dest_dir)
+    except Exception as ex:
+        logging.error("Can't create and modify encfs configuration files.")
+        template = "An exception of type {0} occured. Arguments:\n{1!r}"
+        message = template.format(type(ex).__name__, ex.args)
+        logging.error(message)
+        logging.error("Home directory won't be encrypted.")
+        return False
+
+    # Move user home dir out of the way
+    mount_dir = os.path.join(dest_dir, "home/", username)
+    backup_dir = os.path.join(dest_dir, "var/tmp/", username)
+    shutil.move(mount_dir, backup_dir)
+
+    # Create necessary dirs, encrypted and mounted(unencrypted)
+    encrypted_dir = os.path.join(dest_dir, "home/.encfs/", username)
+    os.makedirs(encrypted_dir, mode=0o755)
+    os.makedirs(mount_dir, mode=0o755)
+
+    # Set owner
+    shutil.chown(encrypted_dir, username, "users")
+    shutil.chown(mount_dir, username, "users")
+
+    # Create encrypted directory
+    try:
+        cmd = ["/bin/echo", "-e", "p\n{0}\n".format(password)]
+        passw = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+        cmd = ['encfs', '-S', encrypted_dir, mount_dir, "--public"]
+        encfs = subprocess.Popen(
+            cmd, stdin=passw.stdout, stdout=subprocess.PIPE)
+        encfs.communicate()
+        if encfs.poll() != 0:
+            logging.error("Can't run encfs. Bad password?")
+    except subprocess.CalledProcessError as err:
+        logging.error("Error running %s: %s", err.cmd, err.output)
+
+    # Restore user home files
+    for name in os.listdir(backup_dir):
+        shutil.move(os.path.join(backup_dir, name),
+                    os.path.join(mount_dir, name))
+
+    # Delete home backup
+    os.rmdir(backup_dir)
+
+
+if __name__ == '__main__':
+    setup("test", "/", "1234")
+
diff --git a/Cnchi/features.py b/Cnchi/features.py
new file mode 100644 (file)
index 0000000..f9c1059
--- /dev/null
@@ -0,0 +1,512 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  features.py
+#
+#  Copyright © 2013-2019 RebornOS
+#
+#  This file is part of Cnchi.
+#
+#  Cnchi 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.
+#
+#  Cnchi 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.
+#
+#  The following additional terms are in effect as per Section 7 of the license:
+#
+#  The preservation of all legal notices and author attributions in
+#  the material or in the Appropriate Legal Notices displayed
+#  by works containing it is required.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+
+""" Features screen """
+import subprocess
+import logging
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+import desktop_info
+import features_info
+
+import misc.extra as misc
+
+from pages.gtkbasebox import GtkBaseBox
+
+from lembrame.dialog import LembrameDialog
+
+from hardware.modules.nvidia import Nvidia
+from hardware.modules.nvidia_390xx import Nvidia390xx
+from hardware.modules.nvidia_340xx import Nvidia340xx
+from hardware.modules.nvidia_304xx import Nvidia304xx
+from hardware.modules.catalyst import Catalyst
+from hardware.modules.amdgpu import AMDGpu
+from hardware.modules.amdgpu_exp import AMDGpuExp
+from hardware.modules.i915 import Intel915
+
+class Graphics():
+    """ Gets graphic device info using the hardware module """
+    """ Removed from nvidia detection: 340xx and 304xx. See below: """
+    """ Nvidia340xx().detect() or Nvidia304xx().detect() """
+
+    @staticmethod
+    def nvidia():
+        """ Returns true if an nVidia card is detected """
+        if (Nvidia().detect() or Nvidia390xx().detect()):
+            return True
+        return False
+
+    @staticmethod
+    def amd():
+        """ Returns true if an AMD card is detected """
+        if (Catalyst().detect() or AMDGpu().detect() or
+            AMDGpuExp().detect()):
+            return True
+        return False
+
+    @staticmethod
+    def i915():
+        """ Returns if an Intel card is detected """
+        return Intel915().detect()
+
+    def bumblebee(self):
+        """ Returns true if an nVidia and an Intel card are detected """
+        return self.nvidia() and self.i915()
+
+
+class Features(GtkBaseBox):
+    """ Features screen class """
+
+    COL_ICON = 0
+    COL_TITLE = 1
+    COL_DESCRIPTION = 2
+    COL_SWITCH = 3
+
+    def __init__(self, params, prev_page="desktop", next_page="cache"):
+        """ Initializes features ui """
+        # This is initialized each time this screen is shown in prepare()
+        self.features = None
+
+        super().__init__(self, params, "features", prev_page, next_page)
+
+        self.graphics = Graphics()
+
+        self.listbox_rows = {}
+
+        self.a11y = params['a11y']
+
+        self.show_advanced = False
+        self.advanced_checkbutton = self.gui.get_object("advanced_checkbutton")
+        self.advanced_checkbutton.set_active(False)
+
+        # Set up list box
+        self.listbox = self.gui.get_object("listbox")
+        self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
+        self.listbox.set_sort_func(self.listbox_sort_by_name, None)
+
+        # Only show ufw rules and aur disclaimer info once
+        self.info_already_shown = {"ufw": False, "aur": False}
+
+        # Only load defaults for each DE the first time this screen is shown
+        self.defaults_loaded = False
+
+        # Store which features where active when lembrame was selected
+        # (when lembrame is selected, all other features are deactivated)
+        self.features_lembrame = []
+
+    def show_advanced_toggled(self, _widget):
+        """ Display or hide advanced features """
+        self.show_advanced = self.advanced_checkbutton.get_active()
+        self.update_advanced_features()
+
+    @staticmethod
+    def on_listbox_row_selected(_listbox, listbox_row):
+        """ Someone selected a different row of the listbox
+            WARNING: IF LIST LAYOUT IS CHANGED THEN THIS SHOULD BE CHANGED ACCORDINGLY. """
+        if listbox_row is not None:
+            for vbox in listbox_row:
+                switch = vbox.get_children()[2]
+                if switch:
+                    switch.set_active(not switch.get_active())
+
+    def add_feature_icon(self, feature, box):
+        """ Adds feature icon to listbox row box """
+        if feature in features_info.ICON_NAMES:
+            icon_name = features_info.ICON_NAMES[feature]
+        else:
+            logging.debug("No icon found for feature %s", feature)
+            icon_name = "missing"
+
+        image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DND)
+        object_name = "image_" + feature
+        image.set_name(object_name)
+        image.set_property('margin_start', 10)
+        self.listbox_rows[feature].append(image)
+        box.pack_start(image, False, False, 0)
+
+    def add_feature_label(self, feature, box):
+        """ Adds feature title and label to listbox row box """
+        text_box = Gtk.VBox()
+
+        object_name = "label_title_" + feature
+        label_title = Gtk.Label.new()
+        label_title.set_halign(Gtk.Align.START)
+        label_title.set_justify(Gtk.Justification.LEFT)
+        label_title.set_name(object_name)
+        self.listbox_rows[feature].append(label_title)
+        text_box.pack_start(label_title, False, True, 0)
+
+        object_name = "label_" + feature
+        label = Gtk.Label.new()
+        label.set_halign(Gtk.Align.START)
+        label.set_justify(Gtk.Justification.LEFT)
+        label.set_name(object_name)
+        self.listbox_rows[feature].append(label)
+        text_box.pack_start(label, False, False, 0)
+        box.pack_start(text_box, False, False, 0)
+
+    def on_switch_activated(self, switch, _gparam):
+        """ Feature has been activated or deactivated """
+        for feature in self.features:
+            row = self.listbox_rows[feature]
+            if row[Features.COL_SWITCH] == switch:
+                is_active = switch.get_active()
+                self.settings.set("feature_" + feature, is_active)
+                # Extra actions on this switch trigger
+                self.extra_switch_actions(feature, is_active)
+
+    def extra_switch_actions(self, feature, is_active):
+        """ Lembrame feature disables all others, any other disables lembrame """
+        if is_active:
+            if feature == 'lembrame':
+                # Disable all switches if Lembrame is selected
+                logging.debug("Activating Lembrame. Deactivating the rest of the switches")
+                self.features_lembrame = []
+                for row_feature in self.listbox_rows:
+                    if row_feature != 'lembrame':
+                        switch = self.listbox_rows[row_feature][Features.COL_SWITCH]
+                        if switch.get_active():
+                            self.features_lembrame.append(row_feature)
+                            switch.set_active(False)
+            else:
+                # Disable lembrame if any other option is activated
+                self.features_lembrame = []
+                try:
+                    switch = self.listbox_rows['lembrame'][Features.COL_SWITCH]
+                    if switch and switch.get_active():
+                        msg = "Activating something else besides Lembrame. Deactivating Lembrame."
+                        logging.debug(msg)
+                        switch.set_active(False)
+                except KeyError as err:
+                    pass
+        elif feature == 'lembrame':
+            # Activate previous deactivated features
+            for row_feature in self.features_lembrame:
+                switch = self.listbox_rows[row_feature][Features.COL_SWITCH]
+                switch.set_active(True)
+            self.features_lembrame = []
+
+    def add_feature_switch(self, feature, box):
+        """ Add a switch so the user can activate/deactivate the feature """
+        object_name = "switch_" + feature
+        switch = Gtk.Switch.new()
+        switch.set_name(object_name)
+        switch.set_property('margin_top', 10)
+        switch.set_property('margin_bottom', 10)
+        switch.set_property('margin_end', 10)
+        switch.connect("notify::active", self.on_switch_activated)
+        self.listbox_rows[feature].append(switch)
+        box.pack_end(switch, False, False, 0)
+
+    def fill_listbox(self):
+        """ Fills listbox with all the features and switches """
+        for listbox_row in self.listbox.get_children():
+            listbox_row.destroy()
+
+        self.listbox_rows = {}
+
+        # Only add graphic-driver feature if an AMD or Nvidia is detected
+        if "graphic_drivers" in self.features:
+            allow = False
+            if self.graphics.amd():
+                allow = True
+            if self.graphics.nvidia() and not self.graphics.bumblebee():
+                allow = True
+            if not allow:
+                logging.debug("Neither AMD nor Nvidia cards have been detected. "
+                              "Removing proprietary graphic drivers feature.")
+                self.features.remove("graphic_drivers")
+
+        for feature in self.features:
+            box = Gtk.Box(spacing=20)
+            box.set_name(feature + "-row")
+
+            self.listbox_rows[feature] = []
+
+            self.add_feature_icon(feature, box)
+            self.add_feature_label(feature, box)
+            self.add_feature_switch(feature, box)
+            # Add row to our gtklist
+            self.listbox.add(box)
+
+        self.listbox.show_all()
+
+    def update_advanced_features(self):
+        """ Shows or hides advanced features """
+        try:
+            if self.features:
+                for feature in self.features:
+                    row = self.listbox_rows[feature]
+                    box = row[Features.COL_ICON].get_parent()
+                    listboxrow = box.get_parent()
+                    if feature in features_info.ADVANCED and not self.show_advanced:
+                        listboxrow.hide()
+                    else:
+                        listboxrow.show()
+        except AttributeError as msg:
+            logging.debug(msg)
+
+    @staticmethod
+    def listbox_sort_by_name(row1, row2, _user_data):
+        """ Sort function for listbox
+            Returns : < 0 if row1 should be before row2, 0 if they are equal and > 0 otherwise
+            WARNING: IF LAYOUT IS CHANGED IN fill_listbox THEN THIS SHOULD BE
+            CHANGED ACCORDINGLY. """
+        box1 = row1.get_child()
+        txt_box1 = box1.get_children()[1]
+        label1 = txt_box1.get_children()[0]
+
+        box2 = row2.get_child()
+        txt_box2 = box2.get_children()[1]
+        label2 = txt_box2.get_children()[0]
+
+        text = [label1.get_text(), label2.get_text()]
+        # sorted_text = misc.sort_list(text, self.settings.get("locale"))
+        sorted_text = misc.sort_list(text)
+
+        # If strings are already well sorted return < 0
+        if text[0] == sorted_text[0]:
+            return -1
+
+        # Strings must be swaped, return > 0
+        return 1
+
+    def set_row_text(self, feature, title, desc, tooltip):
+        """ Set translated text to our listbox feature row """
+        if feature in self.listbox_rows:
+            title = "<span weight='bold' size='large'>{0}</span>".format(title)
+            desc = "<span size='small'>{0}</span>".format(desc)
+            row = self.listbox_rows[feature]
+            row[Features.COL_TITLE].set_markup(title)
+            row[Features.COL_DESCRIPTION].set_markup(desc)
+            for widget in row:
+                widget.set_tooltip_markup(tooltip)
+
+    def translate_ui(self):
+        """ Translates all ui elements """
+
+        self.advanced_checkbutton.set_label(_("Show advanced features"))
+
+        desktop = self.settings.get('desktop')
+        txt = desktop_info.NAMES[desktop] + " - " + _("Feature Selection")
+        self.header.set_subtitle(txt)
+
+        for feature in self.features:
+            title = _(features_info.TITLES[feature])
+            desc = _(features_info.DESCRIPTIONS[feature])
+            tooltip = _(features_info.TOOLTIPS[feature])
+            self.set_row_text(feature, title, desc, tooltip)
+
+        # Sort listbox items
+        self.listbox.invalidate_sort()
+
+    def switch_defaults_on(self):
+        """ Enable some features by default """
+
+        if 'bluetooth' in self.features:
+            try:
+                process1 = subprocess.Popen(["/usr/bin/lsusb"], stdout=subprocess.PIPE)
+                process2 = subprocess.Popen(
+                    ["grep", "-i", "bluetooth"],
+                    stdin=process1.stdout,
+                    stdout=subprocess.PIPE)
+                process1.stdout.close()
+                out, _process_error = process2.communicate()
+                if out.decode():
+                    row = self.listbox_rows['bluetooth']
+                    row[Features.COL_SWITCH].set_active(True)
+            except subprocess.CalledProcessError as err:
+                logging.warning(
+                    "Error checking bluetooth presence. Command %s failed: %s",
+                    err.cmd,
+                    err.output)
+
+        if 'cups' in self.features:
+            row = self.listbox_rows['cups']
+            row[Features.COL_SWITCH].set_active(True)
+
+        if 'visual' in self.features:
+            row = self.listbox_rows['visual']
+            row[Features.COL_SWITCH].set_active(True)
+
+        if 'chromium' in self.features:
+            row = self.listbox_rows['firefox']
+            row[Features.COL_SWITCH].set_active(True)
+
+        if 'aur' in self.features:
+            row = self.listbox_rows['aur']
+            row[Features.COL_SWITCH].set_active(True)
+
+        if 'lts' in self.features:
+            row = self.listbox_rows['lts']
+            row[Features.COL_SWITCH].set_active(True)
+
+        if 'a11y' in self.features and self.a11y:
+            row = self.listbox_rows['a11y']
+            row[Features.COL_SWITCH].set_active(True)
+
+    def show_disclaimer_messages(self):
+        """ Show ufw and AUR warning messages if necessary """
+        # Show ufw info message if ufw is selected (show it only once)
+        if self.settings.get("feature_firewall") and not self.info_already_shown["ufw"]:
+            self.show_info_dialog("ufw")
+            self.info_already_shown["ufw"] = True
+
+        # Show AUR disclaimer if AUR is selected (show it only once)
+        if self.settings.get("feature_aur") and not self.info_already_shown["aur"]:
+            self.show_info_dialog("aur")
+            self.info_already_shown["aur"] = True
+
+    def show_info_dialog(self, feature):
+        """ Some features show an information dialog when this screen is accepted """
+        if feature == "aur":
+            # Aur disclaimer
+            txt1 = _("Arch User Repository - Disclaimer")
+            txt2 = _("The Arch User Repository is a collection of user-submitted PKGBUILDs\n"
+                     "that supplement software available from the official repositories.\n\n"
+                     "The AUR is community driven and NOT supported by Arch or RebornOS.\n")
+        elif feature == "ufw":
+            # Ufw rules info
+            txt1 = _("Uncomplicated Firewall will be installed with these rules:")
+            toallow = misc.get_network()
+            txt2 = _("ufw default deny\nufw allow from {0}\nufw allow Transmission\n"
+                     "ufw allow SSH").format(toallow)
+        else:
+            # No message
+            return
+
+        txt1 = "<big>{0}</big>".format(txt1)
+        txt2 = "<i>{0}</i>".format(txt2)
+
+        info = Gtk.MessageDialog(
+            transient_for=self.get_main_window(),
+            modal=True,
+            destroy_with_parent=True,
+            message_type=Gtk.MessageType.INFO,
+            buttons=Gtk.ButtonsType.CLOSE)
+        info.set_markup(txt1)
+        info.format_secondary_markup(txt2)
+        info.run()
+        info.destroy()
+
+    def ask_nginx(self):
+        """ LAMP: Ask user if he wants Apache or Nginx """
+        if self.settings.get("feature_lamp"):
+            info = Gtk.MessageDialog(
+                transient_for=self.get_main_window(),
+                modal=True,
+                destroy_with_parent=True,
+                message_type=Gtk.MessageType.INFO,
+                buttons=Gtk.ButtonsType.YES_NO)
+            info.set_markup("LAMP / LEMP")
+            msg = _(
+                "Do you want to install the Nginx server instead of the Apache server?")
+            info.format_secondary_markup(msg)
+            response = info.run()
+            info.destroy()
+            if response == Gtk.ResponseType.YES:
+                self.settings.set("feature_lemp", True)
+            else:
+                self.settings.set("feature_lemp", False)
+
+    def ask_lembrame(self):
+        """ Asks user for lembrame credentials """
+        if self.settings.get("feature_lembrame"):
+            dlg = LembrameDialog(
+                self.get_main_window(),
+                self.gui_dir)
+
+            response = dlg.run()
+
+            if response == Gtk.ResponseType.APPLY:
+                logging.debug("Saving Lembrame credentials")
+                self.settings.set(
+                    'lembrame_credentials',
+                    dlg.get_credentials())
+
+            dlg.destroy()
+
+    def store_switches(self):
+        """ Store current feature selections """
+        for feature in self.features:
+            row = self.listbox_rows[feature]
+            is_active = row[Features.COL_SWITCH].get_active()
+            self.settings.set("feature_" + feature, is_active)
+            if is_active:
+                logging.debug("Feature '%s' has been selected", feature)
+
+    def store_values(self):
+        """ Go to next screen, but first save changes """
+
+        self.store_switches()
+        self.show_disclaimer_messages()
+        self.ask_nginx()
+        self.ask_lembrame()
+
+        self.listbox_rows = {}
+
+        return True
+
+    def prepare(self, direction):
+        """ Prepare features screen to get ready to show itself """
+        # Each desktop has its own features
+        desktop = self.settings.get('desktop')
+        self.features = list(
+            set(desktop_info.ALL_FEATURES) -
+            set(desktop_info.EXCLUDED_FEATURES[desktop]))
+        self.fill_listbox()
+        self.translate_ui()
+        self.show_all()
+        if not self.defaults_loaded:
+            self.switch_defaults_on()
+            # Only load defaults once
+            self.defaults_loaded = True
+        else:
+            # Load values user has chosen when this screen is shown again
+            self.load_values()
+        self.update_advanced_features()
+
+    def load_values(self):
+        """ Get previous selected switches values """
+        for feature in self.features:
+            row = self.listbox_rows[feature]
+            is_active = self.settings.get("feature_" + feature)
+            if row[Features.COL_SWITCH] is not None and is_active is not None:
+                row[Features.COL_SWITCH].set_active(is_active)
+
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
diff --git a/Cnchi/features_info.py b/Cnchi/features_info.py
new file mode 100755 (executable)
index 0000000..4db0a99
--- /dev/null
@@ -0,0 +1,354 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  features_info.py
+#
+#  Copyright © 2013-2019 RebornOS
+#
+#  This file is part of Cnchi.
+#
+#  Cnchi 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.
+#
+#  Cnchi 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.
+#
+#  The following additional terms are in effect as per Section 7 of the license:
+#
+#  The preservation of all legal notices and author attributions in
+#  the material or in the Appropriate Legal Notices displayed
+#  by works containing it is required.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+
+""" Features information """
+
+# Note: As each desktop has its own features, these are listed
+# in desktop_info file instead of here.
+
+
+ICON_NAMES = {
+    'a11y': 'preferences-desktop-accessibility',
+    'aur': 'system-software-install',
+    'bluetooth': 'bluetooth',
+    'broadcom': 'cs-drivers',
+    'cups': 'printer',
+    'chromium': 'chromium',
+    'dropbox': 'dropbox',
+    'email': 'thunderbird',
+    'firefox': 'firefox',
+    'firefox-developer-edition': 'firefox-developer-edition',
+    'google-chrome': 'google-chrome',
+#    'firewall': 'network-server',
+    'fonts': 'preferences-desktop-font',
+    'firewire': 'drive-harddisk-ieee1394',
+    'games': 'applications-games',
+#    'graphics': 'apps.com.pixlr',
+    'graphics': 'accessories-painting',
+    'gtk-play': 'applications-games',
+    'hardinfo': 'hardinfo',
+    'qt-play': 'applications-games',
+    'maintenance': 'baobab',
+    'movie': 'artemanufrij.screencast',
+    'graphic_drivers': 'gnome-system',
+    'lamp': 'applications-internet',
+    'lts': 'applications-accessories',
+    'nemo': 'system-file-manager',
+    'nautilus': 'system-file-manager',
+    'qownnotes': 'QOwnNotes',
+    'wps-office': 'wps-office-wpt',
+    'libreoffice': 'libreoffice-writer',
+    'freeoffice': 'libreoffice-writer',
+    'power': 'battery-full-charged',
+#    'plymouth': 'debian-plymouth-manager',
+    'redshift': 'redshift',
+    'sshd': 'gnome-mime-x-directory-smb-share',
+    'spotify': 'spotify-client',
+#    'skype': 'skype',
+    'visual': 'video-display',
+    'vivaldi': 'vivaldi',
+    'vlc': 'vlc',
+    'wallpapers': 'background',
+    'wine': 'wine',
+    'opera': 'opera'}
+
+# These features are considered 'advanced' so it won't be shown by default
+ADVANCED = [ 'lamp', 'sshd', 'visual', 'firewire', 'broadcom', 'nautilus', 'nemo', 'email', 'wallpapers', 'hunspell' ]
+
+# These features are considered 'basic', and will be shown by default
+BASIC = ['opera', 'wine', 'wallpapers', 'vlc', 'vivaldi', 'visual', 'spotify', 'redshift', 'power', 'libreoffice', 'wps-office', 'freeoffice', 'qownnotes', 'lts', 'graphic_drivers', 'movie', 'maintenance', 'qt-play', 'hardinfo', 'gtk-play', 'graphics', 'games', 'fonts', 'firefox', 'firefox-developer-edition', 'google-chrome', 'email', 'dropbox', 'chromium', 'cups', 'bluetooth', 'aur', 'a11y']
+
+# See http://docs.python.org/2/library/gettext.html "22.1.3.4. Deferred translations"
+def _(message):
+    return message
+
+TITLES = {
+    'a11y': _("Adds accessibility packages"),
+    'aur': _("Arch User Repository (AUR) Support"),
+    'bluetooth': _("Bluetooth Support"),
+    'broadcom': _("Broadcom Driver Support"),
+    'cups': _("Printing Support"),
+    'chromium': _("Chromium Web Browser"),
+    'dropbox': _("Dropbox"),
+    'email': _("Desktop Email Client"),
+    'firefox': _("Firefox Web Browser"),
+    'firefox-developer-edition': _("Firefox Developer Edition"),
+    'google-chrome': _("Google Chrome"),
+    'opera': _("Opera Web Browser"),
+    'vivaldi': _("Vivaldi Web Browser"),
+#    'firewall': _("Uncomplicated Firewall"),
+    'fonts': _("Extra Truetype Fonts"),
+    'firewire': _("Support For Firewire Devices"),
+    'games': _("Steam + PlayonLinux"),
+    'graphic_drivers': _("Graphic drivers (Proprietary)"),
+    'gtk-play': _("Popular Games for Linux"),
+    'qt-play': _("Popular Games for Linux"),
+    'hardinfo': _("Hardware Analysis"),
+    'maintenance': _("Applications to Perform System Maintenance"),
+    'movie': _("Common Video Editing Programs for Linux"),
+    'graphics': _("Common Photo editing Programs for Linux"),
+    'hunspell': _("Spell Check"),
+    'lamp': _("Apache (or Nginx) + Mariadb + PHP"),
+    'lts': _("Kernel (LTS version)"),
+    'libreoffice': _("LibreOffice"),
+    'wps-office': _("WPS Office"),
+    'freeoffice': _("FreeOffice"),
+    'power': _("Power Saving"),
+#    'plymouth': _("Boot Screen"),
+    'redshift': _("Redshift"),
+    'sshd': _("Windows sharing SMB"),
+#    'skype': _("Skype"),
+    'spotify': _("Spotify"),
+    'visual': _("Visual Effects"),
+    'vlc': _("VLC"),
+    'wallpapers': _("Wallpapers Cycler"),
+    'wine': _("Run Windows Programs on Linux"),
+    'nemo': _("Nemo File Manager"),
+    'qownnotes': _("QOwnNotes"),
+    'nautilus': _("Nautilus File Manager")}
+
+DESCRIPTIONS = {
+    'a11y': _("Useful packages for individuals who are blind or visually impaired."),
+    'aur': _("The AUR is a community-driven repository for Arch users."),
+    'bluetooth': _("Enables your system to make wireless connections via Bluetooth."),
+    'broadcom': _("Enables your system to effectively use a Broadcom driver"),
+    'chromium': _("Open-source web browser from Google."),
+    'email': _("Installs Thunderbird as your Desktop Email Client"),
+    'dropbox': _("Free file hosting service for Linux (installed from external source to avoid copywrite issues)"),
+    'firefox': _("A popular open-source graphical web browser from Mozilla."),
+    'firefox-developer-edition': _("Firefox for Developers"),
+    'google-chrome': _("Chrome is a free Internet browser officially released by Google"),
+    'opera':_("Opera is an innovative, minimalistic web browser from Opera.Inc"),
+    'vivaldi': _("Vivaldi is a free, fast web browser designed for power-users."),
+    'fonts': _("TrueType fonts from the Google Fonts project."),
+    'firewire': _("Linux Support For Firewire Devices"),
+    'games': _("Installs Steam and Playonlinux for gaming enthusiasts."),
+    'graphic_drivers': _("Installs AMD or Nvidia proprietary graphic driver."),
+    'gtk-play': _("Popular games for Linux, all created for use on your Desktop Environment"),
+    'hunspell': _("Slightly Broken (atm) Spell Check Packages for RebornOS"),
+    'hardinfo': _("Easy application for extensive hardware analysis"),
+    'qt-play': _("Popular games for Linux, all created for use on your Desktop Environment"),
+    'maintenance': _("Common Applications to Perform System Maintenance On Linux"),
+    'movie': _("Common video editing programs for Linux"),
+    'graphics': _("Common Photo editing Programs for Linux"),
+    'lamp': _("Apache (or Nginx) + Mariadb + PHP installation and setup."),
+    'cups': _("Installation of printer drivers and management tools."),
+    'wps-office': _("Office Suit for Linux, made for those used to MS Office"),
+    'libreoffice': _("Open source office suite. Supports editing MS Office files."),
+    'freeoffice': _("FreeOffice is a full-featured Office suite."),
+    'visual': _("Enable transparency, shadows, and other desktop effects."),
+    'vlc': _("Ultimate Media Player For Linux"),
+#    'firewall': _("Control the incoming and outgoing network traffic."),
+    'lts': _("Long term support (LTS) Linux kernel and modules."),
+    'power': _("Power Saving Tools Geared Specifically for Laptops"),
+#    'plymouth': _("Uses Plymouth To Offer You a Polished Boot Screen"),
+    'redshift': _("Color Temperature Adjuster Based on Local Time"),
+    'sshd': _("Provides client access to shared files and printers."),
+#   'skype': _("A User Friendly Video Chat Tool Made By Microsoft"),
+    'spotify': _("A widely popular music, podcast, and video streaming service"),
+    'nemo': _("Default file manager for the Cinnamon desktop."),
+    'wallpapers': _("Wallpapers Cycler That Changes Wallpapers Every Day"),
+    'wine': _("Run Common Windows Programs on Linux Easily"),
+    'qownnotes': _("Your notes on your hard disk and in your own cloud"),
+    'nautilus': _("Default file manager for the Gnome desktop.")}
+
+TOOLTIPS = {
+    'a11y': _("Useful packages for individuals who are blind or visually impaired."),
+    'aur': _("Use yaourt to install AUR packages.\n"
+             "The AUR was created to organize and share new packages\n"
+             "from the community and to help expedite popular packages'\n"
+             "inclusion into the [community] repository."),
+    'bluetooth': _("Bluetooth is a standard for the short-range wireless\n"
+                   "interconnection of cellular phones, computers, and\n"
+                   "other electronic devices. In Linux, the canonical\n"
+                   "implementation of the Bluetooth protocol stack is BlueZ."),
+    'broadcom': _("NOTE: IF YOU ARE UNSURE EXACTLY OF WHAT NEMO IS, IT IS ADVISED TO\n"
+                               "NOT ENABLE THIS FEATURE.\n"
+                              "However, if you are sure that you are using a braodcom driver on your system,\n"
+                              "then it is advised to enable this option as it installs several broadcom dependencies."),
+    'cups': _("CUPS is the standards-based, open source printing\n"
+              "system developed by Apple Inc. for OS® X and other\n"
+              "UNIX®-like operating systems."),
+    'chromium': _("Chromium is an open-source browser project that aims to build a\n"
+                  "safer, faster, and more stable way for all users to experience the web.\n"
+                  "(this is the default)"),
+    'email': _("Thunderbird is one of the most common and stable desktop email clients\n"
+               "for Linux around. It is is a free, open source, cross-platform email, news,\n"
+               "RSS, and chat client developed by the Mozilla Foundation for you."),
+    'dropbox': _("Dropbox is a free file hosting and synchronization service for Linux\n"
+                 "that integrates fully into your file manager - all for free (installed from \n"
+                 "external source to avoid copywrite issues)"),
+    'firefox': _("Mozilla Firefox (known simply as Firefox) is a free and\n"
+                 "open-source web browser developed for Windows, OS X, and Linux,\n"
+                 "with a mobile version for Android, by the Mozilla Foundation and\n"
+                 "its subsidiary, the Mozilla Corporation. Firefox uses the Gecko\n"
+                 "layout engine to render web pages, which implements current and\n"
+                 "anticipated web standards.  Enable this option to install Firefox\n"
+                 "instead of Chromium"),
+    'firefox-developer-edition': _("The Firefox Developer Edition is a modified version of Firefox\n"
+                                   "that is specifically designed for web developers. It also uses\n"
+                                   "a separate profile from the regular version so that running\n"
+                                   "them side-by-side is an option."),
+    'google-chrome': _("Chrome is a free Internet browser officially released by Google\n"
+                       "on December 11, 2008. Its features include synchronization with\n"
+                       "Google services and accounts, tabbed browsing, and automatic\n"
+                       "translation and spell check of web pages. It also features an\n"
+                       "integrated address bar/search bar, called the omnibox."),
+    'opera': _("Opera is a freeware, cross-platform web browser developed by\n"
+              "Opera.Inc. The browser is aimed at conventional internet users\n"
+              "and those who enjoy simplicity"),
+    'vivaldi': _("Vivaldi is a freeware, cross-platform web browser developed by\n"
+                 "Vivaldi Technologies. It was officially launched on April 12, 2016.\n"
+                 "The browser is aimed at staunch technologists, heavy Internet users,\n"
+                 "and previous Opera web browser users disgruntled by Opera's transition\n"
+                 "from the Presto layout engine to the Blink layout engine, which\n"
+                 "removed many popular features."),
+#    'firewall': _("Ufw stands for Uncomplicated Firewall, and is a program for\n"
+#                  "managing a netfilter firewall. It provides a command line\n"
+#                  "interface and aims to be uncomplicated and easy to use."),
+    'fonts': _("Fonts: adobe-source-code-pro, adobe-source-sans-pro, jsmath, lohit\n"
+               "oldstand, openarch, otf-bitter, otf-goudy, andika, anonymous-pro\n"
+               "cantarell, cardo, chromeos-fonts, comfortaa, droid, google-fonts\n"
+               "google-webfonts, inconsolata, kimberly_geswein_print, lekton\n"
+               "medievalsharp, nova, oldstandard, opensans, oxygen, pt-mono\n"
+               "pt-sans, roboto, sil-fonts, sortsmillgoudy, source-code-pro\n"
+               "source-sans-pro, ubuntu-font-family, vollkorn, fira-mono\n"
+               "fira-sans and lato."),
+    'firewire': _("NOTE: IF YOU ARE UNSURE EXACTLY OF WHAT FIREWIRE IS, IT IS ADVISED TO\n"
+                  "NOT ENABLE THIS FEATURE.\n"
+                  "That said, firewire is an alternative introduced by Apple to USB devices\n"
+                  "that offers a much faster data exchange rate. It is often used in cameras\n"
+                  "and other small devices."),
+    'games': _("Steam is one of the most popular gaming clients that supports\n"
+               "linux in technology and gaming, while PlayOnLinux\n"
+               "is a very easy manager to setting up games to play\n"
+               "through wine, instead of doing it manually."),
+    'graphic_drivers': _("Installs AMD or Nvidia proprietary graphics driver instead\n"
+                         "of the open-source variant. Do NOT install this if you have a\n"
+                         "Nvidia Optimus laptop"),
+    'gtk-play': _("Popular games for Linux, ranging from complex games like 0 A.D,\n"
+                  "Battle for Wesnoth, and Super Tux to basics like Solitaire,\n"
+                  "Mines, and Soduku - all tailored for a gtk environemt"),
+    'hardinfo': _("Simple application for hardware analysis and system benchmarking.\n"
+                  "Through this, you can easily view all of your system specs without\n"
+                  "having to revert to the commandline."),
+    'hunspell': _("Blah blah blah... honestly not too much to say for this one. Did I mention it's a spell checker?"),
+    'qt-play': _("Popular games for Linux, ranging from complex games like 0 A.D,\n"
+                 "Battle for Wesnoth, and Super Tux to basics like Solitaire,\n"
+                 "Mines, and Soduku - all tailored for a qt environemt"),
+    'maintenance': _("This option install some common applications used for\n"
+                     "system maintenance in Linux.\n"
+                     "Specificaly, this option installs Bleachbit, Stacer, Timeshift, and RebornOS Recovery.\n"
+                     "BleachBit is a free and open-source disk space cleaner, privacy manager,\n"
+                     "and computer system optimizer. Whereas Bleachbit and Stacer both primarily clean your system,\n"
+                     "Timeshift and RebornOS Recovery are preventative solutions to sudden losses\n"
+                     "of data. Timeshift enables you to easily backup your data locally\n"
+                     "while RebornOS Recovery allows you to save a list of all your installed\n"
+                     "programs in a file so that you can reinstall them later."),
+    'movie': _("Common video editing programs for Linux, such as Open Shot, KdenLive,\n"
+               "Pitivi, and Avidemux"),
+    'graphics': _("Common Photo editing Programs for Linux, such as Gimp, GtThumb,\n"
+                  "Rapid Photo Downloader, Rawtherapee, and DarkTable"),
+    'lamp': _("This option installs a web server (you can choose\n"
+              "Apache or Nginx) plus a database server (Mariadb)\n"
+              "and PHP."),
+    'lts': _("The linux-lts package is an alternative Arch kernel package.\n"
+             "This particular kernel version enjoys long-term support from upstream,\n"
+             "including security fixes and some feature backports. Additionally, this\n"
+             "package includes ext4 support. For RebornOS users seeking a long-term\n"
+             "support kernel, or who want a fallback kernel in case the latest kernel\n"
+             "version causes problems, this option is the answer."),
+    'freeoffice': _("FreeOffice is a full-featured Office suite with word processing,\n"
+                    "spreadsheet and presentation software. It is seamlessly compatible\n"
+                    "with Microsoft Office and available for Windows, Mac and Linux.\n"
+                    "Best of all, it's completely free for both personal and commercial use."),
+    'wps-office': _("WPS Office is the free power-packed Office Suite made to make\n"
+                      "even those most used to Microsoft Office feel at home. Looking nearly\n"
+                      "identical to Microsoft Office, this productivity suite is great for everyone"),
+    'libreoffice': _("LibreOffice is the free power-packed Open Source\n"
+                "personal productivity suite for Windows, Macintosh\n"
+                "and Linux, that gives you six feature-rich applications\n"
+                "for all your document production and data processing\n"
+                "needs: Writer, Calc, Impress, Draw, Math and Base."),
+    'power': _("Two programs are installed through this, namely TLP and Thermald.\n"
+               "TLP will automatically adjust your laptop to optimize your battery\n"
+               "performance in the background without interfering with your daily use at all,\n"
+               "and Thermald will conveniently ensure that your fans and CPU both remain\n"
+               "at acceptable levels"),
+#    'plymouth': _("Dislike commands and status reports flowing across your screen while booting up?\n"
+#                             "Want your computer to have an extra bit of eye-candy? Just enable this option,\n"
+#                             "which will configure Plymouth - the standard boot screen program for Linux - for you"),
+    'redshift': _("Redshift is an application that adjusts the computer display's color temperature\n"
+                          "based upon the time of day - with absolutely no manual intervention needed after\n"
+                          "the initial setup."),
+    'sshd': _("Most usage of SMB involves computers running Microsoft Windows.\n"
+             "Use this option to be able to browse SMB shares from your computer."),
+#    'skype': _("Skype is a user-friendly video chat tool made by Microsoft for all ages.\n"
+#               "While it's Linux support often drags behind the latest version,\n"
+#               "it is still widely popular and well-known, allowing you to converse\n"
+#               "with the ones you love"),
+    'spotify': _("Spotify is a widely popular music, podcast, and video streaming service.\n"
+                 "It offers millions of songs and sound tracks, all available for free.\n"
+                 "However, a paid subscription is required to download the songs and listen to them\n"
+                 "offline. (Installed from external source to avoid copywrite issues)"),
+    'visual': _("Compton is a lightweight, standalone composite manager,\n"
+                "suitable for use with window managers that do not natively\n"
+                "provide compositing functionality. Compton itself is a fork\n"
+                "of xcompmgr-dana, which in turn is a fork of xcompmgr.\n"
+                "See the compton github page for further information."),
+    'vlc': _("VLC is often considered the ultimate media player, no matter\n"
+             "what system you use. It is highly versitile, and can play almost any\n"
+             "media format imaginable, even damaged ones. If you ever have a problem\n"
+             "with videos or music, VLC can likely solve it."),
+    'nemo': _("NOTE: IF YOU ARE UNSURE EXACTLY OF WHAT NEMO IS, IT IS ADVISED TO\n"
+              "NOT ENABLE THIS FEATURE.\n"
+              "That said, Nemo is the default file manager for the Cinnamon desktop.\n"
+              "It is praised for it's features, but does not compare to Konquerer,\n"
+              "the KDE file manager."),
+    'qownnotes': _("QOwnNotes is the open source (GPL) plain-text file markdown note\n"
+                 "taking application for GNU/Linux, Mac OS X and Windows by Patrizio\n"
+                 "Bekerle (pbek on GitHub and IRC), that (optionally) works together\n"
+                 "with the notes application of ownCloud or Nextcloud."),
+    'wallpapers': _("Installs a program known as Variety, an easy way to automatically\n"
+                    "change your wallpaper from thousands of high-quality, free images each day."),
+    'wine': _("Easily run several Windows programs on Linux - safely. Wine offers a simple,\n"
+              "non-technical method of installing Windows software. Simply download the desired\n"
+              ".exe file like you normally would for Windows, and then right click to run using\n"
+              "Wine. It's as easy as that.\n"
+              "In addition, this comes with PlayOnLinux and Lutris both installed for you to make even\n"
+              "the more troublesome games possible on Linux.\n"
+              "NOTE: NOT ALL PROGRAMS WILL WORK WITH WINE, ALTHOUGH THE MAJORITY DO"),
+    'nautilus': _("NOTE: IF YOU ARE UNSURE EXACTLY OF WHAT NAUTILUS IS, IT IS ADVISED TO\n"
+              "NOT ENABLE THIS FEATURE.\n"
+              "That said, Nautilus is the default file manager for the Gnome desktop.\n"
+              "It is praised for it's ease of use, and currently has a few more\n"
+              "features than Deepin's file manager.")}
+
+# Delete previous _() dummy declaration
+del _
diff --git a/Cnchi/geoip.py b/Cnchi/geoip.py
new file mode 100644 (file)
index 0000000..8fc2959
--- /dev/null
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  geoip.py
+#
+# Copyright © 2013-2019 RebornOS
+#
+# This file is part of Cnchi.
+#
+# Cnchi 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.
+#
+# Cnchi 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.
+#
+# The following additional terms are in effect as per Section 7 of the license:
+#
+# The preservation of all legal notices and author attributions in
+# the material or in the Appropriate Legal Notices displayed
+# by works containing it is required.
+#
+# You should have received a copy of the GNU General Public License
+# along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+""" GeoIP Location module
+    Needs python-geoip2 python-maxminddb geoip2-database """
+
+import json
+import logging
+import os
+import time
+import requests
+
+import maxminddb
+import geoip2.database
+import misc.extra as misc
+
+class GeoIP():
+    """ Store GeoIP information """
+
+    REPO_CITY_DATABASE = '/usr/share/GeoIP/GeoLite2-City.mmdb'
+    LOCAL_CITY_DATABASE = '/usr/share/cnchi/data/GeoLite2-City.mmdb'
+
+    SERVERS = ["ipapi.com", "ipgeolocation.io"]
+
+    def __init__(self):
+        self.record = None
+        self._maybe_wait_for_network()
+        self._load_data_and_ip()
+
+    @staticmethod
+    def _maybe_wait_for_network():
+        # Wait until there is an Internet connection available
+        if not misc.has_connection():
+            logging.warning(
+                "Can't get network status. Cnchi will try again in a moment")
+            while not misc.has_connection():
+                time.sleep(4)  # Wait 4 seconds and try again
+
+        logging.debug("A working network connection has been detected.")
+
+    def _load_data_and_ip(self):
+        """ Gets public IP and loads GeoIP2 database """
+        db_path = GeoIP.REPO_CITY_DATABASE
+        if not os.path.exists(db_path):
+            db_path = GeoIP.LOCAL_CITY_DATABASE
+
+        if os.path.exists(db_path):
+            myip = self._get_external_ip()
+            logging.debug("Your external IP address is: %s", myip)
+            if myip:
+                self._load_database(db_path, myip)
+                if self.record:
+                    logging.debug("GeoIP database loaded (%s)", db_path)
+            else:
+                logging.error("Cannot get your external IP address!")
+        else:
+            logging.error("Cannot find Cities GeoIP database")
+
+
+    @staticmethod
+    def _get_external_ip():
+        """ Get external IP """
+        for srv in GeoIP.SERVERS:
+            srv = "http://" + srv[::-1]
+            try:
+                json_text = requests.get(srv).text
+                if not "503 Over Quota" in json_text:
+                    data = json.loads(json_text)
+                    return data['ip']
+            except (requests.ConnectionError, json.decoder.JSONDecodeError) as err:
+                logging.warning(
+                    "Error getting external IP from %s: %s", srv, err)
+        return None
+
+    def _load_database(self, db_path, myip):
+        """ Loads cities database """
+        try:
+            reader = geoip2.database.Reader(db_path)
+            self.record = reader.city(myip)
+        except maxminddb.errors.InvalidDatabaseError as err:
+            logging.error(err)
+
+    def get_city(self):
+        """ Returns city information
+            'city': {'geoname_id', 'names'} """
+        if self.record:
+            return self.record.city
+        return None
+
+    def get_country(self):
+        """ Returns country information
+            'country': {'geoname_id', 'is_in_european_union', 'iso_code', 'names'} """
+        if self.record:
+            return self.record.country
+        return None
+
+    def get_continent(self):
+        """ Returns continent information
+            'continent': {'code', 'geoname_id', 'names'} """
+        if self.record:
+            return self.record.continent
+        return None
+
+    def get_location(self):
+        """ Returns location information
+            'location': {'accuracy_radius', 'latitude', 'longitude', 'time_zone'} """
+        if self.record:
+            return self.record.location
+        return None
+
+
+
+def test_module():
+    """ Test module """
+    geo = GeoIP()
+    print("City:", geo.get_city())
+    print("Country:", geo.get_country())
+    print("Continent:", geo.get_continent())
+    print("Location:", geo.get_location())
+
+
+if __name__ == "__main__":
+    test_module()
diff --git a/Cnchi/grub2.py b/Cnchi/grub2.py
new file mode 100644 (file)
index 0000000..8018705
--- /dev/null
@@ -0,0 +1,472 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# grub2.py
+#
+# Copyright © 2013-2019 RebornOS
+#
+# This file is part of Cnchi.
+#
+# Cnchi 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.
+#
+# Cnchi 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.
+#
+# The following additional terms are in effect as per Section 7 of the license:
+#
+# The preservation of all legal notices and author attributions in
+# the material or in the Appropriate Legal Notices displayed
+# by works containing it is required.
+#
+# You should have received a copy of the GNU General Public License
+# along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+
+""" GRUB2 bootloader installation """
+
+import logging
+import os
+import shutil
+import subprocess
+import re
+import threading
+import time
+
+try:
+    import parted3.fs_module as fs
+    from installation import special_dirs
+    from misc.run_cmd import call, chroot_call
+    from misc.extra import random_generator
+except ImportError:
+    pass
+
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+
+class Grub2(object):
+    """ Class to perform boot loader installation """
+
+    def __init__(self, dest_dir, settings, uuids):
+        self.dest_dir = dest_dir
+        self.settings = settings
+        self.uuids = uuids
+
+    def install(self):
+        """ Install Grub2 bootloader """
+        self.modify_grub_default()
+        self.prepare_grub_d()
+
+        if os.path.exists('/sys/firmware/efi'):
+            logging.debug("Cnchi will install the Grub2 (efi) loader")
+            self.install_efi()
+        else:
+            logging.debug("Cnchi will install the Grub2 (bios) loader")
+            self.install_bios()
+
+        self.check_root_uuid_in_grub()
+
+    def check_root_uuid_in_grub(self):
+        """ Checks grub.cfg for correct root UUID """
+        if self.settings.get("zfs"):
+            # No root uuid checking if using zfs
+            return
+
+        if "/" not in self.uuids:
+            logging.warning(
+                "Root uuid variable is not set. I can't check root UUID"
+                "in grub.cfg, let's hope it's ok")
+            return
+
+        ruuid_str = 'root=UUID={0}'.format(self.uuids["/"])
+
+        cmdline_linux = self.settings.get('GRUB_CMDLINE_LINUX')
+        if cmdline_linux is None:
+            cmdline_linux = ""
+
+        cmdline_linux_default = self.settings.get('GRUB_CMDLINE_LINUX_DEFAULT')
+        if cmdline_linux_default is None:
+            cmdline_linux_default = ""
+
+        boot_command = 'linux /vmlinuz-linux {0} {1} {2}\n'.format(
+            ruuid_str,
+            cmdline_linux,
+            cmdline_linux_default)
+
+        pattern = re.compile(
+            "menuentry 'RebornOS'[\s\S]*initramfs-linux.img\n}")
+
+        cfg = os.path.join(self.dest_dir, "boot/grub/grub.cfg")
+        with open(cfg) as grub_file:
+            parse = grub_file.read()
+
+        if not self.settings.get('use_luks') and ruuid_str not in parse:
+            entry = pattern.search(parse)
+            if entry:
+                logging.debug(
+                    "Wrong uuid in grub.cfg, Cnchi will try to fix it.")
+                new_entry = re.sub(
+                    "linux\t/vmlinuz.*quiet\n",
+                    boot_command,
+                    entry.group())
+                parse = parse.replace(entry.group(), new_entry)
+
+                with open(cfg, 'w') as grub_file:
+                    grub_file.write(parse)
+
+    def modify_grub_default(self):
+        """ If using LUKS as root, we need to modify GRUB_CMDLINE_LINUX
+            GRUB_CMDLINE_LINUX : Command-line arguments to add to menu entries
+            for the Linux kernel.
+            GRUB_CMDLINE_LINUX_DEFAULT : Unless ‘GRUB_DISABLE_RECOVERY’ is set
+            to ‘true’, two menu entries will be generated for each Linux kernel:
+            one default entry and one entry for recovery mode. This option lists
+            command-line arguments to add only to the default menu entry, after
+            those listed in ‘GRUB_CMDLINE_LINUX’. """
+
+        plymouth_bin = os.path.join(self.dest_dir, "usr/bin/plymouth")
+        cmd_linux_default = "quiet"
+        cmd_linux = ""
+
+        # https://www.kernel.org/doc/Documentation/kernel-parameters.txt
+        # cmd_linux_default : quiet splash resume=UUID=ABC zfs=ABC
+
+        if os.path.exists(plymouth_bin):
+            cmd_linux_default += " splash"
+
+        # resume does not work in zfs (or so it seems)
+        if "swap" in self.uuids and not self.settings.get("zfs"):
+            cmd_linux_default += " resume=UUID={0}".format(self.uuids["swap"])
+
+        if self.settings.get("zfs"):
+            zfs_pool_name = self.settings.get("zfs_pool_name")
+            cmd_linux += " zfs={0}".format(zfs_pool_name)
+
+        if self.settings.get('use_luks'):
+            # When using separate boot partition,
+            # add GRUB_ENABLE_CRYPTODISK to grub.cfg
+            if self.uuids["/"] != self.uuids["/boot"]:
+                self.set_grub_option("GRUB_ENABLE_CRYPTODISK", "y")
+
+            # Let GRUB automatically add the kernel parameters for
+            # root encryption
+            luks_root_volume = self.settings.get('luks_root_volume')
+            logging.debug("Luks Root Volume: %s", luks_root_volume)
+
+            if (self.settings.get("partition_mode") == "advanced" and
+                    self.settings.get('use_luks_in_root')):
+                # In advanced, if using luks in root device,
+                # we store root device it in luks_root_device var
+                root_device = self.settings.get('luks_root_device')
+                self.uuids["/"] = fs.get_uuid(root_device)
+
+            cmd_linux += " cryptdevice=/dev/disk/by-uuid/{0}:{1}".format(
+                self.uuids["/"],
+                luks_root_volume)
+
+            if self.settings.get("luks_root_password") == "":
+                # No luks password, so user wants to use a keyfile
+                cmd_linux += " cryptkey=/dev/disk/by-uuid/{0}:ext2:/.keyfile-root".format(
+                    self.uuids["/boot"])
+
+        # Remove leading/ending spaces
+        cmd_linux_default = cmd_linux_default.strip()
+        cmd_linux = cmd_linux.strip()
+
+        # Modify /etc/default/grub
+        self.set_grub_option(
+            "GRUB_THEME", "/boot/grub/themes/Vimix/theme.txt")
+        self.set_grub_option("GRUB_DISTRIBUTOR", "RebornOS")
+        self.set_grub_option("GRUB_CMDLINE_LINUX_DEFAULT", cmd_linux_default)
+        self.set_grub_option("GRUB_CMDLINE_LINUX", cmd_linux)
+
+        # Also store grub line in settings, we'll use it later in check_root_uuid_in_grub()
+        try:
+            self.settings.set('GRUB_CMDLINE_LINUX', cmd_linux)
+        except AttributeError:
+            pass
+
+        logging.debug("Grub configuration completed successfully.")
+
+    def set_grub_option(self, option, cmd):
+        """ Changes a grub setup option in /etc/default/grub """
+        try:
+            default_grub_path = os.path.join(
+                self.dest_dir, "etc/default", "grub")
+            default_grub_lines = []
+
+            with open(default_grub_path, 'r', newline='\n') as grub_file:
+                default_grub_lines = [x for x in grub_file.readlines()]
+
+            with open(default_grub_path, 'w', newline='\n') as grub_file:
+                param_in_file = False
+                param_to_look_for = option + '='
+                for line in default_grub_lines:
+                    if param_to_look_for in line:
+                        # Option was already in file, update it
+                        line = '{0}="{1}"\n'.format(option, cmd)
+                        param_in_file = True
+                    grub_file.write(line)
+
+                if not param_in_file:
+                    # Option was not found. Thus, append new option
+                    grub_file.write('\n{0}="{1}"\n'.format(option, cmd))
+
+            logging.debug('Set %s="%s" in /etc/default/grub', option, cmd)
+        except FileNotFoundError as ex:
+            logging.error(ex)
+        except Exception as ex:
+            tpl1 = "Can't modify {0}".format(default_grub_path)
+            tpl2 = "An exception of type {0} occured. Arguments:\n{1!r}"
+            template = '{0} {1}'.format(tpl1, tpl2)
+            message = template.format(type(ex).__name__, ex.args)
+            logging.error(message)
+
+    def prepare_grub_d(self):
+        """ Copies 10_antergos script into /etc/grub.d/ """
+        grub_d_dir = os.path.join(self.dest_dir, "etc/grub.d")
+        script_dir = os.path.join(self.settings.get("cnchi"), "scripts")
+        script = "10_antergos"
+
+        os.makedirs(grub_d_dir, mode=0o755, exist_ok=True)
+
+        script_path = os.path.join(script_dir, script)
+        if os.path.exists(script_path):
+            try:
+                shutil.copy2(script_path, grub_d_dir)
+                os.chmod(os.path.join(grub_d_dir, script), 0o755)
+            except FileNotFoundError:
+                logging.debug("Could not copy %s to grub.d", script)
+            except FileExistsError:
+                pass
+        else:
+            logging.warning("Can't find script %s", script_path)
+
+    def grub_ripper(self):
+        while True:
+            time.sleep(10)
+            try:
+                ret = subprocess.check_output(
+                    ['pidof', 'grub-mount']).decode().strip()
+                if ret:
+                    subprocess.check_output(['kill', '-9', ret.split()[0]])
+                else:
+                    break
+            except subprocess.CalledProcessError as err:
+                logging.warning("Error running %s: %s", err.cmd, err.output)
+                break
+
+    def run_mkconfig(self):
+        """ Create grub.cfg file using grub-mkconfig """
+        logging.debug("Generating grub.cfg...")
+
+        # Make sure that /dev and others are mounted (binded).
+        special_dirs.mount(self.dest_dir)
+
+        # Hack to kill grub-mount hanging
+        threading.Thread(target=self.grub_ripper).start()
+
+        # Add -l option to os-prober's umount call so that it does not hang
+        self.apply_osprober_patch()
+        logging.debug("Running grub-mkconfig...")
+        locale = self.settings.get("locale")
+        cmd = 'LANG={0} grub-mkconfig -o /boot/grub/grub.cfg'.format(locale)
+        cmd_sh = ['sh', '-c', cmd]
+        if not chroot_call(cmd_sh, self.dest_dir, timeout=300):
+            msg = ("grub-mkconfig does not respond. Killing grub-mount and"
+                   "os-prober so we can continue.")
+            logging.error(msg)
+            call(['killall', 'grub-mount'])
+            call(['killall', 'os-prober'])
+
+    def install_bios(self):
+        """ Install Grub2 bootloader in a BIOS system """
+        grub_location = self.settings.get('bootloader_device')
+        txt = _("Installing GRUB(2) BIOS boot loader in {0}").format(
+            grub_location)
+        logging.info(txt)
+
+        # /dev and others need to be mounted (binded).
+        # We call mount_special_dirs here just to be sure
+        special_dirs.mount(self.dest_dir)
+
+        grub_install = ['grub-install',
+                        '--directory=/usr/lib/grub/i386-pc',
+                        '--target=i386-pc',
+                        '--boot-directory=/boot',
+                        '--recheck']
+
+        # Use --force when installing in /dev/sdXY or in /dev/mmcblk
+        if len(grub_location) > len("/dev/sdX"):
+            grub_install.append("--force")
+
+        grub_install.append(grub_location)
+
+        chroot_call(grub_install, self.dest_dir)
+
+        self.install_locales()
+
+        self.run_mkconfig()
+
+        grub_cfg_path = os.path.join(self.dest_dir, "boot/grub/grub.cfg")
+        with open(grub_cfg_path) as grub_cfg:
+            if "Antergos" in grub_cfg.read():
+                txt = _("GRUB(2) BIOS has been successfully installed.")
+                logging.info(txt)
+                self.settings.set('bootloader_installation_successful', True)
+            else:
+                txt = _("ERROR installing GRUB(2) BIOS.")
+                logging.warning(txt)
+                self.settings.set('bootloader_installation_successful', False)
+
+    def install_efi(self):
+        """ Install Grub2 bootloader in a UEFI system """
+        uefi_arch = "x86_64"
+        spec_uefi_arch = "x64"
+        spec_uefi_arch_caps = "X64"
+        fpath = '/install/boot/efi/EFI/RebornOS'
+        bootloader_id = 'RebornOS' if not os.path.exists(fpath) else \
+            'RebornOS_{0}'.format(random_generator())
+
+        # grub2 in efi needs efibootmgr
+        if not os.path.exists("/usr/bin/efibootmgr"):
+            txt = _(
+                "Please install efibootmgr package to install Grub2 for x86_64-efi platform.")
+            logging.warning(txt)
+            txt = _("GRUB(2) will NOT be installed")
+            logging.warning(txt)
+            self.settings.set('bootloader_installation_successful', False)
+            return
+
+        txt = _("Installing GRUB(2) UEFI {0} boot loader").format(uefi_arch)
+        logging.info(txt)
+
+        grub_install = [
+            'grub-install',
+            '--target={0}-efi'.format(uefi_arch),
+            '--efi-directory=/install/boot/efi',
+            '--bootloader-id={0}'.format(bootloader_id),
+            '--boot-directory=/install/boot',
+            '--recheck']
+        load_module = ['modprobe', '-a', 'efivarfs']
+
+        call(load_module, timeout=15)
+        call(grub_install, timeout=120)
+
+        self.install_locales()
+
+        # Copy grub into dirs known to be used as default by some OEMs
+        # if they do not exist yet.
+        grub_defaults = [
+            os.path.join(
+                self.dest_dir,
+                "boot/efi/EFI/BOOT",
+                "BOOT{0}.efi".format(spec_uefi_arch_caps)),
+            os.path.join(
+                self.dest_dir,
+                "boot/efi/EFI/Microsoft/Boot",
+                "bootmgfw.efi")]
+
+        grub_path = os.path.join(
+            self.dest_dir,
+            "boot/efi/EFI/antergos_grub",
+            "grub{0}.efi".format(spec_uefi_arch))
+
+        for grub_default in grub_defaults:
+            path = grub_default.split()[0]
+            if not os.path.exists(path):
+                msg = _("No OEM loader found in %s. Copying Grub(2) into dir.")
+                logging.info(msg, path)
+                os.makedirs(path, mode=0o755)
+                msg_failed = _("Copying Grub(2) into OEM dir failed: %s")
+                try:
+                    shutil.copy(grub_path, grub_default)
+                except FileNotFoundError:
+                    logging.warning(msg_failed, _("File not found."))
+                except FileExistsError:
+                    logging.warning(msg_failed, _("File already exists."))
+                except Exception as ex:
+                    template = "An exception of type {0} occured. Arguments:\n{1!r}"
+                    message = template.format(type(ex).__name__, ex.args)
+                    logging.error(message)
+
+        self.run_mkconfig()
+
+        paths = [
+            os.path.join(self.dest_dir, "boot/grub/x86_64-efi/core.efi"),
+            os.path.join(
+                self.dest_dir,
+                "boot/efi/EFI/{0}".format(bootloader_id),
+                "grub{0}.efi".format(spec_uefi_arch))]
+
+        exists = True
+        for path in paths:
+            if not os.path.exists(path):
+                exists = False
+                logging.debug("Path '%s' doesn't exist, when it should", path)
+
+        if exists:
+            logging.info("GRUB(2) UEFI install completed successfully")
+            self.settings.set('bootloader_installation_successful', True)
+        else:
+            logging.warning(
+                "GRUB(2) UEFI install may not have completed successfully.")
+            self.settings.set('bootloader_installation_successful', False)
+
+    def apply_osprober_patch(self):
+        """ Adds -l option to os-prober's umount call so that it does not hang """
+        osp_path = os.path.join(
+            self.dest_dir,
+            "usr/lib/os-probes/50mounted-tests")
+        if os.path.exists(osp_path):
+            with open(osp_path) as osp:
+                text = osp.read().replace("umount", "umount -l")
+            with open(osp_path, 'w') as osp:
+                osp.write(text)
+            logging.debug("50mounted-tests file patched successfully")
+        else:
+            logging.warning("Failed to patch 50mounted-tests, file not found.")
+
+    def install_locales(self):
+        """ Install Grub2 locales """
+        logging.debug("Installing Grub2 locales.")
+        dest_locale_dir = os.path.join(self.dest_dir, "boot/grub/locale")
+
+        os.makedirs(dest_locale_dir, mode=0o755, exist_ok=True)
+
+        grub_mo = os.path.join(
+            self.dest_dir,
+            "usr/share/locale/en@quot/LC_MESSAGES/grub.mo")
+
+        try:
+            shutil.copy2(grub_mo, os.path.join(dest_locale_dir, "en.mo"))
+        except FileNotFoundError:
+            logging.warning("Can't install GRUB(2) locale.")
+        except FileExistsError:
+            # Ignore if already exists
+            pass
+
+
+if __name__ == '__main__':
+    os.makedirs("/install/etc/default", mode=0o755, exist_ok=True)
+    shutil.copy2("/etc/default/grub", "/install/etc/default/grub")
+    dest_dir = "/install"
+    settings = {}
+    settings["zfs"] = True
+    settings["zfs_pool_name"] = "Reborn_d3sq"
+    settings["use_luks"] = True
+    uuids = {}
+    uuids["/"] = "ABCD"
+    uuids["/boot"] = "ZXCV"
+    grub2 = Grub2(dest_dir, settings, uuids)
+    grub2.modify_grub_default()
diff --git a/Cnchi/gufw.desktop b/Cnchi/gufw.desktop
new file mode 100644 (file)
index 0000000..eefbedc
--- /dev/null
@@ -0,0 +1,101 @@
+[Desktop Entry]
+Version=1.0
+Name=Firewall Configuration
+Name[ar]=إعدادات الجدار الناري
+Name[ast]=Configuración del tornafueos
+Name[bg]=Конфигуриране на защитна стена
+Name[ca]=Configuració del tallafoc
+Name[cs]=Nastavení firewallu
+Name[da]=Firewallkonfiguration
+Name[de]=Firewall-Konfiguration
+Name[el]=Ρύθμιση τείχους προστασίας
+Name[en_AU]=Firewall Configuration
+Name[en_GB]=Firewall Configuration
+Name[eo]=Fajroŝirmilaj agordoj
+Name[es]=Configuración del cortafuegos
+Name[et]=Tulemüüri seadistus
+Name[fi]=Palomuurin asetukset
+Name[fr]=Configuration du pare-feu
+Name[gl]=Configuración da devasa
+Name[hr]=Postavke vatrozida
+Name[hrx]=Fajř-vannt-ʃtelluŋŋ
+Name[hu]=Tűzfalbeállítás
+Name[id]=Konfigurasi firewall
+Name[is]=Stillingar eldveggs
+Name[it]=Configurazione del firewall
+Name[ja]=ファイアウォール設定ツール
+Name[ko]=방화벽 설정
+Name[ku]=Veavakirina Dîwarê Ewlehiyê
+Name[lt]=Užkardos konfigūravimas
+Name[lv]=Ugunsmūra konfigurācija
+Name[ms]=Konfigurasi Dinding Api
+Name[nb]=Brannmur-oppsett
+Name[nl]=Firewall-configuratie
+Name[oc]=Configuracion del parafuòc
+Name[pl]=Konfiguracja zapory sieciowej
+Name[pt]=Configuração da firewall
+Name[pt_BR]=Configuração do Firewall
+Name[ro]=Configurare Firewall
+Name[ru]=Настройка межсетевого экрана
+Name[si]=ගිනිපවුරු සැකසුම්
+Name[sk]=Nastavenia brány firewall
+Name[sl]=Nastavitev požarnega zidu
+Name[sq]=Konfigurimi i Murit mbrojtës
+Name[sr]=Подешавање мрежне баријере
+Name[sv]=Brandväggs-konfiguration
+Name[ta]=தீச்சுவர் அமைப்பு
+Name[tr]=Güvenlik Duvarı Yapılandırması
+Name[ug]=تور توسۇق سەپلەش
+Name[uk]=Налаштування брандмауера
+Name[vi]=Cấu hình tường lửa
+Name[zh_CN]=防火墙配置
+Name[zh_TW]=防火牆設定
+Comment=An easy way to configure your firewall
+Comment[ast]=Un mou cenciellu de configurar el to tornafueos
+Comment[bg]=Най-лесният начин да конфигурирате вашата защитна стена
+Comment[ca]=Una forma senzilla per configurar el vostre tallafoc
+Comment[cs]=Jednoduchá konfigurace firewallu
+Comment[da]=En nem måde at konfigurere din firewall på
+Comment[de]=Eine einfache Möglichkeit Ihre Firewall zu konfigurieren
+Comment[en_GB]=An easy way to configure your firewall
+Comment[es]=Una manera sencilla de configurar su cortafuegos
+Comment[et]=Lihtne moodus seadistamaks tulemüüri
+Comment[fi]=Hallitse palomuuria vaivatta
+Comment[fr]=Une façon simple de configurer votre pare-feu
+Comment[gl]=Unha forma doada de configurar a devasa
+Comment[hr]=Jednostavan način za podešavanje vašeg vatrozida
+Comment[hu]=Egyszerű módszer a tűzfala beállítására
+Comment[is]=Einföld leið til að stilla eldvegginn þinn
+Comment[it]=Un modo semplice per configurare il firewall
+Comment[ja]=ファイアウォールの設定を簡単に行えます
+Comment[ko]=당신의 방화벽을 쉽게 설정하는 방법입니다
+Comment[ku]=Rêyek hêsanî jibo  veavakirina dîwarê te
+Comment[lt]=Lengvas būdas konfigūruoti užkardą
+Comment[ms]=Merupakan cara mudah mengkonfigur dinding api anda
+Comment[nb]=En enkel måte å sette opp brannmur på
+Comment[nl]=Een eenvoudige manier om uw firewall in te stellen
+Comment[oc]=Un biais simple de configurar vòstre parafuòc
+Comment[pl]=Prosty sposób na skonfigurowanie zapory sieciowej
+Comment[pt]=Uma forma fácil de configurar a sua firewall
+Comment[pt_BR]=Um jeito fácil de configurar seu firewall
+Comment[ro]=O cale mai ușoară pentru configurarea firewall-ului
+Comment[ru]=Простой способ настроить ваш межсетевой экран
+Comment[si]=ඔබේ ගිනිපවුර වෙනස්කිරීමට පහසු මගක්
+Comment[sk]=Jednoduchý spôsob nastavenia vašej brány firewall
+Comment[sl]=Enostaven način nastavitve vašega požarnega zidu
+Comment[sr]=Лак начин за подешавање мрежне баријере
+Comment[sv]=Ett lätt sätt att konfigurera din brandvägg
+Comment[tr]=Güvenlik duvarınızı yapılandırmanın kolay bir yolu
+Comment[uk]=Простий спосіб налаштувати ваш брандмауер
+Comment[vi]=Một cách dễ dàng để cấu hình tường lửa của bạn
+Comment[zh_CN]=配置防火墙的简单方法
+Comment[zh_TW]=簡單設定您的防火牆
+Keywords=gufw;security;firewall;network;
+Categories=GNOME;GTK;Settings;Security;X-GNOME-Settings-Panel;X-GNOME-SystemSettings;X-Unity-Settings-Panel;X-XFCE-SettingsDialog;X-XFCE-SystemSettings;
+Exec=gufw
+Icon=gufw
+Terminal=false
+Type=Application
+X-GNOME-Settings-Panel=gufw
+X-Unity-Settings-Panel=gufw
+X-Ubuntu-Gettext-Domain=gufw
diff --git a/Cnchi/gufw.png b/Cnchi/gufw.png
new file mode 100644 (file)
index 0000000..989b160
Binary files /dev/null and b/Cnchi/gufw.png differ
diff --git a/Cnchi/gufw.svg b/Cnchi/gufw.svg
new file mode 100644 (file)
index 0000000..9f381be
--- /dev/null
@@ -0,0 +1,17 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" version="1">
+ <g fill="#7e97de" transform="matrix(.11222 0 0 .11343 4.2138 4)">
+  <path d="m324.48 51.943-146.49-51.658c-1.076-0.38-2.25-0.38-3.326 0l-146.49 51.658c-1.999 0.705-3.337 2.595-3.337 4.715 0 52.278 13.834 112.71 37.956 165.8 19.566 43.069 54.751 100.52 111.25 129.62 0.719 0.37 1.504 0.555 2.29 0.555s1.571-0.185 2.29-0.555c56.496-29.106 91.68-86.556 111.25-129.62 24.121-53.094 37.955-113.53 37.955-165.8 0-2.12-1.338-4.01-3.337-4.715z"/>
+ </g>
+ <g opacity=".2" transform="matrix(.059409 0 0 .062386 13.525 14.001)">
+  <path d="m324.48 51.943-146.49-51.658c-1.076-0.38-2.25-0.38-3.326 0l-146.49 51.658c-1.999 0.705-3.337 2.595-3.337 4.715 0 52.278 13.834 112.71 37.956 165.8 19.566 43.069 54.751 100.52 111.25 129.62 0.719 0.37 1.504 0.555 2.29 0.555s1.571-0.185 2.29-0.555c56.496-29.106 91.68-86.556 111.25-129.62 24.121-53.094 37.955-113.53 37.955-165.8 0-2.12-1.338-4.01-3.337-4.715z"/>
+ </g>
+ <g fill="#fff" transform="matrix(.059409 0 0 .062386 13.525 13)">
+  <path d="m324.48 51.943-146.49-51.658c-1.076-0.38-2.25-0.38-3.326 0l-146.49 51.658c-1.999 0.705-3.337 2.595-3.337 4.715 0 52.278 13.834 112.71 37.956 165.8 19.566 43.069 54.751 100.52 111.25 129.62 0.719 0.37 1.504 0.555 2.29 0.555s1.571-0.185 2.29-0.555c56.496-29.106 91.68-86.556 111.25-129.62 24.121-53.094 37.955-113.53 37.955-165.8 0-2.12-1.338-4.01-3.337-4.715z"/>
+ </g>
+ <g fill="#5c7bd5" transform="matrix(.11222 0 0 .11343 4.2138 4)">
+  <path fill="#fff" opacity=".2" transform="matrix(8.9114 0 0 8.8161 -37.55 -35.264)" d="m24 4c-0.063 0-0.125 0.0117-0.186 0.0332l-16.439 5.8594c-0.2243 0.0799-0.375 0.2924-0.375 0.5334 0 0.27 0.0209 0.552 0.0273 0.826 0.0528-0.166 0.1795-0.299 0.3477-0.359l16.439-5.8598c0.121-0.0431 0.253-0.0431 0.374 0l16.437 5.8598c0.168 0.06 0.295 0.193 0.348 0.359 0.006-0.274 0.027-0.556 0.027-0.826 0-0.241-0.151-0.4535-0.375-0.5334l-16.437-5.8594c-0.061-0.0215-0.125-0.0332-0.188-0.0332z"/>
+ </g>
+ <g opacity=".2" transform="matrix(.11222 0 0 .11343 4.2138 4.9995)">
+  <path transform="matrix(8.9114,0,0,8.8161,-37.55,-35.264)" d="m7.0273 10.258c-0.0164 0.053-0.0273 0.11-0.0273 0.168 0 5.93 1.5529 12.786 4.26 18.808 2.195 4.886 6.142 11.402 12.482 14.704 0.081 0.041 0.17 0.062 0.258 0.062s0.177-0.021 0.258-0.062c6.34-3.302 10.286-9.818 12.482-14.704 2.707-6.022 4.26-12.878 4.26-18.808 0-0.058-0.011-0.115-0.027-0.168-0.136 5.735-1.65 12.231-4.233 17.976-2.196 4.886-6.142 11.402-12.482 14.704-0.081 0.041-0.17 0.062-0.258 0.062s-0.177-0.021-0.258-0.062c-6.34-3.302-10.287-9.818-12.482-14.704-2.5827-5.745-4.0974-12.241-4.2327-17.976z"/>
+ </g>
+</svg>
diff --git a/Cnchi/hexagon erased.xcf b/Cnchi/hexagon erased.xcf
new file mode 100644 (file)
index 0000000..3640c26
Binary files /dev/null and b/Cnchi/hexagon erased.xcf differ
diff --git a/Cnchi/info.py b/Cnchi/info.py
new file mode 100755 (executable)
index 0000000..c89c9cc
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  info.py
+#
+#  Copyright © 2013-2019 RebornOS
+#
+#  This file is part of Cnchi.
+#
+#  Cnchi 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.
+#
+#  Cnchi 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.
+#
+#  The following additional terms are in effect as per Section 7 of the license:
+#
+#  The preservation of all legal notices and author attributions in
+#  the material or in the Appropriate Legal Notices displayed
+#  by works containing it is required.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+
+""" Set some Cnchi global constants """
+
+CNCHI_VERSION = "RebornOS Mac Installer Gnome based 2020.05.25"
+CNCHI_WEBSITE = "https://rebornos.org"
+CNCHI_RELEASE_STAGE = "production"
+
+if __name__ == '__main__':
+    print(CNCHI_VERSION)
diff --git a/Cnchi/install.py b/Cnchi/install.py
new file mode 100755 (executable)
index 0000000..fa1544a
--- /dev/null
@@ -0,0 +1,524 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# install.py
+#
+# Copyright © 2013-2018 Antergos
+#
+# This file is part of Cnchi.
+#
+# Cnchi 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 2 of the License, or
+# (at your option) any later version.
+#
+# Cnchi 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 Cnchi; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+""" Installation process module. """
+
+import glob
+import logging
+import os
+import shutil
+import sys
+
+from mako.template import Template
+
+from download import download
+
+from installation import special_dirs
+from installation import post_install
+from installation import mount
+
+import misc.extra as misc
+from misc.extra import InstallError
+from misc.run_cmd import call
+from misc.events import Events
+import pacman.pac as pac
+
+import hardware.hardware as hardware
+
+DEST_DIR = "/install"
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+class Installation():
+    """ Installation process thread class """
+
+    TMP_PACMAN_CONF = "/tmp/pacman.conf"
+
+    def __init__(self, settings, callback_queue, packages, metalinks,
+                 mount_devices, fs_devices, ssd=None, blvm=False):
+        """ Initialize installation class """
+
+        self.settings = settings
+        self.events = Events(callback_queue)
+        self.packages = packages
+        self.metalinks = metalinks
+
+        self.method = self.settings.get('partition_mode')
+
+        self.desktop = self.settings.get('desktop').lower()
+
+        # This flag tells us if there is a lvm partition (from advanced install)
+        # If it's true we'll have to add the 'lvm2' hook to mkinitcpio
+        self.blvm = blvm
+
+        if ssd is not None:
+            self.ssd = ssd
+        else:
+            self.ssd = {}
+
+        self.mount_devices = mount_devices
+
+        self.fs_devices = fs_devices
+
+        self.running = True
+        self.error = False
+
+        self.auto_device = ""
+        self.packages = packages
+        self.pacman = None
+
+        self.pacman_cache_dir = ''
+
+        # Cnchi will store here info (packages needed, post install actions, ...)
+        # for the detected hardware
+        self.hardware_install = None
+
+    def queue_fatal_event(self, txt):
+        """ Queues the fatal event and exits process """
+        self.error = True
+        self.running = False
+        self.events.add('error', txt)
+        # self.callback_queue.join()
+        sys.exit(0)
+
+    def mount_partitions(self):
+        """ Do not call this in automatic mode as AutoPartition class mounts
+        the root and boot devices itself. (We call it if using ZFS, though) """
+
+        if os.path.exists(DEST_DIR) and not self.method == "zfs":
+            # If we're recovering from a failed/stoped install, there'll be
+            # some mounted directories. Try to unmount them first.
+            # We use unmount_all_in_directory from auto_partition to do this.
+            # ZFS already mounts everything automagically (except /boot that
+            # is not in zfs)
+            mount.unmount_all_in_directory(DEST_DIR)
+
+        # NOTE: Advanced method formats root by default in advanced.py
+        if "/" in self.mount_devices:
+            root_partition = self.mount_devices["/"]
+        else:
+            root_partition = ""
+
+        # Boot partition
+        if "/boot" in self.mount_devices:
+            boot_partition = self.mount_devices["/boot"]
+        else:
+            boot_partition = ""
+
+        # EFI partition
+        if "/boot/efi" in self.mount_devices:
+            efi_partition = self.mount_devices["/boot/efi"]
+        else:
+            efi_partition = ""
+
+        # Swap partition
+        if "swap" in self.mount_devices:
+            swap_partition = self.mount_devices["swap"]
+        else:
+            swap_partition = ""
+
+        # Mount root partition
+        if self.method == "zfs":
+            # Mount /
+            logging.debug("ZFS: Mounting root")
+            cmd = ["zfs", "mount", "-a"]
+            call(cmd)
+        elif root_partition:
+            txt = "Mounting root partition {0} into {1} directory".format(
+                root_partition, DEST_DIR)
+            logging.debug(txt)
+            cmd = ['mount', root_partition, DEST_DIR]
+            call(cmd, fatal=True)
+
+        # We also mount the boot partition if it's needed
+        boot_path = os.path.join(DEST_DIR, "boot")
+        os.makedirs(boot_path, mode=0o755, exist_ok=True)
+        if boot_partition:
+            txt = _("Mounting boot partition {0} into {1} directory").format(
+                boot_partition, boot_path)
+            logging.debug(txt)
+            cmd = ['mount', boot_partition, boot_path]
+            call(cmd, fatal=True)
+
+        if self.method == "zfs" and efi_partition:
+            # In automatic zfs mode, it could be that we have a specific EFI
+            # partition (different from /boot partition). This happens if using
+            # EFI and grub2 bootloader
+            efi_path = os.path.join(DEST_DIR, "boot", "efi")
+            os.makedirs(efi_path, mode=0o755, exist_ok=True)
+            txt = _("Mounting EFI partition {0} into {1} directory").format(
+                efi_partition, efi_path)
+            logging.debug(txt)
+            cmd = ['mount', efi_partition, efi_path]
+            call(cmd, fatal=True)
+
+        # In advanced mode, mount all partitions (root and boot are already mounted)
+        if self.method == 'advanced':
+            for path in self.mount_devices:
+                if path == "":
+                    # Ignore devices without a mount path
+                    continue
+
+                mount_part = self.mount_devices[path]
+
+                if mount_part not in [root_partition, boot_partition, swap_partition]:
+                    if path[0] == '/':
+                        path = path[1:]
+                    mount_dir = os.path.join(DEST_DIR, path)
+                    try:
+                        os.makedirs(mount_dir, mode=0o755, exist_ok=True)
+                        txt = _("Mounting partition {0} into {1} directory")
+                        txt = txt.format(mount_part, mount_dir)
+                        logging.debug(txt)
+                        cmd = ['mount', mount_part, mount_dir]
+                        call(cmd)
+                    except OSError:
+                        logging.warning(
+                            "Could not create %s directory", mount_dir)
+                elif mount_part == swap_partition:
+                    logging.debug("Activating swap in %s", mount_part)
+                    cmd = ['swapon', swap_partition]
+                    call(cmd)
+
+    @misc.raise_privileges
+    def run(self):
+        """ Run installation """
+
+        # From this point, on a warning situation, Cnchi should try to continue,
+        # so we need to catch the exception here. If we don't catch the exception
+        # here, it will be catched in run() and managed as a fatal error.
+        # On the other hand, if we want to clarify the exception message we can
+        # catch it here and then raise an InstallError exception.
+
+        if not os.path.exists(DEST_DIR):
+            os.makedirs(DEST_DIR, mode=0o755, exist_ok=True)
+
+        # Make sure the antergos-repo-priority package's alpm hook doesn't run.
+        if not os.environ.get('CNCHI_RUNNING', False):
+            os.environ['CNCHI_RUNNING'] = 'True'
+
+        msg = _("Installing using the '{0}' method").format(self.method)
+        self.events.add('info', msg)
+
+        # Mount needed partitions (in automatic it's already done)
+        if self.method in ['alongside', 'advanced', 'zfs']:
+            self.mount_partitions()
+
+        # Nasty workaround:
+        # If pacman was stoped and /var is in another partition than root
+        # (so as to be able to resume install), database lock file will still
+        # be in place. We must delete it or this new installation will fail
+        db_lock = os.path.join(DEST_DIR, "var/lib/pacman/db.lck")
+        if os.path.exists(db_lock):
+            os.remove(db_lock)
+            logging.debug("%s deleted", db_lock)
+
+        # Create some needed folders
+        folders = [
+            os.path.join(DEST_DIR, 'var/lib/pacman'),
+            os.path.join(DEST_DIR, 'etc/pacman.d/gnupg'),
+            os.path.join(DEST_DIR, 'var/log')]
+
+        for folder in folders:
+            os.makedirs(folder, mode=0o755, exist_ok=True)
+
+        # If kernel images exists in /boot they are most likely from a failed
+        # install attempt and need to be removed otherwise pyalpm will raise a
+        # fatal exception later on.
+        kernel_imgs = (
+            "/install/boot/vmlinuz-linux",
+            "/install/boot/vmlinuz-linux-lts",
+            "/install/boot/initramfs-linux.img",
+            "/install/boot/initramfs-linux-fallback.img",
+            "/install/boot/initramfs-linux-lts.img",
+            "/install/boot/initramfs-linux-lts-fallback.img")
+
+        for img in kernel_imgs:
+            if os.path.exists(img):
+                os.remove(img)
+
+        # If intel-ucode or grub2-theme-antergos files exist in /boot they are
+        # most likely either from another linux installation or from a failed
+        # install attempt and need to be removed otherwise pyalpm will refuse
+        # to install those packages (like above)
+        if os.path.exists('/install/boot/intel-ucode.img'):
+            logging.debug("Removing previous intel-ucode.img file found in /boot")
+            os.remove('/install/boot/intel-ucode.img')
+        if os.path.exists('/install/boot/grub/themes/Antergos-Default'):
+            logging.debug("Removing previous Antergos-Default grub2 theme found in /boot")
+            shutil.rmtree('/install/boot/grub/themes/Antergos-Default')
+
+        logging.debug("Preparing pacman...")
+        self.prepare_pacman()
+        logging.debug("Pacman ready")
+
+        # Run driver's pre-install scripts
+        try:
+            logging.debug("Running hardware drivers pre-install jobs...")
+            proprietary = self.settings.get('feature_graphic_drivers')
+            self.hardware_install = hardware.HardwareInstall(
+                self.settings.get("cnchi"),
+                use_proprietary_graphic_drivers=proprietary)
+            self.hardware_install.pre_install(DEST_DIR)
+        except Exception as ex:
+            template = "Error in hardware module. " \
+                       "An exception of type {0} occured. Arguments:\n{1!r}"
+            message = template.format(type(ex).__name__, ex.args)
+            logging.error(message)
+
+        logging.debug("Downloading packages...")
+        self.download_packages()
+
+        # This mounts (binds) /dev and others to /DEST_DIR/dev and others
+        special_dirs.mount(DEST_DIR)
+
+        logging.debug("Installing packages...")
+        self.install_packages()
+
+        logging.debug("Configuring system...")
+        post = post_install.PostInstallation(
+            self.settings,
+            self.events.queue,
+            self.mount_devices,
+            self.fs_devices,
+            self.ssd,
+            self.blvm)
+        post.configure_system(self.hardware_install)
+
+        # This unmounts (unbinds) /dev and others to /DEST_DIR/dev and others
+        special_dirs.umount(DEST_DIR)
+
+        # Run postinstall script (we need special dirs unmounted but dest_dir mounted!)
+        logging.debug("Running postinstall.sh script...")
+        post.set_desktop_settings()
+
+        # Copy installer log to the new installation
+        logging.debug("Copying install log to /var/log/cnchi")
+        post.copy_logs()
+
+        self.events.add('pulse', 'stop')
+        self.events.add('progress_bar', 'hide')
+
+        # Finally, try to unmount DEST_DIR
+        mount.unmount_all_in_directory(DEST_DIR)
+
+        self.running = False
+
+        # Installation finished successfully
+        self.events.add('finished', _("Installation finished"))
+        self.error = False
+        return True
+
+    def download_packages(self):
+        """ Downloads necessary packages """
+
+        self.pacman_cache_dir = os.path.join(DEST_DIR, 'var/cache/pacman/pkg')
+
+        pacman_conf = {}
+        pacman_conf['file'] = Installation.TMP_PACMAN_CONF
+        pacman_conf['cache'] = self.pacman_cache_dir
+
+        download_packages = download.DownloadPackages(
+            package_names=self.packages,
+            pacman_conf=pacman_conf,
+            settings=self.settings,
+            callback_queue=self.events.queue)
+
+        # Metalinks have already been calculated before,
+        # When downloadpackages class has been called in process.py to test
+        # that Cnchi was able to create it before partitioning/formatting
+        download_packages.start_download(self.metalinks)
+
+    def create_pacman_conf_file(self):
+        """ Creates a temporary pacman.conf """
+        myarch = os.uname()[-1]
+        msg = _("Creating a temporary pacman.conf for {0} architecture").format(
+            myarch)
+        logging.debug(msg)
+
+        # Template functionality. Needs Mako (see http://www.makotemplates.org/)
+        template_file_name = os.path.join(
+            self.settings.get('data'), 'pacman.tmpl')
+        file_template = Template(filename=template_file_name)
+        file_rendered = file_template.render(
+            destDir=DEST_DIR,
+            arch=myarch,
+            desktop=self.desktop)
+        filename = Installation.TMP_PACMAN_CONF
+        dirname = os.path.dirname(filename)
+        os.makedirs(dirname, mode=0o755, exist_ok=True)
+        with open(filename, "w") as my_file:
+            my_file.write(file_rendered)
+
+    def prepare_pacman(self):
+        """ Configures pacman and syncs db on destination system """
+
+        self.create_pacman_conf_file()
+
+        msg = _("Updating package manager security. Please wait...")
+        self.events.add('info', msg)
+        self.prepare_pacman_keyring()
+
+        # Init pyalpm
+        try:
+            self.pacman = pac.Pac(
+                Installation.TMP_PACMAN_CONF, self.events.queue)
+        except Exception as ex:
+            self.pacman = None
+            template = ("Can't initialize pyalpm. "
+                        "An exception of type {0} occured. Arguments:\n{1!r}")
+            message = template.format(type(ex).__name__, ex.args)
+            logging.error(message)
+            raise InstallError(message)
+
+        # Refresh pacman databases
+        if not self.pacman.refresh():
+            logging.error("Can't refresh pacman databases.")
+            raise InstallError(_("Can't refresh pacman databases."))
+
+    @staticmethod
+    def prepare_pacman_keyring():
+        """ Add gnupg pacman files to installed system """
+
+        dirs = ["var/cache/pacman/pkg", "var/lib/pacman"]
+        for pacman_dir in dirs:
+            mydir = os.path.join(DEST_DIR, pacman_dir)
+            os.makedirs(mydir, mode=0o755, exist_ok=True)
+
+        # Be sure that haveged is running (liveCD)
+        # haveged is a daemon that generates system entropy; this speeds up
+        # critical operations in cryptographic programs such as gnupg
+        # (including the generation of new keyrings)
+        cmd = ["systemctl", "start", "haveged"]
+        call(cmd)
+
+        # Delete old gnupg files
+        dest_path = os.path.join(DEST_DIR, "etc/pacman.d/gnupg")
+        cmd = ["rm", "-rf", dest_path]
+        call(cmd)
+        os.mkdir(dest_path)
+
+        # Tell pacman-key to regenerate gnupg files
+        # Initialize the pacman keyring
+        cmd = ["pacman-key", "--init", "--gpgdir", dest_path]
+        call(cmd)
+
+        # Load the signature keys
+        # Delete antergos dest_pat, add rebornos dest-path (Rafael)
+        cmd = ["pacman-key", "--populate", "--gpgdir",
+               dest_path, "archlinux", "rebornos"]
+        call(cmd)
+
+        # path = os.path.join(DEST_DIR, "root/.gnupg/dirmngr_ldapservers.conf")
+        # Run dirmngr
+        # https://bbs.archlinux.org/viewtopic.php?id=190380
+        with open(os.devnull, 'r') as dev_null:
+            cmd = ["dirmngr"]
+            call(cmd, stdin=dev_null)
+
+        # Refresh and update the signature keys
+        # cmd = ["pacman-key", "--refresh-keys", "--gpgdir", dest_path]
+        # call(cmd)
+
+    def delete_stale_pkgs(self, stale_pkgs):
+        """ Failure might be due to stale cached packages. Delete them. """
+        for stale_pkg in stale_pkgs:
+            filepath = os.path.join(self.pacman_cache_dir, stale_pkg)
+            to_delete = glob.glob(filepath + '***') if filepath else []
+            if to_delete and len(to_delete) <= 20:
+                for fpath in to_delete:
+                    try:
+                        os.remove(fpath)
+                    except OSError as err:
+                        logging.error(err)
+
+    @staticmethod
+    def use_build_server_repo():
+        """ Setup pacman.conf to use build server repository """
+        with open('/etc/pacman.conf', 'r') as pacman_conf:
+            contents = pacman_conf.readlines()
+        with open('/etc/pacman.conf', 'w') as new_pacman_conf:
+            for line in contents:
+                if 'reborn-mirrorlist' in line:
+                    line = 'Server = https://repo.rebornos.org/RebornOS/'
+                new_pacman_conf.write(line)
+
+    def install_packages(self):
+        """ Start pacman installation of packages """
+        result = False
+        # This shouldn't be necessary if download.py really downloaded all
+        # needed packages, but it does not do it (why?)
+        for cache_dir in self.settings.get('xz_cache'):
+            self.pacman.handle.add_cachedir(cache_dir)
+
+        logging.debug("Installing packages...")
+
+        try:
+            result = self.pacman.install(pkgs=self.packages)
+        except pac.pyalpm.error:
+            pass
+
+        stale_pkgs = self.settings.get('cache_pkgs_md5_check_failed')
+
+        if not result and stale_pkgs and os.path.exists(self.pacman_cache_dir):
+            # Failure might be due to stale cached packages. Delete them and try again.
+            logging.warning(
+                "Can't install necessary packages. Let's try again deleting stale packages first.")
+            self.delete_stale_pkgs(stale_pkgs)
+            self.pacman.refresh()
+            try:
+                result = self.pacman.install(pkgs=self.packages)
+            except pac.pyalpm.error:
+                pass
+
+        if not result:
+            # Failure might be due to antergos mirror issues. Try using build server repo.
+            logging.warning(
+                "Can't install necessary packages. Let's try again using a tier 1 mirror.")
+            self.use_build_server_repo()
+            self.pacman.refresh()
+            try:
+                result = self.pacman.install(pkgs=self.packages)
+            except pac.pyalpm.error:
+                pass
+
+        if not result:
+            txt = _("Can't install necessary packages. Cnchi can't continue.")
+            raise InstallError(txt)
+
+        # All downloading and installing has been done, so we hide progress bar
+        self.events.add('progress_bar', 'hide')
+
+    def is_running(self):
+        """ Checks if thread is running """
+        return self.running
+
+    def is_ok(self):
+        """ Checks if an error has been issued """
+        return not self.error
diff --git a/Cnchi/lightdm-webkit2-greeter.conf b/Cnchi/lightdm-webkit2-greeter.conf
new file mode 100644 (file)
index 0000000..a1af3e7
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# [greeter]
+# debug_mode          = Greeter theme debug mode.
+# detect_theme_errors = Provide an option to load a fallback theme when theme errors are detected.
+# screensaver_timeout = Blank the screen after this many seconds of inactivity.
+# secure_mode         = Don't allow themes to make remote http requests.
+# time_format         = A moment.js format string so the greeter can generate localized time for display.
+# time_language       = Language to use when displaying the time or "auto" to use the system's language.
+# webkit_theme        = Webkit theme to use.
+#
+# NOTE: See moment.js documentation for format string options: http://momentjs.com/docs/#/displaying/format/
+#
+
+[greeter]
+debug_mode          = false
+detect_theme_errors = true
+screensaver_timeout = 300
+secure_mode         = true
+time_format         = LT
+time_language       = auto
+webkit_theme = lightdm-webkit-theme-aether 
+
+#
+# [branding]
+# background_images = Path to directory that contains background images for use by themes.
+# logo              = Path to logo image for use by greeter themes.
+# user_image        = Default user image/avatar. This is used by themes for users that have no .face image.
+#
+# NOTE: Paths must be accessible to the lightdm system user account (so they cannot be anywhere in /home)
+#
+
+[branding]
+background_images = /usr/share/backgrounds/
+logo              = /usr/share/lightdm-webkit/themes/lightdm-webkit-theme-aether/src/img/arch-logo.png
+user_image        = /usr/share/lightdm-webkit/themes/lightdm-webkit-theme-aether/src/img/arch-logo.png
diff --git a/Cnchi/logging_utils.py b/Cnchi/logging_utils.py
new file mode 100755 (executable)
index 0000000..a5f8738
--- /dev/null
@@ -0,0 +1,230 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# logging_utils.py
+#
+# Copyright © 2015-2018 Antergos
+#
+# This file is part of Cnchi.
+#
+# Cnchi 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.
+#
+# Cnchi 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.
+#
+# The following additional terms are in effect as per Section 7 of the license:
+#
+# The preservation of all legal notices and author attributions in
+# the material or in the Appropriate Legal Notices displayed
+# by works containing it is required.
+#
+# You should have received a copy of the GNU General Public License
+# along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
+
+""" Logging utils to ease log calls """
+
+import logging
+import uuid
+import json
+import os
+import requests
+
+from info import CNCHI_VERSION, CNCHI_RELEASE_STAGE
+
+
+class Singleton(type):
+    """ Single instance """
+    _instance = None
+
+    def __call__(cls, *args, **kwargs):
+        if not cls._instance:
+            cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
+        return cls._instance
+
+    def __new__(cls, *args, **kwargs):
+        obj = super().__new__(cls, *args, **kwargs)
+        obj.ip_addr = None
+        obj.install_id = None
+        obj.api_key = None
+        obj.have_install_id = False
+        obj.after_location_screen = False
+        obj.name = 'Cnchi'
+        obj.description = 'Installer'
+        obj.key = 'X-{}-{}'.format(obj.name, obj.description)
+
+        return obj
+
+
+class ContextFilter(logging.Filter, metaclass=Singleton):
+    """ Context filter for logging methods to send logs to bugsnag """
+    LOG_FOLDER = '/var/log/cnchi'
+
+    def __init__(self):
+        super().__init__()
+        self.api_key = self.get_bugsnag_api()
+        self.have_install_id = False
+        self.after_location_screen = False
+        self.install_id = ""
+        self.ip_addr = '1.2.3.4'
+
+    def filter(self, record):
+        uid = str(uuid.uuid1()).split("-")
+        record.uuid = uid[3] + "-" + uid[1] + "-" + uid[2] + "-" + uid[4]
+        record.ip_addr = self.ip_addr
+        record.install_id = self.install_id
+        return True
+
+    def get_and_save_install_id(self, is_location_screen=False):
+        """ Obtain an install identification """
+        if self.have_install_id:
+            return self.install_id
+
+        if is_location_screen:
+            self.after_location_screen = True
+
+        if CNCHI_RELEASE_STAGE == 'development':
+            self.install_id = 'development'
+            self.ip_addr = '1.2.3.4'
+            self.have_install_id = True
+            return 'development'
+
+        info = {'ip': '1.2.3.4', 'id': '0'}
+        url = self.get_url_for_id_request()
+        headers = {self.api_key: CNCHI_VERSION}
+
+        try:
+            req = requests.get(url, headers=headers)
+            req.raise_for_status()
+            info = json.loads(req.json())
+        except Exception as err:
+            logger = logging.getLogger()
+            msg = "Unable to get an Id for this installation. Error: {0}".format(err.args)
+            logger.debug(msg)
+
+        try:
+            self.ip_addr = info['ip']
+            self.install_id = info['id']
+            self.have_install_id = True
+        except (TypeError, KeyError):
+            self.have_install_id = False
+
+        return self.install_id
+
+    @staticmethod
+    def get_bugsnag_api():
+        """ Gets bugsnag API key """
+        config_path = '/etc/cnchi.conf'
+        alt_config_path = '/usr/share/cnchi/data/cnchi.conf'
+        bugsnag_api = None
+
+        if (not os.path.exists(config_path) and
+                os.path.exists(alt_config_path)):
+            config_path = alt_config_path
+
+        if os.path.exists(config_path):
+            with open(config_path) as bugsnag_conf:
+                bugsnag_api = bugsnag_conf.readline().strip()
+
+        return bugsnag_api
+
+    def get_url_for_id_request(self):
+        """ Constructs bugsnag url """
+        build_server = None
+
+        if self.api_key and CNCHI_RELEASE_STAGE != 'development':
+            parts = {
+                1: 'com', 2: 'http', 3: 'hook', 4: 'build',
+                5: 'antergos', 6: 'cnchi', 7: '://'
+            }
+            build_server = '{}{}{}.{}.{}/{}?{}={}'.format(
+                parts[2], parts[7], parts[4], parts[5],
+                parts[1], parts[3], parts[6], self.api_key
+            )
+        return build_server
+
+    @staticmethod
+    def filter_log_lines(log):
+        """ Filter log lines """
+        keep_lines = []
+        look_for = ['[WARNING]', '[ERROR]']
+        log_lines = log.readlines()
+
+        for i, log_line in enumerate(log_lines):
+            for pattern in look_for:
+                if pattern in log_line:
+                    try:
+                        if 10 < i < (len(log_lines) - 10):
+                            keep_lines.extend([log_lines[l]
+                                               for l in range(i - 10, i + 10)])
+                        elif i < 10:
+                            keep_lines.extend([log_lines[l]
+                                               for l in range(0, i)])
+                        elif i > (len(log_lines) - 10):
+                            keep_lines.extend([log_lines[l]
+                                               for l in range(i, len(log_lines))])
+                    except (IndexError, KeyError) as err:
+                        print(err)
+
+        return keep_lines
+
+    def bugsnag_before_notify_callback(self, notification=None):
+        """ Filter unwanted notifications here """
+        if notification is not None:
+            excluded = ["No such interface '/org/freedesktop/UPower'"]
+
+            if any(True for pattern in excluded if pattern in str(notification.exception)):
+                return False
+
+            if self.after_location_screen and not self.have_install_id:
+                self.get_and_save_install_id()
+
+            notification.user = {"id": self.ip_addr,
+                                 "name": self.install_id,
+                                 "install_id": self.install_id}
+
+            logs = [
+                os.path.join(ContextFilter.LOG_FOLDER, '{0}.log'.format(n))
+                for n in ['cnchi', 'cnchi-alpm', 'pacman', 'postinstall']]
+            missing = [f for f in logs if not os.path.exists(f)]
+            if missing:
+                for log in missing:
+                    open(log, 'a').close()
+
+            with open(logs[0], 'r') as cnchi:
+                with open(logs[1], 'r') as pacman:
+                    with open(logs[2], 'r') as postinstall:
+                        log_dict = {'pacman': pacman,
+                                    'postinstall': postinstall}
+                        parse = {
+                            log: [line.strip() for line in log_dict[log]]
+                            for log in log_dict
+                        }
+                        parse['cnchi'] = self.filter_log_lines(cnchi)
+                        notification.add_tab('logs', parse)
+
+            return notification
+        return False
+
+    def send_install_result(self, result):
+        """ Sends install result to bugsnag server (result: str) """
+        try:
+            if self.after_location_screen and not self.have_install_id:
+                self.get_and_save_install_id()
+            build_server = self.get_url_for_id_request()
+            if build_server and self.install_id:
+                url = "{0}&install_id={1}&result={2}"
+                url = url.format(build_server, self.install_id, result)
+                headers = {self.api_key: CNCHI_VERSION}