OSDN Git Service

Initial commit
authorRafael Costa Rega <rcostarega@gmail.com>
Sun, 3 May 2020 19:16:21 +0000 (16:16 -0300)
committerRafael Costa Rega <rcostarega@gmail.com>
Sun, 3 May 2020 19:16:21 +0000 (16:16 -0300)
284 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/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/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-install.desktop [new file with mode: 0644]
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/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-icon-new.png [new file with mode: 0755]
Cnchi/reborn-mirrorlist [new file with mode: 0644]
Cnchi/reborn-mirrorlist2 [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..91c33f1
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,52 @@
+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) 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
+
+
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/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/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..6d17793
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-install.desktop b/Cnchi/antergos-install.desktop
new file mode 100644 (file)
index 0000000..344da88
--- /dev/null
@@ -0,0 +1,15 @@
+[Desktop Entry]
+Comment=Install the operating system to disk
+Comment[pl]=Zainstaluj system RebornOS
+Comment[sk]=Nainštaluje operačný systém na disk
+Exec=sudo /usr/bin/cnchi-start.sh
+GenericName=RebornOS Installer
+Icon=/usr/share/pixmaps/reborn-icon-new.png
+Name=Reborn Installer
+Name[en_US]=RebornOS Installer
+Name[pl]=Zainstaluj RebornOS
+Name[sk]=Inštalovať RebornOS
+Terminal=True
+Type=Application
+Categories=System;
+
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/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..b6eabe2
--- /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 = 'deepin'
+
+        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 Gnome as default
+        self.select_default_row(desktop_info.NAMES["gnome"])
+
+    @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..54f7055
--- /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 """
+
+# Enabled desktops
+
+DESKTOPS = ["base", "cinnamon", "deepin",
+            "gnome", "kde", "mate", "openbox", "xfce"]
+
+DESKTOPS_DEV = DESKTOPS + ["budgie", "enlightenment", "i3", "lxqt", "pantheon", "windows", "apricity"]
+
+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", "mycroft", "graphic_drivers", "lamp", "lts", "freeoffice", "wps-office", "libreoffice", "redshift", "power", "sshd", "spotify", "visual", "vlc", "nautilus", "nemo", "nixnote", "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", "nixnote", "qt-play", "movie", "mycroft", "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': _("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."),
+    '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..13e904d
--- /dev/null
@@ -0,0 +1,359 @@
+#!/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',
+    'mycroft': 'deepin-voice-recorder',
+    'graphic_drivers': 'gnome-system',
+    'lamp': 'applications-internet',
+    'lts': 'applications-accessories',
+    'nemo': 'system-file-manager',
+    'nautilus': 'system-file-manager',
+    'nixnote': 'evernote',
+    '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', 'nixnote', 'lts', 'graphic_drivers', 'mycroft', '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"),
+    'mycroft': _("Mycroft"),
+    '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"),
+    'nixnote': _("Evernote For Linux"),
+    '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"),
+    'mycroft': _("An Open Source alternative to AIs such as Amazon's Alexa and Apple's Siri"),
+    '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"),
+    'nixnote': _("An Opensource implementation of Evernote for Linux"),
+    '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"),
+    'mycroft': _("Open Source alternative to AIs such as Amazon's Alexa and Apple's Siri. Just like other AIs,\n"
+                        "Mycroft is voice activated and can perform numerous tasks. However, unlike other IAs,\n"
+                        "Mycroft does NOT collect any information on its users - keeping your privacy intact"),
+    '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."),
+    'nixnote': _("Nixnote, (formerly Nevernote) is an opensource client for Evernote,\n"
+                 "allowing you to track your digital life in a single, convenient place.\n"
+                 "NOTE: an Evernote account is required for cloud synchronization."),
+    '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..cb3743e
--- /dev/null
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Version=1.0
+Type=Application
+Name=Uncomplicated Firewall
+Comment=Is a program for managing a netfilter firewall.
+Icon=/usr/share/icons/default/gufw.svg
+Exec=gufw
+NoDisplay=false
+Categories=Utility;
+Keywords=firewall
+StartupNotify=true
+Terminal=false
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..31919d8
--- /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 Installer Gnome based 2020.05.02"
+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}
+                req = requests.get(url, headers=headers)
+                json.loads(req.json())
+        except Exception as ex:
+            logger = logging.getLogger()
+            template = "Can't send install result. An exception of type {0} occured. "
+            template += "Arguments:\n{1!r}"
+            message = template.format(type(ex).__name__, ex.args)
+            logger.error(message)
diff --git a/Cnchi/main_window.py b/Cnchi/main_window.py
new file mode 100644 (file)
index 0000000..d0699d1
--- /dev/null
@@ -0,0 +1,515 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# main_window.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 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 Window """
+
+import os
+import multiprocessing
+import logging
+
+import config
+import desktop_info
+import info
+import misc.extra as misc
+
+import pages.welcome
+import pages.language
+import pages.location
+import pages.cache
+import pages.check
+import pages.desktop
+import pages.features
+import pages.keymap
+import pages.timezone
+import pages.user_info
+import pages.slides
+import pages.summary
+import pages.mirrors
+import pages.ask
+import pages.automatic
+import pages.alongside
+import pages.advanced
+import pages.zfs
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, Gdk
+
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+def atk_set_image_description(widget, description):
+    """ Sets the textual description for a widget that displays image/pixmap
+        information onscreen. """
+    atk_widget = widget.get_accessible()
+    if atk_widget is not None:
+        atk_widget.set_object_description(description)
+
+
+def atk_set_object_description(widget, description):
+    """ Sets the textual description for a widget """
+    atk_widget = widget.get_accessible()
+    if atk_widget is not None:
+        atk_widget.set_image_description(description)
+        # atk_object_set_name
+
+
+class MainWindow(Gtk.ApplicationWindow):
+    """ Cnchi main window """
+
+    def __init__(self, app, cmd_line):
+        Gtk.ApplicationWindow.__init__(self, title="Cnchi", application=app)
+
+        self._main_window_width = 875
+        self._main_window_height = 550
+
+        logging.info("Cnchi installer version %s", info.CNCHI_VERSION)
+
+        self.settings = config.Settings()
+        self.gui_dir = self.settings.get('ui')
+
+        if not os.path.exists(self.gui_dir):
+            cnchi_dir = os.path.join(os.path.dirname(__file__), './')
+            self.settings.set('cnchi', cnchi_dir)
+
+            gui_dir = os.path.join(os.path.dirname(__file__), 'ui/')
+            self.settings.set('ui', gui_dir)
+
+            data_dir = os.path.join(os.path.dirname(__file__), 'data/')
+            self.settings.set('data', data_dir)
+
+            self.gui_dir = self.settings.get('ui')
+
+        # By default, always try to use local /var/cache/pacman/pkg
+        xz_cache = ["/var/cache/pacman/pkg"]
+
+        # Check command line
+        if cmd_line.cache and cmd_line.cache not in xz_cache:
+            xz_cache.append(cmd_line.cache)
+
+        # Log cache dirs
+        for xz_path in xz_cache:
+            logging.debug(
+                "Cnchi will use '%s' as a source for cached xz packages",
+                xz_path)
+
+        # Store cache dirs in config
+        self.settings.set('xz_cache', xz_cache)
+
+        data_dir = self.settings.get('data')
+
+        # For things we are not ready for users to test
+        self.settings.set('hidden', cmd_line.hidden)
+
+        # a11y
+        self.settings.set('a11y', cmd_line.a11y)
+
+        # Set enabled desktops
+        if self.settings.get('hidden'):
+            self.settings.set('desktops', desktop_info.DESKTOPS_DEV)
+        elif self.settings.get('a11y'):
+            self.settings.set('desktops', desktop_info.DESKTOPS_A11Y)
+        else:
+            self.settings.set('desktops', desktop_info.DESKTOPS)
+
+        if cmd_line.environment:
+            my_desktop = cmd_line.environment.lower()
+            if my_desktop in desktop_info.DESKTOPS:
+                self.settings.set('desktop', my_desktop)
+                self.settings.set('desktop_ask', False)
+                logging.debug(
+                    "Cnchi will install the %s desktop environment",
+                    my_desktop)
+
+        self.cnchi_ui = Gtk.Builder()
+        path = os.path.join(self.gui_dir, "cnchi.ui")
+        self.cnchi_ui.add_from_file(path)
+
+        main = self.cnchi_ui.get_object("main")
+        # main.set_property("halign", Gtk.Align.CENTER)
+        self.add(main)
+
+        self.header_ui = Gtk.Builder()
+        path = os.path.join(self.gui_dir, "header.ui")
+        self.header_ui.add_from_file(path)
+        self.header = self.header_ui.get_object("header")
+
+        self.logo = self.header_ui.get_object("logo")
+        path = os.path.join(
+            data_dir, "images", "antergos", "antergos-logo-mini2.png")
+        self.logo.set_from_file(path)
+
+        # To honor our css
+        self.header.set_name("header")
+        self.logo.set_name("logo")
+
+        self.main_box = self.cnchi_ui.get_object("main_box")
+        # self.main_box.set_property("halign", Gtk.Align.CENTER)
+
+        ##self.main_box.set_property('width_request', 800)
+
+        self.progressbar = self.cnchi_ui.get_object("main_progressbar")
+        self.progressbar.set_name('process_progressbar')
+        # a11y
+        self.progressbar.set_can_focus(False)
+
+        self.forward_button = self.header_ui.get_object("forward_button")
+        self.backwards_button = self.header_ui.get_object("backwards_button")
+
+        # atk_set_image_description(self.forward_button, _("Next step"))
+        # atk_set_image_description(self.backwards_button, _("Previous step"))
+        # atk_set_object_description(self.forward_button, _("Next step"))
+        # atk_set_object_description(self.backwards_button, _("Previous step"))
+
+        self.forward_button.set_name('fwd_btn')
+        self.forward_button.set_always_show_image(True)
+
+        self.backwards_button.set_name('bk_btn')
+        self.backwards_button.set_always_show_image(True)
+
+        # a11y
+        if cmd_line.a11y:
+            self.forward_button.set_label(_("Next"))
+            self.backwards_button.set_label(_("Back"))
+
+        # Create a queue. Will be used to report pacman messages
+        # (pacman/pac.py) to the main thread (installation/process.py)
+        self.callback_queue = multiprocessing.JoinableQueue()
+
+        if cmd_line.packagelist:
+            self.settings.set('alternate_package_list', cmd_line.packagelist)
+            logging.info(
+                "Using '%s' file as package list",
+                self.settings.get('alternate_package_list'))
+
+        self.set_titlebar(self.header)
+
+        # Prepare params dict to pass common parameters to all screens
+        self.params = dict()
+        self.params['main_window'] = self
+        self.params['header'] = self.header
+        self.params['gui_dir'] = self.gui_dir
+        self.params['forward_button'] = self.forward_button
+        self.params['backwards_button'] = self.backwards_button
+        self.params['callback_queue'] = self.callback_queue
+        self.params['settings'] = self.settings
+        self.params['main_progressbar'] = self.progressbar
+
+        self.params['checks_are_optional'] = cmd_line.no_check
+        self.params['no_tryit'] = cmd_line.no_tryit
+        self.params['a11y'] = cmd_line.a11y
+
+        # Just load the first two screens (the other ones will be loaded later)
+        # We do this so the user has not to wait for all the screens to be
+        # loaded
+        self.pages = dict()
+        self.pages["welcome"] = pages.welcome.Welcome(self.params)
+
+        if os.path.exists('/home/reborn/.config/openbox'):
+            # In minimal iso, load language screen now
+            self.pages["language"] = pages.language.Language(self.params)
+
+            # Fix bugy Gtk window size when using Openbox
+            self._main_window_width = 750
+            self._main_window_height = 450
+
+        self.connect('delete-event', self.on_exit_button_clicked)
+        self.connect('key-release-event', self.on_key_release)
+
+        self.cnchi_ui.connect_signals(self)
+        self.header_ui.connect_signals(self)
+
+        nil, major, minor = info.CNCHI_VERSION.split('.')
+        name = 'Cnchi '
+        title_string = "{0} {1}.{2}.{3}".format(name, nil, major, minor)
+        self.set_title(title_string)
+        self.header.set_title(title_string)
+        self.header.set_subtitle(_("RebornOS Installer"))
+        self.header.set_show_close_button(True)
+        self.tooltip_string = "{0} {1}.{2}.{3}".format(name, nil, major, minor)
+        self.header.forall(self.header_for_all_callback, self.tooltip_string)
+
+        self.set_geometry()
+
+        # Set window icon
+        icon_path = os.path.join(
+            data_dir,
+            "images",
+            "antergos",
+            "antergos-icon.png")
+        self.set_icon_from_file(icon_path)
+
+        # Set the first page to show
+
+        # If minimal iso is detected, skip the welcome page.
+        if os.path.exists('/home/reborn/.config/openbox'):
+            self.current_page = self.pages["language"]
+            self.settings.set('timezone_start', True)
+        else:
+            self.current_page = self.pages["welcome"]
+
+        self.main_box.add(self.current_page)
+
+        # Use our css file
+        style_provider = Gtk.CssProvider()
+
+        style_css = os.path.join(data_dir, "css", "gtk-style.css")
+
+        with open(style_css, 'rb') as css:
+            css_data = css.read()
+
+        style_provider.load_from_data(css_data)
+
+        Gtk.StyleContext.add_provider_for_screen(
+            Gdk.Screen.get_default(), style_provider,
+            Gtk.STYLE_PROVIDER_PRIORITY_USER
+        )
+
+        # Show main window
+        self.show_all()
+
+        self.current_page.prepare('forwards')
+
+        # Hide backwards button
+        self.backwards_button.hide()
+
+        self.progressbar.set_fraction(0)
+        self.progressbar_step = 0
+
+        # Do not hide progress bar for minimal iso as it would break
+        # the widget alignment on language page.
+        if not os.path.exists('/home/reborn/.config/openbox'):
+            # Hide progress bar
+            self.progressbar.hide()
+
+        self.set_focus(None)
+
+        misc.gtk_refresh()
+
+    def header_for_all_callback(self, widget, _data):
+        """ Show tooltip in header """
+        if isinstance(widget, Gtk.Box):
+            widget.forall(self.header_for_all_callback, self.tooltip_string)
+        elif widget.get_style_context().has_class('title'):
+            widget.set_tooltip_text(self.tooltip_string)
+
+    def load_pages(self):
+        """ Preload all installer pages """
+        if not os.path.exists('/home/reborn/.config/openbox'):
+            self.pages["language"] = pages.language.Language(self.params)
+
+        self.pages["check"] = pages.check.Check(self.params)
+        self.pages["location"] = pages.location.Location(self.params)
+
+        self.pages["timezone"] = pages.timezone.Timezone(self.params)
+
+        if self.settings.get('desktop_ask'):
+            self.pages["keymap"] = pages.keymap.Keymap(self.params)
+            self.pages["desktop"] = pages.desktop.DesktopAsk(self.params)
+            self.pages["features"] = pages.features.Features(self.params)
+        else:
+            self.pages["keymap"] = pages.keymap.Keymap(
+                self.params,
+                next_page='features')
+            self.pages["features"] = pages.features.Features(
+                self.params,
+                prev_page='keymap')
+
+        self.pages["cache"] = pages.cache.Cache(self.params)
+        self.pages["mirrors"] = pages.mirrors.Mirrors(self.params)
+
+        self.pages["installation_ask"] = pages.ask.InstallationAsk(
+            self.params)
+        self.pages["installation_automatic"] = pages.automatic.InstallationAutomatic(
+            self.params)
+
+        if self.settings.get("enable_alongside"):
+            self.pages["installation_alongside"] = pages.alongside.InstallationAlongside(
+                self.params)
+        else:
+            self.pages["installation_alongside"] = None
+
+        self.pages["installation_advanced"] = pages.advanced.InstallationAdvanced(
+            self.params)
+        self.pages["installation_zfs"] = pages.zfs.InstallationZFS(
+            self.params)
+        self.pages["user_info"] = pages.user_info.UserInfo(self.params)
+        self.pages["summary"] = pages.summary.Summary(self.params)
+        self.pages["slides"] = pages.slides.Slides(self.params)
+
+        diff = 2
+        if os.path.exists('/home/reborn/.config/openbox'):
+            # In minimal (openbox) we don't have a welcome screen
+            diff = 3
+
+        num_pages = len(self.pages) - diff
+
+        if num_pages > 0:
+            self.progressbar_step = 1.0 / num_pages
+
+    def set_geometry(self):
+        """ Sets Cnchi window geometry """
+        self.set_position(Gtk.WindowPosition.CENTER)
+        self.set_resizable(False)
+
+        (min_width, natural_width) = self.get_preferred_width()
+        (min_height, natural_height) = self.get_preferred_width()
+        logging.debug("Main window minimal size: %dx%d", min_width, min_height)
+        logging.debug("Main window natural size: %dx%d", natural_width, natural_height)
+        logging.debug("Setting main window size to %dx%d",
+                      self._main_window_width, self._main_window_height)
+
+        self.set_size_request(self._main_window_width,
+                              self._main_window_height)
+        self.set_default_size(self._main_window_width,
+                              self._main_window_height)
+
+        geom = Gdk.Geometry()
+        geom.min_width = self._main_window_width
+        geom.min_height = self._main_window_height
+        geom.max_width = self._main_window_width
+        geom.max_height = self._main_window_height
+        geom.base_width = self._main_window_width
+        geom.base_height = self._main_window_height
+        geom.width_inc = 0
+        geom.height_inc = 0
+
+        hints = (Gdk.WindowHints.MIN_SIZE |
+                 Gdk.WindowHints.MAX_SIZE |
+                 Gdk.WindowHints.BASE_SIZE |
+                 Gdk.WindowHints.RESIZE_INC)
+
+        self.set_geometry_hints(None, geom, hints)
+
+    def on_key_release(self, _widget, event, _data=None):
+        """ Callback called when a key is released """
+        if event.keyval == Gdk.keyval_from_name('Escape'):
+            response = self.confirm_quitting()
+            if response == Gtk.ResponseType.YES:
+                self.on_exit_button_clicked(self)
+                self.destroy()
+
+    def confirm_quitting(self):
+        """ Shows confirmation message before quitting """
+        message = Gtk.MessageDialog(
+            transient_for=self,
+            modal=True,
+            destroy_with_parent=True,
+            message_type=Gtk.MessageType.QUESTION,
+            buttons=Gtk.ButtonsType.YES_NO,
+            text=_("Do you really want to quit the installer?"))
+        response = message.run()
+        message.destroy()
+        return response
+
+    def on_exit_button_clicked(self, _widget, _data=None):
+        """ Quit Cnchi """
+        try:
+            misc.remove_temp_files(self.settings.get('temp'))
+            logging.info("Quiting installer...")
+            for proc in multiprocessing.active_children():
+                proc.terminate()
+            logging.shutdown()
+        except KeyboardInterrupt:
+            pass
+
+    def set_progressbar_step(self, add_value):
+        """ Update progress bar """
+        new_value = self.progressbar.get_fraction() + add_value
+        if new_value > 1:
+            new_value = 1
+        if new_value < 0:
+            new_value = 0
+        self.progressbar.set_fraction(new_value)
+        if new_value > 0:
+            self.progressbar.show()
+        else:
+            self.progressbar.hide()
+
+    def on_forward_button_clicked(self, _widget, _data=None):
+        """ Show next screen """
+        next_page = self.current_page.get_next_page()
+
+        if next_page is not None:
+            # self.logo.hide()
+            if next_page not in self.pages.keys():
+                # Load all pages
+                self.load_pages()
+                self.progressbar_step = 1.0 / (len(self.pages) - 2)
+
+            stored = self.current_page.store_values()
+
+            if stored:
+                self.set_progressbar_step(self.progressbar_step)
+                self.main_box.remove(self.current_page)
+
+                self.current_page = self.pages[next_page]
+
+                if self.current_page is not None:
+                    self.current_page.prepare('forwards')
+                    self.main_box.add(self.current_page)
+                    if self.current_page.get_prev_page() is not None:
+                        # There is a previous page, show back button
+                        self.backwards_button.show()
+                        self.backwards_button.set_sensitive(True)
+                    else:
+                        # We can't go back, hide back button
+                        self.backwards_button.hide()
+                        if self.current_page == "slides":
+                            # Show logo in slides screen
+                            self.logo.show_all()
+
+    def on_backwards_button_clicked(self, _widget, _data=None):
+        """ Show previous screen """
+        prev_page = self.current_page.get_prev_page()
+
+        if prev_page is not None:
+            self.set_progressbar_step(-self.progressbar_step)
+
+            # If we go backwards, don't store user changes
+            # self.current_page.store_values()
+
+            self.main_box.remove(self.current_page)
+
+            self.current_page = self.pages[prev_page]
+
+            if self.current_page is not None:
+                self.current_page.prepare('backwards')
+                self.main_box.add(self.current_page)
+
+                if self.current_page.get_prev_page() is None:
+                    # We're at the first page
+                    self.backwards_button.hide()
+                    self.progressbar.hide()
+                    self.logo.show_all()
diff --git a/Cnchi/metalink.py b/Cnchi/metalink.py
new file mode 100755 (executable)
index 0000000..08e8898
--- /dev/null
@@ -0,0 +1,557 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  metalink.py
+#
+#  Parts of code from pm2ml Copyright (C) 2012-2013 Xyne
+#  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 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/>.
+
+
+""" Operations with metalinks """
+
+import logging
+import tempfile
+import os
+
+import hashlib
+import re
+import argparse
+
+from collections import deque
+
+from xml.dom.minidom import getDOMImplementation
+import xml.etree.cElementTree as elementTree
+
+import pyalpm
+
+MAX_URLS = 15
+
+
+def get_info(metalink):
+    """ Reads metalink xml info and returns it """
+
+    # tag = "{urn:ietf:params:xml:ns:metalink}"
+
+    temp_file = tempfile.NamedTemporaryFile(delete=False)
+    temp_file.write(str(metalink).encode('UTF-8'))
+    temp_file.close()
+
+    metalink_info = {}
+    element = {}
+
+    for event, elem in elementTree.iterparse(temp_file.name, events=('start', 'end')):
+        if event == 'start':
+            tag = elem.tag.split('}')[1]
+            if tag == 'file':
+                element['filename'] = elem.attrib['name']
+            elif tag == 'hash':
+                hash_type = elem.attrib['type']
+                try:
+                    element['hash'][hash_type] = elem.text
+                except KeyError:
+                    element['hash'] = {hash_type: elem.text}
+            elif tag == 'url':
+                try:
+                    element['urls'].append(elem.text)
+                except KeyError:
+                    element['urls'] = [elem.text]
+            else:
+                element[tag] = elem.text
+        if event == 'end' and elem.tag.endswith('file'):
+            # Limit to MAX_URLS for each file
+            if len(element['urls']) > MAX_URLS:
+                element['urls'] = element['urls'][:MAX_URLS]
+            key = element['identity']
+            metalink_info[key] = element.copy()
+            element.clear()
+            elem.clear()
+
+    if os.path.exists(temp_file.name):
+        os.remove(temp_file.name)
+
+    return metalink_info
+
+
+def create(alpm, package_name, pacman_conf_file):
+    """ Creates a metalink to download package_name and its dependencies """
+
+    options = ["--conf", pacman_conf_file, "--noconfirm", "--all-deps"]
+
+    if package_name == "databases":
+        options.append("--refresh")
+    else:
+        options.append(package_name)
+
+    download_queue, not_found, missing_deps = build_download_queue(
+        alpm, args=options)
+
+    if not_found:
+        not_found = sorted(not_found)
+        msg = "Can't find these packages: " + ' '.join(not_found)
+        logging.error(msg)
+        return None
+
+    if missing_deps:
+        missing_deps = sorted(missing_deps)
+        msg = "Can't resolve these dependencies: " + ' '.join(missing_deps)
+        logging.error(msg)
+        return None
+
+    if download_queue:
+        metalink = download_queue_to_metalink(download_queue)
+        return metalink
+
+    logging.error("Unable to create download queue for package %s", package_name)
+    return None
+
+# From here comes modified code from pm2ml
+# pm2ml is Copyright (C) 2012-2013 Xyne
+# More info: http://xyne.archlinux.ca/projects/pm2ml
+
+def download_queue_to_metalink(download_queue):
+    """ Converts a download_queue object to a metalink """
+    metalink = Metalink()
+
+    for database, sigs in download_queue.dbs:
+        metalink.add_db(database, sigs)
+
+    for pkg, urls, sigs in download_queue.sync_pkgs:
+        metalink.add_sync_pkg(pkg, urls, sigs)
+
+    return metalink
+
+
+class Metalink():
+    """ Metalink class """
+
+    def __init__(self):
+        self.doc = getDOMImplementation().createDocument(
+            None, "metalink", None)
+        self.doc.documentElement.setAttribute(
+            'xmlns', "urn:ietf:params:xml:ns:metalink")
+        self.files = self.doc.documentElement
+
+    # def __del__(self):
+    #    self.doc.unlink()
+
+    def __str__(self):
+        """ Get a string representation of a metalink """
+        return re.sub(
+            r'(?<=>)\n\s*([^\s<].*?)\s*\n\s*',
+            r'\1',
+            self.doc.toprettyxml(indent=' ')
+        )
+
+    def add_urls(self, element, urls):
+        """Add URL elements to the given element."""
+        for url in urls:
+            url_tag = self.doc.createElement('url')
+            element.appendChild(url_tag)
+            url_val = self.doc.createTextNode(url)
+            url_tag.appendChild(url_val)
+
+    def add_sync_pkg(self, pkg, urls, sigs=False):
+        """Add a sync db package."""
+        file_ = self.doc.createElement("file")
+        file_.setAttribute("name", pkg.filename)
+        self.files.appendChild(file_)
+        for tag, db_attr, attrs in (
+                ('identity', 'name', ()),
+                ('size', 'size', ()),
+                ('version', 'version', ()),
+                ('description', 'desc', ()),
+                ('hash', 'sha256sum', (('type', 'sha256'),)),
+                ('hash', 'md5sum', (('type', 'md5'),))):
+            tag = self.doc.createElement(tag)
+            file_.appendChild(tag)
+            val = self.doc.createTextNode(str(getattr(pkg, db_attr)))
+            tag.appendChild(val)
+            for key, val in attrs:
+                tag.setAttribute(key, val)
+        urls = list(urls)
+        self.add_urls(file_, urls)
+        if sigs:
+            self.add_file(pkg.filename + '.sig', (u + '.sig' for u in urls))
+
+    def add_file(self, name, urls):
+        """Add a signature file."""
+        file_ = self.doc.createElement("file")
+        file_.setAttribute("name", name)
+        self.files.appendChild(file_)
+        self.add_urls(file_, urls)
+
+    def add_db(self, database, sigs=False):
+        """Add a sync db."""
+        file_ = self.doc.createElement("file")
+        name = database.name + '.db'
+        file_.setAttribute("name", name)
+        self.files.appendChild(file_)
+        urls = list(os.path.join(url, database.name + '.db')
+                    for url in database.servers)
+        self.add_urls(file_, urls)
+        if sigs:
+            self.add_file(name + '.sig', (u + '.sig' for u in urls))
+
+
+class PkgSet():
+    """ Represents a set of packages """
+
+    def __init__(self, pkgs=None):
+        """ Init our internal self.pkgs dict with all given packages in pkgs """
+
+        self.pkgs = dict()
+        if pkgs:
+            for pkg in pkgs:
+                self.pkgs[pkg.name] = pkg
+
+    def __repr__(self):
+        return 'PkgSet({0})'.format(repr(self.pkgs))
+
+    def add(self, pkg):
+        """ Adds package info to the set """
+        self.pkgs[pkg.name] = pkg
+
+    def __and__(self, other):
+        new = PkgSet(set(self.pkgs.values()) & set(other.pkgs.values()))
+        return new
+
+    def __iand__(self, other):
+        self.pkgs = self.__and__(other).pkgs
+        return self
+
+    def __or__(self, other):
+        copy = PkgSet(list(self.pkgs.values()))
+        return copy.__ior__(other)
+
+    def __ior__(self, other):
+        self.pkgs.update(other.pkgs)
+        return self
+
+    def __contains__(self, pkg):
+        return pkg.name in self.pkgs
+
+    def __iter__(self):
+        for value in self.pkgs.values():
+            yield value
+
+    def __len__(self):
+        return len(self.pkgs)
+
+
+class DownloadQueue():
+    """ Represents a download queue """
+
+    def __init__(self):
+        self.dbs = list()
+        self.sync_pkgs = list()
+
+    def __bool__(self):
+        return bool(self.dbs or self.sync_pkgs)
+
+    def __nonzero__(self):
+        return self.dbs or self.sync_pkgs
+
+    def add_db(self, database, sigs=False):
+        """ Adds db info and signatures to the queue """
+        self.dbs.append((database, sigs))
+
+    def add_sync_pkg(self, pkg, urls, sigs=False):
+        """ Adds package and its urls to the queue """
+        self.sync_pkgs.append((pkg, urls, sigs))
+
+
+def parse_args(args):
+    """ Parse arguments to build_download_queue function
+        These arguments mimic pacman ones """
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument('pkgs', nargs='*', default=[], metavar='<pkgname>',
+                        help='Packages or groups to download.')
+    parser.add_argument('--all-deps', action='store_true', dest='alldeps',
+                        help='Include all dependencies even if they are already installed.')
+    parser.add_argument('-c', '--conf', metavar='<path>', default='/etc/pacman.conf', dest='conf',
+                        help='Use a different pacman.conf file.')
+    parser.add_argument('--noconfirm', action='store_true', dest='noconfirm',
+                        help='Suppress user prompts.')
+    parser.add_argument('-d', '--nodeps', action='store_true', dest='nodeps',
+                        help='Skip dependencies.')
+    parser.add_argument('--needed', action='store_true', dest='needed',
+                        help='Skip packages if they already exist in the cache.')
+    help_msg = '''Include signature files for repos with optional and required SigLevels.
+        Pass this flag twice to attempt to download signature for all databases and packages.'''
+    parser.add_argument('-s', '--sigs', action='count', default=0, dest='sigs',
+                        help=help_msg)
+    parser.add_argument('-y', '--databases', '--refresh', action='store_true', dest='db',
+                        help='Download databases.')
+
+    return parser.parse_args(args)
+
+
+def get_antergos_repo_pkgs(alpm_handle):
+    """ Returns pkgs from Antergos groups (mate, mate-extra) and
+        the antergos db info """
+
+    antdb = None
+    for database in alpm_handle.get_syncdbs():
+        if database.name == 'Reborn-OS':
+            antdb = database
+            break
+
+    if not antdb:
+        logging.error("Cannot sync Antergos repository database!")
+        return {}, None
+
+    group_names = ['mate', 'mate-extra']
+    groups = []
+    for group_name in group_names:
+        group = antdb.read_grp(group_name)
+        if not group:
+            # Group does not exist
+            group = ['None', []]
+        groups.append(group)
+
+    repo_pkgs = {
+        pkg for group in groups
+        for pkg in group[1] if group}
+
+    return repo_pkgs, antdb
+
+
+def resolve_deps(alpm_handle, other, alldeps):
+    """ Resolve dependencies """
+    missing_deps = []
+    queue = deque(other)
+    local_cache = alpm_handle.get_localdb().pkgcache
+    syncdbs = alpm_handle.get_syncdbs()
+    seen = set(queue)
+    while queue:
+        pkg = queue.popleft()
+        for dep in pkg.depends:
+            if pyalpm.find_satisfier(local_cache, dep) is None or alldeps:
+                for database in syncdbs:
+                    prov = pyalpm.find_satisfier(database.pkgcache, dep)
+                    if prov:
+                        other.add(prov)
+                        if prov.name not in seen:
+                            seen.add(prov.name)
+                            queue.append(prov)
+                        break
+                else:
+                    missing_deps.append(dep)
+    return other, missing_deps
+
+
+def create_package_set(requested, ant_repo_pkgs, antdb, alpm_handle):
+    """ Create package set from requested set """
+
+    found = set()
+    other = PkgSet()
+
+    for pkg in requested:
+        for database in alpm_handle.get_syncdbs():
+            # if pkg is in antergos repo, fetch it from it (instead of another repo)
+            # pkg should be sourced from the antergos repo only.
+            if antdb and pkg in ant_repo_pkgs and database.name != 'Reborn-OS':
+                database = antdb
+
+            syncpkg = database.get_pkg(pkg)
+
+            if syncpkg:
+                other.add(syncpkg)
+                break
+            else:
+                syncgrp = database.read_grp(pkg)
+                if syncgrp:
+                    found.add(pkg)
+                    #other_grp |= PkgSet(syncgrp[1])
+                    other |= PkgSet(syncgrp[1])
+                    break
+
+    return found, other
+
+def build_download_queue(alpm, args=None):
+    """ Function to build a download queue.
+        Needs a pkgname in args """
+
+    pargs = parse_args(args)
+
+    requested = set(pargs.pkgs)
+
+    handle = alpm.get_handle()
+    conf = alpm.get_config()
+
+    missing_deps = list()
+
+    ant_repo_pkgs, antdb = get_antergos_repo_pkgs(handle)
+
+    if not antdb:
+        logging.error("Cannot load antergos repository database")
+        return None, None, None
+
+    found, other = create_package_set(requested, ant_repo_pkgs, antdb, handle)
+
+    # foreign_names = requested - set(x.name for x in other)
+
+    # Resolve dependencies.
+    if other and not pargs.nodeps:
+        other, missing_deps = resolve_deps(handle, other, pargs.alldeps)
+
+    found |= set(other.pkgs)
+    not_found = requested - found
+    if pargs.needed:
+        other = PkgSet(list(check_cache(conf, other)))
+
+    # Create download queue
+    download_queue = DownloadQueue()
+
+    # Add databases (and their signature)
+    if pargs.db:
+        for database in handle.get_syncdbs():
+            try:
+                siglevel = conf[database.name]['SigLevel'].split()[0]
+            except KeyError:
+                siglevel = None
+            download_sig = needs_sig(siglevel, pargs.sigs, 'Database')
+            download_queue.add_db(database, download_sig)
+
+    # Add packages (pkg, url, signature)
+    for pkg in other:
+        try:
+            siglevel = conf[pkg.db.name]['SigLevel'].split()[0]
+        except KeyError:
+            siglevel = None
+        download_sig = needs_sig(siglevel, pargs.sigs, 'Package')
+
+        urls = []
+        server_urls = list(pkg.db.servers)
+        for server_url in server_urls:
+            url = os.path.join(server_url, pkg.filename)
+            urls.append(url)
+
+        # Limit to MAX_URLS url
+        while len(urls) > MAX_URLS:
+            urls.pop()
+
+        download_queue.add_sync_pkg(pkg, urls, download_sig)
+
+    return download_queue, not_found, missing_deps
+
+
+def get_checksum(path, typ):
+    """ Returns checksum of a file """
+    new_hash = hashlib.new(typ)
+    block_size = new_hash.block_size
+    try:
+        with open(path, 'rb') as myfile:
+            buf = myfile.read(block_size)
+            while buf:
+                new_hash.update(buf)
+                buf = myfile.read(block_size)
+        return new_hash.hexdigest()
+    except FileNotFoundError:
+        return -1
+    except IOError as io_error:
+        logging.error(io_error)
+
+
+def check_cache(conf, pkgs):
+    """ Checks package checksum in cache """
+    for pkg in pkgs:
+        for cache in conf.options['CacheDir']:
+            fpath = os.path.join(cache, pkg.filename)
+            for checksum in ('sha256', 'md5'):
+                real_checksum = get_checksum(fpath, checksum)
+                correct_checksum = getattr(pkg, checksum + 'sum')
+                if real_checksum is None or real_checksum != correct_checksum:
+                    yield pkg
+                    break
+            else:
+                continue
+            break
+
+
+def needs_sig(siglevel, insistence, prefix):
+    """ Determines if a signature should be downloaded.
+        The siglevel is the pacman.conf SigLevel for the given repo.
+        The insistence is an integer. Anything below 1 will return false,
+        anything above 1 will return true, and 1 will check if the
+        siglevel is required or optional.
+        The prefix is either "Database" or "Package". """
+
+    if insistence > 1:
+        return True
+    elif insistence == 1 and siglevel:
+        for sl_type in ('Required', 'Optional'):
+            if siglevel == sl_type or siglevel == prefix + sl_type:
+                return True
+    return False
+
+
+def test_module():
+    """ Module test function """
+    import gettext
+
+    _ = gettext.gettext
+
+    formatter = logging.Formatter(
+        '[%(asctime)s] [%(module)s] %(levelname)s: %(message)s',
+        "%Y-%m-%d %H:%M:%S.%f")
+    logger = logging.getLogger()
+    logger.setLevel(logging.DEBUG)
+    stream_handler = logging.StreamHandler()
+    stream_handler.setLevel(logging.DEBUG)
+    stream_handler.setFormatter(formatter)
+    logger.addHandler(stream_handler)
+
+    #import gc
+    #import pprint
+    import sys
+    cnchi_path = "/usr/share/cnchi"
+    sys.path.append(cnchi_path)
+    sys.path.append(os.path.join(cnchi_path, "src"))
+    import pacman.pac as pac
+
+    pacman = pac.Pac(
+        conf_path="/etc/pacman.conf",
+        callback_queue=None)
+
+    print("Creating metalink...")
+    meta4 = create(
+        alpm=pacman,
+        #package_name="ipw2200-fw",
+        package_name="base-devel",
+        pacman_conf_file="/etc/pacman.conf")
+    #print(meta4)
+    #print('=' * 20)
+    if meta4:
+        print(get_info(meta4))
+    # print(get_info(meta4)['ipw2200-fw']['urls'])
+
+    pacman.release()
+    del pacman
+
+
+# Test case
+if __name__ == '__main__':
+    test_module()
diff --git a/Cnchi/mirrors.py b/Cnchi/mirrors.py
new file mode 100755 (executable)
index 0000000..5932b77
--- /dev/null
@@ -0,0 +1,474 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# mirrors.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 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/>.
+
+
+""" Let advanced users manage mirrorlist files """
+
+import logging
+import multiprocessing
+import shutil
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, Gdk, GObject
+
+try:
+    gi.require_foreign("cairo")
+except ImportError:
+    print("No pycairo integration")
+
+import cairo
+from pages.gtkbasebox import GtkBaseBox
+from rank_mirrors import RankMirrors
+
+if __name__ == '__main__':
+    import sys
+    sys.path.append('/usr/share/cnchi/src')
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+
+class MirrorListBoxRow(Gtk.ListBoxRow):
+    """ Represents a mirror """
+    def __init__(self, url, active, switch_cb, drag_cbs):
+        super(Gtk.ListBoxRow, self).__init__()
+        #self.data = data
+        # self.add(Gtk.Label(data))
+
+        self.data = url
+
+        box = Gtk.Box(spacing=20)
+
+        self.handle = Gtk.EventBox.new()
+        self.handle.add(Gtk.Image.new_from_icon_name("open-menu-symbolic", 1))
+        box.pack_start(self.handle, False, False, 0)
+
+        # Add mirror url label
+        self.label = Gtk.Label.new()
+        self.label.set_halign(Gtk.Align.START)
+        self.label.set_justify(Gtk.Justification.LEFT)
+        self.label.set_name(url)
+        # Only show site address
+        url_parts = url.split('/')
+        text_url = url_parts[0] + "//" + url_parts[2]
+        self.label.set_text(text_url)
+        box.pack_start(self.label, False, True, 0)
+
+        # Add mirror switch
+        self.switch = Gtk.Switch.new()
+        self.switch.set_name("switch_" + url)
+        self.switch.set_property('margin_top', 2)
+        self.switch.set_property('margin_bottom', 2)
+        self.switch.set_property('margin_end', 10)
+        self.switch.connect("notify::active", switch_cb)
+        self.switch.set_active(active)
+        box.pack_end(self.switch, False, False, 0)
+
+        self.add(box)
+
+        self.set_selectable(True)
+
+        # Drag and drop
+        # Source
+        self.handle.drag_source_set(
+            Gdk.ModifierType.BUTTON1_MASK, [], Gdk.DragAction.MOVE)
+        self.handle.drag_source_add_text_targets()
+        self.handle.connect("drag-begin", drag_cbs['drag-begin'])
+        self.handle.connect("drag-data-get", drag_cbs['drag-data-get'])
+        #self.handle.connect("drag-data-delete", self.drag_data_delete)
+        #self.handle.connect("drag-end", self.drag_end)
+
+        # Destination
+        self.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.MOVE)
+        self.drag_dest_add_text_targets()
+        self.connect("drag-data-received", drag_cbs['drag-data-received'])
+        #self.connect("drag-motion", self.drag_motion);
+        #self.connect("drag-crop", self.drag_crop);
+
+    def is_active(self):
+        """ Returns if the mirror is active """
+        return self.switch.get_active()
+
+
+class MirrorListBox(Gtk.ListBox):
+    """ List that stores all mirrors """
+    __gsignals__ = {
+        'switch-activated': (GObject.SignalFlags.RUN_FIRST, None, ())
+    }
+
+    # 6 mirrors for Arch repos and 6 for Antergos repos
+    MAX_MIRRORS = 7
+    # DND_ID_LISTBOX_ROW = 6791
+
+    def __init__(self, mirrors_file_path, settings):
+        super(Gtk.ListBox, self).__init__()
+        self.mirrors_file_path = mirrors_file_path
+        self.set_selection_mode(Gtk.SelectionMode.NONE)
+        # self.set_selection_mode(Gtk.SelectionMode.BROWSE)
+        # self.connect("row-selected", self.on_listbox_row_selected)
+        # self.sort_func(self.listbox_sort_by_name, None)
+
+        self.settings = settings
+
+        # List. Each element is a tuple (url, active)
+        self.mirrors = []
+
+        self.load_mirrors()
+        self.fillme()
+
+    @staticmethod
+    def uncommented_mirrors_first(mirrors):
+        """ Put uncommented mirrors first. Order is not guaranteed """
+        commented = []
+        uncommented = []
+        for mirror in mirrors:
+            if mirror.startswith("#"):
+                commented.append(mirror)
+            else:
+                uncommented.append(mirror)
+        return uncommented + commented
+
+    def load_mirrors(self):
+        """ Load mirrors from text file """
+        lines = []
+
+        # Load mirror file contents
+        with open(self.mirrors_file_path) as mfile:
+            lines = mfile.readlines()
+
+        # Discard lines that are not server lines
+        tmp_lines = lines
+        lines = []
+        for line in tmp_lines:
+            line = line.strip()
+            if line.startswith("Server") or line.startswith("#Server"):
+                lines.append(line)
+        tmp_lines = []
+
+        lines = self.uncommented_mirrors_first(lines)
+
+        # Use MAX_MIRRORS at max
+        if len(lines) > MirrorListBox.MAX_MIRRORS:
+            lines = lines[0:MirrorListBox.MAX_MIRRORS]
+
+        # Read mirror info and create mirrors list
+        for line in lines:
+            if line.startswith("#Server"):
+                active = False
+                line = line[1:]
+            else:
+                active = True
+
+            try:
+                url = line.split("=")[1].strip()
+                logging.debug(url)
+                self.mirrors.append((url, active))
+            except KeyError:
+                pass
+
+    def fillme(self):
+        """ Fill listbox with mirrors info """
+        for listboxrow in self.get_children():
+            listboxrow.destroy()
+
+        drag_cbs = {
+            'drag-begin': self.drag_begin,
+            'drag-data-get': self.drag_data_get,
+            'drag-data-received': self.drag_data_received
+        }
+
+        for (url, active) in self.mirrors:
+            box = Gtk.Box(spacing=20)
+            box.set_name(url)
+            row = MirrorListBoxRow(url, active, self.switch_activated, drag_cbs)
+            self.add(row)
+
+    def set_mirror_active(self, url, active):
+        """ Changes the active status in our mirrors list """
+        for index, item in enumerate(self.mirrors):
+            murl, _mact = item
+            if url == murl:
+                self.mirrors[index] = (url, active)
+
+    def get_active_mirrors(self):
+        """ Returns a list with all active mirrors """
+        active_mirrors = []
+        for (url, active) in self.mirrors:
+            if active:
+                active_mirrors.append(url)
+        return active_mirrors
+
+    def switch_activated(self, switch, _gparam):
+        """ Mirror activated """
+        row = switch.get_ancestor(Gtk.ListBoxRow)
+        if row:
+            self.set_mirror_active(row.data, switch.get_active())
+            self.emit("switch-activated")
+
+    def drag_begin(self, widget, drag_context):
+        """ User starts a drag """
+        row = widget.get_ancestor(Gtk.ListBoxRow)
+        alloc = row.get_allocation()
+        surface = cairo.ImageSurface(
+            cairo.FORMAT_ARGB32, alloc.width, alloc.height)
+        ctx = cairo.Context(surface)
+
+        row.get_style_context().add_class("drag-icon")
+        row.draw(ctx)
+        row.get_style_context().remove_class("drag-icon")
+
+        pos_x, pos_y = widget.translate_coordinates(row, 0, 0)
+
+        surface.set_device_offset(-pos_x, -pos_y)
+        Gtk.drag_set_icon_surface(drag_context, surface)
+
+        hand_cursor = Gdk.Cursor(Gdk.CursorType.HAND1)
+        self.get_window().set_cursor(hand_cursor)
+
+    def drag_data_get(self, widget, _drag_context, selection_data, _info, _time):
+        """ When drag data is requested by the destination """
+        row = widget.get_ancestor(Gtk.ListBoxRow)
+        listbox_str = str(self)
+        row_index = row.get_index()
+        data = "{0}|{1}".format(listbox_str, row_index)
+        selection_data.set_text(data, len(data))
+        self.get_window().set_cursor(None)
+
+    def drag_data_received(
+        self, widget, _drag_context, _pos_x, _pos_y, selection_data, _info, _time):
+        """ When drag data is received by the destination """
+        data = selection_data.get_text()
+        try:
+            listbox_str = data.split('|')[0]
+            if listbox_str == str(self):
+                old_index = int(data.split('|')[1])
+                new_index = widget.get_index()
+                self.mirrors.insert(new_index, self.mirrors.pop(old_index))
+                self.fillme()
+                self.show_all()
+        except (KeyError, ValueError) as err:
+            logging.warning(err)
+
+    @staticmethod
+    def trim_mirror_url(server_line):
+        """ Get url from full mirrorlist line """
+
+        if not server_line.startswith("Server") or '=' not in server_line:
+            return server_line
+
+        # Get url part
+        url = server_line.split('=')[1].strip()
+
+        # Remove end part to get the FDQN only
+        url = url.replace("/$repo/os/$arch", "")
+
+        return url
+
+    def save_changes(self, use_rankmirrors=False):
+        """ Save mirrors in mirrors list file """
+        # Save a backup if possible
+        src = self.mirrors_file_path
+        dst = src + ".cnchi-backup"
+
+        try:
+            shutil.copy2(src, dst)
+        except (FileNotFoundError, FileExistsError, OSError) as err:
+            logging.warning(err)
+
+        # ok, now save our changes
+        with open(src, 'w') as mfile:
+            line = "# Mirrorlist file modified by Cnchi\n\n"
+            mfile.write(line)
+            for (url, active) in self.mirrors:
+                line = "Server = {}\n".format(url)
+                if not active:
+                    line = "#" + line
+                mfile.write(line)
+
+        # If this is the Arch mirrorlist and we are not using rankmirrors,
+        # we need to save it to rankmirrors_result in settings
+        arch_mirrors = []
+        if not use_rankmirrors and self.mirrors_file_path == "/etc/pacman.d/mirrorlist":
+            for (url, active) in self.mirrors:
+                if active:
+                    arch_mirrors.append(self.trim_mirror_url(url))
+            self.settings.set('rankmirrors_result', arch_mirrors)
+
+
+class Mirrors(GtkBaseBox):
+    """ Page that shows mirrolists so the user can arrange them manually """
+    """ Remove antergos-mirrorlist, and add reborn-mirrorlist (Rafael) """
+
+    MIRRORLISTS = [
+        "/etc/pacman.d/mirrorlist",
+        "/etc/pacman.d/reborn-mirrorlist"]
+
+    def __init__(self, params, prev_page="cache", next_page="installation_ask"):
+        super().__init__(self, params, "mirrors", prev_page, next_page)
+
+        # Set up lists
+        self.listboxes = []
+        self.scrolledwindows = []
+
+        self.scrolledwindows.append(self.gui.get_object("scrolledwindow1"))
+        self.scrolledwindows.append(self.gui.get_object("scrolledwindow2"))
+
+        for mirror_file in Mirrors.MIRRORLISTS:
+            mirror_listbox = MirrorListBox(mirror_file, self.settings)
+            mirror_listbox.connect("switch-activated", self.switch_activated)
+            self.listboxes.append(mirror_listbox)
+
+        for index, scrolled_window in enumerate(self.scrolledwindows):
+            scrolled_window.add(self.listboxes[index])
+
+        self.listboxes_box = self.gui.get_object("listboxes_box")
+
+        self.use_rankmirrors = True
+        self.use_listboxes = False
+
+        # Boolean variable to check if rank_mirrors has already been run
+        self.rank_mirrors_launched = False
+
+    def switch_activated(self, _widget):
+        """ A mirror has been activated/deactivated. We must check if
+        at least there is one mirror active for each list """
+        self.check_active_mirrors()
+
+    def check_active_mirrors(self):
+        """ Checks if at least there is one mirror active for each list """
+        ok_to_proceed = True
+        for listbox in self.listboxes:
+            if not listbox.get_active_mirrors():
+                ok_to_proceed = False
+        self.forward_button.set_sensitive(ok_to_proceed)
+
+    def rank_radiobutton_toggled(self, _widget):
+        """ Use rankmirrors """
+        self.use_rankmirrors = True
+        self.use_listboxes = False
+        self.forward_button.set_sensitive(True)
+        # self.listboxes_box.hide()
+        self.listboxes_box.set_sensitive(False)
+
+    def leave_radiobutton_toggled(self, _widget):
+        """ Do not modify mirror lists """
+        self.use_rankmirrors = False
+        self.use_listboxes = False
+        self.forward_button.set_sensitive(True)
+        # self.listboxes_box.hide()
+        self.listboxes_box.set_sensitive(False)
+
+    def user_radiobutton_toggled(self, _widget):
+        """ Let user choose mirrorlist ordering """
+        self.use_rankmirrors = False
+        self.use_listboxes = True
+        self.show_all()
+        self.check_active_mirrors()
+        self.listboxes_box.set_sensitive(True)
+
+    def start_rank_mirrors(self):
+        """ Launch rank mirrors process to optimize Arch and Antergos mirrorlists
+            As user can come and go from/to this screen, we must get sure he/she
+            has not already run Rankmirrors before """
+
+        if not self.rank_mirrors_launched:
+            logging.debug("Cnchi is ranking your mirrors lists...")
+            parent_conn, child_conn = multiprocessing.Pipe(duplex=False)
+            # Store parent_conn for later use in summary.py (rankmirrors wait dialog)
+            #proc = RankMirrors(fraction_pipe=child_conn)
+            proc = RankMirrors(child_conn, self.settings)
+            proc.daemon = True
+            proc.name = "rankmirrors"
+            proc.start()
+            self.rank_mirrors_launched = True
+            self.settings.set('rankmirrors_pipe', parent_conn)
+
+    def prepare(self, direction):
+        """ Prepares screen """
+        self.translate_ui()
+        self.show_all()
+        # self.listboxes_box.hide()
+        self.listboxes_box.set_sensitive(False)
+        self.forward_button.set_sensitive(True)
+
+    def translate_ui(self):
+        """ Translates screen before showing it """
+        self.header.set_subtitle(_("Mirrors Selection"))
+
+        self.forward_button.set_always_show_image(True)
+        self.forward_button.set_sensitive(True)
+
+        #bold_style = '<span weight="bold">{0}</span>'
+
+        radio = self.gui.get_object("rank_radiobutton")
+        txt = _("Let Cnchi sort the mirrors lists (recommended)")
+        radio.set_label(txt)
+        radio.set_name('rank_radio_btn')
+
+        radio = self.gui.get_object("leave_radiobutton")
+        txt = _("Leave the mirrors lists as they are")
+        radio.set_label(txt)
+        radio.set_name('leave_radio_btn')
+
+        radio = self.gui.get_object("user_radiobutton")
+        txt = _("Manage the mirrors lists manually (advanced)")
+        radio.set_label(txt)
+        radio.set_name('user_radio_btn')
+
+        intro_txt = _("How would you like to proceed?")
+        intro_label = self.gui.get_object("introduction")
+        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(80)
+
+        lbl = self.gui.get_object("arch_mirrors_label")
+        lbl.set_text(_("Arch Mirrors"))
+
+        lbl = self.gui.get_object("antergos_mirrors_label")
+        lbl.set_text(_("RebornOS Mirrors"))
+
+    def store_values(self):
+        """ Store selected values """
+        if self.use_rankmirrors:
+            self.start_rank_mirrors()
+        if self.use_listboxes:
+            for listbox in self.listboxes:
+                listbox.save_changes(self.use_rankmirrors)
+        return True
+
+    def get_next_page(self):
+        """ Returns next page """
+        return self.next_page
diff --git a/Cnchi/pac.py b/Cnchi/pac.py
new file mode 100755 (executable)
index 0000000..fe4df51
--- /dev/null
@@ -0,0 +1,640 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#  pac.py
+#
+#  This code is based on previous work by Rémy Oudompheng <remy@archlinux.org>
+#
+#  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 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/>.
+
+
+""" Module interface to pyalpm """
+
+from collections import OrderedDict
+
+import logging
+import os
+import sys
+
+from misc.events import Events
+
+import pacman.alpm_include as _alpm
+import pacman.pkginfo as pkginfo
+import pacman.pacman_conf as config
+
+try:
+    import pyalpm
+except ImportError as err:
+    # This is already logged elsewhere
+    # logging.error(err)
+    pass
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+_DEFAULT_ROOT_DIR = "/"
+_DEFAULT_DB_PATH = "/var/lib/pacman"
+
+
+class Pac():
+    """ Communicates with libalpm using pyalpm """
+    LOG_FOLDER = '/var/log/cnchi'
+
+    def __init__(self, conf_path="/etc/pacman.conf", callback_queue=None):
+        self.events = Events(callback_queue)
+
+        self.conflict_to_remove = None
+
+        self.handle = None
+
+        self.logger = None
+        self.setup_logger()
+
+        # Some download indicators (used in cb_dl callback)
+        self.last_target = ""
+        self.last_percent = 0
+        self.already_transferred = 0
+        # Store package total download size
+        self.total_size = 0
+        # Store last action
+        self.last_action = ""
+
+        # Total packages to download
+        self.total_packages_to_download = 0
+        self.downloaded_packages = 0
+
+        self.last_event = {}
+
+        if not os.path.exists(conf_path):
+            raise pyalpm.error
+
+        if conf_path is not None and os.path.exists(conf_path):
+            self.config = config.PacmanConfig(conf_path)
+            self.initialize_alpm()
+            logging.debug('ALPM repository database order is: %s',
+                          self.config.repo_order)
+        else:
+            raise pyalpm.error
+
+    @staticmethod
+    def format_size(size):
+        """ Formats downloaded size into a string """
+        kib_size = size / 1024
+        if kib_size < 1000:
+            size_string = _('%.1f KiB') % (kib_size)
+            return size_string
+        size_string = _('%.2f MiB') % (kib_size / 1024)
+        return size_string
+
+    def get_handle(self):
+        """ Return alpm handle """
+        return self.handle
+
+    def get_config(self):
+        """ Get pacman.conf config """
+        return self.config
+
+    def initialize_alpm(self):
+        """ Set alpm setup """
+        if self.config is not None:
+            root_dir = self.config.options["RootDir"]
+            db_path = self.config.options["DBPath"]
+        else:
+            root_dir = _DEFAULT_ROOT_DIR
+            db_path = _DEFAULT_DB_PATH
+
+        self.handle = pyalpm.Handle(root_dir, db_path)
+
+        logging.debug(
+            "ALPM initialised with root dir %s and db path %s", root_dir, db_path)
+
+        if self.handle is None:
+            raise pyalpm.error
+
+        if self.config is not None:
+            self.config.apply(self.handle)
+
+        # Set callback functions
+        # Callback used for logging
+        self.handle.logcb = self.cb_log
+        # Callback used to report download progress
+        self.handle.dlcb = self.cb_dl
+        # Callback used to report total download size
+        self.handle.totaldlcb = self.cb_totaldl
+        # Callback used for events
+        self.handle.eventcb = self.cb_event
+        # Callback used for questions
+        self.handle.questioncb = self.cb_question
+        # Callback used for operation progress
+        self.handle.progresscb = self.cb_progress
+        # Downloading callback
+        self.handle.fetchcb = None
+
+    def release(self):
+        """ Release alpm handle """
+        if self.handle is not None:
+            del self.handle
+            self.handle = None
+
+    @staticmethod
+    def finalize_transaction(transaction):
+        """ Commit a transaction """
+        try:
+            logging.debug("Prepare alpm transaction...")
+            transaction.prepare()
+            logging.debug("Commit alpm transaction...")
+            transaction.commit()
+        except pyalpm.error as err:
+            logging.error("Can't finalize alpm transaction: %s", err)
+            transaction.release()
+            return False
+        transaction.release()
+        logging.debug("Alpm transaction done.")
+        return True
+
+    def init_transaction(self, options=None):
+        """ Transaction initialization """
+        if options is None:
+            options = {}
+
+        transaction = None
+        try:
+            transaction = self.handle.init_transaction(
+                nodeps=options.get('nodeps', False),
+                dbonly=options.get('dbonly', False),
+                force=options.get('force', False),
+                needed=options.get('needed', False),
+                alldeps=(options.get('mode', None) ==
+                         pyalpm.PKG_REASON_DEPEND),
+                allexplicit=(options.get('mode', None) ==
+                             pyalpm.PKG_REASON_EXPLICIT),
+                cascade=options.get('cascade', False),
+                nosave=options.get('nosave', False),
+                recurse=(options.get('recursive', 0) > 0),
+                recurseall=(options.get('recursive', 0) > 1),
+                unneeded=options.get('unneeded', False),
+                downloadonly=options.get('downloadonly', False))
+        except pyalpm.error as pyalpm_error:
+            logging.error("Can't init alpm transaction: %s", pyalpm_error)
+        return transaction
+
+    def remove(self, pkg_names, options=None):
+        """ Removes a list of package names """
+
+        if not options:
+            options = {}
+
+        # Prepare target list
+        targets = []
+        database = self.handle.get_localdb()
+        for pkg_name in pkg_names:
+            pkg = database.get_pkg(pkg_name)
+            if pkg is None:
+                logging.error("Target %s not found", pkg_name)
+                return False
+            targets.append(pkg)
+
+        transaction = self.init_transaction(options)
+
+        if transaction is None:
+            logging.error("Can't init transaction")
+            return False
+
+        for pkg in targets:
+            logging.debug(
+                "Adding package '%s' to remove transaction", pkg.name)
+            transaction.remove_pkg(pkg)
+
+        return self.finalize_transaction(transaction)
+
+    def refresh(self):
+        """ Sync databases like pacman -Sy """
+        if self.handle is None:
+            logging.error("alpm is not initialised")
+            raise pyalpm.error
+
+        force = True
+        res = True
+        for database in self.handle.get_syncdbs():
+            transaction = self.init_transaction()
+            if transaction:
+                database.update(force)
+                transaction.release()
+            else:
+                res = False
+        return res
+
+    def install(self, pkgs, conflicts=None, options=None):
+        """ Install a list of packages like pacman -S """
+
+        if not conflicts:
+            conflicts = []
+
+        if not options:
+            options = {}
+
+        if self.handle is None:
+            logging.error("alpm is not initialised")
+            raise pyalpm.error
+
+        if not pkgs:
+            logging.error("Package list is empty")
+            raise pyalpm.error
+
+        # Discard duplicates
+        pkgs = list(set(pkgs))
+
+        # `alpm.handle.get_syncdbs()` returns a list (the order is important) so we
+        # have to ensure we don't clobber the priority of the repos.
+        repos = OrderedDict()
+        repo_order = []
+        db_match = [db for db in self.handle.get_syncdbs()
+                    if db.name == 'Reborn-OS']
+        antdb = OrderedDict()
+        antdb['Reborn-OS'] = db_match[0]
+
+        one_repo_groups_names = ['cinnamon', 'mate', 'mate-extra']
+        one_repo_groups = []
+        for one_repo_group_name in one_repo_groups_names:
+            grp = antdb['Reborn-OS'].read_grp(one_repo_group_name)
+            if not grp:
+                # Group does not exist
+                grp = ['None', []]
+            one_repo_groups.append(grp)
+
+        one_repo_pkgs = {pkg for one_repo_group in one_repo_groups
+                         for pkg in one_repo_group[1] if one_repo_group}
+
+        for syncdb in self.handle.get_syncdbs():
+            repo_order.append(syncdb)
+            repos[syncdb.name] = syncdb
+
+        targets = []
+        for name in pkgs:
+            _repos = repos
+
+            if name in one_repo_pkgs:
+                # pkg should be sourced from the antergos repo only.
+                _repos = antdb
+
+            result_ok, pkg = self.find_sync_package(name, _repos)
+
+            if result_ok:
+                # Check that added package is not in our conflicts list
+                if pkg.name not in conflicts:
+                    targets.append(pkg.name)
+            else:
+                # Couldn't find the package, check if it's a group
+                group_pkgs = self.get_group_pkgs(name)
+                if group_pkgs is not None:
+                    # It's a group
+                    for group_pkg in group_pkgs:
+                        # Check that added package is not in our conflicts list
+                        # Ex: connman conflicts with netctl(openresolv),
+                        # which is installed by default with base group
+                        if group_pkg.name not in conflicts:
+                            targets.append(group_pkg.name)
+                else:
+                    # No, it wasn't neither a package nor a group. As we don't
+                    # know if this error is fatal or not, we'll register it and
+                    # we'll allow to continue.
+                    logging.error(
+                        "Can't find a package or group called '%s'", name)
+
+        # Discard duplicates
+        targets = list(set(targets))
+        logging.debug(targets)
+
+        if not targets:
+            logging.error("No targets found")
+            return False
+
+        num_targets = len(targets)
+        logging.debug("%d target(s) found", num_targets)
+
+        # Maybe not all this packages will be downloaded, but it's
+        # how many have to be there before starting the installation
+        self.total_packages_to_download = num_targets
+
+        transaction = self.init_transaction(options)
+
+        if transaction is None:
+            logging.error("Can't initialize alpm transaction")
+            return False
+
+        for _index in range(0, num_targets):
+            result_ok, pkg = self.find_sync_package(targets.pop(), repos)
+            if result_ok:
+                transaction.add_pkg(pkg)
+            else:
+                logging.warning(pkg)
+
+        return self.finalize_transaction(transaction)
+
+    def upgrade(self, pkgs, conflicts=None, options=None):
+        """ Install a list package tarballs like pacman -U """
+
+        conflicts = conflicts if conflicts else []
+        options = options if options else {}
+
+        if self.handle is None:
+            logging.error("alpm is not initialised")
+            raise pyalpm.error
+
+        if not pkgs:
+            logging.error("Package list is empty")
+            raise pyalpm.error
+
+        # Discard duplicates
+        pkgs = list(set(pkgs))
+
+        self.handle.get_localdb()
+
+        # Prepare targets list
+        targets = []
+        for tarball in pkgs:
+            pkg = self.handle.load_pkg(tarball)
+            targets.append(pkg)
+
+        transaction = self.init_transaction(options)
+
+        if transaction is None:
+            logging.error("Can't initialize alpm transaction")
+            return False
+
+        for pkg in targets:
+            transaction.add_pkg(pkg)
+
+        return self.finalize_transaction(transaction)
+
+    @staticmethod
+    def find_sync_package(pkgname, syncdbs):
+        """ Finds a package name in a list of DBs
+        :rtype : tuple (True/False, package or error message)
+        """
+        for database in syncdbs.values():
+            pkg = database.get_pkg(pkgname)
+            if pkg is not None:
+                return True, pkg
+        return False, "Package '{0}' was not found.".format(pkgname)
+
+    def get_group_pkgs(self, group):
+        """ Get group's packages """
+        for repo in self.handle.get_syncdbs():
+            grp = repo.read_grp(group)
+            if grp is not None:
+                _name, pkgs = grp
+                return pkgs
+        return None
+
+    def get_packages_info(self, pkg_names=None):
+        """ Get information about packages like pacman -Si """
+        if not pkg_names:
+            pkg_names = []
+        packages_info = {}
+        if not pkg_names:
+            # Store info from all packages from all repos
+            for repo in self.handle.get_syncdbs():
+                for pkg in repo.pkgcache:
+                    packages_info[pkg.name] = pkginfo.get_pkginfo(
+                        pkg,
+                        level=2,
+                        style='sync')
+        else:
+            repos = OrderedDict((database.name, database)
+                                for database in self.handle.get_syncdbs())
+            for pkg_name in pkg_names:
+                result_ok, pkg = self.find_sync_package(pkg_name, repos)
+                if result_ok:
+                    packages_info[pkg_name] = pkginfo.get_pkginfo(
+                        pkg,
+                        level=2,
+                        style='sync')
+                else:
+                    packages_info = {}
+                    logging.error(pkg)
+        return packages_info
+
+    def get_package_info(self, pkg_name):
+        """ Get information about packages like pacman -Si """
+        repos = OrderedDict((database.name, database)
+                            for database in self.handle.get_syncdbs())
+        result_ok, pkg = self.find_sync_package(pkg_name, repos)
+        if result_ok:
+            info = pkginfo.get_pkginfo(pkg, level=2, style='sync')
+        else:
+            logging.error(pkg)
+            info = {}
+        return info
+
+    # Callback functions
+
+    @staticmethod
+    def cb_question(*args):
+        """ Called to get user input """
+        pass
+
+    def cb_event(self, event, event_data):
+        """ Converts action ID to descriptive text and enqueues it to the events queue """
+        action = self.last_action
+
+        if event == _alpm.ALPM_EVENT_CHECKDEPS_START:
+            action = _('Checking dependencies...')
+        elif event == _alpm.ALPM_EVENT_FILECONFLICTS_START:
+            action = _('Checking file conflicts...')
+        elif event == _alpm.ALPM_EVENT_RESOLVEDEPS_START:
+            action = _('Resolving dependencies...')
+        elif _alpm.ALPM_EVENT_INTERCONFLICTS_START:
+            action = _('Checking inter conflicts...')
+        elif event == _alpm.ALPM_EVENT_PACKAGE_OPERATION_START:
+             # ALPM_EVENT_PACKAGE_OPERATION_START is shown in cb_progress
+            action = ''
+        elif event == _alpm.ALPM_EVENT_INTEGRITY_START:
+            action = _('Checking integrity...')
+            self.already_transferred = 0
+        elif event == _alpm.ALPM_EVENT_LOAD_START:
+            action = _('Loading packages files...')
+        elif event == _alpm.ALPM_EVENT_DELTA_INTEGRITY_START:
+            action = _("Checking target delta integrity...")
+        elif event == _alpm.ALPM_EVENT_DELTA_PATCHES_START:
+            action = _('Applying deltas to packages...')
+        elif event == _alpm.ALPM_EVENT_DELTA_PATCH_START:
+            action = _('Generating {} with {}...')
+            action = action.format(event_data[0], event_data[1])
+        elif event == _alpm.ALPM_EVENT_RETRIEVE_START:
+            action = _('Downloading files from the repositories...')
+        elif event == _alpm.ALPM_EVENT_DISKSPACE_START:
+            action = _('Checking available disk space...')
+        elif event == _alpm.ALPM_EVENT_KEYRING_START:
+            action = _('Checking keys in keyring...')
+        elif event == _alpm.ALPM_EVENT_KEY_DOWNLOAD_START:
+            action = _('Downloading missing keys into the keyring...')
+        elif event == _alpm.ALPM_EVENT_SCRIPTLET_INFO:
+            action = _('Configuring {}').format(self.last_target)
+
+        if action != self.last_action:
+            self.last_action = action
+            self.events.add('info', action)
+
+    def cb_log(self, level, line):
+        """ Log pyalpm warning and error messages.
+            Possible message types:
+            LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_FUNCTION """
+
+        # Strip ending '\n'
+        line = line.rstrip()
+
+        # Log everything to cnchi-alpm.log
+        self.logger.debug(line)
+
+        logmask = pyalpm.LOG_ERROR | pyalpm.LOG_WARNING
+
+        if not level & logmask:
+            # Only log errors and warnings
+            return
+
+        if level & pyalpm.LOG_ERROR:
+            logging.error(line)
+        elif level & pyalpm.LOG_WARNING:
+            logging.warning(line)
+        elif level & pyalpm.LOG_DEBUG or level & pyalpm.LOG_FUNCTION:
+            logging.debug(line)
+
+    def cb_progress(self, target, percent, total, current):
+        """ Shows install progress """
+        if target:
+            action = _("Installing {0} ({1}/{2})").format(target, current, total)
+            percent = current / total
+            self.events.add('info', action)
+        else:
+            percent = round(percent / 100, 2)
+
+        if target != self.last_target:
+            self.last_target = target
+
+        if percent != self.last_percent:
+            self.last_percent = percent
+            self.events.add('percent', percent)
+
+    def cb_totaldl(self, total_size):
+        """ Stores total download size for use in cb_dl and cb_progress """
+        self.total_size = total_size
+
+    def cb_dl(self, filename, transferred, total):
+        """ Shows downloading progress """
+        if filename.endswith('.db'):
+            action = _("Updating {0} database").format(filename.replace('.db', ''))
+        else:
+            action = _("Downloading {}...").format(filename.replace('.pkg.tar.xz', ''))
+
+        # target = self.last_target
+        percent = self.last_percent
+
+        if self.total_size > 0:
+            percent = round((transferred + self.already_transferred) / self.total_size, 2)
+        elif total > 0:
+            percent = round(transferred / total, 2)
+
+        if action != self.last_action:
+            self.last_action = action
+            self.events.add('info', action)
+
+        if percent != self.last_percent:
+            self.last_percent = percent
+            self.events.add('percent', percent)
+        elif transferred == total:
+            self.already_transferred += total
+            self.downloaded_packages += 1
+
+    def is_package_installed(self, package_name):
+        """ Check if package is already installed """
+        database = self.handle.get_localdb()
+        pkgs = database.search(*[package_name])
+        names = []
+        for pkg in pkgs:
+            names.append(pkg.name)
+        return bool(package_name in names)
+
+    def setup_logger(self):
+        """ Configure our logger """
+        self.logger = logging.getLogger(__name__)
+
+        self.logger.setLevel(logging.DEBUG)
+
+        self.logger.propagate = False
+
+        # Log format
+        formatter = logging.Formatter(
+            fmt="%(asctime)s [%(levelname)s] %(filename)s(%(lineno)d) %(funcName)s(): %(message)s")
+
+        if not self.logger.hasHandlers():
+            # File logger
+            try:
+                log_path = os.path.join(Pac.LOG_FOLDER, 'cnchi-alpm.log')
+                file_handler = logging.FileHandler(log_path, mode='w')
+                file_handler.setLevel(logging.DEBUG)
+                file_handler.setFormatter(formatter)
+                self.logger.addHandler(file_handler)
+            except PermissionError as permission_error:
+                print("Can't open ", log_path, " : ", permission_error)
+
+
+def test():
+    """ Test case """
+    import gettext
+    _ = gettext.gettext
+
+    formatter = logging.Formatter(
+        '[%(asctime)s] [%(module)s] %(levelname)s: %(message)s',
+        "%Y-%m-%d %H:%M:%S.%f")
+    logger = logging.getLogger()
+    logger.setLevel(logging.DEBUG)
+    stream_handler = logging.StreamHandler()
+    stream_handler.setLevel(logging.DEBUG)
+    stream_handler.setFormatter(formatter)
+    logger.addHandler(stream_handler)
+
+    try:
+        pacman = Pac("/etc/pacman.conf")
+    except pyalpm.error as ex:
+        print("Can't initialize pyalpm: ", ex)
+        sys.exit(1)
+
+    try:
+        pacman.refresh()
+    except pyalpm.error as err:
+        print("Can't update databases: ", err)
+        sys.exit(1)
+
+    # pacman_options = {"downloadonly": True}
+    # pacman.do_install(pkgs=["base"], conflicts=[], options=pacman_options)
+    pacman.release()
+
+
+if __name__ == "__main__":
+    test()
diff --git a/Cnchi/packages.xml b/Cnchi/packages.xml
new file mode 100755 (executable)
index 0000000..5d947cf
--- /dev/null
@@ -0,0 +1,693 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<cnchi>
+    <editions>
+        <edition name="common" description="Packages common to all DE and base">
+            <packages>
+                <pkgname>common-cosmic-reborn</pkgname>
+                <pkgname>full-base-cosmic-reborn</pkgname>
+                <pkgname>base</pkgname>
+                <pkgname>base-devel</pkgname>
+                <pkgname>rebornos-keyring</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="graphic" description="Common to all graphic desktops">
+            <packages>
+                <pkgname lib="gtk">dconf-editor</pkgname>
+                <!-- <pkgname>xorg-apps</pkgname> -->
+                <!-- <pkgname>xorg-fonts</pkgname> -->
+                <pkgname lang="ja">noto-fonts-cjk</pkgname>                
+                <pkgname lang="ja_JP">noto-fonts-cjk</pkgname>
+                <pkgname lang="ko">noto-fonts-cjk</pkgname>
+                <pkgname lang="ko_KR">noto-fonts-cjk</pkgname>
+                <pkgname lang="zh">noto-fonts-cjk</pkgname>
+                <pkgname lang="zh_CN">noto-fonts-cjk</pkgname>
+                <pkgname lang="zh_HK">noto-fonts-cjk</pkgname>
+                <pkgname lang="zh_SG">noto-fonts-cjk</pkgname>
+                <pkgname lang="zh_TW">noto-fonts-cjk</pkgname>
+                <pkgname lang="ko_KR">noto-fonts-cjk</pkgname>
+                <pkgname>graphic-cosmic-reborn</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="base" description="Without X">
+            <packages>
+                <pkgname>full-base-cosmic-reborn</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="Gnome" description="Gnome Desktop">
+            <packages>
+                <pkgname>gnome-cosmic-reborn</pkgname>
+                <pkgname nm='true' name='NetworkManager'>network-manager-applet</pkgname>
+                <pkgname>gnome</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="Apricity" description="Apricity Desktop">
+            <packages>
+                <pkgname>gnome-cosmic-reborn</pkgname>
+                <pkgname nm='true' name='NetworkManager'>network-manager-applet</pkgname>
+                <pkgname>gnome</pkgname>
+                <pkgname>apricity-theme</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="Cinnamon" description="Cinnamon desktop">
+            <packages>
+                <pkgname nm='true' name='NetworkManager'>network-manager-applet</pkgname>
+                <pkgname>cinnamon-cosmic-reborn</pkgname>
+                <pkgname>cinnamon</pkgname>
+                <!-- <pkgname>lightdm</pkgname> -->
+                <!-- <pkgname>lightdm-webkit2-greeter</pkgname> -->
+            </packages>
+        </edition>
+
+        <edition name="Pantheon" description="Pantheon desktop">
+            <packages>
+                <pkgname nm='true' name='NetworkManager'>network-manager-applet</pkgname>
+                <pkgname>elementary-cosmic-reborn</pkgname>
+                <pkgname>pantheon</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="Windows" description="Windows Interface">
+            <packages>
+                <pkgname nm='true' name='NetworkManager'>network-manager-applet</pkgname>
+                <pkgname>cinnamon-cosmic-reborn</pkgname>
+                <pkgname>cinnamon</pkgname>
+                <!-- <pkgname>lightdm</pkgname> -->
+                <!-- <pkgname>lightdm-webkit2-greeter</pkgname> -->
+                <pkgname>windows-theme</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="XFCE" description="XFCE Desktop">
+            <packages>
+                <pkgname>xfce-cosmic-reborn</pkgname>
+                <pkgname>xfce4-goodies</pkgname>
+                <pkgname>xfce4</pkgname>
+                <pkgname>sddm-theme-gracilis-git</pkgname>
+                <pkgname>sddm-config-editor-git</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="Mate" description="MATE Desktop">
+            <packages>
+                <pkgname>mate-cosmic-reborn</pkgname>
+                <pkgname>mate</pkgname>
+                <pkgname>mate-extra</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="openbox" description="Openbox Window Manager">
+            <packages>
+                <pkgname nm='true' name='NetworkManager'>network-manager-applet</pkgname>
+                <pkgname>openbox-cosmic-reborn</pkgname>
+                <pkgname>lxde-gtk3</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="KDE" description="KDE Desktop">
+            <packages>
+                <pkgname>kde-cosmic-reborn</pkgname>
+                <pkgname>plasma</pkgname>
+                <pkgname>kdebase</pkgname>
+                <pkgname>kdeutils</pkgname>
+            </packages>
+        </edition>
+
+        <!-- EXPERIMENTAL EDITIONS -->
+
+        <edition name="lxqt" description="LXQT Desktop">
+            <packages>
+                <pkgname>lxqt-cosmic-reborn</pkgname>
+                <pkgname>lxqt</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="Enlightenment" description="Enlightenment Desktop">
+            <packages>
+                <pkgname>enlightenment-cosmic-reborn</pkgname>
+                <pkgname>enlightenment</pkgname>
+                <!-- <pkgname nm='true' name='connman'>connman</pkgname> -->
+            </packages>
+        </edition>
+
+        <edition name="Budgie" description="Solus desktop">
+            <packages>
+                <pkgname>budgie-cosmic-reborn</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="i3" description="Tiling window manager">
+            <packages>
+                <pkgname>i3-cosmic-reborn</pkgname>
+            </packages>
+        </edition>
+
+        <edition name="Deepin" description="Deepin Desktop">
+           <packages>
+               <pkgname>deepin-cosmic-reborn</pkgname>
+               <!-- <pkgname nm='true' name='NetworkManager'>network-manager-applet</pkgname> -->
+               <pkgname>reborn-deepin-schemas</pkgname>
+               <!-- <pkgname>deepin-file-manager-root</pkgname> -->
+               <!-- <pkgname>flatplat-blue-theme</pkgname> -->
+               <pkgname dm='true' name='lightdm'>lightdm</pkgname>
+               <pkgname>deepin</pkgname>
+               <pkgname>deepin-extra</pkgname>
+           </packages>
+       </edition>
+    </editions>
+
+    <!-- User desired features -->
+    <features>
+        <feature name="a11y">
+            <pkgname>alsa-utils</pkgname>
+            <pkgname>bluez</pkgname>
+            <pkgname>bluez-libs</pkgname>
+            <!-- <pkgname>brltty-minimal</pkgname> -->
+            <pkgname>clonezilla</pkgname>
+            <pkgname>darkhttpd</pkgname>
+            <pkgname>ddrescue</pkgname>
+            <pkgname>dhclient</pkgname>
+            <pkgname>dnsmasq</pkgname>
+            <pkgname>elinks</pkgname>
+            <pkgname>espeak</pkgname>
+            <pkgname>espeakup</pkgname>
+            <pkgname>ethtool</pkgname>
+            <pkgname>gnu-netcat</pkgname>
+            <pkgname>gpm</pkgname>
+            <pkgname>irssi</pkgname>
+            <pkgname>lftp</pkgname>
+            <pkgname>mc</pkgname>
+            <pkgname>orca</pkgname>
+            <!-- <pkgname>python2-speechd</pkgname> -->
+            <pkgname>vim</pkgname>
+        </feature>
+        <feature name="aur">
+            <pkgname>yay</pkgname>
+            <pkgname>cmake</pkgname>
+            <pkgname>git</pkgname>
+            <pkgname>breezy</pkgname>
+        </feature>
+        <feature name="bluetooth">
+            <pkgname>bluez</pkgname>
+            <pkgname>bluez-utils</pkgname>
+            <pkgname>pulseaudio-bluetooth</pkgname>
+            <pkgname desktops='cinnamon,mate,openbox'>blueberry</pkgname>
+            <pkgname desktops='kde,lxqt'>bluedevil</pkgname>
+            <pkgname desktops='gnome'>gnome-bluetooth</pkgname>
+        </feature>
+        <feature name="broadcom">
+            <pkgname>broadcom-wl</pkgname>
+        </feature>
+        <feature name="cups">
+            <pkgname>cups</pkgname>
+            <pkgname>cups-pk-helper</pkgname>
+            <pkgname>gutenprint</pkgname>
+            <pkgname>foomatic-db</pkgname>
+            <pkgname>foomatic-db-engine</pkgname>
+            <pkgname>foomatic-db-nonfree</pkgname>
+            <pkgname>foomatic-db-gutenprint-ppds</pkgname>
+            <pkgname>foomatic-db-ppds</pkgname>
+            <pkgname>foomatic-db-nonfree-ppds</pkgname>
+            <pkgname>splix</pkgname>
+            <pkgname>gsfonts</pkgname>
+            <pkgname>hplip</pkgname>
+            <pkgname lib='qt'>print-manager</pkgname>
+            <pkgname lib='gtk'>system-config-printer</pkgname>
+            <!-- <pkgname lib='gtk'>python2-gnomekeyring</pkgname> -->
+            <!-- These two are required by hp-setup -->
+            <pkgname>python-pyqt5</pkgname>
+            <pkgname>python-gobject</pkgname>
+        </feature>
+        <feature name="chromium">
+            <pkgname>chromium</pkgname>
+            <pkgname>chromium-widevine</pkgname>
+        </feature>
+        <feature name="dropbox">
+            <pkgname>dropbox</pkgname>
+        </feature>
+        <feature name="firefox">
+            <pkgname>firefox</pkgname>
+              <pkgname lang="sq">firefox-i18n-sq</pkgname>
+              <pkgname lang="ar">firefox-i18n-ar</pkgname>
+              <pkgname lang="ast">firefox-i18n-ast</pkgname>
+              <pkgname lang="eu">firefox-i18n-eu</pkgname>
+              <pkgname lang="be">firefox-i18n-be</pkgname>
+              <pkgname lang="bn">firefox-i18n-bn</pkgname>
+              <pkgname lang="bs">firefox-i18n-bs</pkgname>
+              <pkgname lang="bg">firefox-i18n-bg</pkgname>
+              <pkgname lang="ca">firefox-i18n-ca</pkgname>
+              <pkgname lang="zh_CN">firefox-i18n-zh-cn</pkgname>
+              <pkgname lang="zh_TW">firefox-i18n-zh-tw</pkgname>
+              <pkgname lang="hr">firefox-i18n-hr</pkgname>
+              <pkgname lang="cs">firefox-i18n-cs</pkgname>
+              <pkgname lang="da">firefox-i18n-da</pkgname>
+              <pkgname lang="nl">firefox-i18n-nl</pkgname>
+              <pkgname lang="eo">firefox-i18n-eo</pkgname>
+              <pkgname lang="et">firefox-i18n-et</pkgname>
+              <pkgname lang="fi">firefox-i18n-fi</pkgname>
+              <pkgname lang="fr">firefox-i18n-fr</pkgname>
+              <pkgname lang="gl">firefox-i18n-gl</pkgname>
+              <pkgname lang="ka">firefox-i18n-ka</pkgname>
+              <pkgname lang="de">firefox-i18n-de</pkgname>
+              <pkgname lang="el">firefox-i18n-el</pkgname>
+              <pkgname lang="he">firefox-i18n-he</pkgname>
+              <pkgname lang="hi">firefox-i18n-hi-in</pkgname>
+              <pkgname lang="hu">firefox-i18n-hu</pkgname>
+              <pkgname lang="id">firefox-i18n-id</pkgname>
+              <pkgname lang="it">firefox-i18n-it</pkgname>
+              <pkgname lang="ja">firefox-i18n-ja</pkgname>
+              <pkgname lang="kk">firefox-i18n-kk</pkgname>
+              <pkgname lang="km">firefox-i18n-km</pkgname>
+              <pkgname lang="ko">firefox-i18n-ko</pkgname>
+              <pkgname lang="lv">firefox-i18n-lv</pkgname>
+              <pkgname lang="lt">firefox-i18n-lt</pkgname>
+              <pkgname lang="mk">firefox-i18n-mk</pkgname>
+              <pkgname lang="mr">firefox-i18n-mr</pkgname>
+              <pkgname lang="ne">firefox-i18n-ne-np</pkgname>
+              <pkgname lang="nb_NO">firefox-i18n-nb-no</pkgname>
+              <pkgname lang="nn_NO">firefox-i18n-nn-no</pkgname>
+              <pkgname lang="pl">firefox-i18n-pl</pkgname>
+              <pkgname lang="pt">firefox-i18n-pt-pt</pkgname>
+              <pkgname lang="pt_BR">firefox-i18n-pt-br</pkgname>
+              <pkgname lang="pa">firefox-i18n-pa-in</pkgname>
+              <pkgname lang="ro">firefox-i18n-ro</pkgname>
+              <pkgname lang="ru">firefox-i18n-ru</pkgname>
+              <pkgname lang="sr">firefox-i18n-sr</pkgname>
+              <pkgname lang="sk">firefox-i18n-sk</pkgname>
+              <pkgname lang="sl">firefox-i18n-sl</pkgname>
+              <pkgname lang="es">firefox-i18n-es-es</pkgname>
+              <pkgname lang="sv">firefox-i18n-sv-se</pkgname>
+              <pkgname lang="ta">firefox-i18n-ta</pkgname>
+              <pkgname lang="th">firefox-i18n-th</pkgname>
+              <pkgname lang="tr">firefox-i18n-tr</pkgname>
+              <pkgname lang="uk">firefox-i18n-uk</pkgname>
+              <pkgname lang="vi">firefox-i18n-vi</pkgname>
+              <pkgname lang="cy">firefox-i18n-cy</pkgname>
+        </feature>
+        <feature name="firefox-developer-edition">
+            <pkgname>firefox-developer-edition</pkgname>
+              <pkgname lang="sq">firefox-developer-edition-i18n-sq</pkgname>
+              <pkgname lang="ar">firefox-developer-edition-i18n-ar</pkgname>
+              <pkgname lang="ast">firefox-developer-edition-i18n-ast</pkgname>
+              <pkgname lang="eu">firefox-developer-edition-i18n-eu</pkgname>
+              <pkgname lang="be">firefox-developer-edition-i18n-be</pkgname>
+              <pkgname lang="bn">firefox-developer-edition-i18n-bn</pkgname>
+              <pkgname lang="bs">firefox-developer-edition-i18n-bs</pkgname>
+              <pkgname lang="bg">firefox-developer-edition-i18n-bg</pkgname>
+              <pkgname lang="ca">firefox-developer-edition-i18n-ca</pkgname>
+              <pkgname lang="zh_CN">firefox-developer-edition-i18n-zh-cn</pkgname>
+              <pkgname lang="zh_TW">firefox-developer-edition-i18n-zh-tw</pkgname>
+              <pkgname lang="hr">firefox-developer-edition-i18n-hr</pkgname>
+              <pkgname lang="cs">firefox-developer-edition-i18n-cs</pkgname>
+              <pkgname lang="da">firefox-developer-edition-i18n-da</pkgname>
+              <pkgname lang="nl">firefox-developer-edition-i18n-nl</pkgname>
+              <pkgname lang="eo">firefox-developer-edition-i18n-eo</pkgname>
+              <pkgname lang="et">firefox-developer-edition-i18n-et</pkgname>
+              <pkgname lang="fi">firefox-developer-edition-i18n-fi</pkgname>
+              <pkgname lang="fr">firefox-developer-edition-i18n-fr</pkgname>
+              <pkgname lang="gl">firefox-developer-edition-i18n-gl</pkgname>
+              <pkgname lang="ka">firefox-developer-edition-i18n-ka</pkgname>
+              <pkgname lang="de">firefox-developer-edition-i18n-de</pkgname>
+              <pkgname lang="el">firefox-developer-edition-i18n-el</pkgname>
+              <pkgname lang="he">firefox-developer-edition-i18n-he</pkgname>
+              <pkgname lang="hi">firefox-developer-edition-i18n-hi-in</pkgname>
+              <pkgname lang="hu">firefox-developer-edition-i18n-hu</pkgname>
+              <pkgname lang="id">firefox-developer-edition-i18n-id</pkgname>
+              <pkgname lang="it">firefox-developer-edition-i18n-it</pkgname>
+              <pkgname lang="ja">firefox-developer-edition-i18n-ja</pkgname>
+              <pkgname lang="kk">firefox-developer-edition-i18n-kk</pkgname>
+              <pkgname lang="km">firefox-developer-edition-i18n-km</pkgname>
+              <pkgname lang="ko">firefox-developer-edition-i18n-ko</pkgname>
+              <pkgname lang="lv">firefox-developer-edition-i18n-lv</pkgname>
+              <pkgname lang="lt">firefox-developer-edition-i18n-lt</pkgname>
+              <pkgname lang="mk">firefox-developer-edition-i18n-mk</pkgname>
+              <pkgname lang="mr">firefox-developer-edition-i18n-mr</pkgname>
+              <pkgname lang="ne">firefox-developer-edition-i18n-ne-np</pkgname>
+              <pkgname lang="nb_NO">firefox-developer-edition-i18n-nb-no</pkgname>
+              <pkgname lang="nn_NO">firefox-developer-edition-i18n-nn-no</pkgname>
+              <pkgname lang="pl">firefox-developer-edition-i18n-pl</pkgname>
+              <pkgname lang="pt">firefox-developer-edition-i18n-pt-pt</pkgname>
+              <pkgname lang="pt_BR">firefox-developer-edition-i18n-pt-br</pkgname>
+              <pkgname lang="pa">firefox-developer-edition-i18n-pa-in</pkgname>
+              <pkgname lang="ro">firefox-developer-edition-i18n-ro</pkgname>
+              <pkgname lang="ru">firefox-developer-edition-i18n-ru</pkgname>
+              <pkgname lang="sr">firefox-developer-edition-i18n-sr</pkgname>
+              <pkgname lang="sk">firefox-developer-edition-i18n-sk</pkgname>
+              <pkgname lang="sl">firefox-developer-edition-i18n-sl</pkgname>
+              <pkgname lang="es">firefox-developer-edition-i18n-es-es</pkgname>
+              <pkgname lang="sv">firefox-developer-edition-i18n-sv-se</pkgname>
+              <pkgname lang="ta">firefox-developer-edition-i18n-ta</pkgname>
+              <pkgname lang="th">firefox-developer-edition-i18n-th</pkgname>
+              <pkgname lang="tr">firefox-developer-edition-i18n-tr</pkgname>
+              <pkgname lang="uk">firefox-developer-edition-i18n-uk</pkgname>
+              <pkgname lang="vi">firefox-developer-edition-i18n-vi</pkgname>
+              <pkgname lang="cy">firefox-developer-edition-i18n-cy</pkgname>            
+        </feature>
+        <feature name="google-chrome">
+        <pkgname>google-chrome</pkgname>
+        </feature>
+        <feature name="hunspell">
+                <pkgname lang="de">hunspell-de</pkgname> <!-- German hunspell dictionaries -->
+                <pkgname lang="en">hunspell-en_AU</pkgname> <!-- English hunspell dictionaries -->
+                <pkgname lang="en">hunspell-en_CA</pkgname> <!-- English hunspell dictionaries -->
+                <pkgname lang="en">hunspell-en_GB</pkgname> <!-- English hunspell dictionaries -->
+                <pkgname lang="en">hunspell-en_US</pkgname> <!-- English hunspell dictionaries -->
+                <pkgname lang="es">hunspell-es_es</pkgname> <!-- Spanish hunspell dictionaries -->
+                <pkgname lang="fr">hunspell-fr</pkgname> <!-- French (modern) hunspell dictionaries -->
+                <pkgname lang="he">hunspell-he</pkgname> <!-- Hebrew hunspell dictionary -->
+                <pkgname lang="it">hunspell-it</pkgname> <!-- Italian dictionary for Hunspell -->
+                <pkgname lang="ro">hunspell-ro</pkgname> <!-- Romanian dictionary for Hunspell -->
+                <pkgname lang="el">hunspell-el</pkgname> <!-- Greek hunspell dictionary -->
+                <pkgname lang="hu">hunspell-hu</pkgname> <!-- Hungarian hunspell dictionary -->
+                <pkgname lang="nl">hunspell-nl</pkgname> <!-- Dutch hunspell dictionaries -->
+                <pkgname lang="pl">hunspell-pl</pkgname> <!-- Polish dictionary for Hunspell -->
+        </feature>
+        <feature name="vivaldi">
+            <pkgname>vivaldi</pkgname>
+            <pkgname>vivaldi-ffmpeg-codecs</pkgname>
+        </feature>
+        <feature name="opera">
+            <pkgname>opera</pkgname>
+        </feature>
+        <feature name="firewall">
+            <pkgname>gufw</pkgname>
+            <pkgname>ufw</pkgname>
+        </feature>
+        <feature name="firewire">
+            <pkgname>dvgrab</pkgname>
+            <pkgname>libffado</pkgname>
+            <pkgname>libraw1394</pkgname>
+        </feature>
+        <feature name="games">
+            <pkgname>steam-native-runtime</pkgname>
+            <pkgname>steam</pkgname>
+            <pkgname>playonlinux</pkgname>
+            <pkgname arch="x86_64">lib32-libldap</pkgname>
+        </feature>
+        <feature name="gtk-play">
+            <pkgname>0ad</pkgname>
+            <pkgname>megaglest</pkgname>
+            <pkgname>supertux</pkgname>
+            <pkgname>supertuxkart</pkgname>
+            <pkgname>wesnoth</pkgname>
+            <pkgname>xonotic</pkgname>
+            <pkgname>aisleriot</pkgname>
+            <pkgname>astromenace</pkgname>
+            <pkgname>extremetuxracer</pkgname>
+            <pkgname>gnome-chess</pkgname>
+            <pkgname>gnome-mines</pkgname>
+            <pkgname>gnome-sudoku</pkgname>
+            <pkgname>gnome-mahjongg</pkgname>
+        </feature>
+        <feature name="qt-play">
+            <pkgname>0ad</pkgname>
+            <pkgname>megaglest</pkgname>
+            <pkgname>supertux</pkgname>
+            <pkgname>supertuxkart</pkgname>
+            <pkgname>wesnoth</pkgname>
+            <pkgname>xonotic</pkgname>
+            <pkgname>aisleriot</pkgname>
+            <pkgname>astromenace</pkgname>
+            <pkgname>extremetuxracer</pkgname>
+            <pkgname>knights</pkgname>
+            <pkgname>kmines</pkgname>
+            <pkgname>ksudoku</pkgname>
+            <pkgname>kmahjongg</pkgname>
+        </feature>
+        <feature name="graphics">
+            <pkgname>gimp</pkgname>
+            <pkgname>gthumb</pkgname>
+            <pkgname>rapid-photo-downloader</pkgname>
+            <pkgname>raw-thumbnailer</pkgname>
+            <pkgname>rawtherapee</pkgname>
+            <pkgname>darktable</pkgname>
+        </feature>
+        <feature name="hardinfo">
+            <pkgname>hardinfo</pkgname>
+        </feature>
+        <feature name="maintenance">
+            <pkgname>bleachbit</pkgname>
+            <pkgname>timeshift</pkgname>
+            <pkgname>xapps</pkgname>
+            <pkgname>stacer</pkgname>
+        </feature>
+        <feature name="movie">
+            <pkgname>openshot</pkgname>
+            <pkgname>kdenlive</pkgname>
+            <pkgname>dvdauthor</pkgname>
+            <pkgname>frei0r-plugins</pkgname>
+            <pkgname>pitivi</pkgname>
+            <pkgname>avidemux-cli</pkgname>
+        </feature>
+        <feature name="mycroft">
+            <pkgname>mycroft-reborn</pkgname>
+        </feature>
+        <feature name="plymouth">
+            <pkgname>plymouth</pkgname>
+            <pkgname>arch-plymouthize</pkgname>
+            <pkgname>plymouth-theme-arch-charge-big</pkgname>
+        </feature>
+        <feature name="redshift">
+            <pkgname>redshift</pkgname>
+            <pkgname>redshiftgui</pkgname>
+        </feature>
+        <feature name="skype">
+            <pkgname>skypeforlinux-stable-bin</pkgname>
+        </feature>
+        <feature name="spotify">
+            <pkgname>spotify</pkgname>
+        </feature>
+        <feature name="email">
+            <pkgname>thunderbird</pkgname>
+              <pkgname lang="sq">thunderbird-i18n-sq</pkgname>
+              <pkgname lang="ar">thunderbird-i18n-ar</pkgname>
+              <pkgname lang="ast">thunderbird-i18n-ast</pkgname>
+              <pkgname lang="eu">thunderbird-i18n-eu</pkgname>
+              <pkgname lang="be">thunderbird-i18n-be</pkgname>
+              <pkgname lang="bg">thunderbird-i18n-bg</pkgname>
+              <pkgname lang="ca">thunderbird-i18n-ca</pkgname>
+              <pkgname lang="zh_CN">thunderbird-i18n-zh-cn</pkgname>
+              <pkgname lang="zh_TW">thunderbird-i18n-zh-tw</pkgname>
+              <pkgname lang="hr">thunderbird-i18n-hr</pkgname>
+              <pkgname lang="cs">thunderbird-i18n-cs</pkgname>
+              <pkgname lang="da">thunderbird-i18n-da</pkgname>
+              <pkgname lang="nl">thunderbird-i18n-nl</pkgname>
+              <pkgname lang="et">thunderbird-i18n-et</pkgname>
+              <pkgname lang="fi">thunderbird-i18n-fi</pkgname>
+              <pkgname lang="fr">thunderbird-i18n-fr</pkgname>
+              <pkgname lang="gl">thunderbird-i18n-gl</pkgname>
+              <pkgname lang="de">thunderbird-i18n-de</pkgname>
+              <pkgname lang="el">thunderbird-i18n-el</pkgname>
+              <pkgname lang="he">thunderbird-i18n-he</pkgname>
+              <pkgname lang="hu">thunderbird-i18n-hu</pkgname>
+              <pkgname lang="id">thunderbird-i18n-id</pkgname>
+              <pkgname lang="it">thunderbird-i18n-it</pkgname>
+              <pkgname lang="ja">thunderbird-i18n-ja</pkgname>
+              <pkgname lang="ko">thunderbird-i18n-ko</pkgname>
+              <pkgname lang="lt">thunderbird-i18n-lt</pkgname>
+              <pkgname lang="nb_NO">thunderbird-i18n-nb-no</pkgname>
+              <pkgname lang="nn_NO">thunderbird-i18n-nn-no</pkgname>
+              <pkgname lang="pl">thunderbird-i18n-pl</pkgname>
+              <pkgname lang="pt">thunderbird-i18n-pt-pt</pkgname>
+              <pkgname lang="pt_BR">thunderbird-i18n-pt-br</pkgname>
+              <pkgname lang="ro">thunderbird-i18n-ro</pkgname>
+              <pkgname lang="ru">thunderbird-i18n-ru</pkgname>
+              <pkgname lang="sr">thunderbird-i18n-sr</pkgname>
+              <pkgname lang="sk">thunderbird-i18n-sk</pkgname>
+              <pkgname lang="sl">thunderbird-i18n-sl</pkgname>
+              <pkgname lang="es">thunderbird-i18n-es-es</pkgname>
+              <pkgname lang="sv">thunderbird-i18n-sv-se</pkgname>
+              <pkgname lang="tr">thunderbird-i18n-tr</pkgname>
+              <pkgname lang="uk">thunderbird-i18n-uk</pkgname>
+              <pkgname lang="vi">thunderbird-i18n-vi</pkgname>
+              <pkgname lang="cy">thunderbird-i18n-cy</pkgname>
+        </feature>
+        <feature name="lamp">
+            <pkgname>apache</pkgname>
+            <pkgname>php</pkgname>
+            <pkgname>php-apache</pkgname>
+            <pkgname>mariadb</pkgname>
+            <pkgname>phpmyadmin</pkgname>
+            <pkgname>php-mcrypt</pkgname>
+        </feature>
+        <feature name="lemp">
+            <pkgname>nginx</pkgname>
+            <pkgname>php-fpm</pkgname>
+            <pkgname>mariadb</pkgname>
+            <pkgname>phpmyadmin</pkgname>
+            <pkgname>php-mcrypt</pkgname>
+        </feature>
+        <feature name="lts">
+            <pkgname>linux-lts</pkgname>
+        </feature>
+       <feature name="freeoffice">
+              <pkgname>freeoffice</pkgname>
+        </feature>
+       <feature name="libreoffice">
+              <pkgname>libreoffice-fresh</pkgname>
+              <pkgname lang="sq">libreoffice-fresh-sq</pkgname>
+              <pkgname lang="am">libreoffice-fresh-am</pkgname>
+              <pkgname lang="ar">libreoffice-fresh-ar</pkgname>
+              <pkgname lang="ast">libreoffice-fresh-ast</pkgname>
+              <pkgname lang="eu">libreoffice-fresh-eu</pkgname>
+              <pkgname lang="be">libreoffice-fresh-be</pkgname>
+              <pkgname lang="bn">libreoffice-fresh-bn</pkgname>
+              <pkgname lang="bs">libreoffice-fresh-bs</pkgname>
+              <pkgname lang="bg">libreoffice-fresh-bg</pkgname>
+              <pkgname lang="ca">libreoffice-fresh-ca</pkgname>
+              <pkgname lang="zh_CN">libreoffice-fresh-zh-cn</pkgname>
+              <pkgname lang="zh_TW">libreoffice-fresh-zh-tw</pkgname>
+              <pkgname lang="hr">libreoffice-fresh-hr</pkgname>
+              <pkgname lang="cs">libreoffice-fresh-cs</pkgname>
+              <pkgname lang="da">libreoffice-fresh-da</pkgname>
+              <pkgname lang="nl">libreoffice-fresh-nl</pkgname>
+              <pkgname lang="eo">libreoffice-fresh-eo</pkgname>
+              <pkgname lang="et">libreoffice-fresh-et</pkgname>
+              <pkgname lang="fi">libreoffice-fresh-fi</pkgname>
+              <pkgname lang="fr">libreoffice-fresh-fr</pkgname>
+              <pkgname lang="gl">libreoffice-fresh-gl</pkgname>
+              <pkgname lang="ka">libreoffice-fresh-ka</pkgname>
+              <pkgname lang="de">libreoffice-fresh-de</pkgname>
+              <pkgname lang="el">libreoffice-fresh-el</pkgname>
+              <pkgname lang="gu">libreoffice-fresh-gu</pkgname>
+              <pkgname lang="he">libreoffice-fresh-he</pkgname>
+              <pkgname lang="hi">libreoffice-fresh-hi</pkgname>
+              <pkgname lang="hu">libreoffice-fresh-hu</pkgname>
+              <pkgname lang="id">libreoffice-fresh-id</pkgname>
+              <pkgname lang="ga">libreoffice-fresh-ga</pkgname>
+              <pkgname lang="it">libreoffice-fresh-it</pkgname>
+              <pkgname lang="ja">libreoffice-fresh-ja</pkgname>
+              <pkgname lang="kk">libreoffice-fresh-kk</pkgname>
+              <pkgname lang="km">libreoffice-fresh-km</pkgname>
+              <pkgname lang="ko">libreoffice-fresh-ko</pkgname>
+              <pkgname lang="lv">libreoffice-fresh-lv</pkgname>
+              <pkgname lang="lt">libreoffice-fresh-lt</pkgname>
+              <pkgname lang="mk">libreoffice-fresh-mk</pkgname>
+              <pkgname lang="ml">libreoffice-fresh-ml</pkgname>
+              <pkgname lang="mr">libreoffice-fresh-mr</pkgname>
+              <pkgname lang="ne">libreoffice-fresh-ne</pkgname>
+              <pkgname lang="pl">libreoffice-fresh-pl</pkgname>
+              <pkgname lang="pt">libreoffice-fresh-pt</pkgname>
+              <pkgname lang="pt_BR">libreoffice-fresh-pt-br</pkgname>
+              <pkgname lang="ro">libreoffice-fresh-ro</pkgname>
+              <pkgname lang="ru">libreoffice-fresh-ru</pkgname>
+              <pkgname lang="sr">libreoffice-fresh-sr</pkgname>
+              <pkgname lang="sk">libreoffice-fresh-sk</pkgname>
+              <pkgname lang="sl">libreoffice-fresh-sl</pkgname>
+              <pkgname lang="es">libreoffice-fresh-es</pkgname>
+              <pkgname lang="sv">libreoffice-fresh-sv</pkgname>
+              <pkgname lang="ta">libreoffice-fresh-ta</pkgname>
+              <pkgname lang="th">libreoffice-fresh-th</pkgname>
+              <pkgname lang="tr">libreoffice-fresh-tr</pkgname>
+              <pkgname lang="uk">libreoffice-fresh-uk</pkgname>
+              <pkgname lang="vi">libreoffice-fresh-vi</pkgname>
+              <pkgname lang="cy">libreoffice-fresh-cy</pkgname>
+        </feature>
+        <feature name="wps-office">
+            <pkgname>wps-office</pkgname>
+            <pkgname>wps-office-mime</pkgname>
+            <pkgname>wps-office-fonts</pkgname>
+            <pkgname lang="es">wps-office-mui-es-es</pkgname>
+            <pkgname lang="es">wps-office-extension-spanish-dictionary</pkgname>
+            <pkgname lang="pt_BR">wps-office-mui-pt-br</pkgname>
+            <pkgname lang="pt_BR">wps-office-extension-portuguese-brazilian-dictionary</pkgname>
+            <pkgname lang="ru">wps-office-mui-ru-ru</pkgname>
+            <pkgname lang="ru">wps-office-extension-russian-dictionary</pkgname>
+            <pkgname lang="uk">wps-office-extension-english-uk-dictionary</pkgname>
+       </feature>
+        <feature name="power">
+            <pkgname>tlp</pkgname>
+            <pkgname>thermald</pkgname>
+        </feature>
+        <feature name="sshd">
+            <pkgname>openssh</pkgname>
+        </feature>
+        <feature name="visual">
+            <!-- Change compton for picom -->
+            <pkgname>picom</pkgname>
+            <!-- <pkgname>paranoid</pkgname> -->
+        </feature>
+       <feature name="nemo">
+            <pkgname>nemo</pkgname>
+            <!-- <pkgname>nemo-root</pkgname> -->
+        </feature>
+       <feature name="nautilus">
+           <pkgname>nautilus</pkgname>
+            <!-- <pkgname>nautilus-root</pkgname> -->
+        </feature>
+        <feature name="nixnote">
+            <pkgname>nixnote2</pkgname>
+       </feature>
+        <feature name="vlc">
+            <pkgname>vlc</pkgname>
+            <pkgname>phonon-qt5-vlc</pkgname>
+        </feature>
+        <feature name="wallpapers">
+            <pkgname>variety</pkgname>
+        </feature>
+        <feature name="wine">
+            <pkgname>wine</pkgname>
+            <pkgname>winetricks</pkgname>
+            <pkgname>playonlinux</pkgname>
+            <pkgname>lutris</pkgname>
+        </feature>
+    </features>
+
+    <net>
+        <broadcom>
+            <pkgname>broadcom-wl</pkgname>
+        </broadcom>
+    </net>
+
+    <filesystems>
+        <pkgname>btrfs-progs</pkgname>      <!-- for btrfs partitions -->
+        <pkgname>cifs-utils</pkgname>       <!-- for smb shares -->
+        <pkgname>dmraid</pkgname>           <!-- Device mapper RAID interface -->
+        <pkgname>dosfstools</pkgname>       <!-- for FAT16 and FAT32 partitions -->
+        <pkgname>exfat-utils</pkgname>      <!-- for exFAT partitions -->
+        <pkgname>e2fsprogs</pkgname>        <!-- Ext2/3/4 filesystem utilities -->
+        <pkgname>f2fs-tools</pkgname>       <!-- for Flash-Friendly File System -->
+        <pkgname>jfsutils</pkgname>         <!-- for jfs partitions -->
+        <pkgname>nfs-utils</pkgname>        <!-- for Network File Systems (NFS)-->
+        <pkgname>nilfs-utils</pkgname>      <!-- for nilfs2 support -->
+        <pkgname>ntfs-3g</pkgname>          <!-- for NTFS support -->
+        <pkgname>reiserfsprogs</pkgname>    <!-- for reiser partitions -->
+        <pkgname>xfsprogs</pkgname>         <!-- for xfs partitions -->
+    </filesystems>
+
+    <zfs>
+        <pkgname>grub-zfs</pkgname>
+        <pkgname>spl</pkgname>
+        <pkgname>spl-utils</pkgname>
+        <pkgname>zfs</pkgname>
+        <pkgname>zfs-utils</pkgname>
+    </zfs>
+
+    <locale_fonts>
+        <chinese>
+            <pkgname>opendesktop-fonts</pkgname>
+        </chinese>
+    </locale_fonts>
+
+    <bootloaders>
+        <bootloader name="grub2">
+            <pkgname>efibootmgr</pkgname>
+            <pkgname>grub</pkgname>
+            <pkgname>os-prober</pkgname>
+            <!-- <pkgname>grub2-theme-antergos</pkgname> -->
+            <pkgname>grub2-theme-vimix-git</pkgname>
+        </bootloader>
+        <bootloader name="systemd-boot">
+            <!-- previously called gummiboot, now comes with systemd -->
+            <pkgname>efibootmgr</pkgname>
+        </bootloader>
+        <bootloader name="refind">
+            <pkgname>efibootmgr</pkgname>
+            <pkgname>refind-efi</pkgname>
+        </bootloader>
+    </bootloaders>
+</cnchi>
diff --git a/Cnchi/pacman.conf b/Cnchi/pacman.conf
new file mode 100755 (executable)
index 0000000..b3f4885
--- /dev/null
@@ -0,0 +1,99 @@
+#
+# /etc/pacman.conf
+#
+# See the pacman.conf(5) manpage for option and repository directives
+
+#
+# GENERAL OPTIONS
+#
+[options]
+# The following paths are commented out with their default values listed.
+# If you wish to use different paths, uncomment and update the paths.
+#RootDir     = /
+#DBPath      = /var/lib/pacman/
+#CacheDir    = /var/cache/pacman/pkg/
+#LogFile     = /var/log/pacman.log
+#GPGDir      = /etc/pacman.d/gnupg/
+HoldPkg     = pacman glibc
+#XferCommand = /usr/bin/curl -C - -f %u > %o
+#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
+#CleanMethod = KeepInstalled
+#UseDelta    = 0.7
+Architecture = auto
+
+# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
+#IgnorePkg   =
+#IgnoreGroup =
+
+#NoUpgrade   =
+#NoExtract   =
+
+# Misc options
+#UseSyslog
+#Color
+#TotalDownload
+# We cannot check disk space from within a chroot environment
+#CheckSpace
+#VerbosePkgLists
+
+# By default, pacman accepts packages signed by keys that its local keyring
+# trusts (see pacman-key and its man page), as well as unsigned packages.
+SigLevel    = Required DatabaseOptional
+LocalFileSigLevel = Optional
+#RemoteFileSigLevel = Required
+
+# NOTE: You must run `pacman-key --init` before first using pacman; the local
+# keyring can then be populated with the keys of all official Arch Linux
+# packagers with `pacman-key --populate archlinux`.
+
+#
+# REPOSITORIES
+#   - can be defined here or included from another file
+#   - pacman will search repositories in the order defined here
+#   - local/custom mirrors can be added here or in separate files
+#   - repositories listed first will take precedence when packages
+#     have identical names, regardless of version number
+#   - URLs will have $repo replaced by the name of the current repo
+#   - URLs will have $arch replaced by the name of the architecture
+#
+# Repository entries are of the format:
+#       [repo-name]
+#       Server = ServerName
+#       Include = IncludePath
+#
+# The header [repo-name] is crucial - it must be present and
+# uncommented to enable the repo.
+#
+
+# The testing repositories are disabled by default. To enable, uncomment the
+# repo name header and Include lines. You can add preferred servers immediately
+# after the header, and they will be used before the default mirrors.
+
+#[testing]
+#Include = /etc/pacman.d/mirrorlist
+
+[Reborn-OS]
+SigLevel = Optional TrustAll
+Include = /etc/pacman.d/reborn-mirrorlist
+
+[core]
+Include = /etc/pacman.d/mirrorlist
+
+[extra]
+Include = /etc/pacman.d/mirrorlist
+
+#[community-testing]
+#Include = /etc/pacman.d/mirrorlist
+
+[community]
+Include = /etc/pacman.d/mirrorlist
+
+[multilib]
+Include = /etc/pacman.d/mirrorlist
+
+# An example of a custom package repository.  See the pacman manpage for
+# tips on creating your own repositories.
+#[custom]
+#SigLevel = Optional TrustAll
+#Server = file:///home/custompkgs
+
diff --git a/Cnchi/pacman.tmpl b/Cnchi/pacman.tmpl
new file mode 100755 (executable)
index 0000000..63f9390
--- /dev/null
@@ -0,0 +1,26 @@
+[options]
+Architecture = auto
+SigLevel = Required DatabaseOptional
+LocalFileSigLevel = Optional
+
+RootDir = ${destDir}
+DBPath = ${destDir}/var/lib/pacman/
+CacheDir = ${destDir}/var/cache/pacman/pkg
+LogFile = /tmp/pacman.log
+
+# Repositories
+[Reborn-OS]
+SigLevel = Optional TrustAll
+Include = /etc/pacman.d/reborn-mirrorlist
+
+[core]
+Include = /etc/pacman.d/mirrorlist
+
+[extra]
+Include = /etc/pacman.d/mirrorlist
+
+[community]
+Include = /etc/pacman.d/mirrorlist
+
+[multilib]
+Include = /etc/pacman.d/mirrorlist
diff --git a/Cnchi/pacman2.conf b/Cnchi/pacman2.conf
new file mode 100644 (file)
index 0000000..abbac17
--- /dev/null
@@ -0,0 +1,98 @@
+#
+# /etc/pacman.conf
+#
+# See the pacman.conf(5) manpage for option and repository directives
+
+#
+# GENERAL OPTIONS
+#
+[options]
+# The following paths are commented out with their default values listed.
+# If you wish to use different paths, uncomment and update the paths.
+#RootDir     = /
+#DBPath      = /var/lib/pacman/
+#CacheDir    = /var/cache/pacman/pkg/
+#LogFile     = /var/log/pacman.log
+#GPGDir      = /etc/pacman.d/gnupg/
+HoldPkg     = pacman glibc
+#XferCommand = /usr/bin/curl -C - -f %u > %o
+#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
+#CleanMethod = KeepInstalled
+#UseDelta    = 0.7
+Architecture = auto
+
+# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
+#IgnorePkg   =
+#IgnoreGroup =
+
+#NoUpgrade   =
+#NoExtract   =
+
+# Misc options
+#UseSyslog
+#Color
+#TotalDownload
+# We cannot check disk space from within a chroot environment
+#CheckSpace
+#VerbosePkgLists
+
+# By default, pacman accepts packages signed by keys that its local keyring
+# trusts (see pacman-key and its man page), as well as unsigned packages.
+SigLevel    = Required DatabaseOptional
+LocalFileSigLevel = Optional
+#RemoteFileSigLevel = Required
+
+# NOTE: You must run `pacman-key --init` before first using pacman; the local
+# keyring can then be populated with the keys of all official Arch Linux
+# packagers with `pacman-key --populate archlinux`.
+
+#
+# REPOSITORIES
+#   - can be defined here or included from another file
+#   - pacman will search repositories in the order defined here
+#   - local/custom mirrors can be added here or in separate files
+#   - repositories listed first will take precedence when packages
+#     have identical names, regardless of version number
+#   - URLs will have $repo replaced by the name of the current repo
+#   - URLs will have $arch replaced by the name of the architecture
+#
+# Repository entries are of the format:
+#       [repo-name]
+#       Server = ServerName
+#       Include = IncludePath
+#
+# The header [repo-name] is crucial - it must be present and
+# uncommented to enable the repo.
+#
+
+# The testing repositories are disabled by default. To enable, uncomment the
+# repo name header and Include lines. You can add preferred servers immediately
+# after the header, and they will be used before the default mirrors.
+
+#[testing]
+#Include = /etc/pacman.d/mirrorlist
+
+[Reborn-OS]
+Include = /etc/pacman.d/reborn-mirrorlist
+
+[core]
+Include = /etc/pacman.d/mirrorlist
+
+[extra]
+Include = /etc/pacman.d/mirrorlist
+
+#[community-testing]
+#Include = /etc/pacman.d/mirrorlist
+
+[community]
+Include = /etc/pacman.d/mirrorlist
+
+[multilib]
+Include = /etc/pacman.d/mirrorlist
+
+# An example of a custom package repository.  See the pacman manpage for
+# tips on creating your own repositories.
+#[custom]
+#SigLevel = Optional TrustAll
+#Server = file:///home/custompkgs
+
diff --git a/Cnchi/pacman2.tmpl b/Cnchi/pacman2.tmpl
new file mode 100644 (file)
index 0000000..4f1f978
--- /dev/null
@@ -0,0 +1,30 @@
+[options]
+Architecture = auto
+SigLevel = Required DatabaseOptional
+LocalFileSigLevel = Optional
+
+RootDir = ${destDir}
+DBPath = ${destDir}/var/lib/pacman/
+CacheDir = ${destDir}/var/cache/pacman/pkg
+LogFile = /tmp/pacman.log
+
+# Repositories
+[Reborn-OS]
+SigLevel = Optional TrustAll
+Include = /etc/pacman.d/reborn-mirrorlist
+
+[core]
+Include = /etc/pacman.d/mirrorlist
+
+[extra]
+Include = /etc/pacman.d/mirrorlist
+
+[community]
+Include = /etc/pacman.d/mirrorlist
+
+% if arch == 'x86_64':
+[multilib]
+Include = /etc/pacman.d/mirrorlist
+% endif
+
+
diff --git a/Cnchi/post_install.py b/Cnchi/post_install.py
new file mode 100755 (executable)
index 0000000..1719380
--- /dev/null
@@ -0,0 +1,871 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# post_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.
+
+""" Post-Installation process module. """
+
+import crypt
+import logging
+import os
+import shutil
+import time
+
+import desktop_info
+
+from installation import mkinitcpio
+from installation import systemd_networkd
+from installation.boot import loader
+
+from installation.post_fstab import PostFstab
+from installation.post_features import PostFeatures
+from installation import services as srv
+
+from misc.events import Events
+import misc.gocryptfs as gocryptfs
+from misc.run_cmd import call, chroot_call
+
+import parted3.fs_module as fs
+
+from lembrame.lembrame import Lembrame
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+DEST_DIR = "/install"
+
+class PostInstallation():
+    """ Post-Installation process thread class """
+    POSTINSTALL_SCRIPT = 'postinstall.sh'
+    LOG_FOLDER = '/var/log/cnchi'
+
+    def __init__(
+            self, settings, callback_queue, mount_devices, fs_devices, ssd=None, blvm=False):
+
+        """ Initialize installation class """
+        self.settings = settings
+        self.events = Events(callback_queue)
+
+        self.pacman_conf_updated = False
+
+        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.virtual_box = self.settings.get('is_vbox')
+
+    def copy_logs(self):
+        """ Copy Cnchi logs to new installation """
+        log_dest_dir = os.path.join(DEST_DIR, "var/log/cnchi")
+        os.makedirs(log_dest_dir, mode=0o755, exist_ok=True)
+
+        datetime = "{0}-{1}".format(time.strftime("%Y%m%d"),
+                                    time.strftime("%H%M%S"))
+
+        file_names = [
+            'cnchi', 'cnchi-alpm', 'postinstall', 'pacman']
+
+        for name in file_names:
+            src = os.path.join(PostInstallation.LOG_FOLDER, "{0}.log".format(name))
+            dst = os.path.join(
+                log_dest_dir, "{0}-{1}.log".format(name, datetime))
+            try:
+                shutil.copy(src, dst)
+            except FileNotFoundError as err:
+                logging.warning("Can't copy %s log to %s: %s", src, dst, str(err))
+            except FileExistsError:
+                pass
+
+        # Store install id for later use by antergos-pkgstats
+        with open(os.path.join(log_dest_dir, 'install_id'), 'w') as install_record:
+            install_id = self.settings.get('install_id')
+            if not install_id:
+                install_id = '0'
+            install_record.write(install_id)
+
+    @staticmethod
+    def copy_network_config():
+        """ Copies Network Manager configuration """
+        source_nm = "/etc/NetworkManager/system-connections/"
+        target_nm = os.path.join(
+            DEST_DIR, "etc/NetworkManager/system-connections")
+
+        # Sanity checks.  We don't want to do anything if a network
+        # configuration already exists on the target
+        if os.path.exists(source_nm) and os.path.exists(target_nm):
+            for network in os.listdir(source_nm):
+                # Skip LTSP live
+                if network == "LTSP":
+                    continue
+
+                source_network = os.path.join(source_nm, network)
+                target_network = os.path.join(target_nm, network)
+
+                if os.path.exists(target_network):
+                    continue
+
+                try:
+                    shutil.copy(source_network, target_network)
+                except FileNotFoundError:
+                    logging.warning(
+                        "Can't copy network configuration files, file %s not found", source_network)
+                except FileExistsError:
+                    pass
+
+    def set_scheduler(self):
+        """ Copies udev rule for SSDs """
+        rule_src = os.path.join(
+            self.settings.get('cnchi'),
+            'scripts/60-schedulers.rules')
+        rule_dst = os.path.join(
+            DEST_DIR,
+            "etc/udev/rules.d/60-schedulers.rules")
+        try:
+            shutil.copy2(rule_src, rule_dst)
+            os.chmod(rule_dst, 0o755)
+        except FileNotFoundError:
+            logging.warning(
+                "Cannot copy udev rule for SSDs, file %s not found.",
+                rule_src)
+        except FileExistsError:
+            pass
+
+    @staticmethod
+    def change_user_password(user, new_password):
+        """ Changes the user's password """
+        shadow_password = crypt.crypt(new_password, crypt.mksalt())
+        chroot_call(['usermod', '-p', shadow_password, user])
+
+    @staticmethod
+    def auto_timesetting():
+        """ Set hardware clock """
+        cmd = ["hwclock", "--systohc", "--utc"]
+        call(cmd)
+        try:
+            shutil.copy2("/etc/adjtime", os.path.join(DEST_DIR, "etc/"))
+        except FileNotFoundError:
+            logging.warning("File /etc/adjtime not found!")
+        except FileExistsError:
+            pass
+
+    @staticmethod
+    def update_pacman_conf():
+        """ Add Antergos and multilib repos """
+        """ Now, remove antergos repo, and add Reborn-OS repo (Rafael) """
+        path = os.path.join(DEST_DIR, "etc/pacman.conf")
+        if os.path.exists(path):
+            with open(path) as pacman_file:
+                paclines = pacman_file.readlines()
+
+            mode = os.uname()[-1]
+            multilib_open = False
+
+            with open(path, 'w') as pacman_file:
+                for pacline in paclines:
+                    if mode == "x86_64" and pacline == '#[multilib]\n':
+                        multilib_open = True
+                        pacline = '[multilib]\n'
+                    elif mode == 'x86_64' and multilib_open and pacline.startswith('#Include ='):
+                        pacline = pacline[1:]
+                        multilib_open = False
+                    elif pacline == '#[testing]\n':
+                        antlines = '\n#[Reborn-OS-staging]\n'
+                        antlines += '#SigLevel = Optional TrustAll\n'
+                        antlines += '#Server = http://repo-de.rebornos.org/Reborn-OS/\n\n'
+                        antlines += '[Reborn-OS]\n'
+                        antlines += 'SigLevel = Optional TrustAll\n'
+                        antlines += 'Include = /etc/pacman.d/reborn-mirrorlist\n\n'
+                        pacman_file.write(antlines)
+
+                    pacman_file.write(pacline)
+        else:
+            logging.warning("Can't find pacman configuration file")
+
+    @staticmethod
+    def uncomment_locale_gen(locale):
+        """ Uncomment selected locale in /etc/locale.gen """
+
+        path = os.path.join(DEST_DIR, "etc/locale.gen")
+
+        if os.path.exists(path):
+            with open(path) as gen:
+                text = gen.readlines()
+
+            with open(path, "w") as gen:
+                for line in text:
+                    if locale in line and line[0] == "#":
+                        # remove trailing '#'
+                        line = line[1:]
+                    gen.write(line)
+        else:
+            logging.error("Can't find locale.gen file")
+
+    @staticmethod
+    def fix_thermald_service():
+        """ Adds --ignore-cpuid-check to thermald service file """
+        path = os.path.join(DEST_DIR, "usr/lib/systemd/system/thermald.service")
+        if os.path.exists(path):
+            with open(path, 'r') as fin:
+                lines = fin.readlines()
+            for index, line in enumerate(lines):
+                if line.startswith("ExecStart") and "--ignore-cpuid-check" not in line:
+                    lines[index] += " --ignore-cpuid-check"
+            with open(path, 'w') as fout:
+                fout.writelines(lines)
+
+    def setup_display_manager(self):
+        """ Configures LightDM desktop manager, including autologin. """
+        txt = _("Configuring LightDM desktop manager...")
+        self.events.add('info', txt)
+
+        if self.desktop in desktop_info.SESSIONS:
+            session = desktop_info.SESSIONS[self.desktop]
+        else:
+            session = "default"
+
+        username = self.settings.get('user_name')
+        autologin = not self.settings.get('require_password')
+
+        lightdm_greeter = "lightdm-webkit2-greeter"
+
+        lightdm_conf_path = os.path.join(DEST_DIR, "etc/lightdm/lightdm.conf")
+        try:
+            # Setup LightDM as Desktop Manager
+            with open(lightdm_conf_path) as lightdm_conf:
+                text = lightdm_conf.readlines()
+
+            with open(lightdm_conf_path, "w") as lightdm_conf:
+                for line in text:
+                    if autologin:
+                        # Enable automatic login
+                        if '#autologin-user=' in line:
+                            line = 'autologin-user={0}\n'.format(username)
+                        if '#autologin-user-timeout=0' in line:
+                            line = 'autologin-user-timeout=0\n'
+                    # Set correct DE session
+                    if '#user-session=default' in line:
+                        line = 'user-session={0}\n'.format(session)
+                    # Set correct greeter
+                    if '#greeter-session=example-gtk-gnome' in line:
+                        line = 'greeter-session={0}\n'.format(lightdm_greeter)
+                    if 'session-wrapper' in line:
+                        line = 'session-wrapper=/etc/lightdm/Xsession\n'
+                    lightdm_conf.write(line)
+            txt = _("LightDM display manager configuration completed.")
+            logging.debug(txt)
+        except FileNotFoundError:
+            txt = _("Error while trying to configure the LightDM display manager")
+            logging.warning(txt)
+
+    @staticmethod
+    def alsa_mixer_setup():
+        """ Sets ALSA mixer settings """
+
+        alsa_commands = [
+            "Master 70% unmute", "Front 70% unmute", "Side 70% unmute", "Surround 70% unmute",
+            "Center 70% unmute", "LFE 70% unmute", "Headphone 70% unmute", "Speaker 70% unmute",
+            "PCM 70% unmute", "Line 70% unmute", "External 70% unmute", "FM 50% unmute",
+            "Master Mono 70% unmute", "Master Digital 70% unmute", "Analog Mix 70% unmute",
+            "Aux 70% unmute", "Aux2 70% unmute", "PCM Center 70% unmute", "PCM Front 70% unmute",
+            "PCM LFE 70% unmute", "PCM Side 70% unmute", "PCM Surround 70% unmute",
+            "Playback 70% unmute", "PCM,1 70% unmute", "DAC 70% unmute", "DAC,0 70% unmute",
+            "DAC,1 70% unmute", "Synth 70% unmute", "CD 70% unmute", "Wave 70% unmute",
+            "Music 70% unmute", "AC97 70% unmute", "Analog Front 70% unmute",
+            "VIA DXS,0 70% unmute", "VIA DXS,1 70% unmute", "VIA DXS,2 70% unmute",
+            "VIA DXS,3 70% unmute", "Mic 70% mute", "IEC958 70% mute",
+            "Master Playback Switch on", "Master Surround on",
+            "SB Live Analog/Digital Output Jack off", "Audigy Analog/Digital Output Jack off"]
+
+        for alsa_command in alsa_commands:
+            cmd = ["amixer", "-q", "-c", "0", "sset"]
+            cmd.extend(alsa_command.split())
+            chroot_call(cmd)
+
+        # Save settings
+        logging.debug("Saving ALSA settings...")
+        chroot_call(['alsactl', 'store'])
+        logging.debug("ALSA settings saved.")
+
+    @staticmethod
+    def set_fluidsynth():
+        """ Sets fluidsynth configuration file """
+        fluid_path = os.path.join(DEST_DIR, "etc/conf.d/fluidsynth")
+        if os.path.exists(fluid_path):
+            audio_system = "alsa"
+            pulseaudio_path = os.path.join(DEST_DIR, "usr/bin/pulseaudio")
+            if os.path.exists(pulseaudio_path):
+                audio_system = "pulse"
+            with open(fluid_path, "w") as fluid_conf:
+                fluid_conf.write('# Created by Cnchi, Antergos installer\n')
+                txt = 'SYNTHOPTS="-is -a {0} -m alsa_seq -r 48000"\n\n'
+                txt = txt.format(audio_system)
+                fluid_conf.write(txt)
+
+    @staticmethod
+    def patch_user_dirs_update_gtk():
+        """ Patches user-dirs-update-gtk.desktop so it is run in
+            XFCE, MATE and Cinnamon """
+        path = os.path.join(DEST_DIR, "etc/xdg/autostart/user-dirs-update-gtk.desktop")
+        if os.path.exists(path):
+            with open(path, 'r') as user_dirs:
+                lines = user_dirs.readlines()
+            with open(path, 'w') as user_dirs:
+                for line in lines:
+                    if "OnlyShowIn=" in line:
+                        line = "OnlyShowIn=GNOME;LXDE;Unity;XFCE;MATE;Cinnamon\n"
+                    user_dirs.write(line)
+
+    def set_keymap(self):
+        """ Set X11 and console keymap """
+        keyboard_layout = self.settings.get("keyboard_layout")
+        keyboard_variant = self.settings.get("keyboard_variant")
+        # localectl set-x11-keymap es cat
+        cmd = ['localectl', 'set-x11-keymap', keyboard_layout]
+        if keyboard_variant:
+            cmd.append(keyboard_variant)
+        # Systemd based tools like localectl do not work inside a chroot
+        # This will set correct keymap to live media, we will copy
+        # the created files to destination
+        call(cmd)
+        # Copy 00-keyboard.conf and vconsole.conf files to destination
+        path = os.path.join(DEST_DIR, "etc/X11/xorg.conf.d")
+        os.makedirs(path, mode=0o755, exist_ok=True)
+        files = ["/etc/X11/xorg.conf.d/00-keyboard.conf", "/etc/vconsole.conf"]
+        for src in files:
+            try:
+                if os.path.exists(src):
+                    dst = os.path.join(DEST_DIR, src[1:])
+                    shutil.copy(src, dst)
+                    logging.debug("%s copied.", src)
+            except FileNotFoundError:
+                logging.error("File %s not found in live media", src)
+            except FileExistsError:
+                pass
+            except shutil.Error as err:
+                logging.error(err)
+  
+    @staticmethod
+    def get_installed_zfs_version():
+        """ Get installed zfs version """
+        zfs_version = "0.6.5.4"
+        path = "/install/usr/src"
+        for file_name in os.listdir(path):
+            if file_name.startswith("zfs") and not file_name.startswith("zfs-utils"):
+                try:
+                    zfs_version = file_name.split("-")[1]
+                    logging.info(
+                        "Installed zfs module's version: %s", zfs_version)
+                except KeyError:
+                    logging.warning("Can't get zfs version from %s", file_name)
+        return zfs_version
+
+    @staticmethod
+    def get_installed_kernel_versions():
+        """ Get installed kernel versions """
+        kernel_versions = []
+        path = "/install/usr/lib/modules"
+        for file_name in os.listdir(path):
+            if not file_name.startswith("extramodules"):
+                kernel_versions.append(file_name)
+        return kernel_versions
+
+    def set_desktop_settings(self):
+        """ Runs postinstall.sh that sets DE settings
+            Postinstall script uses arch-chroot, so we don't have to worry
+            about /proc, /dev, ... """
+        logging.debug("Running Cnchi post-install script")
+        keyboard_layout = self.settings.get("keyboard_layout")
+        keyboard_variant = self.settings.get("keyboard_variant")
+        # Call post-install script to fine tune our setup
+        script_path_postinstall = os.path.join(
+            self.settings.get('cnchi'),
+            "scripts",
+            PostInstallation.POSTINSTALL_SCRIPT)
+        cmd = [
+            "/usr/bin/bash",
+            script_path_postinstall,
+            self.settings.get('user_name'),
+            DEST_DIR,
+            self.desktop,
+            self.settings.get("locale"),
+            str(self.virtual_box),
+            keyboard_layout]
+
+        # Keyboard variant is optional
+        if keyboard_variant:
+            cmd.append(keyboard_variant)
+
+        call(cmd, timeout=300)
+        logging.debug("Post install script completed successfully.")
+
+    @staticmethod
+    def modify_makepkg():
+        """ Modify the makeflags to allow for threading.
+            Use threads for xz compression. """
+        makepkg_conf_path = os.path.join(DEST_DIR, 'etc/makepkg.conf')
+        if os.path.exists(makepkg_conf_path):
+            with open(makepkg_conf_path, 'r') as makepkg_conf:
+                contents = makepkg_conf.readlines()
+            with open(makepkg_conf_path, 'w') as makepkg_conf:
+                for line in contents:
+                    if '#MAKEFLAGS' in line:
+                        line = 'MAKEFLAGS="-j$(nproc)"\n'
+                    elif 'COMPRESSXZ' in line:
+                        line = 'COMPRESSXZ=(xz -c -z - --threads=0)\n'
+                    makepkg_conf.write(line)
+
+    def setup_user(self):
+        """ Set user parameters """
+        username = self.settings.get('user_name')
+        fullname = self.settings.get('user_fullname')
+        password = self.settings.get('user_password')
+        hostname = self.settings.get('hostname')
+
+        sudoers_dir = os.path.join(DEST_DIR, "etc/sudoers.d")
+        if not os.path.exists(sudoers_dir):
+            os.mkdir(sudoers_dir, 0o710)
+        sudoers_path = os.path.join(sudoers_dir, "10-installer")
+        try:
+            with open(sudoers_path, "w") as sudoers:
+                sudoers.write('{0} ALL=(ALL) ALL\n'.format(username))
+            os.chmod(sudoers_path, 0o440)
+            logging.debug("Sudo configuration for user %s done.", username)
+        except IOError as io_error:
+            # Do not fail if can't write 10-installer file.
+            # Something bad must be happening, though.
+            logging.error(io_error)
+
+        # Setup user
+
+        default_groups = 'wheel'
+
+        if self.virtual_box:
+            # Why there is no vboxusers group? Add it ourselves.
+            chroot_call(['groupadd', 'vboxusers'])
+            default_groups += ',vboxusers,vboxsf'
+            srv.enable_services(["vboxservice"])
+
+        if self.settings.get('require_password') is False:
+            # Prepare system for autologin.
+            # LightDM needs the user to be in the autologin group.
+            chroot_call(['groupadd', 'autologin'])
+            default_groups += ',autologin'
+
+        cmd = [
+            'useradd', '--create-home',
+            '--shell', '/bin/bash',
+            '--groups', default_groups,
+            username]
+        chroot_call(cmd)
+        logging.debug("User %s added.", username)
+
+        self.change_user_password(username, password)
+
+        chroot_call(['chfn', '-f', fullname, username])
+        home_dir = os.path.join("/home", username)
+        cmd = ['chown', '-R', '{0}:{0}'.format(username), home_dir]
+        chroot_call(cmd)
+
+        # Set hostname
+        hostname_path = os.path.join(DEST_DIR, "etc/hostname")
+        if not os.path.exists(hostname_path):
+            with open(hostname_path, "w") as hostname_file:
+                hostname_file.write(hostname)
+
+        logging.debug("Hostname set to %s", hostname)
+
+        # User password is the root password
+        self.change_user_password('root', password)
+        logging.debug("Set the same password to root.")
+
+        # set user's avatar
+        avatar = self.settings.get('user_avatar')
+        if avatar and os.path.exists(avatar):
+            try:
+                dst = os.path.join(
+                    DEST_DIR,
+                    'var/lib/AccountsService/icons',
+                    username + '.png')
+                shutil.copy(avatar, dst)
+            except FileNotFoundError:
+                logging.warning("Can't copy %s log to %s", avatar, dst)
+            except FileExistsError:
+                pass
+
+        ## Encrypt user's home directory if requested
+        if self.settings.get('encrypt_home'):
+            self.events.add('info', _("Encrypting user home dir..."))
+            gocryptfs.setup(username, "users", DEST_DIR, password)
+            logging.debug("User home dir encrypted")
+
+    @staticmethod
+    def nano_setup():
+        """ Enable colors and syntax highlighting in nano editor """
+        nanorc_path = os.path.join(DEST_DIR, 'etc/nanorc')
+        if os.path.exists(nanorc_path):
+            logging.debug(
+                "Enabling colors and syntax highlighting in nano editor")
+            with open(nanorc_path, 'a') as nanorc:
+                nanorc.write('\n')
+                nanorc.write('# Added by Cnchi (Antergos Installer)\n')
+                nanorc.write('set titlecolor brightwhite,blue\n')
+                nanorc.write('set statuscolor brightwhite,green\n')
+                nanorc.write('set numbercolor cyan\n')
+                nanorc.write('set keycolor cyan\n')
+                nanorc.write('set functioncolor green\n')
+                nanorc.write('include "/usr/share/nano/*.nanorc"\n')
+
+    def rebuild_zfs_modules(self):
+        """ Sometimes dkms tries to build the zfs module before spl. """
+        self.events.add('info', _("Building zfs modules..."))
+        zfs_version = self.get_installed_zfs_version()
+        spl_module = 'spl/{}'.format(zfs_version)
+        zfs_module = 'zfs/{}'.format(zfs_version)
+        kernel_versions = self.get_installed_kernel_versions()
+        if kernel_versions:
+            for kernel_version in kernel_versions:
+                logging.debug(
+                    "Installing zfs v%s modules for kernel %s", zfs_version, kernel_version)
+                chroot_call(
+                    ['dkms', 'install', spl_module, '-k', kernel_version])
+                chroot_call(
+                    ['dkms', 'install', zfs_module, '-k', kernel_version])
+        else:
+            # No kernel version found, try to install for current kernel
+            logging.debug(
+                "Installing zfs v%s modules for current kernel.", zfs_version)
+            chroot_call(['dkms', 'install', spl_module])
+            chroot_call(['dkms', 'install', zfs_module])
+
+    def pamac_setup(self):
+        """ Enable AUR in pamac if AUR feature selected """
+        pamac_conf = os.path.join(DEST_DIR, 'etc/pamac.conf')
+        if os.path.exists(pamac_conf) and self.settings.get('feature_aur'):
+            logging.debug("Enabling AUR options in pamac")
+            with open(pamac_conf, 'r') as pamac_conf_file:
+                file_data = pamac_conf_file.read()
+            file_data = file_data.replace("#EnableAUR", "EnableAUR")
+            file_data = file_data.replace(
+                "#SearchInAURByDefault", "SearchInAURByDefault")
+            file_data = file_data.replace(
+                "#CheckAURUpdates", "CheckAURUpdates")
+            with open(pamac_conf, 'w') as pamac_conf_file:
+                pamac_conf_file.write(file_data)
+
+    @staticmethod
+    def setup_timesyncd():
+        """ Setups and enables time sync service """
+        timesyncd_path = os.path.join(DEST_DIR, "etc/systemd/timesyncd.conf")
+        try:
+            with open(timesyncd_path, 'w') as timesyncd:
+                timesyncd.write("[Time]\n")
+                timesyncd.write("NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org "
+                                "2.arch.pool.ntp.org 3.arch.pool.ntp.org\n")
+                timesyncd.write("FallbackNTP=0.pool.ntp.org 1.pool.ntp.org "
+                                "0.fr.pool.ntp.org\n")
+        except FileNotFoundError as err:
+            logging.warning("Can't find %s file: %s", timesyncd_path, err)
+        chroot_call(['systemctl', '-fq', 'enable',
+                     'systemd-timesyncd.service'])
+
+    def check_btrfs(self):
+        """ Checks if any device will be using btrfs """
+        for mount_point in self.mount_devices:
+            partition_path = self.mount_devices[mount_point]
+            uuid = fs.get_uuid(partition_path)
+            if uuid and partition_path in self.fs_devices:
+                myfmt = self.fs_devices[partition_path]
+                if myfmt == 'btrfs':
+                    return True
+        return False
+
+    def configure_system(self, hardware_install):
+        """ Final install steps.
+            Set clock, language, timezone, run mkinitcpio,
+            populate pacman keyring, setup systemd services, ... """
+
+        self.events.add('pulse', 'start')
+        self.events.add('info', _("Configuring your new system"))
+
+        auto_fstab = PostFstab(
+            self.method, self.mount_devices, self.fs_devices, self.ssd, self.settings)
+        auto_fstab.run()
+        if auto_fstab.root_uuid:
+            self.settings.set('ruuid', auto_fstab.root_uuid)
+        logging.debug("fstab file generated.")
+
+        # Check if we have any btrfs device
+        if self.check_btrfs():
+            self.settings.set('btrfs', True)
+
+        # If SSD was detected copy udev rule for deadline scheduler
+        if self.ssd:
+            self.set_scheduler()
+            logging.debug("SSD udev rule copied successfully")
+
+        # Copy configured networks in Live medium to target system
+        if self.settings.get("network_manager") == "NetworkManager":
+            self.copy_network_config()
+
+        if self.desktop == "base":
+            # Setup systemd-networkd for systems that won't use the
+            # networkmanager or connman daemons (atm it's just base install)
+            # Enable systemd_networkd services
+            # https://github.com/Antergos/Cnchi/issues/332#issuecomment-108745026
+            srv.enable_services(["systemd-networkd", "systemd-resolved"])
+            # Setup systemd_networkd
+            # TODO: Ask user for SSID and passphrase if a wireless link is
+            # found (here or inside systemd_networkd.setup() ?)
+            systemd_networkd.setup()
+
+        logging.debug("Network configuration done.")
+
+        # Copy mirror list
+        mirrorlist_src_path = '/etc/pacman.d/mirrorlist'
+        mirrorlist_dst_path = os.path.join(DEST_DIR, 'etc/pacman.d/mirrorlist')
+        try:
+            shutil.copy2(mirrorlist_src_path, mirrorlist_dst_path)
+            logging.debug("Mirror list copied.")
+        except FileNotFoundError:
+            logging.error(
+                "Can't copy mirrorlist file. File %s not found",
+                mirrorlist_src_path)
+        except FileExistsError:
+            logging.warning("File %s already exists.", mirrorlist_dst_path)
+
+        # Add Antergos repo to /etc/pacman.conf
+        self.update_pacman_conf()
+        self.pacman_conf_updated = True
+        logging.debug("pacman.conf has been created successfully")
+
+        # Enable some useful services
+        services = []
+        if self.desktop != "base":
+            # In base there's no desktop manager ;)
+            services.append(self.settings.get("desktop_manager"))
+            # In base we use systemd-networkd (setup already done above)
+            services.append(self.settings.get("network_manager"))
+            # If bumblebee (optimus cards) is installed, enable it
+            if os.path.exists(os.path.join(DEST_DIR, "usr/lib/systemd/system/bumblebeed.service")):
+                services.extend(["bumblebee"])
+        services.extend(["ModemManager", "haveged"])
+        if self.method == "zfs":
+            # Beginning with ZOL version 0.6.5.8 the ZFS service unit files have
+            # been changed so that you need to explicitly enable any ZFS services
+            # you want to run.
+            services.extend(["zfs.target", "zfs-import-cache", "zfs-mount"])
+        srv.enable_services(services)
+
+        # Enable timesyncd service
+        if self.settings.get("use_timesyncd"):
+            self.setup_timesyncd()
+
+        # Set timezone
+        zone = self.settings.get("timezone_zone")
+        if zone:
+            zoneinfo_path = os.path.join("/usr/share/zoneinfo", zone)
+            localtime_path = "/etc/localtime"
+            chroot_call(['ln', '-sf', zoneinfo_path, localtime_path])
+            logging.debug("Timezone set to %s", zoneinfo_path)
+        else:
+            logging.warning(
+                "Can't read selected timezone! Will leave it to UTC.")
+
+        # Configure detected hardware
+        # NOTE: Because hardware can need extra repos, this code must run
+        # always after having called the update_pacman_conf method
+        if self.pacman_conf_updated and hardware_install:
+            try:
+                logging.debug("Running hardware drivers post-install jobs...")
+                hardware_install.post_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)
+
+        self.setup_user()
+
+        # Generate locales
+        locale = self.settings.get("locale")
+        self.events.add('info', _("Generating locales..."))
+        self.uncomment_locale_gen(locale)
+        chroot_call(['locale-gen'])
+        locale_conf_path = os.path.join(DEST_DIR, "etc/locale.conf")
+        with open(locale_conf_path, "w") as locale_conf:
+            locale_conf.write('LANG={0}\n'.format(locale))
+            locale_conf.write('LC_COLLATE={0}\n'.format(locale))
+
+        # environment_path = os.path.join(DEST_DIR, "etc/environment")
+        # with open(environment_path, "w") as environment:
+        #    environment.write('LANG={0}\n'.format(locale))
+
+        self.events.add('info', _("Adjusting hardware clock..."))
+        self.auto_timesetting()
+
+        self.events.add('info', _("Configuring keymap..."))
+        self.set_keymap()
+
+        # Install configs for root
+        chroot_call(['cp', '-av', '/etc/skel/.', '/root/'])
+
+        self.events.add('info', _("Configuring hardware..."))
+
+        # Copy generated xorg.conf to target
+        if os.path.exists("/etc/X11/xorg.conf"):
+            src = "/etc/X11/xorg.conf"
+            dst = os.path.join(DEST_DIR, 'etc/X11/xorg.conf')
+            shutil.copy2(src, dst)
+
+        # Configure ALSA
+        # self.alsa_mixer_setup()
+        #logging.debug("Updated Alsa mixer settings")
+
+        # Set pulse
+        # if os.path.exists(os.path.join(DEST_DIR, "usr/bin/pulseaudio-ctl")):
+        #    chroot_run(['pulseaudio-ctl', 'normal'])
+
+        # Set fluidsynth audio system (in our case, pulseaudio)
+        self.set_fluidsynth()
+        logging.debug("Updated fluidsynth configuration file")
+
+        # Workaround for pacman-key bug FS#45351
+        # https://bugs.archlinux.org/task/45351
+        # We have to kill gpg-agent because if it stays around we can't
+        # reliably unmount the target partition.
+        logging.debug("Stopping gpg agent...")
+        chroot_call(['killall', '-9', 'gpg-agent'])
+
+        # FIXME: Temporary workaround for spl and zfs packages
+        if self.method == "zfs":
+            self.rebuild_zfs_modules()
+
+        # Let's start without using hwdetect for mkinitcpio.conf.
+        # It should work out of the box most of the time.
+        # This way we don't have to fix deprecated hooks.
+        # NOTE: With LUKS or LVM maybe we'll have to fix deprecated hooks.
+        self.events.add('info', _("Configuring System Startup..."))
+        mkinitcpio.run(DEST_DIR, self.settings, self.mount_devices, self.blvm)
+
+        # Patch user-dirs-update-gtk.desktop
+        self.patch_user_dirs_update_gtk()
+        logging.debug("File user-dirs-update-gtk.desktop patched.")
+
+        # Set lightdm config including autologin if selected
+        if self.desktop != "base":
+            self.setup_display_manager()
+
+        # Configure user features (firewall, libreoffice language pack, ...)
+        #self.setup_features()
+        post_features = PostFeatures(DEST_DIR, self.settings)
+        post_features.setup()
+
+        # Install boot loader (always after running mkinitcpio)
+        if self.settings.get('bootloader_install'):
+            try:
+                self.events.add('info', _("Installing bootloader..."))
+                boot_loader = loader.Bootloader(
+                    DEST_DIR,
+                    self.settings,
+                    self.mount_devices)
+                boot_loader.install()
+            except Exception as ex:
+                template = "Cannot install bootloader. " \
+                    "An exception of type {0} occured. Arguments:\n{1!r}"
+                message = template.format(type(ex).__name__, ex.args)
+                logging.error(message)
+
+        # Create an initial database for mandb (slow)
+        #self.events.add('info', _("Updating man pages..."))
+        #chroot_call(["mandb", "--quiet"])
+
+        # Initialise pkgfile (pacman .files metadata explorer) database
+        logging.debug("Updating pkgfile database")
+        chroot_call(["pkgfile", "--update"])
+
+        if self.desktop != "base":
+            # avahi package seems to fail to create its user and group in some cases (¿?)
+            cmd = ["groupadd", "-r", "-g", "84", "avahi"]
+            chroot_call(cmd)
+            cmd = ["useradd", "-r", "-u", "84", "-g", "avahi", "-d", "/", "-s",
+                   "/bin/nologin", "-c", "avahi", "avahi"]
+            chroot_call(cmd)
+
+        # Install sonar (a11y) gsettings if present in the ISO (and a11y is on)
+        src = "/usr/share/glib-2.0/schemas/92_antergos_sonar.gschema.override"
+        if self.settings.get('a11y') and os.path.exists(src):
+            dst = os.path.join(DEST_DIR, 'usr/share/glib-2.0/schemas')
+            shutil.copy2(src, dst)
+
+        # Enable AUR in pamac if AUR feature selected
+        self.pamac_setup()
+
+        # Apply makepkg tweaks upon install (issue #871)
+        self.modify_makepkg()
+
+        self.nano_setup()
+
+        logging.debug("Setting .bashrc to load .bashrc.aliases")
+        bashrc_files = ["etc/skel/.bashrc"]
+        username = self.settings.get('user_name')
+        bashrc_files.append("home/{}/.bashrc".format(username))
+        for bashrc_file in bashrc_files:
+            bashrc_file = os.path.join(DEST_DIR, bashrc_file)
+            if os.path.exists(bashrc_file):
+                with open(bashrc_file, 'a') as bashrc:
+                    bashrc.write('\n')
+                    bashrc.write('if [ -e ~/.bashrc.aliases ] ; then\n')
+                    bashrc.write('   source ~/.bashrc.aliases\n')
+                    bashrc.write('fi\n')
+
+        # Fixes thermald service file
+        self.fix_thermald_service()
+
+        # Overwrite settings with Lembrame if enabled
+        # TODO: Rethink this function because we need almost everything but some things for Lembrame
+        if self.settings.get("feature_lembrame"):
+            logging.debug("Overwriting configs from Lembrame")
+            self.events.add('info', _("Overwriting configs from Lembrame"))
+
+            lembrame = Lembrame(self.settings)
+            lembrame.overwrite_content()
+
+        # This must be done at the end of the installation when using zfs
+        if self.method == "zfs":
+            logging.debug("Installation done, exporting ZFS pool")
+            pool_name = self.settings.get("zfs_pool_name")
+            cmd = ["zpool", "export", "-f", pool_name]
+            call(cmd)
diff --git a/Cnchi/postinstall.sh b/Cnchi/postinstall.sh
new file mode 100755 (executable)
index 0000000..db50504
--- /dev/null
@@ -0,0 +1,670 @@
+#!/usr/bin/bash
+# -*- coding: utf-8 -*-
+#
+#  postinstall.sh
+#
+#  Copyright © 2013-2016 Antergos
+#
+# Modifications 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/>.
+#
+# Set xorg config files
+set_xorg_touchpad() {
+    cp /usr/share/cnchi/scripts/postinstall/50-synaptics.conf ${CN_DESTDIR}/etc/X11/xorg.conf.d/50-synaptics.conf
+    cp /usr/share/cnchi/scripts/postinstall/99-killX.conf ${CN_DESTDIR}/etc/X11/xorg.conf.d/99-killX.conf
+
+    # Fix sensitivity for chromebooks
+    if lsmod | grep -q cyapa; then
+        cp /usr/share/cnchi/scripts/postinstall/50-cros-touchpad.conf ${CN_DESTDIR}/etc/X11/xorg.conf.d/50-cros-touchpad.conf
+    fi
+}
+
+set_xscreensaver() {
+    # xscreensaver config
+    cp /usr/share/cnchi/scripts/postinstall/xscreensaver ${CN_DESTDIR}/home/${CN_USER_NAME}/.xscreensaver
+    cp ${CN_DESTDIR}/home/${CN_USER_NAME}/.xscreensaver ${CN_DESTDIR}/etc/skel
+
+    if [[ -f ${CN_DESTDIR}/etc/xdg/autostart/xscreensaver.desktop ]]; then
+        rm ${CN_DESTDIR}/etc/xdg/autostart/xscreensaver.desktop
+    fi
+}
+
+set_gsettings() {
+    # Set gsettings input-source
+    CN_KEYBOARD=""
+    CN_INPUT_SCHEMA="${CN_DESTDIR}/usr/share/glib-2.0/schemas/90_antergos.input-sources.gschema.override"
+    if [[ "${CN_KEYBOARD_LAYOUT}" != '' ]]; then
+        if [[ "${CN_KEYBOARD_VARIANT}" != '' ]]; then
+            CN_KEYBOARD=${CN_KEYBOARD_LAYOUT}+${CN_KEYBOARD_VARIANT}
+        else
+            CN_KEYBOARD=${CN_KEYBOARD_LAYOUT}
+        fi
+        echo "[org.cinnamon.desktop.input-sources]" > ${CN_INPUT_SCHEMA}
+        echo "sources=[('xkb','${CN_KEYBOARD}')]" >> ${CN_INPUT_SCHEMA}
+        echo " " >> ${CN_INPUT_SCHEMA}
+        echo "[org.gnome.desktop.input-sources]" >> ${CN_INPUT_SCHEMA}
+        echo "sources=[('xkb','${CN_KEYBOARD}')]" >> ${CN_INPUT_SCHEMA}
+    fi
+
+    # Set default Internet browser
+    for CN_SCHEMA_OVERRIDE in ${CN_DESTDIR}/usr/share/glib-2.0/schemas/90_antergos*; do
+        if [ "${CN_BROWSER}" != "" ]; then
+            sed -i "s|chromium|${CN_BROWSER}|g" "${CN_SCHEMA_OVERRIDE}"
+        else
+            sed -i "s|'chromium.desktop',||g" "${CN_SCHEMA_OVERRIDE}"
+        fi
+    done
+
+    glib-compile-schemas "${CN_DESTDIR}/usr/share/glib-2.0/schemas"
+}
+
+set_dmrc() {
+    # Set session in .dmrc
+    echo "[Desktop]" > ${CN_DESTDIR}/home/${CN_USER_NAME}/.dmrc
+    echo "Session=$1" >> ${CN_DESTDIR}/home/${CN_USER_NAME}/.dmrc
+    chroot ${CN_DESTDIR} chown ${CN_USER_NAME}:users /home/${CN_USER_NAME}/.dmrc
+}
+
+common_settings() {
+    # Set skel directory (not needed, antergos-desktop-settings does this)
+    #cp -R ${CN_DESTDIR}/home/${CN_USER_NAME}/.config ${CN_DESTDIR}/etc/skel
+
+    # Set .bashrc (antergos-desktop-settings can't set it because it's already in bash package)
+    if [[ -f "${CN_DESTDIR}/etc/skel/bashrc" ]]; then
+        cp ${CN_DESTDIR}/etc/skel/bashrc ${CN_DESTDIR}/etc/skel/.bashrc
+        cp ${CN_DESTDIR}/etc/skel/bashrc ${CN_DESTDIR}/home/${CN_USER_NAME}/.bashrc
+    fi
+
+    # Setup root defaults
+    cp -R ${CN_DESTDIR}/etc/skel/. ${CN_DESTDIR}/root
+}
+
+gnome_settings() {
+    set_gsettings
+    set_xscreensaver
+
+    set_dmrc gnome
+}
+
+cinnamon_settings() {
+    set_gsettings
+    set_xscreensaver
+
+    # Copy menu@cinnamon.org.json to set menu icon
+    mkdir -p ${CN_DESTDIR}/home/${CN_USER_NAME}/.cinnamon/configs/menu@cinnamon.org/
+    cp -f /usr/share/cnchi/scripts/postinstall/menu@cinnamon.org.json ${CN_DESTDIR}/home/${CN_USER_NAME}/.cinnamon/configs/menu@cinnamon.org/
+
+    # Copy panel-launchers@cinnamon.org.json to set launchers
+    PANEL_LAUNCHER="/usr/share/cnchi/scripts/postinstall/panel-launchers@cinnamon.org.json"
+    if [[ firefox = "${CN_BROWSER}" ]]; then
+        sed -i 's|chromium|firefox|g' ${PANEL_LAUNCHER}
+    elif [ "${CN_BROWSER}" == "" ]; then
+        sed -i 's|"chromium.desktop",||g' ${PANEL_LAUNCHER}
+    fi
+
+    mkdir -p ${CN_DESTDIR}/home/${CN_USER_NAME}/.cinnamon/configs/panel-launchers@cinnamon.org/
+    cp -f /usr/share/cnchi/scripts/postinstall/panel-launchers@cinnamon.org.json ${CN_DESTDIR}/home/${CN_USER_NAME}/.cinnamon/configs/panel-launchers@cinnamon.org/
+
+    set_dmrc cinnamon
+}
+
+xfce_settings() {
+    set_gsettings
+    set_xscreensaver
+
+    # Set XFCE settings
+    mkdir -p ${CN_DESTDIR}/home/${CN_USER_NAME}/.config/xfce4/xfconf/xfce-perchannel-xml
+    cp -R ${CN_DESTDIR}/etc/xdg/xfce4/panel ${CN_DESTDIR}/etc/xdg/xfce4/helpers.rc ${CN_DESTDIR}/home/${CN_USER_NAME}/.config/xfce4
+
+    HELPERS_RC="${CN_DESTDIR}/home/${CN_USER_NAME}/.config/xfce4/helpers.rc"
+    if [[ ${CN_BROWSER} = "chromium" ]]; then
+        sed -i "s/WebBrowser=firefox/WebBrowser=chromium/" ${HELPERS_RC}
+    elif [ "${CN_BROWSER}" == "" ]; then
+        sed -i "s/WebBrowser=firefox//" ${HELPERS_RC}
+    fi
+
+    set_dmrc xfce
+
+    # Add lxpolkit to autostart apps
+    cp /etc/xdg/autostart/lxpolkit.desktop ${CN_DESTDIR}/home/${CN_USER_NAME}/.config/autostart
+}
+
+openbox_settings() {
+    set_gsettings
+    set_xscreensaver
+
+    set_dmrc openbox
+
+    # Set Numix theme in oblogout
+    if [[ -f /etc/oblogout.conf ]]; then
+        sed -i 's|buttontheme = oxygen|buttontheme = Numix|g' "${CN_DESTDIR}/etc/oblogout.conf"
+    fi
+}
+
+kde_settings() {
+    set_gsettings
+    set_xscreensaver
+
+    set_dmrc kde-plasma
+
+    # Force QtCurve to use our theme
+    rm -R ${CN_DESTDIR}/usr/share/kstyle/themes/qtcurve.themerc
+
+    # Setup user defaults
+    #if [ -f "${CN_DESTDIR}/usr/share/antergos-kde-setup/install.sh" ]; then
+    #    chroot ${CN_DESTDIR} /usr/share/antergos-kde-setup/install.sh ${CN_USER_NAME}
+    #elif [ -f "${CN_DESTDIR}/usr/share/antergos-desktop" ]; then
+    #    chroot ${CN_DESTDIR} /usr/bin/antergos-desktop plasma ${CN_USER_NAME}
+    #fi
+
+    cp ${CN_DESTDIR}/etc/skel/.gtkrc-2.0-kde4 ${CN_DESTDIR}/root
+    chroot ${CN_DESTDIR} "ln -s /root/.gtkrc-2.0-kde4 /root/.gtkrc-2.0"
+
+    # Set default directories
+    chroot ${CN_DESTDIR} su -c xdg-user-dirs-update ${CN_USER_NAME}
+}
+
+mate_settings() {
+    set_gsettings
+    set_xscreensaver
+
+    set_dmrc mate
+
+    # Set MintMenu Favorites
+    APP_LIST="/usr/share/cnchi/scripts/postinstall/applications.list"
+    if [[ "${CN_BROWSER}" = 'firefox' ]]; then
+        sed -i 's|chromium|firefox|g' ${APP_LIST}
+    elif [ "${CN_BROWSER}" == "" ]; then
+        sed -i 's|location:/usr/share/applications/chromium.desktop||g' ${APP_LIST}
+    fi
+
+    cp ${APP_LIST} "${CN_DESTDIR}/usr/lib/linuxmint/mintMenu/applications.list"
+
+    # Work-around for bug in mate-panel
+    CN_POST_INSTALL_DIR=/usr/share/cnchi/scripts/postinstall
+    CN_HOTFIX_SCRIPT="${CN_POST_INSTALL_DIR}/first-boot-hotfix.sh"
+    CN_HOTFIX_DESKTOP="${CN_POST_INSTALL_DIR}/first-boot-hotfix.desktop"
+    cp "${CN_HOTFIX_SCRIPT}" "${CN_DESTDIR}/usr/bin"
+    mkdir -p "${CN_DESTDIR}/home/${CN_USER_NAME}/.config/autostart"
+    cp "${CN_HOTFIX_DESKTOP}" "${CN_DESTDIR}/home/${CN_USER_NAME}/.config/autostart"
+    chmod +x "${CN_DESTDIR}/usr/bin/first-boot-hotfix.sh"
+}
+
+nox_settings() {
+    echo "Done"
+}
+
+# Experimental DE's
+
+lxqt_settings() {
+    set_gsettings
+    set_xscreensaver
+
+    set_dmrc razor
+}
+
+enlightenment_settings() {
+    set_gsettings
+    set_xscreensaver
+
+    # http://git.enlightenment.org/core/enlightenment.git/plain/data/tools/enlightenment_remote
+
+    # Setup user defaults
+    #chroot ${CN_DESTDIR} /usr/share/antergos-enlightenment-setup/install.sh ${CN_USER_NAME}
+    #chroot ${CN_DESTDIR} /usr/bin/antergos-desktop enlightenment ${CN_USER_NAME}
+
+    # Set Keyboard layout
+    E_CFG="/home/${CN_USER_NAME}/.e/e/config/standard/e.cfg"
+    E_SRC="/home/${CN_USER_NAME}/.e/e/config/standard/e.src"
+
+    ${CN_DESTDIR}/usr/bin/eet -d ${E_CFG} config ${E_SRC}
+    sed -i 's/"us"/"${CN_KEYBOARD_LAYOUT}"/' ${E_SRC}
+    if [[ "${CN_KEYBOARD_VARIANT}" != '' ]]; then
+        sed -i 's/"basic"/"${CN_KEYBOARD_VARIANT}"/' ${E_SRC}
+    fi
+    ${CN_DESTDIR}/usr/bin/eet -e ${E_CFG} config ${E_SRC} 1
+
+    set_dmrc enlightenment
+
+    echo "QT_STYLE_OVERRIDE=gtk" >> ${CN_DESTDIR}/etc/environment
+
+    # Add lxpolkit to autostart apps
+    cp /etc/xdg/autostart/lxpolkit.desktop ${CN_DESTDIR}/home/${CN_USER_NAME}/.config/autostart
+}
+
+budgie_settings() {
+    set_gsettings
+    set_xscreensaver
+
+    set_dmrc budgie
+}
+
+i3_settings() {
+    set_gsettings
+    set_xscreensaver
+
+    set_dmrc i3
+}
+
+postinstall() {
+    # Specific user configurations
+    if [[ -f /usr/share/applications/firefox.desktop ]]; then
+        export CN_BROWSER=firefox
+    elif [[ -f /usr/share/applications/chromium.desktop ]]; then
+        export CN_BROWSER=chromium
+    else
+        export CN_BROWSER=""
+    fi
+
+    # Workaround for LightDM bug https://bugs.launchpad.net/lightdm/+bug/1069218
+    sed -i 's|UserAccounts|UserList|g' "${CN_DESTDIR}/etc/lightdm/users.conf"
+
+    ## Unmute alsa channels
+    #chroot "${CN_DESTDIR}" amixer -c 0 -q set Master playback 50% unmute
+
+    # Configure touchpad. Skip with base installs
+    if [[ "base" != "${CN_DESKTOP}" ]]; then
+        set_xorg_touchpad
+    fi
+
+    # Fix ugly styles for Qt applications when running under GTK-based desktops and Qt 5.7+
+    if [[ kde != "${CN_DESKTOP}" && lxqt != "${CN_DESKTOP}" ]]; then
+        mkdir -p "${CN_DESTDIR}/home/${CN_USER_NAME}/.config/qt5ct" "${CN_DESTDIR}/etc/skel/qt5ct"
+        cp /usr/share/cnchi/scripts/postinstall/qt5ct.conf "${CN_DESTDIR}/etc/skel/qt5ct"
+        cp /usr/share/cnchi/scripts/postinstall/qt5ct.conf "${CN_DESTDIR}/home/${CN_USER_NAME}/.config/qt5ct"
+    fi
+
+    # Monkey patch session wrapper
+    cp /usr/share/cnchi/scripts/postinstall/Xsession "${CN_DESTDIR}/etc/lightdm"
+    chmod +x "${CN_DESTDIR}/etc/lightdm/Xsession"
+
+    # Configure fontconfig
+    FONTCONFIG_FILE="/usr/share/cnchi/scripts/fonts.conf"
+    if [[ -f "${FONTCONFIG_FILE}" ]]; then
+        FONTCONFIG_DIR="${CN_DESTDIR}/home/${CN_USER_NAME}/.config/fontconfig"
+        mkdir -p "${FONTCONFIG_DIR}"
+        cp "${FONTCONFIG_FILE}" "${FONTCONFIG_DIR}"
+    fi
+
+    # Set RebornOS name in filesystem files
+    cp /etc/arch-release "${CN_DESTDIR}/etc/"
+    cp /etc/os-release "${CN_DESTDIR}/etc/"
+    sed -i 's|Arch|RebornOS|g' "${CN_DESTDIR}/etc/issue"
+
+    # Set common desktop settigns
+    common_settings
+
+    # Set desktop-specific settings
+    ${CN_DESKTOP}_settings
+
+    # Set some environment vars
+    env_files=("${CN_DESTDIR}/etc/environment"
+        "${CN_DESTDIR}/home/${CN_USER_NAME}/.bashrc"
+        "${CN_DESTDIR}/etc/skel/.bashrc"
+    "${CN_DESTDIR}/etc/profile")
+
+    for file in "${env_files[@]}"
+    do
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based <---" >> "${file}"
+        if [ "${CN_BROWSER}" != "" ]; then
+            echo "BROWSER=/usr/bin/${CN_BROWSER}" >> "${file}"
+        fi
+        echo "EDITOR=/usr/bin/nano" >> "${file}"
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based <---" >> "${file}"
+    done
+
+    # Uncomplicated Firewall Workaround (Rafael from RebornOS)
+    cp /usr/share/cnchi/gufw.svg ${CN_DESTDIR}/usr/share/icons/default/gufw.svg
+    cp /usr/share/cnchi/gufw.png ${CN_DESTDIR}/usr/share/icons/hicolor/48x48/apps/gufw.png
+    cp /usr/share/cnchi/gufw.desktop ${CN_DESTDIR}/usr/share/applications/gufw.desktop
+
+    # Configure makepkg so that it doesn't compress packages after building.
+    # Most users are building packages to install them locally so there's no need for compression.
+    sed -i "s|^PKGEXT='.pkg.tar.xz'|PKGEXT='.pkg.tar'|g" "${CN_DESTDIR}/etc/makepkg.conf"
+
+    # Set lightdm-webkit2-greeter in lightdm.conf. This should have been done here (not in the pkg) all along.
+    if [[ deepin = "${CN_DESKTOP}" ]]; then
+        rm ${CN_DESTDIR}/etc/lightdm/lightdm.conf
+        cp /etc/lightdm/lightdm.conf ${CN_DESTDIR}/etc/lightdm/
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd deepin-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd qt5ct --noconfirm --> Not installed now
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/deepin-fix.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/deepin-fix.service ${CN_DESTDIR}/etc/systemd/system/
+        chroot ${CN_DESTDIR} sudo systemctl enable deepin-fix.service
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+    fi
+
+    if [[ gnome = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable gdm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd gnome-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based for Gnome Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        echo "QT_QPA_PLATFORMTHEME=qt5ct" >> ${CN_DESTDIR}/etc/environment
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based for Gnome Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+    fi
+
+    if [[ apricity = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable gdm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd gnome-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based for Apricity Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        echo "QT_QPA_PLATFORMTHEME=qt5ct" >> ${CN_DESTDIR}/etc/environment
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based for Apricity Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+    fi
+
+    if [[ kde = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable sddm
+        cp /usr/share/cnchi/sddm.conf ${CN_DESTDIR}/etc/
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd kde-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+    fi
+
+    if [[ budgie = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable lightdm
+           sed -i 's/^webkit_theme\s*=\s*\(.*\)/webkit_theme = lightdm-webkit-theme-aether #\1/g' ${CN_DESTDIR}/etc/lightdm/lightdm-webkit2-greeter.conf
+           sed -i 's/^\(#?greeter\)-session\s*=\s*\(.*\)/greeter-session = lightdm-webkit2-greeter #\1/ #\2g' ${CN_DESTDIR}/etc/lightdm/lightdm.conf
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd budgie-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based for Budgie Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        echo "QT_QPA_PLATFORMTHEME=qt5ct" >> ${CN_DESTDIR}/etc/environment
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based for Budgie Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+    fi
+
+    if [[ i3 = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable sddm
+        cp /usr/share/cnchi/sddm.conf ${CN_DESTDIR}/etc/
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd i3-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based for i3 Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        echo "QT_QPA_PLATFORMTHEME=qt5ct" >> ${CN_DESTDIR}/etc/environment
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based for i3 Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+    fi
+
+    if [ -f "${CN_DESTDIR}/usr/bin/enlightenment" ]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable sddm
+        cp /usr/share/cnchi/sddm.conf ${CN_DESTDIR}/etc/
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd enlightenment-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based for Enlightenment Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        echo "QT_QPA_PLATFORMTHEME=qt5ct" >> ${CN_DESTDIR}/etc/environment
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based for Enlightenment Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+    fi
+
+    if [[ lxqt = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable sddm
+        cp /usr/share/cnchi/sddm.conf ${CN_DESTDIR}/etc/
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd lxqt-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+    fi
+
+    if [ -f "${CN_DESTDIR}/usr/bin/lxsession" ]; then
+        rm ${CN_DESTDIR}/etc/lightdm/lightdm-webkit2-greeter.conf
+        cp /usr/share/cnchi/lightdm-webkit2-greeter.conf ${CN_DESTDIR}/etc/lightdm/
+        chmod go=rx ${CN_DESTDIR}/var/lib/lightdm-data
+        chroot ${CN_DESTDIR} systemctl -fq enable lxdm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd openbox-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/conky-start.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/tint2-start.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/obmenu-gen.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/openbox-config.sh ${CN_DESTDIR}/usr/bin/
+        chroot ${CN_DESTDIR} obmenu-generator -p -i
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+    fi
+
+    if [[ mate = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable sddm
+        cp /usr/share/cnchi/sddm.conf ${CN_DESTDIR}/etc/
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd mate-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        chroot ${CN_DESTDIR} mate-panel --reset --layout default
+        # chroot ${CN_DESTDIR} mate-panel --reset --layout cinnamon
+        # chroot ${CN_DESTDIR} sudo mate-panel --reset --layout cinnamon
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based for MATE Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        echo "QT_QPA_PLATFORMTHEME=qt5ct" >> ${CN_DESTDIR}/etc/environment
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based for MATE Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+    fi
+
+if [[ xfce = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable sddm
+        cp /usr/share/cnchi/sddm.conf ${CN_DESTDIR}/etc/
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd xfce-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based for XFCE Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        echo "QT_QPA_PLATFORMTHEME=qt5ct" >> ${CN_DESTDIR}/etc/environment
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based for XFCE Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+fi
+
+if [[ cinnamon = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable sddm
+        cp /usr/share/cnchi/sddm.conf ${CN_DESTDIR}/etc/
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd cinnamon-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based for Cinnamon Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        echo "QT_QPA_PLATFORMTHEME=qt5ct" >> ${CN_DESTDIR}/etc/environment
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based for Cinnamon Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+fi
+
+if [[ pantheon = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable sddm
+        cp /usr/share/cnchi/sddm.conf ${CN_DESTDIR}/etc/
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd elementary-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based for Pantheon Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        echo "QT_QPA_PLATFORMTHEME=qt5ct" >> ${CN_DESTDIR}/etc/environment
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based for Pantheon Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+fi
+
+if [[ windows = "${CN_DESKTOP}" ]]; then
+        chroot ${CN_DESTDIR} systemctl -fq enable sddm
+        cp /usr/share/cnchi/sddm.conf ${CN_DESTDIR}/etc/
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd cinnamon-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd common-cosmic-reborn --noconfirm
+        # chroot ${CN_DESTDIR} sudo pacman -Rdd graphic-cosmic-reborn --noconfirm
+        cp /usr/share/cnchi/flatpak.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/pkcon2.sh ${CN_DESTDIR}/usr/bin/
+        cp /usr/share/cnchi/flatpak.desktop ${CN_DESTDIR}/usr/share/applications/
+        cp /usr/share/cnchi/update.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+        cp /usr/share/cnchi/updating.sh ${CN_DESTDIR}/usr/bin/
+        echo "# ---> Added by Cnchi RebornOS Installer Gnome based for Windows Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        echo "QT_QPA_PLATFORMTHEME=qt5ct" >> ${CN_DESTDIR}/etc/environment
+        echo "# ---> End added by Cnchi RebornOS Installer Gnome based for Windows Desktop <---" >> ${CN_DESTDIR}/etc/environment
+        chroot ${CN_DESTDIR} systemctl enable earlyoom
+        chroot ${CN_DESTDIR} systemctl enable ufw
+fi
+
+    # Copy pacman.conf file over
+    rm ${CN_DESTDIR}/etc/pacman.conf
+    cp /usr/share/cnchi/pacman.conf ${CN_DESTDIR}/etc/
+    cp /etc/pacman.d/reborn-mirrorlist ${CN_DESTDIR}/etc/pacman.d/
+
+    #Copy blacklist.conf file over
+    cp /etc/modprobe.d/blacklist.conf ${CN_DESTDIR}/etc/modprobe.d/
+
+    #Copy Plymouth Files over if the Plymouth feature has been selected
+    if [ -f "${CN_DESTDIR}/usr/bin/plymouth" ]; then
+    echo "[STATUS] Plymouth selected. Configuring now..." >${CN_DESTDIR}/var/log/cnchi/plymouth.log
+    echo "[STATUS] Plymouth selected. Configuring now..." >/tmp/cnchi.log
+    cp /usr/share/cnchi/plymouth.sh ${CN_DESTDIR}/usr/bin/
+    cp /usr/share/cnchi/plymouth-reborn.desktop ${CN_DESTDIR}/etc/xdg/autostart/
+    chroot ${CN_DESTDIR} plymouth-set-default-theme -R arch-charge-big
+    echo "[SUCCESS] Plymouth has been installed and configured" >${CN_DESTDIR}/var/log/cnchi/plymouth.log
+    echo "[SUCCESS] Plymouth has been installed and configured" >/tmp/cnchi.log
+    else
+    echo "[STATUS] Plymouth not selected" >${CN_DESTDIR}/var/log/cnchi/plymouth.log
+    echo "[STATUS] Plymouth not selected" >/tmp/postinstall.log
+    fi
+
+    # Ensure user permissions are set in /home
+    chroot "${CN_DESTDIR}" chown -R "${CN_USER_NAME}:users" "/home/${CN_USER_NAME}"
+
+    # Remove reborn user if it still exists
+    if [ -d "${CN_DESTDIR}/home/reborn" ]; then
+    chroot ${CN_DESTDIR} sudo rm -rf /home/reborn
+    fi
+    
+    # Remove rebornos user if it still exists
+    if [ -d "${CN_DESTDIR}/home/rebornos" ]; then
+    chroot ${CN_DESTDIR} sudo rm -rf /home/reborn
+    fi
+
+    # Start vbox client services if we are installed in vbox
+    if [[ ${CN_IS_VBOX} = "True" ]] || { [[ $(systemd-detect-virt) ]] && [[ 'oracle' = $(systemd-detect-virt -v) ]]; }; then
+        # TODO: This should be done differently
+        sed -i 's|echo "X|/usr/bin/VBoxClient-all \&\necho "X|g' "${CN_DESTDIR}/etc/lightdm/Xsession"
+    fi
+}
+
+touch /tmp/.postinstall.lock
+echo "Called installation script with these parameters: [$1] [$2] [$3] [$4] [$5] [$6] [$7]" > /tmp/postinstall.log
+CN_USER_NAME=$1
+CN_DESTDIR=$2
+CN_DESKTOP=$3
+CN_LOCALE=$4
+CN_IS_VBOX=$5
+CN_KEYBOARD_LAYOUT=$6
+CN_KEYBOARD_VARIANT=$7
+
+# Use this to test this script (remember to mount /install manually before testing)
+#chroot_setup "${CN_DESTDIR}"
+
+{ postinstall; } >> /tmp/postinstall.log 2>&1
+rm /tmp/.postinstall.lock
+
diff --git a/Cnchi/rank_mirrors.py b/Cnchi/rank_mirrors.py
new file mode 100755 (executable)
index 0000000..e7f71e3
--- /dev/null
@@ -0,0 +1,411 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# rank_mirrors.py
+#
+# Copyright © 2012, 2013 Xyne
+# 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 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/>.
+
+
+""" Creates mirrorlist sorted by both latest updates and fastest connection """
+
+import http.client
+import logging
+import multiprocessing
+import os
+import queue
+import subprocess
+import threading
+import time
+import urllib.request
+import urllib.error
+
+import feedparser
+import requests
+
+import update_db
+import misc.extra as misc
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+class RankMirrors(multiprocessing.Process):
+    """ Process class that downloads and sorts the mirrorlist """
+
+    REPOSITORIES = ['arch']
+    MIRROR_OK_RSS = 'Alert Details: Successful response received'
+
+    MIRROR_STATUS = {
+        'arch': 'http://www.archlinux.org/mirrors/status/json/'}
+
+    MIRRORLIST = {
+        'arch': '/etc/pacman.d/mirrorlist'}
+
+    MIRRORLIST_URL = {
+        'arch': "https://www.archlinux.org/mirrorlist/all/"}
+
+    DB_SUBPATHS = {
+        'arch': 'core/os/x86_64/{0}-{1}-x86_64.pkg.tar.xz'}
+
+    def __init__(self, fraction_pipe, settings):
+        """ Initialize process class
+            fraction_pipe is a pipe used to send progress for a gtk.progress widget update
+            in another process (see start_rank_mirrors() in mirrors.py) """
+        super(RankMirrors, self).__init__()
+        self.settings = settings
+        self.fraction_pipe = fraction_pipe
+        # Antergos mirrors info is returned as RSS, arch's as JSON
+        self.data = {'arch': {}}
+        self.mirrorlist_ranked = {'arch': []}
+
+    @staticmethod
+    def is_good_mirror(mirror):
+        """ Check if mirror info is good enough """
+        if 'summary' in mirror.keys():
+            # RSS antergos status mirror
+            return bool(mirror['summary'] == RankMirrors.MIRROR_OK_RSS)
+
+        # JSON arch status mirror
+        return (mirror['last_sync'] and
+                mirror['completion_pct'] == 1.0 and
+                mirror['protocol'] == 'http' and
+                int(mirror['delay']) <= 3600)
+
+    def get_mirror_stats(self):
+        """ Retrieve all mirrors status RSS data. """
+        # Load status data (JSON) for arch mirrors
+        if not self.data['arch']:
+            try:
+                req = requests.get(
+                    RankMirrors.MIRROR_STATUS['arch'],
+                    headers={'User-Agent': 'Mozilla/5.0'}
+                )
+                self.data['arch'] = req.json()
+            except requests.RequestException as err:
+                logging.warning(
+                    'Failed to retrieve mirror status information: %s', err)
+
+        # Load status data (RSS) for antergos mirrors
+#        if not self.data['antergos']:
+#            self.data['antergos'] = feedparser.parse(
+#                RankMirrors.MIRROR_STATUS['antergos'])
+
+        mirrors = {'arch': []}
+
+        try:
+            # Filter incomplete mirrors and mirrors that haven't synced.
+            mirrors['arch'] = self.data['arch']['urls']
+            mirrors['arch'] = [m for m in mirrors['arch'] if self.is_good_mirror(m)]
+            #self.data['arch']['urls'] = mirrors['arch']
+        except KeyError as err:
+            logging.warning('Failed to parse retrieved mirror data: %s', err)
+
+        mirror_urls = []
+#        for mirror in self.data['antergos']['entries']:
+#            title = mirror['title']
+#            if "is UP" in title:
+#                # In RSS, all mirrors are in http:// format, we prefer https://
+#                mirror['url'] = mirror['link'].replace('http://', 'https://')
+#                if mirror['url'] not in mirror_urls:
+#                    mirrors['antergos'].append(mirror)
+#                    mirror_urls.append(mirror['url'])
+
+        return mirrors
+
+    @staticmethod
+    def get_antergos_mirror_url(mirror_url):
+        """ Get full mirror url from the stats mirror url """
+        lines = []
+        mirrorlist_path = RankMirrors.MIRRORLIST['antergos']
+        with open(mirrorlist_path, 'r') as mirror_file:
+            lines = mirror_file.readlines()
+        for line in lines:
+            if mirror_url in line:
+                return line.split('=')[1].strip()
+        logging.warning("%s not found in %s", mirror_url, mirrorlist_path)
+        return None
+
+    @staticmethod
+    def get_package_version(name):
+        """ Returns pkg_name package version """
+        try:
+            cmd = ["/usr/bin/pacman", "-Ss", name]
+            line = subprocess.check_output(cmd).decode().split()
+            version = line[1]
+            logging.debug(
+                '%s version is: %s (used to test mirror speed)', name, version)
+        except subprocess.CalledProcessError as err:
+            logging.warning(err)
+            version = False
+        return version
+
+    def sort_mirrors_by_speed(self, mirrors=None, max_threads=8):
+        """ Sorts mirror list """
+
+        test_packages = {
+            'arch': {'name':'cryptsetup', 'version': ''}}
+
+        rated_mirrors = {'arch': []}
+
+        for key, value in test_packages.items():
+            test_packages[key]['version'] = self.get_package_version(value['name'])
+
+        total_num_mirrors = 0
+        for key in mirrors.keys():
+            total_num_mirrors += len(mirrors[key])
+        num_mirrors_done = 0
+        old_fraction = -1
+
+        num_threads = min(max_threads, total_num_mirrors)
+        # URL input queue.Queue
+        q_in = queue.Queue()
+        # URL and rate output queue.Queue
+        q_out = queue.Queue()
+
+        name = ""
+        version = ""
+        rates = {}
+
+        for repo in RankMirrors.REPOSITORIES:
+            name = test_packages[repo]['name']
+            version = test_packages[repo]['version']
+
+            def worker():
+                """ worker thread. Retrieves data to test mirror speed """
+                while not q_in.empty():
+                    mirror_url, full_url = q_in.get()
+                    # Leave the rate as 0 if the connection fails.
+                    rate = 0
+                    dtime = float('NaN')
+                    if full_url:
+                        req = urllib.request.Request(url=full_url)
+                        try:
+                            time0 = time.time()
+                            with urllib.request.urlopen(req, None, 5) as my_file:
+                                size = len(my_file.read())
+                                dtime = time.time() - time0
+                                rate = size / dtime
+                        except (OSError, urllib.error.HTTPError,
+                                http.client.HTTPException) as err:
+                            logging.warning("Couldn't download %s", full_url)
+                            logging.warning(err)
+                    q_out.put((mirror_url, full_url, rate, dtime))
+                    q_in.task_done()
+
+            # Load the input queue.Queue
+            url_len = 0
+            for mirror in mirrors[repo]:
+                url_len = max(url_len, len(mirror['url']))
+                if repo == 'antergos':
+                    url = self.get_antergos_mirror_url(mirror['url'])
+                    # Save mirror url
+                    mirror['url'] = url
+                    if url is None:
+                        package_url = None
+                    else:
+                        # Compose package url
+                        package_url = url.replace('$repo', 'antergos').replace('$arch', 'x86_64')
+                        package_url += RankMirrors.DB_SUBPATHS['antergos'].format(name, version)
+                else:
+                    package_url = mirror['url']
+                if mirror['url'] and package_url:
+                    q_in.put((mirror['url'], package_url))
+
+            # Launch threads
+            my_threads = []
+            for _index in range(num_threads):
+                my_thread = threading.Thread(target=worker)
+                my_thread.start()
+                my_threads.append(my_thread)
+
+            # Remove mirrors that are not present in antergos-mirrorlist
+            if repo == 'antergos':
+                mirrors_pruned = []
+                for mirror in mirrors[repo]:
+                    if mirror['url'] is not None:
+                        mirrors_pruned.append(mirror)
+                mirrors[repo] = mirrors_pruned
+
+            # Wait for queue to empty
+            while not q_in.empty():
+                fraction = (float(q_out.qsize()) + num_mirrors_done) / float(total_num_mirrors)
+                if fraction != old_fraction:
+                    if self.fraction_pipe:
+                        self.fraction_pipe.send(fraction)
+                    old_fraction = fraction
+
+            num_mirrors_done += q_out.qsize()
+
+            # Wait for all threads to complete
+            q_in.join()
+
+            # Log some extra data.
+            url_len = str(url_len)
+            fmt = '%-' + url_len + 's  %14s  %9s'
+            logging.debug(fmt, _("Server"), _("Rate"), _("Time"))
+
+            # Loop over the mirrors just to ensure that we get the rate for each.
+            # The value in the loop does not (necessarily) correspond to the mirror.
+            fmt = '%-' + url_len + 's  %8.2f KiB/s  %7.2f s'
+            for mirror in mirrors[repo]:
+                url, full_url, rate, dtime = q_out.get()
+                if full_url:
+                    kibps = rate / 1024.0
+                    logging.debug(fmt, url, kibps, dtime)
+                    rates[url] = rate
+                q_out.task_done()
+
+            # Wait for all threads to finnish (all will be finished, but...)
+            for my_thread in my_threads:
+                my_thread.join()
+
+            # Sort mirrors by rate
+            try:
+                rated_mirrors[repo] = [m for m in mirrors[repo] if rates[m['url']] > 0]
+                rated_mirrors[repo].sort(key=lambda m: rates[m['url']], reverse=True)
+            except KeyError as err:
+                logging.warning(err)
+
+        return rated_mirrors
+
+    @staticmethod
+    def uncomment_mirrors():
+        """ Uncomment mirrors and comment out auto selection so
+        rankmirrors can find the best mirror. """
+
+#        comment_urls = [
+#            'http://mirrors.antergos.com/$repo/$arch',
+#            'sourceforge']
+
+        for repo in RankMirrors.REPOSITORIES:
+            if os.path.exists(RankMirrors.MIRRORLIST[repo]):
+                with open(RankMirrors.MIRRORLIST[repo]) as mirrors:
+                    lines = [x.strip() for x in mirrors.readlines()]
+
+                for i, line in enumerate(lines):
+                    if line.startswith("#Server"):
+                        # if server is commented, uncoment it.
+                        lines[i] = line.lstrip("#")
+
+                    if line.startswith("Server"):
+                        # Let's see if we have to comment out this server
+                        for url in comment_urls:
+                            if url in line:
+                                lines[i] = '#' + line
+
+                # Write new one
+                with misc.raised_privileges():
+                    try:
+                        with open(RankMirrors.MIRRORLIST[repo], 'w') as mirrors_file:
+                            mirrors_file.write("\n".join(lines) + "\n")
+                    except (OSError, PermissionError) as err:
+                        logging.error(err)
+        update_db.sync()
+
+    def filter_and_sort_mirrorlists(self):
+        """ Filter and sort mirrors """
+
+        mlist = self.get_mirror_stats()
+        mirrors = self.sort_mirrors_by_speed(mirrors=mlist)
+
+        for repo in ['arch']:
+            self.mirrorlist_ranked[repo] = []
+
+        for repo in ['arch']:
+            output = '# {} mirrorlist generated by cnchi #\n'.format(repo)
+            for mirror in mirrors[repo]:
+                self.mirrorlist_ranked[repo].append(mirror['url'])
+                if repo == 'arch':
+                    output += "Server = {0}{1}/os/{2}\n".format(mirror['url'], '$repo', '$arch')
+                else:
+                    output += "Server = {0}\n".format(mirror['url'])
+
+            # Write modified mirrorlist
+            with misc.raised_privileges():
+                try:
+                    with open(RankMirrors.MIRRORLIST[repo], 'w') as mirrors_file:
+                        mirrors_file.write(output)
+                except (OSError, PermissionError) as err:
+                    logging.error(err)
+                update_db.sync()
+
+    def run(self):
+        """ Run process """
+        # Wait until there is an Internet connection available
+        while not misc.has_connection():
+            time.sleep(2)  # Delay, try again after 2 seconds
+
+        logging.debug("Updating both mirrorlists (Arch and Antergos)...")
+        self.update_mirrorlists()
+
+        self.uncomment_mirrors()
+
+        logging.debug("Filtering and sorting mirrors...")
+        self.filter_and_sort_mirrorlists()
+
+        if self.settings:
+            self.mirrorlist_ranked['arch'] = [
+                x for x in self.mirrorlist_ranked['arch'] if x]
+            self.settings.set('rankmirrors_result', self.mirrorlist_ranked['arch'])
+
+        if self.fraction_pipe:
+            self.fraction_pipe.send(1)
+            self.fraction_pipe.close()
+
+        logging.debug("Auto mirror selection has been run successfully.")
+
+
+    @staticmethod
+    def update_mirrorlists():
+        """ Download mirror lists from archlinux and github """
+        for repo in RankMirrors.REPOSITORIES:
+            url = RankMirrors.MIRRORLIST_URL[repo]
+            req = urllib.request.Request(url=url)
+            try:
+                with urllib.request.urlopen(req, None, 5) as my_file:
+                    data = my_file.read()
+                with misc.raised_privileges():
+                    with open(RankMirrors.MIRRORLIST[repo], 'wb') as mirror_file:
+                        mirror_file.write(data)
+            except (OSError, urllib.error.HTTPError, http.client.HTTPException) as err:
+                logging.warning("Couldn't download %s", url)
+                logging.warning(err)
+
+def test_module():
+    """ Helper function to test this module """
+    logger = logging.getLogger()
+    logger.setLevel(logging.DEBUG)
+
+    proc = RankMirrors(None, None)
+    proc.daemon = True
+    proc.name = "rankmirrors"
+    proc.start()
+    proc.join()
+
+if __name__ == '__main__':
+    test_module()
diff --git a/Cnchi/reborn-icon-new.png b/Cnchi/reborn-icon-new.png
new file mode 100755 (executable)
index 0000000..181725b
Binary files /dev/null and b/Cnchi/reborn-icon-new.png differ
diff --git a/Cnchi/reborn-mirrorlist b/Cnchi/reborn-mirrorlist
new file mode 100644 (file)
index 0000000..d23e920
--- /dev/null
@@ -0,0 +1,9 @@
+################################################################################
+#################### Reborn OS mirrorlist generated by hand ####################
+################################################################################
+
+# US Server
+Server = https://repo.rebornos.org/RebornOS/
+
+# European Server
+Server = http://repo-de.rebornos.org/Reborn-OS/
diff --git a/Cnchi/reborn-mirrorlist2 b/Cnchi/reborn-mirrorlist2
new file mode 100644 (file)
index 0000000..5c1035f
--- /dev/null
@@ -0,0 +1,9 @@
+################################################################################
+#################### Reborn OS mirrorlist generated by hand ####################
+################################################################################
+
+# US Server
+Server = https://repo.rebornos.org/RebornOS/
+
+# European Server
+#Server = https://repo.itmettke.de/Reborn-OS/
diff --git a/Cnchi/refresh-keys.desktop b/Cnchi/refresh-keys.desktop
new file mode 100644 (file)
index 0000000..7fef0fe
--- /dev/null
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Type=Application
+Name=refresh-keys.desktop
+Comment=Execute cnchi-start.sh at first boot.
+Exec=/usr/bin/refresh-keys.sh
+GenericName=Refresh Keys
+Terminal=false
+NoDisplay=true
+X-DEEPIN-Autostart-enabled=true
+X-GNOME-Autostart-enabled=true
+
diff --git a/Cnchi/refresh-keys.sh b/Cnchi/refresh-keys.sh
new file mode 100755 (executable)
index 0000000..56667c9
--- /dev/null
@@ -0,0 +1,8 @@
+gsettings set org.gnome.shell favorite-apps "['firefox.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.gedit.desktop', 'gparted.desktop', 'antergos-install.desktop', 'org.gnome.Terminal.desktop']"
+sudo systemctl start NetworkManager.service
+sudo rm -rf /etc/pacman.d/gnupg
+sudo pacman-key --init
+sudo pacman-key --populate archlinux rebornos
+sudo pacman -Syy
+sudo cnchi-start.sh
+
diff --git a/Cnchi/sddm.conf b/Cnchi/sddm.conf
new file mode 100644 (file)
index 0000000..2912a5b
--- /dev/null
@@ -0,0 +1,38 @@
+# Generated by SDDM Configuration Editor
+[Autologin]
+Relogin=false
+Session=
+User=
+
+[Theme]
+Current=gracilis
+DisableAvatarsThreshold=7
+EnableAvatars=true
+FacesDir=/usr/share/sddm/faces
+ThemeDir=/usr/share/sddm/themes
+
+[Users]
+DefaultPath=/usr/local/bin:/usr/bin:/bin
+MaximumUid=60000
+MinimumUid=1000
+RememberLastSession=true
+RememberLastUser=true
+
+[Wayland]
+SessionCommand=/usr/share/sddm/scripts/wayland-session
+SessionDir=/usr/share/wayland-sessions
+SessionLogFile=.local/share/sddm/wayland-session.log
+
+[X11]
+DisplayCommand=/usr/share/sddm/scripts/Xsetup
+DisplayStopCommand=/usr/share/sddm/scripts/Xstop
+MinimumVT=1
+ServerArguments=-nolisten tcp
+ServerPath=/usr/bin/X
+SessionCommand=/usr/share/sddm/scripts/Xsession
+SessionDir=/usr/share/xsessions
+SessionLogFile=.local/share/sddm/xorg-session.log
+UserAuthFile=.Xauthority
+XauthPath=/usr/bin/xauth
+XephyrPath=/usr/bin/Xephyr
+
diff --git a/Cnchi/select_packages.py b/Cnchi/select_packages.py
new file mode 100755 (executable)
index 0000000..088be45
--- /dev/null
@@ -0,0 +1,526 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# select_packages.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.
+
+""" Package list generation module. """
+
+import logging
+import os
+import subprocess
+import requests
+from requests.exceptions import RequestException
+
+import xml.etree.cElementTree as elementTree
+
+import desktop_info
+
+import pacman.pac as pac
+
+from misc.events import Events
+import misc.extra as misc
+from misc.extra import InstallError
+
+import hardware.hardware as hardware
+
+from lembrame.lembrame import Lembrame
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+
+class SelectPackages():
+    """ Package list creation class """
+
+    PKGLIST_URL = 'https://gitlab.com/reborn-os-team/cnchi/blob/master/Cnchi/packages.xml'
+
+    def __init__(self, settings, callback_queue):
+        """ Initialize package class """
+
+        self.events = Events(callback_queue)
+        self.settings = settings
+        self.desktop = self.settings.get('desktop')
+
+        # Packages to be removed
+        self.conflicts = []
+
+        # Packages to be installed
+        self.packages = []
+
+        self.vbox = False
+
+        self.xml_root = None
+
+        # If Lembrame enabled set pacman.conf pointing to the decrypted folder
+        if self.settings.get('feature_lembrame'):
+            self.lembrame = Lembrame(self.settings)
+            path = os.path.join(self.lembrame.config.folder_file_path, 'pacman.conf')
+            self.settings.set('pacman_config_file', path)
+
+    def create_package_list(self):
+        """ Create package list """
+
+        # Common vars
+        self.packages = []
+
+        logging.debug("Refreshing pacman databases...")
+        self.refresh_pacman_databases()
+        logging.debug("Pacman ready")
+
+        logging.debug("Selecting packages...")
+        self.select_packages()
+        logging.debug("Packages selected")
+
+        # Fix bug #263 (v86d moved from [extra] to AUR)
+        if "v86d" in self.packages:
+            self.packages.remove("v86d")
+            logging.debug("Removed 'v86d' package from list")
+
+        if self.vbox:
+            self.settings.set('is_vbox', True)
+
+    @misc.raise_privileges
+    def refresh_pacman_databases(self):
+        """ Updates pacman databases """
+        # Init pyalpm
+        try:
+            pacman = pac.Pac(self.settings.get('pacman_config_file'), self.events.queue)
+        except Exception as ex:
+            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 pacman.refresh():
+            logging.error("Can't refresh pacman databases.")
+            txt = _("Can't refresh pacman databases.")
+            raise InstallError(txt)
+
+        try:
+            pacman.release()
+            del pacman
+        except Exception as ex:
+            template = (
+                "Can't release 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)
+
+    def add_package(self, pkg):
+        """ Adds xml node text to our package list
+            returns TRUE if the package is added """
+        libs = desktop_info.LIBS
+
+        arch = pkg.attrib.get('arch')
+        if arch and arch != os.uname()[-1]:
+            return False
+
+        lang = pkg.attrib.get('lang')
+        locale = self.settings.get("locale").split('.')[0][:2]
+        if lang and lang != locale:
+            return False
+
+        lib = pkg.attrib.get('lib')
+        if lib and self.desktop not in libs[lib]:
+            return False
+
+        desktops = pkg.attrib.get('desktops')
+        if desktops and self.desktop not in desktops:
+            return False
+
+        # If package is a Desktop Manager or a Network Manager,
+        # save the name to activate the correct service later
+        if pkg.attrib.get('dm'):
+            self.settings.set("desktop_manager", pkg.attrib.get('name'))
+        if pkg.attrib.get('nm'):
+            self.settings.set("network_manager", pkg.attrib.get('name'))
+
+        # check conflicts attrib
+        conflicts = pkg.attrib.get('conflicts')
+        if conflicts:
+            self.add_conflicts(pkg.attrib.get('conflicts'))
+
+        # finally, add package
+        self.packages.append(pkg.text)
+        return True
+
+    def load_xml_local(self, xml_filename):
+        """ Load xml packages list from file name """
+        self.events.add('info', _("Reading local package list..."))
+        if os.path.exists(xml_filename):
+            logging.debug("Loading %s", xml_filename)
+            xml_tree = elementTree.parse(xml_filename)
+            self.xml_root = xml_tree.getroot()
+        else:
+            logging.warning("Cannot find %s file", xml_filename)
+
+    def load_xml_remote(self):
+        """ Load xml packages list from url """
+        self.events.add('info', _("Getting online package list..."))
+        url = SelectPackages.PKGLIST_URL
+        logging.debug("Getting url %s...", url)
+        try:
+            req = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
+            self.xml_root = elementTree.fromstring(req.content)
+        except RequestException as url_error:
+            msg = "Can't retrieve remote package list: {}".format(
+                url_error)
+            logging.warning(msg)
+
+    def load_xml_root_node(self):
+        """ Loads xml data, storing the root node """
+        self.xml_root = None
+
+        alternate_package_list = self.settings.get('alternate_package_list')
+        if alternate_package_list:
+            # Use file passed by parameter (overrides server one)
+            self.load_xml_local(alternate_package_list)
+
+        if self.xml_root is None:
+            # The list of packages is retrieved from an online XML to let us
+            # control the pkgname in case of any modification
+            self.load_xml_remote()
+
+        if self.xml_root is None:
+            # If the installer can't retrieve the remote file Cnchi will use
+            # a local copy, which might be updated or not.
+            xml_filename = os.path.join(self.settings.get('data'), 'packages.xml')
+            self.load_xml_local(xml_filename)
+
+        if self.xml_root is None:
+            txt = "Could not load packages XML file (neither local nor from the Internet)"
+            logging.error(txt)
+            txt = _("Could not load packages XML file (neither local nor from the Internet)")
+            raise InstallError(txt)
+
+    def add_drivers(self):
+        """ Add package drivers """
+        try:
+            # Detect which hardware drivers are needed
+            hardware_install = hardware.HardwareInstall(
+                self.settings.get('cnchi'),
+                self.settings.get('feature_graphic_drivers'))
+            driver_names = hardware_install.get_found_driver_names()
+            if driver_names:
+                logging.debug(
+                    "Hardware module detected these drivers: %s",
+                    driver_names)
+
+            # Add needed hardware packages to our list
+            hardware_pkgs = hardware_install.get_packages()
+            if hardware_pkgs:
+                logging.debug(
+                    "Hardware module added these packages: %s",
+                    ", ".join(hardware_pkgs))
+                if 'virtualbox' in hardware_pkgs:
+                    self.vbox = True
+                self.packages.extend(hardware_pkgs)
+
+            # Add conflicting hardware packages to our conflicts list
+            self.conflicts.extend(hardware_install.get_conflicts())
+        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)
+
+    def add_filesystems(self):
+        """ Add filesystem packages """
+        logging.debug("Adding filesystem packages")
+        for child in self.xml_root.iter("filesystems"):
+            for pkg in child.iter('pkgname'):
+                self.add_package(pkg)
+
+        # Add ZFS filesystem
+        if self.settings.get('zfs'):
+            logging.debug("Adding zfs packages")
+            for child in self.xml_root.iter("zfs"):
+                for pkg in child.iter('pkgname'):
+                    self.add_package(pkg)
+
+    def maybe_add_chinese_fonts(self):
+        """ Add chinese fonts if necessary """
+        lang_code = self.settings.get("language_code")
+        if lang_code in ["zh_TW", "zh_CN"]:
+            logging.debug("Selecting chinese fonts.")
+            for child in self.xml_root.iter('chinese'):
+                for pkg in child.iter('pkgname'):
+                    self.add_package(pkg)
+
+    def maybe_add_bootloader(self):
+        """ Add bootloader packages if needed """
+        if self.settings.get('bootloader_install'):
+            boot_loader = self.settings.get('bootloader')
+            bootloader_found = False
+            for child in self.xml_root.iter('bootloader'):
+                if child.attrib.get('name') == boot_loader:
+                    txt = _("Adding '%s' bootloader packages")
+                    logging.debug(txt, boot_loader)
+                    bootloader_found = True
+                    for pkg in child.iter('pkgname'):
+                        self.add_package(pkg)
+            if not bootloader_found:
+                logging.warning(
+                    "Couldn't find %s bootloader packages!", boot_loader)
+
+    def add_edition_packages(self):
+        """ Add common and specific edition packages """
+        for editions in self.xml_root.iter('editions'):
+            for edition in editions.iter('edition'):
+                name = edition.attrib.get('name').lower()
+
+                # Add common packages to all desktops (including base)
+                if name == 'common':
+                    for pkg in edition.iter('pkgname'):
+                        self.add_package(pkg)
+
+                # Add common graphical packages (not if installing 'base')
+                if name == 'graphic' and self.desktop != 'base':
+                    for pkg in edition.iter('pkgname'):
+                        self.add_package(pkg)
+
+                # Add specific desktop packages
+                if name == self.desktop:
+                    logging.debug("Adding %s desktop packages", self.desktop)
+                    for pkg in edition.iter('pkgname'):
+                        self.add_package(pkg)
+
+    def maybe_add_vbox_packages(self):
+        """ Adds specific virtualbox packages if running inside a VM """
+        if self.vbox:
+            # Add virtualbox-guest-utils-nox package if 'base' is installed in a vbox vm
+            if self.desktop == 'base':
+                self.packages.append('virtualbox-guest-utils-nox')
+
+            # Add linux-lts-headers if LTS kernel is installed in a vbox vm
+            if self.settings.get('feature_lts'):
+                self.packages.append('linux-lts-headers')
+
+    def select_packages(self):
+        """ Get package list from the Internet and add specific packages to it """
+        self.packages = []
+
+        # Load package list
+        self.load_xml_root_node()
+
+        # Add common and desktop specific packages
+        self.add_edition_packages()
+
+        # Add drivers' packages
+        self.add_drivers()
+
+        # Add file system packages
+        self.add_filesystems()
+
+        # Add chinese fonts (if necessary)
+        self.maybe_add_chinese_fonts()
+
+        # Add bootloader (if user chose it)
+        self.maybe_add_bootloader()
+
+        # Add extra virtualbox packages (if needed)
+        self.maybe_add_vbox_packages()
+
+        # Check for user desired features and add them to our installation
+        logging.debug(
+            "Check for user desired features and add them to our installation")
+        self.add_features()
+        logging.debug("All features needed packages have been added")
+
+        # Add Lembrame packages but install Cnchi defaults too
+        # TODO: Lembrame has to generate a better package list indicating DM and stuff
+        if self.settings.get("feature_lembrame"):
+            self.events.add('info', _("Appending list of packages from Lembrame"))
+            self.packages = self.packages + self.lembrame.get_pacman_packages()
+
+        # Remove duplicates and conflicting packages
+        self.cleanup_packages_list()
+        logging.debug("Packages list: %s", ','.join(self.packages))
+
+        # Check if all packages ARE in the repositories
+        # This is done mainly to avoid errors when Arch removes a package silently
+        self.check_packages()
+
+    def check_packages(self):
+        """ Checks that all selected packages ARE in the repositories """
+        not_found = []
+        self.events.add('percent', 0)
+        self.events.add('info', _("Checking that all selected packages are available online..."))
+        num_pkgs = len(self.packages)
+        for index, pkg_name in enumerate(self.packages):
+            # TODO: Use libalpm instead
+            cmd = ["/usr/bin/pacman", "-Ss", pkg_name]
+            try:
+                output = subprocess.check_output(cmd).decode()
+            except subprocess.CalledProcessError:
+                output = ""
+
+            if pkg_name not in output:
+                not_found.append(pkg_name)
+                logging.error("Package %s...NOT FOUND!", pkg_name)
+
+            percent = (index + 1) / num_pkgs
+            self.events.add('percent', percent)
+
+        if not_found:
+            txt = _("Cannot find these packages: {}").format(', '.join(not_found))
+            raise misc.InstallError(txt)
+
+
+    def cleanup_packages_list(self):
+        """ Cleans up a bit our packages list """
+        # Remove duplicates
+        self.packages = list(set(self.packages))
+        self.conflicts = list(set(self.conflicts))
+
+        # Check the list of packages for empty strings and remove any that we find.
+        self.packages = [pkg for pkg in self.packages if pkg != '']
+        self.conflicts = [pkg for pkg in self.conflicts if pkg != '']
+
+        # Remove any package from self.packages that is already in self.conflicts
+        if self.conflicts:
+            logging.debug("Conflicts list: %s", ", ".join(self.conflicts))
+            for pkg in self.conflicts:
+                if pkg in self.packages:
+                    self.packages.remove(pkg)
+
+    def add_conflicts(self, conflicts):
+        """ Maintains a list of conflicting packages """
+        if conflicts:
+            if ',' in conflicts:
+                for conflict in conflicts.split(','):
+                    conflict = conflict.rstrip()
+                    if conflict not in self.conflicts:
+                        self.conflicts.append(conflict)
+            else:
+                self.conflicts.append(conflicts)
+
+    def add_hunspell(self, language_code):
+        """ Adds hunspell dictionary """
+        # Try to read available codes from hunspell.txt
+        data_dir = self.settings.get("data")
+        path = os.path.join(data_dir, "hunspell.txt")
+        if os.path.exists(path):
+            with open(path, 'r') as lang_file:
+                lang_codes = lang_file.read().split()
+        else:
+            # hunspell.txt not available, let's use this hardcoded version (as failsafe)
+            lang_codes = [
+                'de-frami', 'de', 'en', 'en_AU', 'en_CA', 'en_GB', 'en_US',
+                'es_any', 'es_ar', 'es_bo', 'es_cl', 'es_co', 'es_cr', 'es_cu',
+                'es_do', 'es_ec', 'es_es', 'es_gt', 'es_hn', 'es_mx', 'es_ni',
+                'es_pa', 'es_pe', 'es_pr', 'es_py', 'es_sv', 'es_uy', 'es_ve',
+                'fr', 'he', 'it', 'ro', 'el', 'hu', 'nl', 'pl']
+
+        if language_code in lang_codes:
+            pkg_text = "hunspell-{0}".format(language_code)
+            logging.debug(
+                "Adding hunspell dictionary for %s language", pkg_text)
+            self.packages.append(pkg_text)
+        else:
+            logging.debug(
+                "No hunspell language dictionary found for %s language code", language_code)
+
+    def add_libreoffice_language(self):
+        """ Adds libreoffice language package """
+        lang_name = self.settings.get('language_name').lower()
+        if lang_name == 'english':
+            # There're some English variants available but not all of them.
+            locale = self.settings.get('locale').split('.')[0]
+            if locale in ['en_GB', 'en_ZA']:
+                code = locale
+            else:
+                code = None
+        else:
+            # All the other language packs use their language code
+            code = self.settings.get('language_code')
+
+        if code:
+            code = code.replace('_', '-').lower()
+            pkg_text = "libreoffice-fresh-{0}".format(code)
+            logging.debug(
+                "Adding libreoffice language package (%s)", pkg_text)
+            self.packages.append(pkg_text)
+            self.add_hunspell(code)
+
+    def add_firefox_language(self):
+        """ Add firefox language package """
+        # Try to load available languages from firefox.txt (easy updating if necessary)
+        data_dir = self.settings.get("data")
+        path = os.path.join(data_dir, "firefox.txt")
+        if os.path.exists(path):
+            with open(path, 'r') as lang_file:
+                lang_codes = lang_file.read().split()
+        else:
+            # Couldn't find firefox.txt, use this hardcoded version then (as failsafe)
+            lang_codes = [
+                'ach', 'af', 'an', 'ar', 'as', 'ast', 'az', 'be', 'bg', 'bn-bd',
+                'bn-in', 'br', 'bs', 'ca', 'cs', 'cy', 'da', 'de', 'dsb', 'el',
+                'en-gb', 'en-us', 'en-za', 'eo', 'es-ar', 'es-cl', 'es-es',
+                'es-mx', 'et', 'eu', 'fa', 'ff', 'fi', 'fr', 'fy-nl', 'ga-ie',
+                'gd', 'gl', 'gu-in', 'he', 'hi-in', 'hr', 'hsb', 'hu', 'hy-am',
+                'id', 'is', 'it', 'ja', 'kk', 'km', 'kn', 'ko', 'lij', 'lt', 'lv',
+                'mai', 'mk', 'ml', 'mr', 'ms', 'nb-no', 'nl', 'nn-no', 'or',
+                'pa-in', 'pl', 'pt-br', 'pt-pt', 'rm', 'ro', 'ru', 'si', 'sk',
+                'sl', 'son', 'sq', 'sr', 'sv-se', 'ta', 'te', 'th', 'tr', 'uk',
+                'uz', 'vi', 'xh', 'zh-cn', 'zh-tw']
+
+        lang_code = self.settings.get('language_code')
+        lang_code = lang_code.replace('_', '-').lower()
+        if lang_code in lang_codes:
+            pkg_text = "firefox-i18n-{0}".format(lang_code)
+            logging.debug("Adding firefox language package (%s)", pkg_text)
+            self.packages.append(pkg_text)
+
+    def add_features(self):
+        """ Selects packages based on user selected features """
+        for xml_features in self.xml_root.iter('features'):
+            for xml_feature in xml_features.iter('feature'):
+                feature = xml_feature.attrib.get("name")
+
+                # If LEMP is selected, do not install lamp even if it's selected
+                if feature == "lamp" and self.settings.get("feature_lemp"):
+                    continue
+
+                # Add packages from each feature
+                if self.settings.get("feature_" + feature):
+                    logging.debug("Adding packages for '%s' feature.", feature)
+                    for pkg in xml_feature.iter('pkgname'):
+                        if self.add_package(pkg):
+                            logging.debug(
+                                "Selecting package %s for feature %s",
+                                pkg.text,
+                                feature)
+
+        # Add libreoffice language package
+        if self.settings.get('feature_office'):
+            self.add_libreoffice_language()
+
+        # Add firefox language package
+        if self.settings.get('feature_firefox'):
+            self.add_firefox_language()
diff --git a/Cnchi/show_message.py b/Cnchi/show_message.py
new file mode 100644 (file)
index 0000000..92a4228
--- /dev/null
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# show_message.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 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/>.
+
+""" Helper functions to show Gtk message dialogs """
+
+import sys
+import os
+#import multiprocessing
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(msg):
+        return msg
+
+
+def fatal_error(parent, my_message):
+    """ Shows an error message and quits """
+
+    path = "/var/tmp/cnchi/.setup-running"
+    if os.path.exists(path):
+        os.remove(path)
+
+    # multiprocessing.active_children()
+
+    error(parent, my_message)
+    sys.exit(1)
+
+
+def error(parent, my_message):
+    """ Shows an error message """
+
+    if not isinstance(parent, Gtk.Window):
+        parent = None
+
+    my_message = str(my_message)
+    msg_dialog = Gtk.MessageDialog(transient_for=parent,
+                                   modal=True,
+                                   destroy_with_parent=True,
+                                   message_type=Gtk.MessageType.ERROR,
+                                   buttons=Gtk.ButtonsType.CLOSE,
+                                   text=_("RebornOS Installer - Error"))
+    msg_dialog.format_secondary_text(my_message)
+    msg_dialog.run()
+    msg_dialog.destroy()
+
+
+def warning(parent, my_message):
+    """ Shows a warning message """
+
+    if not isinstance(parent, Gtk.Window):
+        parent = None
+
+    my_message = str(my_message)
+    msg_dialog = Gtk.MessageDialog(transient_for=parent,
+                                   modal=True,
+                                   destroy_with_parent=True,
+                                   message_type=Gtk.MessageType.WARNING,
+                                   buttons=Gtk.ButtonsType.CLOSE,
+                                   text=_("RebornOS Installer - Warning"))
+    msg_dialog.format_secondary_text(my_message)
+    msg_dialog.run()
+    msg_dialog.destroy()
+
+
+def message(parent, my_message):
+    """ Show message """
+
+    if not isinstance(parent, Gtk.Window):
+        parent = None
+
+    my_message = str(my_message)
+    msg_dialog = Gtk.MessageDialog(transient_for=parent,
+                                   modal=True,
+                                   destroy_with_parent=True,
+                                   message_type=Gtk.MessageType.INFO,
+                                   buttons=Gtk.ButtonsType.CLOSE,
+                                   text=_("RebornOS Installer - Information"))
+    msg_dialog.format_secondary_text(my_message)
+    msg_dialog.run()
+    msg_dialog.destroy()
+
+
+def question(parent, my_message):
+    """ Shows a question message """
+
+    if not isinstance(parent, Gtk.Window):
+        parent = None
+
+    my_message = str(my_message)
+    msg_dialog = Gtk.MessageDialog(transient_for=parent,
+                                   modal=True,
+                                   destroy_with_parent=True,
+                                   message_type=Gtk.MessageType.QUESTION,
+                                   buttons=Gtk.ButtonsType.YES_NO,
+                                   text=_("RebornOS Installer - Confirmation"))
+    msg_dialog.format_secondary_text(my_message)
+    response = msg_dialog.run()
+    msg_dialog.destroy()
+    return response
diff --git a/Cnchi/slides.py b/Cnchi/slides.py
new file mode 100755 (executable)
index 0000000..ba0567e
--- /dev/null
@@ -0,0 +1,345 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# slides.py
+#
+# Copyright © 2013-2018 Reborn
+#
+# 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/>.
+
+
+""" Shows slides while installing. Also manages installing messages and progress bars """
+
+import sys
+import logging
+import os
+import queue
+import subprocess
+
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, GLib, GdkPixbuf
+
+import show_message as show
+import misc.extra as misc
+
+from pages.gtkbasebox import GtkBaseBox
+
+from logging_utils import ContextFilter
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+class Slides(GtkBaseBox):
+    """ Slides page """
+
+    # Check events queue every second
+    MANAGE_EVENTS_TIMER = 1000
+
+    # Change image slide every half minute
+    SLIDESHOW_TIMER = 35000
+
+    def __init__(self, params, prev_page=None, next_page=None):
+        """ Initialize class and its vars """
+        super().__init__(self, params, 'slides', prev_page, next_page)
+
+        self.progress_bar = self.gui.get_object('progress_bar')
+        self.progress_bar.set_show_text(True)
+        self.progress_bar.set_name('i_progressbar')
+
+        self.downloads_progress_bar = self.gui.get_object('downloads_progress_bar')
+        self.downloads_progress_bar.set_show_text(True)
+        self.downloads_progress_bar.set_name('a_progressbar')
+
+        self.info_label = self.gui.get_object('info_label')
+
+        self.fatal_error = False
+        self.should_pulse = False
+
+        self.revealer = self.gui.get_object('revealer1')
+        self.revealer.connect('notify::child-revealed', self.image_revealed)
+        self.slide = 0
+        self.stop_slideshow = False
+
+        GLib.timeout_add(Slides.MANAGE_EVENTS_TIMER, self.manage_events_from_cb_queue)
+
+    def translate_ui(self):
+        """ Translates all ui elements """
+        if not self.info_label.get_label():
+            self.info_label.set_markup(_("Please wait..."))
+
+        self.header.set_subtitle(_("Installing RebornOS..."))
+
+    def prepare(self, direction):
+        """ Prepare slides screen """
+        self.translate_ui()
+        self.show_all()
+
+        # Last screen reached, hide main progress bar (the one at the top).
+        self.main_progressbar.hide()
+
+        # Also hide total downloads progress bar
+        self.downloads_progress_bar.hide()
+
+        # Hide backwards and forwards buttons
+        self.backwards_button.hide()
+        self.forward_button.hide()
+
+        # Hide close button (we've reached the point of no return)
+        self.header.set_show_close_button(False)
+
+        # Set slide image (and show it)
+        self.reveal_next_slide()
+
+    def reveal_next_slide(self):
+        """ Loads slide and reveals it """
+        if not self.stop_slideshow:
+            self.slide = ((self.slide + 1) % 3) + 1
+            if 0 < self.slide <= 3:
+                try:
+                    data_dir = self.settings.get('data')
+                    path = os.path.join(data_dir, 'images/slides',
+                                        '{}.png'.format(self.slide))
+                    img = self.gui.get_object('slide1')
+                    # img.set_from_file(path)
+                    # 800x334
+                    pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
+                        path, 820, 334, False)
+                    # set the content of the image as the pixbuf
+                    img.set_from_pixbuf(pixbuf)
+                    # Reveal image
+                    self.revealer.set_reveal_child(True)
+                except FileNotFoundError:
+                    # FIXME: Installation process finishes before we can read these values ?¿
+                    logging.warning("Can't get configuration values.")
+                    self.stop_slideshow = True
+
+    def image_revealed(self, revealer, _revealed):
+        """ Called when a image slide is shown
+            revealer: Gtk.Revealer
+            revealed: GParamBoolean """
+        if not self.stop_slideshow:
+            if revealer.get_child_revealed() and not self.stop_slideshow:
+                GLib.timeout_add(Slides.SLIDESHOW_TIMER, self.hide_slide)
+            else:
+                self.reveal_next_slide()
+
+    def hide_slide(self):
+        """ Hide image shown in slideshow, this will trigger image_revealed()
+        so the next slide image will be revealed """
+        self.revealer.set_reveal_child(False)
+        return False
+
+    @staticmethod
+    def store_values():
+        """ Nothing to be done here """
+        return False
+
+    def stop_pulse(self):
+        """ Stop pulsing progressbar """
+        self.should_pulse = False
+        # self.progress_bar.hide()
+        self.info_label.show_all()
+
+    def start_pulse(self):
+        """ Start pulsing progressbar """
+
+        def pbar_pulse():
+            """ Pulse progressbar """
+            if self.should_pulse:
+                self.progress_bar.pulse()
+            return self.should_pulse
+
+        if not self.should_pulse:
+            # Hide any text that might be in info area
+            self.info_label.set_markup("")
+            self.info_label.hide()
+            # Show progress bar (just in case)
+            self.progress_bar.show_all()
+            self.progress_bar.set_show_text(True)
+            self.should_pulse = True
+            GLib.timeout_add(100, pbar_pulse)
+
+    def manage_events_from_cb_queue(self):
+        """ We should be quick here and do as less as possible """
+
+        if self.fatal_error:
+            return False
+
+        if self.callback_queue is None:
+            return True
+
+        while not self.callback_queue.empty():
+            try:
+                event = self.callback_queue.get_nowait()
+            except ValueError as queue_error:
+                # Calling get_nowait so many times can issue a ValueError
+                # exception with this error: semaphore or lock released too
+                # many times. Log it anyways to keep an eye on this error
+                logging.error(queue_error)
+                return True
+            except queue.Empty:
+                # Queue is empty, just quit.
+                return True
+
+            if event[0] == 'percent':
+                self.progress_bar.set_fraction(float(event[1]))
+            elif event[0] == 'downloads_percent':
+                self.downloads_progress_bar.set_fraction(float(event[1]))
+            elif event[0] == 'progress_bar_show_text':
+                if event[1]:
+                    self.progress_bar.set_text(event[1])
+                else:
+                    self.progress_bar.set_text("")
+            elif event[0] == 'progress_bar':
+                if event[1] == 'hide':
+                    self.progress_bar.hide()
+                elif event[1] == 'show':
+                    self.progress_bar.show()
+            elif event[0] == 'downloads_progress_bar':
+                if event[1] == 'hide':
+                    self.downloads_progress_bar.hide()
+                elif event[1] == 'show':
+                    self.downloads_progress_bar.show()
+            elif event[0] == 'pulse':
+                if event[1] == 'stop':
+                    self.stop_pulse()
+                elif event[1] == 'start':
+                    self.start_pulse()
+            elif event[0] == 'finished':
+                logging.info(event[1])
+                self.installation_finished()
+            elif event[0] == 'error':
+                self.callback_queue.task_done()
+                self.install_error(event[1])
+            elif event[0] == 'info':
+                logging.info(event[1])
+                if self.should_pulse:
+                    self.progress_bar.set_text(event[1])
+                else:
+                    self.info_label.set_markup(event[1])
+            elif event[0] == 'cache_pkgs_md5_check_failed':
+                logging.debug(
+                    'Adding %s to cache_pkgs_md5_check_failed list', event[1])
+                self.settings.set('cache_pkgs_md5_check_failed', event[1])
+            else:
+                logging.warning("Event %s not recognised. Ignoring.", event[0])
+
+            self.callback_queue.task_done()
+
+        return True
+
+    def empty_queue(self):
+        """ Empties messages queue """
+        while not self.callback_queue.empty():
+            try:
+                self.callback_queue.get_nowait()
+                self.callback_queue.task_done()
+            except queue.Empty:
+                return
+
+    @staticmethod
+    def reboot():
+        """ Reboots the system, used when installation is finished """
+        with misc.raised_privileges():
+            try:
+                cmd = ["sync"]
+                subprocess.call(cmd)
+                cmd = ["/usr/bin/systemctl", "reboot", "--force", "--no-wall"]
+                subprocess.call(cmd)
+            except subprocess.CalledProcessError as error:
+                logging.error(error)
+
+    def check_bootloader(self):
+        """ Check that bootloader has been successfuly installed
+            Shows a dialog to the user in case something has failed """
+        bootloader_install = self.settings.get('bootloader_install')
+        bootloader_install_ok = self.settings.get('bootloader_installation_successful')
+
+        if bootloader_install and not bootloader_install_ok:
+            # Warn user about GRUB and ask if we should open wiki page.
+            boot_warn = _(
+                "IMPORTANT: There may have been a problem with the bootloader installation "
+                "which could prevent your system from booting properly. Before rebooting, "
+                "you may want to verify whether or not the bootloader is installed and "
+                "configured.\n\n"
+                "The Arch Linux Wiki contains troubleshooting information:\n"
+                "\thttps://wiki.archlinux.org/index.php/GRUB\n\n"
+                "Would you like to view the wiki page now?")
+            response = show.question(self.get_main_window(), boot_warn)
+            if response == Gtk.ResponseType.YES:
+                import webbrowser
+                misc.drop_privileges()
+                wiki_url = 'https://wiki.archlinux.org/index.php/GRUB'
+                webbrowser.open(wiki_url)
+
+    def installation_finished(self):
+        """ Installation finished """
+        log_util = ContextFilter()
+        log_util.send_install_result("True")
+
+        self.stop_slideshow = True
+
+        install_ok = _(
+            "Installation Complete!\n"
+            "Do you want to restart your system now?")
+        response = show.question(self.get_main_window(), install_ok)
+
+        try:
+            misc.remove_temp_files(self.settings.get('temp'))
+        except FileNotFoundError:
+            # FIXME: Installation process finishes before we can read these values ?¿
+            logging.warning("Can't get configuration values.")
+
+        logging.shutdown()
+
+        if response == Gtk.ResponseType.YES:
+            self.reboot()
+        else:
+            sys.exit(0)
+
+    def install_error(self, error):
+        """ A fatal error has been issued """
+
+        self.stop_slideshow = True
+
+        # Empty the events queue
+        self.empty_queue()
+
+        log_util = ContextFilter()
+        log_util.send_install_result("False")
+        if log_util.have_install_id:
+            # Add install id to error message
+            # (we can lookup logs on bugsnag by the install id)
+            tpl = _(
+                'Please reference the following number when reporting this error: ')
+            error_message = '{0}\n{1}{2}'.format(
+                error, tpl, log_util.install_id)
+        else:
+            error_message = error
+
+        show.fatal_error(self.get_main_window(), error_message)
diff --git a/Cnchi/systemd_boot.py b/Cnchi/systemd_boot.py
new file mode 100644 (file)
index 0000000..149911b
--- /dev/null
@@ -0,0 +1,164 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# systemd_boot.py
+#
+# Copyright © 2013-2017 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/>.
+
+
+""" Systemd-boot (gummiboot) installation """
+
+import logging
+import os
+
+import parted3.fs_module as fs
+
+from misc.run_cmd import chroot_call
+
+
+class SystemdBoot(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 Systemd-boot bootloader to the EFI System Partition """
+        logging.debug("Cnchi will install the Systemd-boot (Gummiboot) loader")
+
+        # Setup bootloader menu
+        menu_dir = os.path.join(self.dest_dir, "boot/loader")
+        os.makedirs(menu_dir, mode=0o755, exist_ok=True)
+        menu_path = os.path.join(menu_dir, "loader.conf")
+        with open(menu_path, 'w') as menu_file:
+            menu_file.write("default antergos\n")
+            menu_file.write("timeout 3\n")
+
+        # Setup boot entries
+        conf = {}
+        options = ""
+
+        if not self.settings.get('use_luks'):
+            options = "root=UUID={0} rw quiet".format(self.uuids["/"])
+        else:
+            luks_root_volume = self.settings.get('luks_root_volume')
+            logging.debug("Luks Root Volume: %s", luks_root_volume)
+            mapper = "/dev/mapper/{0}".format(luks_root_volume)
+            luks_root_volume_uuid = fs.get_uuid(mapper)
+
+            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)
+
+            key = ""
+            if not self.settings.get("luks_root_password"):
+                key = "cryptkey=UUID={0}:ext2:/.keyfile-root"
+                key = key.format(self.uuids["/boot"])
+
+            if not self.settings.get('use_lvm'):
+                options = "cryptdevice=UUID={0}:{1} {2} root=UUID={3} rw quiet"
+                options = options.format(
+                    self.uuids["/"],
+                    luks_root_volume,
+                    key,
+                    luks_root_volume_uuid)
+            else:
+                # Quick fix for issue #595 (lvm+luks)
+                options = "cryptdevice=UUID={0}:{1} {2} root=/dev/dm-1 rw quiet"
+                options = options.format(
+                    self.uuids["/"],
+                    luks_root_volume,
+                    key)
+
+        if self.settings.get("zfs"):
+            zfs_pool_name = self.settings.get("zfs_pool_name")
+            options += ' zfs={0}'.format(zfs_pool_name)
+
+        conf['default'] = []
+        conf['default'].append("title\tRebornOS\n")
+        conf['default'].append("linux\t/vmlinuz-linux\n")
+        conf['default'].append("initrd\t/intel-ucode.img\n")
+        conf['default'].append("initrd\t/initramfs-linux.img\n")
+        conf['default'].append("options\t{0}\n\n".format(options))
+
+        conf['fallback'] = []
+        conf['fallback'].append("title\tRebornOS (fallback)\n")
+        conf['fallback'].append("linux\t/vmlinuz-linux\n")
+        conf['fallback'].append("initrd\t/intel-ucode.img\n")
+        conf['fallback'].append("initrd\t/initramfs-linux-fallback.img\n")
+        conf['fallback'].append("options\t{0}\n\n".format(options))
+
+        if self.settings.get('feature_lts'):
+            conf['lts'] = []
+            conf['lts'].append("title\tRebornOS LTS\n")
+            conf['lts'].append("linux\t/vmlinuz-linux-lts\n")
+            conf['lts'].append("initrd\t/intel-ucode.img\n")
+            conf['lts'].append("initrd\t/initramfs-linux-lts.img\n")
+            conf['lts'].append("options\t{0}\n\n".format(options))
+
+            conf['lts_fallback'] = []
+            conf['lts_fallback'].append("title\tRebornOS LTS (fallback)\n")
+            conf['lts_fallback'].append("linux\t/vmlinuz-linux-lts\n")
+            conf['lts_fallback'].append("initrd\t/intel-ucode.img\n")
+            conf['lts_fallback'].append("initrd\t/initramfs-linux-lts-fallback.img\n")
+            conf['lts_fallback'].append("options\t{0}\n\n".format(options))
+
+        # Write boot entries
+        entries_dir = os.path.join(self.dest_dir, "boot/loader/entries")
+        os.makedirs(entries_dir, mode=0o755, exist_ok=True)
+
+        entry_path = os.path.join(entries_dir, "antergos.conf")
+        with open(entry_path, 'w') as entry_file:
+            for line in conf['default']:
+                entry_file.write(line)
+
+        entry_path = os.path.join(entries_dir, "antergos-fallback.conf")
+        with open(entry_path, 'w') as entry_file:
+            for line in conf['fallback']:
+                entry_file.write(line)
+
+        if self.settings.get('feature_lts'):
+            entry_path = os.path.join(entries_dir, "antergos-lts.conf")
+            with open(entry_path, 'w') as entry_file:
+                for line in conf['lts']:
+                    entry_file.write(line)
+
+            entry_path = os.path.join(
+                entries_dir, "antergos-lts-fallback.conf")
+            with open(entry_path, 'w') as entry_file:
+                for line in conf['lts_fallback']:
+                    entry_file.write(line)
+
+        # Install bootloader
+        logging.debug("Installing systemd-boot bootloader...")
+        cmd = ['bootctl', '--path=/boot', 'install']
+        if chroot_call(cmd, self.dest_dir, 300) is False:
+            self.settings.set('bootloader_installation_successful', False)
+        else:
+            self.settings.set('bootloader_installation_successful', True)
diff --git a/Cnchi/timezone.py b/Cnchi/timezone.py
new file mode 100644 (file)
index 0000000..2490c06
--- /dev/null
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# timezone.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 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/>.
+
+""" Timezone screen """
+
+import hashlib
+import http.client
+import logging
+import multiprocessing
+import os
+import queue
+import time
+import urllib.request
+import urllib.error
+
+import misc.tz as tz
+import misc.extra as misc
+import widgets.timezonemap as timezonemap
+from pages.gtkbasebox import GtkBaseBox
+
+import geoip
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+
+class Timezone(GtkBaseBox):
+    """ Timezone screen """
+
+    def __init__(self, params, prev_page="location", next_page="keymap"):
+        super().__init__(self, params, "timezone", prev_page, next_page)
+
+        self.map_window = self.gui.get_object('timezone_map_window')
+
+        self.combobox_zone = self.gui.get_object('comboboxtext_zone')
+        self.combobox_region = self.gui.get_object('comboboxtext_region')
+
+        # Show regions in three columns
+        self.combobox_region.set_wrap_width(3)
+
+        self.tzdb = tz.Database()
+        self.timezone = None
+
+        # This is for populate_cities
+        self.old_zone = None
+
+        # Autotimezone process will store detected coords in this queue
+        self.auto_timezone_coords = multiprocessing.Queue()
+
+        # Process to try to determine timezone.
+        self.autodetected_coords = None
+        self.start_auto_timezone_process()
+
+        # Setup window
+        self.tzmap = timezonemap.TimezoneMap()
+        self.tzmap.connect('location-changed', self.on_location_changed)
+
+        # Strip .UTF-8 from locale, icu doesn't parse it
+        self.locale = os.environ['LANG'].rsplit('.', 1)[0]
+        self.map_window.add(self.tzmap)
+        self.tzmap.show()
+
+    def translate_ui(self):
+        """ Translates all ui elements """
+        label = self.gui.get_object('label_zone')
+        txt = _("Zone:")
+        label.set_markup(txt)
+
+        label = self.gui.get_object('label_region')
+        txt = _("Region:")
+        label.set_markup(txt)
+
+        label = self.gui.get_object('label_ntp')
+        txt = _("Use Network Time Protocol (NTP) for clock synchronization")
+        label.set_markup(txt)
+
+        self.header.set_subtitle(_("Select Your Timezone"))
+
+    def on_location_changed(self, _tzmap, tz_location):
+        """ User changed its location """
+        # loc = self.tzdb.get_loc(self.timezone)
+        if not tz_location:
+            self.timezone = None
+            self.forward_button.set_sensitive(False)
+        else:
+            self.timezone = tz_location.get_property('zone')
+            logging.info("Location changed to : %s", self.timezone)
+            self.update_comboboxes(self.timezone)
+            self.forward_button.set_sensitive(True)
+
+    def update_comboboxes(self, timezone):
+        """ Location has changed, update comboboxes """
+        zone, region = timezone.split('/', 1)
+        self.select_combobox_item(self.combobox_zone, zone)
+        self.populate_cities(zone)
+        self.select_combobox_item(self.combobox_region, region)
+
+    @staticmethod
+    def select_combobox_item(combobox, item):
+        """ Make combobox select an item """
+        tree_model = combobox.get_model()
+        tree_iter = tree_model.get_iter_first()
+
+        while tree_iter is not None:
+            value = tree_model.get_value(tree_iter, 0)
+            if value == item:
+                combobox.set_active_iter(tree_iter)
+                tree_iter = None
+            else:
+                tree_iter = tree_model.iter_next(tree_iter)
+
+    def set_timezone(self, timezone):
+        """ Set timezone in tzmap """
+        if timezone:
+            self.timezone = timezone
+            res = self.tzmap.set_timezone(timezone)
+            # res will be False if the timezone is unrecognised
+            self.forward_button.set_sensitive(res)
+
+    def on_zone_combobox_changed(self, _widget):
+        """ Zone changed """
+        new_zone = self.combobox_zone.get_active_text()
+        if new_zone is not None:
+            self.populate_cities(new_zone)
+
+    def on_region_combobox_changed(self, _widget):
+        """ Region changed """
+        new_zone = self.combobox_zone.get_active_text()
+        new_region = self.combobox_region.get_active_text()
+        if new_zone is not None and new_region is not None:
+            new_timezone = "{0}/{1}".format(new_zone, new_region)
+            # Only set timezone if it has changed :p
+            if self.timezone != new_timezone:
+                self.set_timezone(new_timezone)
+
+    def populate_zones(self):
+        """ Get all zones and fill our model """
+        zones = []
+        for loc in self.tzdb.locations:
+            zone = loc.zone.split('/', 1)[0]
+            if zone not in zones:
+                zones.append(zone)
+        zones.sort()
+        tree_model = self.combobox_zone.get_model()
+        tree_model.clear()
+        for zone in zones:
+            tree_model.append([zone, zone])
+
+    def populate_cities(self, selected_zone):
+        """ Get all cities and populate our model """
+        if self.old_zone != selected_zone:
+            regions = []
+            for loc in self.tzdb.locations:
+                zone, region = loc.zone.split('/', 1)
+                if zone == selected_zone:
+                    regions.append(region)
+            regions.sort()
+            tree_model = self.combobox_region.get_model()
+            tree_model.clear()
+            for region in regions:
+                tree_model.append([region, region])
+            self.old_zone = selected_zone
+
+    def prepare(self, direction):
+        """ Prepare screen before showing it """
+        self.translate_ui()
+        self.populate_zones()
+        self.timezone = None
+        self.forward_button.set_sensitive(False)
+
+        if self.autodetected_coords is None:
+            try:
+                self.autodetected_coords = self.auto_timezone_coords.get(
+                    False, timeout=20)
+            except queue.Empty:
+                logging.warning("Can't autodetect timezone coordinates")
+
+        if self.autodetected_coords:
+            coords = self.autodetected_coords
+            try:
+                latitude = float(coords[0])
+                longitude = float(coords[1])
+                timezone = self.tzmap.get_timezone_at_coords(
+                    latitude, longitude)
+                self.set_timezone(timezone)
+                self.forward_button.set_sensitive(True)
+            except ValueError as value_error:
+                self.autodetected_coords = None
+                logging.warning(
+                    "Can't autodetect timezone coordinates: %s", value_error)
+
+        self.show_all()
+
+    def start_auto_timezone_process(self):
+        """ Starts timezone thread """
+        proc = AutoTimezoneProcess(self.auto_timezone_coords, self.settings)
+        proc.daemon = True
+        proc.name = "timezone"
+        proc.start()
+
+    @staticmethod
+    def log_location(loc):
+        """ Log selected location """
+        logging.debug("timezone human zone: %s", loc.human_zone)
+        logging.debug("timezone country: %s", loc.country)
+        logging.debug("timezone zone: %s", loc.zone)
+        logging.debug("timezone human country: %s", loc.human_country)
+
+        if loc.comment:
+            logging.debug("timezone comment: %s", loc.comment)
+
+        if loc.latitude:
+            logging.debug("timezone latitude: %s", loc.latitude)
+
+        if loc.longitude:
+            logging.debug("timezone longitude: %s", loc.longitude)
+
+    def store_values(self):
+        """ The user clicks 'next' """
+        loc = self.tzdb.get_loc(self.timezone)
+
+        if loc:
+            self.settings.set("timezone_zone", loc.zone)
+            self.settings.set("timezone_human_zone", loc.human_zone)
+            self.settings.set("timezone_country", loc.country)
+            self.settings.set("timezone_human_country", loc.human_country)
+
+            if loc.comment:
+                self.settings.set("timezone_comment", loc.comment)
+            else:
+                self.settings.set("timezone_comment", "")
+
+            if loc.latitude:
+                self.settings.set("timezone_latitude", loc.latitude)
+            else:
+                self.settings.set("timezone_latitude", "")
+
+            if loc.longitude:
+                self.settings.set("timezone_longitude", loc.longitude)
+            else:
+                self.settings.set("timezone_longitude", "")
+
+            # Logs timezone info
+            self.log_location(loc)
+
+        # This way process.py will know that all info has been entered
+        self.settings.set("timezone_done", True)
+
+        if self.settings.get('use_timesyncd'):
+            logging.debug(
+                "Cnchi will setup network time using systemd-timesyncd")
+        else:
+            logging.debug("Cnchi won't setup network time")
+
+        return True
+
+    def on_switch_ntp_activate(self, ntp_switch, _data):
+        """ activated/deactivated ntp switch """
+        self.settings.set('use_timesyncd', ntp_switch.get_active())
+
+
+class AutoTimezoneProcess(multiprocessing.Process):
+    """ Thread that asks our server for user's location """
+
+    def __init__(self, coords_queue, settings):
+        super(AutoTimezoneProcess, self).__init__()
+        self.coords_queue = coords_queue
+        self.settings = settings
+
+    def run(self):
+        """ main thread method """
+        # Do not start looking for our timezone until we've reached the
+        # language screen (welcome.py sets timezone_start to true when
+        # next is clicked)
+        while not self.settings.get('timezone_start'):
+            time.sleep(2)
+
+        coords = self.use_geoip()
+        if not coords:
+            msg = "Could not detect your timezone using GeoIP database. Let's use another method."
+            logging.warning(msg)
+            coords = self.use_geo_antergos()
+
+        if coords:
+            logging.debug(
+                _("Timezone (latitude %s, longitude %s) detected."),
+                coords[0],
+                coords[1])
+            self.coords_queue.put(coords)
+        else:
+            logging.warning("Could not detect your timezone!")
+
+    @staticmethod
+    def use_geoip():
+        """ Determine our location using GeoIP database """
+        logging.debug("Getting your location using GeoIP database")
+        location = geoip.GeoIP().get_location()
+        if location:
+            return [location.latitude, location.longitude]
+        return None
+
+    @staticmethod
+    def maybe_wait_for_network():
+        """ Waits 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 use_geo_antergos(self):
+        """ Determine our location using geo.antergos.com """
+        # Calculate logo hash
+        logo = "data/images/antergos/antergos-logo-mini2.png"
+        logo_path = os.path.join(self.settings.get("cnchi"), logo)
+        with open(logo_path, "rb") as logo_file:
+            logo_bytes = logo_file.read()
+        logo_hasher = hashlib.sha1()
+        logo_hasher.update(logo_bytes)
+        logo_digest = logo_hasher.digest()
+
+        # Wait until there is an Internet connection available
+        self.maybe_wait_for_network()
+
+        # OK, now get our timezone
+        logging.debug("We have connection. Let's get our timezone")
+
+        try:
+            url = urllib.request.Request(
+                url="http://geo.antergos.com",
+                data=logo_digest,
+                headers={"User-Agent": "RebornOS Installer", "Connection": "close"})
+            with urllib.request.urlopen(url) as conn:
+                coords = conn.read().decode('utf-8').strip()
+            if coords == "0 0":
+                # Sometimes server returns 0 0, we treat it as an error
+                coords = None
+            else:
+                coords = coords.split()
+        except (OSError, urllib.error.HTTPError, http.client.HTTPException) as err:
+            template = "Error getting timezone coordinates. " \
+                "An exception of type {0} occured. Arguments:\n{1!r}"
+            message = template.format(type(err).__name__, err.args)
+            logging.error(message)
+            coords = None
+        return coords
diff --git a/Cnchi/update_db.py b/Cnchi/update_db.py
new file mode 100755 (executable)
index 0000000..dea59b2
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# update_db.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 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/>.
+
+""" Updates mirrorlists and databases """
+
+import logging
+import os
+import shutil
+import subprocess
+
+import misc.extra as misc
+
+def sync():
+    """ Synchronize cached writes to persistent storage """
+    try:
+        subprocess.check_call(['/usr/bin/sync'])
+    except subprocess.CalledProcessError as why:
+        logging.warning(
+            "Can't synchronize cached writes to persistent storage: %s",
+            why)
+
+def update_mirrorlists():
+    """ Make sure we have the latest mirrorlist files """
+    """ Remove antergos-mirrorlis, and add reborn-mirrorlist (Rafael) """
+    mirrorlists = [
+        "/etc/pacman.d/mirrorlist",
+        "/etc/pacman.d/reborn-mirrorlist"]
+    cmd = [
+        'pacman',
+        '-Syy',
+        '--noconfirm',
+        '--noprogressbar',
+        '--quiet',
+        'pacman-mirrorlist',
+        'reborn-mirrorlist']
+    with misc.raised_privileges():
+        try:
+            with open(os.devnull, 'w') as fnull:
+                subprocess.call(cmd, stdout=fnull,
+                                stderr=subprocess.STDOUT)
+            # Use the new downloaded mirrorlist (.pacnew) files (if any)
+            for mirrorlist in mirrorlists:
+                pacnew_path = mirrorlist + ".pacnew"
+                if os.path.exists(pacnew_path):
+                    shutil.copy(pacnew_path, mirrorlist)
+            sync()
+            logging.debug("Mirrorlists updated successfully")
+        except subprocess.CalledProcessError as why:
+            logging.warning(
+                'Cannot update mirrorlists files: %s', why)
+        except OSError as why:
+            logging.warning('Error copying new mirrorlist files: %s', why)
+
+def test():
+    """ Tests this module """
+    update_mirrorlists()
+    with misc.raised_privileges():
+        sync()
+
+
+if __name__ == '__main__':
+    test()
diff --git a/Cnchi/updating.sh b/Cnchi/updating.sh
new file mode 100755 (executable)
index 0000000..4eb193a
--- /dev/null
@@ -0,0 +1,6 @@
+wget --spider www.google.com
+if [ "$?" = 0 ]; then
+  sudo rm /etc/xdg/autostart/update.desktop
+  exec flatpak.sh
+else exec /usr/bin/updating.sh
+fi
diff --git a/Cnchi/welcome.py b/Cnchi/welcome.py
new file mode 100644 (file)
index 0000000..932ba30
--- /dev/null
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# welcome.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 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/>.
+
+""" Welcome screen """
+
+import os
+import logging
+import multiprocessing
+import sys
+
+import gi
+gi.require_version('GdkPixbuf', '2.0')
+from gi.repository import GdkPixbuf
+
+import misc.extra as misc
+from pages.gtkbasebox import GtkBaseBox
+
+# When testing, no _() is available
+try:
+    _("")
+except NameError as err:
+    def _(message):
+        return message
+
+
+class Welcome(GtkBaseBox):
+    """ Welcome screen class """
+
+    def __init__(self, params, prev_page=None, next_page="language"):
+        super().__init__(self, params, "welcome", prev_page, next_page)
+
+        data_dir = self.settings.get('data')
+        welcome_dir = os.path.join(data_dir, "images", "welcome")
+
+        self.main_window = params['main_window']
+
+        self.labels = {'welcome': self.gui.get_object("welcome_label"),
+                       'tryit': self.gui.get_object("tryit_welcome_label"),
+                       'installit': self.gui.get_object("installit_welcome_label"),
+                       'loading': self.gui.get_object("loading_label")}
+
+        self.buttons = {'tryit': self.gui.get_object("tryit_button"),
+                        # 'cli': self.gui.get_object("cli_button"),
+                        'graph': self.gui.get_object("graph_button")}
+
+        for key in self.buttons:
+            btn = self.buttons[key]
+            btn.set_name(key + "_btn")
+
+        self.images = {'tryit': self.gui.get_object("tryit_image"),
+                       # 'cli': self.gui.get_object("cli_image"),
+                       'graph': self.gui.get_object("graph_image")}
+
+        self.filenames = {
+            'tryit': {
+                'path': os.path.join(welcome_dir, "try-it.svg"),
+                'width': 211,
+                'height': 185},
+            'graph': {
+                'path': os.path.join(welcome_dir, "install-it.svg"),
+                'width': 211,
+                'height': 185}}
+
+        # a11y
+        self.labels['tryit'].set_mnemonic_widget(self.buttons['tryit'])
+        self.labels['installit'].set_mnemonic_widget(self.buttons['graph'])
+
+        for key in self.images:
+            image = self.filenames[key]
+            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
+                image['path'],
+                image['width'],
+                image['height'])
+            self.images[key].set_from_pixbuf(pixbuf)
+
+    def translate_ui(self):
+        """ Translates all ui elements """
+        if not self.no_tryit:
+            txt = _("Use RebornOS without making any changes to your system.")
+        else:
+            txt = ""
+        self.labels['tryit'].set_markup(txt)
+        self.labels['tryit'].set_name('tryit_label')
+
+        txt = _("Create a permanent place for RebornOS on your system.")
+        self.labels['installit'].set_markup(txt)
+        self.labels['installit'].set_name('installit_label')
+
+        txt = _("Try It")
+        self.buttons['tryit'].set_label(txt)
+
+        # txt = _("CLI Installer")
+        # self.buttons['cli'].set_label(txt)
+
+        txt = _("Install It")
+        self.buttons['graph'].set_label(txt)
+
+        txt = _("Welcome to RebornOS!")
+        self.header.set_subtitle(txt)
+
+    def quit_cnchi(self):
+        """ Quits installer """
+        misc.remove_temp_files(self.settings.get('temp'))
+        for proc in multiprocessing.active_children():
+            proc.terminate()
+        logging.shutdown()
+        sys.exit(0)
+
+    def on_tryit_button_clicked(self, _widget, _data=None):
+        """ Try live CD, quits installer """
+        self.quit_cnchi()
+
+    def on_graph_button_clicked(self, _widget, _data=None):
+        """ User wants to install """
+        self.show_loading_message()
+        # Tell timezone process to start searching now
+        self.settings.set('timezone_start', True)
+        # Simulate a forward button click
+        self.forward_button.clicked()
+
+    def show_loading_message(self, do_show=True):
+        """ Shows a message so the user knows Cnchi is loading pages
+            only when running from liveCD """
+        if do_show:
+            txt = _("Loading, please wait...")
+        else:
+            txt = ""
+        self.labels['loading'].set_markup(txt)
+        self.labels['loading'].queue_draw()
+        misc.gtk_refresh()
+
+    def store_values(self):
+        """ Store changes (none in this page) """
+        self.forward_button.show()
+        return True
+
+    def prepare(self, direction):
+        """ Prepare page before showing it """
+        self.translate_ui()
+        self.show_all()
+        self.forward_button.hide()
+
+        # a11y Set install option as default if ENTER is pressed
+        self.buttons['graph'].set_can_default(True)
+        self.main_window.set_default(self.buttons['graph'])
+
+        if self.no_tryit:
+            self.buttons['tryit'].set_sensitive(False)
+        if direction == "backwards":
+            self.show_loading_message(do_show=False)
diff --git a/HELP_ME.sh b/HELP_ME.sh
new file mode 100755 (executable)
index 0000000..65b9ad0
--- /dev/null
@@ -0,0 +1,102 @@
+#  Copyright © 2016-2019 RebornOS
+#
+#  This file is part of RebornOS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+#!/bin/bash
+#####################################
+# Script to make ISO testing easier #
+#####################################
+
+QUESTION(){
+echo
+echo "Please select your preferred course of action:"
+echo
+options=("Build an ISO" "Update code to latest stuff on Gitlab" "Change Branches" "Quit")
+select opt in "${options[@]}"
+do
+    case $opt in
+        "Build an ISO")
+            BUILD;break;;
+        "Update code to latest stuff on Gitlab")
+            UPDATE;break;;
+        "Change Branches")
+           BRANCHES;break;;
+        "Quit")
+            break
+            ;;
+        *) echo "ERROR!!! ERROR!!!! SOUND THE ALARM!!!" 
+            echo "Sadly, option $REPLY is not possible! Please select either option 1, 2, or 3 instead. Thank you!";;
+    esac
+done
+}
+
+BRANCHES(){
+touch /tmp/branch.txt
+yad --form --separator='\n' \
+    --field="Branch:cb" "master!development!">/tmp/branch.txt \
+MY_BRANCH=$(sed '1q;d' /tmp/branch.txt)
+sudo git checkout $(sed '1q;d' /tmp/branch.txt)
+echo
+echo
+echo "DONE"
+rm -f /tmp/branch.txt
+echo
+echo
+}
+
+BUILD(){
+echo "ENSURING ALL DEPENDENCIES ARE ALREADY INSTALLED..."
+sudo pacman -S arch-install-scripts cpio dosfstools git libisoburn mkinitcpio-nfs-utils make patch squashfs-tools wget lynx archiso yad --noconfirm --needed
+echo
+if [ -f ./work/pacman.conf ]; then
+echo "REMOVING FILES FROM PREVIOUS BUILD..."
+rm -rf ./work 
+echo "WELL THAT TOOK AWHILE! BUT NOW WE'RE DONE :)"
+fi
+echo
+echo 
+echo "###################"
+echo "# BUILDING ISO... #"
+echo "###################"
+echo
+echo
+./build.sh -v
+}
+
+UPDATE(){
+if [ -f ./work/pacman.conf ]; then
+echo
+echo "CLEANING EVERYTHING FRIST FROM PREVIOUS BUILDS..."
+rm -rf ./work 
+echo
+echo "WELL THAT TOOK AWHILE! BUT NOW WE'RE DONE :)"
+fi
+echo
+echo
+echo "UPDATING TO THE LATEST AND GREATEST..."
+sudo git pull
+echo
+echo "DONE"
+}
+
+export -f QUESTION BUILD UPDATE BRANCHES
+QUESTION
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..9387be6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,300 @@
+# RebornOS installer cnchi Gnome based
+
+![License](https://img.shields.io/github/license/antergos/cnchi.svg)
+
+The latest stable version is: RebornOS-2020.04.29-x86_64.iso
+
+<p align="center">
+<img src="https://repo.rebornos.org/RebornOS/sources/cnchi/cnchi-screenshot.png">
+</p>
+
+<!-- ![Deepin_Image](/images/deepin.png) -->
+
+## Download Locations ##
+
+From the web page of <a href="https://rebornos.org/" class="button">RebornOS</a>
+
+From our download page on <a href="https://osdn.net/projects/rebornos/releases" class="button">OSDN</a>
+
+From our download page on <a href="https://sourceforge.net/projects/rebornos/files/" class="button">Sourceforge</a>
+
+From our <a href="https://repo.rebornos.org/RebornOS/iso/" class="button">Server</a>
+
+<br>
+
+# To Manually Build Yourself
+
+### Dependencies
+- isolinux/syslinux
+- arch-install-scripts
+- cpio
+- dosfstools
+- git 
+- libisoburn
+- mkinitcpio-nfs-utils
+- make
+- opendesktop-fonts
+- patch
+- squashfs-tools
+- archiso
+- lynx
+- wget
+- yad
+
+<br>
+
+### Free space
+
+Please check that you have 5GB (or more) of free harddisk space in your root partition:
+`df -h /`
+
+<br>
+
+### Instructions
+
+1. Install dependencies:
+```
+sudo pacman -S arch-install-scripts cpio dosfstools git libisoburn mkinitcpio-nfs-utils make patch squashfs-tools wget lynx archiso yad --noconfirm --needed
+```
+2. Clone the repository recursively and `cd` into it (a folder named `cnchi-gnome-based` will be created):
+```
+git clone https://gitlab.com/reborn-os-team/rebornos-cnchi/cnchi-gnome-based.git --recursive && cd cnchi-gnome-based
+```
+3. Create an `out` folder by running:
+```
+sudo mkdir out
+```
+4. Begin building it:
+```
+sudo ./build.sh -v
+```
+
+<br>
+
+### That's it!
+
+To rebuild the ISO, simply remove the `build` folder in addition to emptying the `out` folder. Next, re-enter the command from step 4.
+
+<br>
+
+### General Notes and Information
+
+- `build.sh`: script used to build the ISO. 
+- **`HELP_ME.sh`: script to easily build and update an ISO easily. No advance knowledge needed!**
+- `packages.both`: simply a list of all packages to be installed in an ISO during the build process. To change the DE, login manager, or any other grouping of packages, simply edit this file.
+- `pacman.conf`: the pacman.conf file used by build.sh to build the ISO. With this, it can ignore whatever personal configs you have on your own system in `/etc/pacman.conf` and instead focus solely on this one.
+- `run.sh`: um... useless file. Going to get rid of it soon.
+- `TEST_FILE.sh`: guess what? Another outdated, useless file waiting to be gotten rid of.
+- `translations.sh`: translation magic!
+- `pacman-init.service`: adds a pacman module to systemd. I think it works in our ISO, but honestly its something snagged from the Antergos ISO - and I don't entirely know if it does anything in ours. However, it sure does sound like a useful file, right?
+- `clean.sh`: easy file to use to remove your `build` and `out` folder, in general just an easy method of getting everything all sparkly clean and ready to build another ISO.
+- `config`: configurations for what version of Cnchi to use in our ISO, as well as where to fetch it from.
+- `gitv2.sh`: a secial file made by Palanthis so as to automate pushing dev changes up to Gitlab here.
+
+<br>
+
+### How Delete Directory
+
+Once the installation ISO is generated, and copied elsewhere, the directory used for its creation can be deleted by:
+
+```
+sudo rm -r cnchi-gnome-based
+```
+
+You can access the RebornOS page by <a href="https://rebornos.org/" class="button">clicking here</a>.
+
+<br>
+<br>
+<br>
+<br>
+
+# Cómo construir la ISO del instalador de RebornOS
+
+La última versión estable es: RebornOS-2020.04.29-x86_64.iso
+
+## Sitios desde donde descargar el instalador: ##
+
+Desde la página web de <a href="https://rebornos.org/" class="button">RebornOS</a>
+
+Desde nuestra página de descargas en <a href="https://osdn.net/projects/rebornos/releases" class="button">OSDN</a>
+
+Desde nuestra página de descargas en <a href="https://sourceforge.net/projects/rebornos/files/" class="button">Sourceforge</a>
+
+Desde nuestro <a href="https://repo.rebornos.org/RebornOS/iso/" class="button">Servidor</a>
+
+<br>
+
+### Dependencias
+- isolinux/syslinux
+- arch-install-scripts
+- cpio
+- dosfstools
+- git 
+- libisoburn
+- mkinitcpio-nfs-utils
+- make
+- opendesktop-fonts
+- patch
+- squashfs-tools
+- archiso
+- lynx
+- wget
+- yad
+
+<br>
+
+### Pasos a llevar a cabo para crear el instalador
+
+1. Primero, deberemos de instalar las dependencias:
+
+```
+sudo pacman -S arch-install-scripts cpio dosfstools git libisoburn mkinitcpio-nfs-utils make patch squashfs-tools wget lynx archiso yad --noconfirm --needed
+```
+Una vez instaladas, reiniciamos el sistema.
+
+2. Luego, clonamos el repositorio localmente, y entramos a él (se creará una carpeta de nombre `cnchi-gnome-based`). Esto se hace con:
+
+```
+git clone https://gitlab.com/reborn-os-team/rebornos-cnchi/cnchi-gnome-based.git --recursive && cd cnchi-gnome-based
+```
+
+3. Creamos una carpeta `out`:
+
+```
+sudo mkdir out
+```
+
+4. Y ahora, comenzamos la construcción de la ISO con:
+
+```
+sudo ./build.sh -v
+```
+
+Una vez finalizado sin errores, la ISO generada estará dentro de la carpeta `out`.
+
+Si quisiéramos regenerar la ISO, al volver a ejecutar el paso `4`, automáticamente se eliminarán los contenidos de las carpetas de trabajo, y de generación del instalador.
+
+En el caso de que hayamos copiado la ISO a alguna otra parte de nuestro computador, podremos eliminar esta carpeta:
+
+```
+sudo rm -r cnchi-gnome-based
+```
+
+<br>
+
+### Un poco de información
+
+- `build.sh`: es el script utilizado para construir la ISO del instalador. 
+- **`HELP_ME.sh`: script para construir y actualizar la ISO fácilmente. ¡No se necesitan conocimientos avanzados!**
+- `packages.both`: contiene un listado de paquetes a ser instalados en la ISO durante su generación. Para cambiar el escritorio, el login manager, o cualquier otro paquete, simplemente edite este archivo.
+- `pacman.conf`: es el archivo pacman.conf utilizado por build.sh para generar la ISO. Al utilizar éste, se ignorará cualquier configuración personal existente en su sistema (en `/etc/pacman.conf`).
+- `run.sh`: Archivo inútil Voy a deshacerme de él pronto.
+- `TEST_FILE.sh`: ¿Adivina qué? Otro archivo obsoleto e inútil a la espera de ser eliminado.
+- `translations.sh`: ¡Traducción mágica!
+- `pacman-init.service`: Agrega un módulo pacman a systemd. Creo que funciona en nuestro ISO, pero honestamente es algo enganchado con el ISO de Antergos, y no sé del todo si hace algo en el nuestro. Sin embargo, seguro que suena como un archivo útil, ¿verdad?
+- `clean.sh`: Archivo fácil de usar para eliminar su carpeta `build` y` out`, en general solo un método fácil para tener todo limpio y listo para construir otro ISO.
+- `config`: Configuraciones para qué versión de Cnchi usar en nuestro ISO, así como de dónde obtenerla.
+- `gitv2.sh`: Un archivo especial hecho por Palanthis para automatizar los cambios de desarrollo de empuje hasta Gitlab aquí.
+
+<br>
+
+Pueden acceder a la página de RebornOS <a href="https://rebornos.org/" class="button">haciendo clic aquí</a>.
+
+<br>
+<br>
+<br>
+<br>
+
+
+# Comment construire l'installateur ISO de RebornOS
+
+La dernière version stable est: RebornOS-2020.04.29-x86_64.iso
+
+## Lieux de téléchargement de l'installateur : ##
+
+Sur le site Web de <a href="https://rebornos.org/" class="button">RebornOS</a>
+
+Depuis notre page de téléchargement sur <a href="https://osdn.net/projects/rebornos/releases" class="button">OSDN</a>
+
+Depuis notre page de téléchargement sur <a href="https://sourceforge.net/projects/rebornos/files/" class="button">Sourceforge</a>
+
+À partir de notre <a href="https://repo.rebornos.org/RebornOS/iso/" class="button">Serveur</a>
+
+<br>
+
+### Dépendances
+
+- isolinux/syslinux
+- arch-install-scripts
+- cpio
+- dosfstools
+- git 
+- libisoburn
+- mkinitcpio-nfs-utils
+- make
+- opendesktop-fonts
+- patch
+- squashfs-tools
+- archiso
+- lynx
+- wget
+- yad
+
+<br>
+
+### Étapes à suivre pour créer le programme d'installation
+
+1. Nous devrons installer les dépendances:
+
+```
+sudo pacman -S arch-install-scripts cpio dosfstools git libisoburn mkinitcpio-nfs-utils make patch squashfs-tools wget lynx archiso yad --noconfirm --needed
+```
+Une fois installé, nous redémarrons le système.
+
+2. Ensuite, cloner le référentiel localement, et l'entrer (un dossier nommé `cnchi-gnome-based` sera créé). Ceci est fait avec :
+
+```
+git clone https://gitlab.com/reborn-os-team/rebornos-cnchi/cnchi-gnome-based.git --recursive && cd cnchi-gnome-based
+```
+
+3. Créez un dossier `out`:
+
+```
+sudo mkdir out
+```
+
+Et maintenant, nous commençons la construction de l'ISO avec:
+
+```
+sudo ./build.sh -v
+```
+
+Une fois terminé sans erreur, l'ISO généré sera dans le dossier `out`.
+
+Si nous voulons régénérer l'ISO, en exécutant à nouveau l'étape `4`, le contenu des dossiers de travail et des dossiers de génération de l'installateur sera automatiquement supprimé.
+
+Si nous avons copié l'ISO dans une autre partie de notre ordinateur, nous pourrons supprimer ce dossier :
+
+```
+sudo rm -r cnchi-gnome-based
+```
+
+### Un peu d'information:
+
+- `build.sh`: est le script utilisé pour construire l'ISO de l'installateur. 
+- **`HELP_ME.sh`: script pour construire et mettre à jour facilement l'ISO. Aucune connaissance avancée n'est nécessaire!**
+- `packages.both`: contient une liste de paquets à installer dans l'ISO pendant sa génération. Pour changer le bureau, le gestionnaire de connexion ou tout autre paquet, éditez simplement ce fichier.
+- `pacman.conf`: est le fichier pacman.conf utilisé par build.sh pour générer l'ISO. Lors de son utilisation, toute configuration personnelle existante dans votre système (dans `/etc/pacman.conf`) sera ignorée.
+- `run.sh`: Je vais bientôt m'en débarrasser.
+- `TEST_FILE.sh`: Devinez quoi ? Un autre fichier obsolète et inutile qui attend d'être supprimé.
+- `translations.sh`: Traduction magique!
+- `pacman-init.service`: Ajouter un module pacman à systemd. Je pense que cela fonctionne sur notre ISO, mais honnêtement, c'est un peu accro à l'Ancien ISO, et je ne sais pas si cela fait quoi que ce soit sur le nôtre. Cependant, cela sonne certainement comme un fichier utile, n'est-ce pas?
+- `clean.sh`: Fichier facile à utiliser pour supprimer votre dossier `build' et `out', en général une méthode simple pour avoir tout propre et prêt à construire un autre ISO.
+- `config`: Configurations pour quelle version de Cnchi utiliser dans notre ISO, ainsi que où l'obtenir.
+- `gitv2.sh`: Un fichier spécial réalisé par Palanthis pour automatiser les changements de développement push jusqu'à Gitlab ici.
+
+<br>
+
+Vous pouvez accéder à la page RebornOS <a href="https://rebornos.org/" class="button">en cliquant ici</a>.
+
+
diff --git a/TEST_FILE.sh b/TEST_FILE.sh
new file mode 100644 (file)
index 0000000..5d207fa
--- /dev/null
@@ -0,0 +1,54 @@
+#  Copyright © 2016-2019 RebornOS
+#
+#  This file is part of RebornOS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-timeout '0' && gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-timeout '0'
+WM=`wmctrl -m | grep Name | awk '{print $2}'`
+echo $WM
+LINE=$(grep -o "B" <<<"$WM" | wc -l)
+echo $LINE
+if [[ $LINE == 1 ]]; then
+dconf write /org/gnome/desktop/background/picture-uri "'file:///usr/share/backgrounds/abstract1-reborn2.png'"
+dconf write /org/gnome/desktop/interface/gtk-theme "'Evo-Pop'"
+dconf write /org/gnome/desktop/interface/icon-name "'Flat-Remix'"
+fi
+if [ $LINE -eq "0" ]; then
+dconf write /org/gnome/desktop/background/picture-uri "'file:///usr/share/backgrounds/elementary.jpg'"
+dconf write /org/gnome/shell/extensions/user-theme/name "'Arctic-Apricity'"
+dconf write /org/gnome/desktop/interface/gtk-theme "'Arc'"
+dconf write /org/gnome/desktop/interface/icon-name "'Apricity-Icons'"
+fi
+wget --spider www.google.com
+if [ "$?" = 0 ]; then
+  sudo rm -rf /etc/pacman.d/gnupg
+  sudo pacman -Syy
+  sudo pacman-key --init
+  sudo pacman-key --populate archlinux rebornos
+  sudo pacman-key --refresh-keys
+  sudo pacman -Syy
+  reflector --verbose --latest 10 --sort rate --save /etc/pacman.d/mirrorlist
+# if [ ! -z $(grep "eu" "etc/pacman.d/mirrorlist") ]; then 
+# sudo cp /usr/bin/cnchi/pacman.conf /etc/
+# sudo mv /usr/bin/cnchi/reborn-mirrorlist2 /etc/pacman.d/reborn-mirrorlist
+# fi
+else exec /usr/bin/internet.sh
+fi
diff --git a/airootfs/README.md b/airootfs/README.md
new file mode 100644 (file)
index 0000000..0f10892
--- /dev/null
@@ -0,0 +1,19 @@
+# Reborn-OS
+![Deepin_Image](/images/deepin.png)
+
+## Download Locations ##
+- <a href="https://sourceforge.net/projects/antergos-deepin/" class="button">Sourceforge</a> 
+
+### About Airootfs
+
+The `airootfs` folder is kind of like a virtual system. Basically, the `build.sh` script treats the `airootfs` folder as the `/` one on your real system. But through this, it is able to isolate itself from your system, kind of like Docker. 
+
+### How to Add Files (And Remove Them)
+
+**Add Files:**
+- Simply treat the `airootfs` folder as your root one (`/`) and as such, begin branching out from here. You can already see the `etc` folder present, among others. As such, just create the path, starting from root, to your desired addition in the system and add it in.
+- **Exception**: the `Home` folder. This folder is not present or understood by the `build.sh` script. Instead, it uses the `airootfs/etc/skel` folder as your future home folder. Therefore, to add a file to the ISOs Home directory, all you need to do is add your file to that `skel` folder.
+- **One more exception**: the `root` folder. This folder is simply used by the `build.sh` script to store building commands that are intended to be isolated to the future ISO's system, and cannot run at all on your full, real system. As such, you can find the `customize_airootfs.sh` script inside there, which `build.sh` called up in its build to run special commands that should only have an impact on the future ISO - such as enabling certain systemd modules.
+
+**Remove Files:**
+- Same as adding, but reverse! Delete away my friend.
diff --git a/airootfs/etc/arch-release b/airootfs/etc/arch-release
new file mode 100644 (file)
index 0000000..d2de066
--- /dev/null
@@ -0,0 +1 @@
+RebornOS release <VERSION> (ISO-Rolling)
diff --git a/airootfs/etc/fstab b/airootfs/etc/fstab
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/airootfs/etc/hostname b/airootfs/etc/hostname
new file mode 100644 (file)
index 0000000..3593c71
--- /dev/null
@@ -0,0 +1 @@
+RebornOS
diff --git a/airootfs/etc/lightdm/lightdm.conf b/airootfs/etc/lightdm/lightdm.conf
new file mode 100644 (file)
index 0000000..b78875e
--- /dev/null
@@ -0,0 +1,160 @@
+#
+# General configuration
+#
+# start-default-seat = True to always start one seat if none are defined in the configuration
+# greeter-user = User to run greeter as
+# minimum-display-number = Minimum display number to use for X servers
+# minimum-vt = First VT to run displays on
+# lock-memory = True to prevent memory from being paged to disk
+# user-authority-in-system-dir = True if session authority should be in the system location
+# guest-account-script = Script to be run to setup guest account
+# logind-check-graphical = True to on start seats that are marked as graphical by logind
+# log-directory = Directory to log information to
+# run-directory = Directory to put running state in
+# cache-directory = Directory to cache to
+# sessions-directory = Directory to find sessions
+# remote-sessions-directory = Directory to find remote sessions
+# greeters-directory = Directory to find greeters
+#
+[LightDM]
+#start-default-seat=true
+#greeter-user=lightdm
+#minimum-display-number=0
+#minimum-vt=1
+#lock-memory=true
+#user-authority-in-system-dir=true
+#guest-account-script=guest-account
+#logind-check-graphical=false
+#log-directory=/var/log/lightdm
+run-directory=/var/run/lightdm
+#cache-directory=/var/cache/lightdm
+#sessions-directory=/usr/share/lightdm/sessions:/usr/share/xsessions
+#remote-sessions-directory=/usr/share/lightdm/remote-sessions
+#greeters-directory=/usr/share/lightdm/greeters:/usr/share/xgreeters
+
+#
+# Seat defaults
+#
+# type = Seat type (xlocal, xremote)
+# pam-service = lightdm
+# pam-autologin-service = lightdm-autologin
+# pam-greeter-service = lightdm-greeter
+# xserver-command = X server command to run (can also contain arguments e.g. X -special-option)
+# xmir-command = Xmir server command to run (can also contain arguments e.g. Xmir -special-option)
+# xserver-layout = Layout to pass to X server
+# xserver-config = Config file to pass to X server
+# xserver-allow-tcp = True if TCP/IP connections are allowed to this X server
+# xserver-share = True if the X server is shared for both greeter and session
+# xserver-hostname = Hostname of X server (only for type=xremote)
+# xserver-display-number = Display number of X server (only for type=xremote)
+# xdmcp-manager = XDMCP manager to connect to (implies xserver-allow-tcp=true)
+# xdmcp-port = XDMCP UDP/IP port to communicate on
+# xdmcp-key = Authentication key to use for XDM-AUTHENTICATION-1 (stored in keys.conf)
+# unity-compositor-command = Unity compositor command to run (can also contain arguments e.g. unity-system-compositor -special-option)
+# unity-compositor-timeout = Number of seconds to wait for compositor to start
+# greeter-session = lightdm-deepin-greeter
+# greeter-hide-users = True to hide the user list
+# greeter-allow-guest = True if the greeter should show a guest login option
+# greeter-show-manual-login = True if the greeter should offer a manual login option
+# greeter-show-remote-login = True if the greeter should offer a remote login option
+# user-session = deepin
+# allow-user-switching = True if allowed to switch users
+# allow-guest = True if guest login is allowed
+# guest-session = Session to load for guests (overrides user-session)
+# session-wrapper = Wrapper script to run session with
+# greeter-wrapper = Wrapper script to run greeter with
+# guest-wrapper = Wrapper script to run guest sessions with
+# display-setup-script = Script to run when starting a greeter session (runs as root)
+# display-stopped-script = Script to run after stopping the display server (runs as root)
+# greeter-setup-script = Script to run when starting a greeter (runs as root)
+# session-setup-script = Script to run when starting a user session (runs as root)
+# session-cleanup-script = Script to run when quitting a user session (runs as root)
+# autologin-guest = True to log in as guest by default
+# autologin-user = reborn
+# autologin-user-timeout = Number of seconds to wait before loading default user
+# autologin-session = deepin
+# autologin-in-background = True if autologin session should not be immediately activated
+# exit-on-failure = True
+#
+[Seat:*]
+#type=xlocal
+pam-service=lightdm
+pam-autologin-service=lightdm-autologin
+pam-greeter-service=lightdm-greeter
+#xserver-command=X
+#xmir-command=Xmir
+#xserver-layout=
+#xserver-config=
+#xserver-allow-tcp=false
+#xserver-share=true
+#xserver-hostname=
+#xserver-display-number=
+#xdmcp-manager=
+#xdmcp-port=177
+#xdmcp-key=
+#unity-compositor-command=unity-system-compositor
+#unity-compositor-timeout=60
+greeter-session=lightdm-deepin-greeter
+#greeter-hide-users=false
+#greeter-allow-guest=true
+#greeter-show-manual-login=false
+#greeter-show-remote-login=true
+user-session=deepin
+#allow-user-switching=true
+#allow-guest=true
+#guest-session=
+session-wrapper=/etc/lightdm/Xsession
+#greeter-wrapper=
+#guest-wrapper=
+#display-setup-script=
+#display-stopped-script=
+#greeter-setup-script=
+#session-setup-script=
+#session-cleanup-script=
+#autologin-guest=false
+autologin-user=reborn
+autologin-user-timeout=0
+#autologin-in-background=false
+#autologin-session=deepin
+exit-on-failure=true
+
+#
+# Seat configuration
+#
+# Each seat must start with "Seat:".
+# Uses settings from [SeatDefaults], any of these can be overriden by setting them in this section.
+#
+#[Seat:0]
+
+#
+# XDMCP Server configuration
+#
+# enabled = True if XDMCP connections should be allowed
+# port = UDP/IP port to listen for connections on
+# key = Authentication key to use for XDM-AUTHENTICATION-1 or blank to not use authentication (stored in keys.conf)
+#
+# The authentication key is a 56 bit DES key specified in hex as 0xnnnnnnnnnnnnnn.  Alternatively
+# it can be a word and the first 7 characters are used as the key.
+#
+[XDMCPServer]
+#enabled=false
+#port=177
+#key=
+
+#
+# VNC Server configuration
+#
+# enabled = True if VNC connections should be allowed
+# command = Command to run Xvnc server with
+# port = TCP/IP port to listen for connections on
+# width = Width of display to use
+# height = Height of display to use
+# depth = Color depth of display to use
+#
+[VNCServer]
+#enabled=false
+#command=Xvnc
+#port=5900
+#width=1024
+#height=768
+#depth=8
diff --git a/airootfs/etc/locale.conf b/airootfs/etc/locale.conf
new file mode 100644 (file)
index 0000000..01ec548
--- /dev/null
@@ -0,0 +1 @@
+LANG=en_US.UTF-8
diff --git a/airootfs/etc/locale.gen b/airootfs/etc/locale.gen
new file mode 100644 (file)
index 0000000..2fde1c8
--- /dev/null
@@ -0,0 +1,318 @@
+
+# Configuration file for locale-gen
+#
+# lists of locales that are to be generated by the locale-gen command.
+#
+# Each line is of the form:
+#
+#     <locale> <charset>
+#
+#  where <locale> is one of the locales given in /usr/share/i18n/locales
+#  and <charset> is one of the character sets listed in /usr/share/i18n/charmaps
+#
+#  Examples:
+#  en_US ISO-8859-1
+#  en_US.UTF-8 UTF-8
+#  de_DE ISO-8859-1
+#  de_DE@euro ISO-8859-15
+#
+#  The locale-gen command will generate all the locales,
+#  placing them in /usr/lib/locale.
+#
+#  A list of supported locales is included in this file.
+#  Uncomment the ones you need.
+#
+
+aa_DJ.UTF-8 UTF-8
+aa_ER UTF-8
+aa_ER@saaho UTF-8
+aa_ET UTF-8
+af_ZA.UTF-8 UTF-8
+ak_GH UTF-8
+am_ET UTF-8
+an_ES.UTF-8 UTF-8
+anp_IN UTF-8
+ar_AE.UTF-8 UTF-8
+ar_BH.UTF-8 UTF-8
+ar_DZ.UTF-8 UTF-8
+ar_EG.UTF-8 UTF-8
+ar_IN UTF-8
+ar_IQ.UTF-8 UTF-8
+ar_JO.UTF-8 UTF-8
+ar_KW.UTF-8 UTF-8
+ar_LB.UTF-8 UTF-8
+ar_LY.UTF-8 UTF-8
+ar_MA.UTF-8 UTF-8
+ar_OM.UTF-8 UTF-8
+ar_QA.UTF-8 UTF-8
+ar_SA.UTF-8 UTF-8
+ar_SD.UTF-8 UTF-8
+ar_SS UTF-8
+ar_SY.UTF-8 UTF-8
+ar_TN.UTF-8 UTF-8
+ar_YE.UTF-8 UTF-8
+ayc_PE UTF-8
+az_AZ UTF-8
+as_IN UTF-8
+ast_ES.UTF-8 UTF-8
+be_BY.UTF-8 UTF-8
+be_BY@latin UTF-8
+bem_ZM UTF-8
+ber_DZ UTF-8
+ber_MA UTF-8
+bg_BG.UTF-8 UTF-8
+bhb_IN.UTF-8 UTF-8
+bho_IN UTF-8
+bn_BD UTF-8
+bn_IN UTF-8
+bo_CN UTF-8
+bo_IN UTF-8
+br_FR.UTF-8 UTF-8
+brx_IN UTF-8
+bs_BA.UTF-8 UTF-8
+byn_ER UTF-8
+ca_AD.UTF-8 UTF-8
+ca_ES.UTF-8 UTF-8
+ca_FR.UTF-8 UTF-8
+ca_IT.UTF-8 UTF-8
+ce_RU UTF-8
+chr_US UTF-8
+cmn_TW UTF-8
+crh_UA UTF-8
+cs_CZ.UTF-8 UTF-8
+csb_PL UTF-8
+cv_RU UTF-8
+cy_GB.UTF-8 UTF-8
+da_DK.UTF-8 UTF-8
+de_AT.UTF-8 UTF-8
+de_BE.UTF-8 UTF-8
+de_CH.UTF-8 UTF-8
+de_DE.UTF-8 UTF-8
+de_IT.UTF-8 UTF-8
+de_LI.UTF-8 UTF-8
+de_LU.UTF-8 UTF-8
+doi_IN UTF-8
+dv_MV UTF-8
+dz_BT UTF-8
+el_GR.UTF-8 UTF-8
+el_CY.UTF-8 UTF-8
+en_AG UTF-8
+en_AU.UTF-8 UTF-8
+en_BW.UTF-8 UTF-8
+en_CA.UTF-8 UTF-8
+en_DK.UTF-8 UTF-8
+en_GB.UTF-8 UTF-8
+en_HK.UTF-8 UTF-8
+en_IE.UTF-8 UTF-8
+en_IL UTF-8
+en_IN UTF-8
+en_NG UTF-8
+en_NZ.UTF-8 UTF-8
+en_PH.UTF-8 UTF-8
+en_SG.UTF-8 UTF-8
+en_US.UTF-8 UTF-8
+en_ZA.UTF-8 UTF-8
+en_ZM UTF-8
+en_ZW.UTF-8 UTF-8
+eo UTF-8
+es_AR.UTF-8 UTF-8
+es_BO.UTF-8 UTF-8
+es_CL.UTF-8 UTF-8
+es_CO.UTF-8 UTF-8
+es_CR.UTF-8 UTF-8
+es_CU UTF-8
+es_DO.UTF-8 UTF-8
+es_EC.UTF-8 UTF-8
+es_ES.UTF-8 UTF-8
+es_GT.UTF-8 UTF-8
+es_HN.UTF-8 UTF-8
+es_MX.UTF-8 UTF-8
+es_NI.UTF-8 UTF-8
+es_PA.UTF-8 UTF-8
+es_PE.UTF-8 UTF-8
+es_PR.UTF-8 UTF-8
+es_PY.UTF-8 UTF-8
+es_SV.UTF-8 UTF-8
+es_US.UTF-8 UTF-8
+es_UY.UTF-8 UTF-8
+es_VE.UTF-8 UTF-8
+et_EE.UTF-8 UTF-8
+eu_ES.UTF-8 UTF-8
+fa_IR UTF-8
+ff_SN UTF-8
+fi_FI.UTF-8 UTF-8
+fil_PH UTF-8
+fo_FO.UTF-8 UTF-8
+fr_BE.UTF-8 UTF-8
+fr_CA.UTF-8 UTF-8
+fr_CH.UTF-8 UTF-8
+fr_FR.UTF-8 UTF-8
+fr_LU.UTF-8 UTF-8
+fur_IT UTF-8
+fy_NL UTF-8
+fy_DE UTF-8
+ga_IE.UTF-8 UTF-8
+gd_GB.UTF-8 UTF-8
+gez_ER UTF-8
+gez_ER@abegede UTF-8
+gez_ET UTF-8
+gez_ET@abegede UTF-8
+gl_ES.UTF-8 UTF-8
+gu_IN UTF-8
+gv_GB.UTF-8 UTF-8
+ha_NG UTF-8
+hak_TW UTF-8
+he_IL.UTF-8 UTF-8
+hi_IN UTF-8
+hne_IN UTF-8
+hr_HR.UTF-8 UTF-8
+hsb_DE.UTF-8 UTF-8
+ht_HT UTF-8
+hu_HU.UTF-8 UTF-8
+hy_AM UTF-8
+ia_FR UTF-8
+id_ID.UTF-8 UTF-8
+ig_NG UTF-8
+ik_CA UTF-8
+is_IS.UTF-8 UTF-8
+it_CH.UTF-8 UTF-8
+it_IT.UTF-8 UTF-8
+iu_CA UTF-8
+ja_JP.UTF-8 UTF-8
+ka_GE.UTF-8 UTF-8
+kk_KZ.UTF-8 UTF-8
+kl_GL.UTF-8 UTF-8
+km_KH UTF-8
+kn_IN UTF-8
+ko_KR.UTF-8 UTF-8
+kok_IN UTF-8
+ks_IN UTF-8
+ks_IN@devanagari UTF-8
+ku_TR.UTF-8 UTF-8
+kw_GB.UTF-8 UTF-8
+ky_KG UTF-8
+lb_LU UTF-8
+lg_UG.UTF-8 UTF-8
+li_BE UTF-8
+li_NL UTF-8
+lij_IT UTF-8
+ln_CD UTF-8
+lo_LA UTF-8
+lt_LT.UTF-8 UTF-8
+lv_LV.UTF-8 UTF-8
+lzh_TW UTF-8
+mag_IN UTF-8
+mai_IN UTF-8
+mg_MG.UTF-8 UTF-8
+mhr_RU UTF-8
+mi_NZ.UTF-8 UTF-8
+mk_MK.UTF-8 UTF-8
+ml_IN UTF-8
+mn_MN UTF-8
+mni_IN UTF-8
+mr_IN UTF-8
+ms_MY.UTF-8 UTF-8
+mt_MT.UTF-8 UTF-8
+my_MM UTF-8
+nan_TW UTF-8
+nan_TW@latin UTF-8
+nb_NO.UTF-8 UTF-8
+nds_DE UTF-8
+nds_NL UTF-8
+ne_NP UTF-8
+nhn_MX UTF-8
+niu_NU UTF-8
+niu_NZ UTF-8
+nl_AW UTF-8
+nl_BE.UTF-8 UTF-8
+nl_NL.UTF-8 UTF-8
+nn_NO.UTF-8 UTF-8
+nr_ZA UTF-8
+nso_ZA UTF-8
+oc_FR.UTF-8 UTF-8
+om_ET UTF-8
+om_KE.UTF-8 UTF-8
+or_IN UTF-8
+os_RU UTF-8
+pa_IN UTF-8
+pa_PK UTF-8
+pap_AW UTF-8
+pap_CW UTF-8
+pl_PL.UTF-8 UTF-8
+ps_AF UTF-8
+pt_BR.UTF-8 UTF-8
+pt_PT.UTF-8 UTF-8
+quz_PE UTF-8
+raj_IN UTF-8
+ro_RO.UTF-8 UTF-8
+ru_RU.UTF-8 UTF-8
+ru_UA.UTF-8 UTF-8
+rw_RW UTF-8
+sa_IN UTF-8
+sat_IN UTF-8
+sc_IT UTF-8
+sd_IN UTF-8
+sd_IN@devanagari UTF-8
+se_NO UTF-8
+sgs_LT UTF-8
+shs_CA UTF-8
+si_LK UTF-8
+sid_ET UTF-8
+sk_SK.UTF-8 UTF-8
+sl_SI.UTF-8 UTF-8
+so_DJ.UTF-8 UTF-8
+so_ET UTF-8
+so_KE.UTF-8 UTF-8
+so_SO.UTF-8 UTF-8
+sq_AL.UTF-8 UTF-8
+sq_MK UTF-8
+sr_ME UTF-8
+sr_RS UTF-8
+sr_RS@latin UTF-8
+ss_ZA UTF-8
+st_ZA.UTF-8 UTF-8
+sv_FI.UTF-8 UTF-8
+sv_SE.UTF-8 UTF-8
+sw_KE UTF-8
+sw_TZ UTF-8
+szl_PL UTF-8
+ta_IN UTF-8
+ta_LK UTF-8
+tcy_IN.UTF-8 UTF-8
+te_IN UTF-8
+tg_TJ.UTF-8 UTF-8
+th_TH.UTF-8 UTF-8
+the_NP UTF-8
+ti_ER UTF-8
+ti_ET UTF-8
+tig_ER UTF-8
+tk_TM UTF-8
+tl_PH.UTF-8 UTF-8
+tn_ZA UTF-8
+tr_CY.UTF-8 UTF-8
+tr_TR.UTF-8 UTF-8
+ts_ZA UTF-8
+tt_RU UTF-8
+tt_RU@iqtelif UTF-8
+ug_CN UTF-8
+uk_UA.UTF-8 UTF-8
+unm_US UTF-8
+ur_IN UTF-8
+ur_PK UTF-8
+uz_UZ.UTF-8 UTF-8
+uz_UZ@cyrillic UTF-8
+ve_ZA UTF-8
+vi_VN UTF-8
+wa_BE.UTF-8 UTF-8
+wae_CH UTF-8
+wal_ET UTF-8
+wo_SN UTF-8
+xh_ZA.UTF-8 UTF-8
+yi_US.UTF-8 UTF-8
+yo_NG UTF-8
+yue_HK UTF-8
+zh_CN.UTF-8 UTF-8
+zh_HK.UTF-8 UTF-8
+zh_SG.UTF-8 UTF-8
+zh_TW.UTF-8 UTF-8
+zu_ZA.UTF-8 UTF-8
diff --git a/airootfs/etc/lsb-release b/airootfs/etc/lsb-release
new file mode 100644 (file)
index 0000000..71dfdec
--- /dev/null
@@ -0,0 +1,4 @@
+LSB_VERSION=1.4
+DISTRIB_ID=RebornOS
+DISTRIB_RELEASE=rolling
+DISTRIB_DESCRIPTION="RebornOS Linux"
diff --git a/airootfs/etc/machine-id b/airootfs/etc/machine-id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/airootfs/etc/modprobe.d/blacklist.conf b/airootfs/etc/modprobe.d/blacklist.conf
new file mode 100644 (file)
index 0000000..bd38dbd
--- /dev/null
@@ -0,0 +1 @@
+blacklist CONFIG_CRYPTO_SPECK
diff --git a/airootfs/etc/os-release b/airootfs/etc/os-release
new file mode 100644 (file)
index 0000000..b81c909
--- /dev/null
@@ -0,0 +1,9 @@
+NAME="RebornOS"
+VERSION="Gnome RebornOS"
+ID=RebornOS
+ID_LIKE=arch
+PRETTY_NAME="RebornOS"
+ANSI_COLOR="1;34;40"
+HOME_URL="https://rebornos.org/"
+SUPPORT_URL="https://rebornos.freeforums.net/"
+BUG_REPORT_URL="https://gitlab.com/reborn-os-team"
diff --git a/airootfs/etc/pacman.conf b/airootfs/etc/pacman.conf
new file mode 100644 (file)
index 0000000..9ceeaf4
--- /dev/null
@@ -0,0 +1,98 @@
+#
+# /etc/pacman.conf
+#
+# See the pacman.conf(5) manpage for option and repository directives
+
+#
+# GENERAL OPTIONS
+#
+[options]
+# The following paths are commented out with their default values listed.
+# If you wish to use different paths, uncomment and update the paths.
+#RootDir     = /
+#DBPath      = /var/lib/pacman/
+#CacheDir    = /var/cache/pacman/pkg/
+#LogFile     = /var/log/pacman.log
+#GPGDir      = /etc/pacman.d/gnupg/
+HoldPkg     = pacman glibc
+#XferCommand = /usr/bin/curl -C - -f %u > %o
+#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
+#CleanMethod = KeepInstalled
+#UseDelta    = 0.7
+Architecture = auto
+
+# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
+#IgnorePkg   =
+#IgnoreGroup =
+
+#NoUpgrade   =
+#NoExtract   =
+
+# Misc options
+#UseSyslog
+#Color
+#TotalDownload
+# We cannot check disk space from within a chroot environment
+#CheckSpace
+#VerbosePkgLists
+
+# By default, pacman accepts packages signed by keys that its local keyring
+# trusts (see pacman-key and its man page), as well as unsigned packages.
+SigLevel    = Required DatabaseOptional
+LocalFileSigLevel = Optional
+#RemoteFileSigLevel = Required
+
+# NOTE: You must run `pacman-key --init` before first using pacman; the local
+# keyring can then be populated with the keys of all official Arch Linux
+# packagers with `pacman-key --populate archlinux`.
+
+#
+# REPOSITORIES
+#   - can be defined here or included from another file
+#   - pacman will search repositories in the order defined here
+#   - local/custom mirrors can be added here or in separate files
+#   - repositories listed first will take precedence when packages
+#     have identical names, regardless of version number
+#   - URLs will have $repo replaced by the name of the current repo
+#   - URLs will have $arch replaced by the name of the architecture
+#
+# Repository entries are of the format:
+#       [repo-name]
+#       Server = ServerName
+#       Include = IncludePath
+#
+# The header [repo-name] is crucial - it must be present and
+# uncommented to enable the repo.
+#
+
+# The testing repositories are disabled by default. To enable, uncomment the
+# repo name header and Include lines. You can add preferred servers immediately
+# after the header, and they will be used before the default mirrors.
+
+#[testing]
+#Include = /etc/pacman.d/mirrorlist
+
+[Reborn-OS]
+SigLevel = Optional TrustAll
+Include = /etc/pacman.d/reborn-mirrorlist
+
+[core]
+Include = /etc/pacman.d/mirrorlist
+
+[extra]
+Include = /etc/pacman.d/mirrorlist
+
+#[community-testing]
+#Include = /etc/pacman.d/mirrorlist
+
+[community]
+Include = /etc/pacman.d/mirrorlist
+
+[multilib]
+Include = /etc/pacman.d/mirrorlist
+
+# An example of a custom package repository.  See the pacman manpage for
+# tips on creating your own repositories.
+#[custom]
+#SigLevel = Optional TrustAll
+#Server = file:///home/custompkgs
diff --git a/airootfs/etc/pam.d/su b/airootfs/etc/pam.d/su
new file mode 100644 (file)
index 0000000..70768fc
--- /dev/null
@@ -0,0 +1,7 @@
+
+#%PAM-1.0
+auth           sufficient      pam_rootok.so
+auth           sufficient      pam_wheel.so trust use_uid
+auth           required        pam_unix.so
+account                required        pam_unix.so
+session required pam_unix.so
diff --git a/airootfs/etc/reborn-mirrorlist b/airootfs/etc/reborn-mirrorlist
new file mode 100644 (file)
index 0000000..82d5b04
--- /dev/null
@@ -0,0 +1,9 @@
+################################################################################
+#################### Reborn OS mirrorlist generated by cnchi ###################
+################################################################################
+
+# US Server
+Server = https://repo.rebornos.org/RebornOS/
+
+# European Server
+Server = http://repo-de.rebornos.org/Reborn-OS/
diff --git a/airootfs/etc/sddm.conf b/airootfs/etc/sddm.conf
new file mode 100644 (file)
index 0000000..d51a96f
--- /dev/null
@@ -0,0 +1,42 @@
+# Generated by SDDM Configuration Editor
+# Session=/usr/share/xsessions/gnome.desktop
+# Session=/usr/share/xsessions/gnome-classic.desktop
+# Session=/usr/share/xsessions/gnome-xorg.desktop
+# Session=/usr/share/xsessions/budgie-desktop.desktop
+[Autologin]
+Relogin=false
+Session=/usr/share/xsessions/gnome-xorg.desktop
+User=rebornos
+
+[Theme]
+Current=gracilis
+DisableAvatarsThreshold=7
+EnableAvatars=true
+FacesDir=/usr/share/sddm/faces
+ThemeDir=/usr/share/sddm/themes
+
+[Users]
+DefaultPath=/usr/local/bin:/usr/bin:/bin
+MaximumUid=60000
+MinimumUid=1000
+RememberLastSession=true
+RememberLastUser=true
+
+[Wayland]
+SessionCommand=/usr/share/sddm/scripts/wayland-session
+SessionDir=/usr/share/wayland-sessions
+SessionLogFile=.local/share/sddm/wayland-session.log
+
+[X11]
+DisplayCommand=/usr/share/sddm/scripts/Xsetup
+DisplayStopCommand=/usr/share/sddm/scripts/Xstop
+MinimumVT=1
+ServerArguments=-nolisten tcp
+ServerPath=/usr/bin/X
+SessionCommand=/usr/share/sddm/scripts/Xsession
+SessionDir=/usr/share/xsessions
+SessionLogFile=.local/share/sddm/xorg-session.log
+UserAuthFile=.Xauthority
+XauthPath=/usr/bin/xauth
+XephyrPath=/usr/bin/Xephyr
+
diff --git a/airootfs/etc/shells b/airootfs/etc/shells
new file mode 100644 (file)
index 0000000..f6022e0
--- /dev/null
@@ -0,0 +1,13 @@
+
+#
+# /etc/shells
+#
+
+/bin/sh
+/bin/bash
+/usr/bin/bash
+
+# End of file
+/bin/zsh
+/usr/bin/zsh
+/usr/bin/git-shell
diff --git a/airootfs/etc/skel/.bash_profile2 b/airootfs/etc/skel/.bash_profile2
new file mode 100644 (file)
index 0000000..debfc1f
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# ~/.bash_profile
+#
+
+[[ -f ~/.bashrc ]] && . ~/.bashrc
+
+if [ -z "$DISPLAY" ] && [ -n "$XDG_VTNR" ] && [ "$XDG_VTNR" -eq 1 ]; then
+  exec startx
+fi
diff --git a/airootfs/etc/skel/.bashrc b/airootfs/etc/skel/.bashrc
new file mode 100644 (file)
index 0000000..f609520
--- /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
+
+screenfetch -t -w -D 'ARCH'
+
+path() ( IFS=: ; printf '%s\n' $PATH ; )
diff --git a/airootfs/etc/skel/.config/autostart/refresh.desktop b/airootfs/etc/skel/.config/autostart/refresh.desktop
new file mode 100644 (file)
index 0000000..8f333fb
--- /dev/null
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Application
+Name=refresh.desktop
+Comment=Updates pacman repos at first boot.
+Exec=/usr/bin/internet.sh
+GenericName=Repository Updater
+Terminal=false
+NoDisplay=true
+X-DEEPIN-Autostart-enabled=true
+X-GNOME-Autostart-enabled=true
diff --git a/airootfs/etc/skel/.dmrc b/airootfs/etc/skel/.dmrc
new file mode 100644 (file)
index 0000000..1077fa8
--- /dev/null
@@ -0,0 +1,2 @@
+[Desktop]
+Session=deepin
diff --git a/airootfs/etc/skel/.inputrc b/airootfs/etc/skel/.inputrc
new file mode 100644 (file)
index 0000000..c84f078
--- /dev/null
@@ -0,0 +1,2 @@
+set show-all-if-ambiguous on
+
diff --git a/airootfs/etc/skel/.xinitrc2 b/airootfs/etc/skel/.xinitrc2
new file mode 100644 (file)
index 0000000..15d741c
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+#~/.xinitrc
+#
+#Executed by startx (window manager from here)
+
+
+if [-d /etc/X11/xinit/xinitrc.d]; then
+for f in /etc/X11/xinit/xintirc.d*; do
+[-x "$f"]&& ."$f"
+done
+unset f
+fi
+
+exec startdde
diff --git a/airootfs/etc/skel/.xsession2 b/airootfs/etc/skel/.xsession2
new file mode 100644 (file)
index 0000000..5d09e88
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+#
+#~/.xsession
+#
+# Executed at login
+#
+
+/bin/bash --login -l ~/.xinitrc
diff --git a/airootfs/etc/sudoers.d/g_wheel b/airootfs/etc/sudoers.d/g_wheel
new file mode 100644 (file)
index 0000000..7c499c2
--- /dev/null
@@ -0,0 +1 @@
+%wheel ALL=(ALL) NOPASSWD: ALL
diff --git a/airootfs/etc/systemd/scripts/choose-mirror b/airootfs/etc/systemd/scripts/choose-mirror
new file mode 100755 (executable)
index 0000000..0ae0806
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+get_cmdline() {
+    local param
+    for param in $(< /proc/cmdline); do
+        case "${param}" in
+            $1=*) echo "${param##*=}";
+            return 0
+            ;;
+        esac
+    done
+}
+
+mirror=$(get_cmdline mirror)
+[[ $mirror = auto ]] && mirror=$(get_cmdline archiso_http_srv)
+[[ $mirror ]] || exit 0
+
+mv /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.orig
+cat >/etc/pacman.d/mirrorlist << EOF
+#
+# Arch Linux repository mirrorlist
+# Generated by archiso
+#
+
+Server = ${mirror%%/}/\$repo/os/\$arch
+EOF
diff --git a/airootfs/etc/systemd/system/choose-mirror.service b/airootfs/etc/systemd/system/choose-mirror.service
new file mode 100644 (file)
index 0000000..1e4d771
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=Choose mirror from the kernel command line
+ConditionKernelCommandLine=mirror
+
+[Service]
+Type=oneshot
+ExecStart=/etc/systemd/scripts/choose-mirror
+
+[Install]
+WantedBy=multi-user.target
diff --git a/airootfs/etc/systemd/system/etc-pacman.d-gnupg.mount b/airootfs/etc/systemd/system/etc-pacman.d-gnupg.mount
new file mode 100644 (file)
index 0000000..4eab551
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Temporary /etc/pacman.d/gnupg directory
+
+[Mount]
+What=tmpfs
+Where=/etc/pacman.d/gnupg
+Type=tmpfs
+Options=mode=0755
diff --git a/airootfs/etc/systemd/system/getty@tty1.service.d/autologin.conf b/airootfs/etc/systemd/system/getty@tty1.service.d/autologin.conf
new file mode 100644 (file)
index 0000000..d1d8474
--- /dev/null
@@ -0,0 +1,3 @@
+[Service]
+ExecStart=
+ExecStart=-/sbin/agetty --autologin root --noclear %I 38400 linux
diff --git a/airootfs/etc/systemd/system/internet.service b/airootfs/etc/systemd/system/internet.service
new file mode 100644 (file)
index 0000000..f8b10da
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=script that requires network
+After=network.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/internet.sh
+
+[Install]
+WantedBy=multi-user.target
diff --git a/airootfs/etc/systemd/system/lightdm.service b/airootfs/etc/systemd/system/lightdm.service
new file mode 100644 (file)
index 0000000..019a813
--- /dev/null
@@ -0,0 +1,15 @@
+[Unit]
+Description=Light Display Manager
+Documentation=man:lightdm(1)
+After=dev-dri-card0.device systemd-user-sessions.service getty@tty1.service vboxservice.service livecd.service pacman-init.service
+
+[Service]
+ExecStart=/usr/bin/lightdm
+Restart=always
+IgnoreSIGPIPE=no
+BusName=org.freedesktop.DisplayManager
+StartLimitAction=none
+RestartSec=2
+
+[Install]
+Alias=display-manager.service
diff --git a/airootfs/etc/systemd/system/pacman-init.service b/airootfs/etc/systemd/system/pacman-init.service
new file mode 100644 (file)
index 0000000..34a4921
--- /dev/null
@@ -0,0 +1,15 @@
+[Unit]
+Description=Initializes Pacman keyring
+Wants=haveged.service
+After=haveged.service
+Requires=etc-pacman.d-gnupg.mount
+After=etc-pacman.d-gnupg.mount
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/bin/pacman-key --init
+ExecStart=/usr/bin/pacman-key --populate archlinux rebornos
+
+[Install]
+WantedBy=multi-user.target
diff --git a/airootfs/etc/udev/rules.d/81-dhcpcd.rules b/airootfs/etc/udev/rules.d/81-dhcpcd.rules
new file mode 100644 (file)
index 0000000..1c4053c
--- /dev/null
@@ -0,0 +1 @@
+ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="en*|eth*", ENV{SYSTEMD_WANTS}="dhcpcd@$name.service"
diff --git a/airootfs/etc/xdg/autostart/cnchi.desktop b/airootfs/etc/xdg/autostart/cnchi.desktop
new file mode 100755 (executable)
index 0000000..5040ee1
--- /dev/null
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Application
+Name=Reborn Installer
+Exec=/usr/bin/antergos-welcome
+Icon=/usr/share/applications/cnchi.png
+Terminal=True
diff --git a/airootfs/etc/xdg/autostart/internet.desktop b/airootfs/etc/xdg/autostart/internet.desktop
new file mode 100644 (file)
index 0000000..e2c9390
--- /dev/null
@@ -0,0 +1,4 @@
+[Desktop Entry]
+Type=Application
+Name=Refresh Pacman Databases
+Exec=bash /usr/bin/internet.sh
diff --git a/airootfs/root/.automated_script.sh b/airootfs/root/.automated_script.sh
new file mode 100755 (executable)
index 0000000..81a98a1
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+script_cmdline ()
+{
+    local param
+    for param in $(< /proc/cmdline); do
+        case "${param}" in
+            script=*) echo "${param#*=}" ; return 0 ;;
+        esac
+    done
+}
+
+automated_script ()
+{
+    local script rt
+    script="$(script_cmdline)"
+    if [[ -n "${script}" && ! -x /tmp/startup_script ]]; then
+        if [[ "${script}" =~ ^http:// || "${script}" =~ ^ftp:// ]]; then
+            wget "${script}" --retry-connrefused -q -O /tmp/startup_script >/dev/null
+            rt=$?
+        else
+            cp "${script}" /tmp/startup_script
+            rt=$?
+        fi
+        if [[ ${rt} -eq 0 ]]; then
+            chmod +x /tmp/startup_script
+            /tmp/startup_script
+        fi
+    fi
+}
+
+if [[ $(tty) == "/dev/tty1" ]]; then
+    automated_script
+fi
diff --git a/airootfs/root/.zlogin b/airootfs/root/.zlogin
new file mode 100644 (file)
index 0000000..f598e43
--- /dev/null
@@ -0,0 +1 @@
+~/.automated_script.sh
diff --git a/airootfs/root/customize_airootfs.sh b/airootfs/root/customize_airootfs.sh
new file mode 100755 (executable)
index 0000000..77ac5f8
--- /dev/null
@@ -0,0 +1,88 @@
+#!/bin/bash
+
+set -e -u
+
+sed -i 's/#\(en_US\.UTF-8\)/\1/' /etc/locale.gen
+locale-gen
+
+ln -sf /usr/share/zoneinfo/UTC /etc/localtime
+
+usermod -s /usr/bin/bash root
+cp -aT /etc/skel/ /root/
+chmod 700 /root
+
+sed -i 's/#\(PermitRootLogin \).\+/\1yes/' /etc/ssh/sshd_config
+sed -i "s/#Server/Server/g" /etc/pacman.d/mirrorlist
+sed -i 's/#\(Storage=\)auto/\1volatile/' /etc/systemd/journald.conf
+
+sed -i 's/#\(HandleSuspendKey=\)suspend/\1ignore/' /etc/systemd/logind.conf
+sed -i 's/#\(HandleHibernateKey=\)hibernate/\1ignore/' /etc/systemd/logind.conf
+sed -i 's/#\(HandleLidSwitch=\)suspend/\1ignore/' /etc/systemd/logind.conf
+
+systemctl set-default graphical.target
+systemctl -fq enable pacman-init.service
+
+# EXPERIMENTAL
+
+# Enable Services
+
+        if [ -f "/etc/systemd/system/livecd.service" ]; then
+            systemctl -fq enable livecd
+        fi
+        systemctl -fq enable systemd-networkd
+        if [ -f "/usr/lib/systemd/system/NetworkManager.service" ]; then
+            systemctl -fq enable NetworkManager NetworkManager-wait-online
+        fi
+        if [ -f "/etc/systemd/system/livecd-alsa-unmuter.service" ]; then
+            systemctl -fq enable livecd-alsa-unmuter
+        fi
+        if [ -f "/etc/systemd/system/vboxservice.service" ]; then
+            systemctl -fq enable vboxservice
+        fi
+        systemctl -fq enable ModemManager
+        systemctl -fq enable upower
+
+        systemctl -fq enable sddm
+        chmod +x /etc/lightdm/Xsession
+       
+        # Disable pamac if present
+        if [ -f "/usr/lib/systemd/system/pamac.service" ]; then
+            systemctl -fq disable pamac pamac-cleancache.timer pamac-mirrorlist.timer
+        fi
+        # Enable systemd-timesyncd (ntp)
+       systemctl -fq enable systemd-timesyncd
+       
+       #Enable Repository Configuration
+       systemctl -fq enable internet.service
+
+# dkms autoinstall
+
+# Enable lightdm by disabling root login
+        echo "Adding autologin group"
+        groupadd -r autologin
+        echo "Adding nopasswdlogin group"
+        groupadd -r nopasswdlogin
+        echo "Adding RebornOS user"
+        useradd -m -g users -G "audio,disk,optical,wheel,network,autologin,nopasswdlogin" rebornos
+        # Set Reborn account passwordless
+        passwd -d rebornos
+       chown -R rebornos:users /home/rebornos
+       echo "DONE FIXING ROOT LOGIN"
+
+#Various fixes
+        if [ -f /usr/bin/update-ca-trust ]; then
+            /usr/bin/update-ca-trust
+        fi
+        if [ -f /usr/bin/update-desktop-database ]; then
+           /usr/bin/update-desktop-database --quiet
+        fi
+        if [ -f /usr/bin/update-mime-database ]; then
+           /usr/bin/update-mime-database /usr/share/mime
+        fi
+        if [ -f /usr/bin/gdk-pixbuf-query-loaders ]; then
+            /usr/bin/gdk-pixbuf-query-loaders --update-cache
+        fi
+
+# Fix sudoers
+        chown -R root:root /etc/
+        chmod 660 /etc/sudoers
diff --git a/airootfs/root/install.txt b/airootfs/root/install.txt
new file mode 100644 (file)
index 0000000..3c8f171
--- /dev/null
@@ -0,0 +1,3 @@
+View this installation guide online at
+https://wiki.archlinux.org/index.php/Installation_Guide
+
diff --git a/airootfs/usr/bin/auto-partition.sh b/airootfs/usr/bin/auto-partition.sh
new file mode 100644 (file)
index 0000000..cf702db
--- /dev/null
@@ -0,0 +1,199 @@
+#!/bin/bash
+# REQUIREMENTS: Must have only 1 free, unformatted partition on /dev/sda, that also happens to be at least 30 GB in size.
+START()
+{
+if [[ $(parted /dev/sda unit B print free | grep 'Free Space' | awk '{print $4}' | wc -c) -le 14 ]]; then
+        echo "ERROR - no unformatted partitions to use"
+        echo "[ERROR] No unformatted partitions to use" >>/tmp/cnchi.log
+
+elif [[ $(parted /dev/sda unit B print free | grep 'Free Space' | awk '{print $4}' | wc -c) -ge 16 ]]; then
+        echo "ERROR - too many unformatted partition to use (must be only 1)"
+        echo "[ERROR] Too many unformatted partitions to use (must only be 1)" >>/tmp/cnchi.log
+
+else
+        echo "[SUCCESS]  An unformatted partition exists. Continuing to auto-partition it for you..." >>/tmp/cnchi.log
+        echo $(parted /dev/sda unit B print free | grep 'Free Space' | awk '{print $3}' | tr -d B) | awk  '{print $2}' >/partition.txt
+       echo $(sed '2q;d' /partition.txt)
+export SIZE=$(sed '2q;d' /partition.txt)
+TEST
+fi
+
+if [[ $(parted /dev/sda unit B print free | grep 'Free Space' | awk '{print $4}' | wc -c) -le 4 ]]; then
+        echo "Confirmed - no unformatted partitions to use"
+        echo "[ERROR] No unformatted partitions to use" >>/tmp/cnchi.log
+
+elif [[ $(parted /dev/sda unit B print free | grep 'Free Space' | awk '{print $4}' | wc -c) -ge 6 ]]; then
+        echo "Confirmed - too many unformatted partition to use (must be only 1)"
+        echo "[ERROR] Too many unformatted partitions to use (must only be 1)" >>/tmp/cnchi.log
+
+else
+        echo "SUCCESS - never mind! Found an unformatted partition to use."
+        echo "[SUCCESS]  An unformatted partition exists. Continuing to auto-partition it for you..." >>/tmp/cnchi.log
+        echo $(parted /dev/sda unit B print free | grep 'Free Space' | awk '{print $3}' | tr -d B) | awk  '{print $2}' >/partition.txt
+       echo $(sed '1q;d' /partition.txt)
+export SIZE=$(sed '1q;d' /partition.txt)
+TEST
+fi
+}
+
+TEST(){
+if [[ $(parted /dev/sda unit B print free | grep 'Free Space' | awk '{print $4}' | wc -c) -eq 15 ]]; then
+export SIZE=$(sed '2q;d' /partition.txt)
+fi 
+if [[ $(parted /dev/sda unit B print free | grep 'Free Space' | awk '{print $4}' | wc -c) -eq 5 ]]; then
+export SIZE=$(sed '1q;d' /partition.txt)
+fi
+if [ "$SIZE" -ge 30000000000 ]; then
+    echo "[SUCCESS] Partition is large enough to continue" >>/tmp/cnchi.log 
+    FIRMWARE
+else
+    echo "[ERROR] Partition is too small to continue" >>/tmp/cnchi.log
+fi
+}
+
+FIRMWARE(){
+if [ -f "/sys/firmware/efi" ]; then
+echo "[INFO] UEFI system detected" >>/tmp/cnchi.log
+SIZES_UEFI
+else
+echo "[INFO] BIOS system detected" >>/tmp/cnchi.log
+SIZES_BIOS
+fi
+}
+
+SIZES_UEFI(){
+export SIZE=$(sed '1q;d' /partition.txt)
+# Root = 36%
+echo $(awk -vn=$SIZE 'BEGIN{print(n*0.35)}') >>/tmp/rooting.txt
+export ROOT=$(sed '1q;d' /tmp/rooting.txt) 
+echo "[INFO] Root partition will be $ROOT bytes large" >>/tmp/cnchi.log
+# Home = 64%
+echo $(awk -vn=$SIZE 'BEGIN{print(n*0.63)}') >>/tmp/homing.txt
+export HOME=$(sed '1q;d' /tmp/homing.txt) 
+echo "[INFO] Home partition will be $HOME bytes large" >>/tmp/cnchi.log
+# Swap = 1%
+echo $(awk -vn=$SIZE 'BEGIN{print(n*0.01)}') >>/tmp/swapping.txt
+export SWAP=$(sed '1q;d' /tmp/swapping.txt) 
+echo "[INFO] Swap partition will be $SWAP bytes large" >>/tmp/cnchi.log
+#export SWAP=512M
+#echo "[INFO] SWAP file $SWAP large will be created" >>/tmp/cnchi.log
+# Boot partition
+echo $(df -h --output=source,fstype,size,used,avail,pcent,target -x tmpfs -x devtmpfs | grep "/dev/sd" | grep "/boot/efi") >/partitioning.txt
+export BOOT=echo $(sed '1q;d' /partitioning.txt) | awk '{print $1}'
+echo "[INFO] Boot partition to be used is $BOOT" >>/tmp/cnchi.log
+PARTITION_ROOT
+}
+
+SIZES_BIOS(){
+export SIZE=$(sed '1q;d' /partition.txt)
+# Root = 36%
+echo $(awk -vn=$SIZE 'BEGIN{print(n*0.35)}') >>/tmp/rooting.txt
+export ROOT=$(sed '1q;d' /tmp/rooting.txt) 
+echo "[INFO] Root partition will be $ROOT bytes large" >>/tmp/cnchi.log
+# Home = 63%
+echo $(awk -vn=$SIZE 'BEGIN{print(n*0.63)}') >>/tmp/homing.txt
+export HOME=$(sed '1q;d' /tmp/homing.txt) 
+echo "[INFO] Home partition will be $HOME bytes large" >>/tmp/cnchi.log
+# Swap = 1%
+echo $(awk -vn=$SIZE 'BEGIN{print(n*0.01)}') >>/tmp/swapping.txt
+export SWAP=$(sed '1q;d' /tmp/swapping.txt) 
+echo "[INFO] Swap partition will be $SWAP bytes large" >>/tmp/cnchi.log
+# Boot = 1%
+echo $(awk -vn=$SIZE 'BEGIN{print(n*0.01)}') >>/tmp/booting.txt
+export SWAP=$(sed '1q;d' /tmp/booting.txt) 
+echo "[INFO] Boot partition will be $BOOT bytes large" >>/tmp/cnchi.log
+#export SWAP=512M
+#echo "[INFO] SWAP file $SWAP large will be created" >>/tmp/cnchi.log
+PARTITION_ROOT
+}
+
+PARTITION_ROOT(){
+export ROOT=$(sed '1q;d' /tmp/rooting.txt) 
+echo "[INFO] Root partition is being created..." >>/tmp/cnchi.log
+export ROOT_START=echo $(parted /dev/sda unit B print free | grep 'Free Space' | tr -d B) | awk '{print $6}' 
+export ROOT_END=echo $(parted /dev/sda unit B print free | grep 'Free Space' | tr -d B) | awk '{print $7}'
+export ROOT_SIZE=$((ROOT_START+ROOT))
+unit B mkpartfs primary ext4 $ROOT_START $ROOT_SIZE
+# Determine what partition was just created
+echo $(fdisk -l --bytes | grep $ROOT_SIZE) | awk '{print $1}' >/root.txt
+export ROOT_PARTITION=$(sed '1q;d' /root.txt)
+e2label $ROOT_PARTITION "ROOT"
+echo "$ROOT_PARTITION / ext4 defaults,relatime,data=ordered 0 1" >>/etc/fstab
+echo "[SUCCESS] Root partition has been created as $ROOT_PARTITION" >>/tmp/cnchi.log
+PARTITION_HOME
+}
+
+PARTITION_HOME(){
+export HOME=$(sed '1q;d' /tmp/homing.txt) 
+echo "[INFO] Home partition is being created..." >>/tmp/cnchi.log
+export HOME_START=echo $(parted /dev/sda unit B print free | grep 'Free Space' | tr -d B) | awk '{print $6}' 
+export HOME_END=echo $(parted /dev/sda unit B print free | grep 'Free Space' | tr -d B) | awk '{print $7}'
+export HOME_SIZE=$((HOME_START+HOME))
+unit B mkpartfs primary ext4 $HOME_START $HOME_SIZE
+# Determine what partition was just created
+echo $(fdisk -l --bytes | grep $HOME_SIZE) | awk '{print $1}' >/home.txt
+export HOME_PARTITION=$(sed '1q;d' /home.txt)
+e2label $HOME_PARTITION "HOME"
+echo "$HOME_PARTITION /home ext4 defaults,relatime,data=ordered 0 0" >>/etc/fstab
+echo "[SUCCESS] Home partition has been created as $HOME_PARTITION" >>/tmp/cnchi.log
+PARTITION_SWAP
+}
+
+PARTITION_SWAP(){
+export SWAP=$(sed '1q;d' /tmp/swapping.txt) 
+echo "[INFO] Swap partition is being created..." >>/tmp/cnchi.log
+export SWAP_START=echo $(parted /dev/sda unit B print free | grep 'Free Space' | tr -d B) | awk '{print $6}' 
+export SWAP_END=echo $(parted /dev/sda unit B print free | grep 'Free Space' | tr -d B) | awk '{print $7}'
+export SWAP_SIZE=$((SWAP_START+SWAP))
+unit B mkpartfs primary swap $SWAP_START $SWAP_SIZE
+# Determine what partition was just created
+echo $(fdisk -l --bytes | grep $SWAP_SIZE) | awk '{print $1}' >/swap.txt
+export SWAP_PARTITION=$(sed '1q;d' /swap.txt)
+e2label $SWAP_PARTITION "swap"
+echo "$SWAP_PARTITION swap swap defaults 0 0" >>/etc/fstab
+echo "[SUCCESS] Swap partition has been created as $HOME_PARTITION" >>/tmp/cnchi.log
+swapon $SWAP_PARTITION
+PARTITION_UEFI_BIOS
+}
+
+PARTITION_UEFI_BIOS(){
+if [ -f "/sys/firmware/efi" ]; then
+echo $(fdisk -l --bytes | grep EFI) | awk '{print $1}' >/esp.txt
+export ESP_PARTITION=$(sed '1q;d' /esp.txt)
+echo "$ESP_PARTITION /boot/efi vfat defaults,relatime 0 0" >>/etc/fstab
+echo "[SUCCESS] Boot (EFI) partition has been created as $ESP_PARTITION" >>/tmp/cnchi.log
+else
+echo "[INFO] Boot partition is being created..." >>/tmp/cnchi.log
+export BOOT=$(sed '1q;d' /tmp/booting.txt) 
+export BOOT_START=echo $(parted /dev/sda unit B print free | grep 'Free Space' | tr -d B) | awk '{print $6}' 
+export BOOT_END=echo $(parted /dev/sda unit B print free | grep 'Free Space' | tr -d B) | awk '{print $7}'
+export BOOT_SIZE=$((HOME_START+HOME))
+unit B mkpartfs primary ext4 $BOOT_START $BOOT_SIZE
+# Determine what partition was just created
+echo $(fdisk -l --bytes | grep $BOOT_SIZE) | awk '{print $1}' >/boot.txt
+export HOME_PARTITION=$(sed '1q;d' /boot.txt)
+e2label $BOOT_PARTITION "BOOT"
+echo "$BOOT_PARTITION /boot ext4 defaults,relatime,data=ordered 0 0" >>/etc/fstab
+echo "[SUCCESS] Boot partition has been created as $BOOT_PARTITION" >>/tmp/cnchi.log
+fi
+CLEAN
+}
+
+CLEAN(){
+rm -f /partition.txt
+rm -f /partitioning.txt
+rm -f /root.txt
+rm -f /home.txt
+rm -f /swap.txt
+rm -f /tmp/rooting.txt
+rm -f /tmp/homing.txt
+rm -f /tmp/swapping.txt
+if [ -f "/sys/firmware/efi" ]; then
+rm -f /esp.txt
+else
+rm -f /boot.txt
+rm -f /tmp/booting.txt
+fi
+}
+export -f START TEST FIRMWARE SIZES_BIOS SIZES_UEFI PARTITION_ROOT PARTITION_HOME PARTITION_SWAP PARTITION_UEFI_BIOS CLEAN
+START
diff --git a/airootfs/usr/bin/cnchi b/airootfs/usr/bin/cnchi
new file mode 100755 (executable)
index 0000000..6005ac4
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+pkexec /usr/share/cnchi/bin/cnchi -s bugsnag ${@}
diff --git a/airootfs/usr/bin/cnchi-git b/airootfs/usr/bin/cnchi-git
new file mode 100644 (file)
index 0000000..f506c75
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+notify-send -t 10000 -a "Cnchi" -i /usr/share/cnchi/data/antergos-icon.png "Updating Cnchi with the latest GIT testing code. Please wait..."
+killall python
+sudo rm -rf /usr/share/cnchi.old
+sudo mv /usr/share/cnchi /usr/share/cnchi.old
+sudo rm -rf cnchi
+git clone https://github.com/Antergos/Cnchi cnchi
+cd cnchi
+git checkout 0.14.x
+./run -d -v
diff --git a/airootfs/usr/bin/cnchi-rank.sh b/airootfs/usr/bin/cnchi-rank.sh
new file mode 100755 (executable)
index 0000000..27e55b4
--- /dev/null
@@ -0,0 +1,68 @@
+#!/bin/bash
+if [[ $LANG = *"en"* ]]; then
+export NOTICE="DONE!"
+export NOTICE2="Cnchi is ranking your mirrors. Please wait..."
+
+elif [[ $LANG = *"es"* ]]; then
+export NOTICE="HECHO!"
+export NOTICE2="Cnchi clasifica tus espejos. Por favor espera..."
+
+elif [[ $LANG = *"fr"* ]]; then
+export NOTICE="DONE!"
+export NOTICE2="Cnchi classe vos miroirs. S'il vous plaît, attendez..."
+
+elif [[ $LANG = *"hi"* ]]; then
+export NOTICE="किया"
+export NOTICE2="Cnchi आपके दर्पण रैंकिंग है। कृपया प्रतीक्षा करें..."
+
+elif [[ $LANG = *"ar"* ]]; then
+export NOTICE="انتهت المهمة"
+export NOTICE2="Cnchi هو ترتيب المرايا الخاصة بك. أرجو الإنتظار..."
+
+elif [[ $LANG = *"pt"* ]]; then
+export NOTICE="COMPLETO!"
+export NOTICE2="Cnchi está classificando seus espelhos. Por favor, espere..."
+
+elif [[ $LANG = *"de"* ]]; then
+export NOTICE="Vollständig!"
+export NOTICE2="Cnchi zählt deine Spiegel. Warten Sie mal..."
+
+elif [[ $LANG = *"it"* ]]; then
+export NOTICE="COMPLETARE!"
+export NOTICE2="Cnchi classifica i tuoi specchi. Attendere prego..."
+
+elif [[ $LANG = *"zh"* ]]; then
+export NOTICE="在任务完成后"
+export NOTICE2="Cnchi正在为你的镜子排名。 请稍候..."
+
+else
+export NOTICE="DONE!"
+export NOTICE2="Cnchi is ranking your mirrors. Please wait..."
+fi
+WAIT(){
+yad --center --skip-taskbar --undecorated --no-buttons --form --on-top  --width=250 --no-escape --skip-taskbar --text-align=center \
+--text="<b><big><big>$NOTICE2</big></big></b>"
+}
+WAIT2(){
+if  (tail -F -n1 /tmp/cnchi.log &) | grep -q "Auto mirror selection has been run successfully"
+then
+sudo pkill yad
+yad --center-skip-taskbar --undecorated --no-buttons --form --no-escape --timeout="1" --width=250 --height=30  --on-top --skip-taskbar --text-align=center \
+--text="<b><big><big>$NOTICE</big></big></b>" \
+else
+echo "Mirrors are ranked!"
+fi
+}
+START(){
+if [[ -f /tmp/cnchi.log ]]
+then
+echo "Starting..."
+else
+sudo /usr/bin/cnchi-rank.sh
+fi
+(tail -F -n1 /tmp/cnchi.log &) | grep -q "Cnchi is ranking your mirrors lists..."
+WAIT &
+WAIT2
+}
+export -f WAIT WAIT2 START
+START
diff --git a/airootfs/usr/bin/cnchi-start.sh b/airootfs/usr/bin/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/airootfs/usr/bin/install-antergos.sh b/airootfs/usr/bin/install-antergos.sh
new file mode 100644 (file)
index 0000000..e548e01
--- /dev/null
@@ -0,0 +1,4 @@
+
+#!/bin/bash
+
+sudo sh /usr/bin/pacman-boot.sh && sudo sh /usr/bin/cnchi-start.sh
diff --git a/airootfs/usr/bin/internet.sh b/airootfs/usr/bin/internet.sh
new file mode 100755 (executable)
index 0000000..8e5774a
--- /dev/null
@@ -0,0 +1,58 @@
+#  Copyright © 2016-2019 Reborn OS
+#
+#  This file is part of Reborn OS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-timeout '0' && gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-timeout '0'
+systemd-detect-virt
+if [ "$?" != oracle ]; then
+echo "Running in Virtualbox"
+fi
+#sed '54 c\
+#> http://experiencing-reborn.weebly.com/welcome.html' /usr/share/cnchi/src/pages/slides.py
+sudo pacman -Scc --noconfirm
+sudo paccache -ruk0 
+sudo paccache -rk 0
+sudo paccache -r --keep 0
+sudo rm -rf /install/var/cache/pacman/pkg/*
+if (( $(fping google.com | grep -o "alive") == "alive" )); then
+       sudo rm -rf /etc/pacman.d/gnupg
+       sudo pacman -Syy
+       sudo pacman-key --init
+       sudo pacman-key --populate archlinux rebornos
+       sudo pacman-key --refresh-keys
+       sudo pacman -Syy
+       sudo reflector --verbose -p https --sort rate --save /etc/pacman.d/mirrorlist
+       sudo rm -f /var/lib/pacman/sync/*.db
+       sudo rm -f /var/lib/pacman/sync/*.files
+       sudo pacman -Syy
+       sudo pacman -Fy
+       sudo pacman -Syy
+       # if [ ! -z $(grep "eu" "etc/pacman.d/mirrorlist") ]; then 
+       # sudo cp /usr/bin/cnchi/pacman.conf /etc/
+       # sudo mv /usr/bin/cnchi/reborn-mirrorlist2 /etc/pacman.d/reborn-mirrorlist
+       # fi
+       echo "######################################################"
+       echo "#####################   DONE   #######################"
+       echo "######################################################"
+else 
+       bash /usr/bin/internet.sh
+fi
diff --git a/airootfs/usr/bin/pacman-boot.sh b/airootfs/usr/bin/pacman-boot.sh
new file mode 100644 (file)
index 0000000..14b24b6
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+NETWORK_ALIVE=`ping -c1 google.com 2>&1 | grep unknown`
+
+while [[ "$NETWORK_ALIVE" != "" ]];do
+NETWORK_ALIVE=`ping -c1 google.com 2>&1 | grep unknown`
+sleep 10
+done
+
+if [[ NETWORK_ALIVE == '' ]];then
+pacman -Syy
+fi
diff --git a/airootfs/usr/share/applications/antergos-install.desktop b/airootfs/usr/share/applications/antergos-install.desktop
new file mode 100644 (file)
index 0000000..2b5bd2e
--- /dev/null
@@ -0,0 +1,14 @@
+[Desktop Entry]
+Comment=Install the operating system to disk
+Comment[pl]=Zainstaluj system RebornOS
+Comment[sk]=Nainštaluje operačný systém na disk
+Exec=/usr/bin/cnchi-start.sh
+GenericName=RebornOS Installer
+Icon=/usr/share/pixmaps/cnchi.png
+Name=RebornOS Installer
+Name[en_US]=RebornOS Installer
+Name[pl]=Zainstaluj RebornOS
+Name[sk]=Inštalovať RebornOS
+Terminal=True
+Type=Application
+Categories=System;
diff --git a/airootfs/usr/share/applications/cnchi.desktop b/airootfs/usr/share/applications/cnchi.desktop
new file mode 100755 (executable)
index 0000000..a8ca012
--- /dev/null
@@ -0,0 +1,14 @@
+[Desktop Entry]
+Name=Install RebornOS
+Name[pl]=Zainstaluj RebornOS
+Name[sk]=Inštalovať RebornOS
+GenericName=Live Installer
+Comment=Install the operating system to disk
+Comment[pl]=Zainstaluj system RebornOS
+Comment[sk]=Nainštaluje operačný systém na disk
+Exec=/usr/bin/cnchi
+Icon=cnchi
+Terminal=False
+Type=Application
+Categories=System;
+Name[en_US.UTF-8]=Install RebornOS
diff --git a/airootfs/usr/share/applications/cnchi.png b/airootfs/usr/share/applications/cnchi.png
new file mode 100644 (file)
index 0000000..181725b
Binary files /dev/null and b/airootfs/usr/share/applications/cnchi.png differ
diff --git a/airootfs/usr/share/applications/deepin-root-filemanager.desktop b/airootfs/usr/share/applications/deepin-root-filemanager.desktop
new file mode 100644 (file)
index 0000000..1fd387d
--- /dev/null
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Version=1.0
+Type=Application
+Name=Deepin File Manager (Root)
+Comment=Access and organize files with root priviliges
+Icon=dde-file-manager
+Exec=dde-file-manager-pkexec
+NoDisplay=false
+Categories=Core;FileManager;GNOME;GTK;System;System-Tools;Utilities;
+Keywords=root;administrator;folder;manager;explore;disk;filesystem;deepin;
+MimeType=inode/directory;application/x-gnome-saved-search;
+StartupNotify=true
+Terminal=false
diff --git a/airootfs/usr/share/applications/flatpak.desktop b/airootfs/usr/share/applications/flatpak.desktop
new file mode 100644 (file)
index 0000000..d1aaf99
--- /dev/null
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Comment=Flatpak For RebornOS
+Exec=/usr/bin/flatpak.sh
+GenericName=Flatpak Installer
+Icon=/usr/share/icons/default/reborn-updates.svg
+Name=Flatpak Installer
+Name[en_US]=Flatpak Installer
+Terminal=False
+Type=Application
+Categories=System;
+NoDisplay=false
diff --git a/airootfs/usr/share/backgrounds/abstract1-reborn2.png b/airootfs/usr/share/backgrounds/abstract1-reborn2.png
new file mode 100644 (file)
index 0000000..525ecb3
Binary files /dev/null and b/airootfs/usr/share/backgrounds/abstract1-reborn2.png differ
diff --git a/airootfs/usr/share/backgrounds/japanese-lake.jpg b/airootfs/usr/share/backgrounds/japanese-lake.jpg
new file mode 100644 (file)
index 0000000..8cb3c16
Binary files /dev/null and b/airootfs/usr/share/backgrounds/japanese-lake.jpg differ
diff --git a/airootfs/usr/share/locale/af-ZA/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/af-ZA/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..fa33919
Binary files /dev/null and b/airootfs/usr/share/locale/af-ZA/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/af/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/af/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..e003368
Binary files /dev/null and b/airootfs/usr/share/locale/af/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ar-SA/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ar-SA/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..00cd1a3
Binary files /dev/null and b/airootfs/usr/share/locale/ar-SA/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ar/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ar/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..0ce7f2b
Binary files /dev/null and b/airootfs/usr/share/locale/ar/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ast/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ast/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..c56cffb
Binary files /dev/null and b/airootfs/usr/share/locale/ast/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/az-AZ/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/az-AZ/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..1571e1a
Binary files /dev/null and b/airootfs/usr/share/locale/az-AZ/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/az/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/az/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..72d6675
Binary files /dev/null and b/airootfs/usr/share/locale/az/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/az@latin/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/az@latin/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..223029d
Binary files /dev/null and b/airootfs/usr/share/locale/az@latin/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/az_IR/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/az_IR/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..acaa755
Binary files /dev/null and b/airootfs/usr/share/locale/az_IR/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/be/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/be/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..aad2f98
Binary files /dev/null and b/airootfs/usr/share/locale/be/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/bg/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/bg/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..c2afc2d
Binary files /dev/null and b/airootfs/usr/share/locale/bg/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/bn-BD/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/bn-BD/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..c71bdef
Binary files /dev/null and b/airootfs/usr/share/locale/bn-BD/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/bs-BA/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/bs-BA/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..9c87ea9
Binary files /dev/null and b/airootfs/usr/share/locale/bs-BA/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/bs/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/bs/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..af881cf
Binary files /dev/null and b/airootfs/usr/share/locale/bs/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ca/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ca/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..a3d4eae
Binary files /dev/null and b/airootfs/usr/share/locale/ca/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ca@valencia/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ca@valencia/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..263bc4a
Binary files /dev/null and b/airootfs/usr/share/locale/ca@valencia/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/cs-CZ/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/cs-CZ/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..30c62e6
Binary files /dev/null and b/airootfs/usr/share/locale/cs-CZ/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/cs/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/cs/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..db50a23
Binary files /dev/null and b/airootfs/usr/share/locale/cs/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/da/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/da/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..a0ad6e7
Binary files /dev/null and b/airootfs/usr/share/locale/da/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/de-AT/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/de-AT/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..2cf8c36
Binary files /dev/null and b/airootfs/usr/share/locale/de-AT/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/de/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/de/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..a0cda7d
Binary files /dev/null and b/airootfs/usr/share/locale/de/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/el/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/el/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..b69c909
Binary files /dev/null and b/airootfs/usr/share/locale/el/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/en/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/en/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..db63d8f
Binary files /dev/null and b/airootfs/usr/share/locale/en/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/en_GB/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/en_GB/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..f4b180d
Binary files /dev/null and b/airootfs/usr/share/locale/en_GB/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/eo/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/eo/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..3937311
Binary files /dev/null and b/airootfs/usr/share/locale/eo/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/es-VE/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/es-VE/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..6f6fce6
Binary files /dev/null and b/airootfs/usr/share/locale/es-VE/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/es/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/es/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..f0e3c3f
Binary files /dev/null and b/airootfs/usr/share/locale/es/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/es_419/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/es_419/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..3dc0015
Binary files /dev/null and b/airootfs/usr/share/locale/es_419/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/es_AR/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/es_AR/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..3c14d76
Binary files /dev/null and b/airootfs/usr/share/locale/es_AR/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/es_CL/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/es_CL/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..739302b
Binary files /dev/null and b/airootfs/usr/share/locale/es_CL/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/es_MX/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/es_MX/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..fa25df1
Binary files /dev/null and b/airootfs/usr/share/locale/es_MX/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/et/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/et/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..e242642
Binary files /dev/null and b/airootfs/usr/share/locale/et/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/eu/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/eu/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..518a653
Binary files /dev/null and b/airootfs/usr/share/locale/eu/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/fa/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/fa/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..b5cb2dd
Binary files /dev/null and b/airootfs/usr/share/locale/fa/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/fa_IR/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/fa_IR/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..d888c3d
Binary files /dev/null and b/airootfs/usr/share/locale/fa_IR/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/fi/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/fi/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..cd54eb6
Binary files /dev/null and b/airootfs/usr/share/locale/fi/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/fr-BE/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/fr-BE/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..d5b71d7
Binary files /dev/null and b/airootfs/usr/share/locale/fr-BE/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/fr-CA/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/fr-CA/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..5e0fc7b
Binary files /dev/null and b/airootfs/usr/share/locale/fr-CA/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/fr-FR/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/fr-FR/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..91370b6
Binary files /dev/null and b/airootfs/usr/share/locale/fr-FR/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/fr/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/fr/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..33a259f
Binary files /dev/null and b/airootfs/usr/share/locale/fr/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/gl/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/gl/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..021fc11
Binary files /dev/null and b/airootfs/usr/share/locale/gl/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/gu/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/gu/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..52d735b
Binary files /dev/null and b/airootfs/usr/share/locale/gu/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/he/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/he/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..6a89ce9
Binary files /dev/null and b/airootfs/usr/share/locale/he/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/hi/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/hi/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..d94ae41
Binary files /dev/null and b/airootfs/usr/share/locale/hi/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/hr/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/hr/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..57023ce
Binary files /dev/null and b/airootfs/usr/share/locale/hr/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/hu/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/hu/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..fe84e23
Binary files /dev/null and b/airootfs/usr/share/locale/hu/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/id/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/id/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..f391a11
Binary files /dev/null and b/airootfs/usr/share/locale/id/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/it-IT/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/it-IT/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..993f307
Binary files /dev/null and b/airootfs/usr/share/locale/it-IT/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/it/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/it/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..5842509
Binary files /dev/null and b/airootfs/usr/share/locale/it/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/it_CH/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/it_CH/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..ec37767
Binary files /dev/null and b/airootfs/usr/share/locale/it_CH/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ja/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ja/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..baac233
Binary files /dev/null and b/airootfs/usr/share/locale/ja/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ka/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ka/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..ad2767c
Binary files /dev/null and b/airootfs/usr/share/locale/ka/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ko/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ko/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..905962e
Binary files /dev/null and b/airootfs/usr/share/locale/ko/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ky/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ky/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..4b0334e
Binary files /dev/null and b/airootfs/usr/share/locale/ky/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/lt/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/lt/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..b8242c0
Binary files /dev/null and b/airootfs/usr/share/locale/lt/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/mk/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/mk/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..9c24ed5
Binary files /dev/null and b/airootfs/usr/share/locale/mk/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/mr/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/mr/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..a290a22
Binary files /dev/null and b/airootfs/usr/share/locale/mr/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ms/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ms/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..b246abb
Binary files /dev/null and b/airootfs/usr/share/locale/ms/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ms_MY/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ms_MY/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..92af846
Binary files /dev/null and b/airootfs/usr/share/locale/ms_MY/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/nb/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/nb/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..0be411c
Binary files /dev/null and b/airootfs/usr/share/locale/nb/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/nl/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/nl/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..6f7c326
Binary files /dev/null and b/airootfs/usr/share/locale/nl/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/pa/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/pa/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..bc5c9be
Binary files /dev/null and b/airootfs/usr/share/locale/pa/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/pl-PL/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/pl-PL/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..faec3cc
Binary files /dev/null and b/airootfs/usr/share/locale/pl-PL/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/pl/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/pl/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..b6ad575
Binary files /dev/null and b/airootfs/usr/share/locale/pl/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/pt/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/pt/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..c006446
Binary files /dev/null and b/airootfs/usr/share/locale/pt/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/pt_BR/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/pt_BR/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..05c70c7
Binary files /dev/null and b/airootfs/usr/share/locale/pt_BR/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/pt_PT/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/pt_PT/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..0fb8e2d
Binary files /dev/null and b/airootfs/usr/share/locale/pt_PT/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ro-RO/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ro-RO/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..84f2baf
Binary files /dev/null and b/airootfs/usr/share/locale/ro-RO/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ro/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ro/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..d5d7f29
Binary files /dev/null and b/airootfs/usr/share/locale/ro/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ru/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ru/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..eeb121f
Binary files /dev/null and b/airootfs/usr/share/locale/ru/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/sk/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/sk/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..03bdcb5
Binary files /dev/null and b/airootfs/usr/share/locale/sk/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/sl-SI/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/sl-SI/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..b8e0925
Binary files /dev/null and b/airootfs/usr/share/locale/sl-SI/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/sl/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/sl/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..f4455eb
Binary files /dev/null and b/airootfs/usr/share/locale/sl/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/sr-RS/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/sr-RS/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..9b0a266
Binary files /dev/null and b/airootfs/usr/share/locale/sr-RS/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/sr/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/sr/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..df03b06
Binary files /dev/null and b/airootfs/usr/share/locale/sr/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/sr@latin/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/sr@latin/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..b2714e5
Binary files /dev/null and b/airootfs/usr/share/locale/sr@latin/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/sv/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/sv/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..e0c3791
Binary files /dev/null and b/airootfs/usr/share/locale/sv/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/szl/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/szl/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..34d3f98
Binary files /dev/null and b/airootfs/usr/share/locale/szl/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ta/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ta/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..99b47e2
Binary files /dev/null and b/airootfs/usr/share/locale/ta/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/ta_IN/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/ta_IN/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..a72a518
Binary files /dev/null and b/airootfs/usr/share/locale/ta_IN/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/tg/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/tg/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..c6417e2
Binary files /dev/null and b/airootfs/usr/share/locale/tg/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/tl_PH/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/tl_PH/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..2397863
Binary files /dev/null and b/airootfs/usr/share/locale/tl_PH/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/tr-TR/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/tr-TR/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..3c8a9dc
Binary files /dev/null and b/airootfs/usr/share/locale/tr-TR/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/tr/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/tr/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..500729f
Binary files /dev/null and b/airootfs/usr/share/locale/tr/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/uk/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/uk/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..96f1e16
Binary files /dev/null and b/airootfs/usr/share/locale/uk/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/vi-VN/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/vi-VN/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..3d69659
Binary files /dev/null and b/airootfs/usr/share/locale/vi-VN/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/vi/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/vi/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..6670d0e
Binary files /dev/null and b/airootfs/usr/share/locale/vi/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/wa/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/wa/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..68297a9
Binary files /dev/null and b/airootfs/usr/share/locale/wa/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/xh/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/xh/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..45525cb
Binary files /dev/null and b/airootfs/usr/share/locale/xh/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/zh/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/zh/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..5b8d843
Binary files /dev/null and b/airootfs/usr/share/locale/zh/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/zh_CN.GB2312/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/zh_CN.GB2312/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..adf0c00
Binary files /dev/null and b/airootfs/usr/share/locale/zh_CN.GB2312/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/zh_CN/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/zh_CN/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..e341534
Binary files /dev/null and b/airootfs/usr/share/locale/zh_CN/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/zh_TW/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/zh_TW/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..b425117
Binary files /dev/null and b/airootfs/usr/share/locale/zh_TW/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/locale/zu/LC_MESSAGES/cnchi.mo b/airootfs/usr/share/locale/zu/LC_MESSAGES/cnchi.mo
new file mode 100644 (file)
index 0000000..8ce310f
Binary files /dev/null and b/airootfs/usr/share/locale/zu/LC_MESSAGES/cnchi.mo differ
diff --git a/airootfs/usr/share/pacman/keyrings/rebornos-revoked b/airootfs/usr/share/pacman/keyrings/rebornos-revoked
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/airootfs/usr/share/pacman/keyrings/rebornos-trusted b/airootfs/usr/share/pacman/keyrings/rebornos-trusted
new file mode 100644 (file)
index 0000000..b186967
--- /dev/null
@@ -0,0 +1,3 @@
+428836525A2B0D53B8F97ABDDBF573D69DE9F4B5:4:
+DB1CC7C341382E070279E7DAA68B7FA972447061:4:
+2F6D8657D79199892002E6465F819C731E28BDAE:4:
diff --git a/airootfs/usr/share/pacman/keyrings/rebornos.gpg b/airootfs/usr/share/pacman/keyrings/rebornos.gpg
new file mode 100644 (file)
index 0000000..dd5cfe7
--- /dev/null
@@ -0,0 +1,135 @@
+-----Keegan Milsten (Old)-----
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEWo6pUhYJKwYBBAHaRw8BAQdASkeBeGxEN+q1hrjEmQNA7I7UZzKhnN4UH+Mu
+GrqcQLK0LUtlZWdhbiBNaWxzdGVuIDxrZWVnYW5taWxzdGVuQHByb3Rvbm1haWwu
+Y29tPoiQBBMWCAA4FiEEQog2UlorDVO4+Xq92/Vz1p3p9LUFAlqRU5ECGwEFCwkI
+BwIGFQoJCAsCBBYCAwECHgECF4AACgkQ2/Vz1p3p9LXw+wD+LA6g8MIe6kfiK78M
+xFAk3winN1wWeuMNNIhyve1EALcA/j0lySosbVVPRRQrNLLRrJnZc2QGJH1P7S3k
+/d8jOhIGuDMEWpFXLBYJKwYBBAHaRw8BAQdA4zNVDrXWrol1kB+AS1VIVzh6BXz2
+DLiF/WQR23bEMyaIeAQYFggAIBYhBEKINlJaKw1TuPl6vdv1c9ad6fS1BQJakVcs
+AhsgAAoJENv1c9ad6fS1GbIA/Rtf/SFDg2mkbgi5B669ZERH57wde8PAadbk4tZr
+QWiFAPsEwQXCDpdarUafO5DFkrLS1hEQYbyzJkVBrKX98rGpA7gzBFqRWMYWCSsG
+AQQB2kcPAQEHQMcqaQM01ArigfbKLuDuowh0h+Ze/DWnejCR2MZnid9hiO8EGBYI
+ACAWIQRCiDZSWisNU7j5er3b9XPWnen0tQUCWpFYxgIbAgCBCRDb9XPWnen0tXYg
+BBkWCAAdFiEE8LL1diJMLGwkjAQu0H70vUknPakFAlqRWMYACgkQ0H70vUknPakx
+WAEA2I4qzSBrPRDNcg8DmhVL8nSO5RMwv14muIP/eZQuk8sBANSLPxK1ZNJBheSh
+Mt5Rf/7awTWpkY5vEDxRqgBsskEARKUA/1XDCGuJd8Qo0BBTsjFCG/QfczUY6S+Z
+Aq9CJlzEYXNwAQCKWzDJmyyefNkE6EzRtw8JK3P6rMvRgOk9u1Ljen5VCrg4BFqR
+WPMSCisGAQQBl1UBBQEBB0CtYJqR8ujykn5wwqzXrJ5pBxgQKy8PNxEw5B7UTY5W
+WQMBCAeIeAQYFggAIBYhBEKINlJaKw1TuPl6vdv1c9ad6fS1BQJakVjzAhsMAAoJ
+ENv1c9ad6fS1rlEA/j2KbJS+oYGu03P0HGrlIC3SwAUSJ/MlADAmEbjPqY6pAQDA
+jFRiCRdJOXihTTXLdq90sqNdliCsCRpoFzY6/u71Cg==
+=TQLH
+-----END PGP PUBLIC KEY BLOCK-----
+
+-----Keegan Milsten (new)-----
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+=b5OE
+-----END PGP PUBLIC KEY BLOCK-----
+
+-----Rafael Costa Rega-----
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+wSRUZHgACt6eWRsQJK/rfDmHdRM=
+=Axuz
+-----END PGP PUBLIC KEY BLOCK-----
\ No newline at end of file
diff --git a/airootfs/usr/share/pixmaps/cnchi.png b/airootfs/usr/share/pixmaps/cnchi.png
new file mode 100644 (file)
index 0000000..d7990c4
Binary files /dev/null and b/airootfs/usr/share/pixmaps/cnchi.png differ
diff --git a/build.sh b/build.sh
new file mode 100755 (executable)
index 0000000..8939cd8
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,613 @@
+#!/usr/bin/bash
+
+set -e -u
+
+
+if [ -f config ]; then
+    source ./config
+else
+    # No config file!
+    exit 1
+fi
+
+iso_name=RebornOS
+iso_label="RebornOS_$(date +%Y%m)"
+iso_version=$(date +%Y.%m.%d)
+install_dir=arch
+work_dir=work
+out_dir=out
+gpg_key=
+
+arch=$(uname -m)
+verbose=""
+script_path=$(readlink -f ${0%/*})
+
+if [ -d ${script_path}/work ]; then
+     echo "Removing build directory for you. Please be patient..."
+     rm -rf ${script_path}/work
+    echo "######################################################"
+    echo "######################## DONE ########################"
+    echo "######################################################"
+fi
+
+
+_usage ()
+{
+    echo "usage ${0} [options]"
+    echo
+    echo " General options:"
+    echo "    -N <iso_name>      Set an iso filename (prefix)"
+    echo "                        Default: ${iso_name}"
+    echo "    -V <iso_version>   Set an iso version (in filename)"
+    echo "                        Default: ${iso_version}"
+    echo "    -L <iso_label>     Set an iso label (disk label)"
+    echo "                        Default: ${iso_label}"
+    echo "    -D <install_dir>   Set an install_dir (directory inside iso)"
+    echo "                        Default: ${install_dir}"
+    echo "    -w <work_dir>      Set the working directory"
+    echo "                        Default: ${work_dir}"
+    echo "    -o <out_dir>       Set the output directory"
+    echo "                        Default: ${out_dir}"
+    echo "    -v                 Enable verbose output"
+    echo "    -h                 This help message"
+    exit ${1}
+}
+
+# Helper function to run make_*() only one time per architecture.
+run_once() {
+    if [[ ! -e ${work_dir}/build.${1}_${arch} ]]; then
+        $1
+        touch ${work_dir}/build.${1}_${arch}
+    fi
+}
+
+# Setup custom pacman.conf with current cache directories.
+make_pacman_conf() {
+    local _cache_dirs
+    _cache_dirs=($(pacman -v 2>&1 | grep '^Cache Dirs:' | sed 's/Cache Dirs:\s*//g'))
+sed -r "s|^#?\\s*CacheDir.+|CacheDir = $(echo -n ${_cache_dirs[@]})|g" ${script_path}/pacman.conf > ${work_dir}/pacman.conf
+}
+
+# Base installation, plus needed packages (airootfs)
+make_basefs() {
+    setarch ${arch} mkarchiso ${verbose} -w "${work_dir}/${arch}" -C "${work_dir}/pacman.conf" -D "${install_dir}" init
+    setarch ${arch} mkarchiso ${verbose} -w "${work_dir}/${arch}" -C "${work_dir}/pacman.conf" -D "${install_dir}" -p "haveged intel-ucode memtest86+ mkinitcpio-nfs-utils nbd" install
+}
+
+# Additional packages (airootfs)
+make_packages() {
+     setarch ${arch} mkarchiso ${verbose} -w "${work_dir}/${arch}" -C "${work_dir}/pacman.conf" -D "${install_dir}" -p "$(grep -h -v ^# ${script_path}/packages.{both,${arch}})" install
+}
+
+# Needed packages for x86_64 EFI boot
+make_packages_efi() {
+    setarch ${arch} mkarchiso ${verbose} -w "${work_dir}/${arch}" -C "${work_dir}/pacman.conf" -D "${install_dir}" -p "efitools efibootmgr" install
+}
+# Copy mkinitcpio archiso hooks and build initramfs (airootfs)
+make_setup_mkinitcpio() {
+    local _hook
+    mkdir -p ${work_dir}/${arch}/airootfs/etc/initcpio/hooks
+    mkdir -p ${work_dir}/${arch}/airootfs/etc/initcpio/install
+    for _hook in archiso archiso_shutdown archiso_pxe_common archiso_pxe_nbd archiso_pxe_http archiso_pxe_nfs archiso_loop_mnt; do
+        cp /usr/lib/initcpio/hooks/${_hook} ${work_dir}/${arch}/airootfs/etc/initcpio/hooks
+        cp /usr/lib/initcpio/install/${_hook} ${work_dir}/${arch}/airootfs/etc/initcpio/install
+    done
+    sed -i "s|/usr/lib/initcpio/|/etc/initcpio/|g" ${work_dir}/${arch}/airootfs/etc/initcpio/install/archiso_shutdown
+    cp /usr/lib/initcpio/install/archiso_kms ${work_dir}/${arch}/airootfs/etc/initcpio/install
+    cp /usr/lib/initcpio/archiso_shutdown ${work_dir}/${arch}/airootfs/etc/initcpio
+    cp ${script_path}/mkinitcpio.conf ${work_dir}/${arch}/airootfs/etc/mkinitcpio-archiso.conf
+    gnupg_fd=
+    if [[ ${gpg_key} ]]; then
+      gpg --export ${gpg_key} >${work_dir}/gpgkey
+      exec 17<>${work_dir}/gpgkey
+    fi
+    ARCHISO_GNUPG_FD=${gpg_key:+17} setarch ${arch} mkarchiso ${verbose} -w "${work_dir}/${arch}" -C "${work_dir}/pacman.conf" -D "${install_dir}" -r 'mkinitcpio -c /etc/mkinitcpio-archiso.conf -k /boot/vmlinuz-linux -g /boot/archiso.img' run
+    if [[ ${gpg_key} ]]; then
+      exec 17<&-
+    fi
+}
+# Customize installation (airootfs)
+make_customize_airootfs() {
+   cp -af ${script_path}/airootfs ${work_dir}/${arch}
+    curl -o ${work_dir}/${arch}/airootfs/etc/pacman.d/mirrorlist 'https://www.archlinux.org/mirrorlist/?country=all&protocol=http&use_mirror_status=on'
+    lynx -dump -nolist 'https://wiki.archlinux.org/index.php/Installation_Guide?action=render' >> ${work_dir}/${arch}/airootfs/root/install.txt
+    setarch ${arch} mkarchiso ${verbose} -w "${work_dir}/${arch}" -C "${work_dir}/pacman.conf" -D "${install_dir}" -r '/root/customize_airootfs.sh' run
+    rm ${work_dir}/${arch}/airootfs/root/customize_airootfs.sh
+    rm -f ${work_dir}/${arch}airootfs/etc/xdg/autostart/vboxclient.desktop
+        if [ -f "${work_dir}/${arch}/airootfs/etc/xdg/autostart/pamac-tray.desktop" ]; then
+            rm ${work_dir}/${arch}/airootfs/etc/xdg/autostart/pamac-tray.desktop
+        fi
+        ln -sf /usr/share/zoneinfo/UTC ${work_dir}/${arch}/airootfs/etc/localtime
+        chmod 750 ${work_dir}/${arch}/airootfs/etc/sudoers.d
+        chmod 440 ${work_dir}/${arch}/airootfs/etc/sudoers.d/g_wheel
+cp -L ${script_path}/set_password ${work_dir}/${arch}/airootfs/usr/bin
+}
+####################################################################################
+# Install cnchi installer from Git
+make_cnchi() {
+# Install iso_hotfix_utility files to root-image
+#    echo
+#    echo ">>> Installing iso-hotfix-utility..."
+#    echo
+#    wget "${ISO_HOTFIX_UTILITY_URL}" -O ${script_path}/iso-hotfix-utility.tar.gz
+#    tar xfz ${script_path}/iso-hotfix-utility.tar.gz -C ${script_path}
+#    rm -f ${script_path}/iso-hotfix-utility.tar.gz
+#    mv "${script_path}/iso-hotfix-utility-${ISO_HOTFIX_UTILITY_VERSION}" ${script_path}/iso-hotfix-utility
+#    cp "${script_path}/iso-hotfix-utility/iso-hotfix-utility" "${work_dir}/${arch}/airootfs/usr/bin/pacman-boot"
+#    chmod 755 "${work_dir}/${arch}/airootfs/usr/bin/pacman-boot"
+#    mkdir -p "${work_dir}/${arch}/airootfs/etc/iso-hotfix-utility.d"
+#    for _file in ${script_path}/iso-hotfix-utility/dist/**
+#    do
+#        install -m755 -t "${work_dir}/${arch}/airootfs/etc/iso-hotfix-utility.d" "${_file}"
+#    done
+#    for fpath in ${script_path}/iso-hotfix-utility/po/*; do
+#        if [[ -f "${fpath}" ]] && [[ "${fpath}" != 'po/CNCHI_UPDATER.po' ]]; then
+#            STRING_PO=`echo ${fpath#*/}`
+#            STRING=`echo ${STRING_PO%.po}`
+#            mkdir -p "${work_dir}/${arch}/airootfs/usr/share/locale/${STRING}/LC_MESSAGES"
+#            msgfmt "${fpath}" -o "${work_dir}/${arch}/airootfs/usr/share/locale/${STRING}/LC_MESSAGES/CNCHI_UPDATER.mo"
+#            echo "${STRING} installed..."
+#            echo "ISO-HOTFIX-UTILITY IS NOW BUILT"
+#        fi
+#    done
+#    rm -rf ${script_path}/iso-hotfix-utility
+#
+    echo
+#   echo ">>> Warning! Installing Cnchi Installer from GIT (${CNCHI_GIT_BRANCH} branch)"
+#   wget "${CNCHI_GIT_URL}" -O ${script_path}/cnchi-git.zip
+    # The two previous line (commented) performed the download from Antergos Github.
+    # Now it's downloaded from our server:
+    echo "Downloading cnchi from our server:"
+    wget "${CNCHI_REBORNOS_URL}" -O ${script_path}/cnchi-git.zip
+    unzip ${script_path}/cnchi-git.zip -d ${script_path}
+    rm -f ${script_path}/cnchi-git.zip
+    CNCHI_SRC="${script_path}/Cnchi-${CNCHI_GIT_BRANCH}"
+        install -d ${work_dir}/${arch}/airootfs/usr/share/{cnchi,locale}
+       install -Dm755 "${CNCHI_SRC}/bin/cnchi" "${work_dir}/${arch}/airootfs/usr/bin/cnchi"
+         echo
+         echo "COPIED STARTUP FILE OVER"
+         echo
+       install -Dm755 "${CNCHI_SRC}/cnchi.desktop" "${work_dir}/${arch}/airootfs/usr/share/applications/cnchi.desktop"
+         echo
+         echo "COPIED DESKTOP FILE OVER"
+         echo
+       install -Dm644 "${CNCHI_SRC}/data/images/antergos/antergos-icon.png" "${work_dir}/${arch}/airootfs/usr/share/pixmaps/cnchi.png"
+         echo
+         echo "COPIED CNCHI ICON OVER"
+         echo
+    # TODO: This should be included in Cnchi's src code as a separate file
+    # (as both files are needed to run cnchi)
+    sed -r -i 's|\/usr.+ -v|pkexec /usr/share/cnchi/bin/cnchi -s bugsnag|g' "${work_dir}/${arch}/airootfs/usr/bin/cnchi"
+    echo
+    echo "MODIFIED STARTUP COMMAND FOR CNCHI"
+    echo
+    for i in ${CNCHI_SRC}/src ${CNCHI_SRC}/bin ${CNCHI_SRC}/data ${CNCHI_SRC}/scripts ${CNCHI_SRC}/ui; do
+        cp -R ${i} "${work_dir}/${arch}/airootfs/usr/share/cnchi/"
+        echo
+        echo "COPIED CNCHI'S MAIN SUBDIRECTORIES OVER TO BUILD FOLDER"
+        echo
+    done
+    echo
+    echo "REMOVED UNNECESARY .PO FILE"
+    rm -f ${CNCHI_SRC}/po/update_po.sh
+    echo
+    for files in ${CNCHI_SRC}/po/*; do
+        if [[ -f "$files" ]] && [[ "$files" != 'po/cnchi.pot' ]]; then
+            STRING_PO=`echo ${files#*/}`
+            STRING=`echo ${STRING_PO%.po}`
+            mkdir -p ${work_dir}/${arch}/airootfs/usr/share/locale/${STRING}/LC_MESSAGES
+            msgfmt $files -o ${work_dir}/${arch}/airootfs/usr/share/locale/${STRING}/LC_MESSAGES/cnchi.mo
+            echo "${STRING} installed..."
+           echo "CNCHI IS NOW BUILT"
+        fi
+    done
+rm -rf ${script_path}/Cnchi-${CNCHI_GIT_BRANCH}
+            echo
+            echo "##################################################################"
+            echo "WRAPPED UP LOOSE ENDS - WILL BE MOVING ON NOW TO CUSTOMIZATIONS..."
+            echo "##################################################################"
+            echo
+            echo
+}
+########################################################################################
+
+make_fixes() {
+       # Remove Antergos gsettings file
+#      rm ${work_dir}/${arch}/airootfs/usr/share/glib-2.0/schemas/90_deepin-default-gsettings.gschema.override
+       echo "ANTERGOS GSETTINGS REMOVED"
+        # Setup gsettings if gsettings folder exists
+        if [ -d ${script_path}/gsettings ]; then
+            # Copying GSettings XML schema files
+            mkdir -p ${work_dir}/${arch}/airootfs/usr/share/glib-2.0/schemas
+            for _schema in ${script_path}/gsettings/*.gschema.override; do
+                echo ">>> Will use ${_schema}"
+                cp ${_schema} ${work_dir}/${arch}/airootfs/usr/share/glib-2.0/schemas
+            done
+            # Compile GSettings XML schema files
+            # rm ${work_dir}/${arch}/airootfs/usr/share/glib-2.0/schemas/98_gnome.gschema.override
+            # rm ${work_dir}/${arch}/airootfs/usr/share/glib-2.0/schemas/95_budgie.gschema.override
+            echo "REMOVED APRICITY GSETTINGS"
+            ${work_dir}/${arch}/airootfs/usr/bin/glib-compile-schemas ${work_dir}/${arch}/airootfs/usr/share/glib-2.0/schemas
+          echo
+          echo "GENERATED REBORNOS GSETTINGS"
+          echo
+        fi
+#Use lightdm.conf from local direcectory instead of default one
+echo "Removing unnecessary lightdm.conf"
+rm ${work_dir}/${arch}/airootfs/etc/lightdm/lightdm.conf
+echo "Copying correct lightdm.conf file over"
+cp ${script_path}/airootfs/etc/lightdm/lightdm.conf ${work_dir}/${arch}/airootfs/etc/lightdm/
+echo "DONE"
+echo
+#Use sddm.conf from local direcectory instead of default one
+echo "Removing unnecessary sddm.conf"
+rm ${work_dir}/${arch}/airootfs/etc/sddm.conf
+echo "Copying correct sddm.conf file over"
+cp ${script_path}/airootfs/etc/sddm.conf ${work_dir}/${arch}/airootfs/etc/
+echo "DONE"
+echo
+#Copy RebornOS Mirrorlist
+echo "Setting up RebornOS Mirrorlist"
+mkdir -p ${work_dir}/${arch}/airootfs/etc/pacman.d
+cp ${script_path}/airootfs/etc/reborn-mirrorlist ${work_dir}/${arch}/airootfs/etc/pacman.d/
+echo "DONE"
+echo
+#Copy pacman-init.service over
+#echo "Copying pacman-init.service"
+#cp ${script_path}/pacman-init.service ${work_dir}/${arch}/airootfs/etc/systemd/system/
+echo "DONE"
+echo
+#Replace pacman.conf with RebornOS's
+echo "Replacing pacman.conf with RebornOS's"
+rm ${work_dir}/${arch}/airootfs/etc/pacman.conf
+cp ${script_path}/Cnchi/pacman.conf ${work_dir}/${arch}/airootfs/etc/
+cp ${script_path}/Cnchi/pacman.conf ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/Cnchi/pacman2.conf ${work_dir}/${arch}/airootfs/
+mv ${work_dir}/${arch}/airootfs/pacman2.conf ${work_dir}/${arch}/airootfs/tmp/pacman.conf
+echo "DONE"
+echo
+#Editting Cnchi
+echo "Moving Cnchi files over..."
+rm ${work_dir}/${arch}/airootfs/usr/share/applications/cnchi.desktop
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/features.py
+cp ${script_path}/Cnchi/features.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/desktop_info.py
+cp ${script_path}/Cnchi/desktop_info.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+#rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/encfs.py
+#cp ${script_path}/Cnchi/encfs.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/boot/grub2.py
+cp ${script_path}/Cnchi/grub2.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/boot/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/scripts/10_antergos
+cp ${script_path}/Cnchi/10_antergos ${work_dir}/${arch}/airootfs/usr/share/cnchi/scripts/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/boot/systemd_boot.py
+cp ${script_path}/Cnchi/systemd_boot.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/boot/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/scripts/postinstall.sh
+cp ${script_path}/Cnchi/postinstall.sh ${work_dir}/${arch}/airootfs/usr/share/cnchi/scripts/
+#rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/main_window.py
+#cp ${script_path}/Cnchi/main_window.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/show_message.py
+cp ${script_path}/Cnchi/show_message.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/slides.py
+cp ${script_path}/Cnchi/slides.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/
+#rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/ask.py
+#cp ${script_path}/Cnchi/ask.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/check.py
+cp ${script_path}/Cnchi/check.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/
+#rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/timezone.py
+#cp ${script_path}/Cnchi/timezone.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/welcome.py
+cp ${script_path}/Cnchi/welcome.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/
+#rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/desktop.py
+#cp ${script_path}/Cnchi/desktop.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/
+#rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/download/metalink.py
+#cp ${script_path}/Cnchi/metalink.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/download/
+#rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/pac.py
+#cp ${script_path}/Cnchi/pac.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/antergos/antergos-logo-mini2.png
+cp ${script_path}/Cnchi/antergos-logo-mini2.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/antergos/
+cp ${script_path}/Cnchi/20-intel.conf ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/Cnchi/lightdm-webkit2-greeter.conf ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/slides/1.png
+cp ${script_path}/Cnchi/1.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/slides/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/slides/2.png
+cp ${script_path}/Cnchi/2.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/slides/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/slides/3.png
+cp ${script_path}/Cnchi/3.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/slides/
+# cp ${script_path}/Cnchi/4.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/slides/
+cp ${script_path}/Cnchi/sddm.conf ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+rm ${work_dir}/${arch}/airootfs/usr/share/pixmaps/cnchi.png
+cp ${script_path}/airootfs/usr/share/applications/cnchi.png ${work_dir}/${arch}/airootfs/usr/share/pixmaps/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/antergos/antergos-icon.png
+cp ${script_path}/Cnchi/antergos-icon.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/antergos/antergos-icon.png
+cp ${script_path}/scripts/tint2-start.desktop ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/conky-start.desktop ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/obmenu-gen.desktop ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/plymouth-reborn.desktop ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/mate-panel.desktop ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/plymouth.sh ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/flatpak.sh ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/pkcon.sh ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/pkcon2.sh ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/flatpak.desktop ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+#cp ${script_path}/Cnchi/pacman2.conf ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/update.desktop ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/openbox-config.sh ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/deepin-fix.sh ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/scripts/deepin-fix.service ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/images/pantheon.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/deepin.png
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/kde.png
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/lxqt.png
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/openbox.png
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/xfce.png
+cp ${script_path}/images/apricity.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+cp ${script_path}/images/deepin.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+cp ${script_path}/images/cinnamon.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+cp ${script_path}/images/windows.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+cp ${script_path}/images/kde.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+cp ${script_path}/images/i3.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+cp ${script_path}/images/lxqt.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+cp ${script_path}/images/openbox.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+cp ${script_path}/images/enlightenment.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+cp ${script_path}/images/xfce.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/images/desktops/
+cp ${script_path}/images/desktop-environment-apricity.svg ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/icons/scalable/
+cp ${script_path}/images/desktop-environment-pantheon.svg ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/icons/scalable/
+cp ${script_path}/images/desktop-environment-windows.svg ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/icons/scalable/
+cp ${script_path}/images/desktop-environment-budgie.svg ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/icons/scalable/
+cp ${script_path}/images/desktop-environment-i3.svg ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/icons/scalable/
+cp ${script_path}/Cnchi/reborn-mirrorlist ${work_dir}/${arch}/airootfs/etc/pacman.d/
+#
+# Replacement made by Rafael from RebornOS
+#
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/pacman.conf
+cp ${script_path}/Cnchi/pacman.conf ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+rm ${work_dir}/${arch}/airootfs/etc/pacman.conf
+cp ${script_path}/Cnchi/pacman.conf ${work_dir}/${arch}/airootfs/etc/
+chmod 644 /etc/pacman.conf
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/pacman.tmpl
+cp ${script_path}/Cnchi/pacman.tmpl ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/info.py
+cp ${script_path}/Cnchi/info.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/update_db.py
+cp ${script_path}/Cnchi/update_db.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/cnchi.py
+cp ${script_path}/Cnchi/cnchi.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/rank_mirrors.py
+cp ${script_path}/Cnchi/rank_mirrors.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/features_info.py
+cp ${script_path}/Cnchi/features_info.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/packages.xml
+cp ${script_path}/Cnchi/packages.xml ${work_dir}/${arch}/airootfs/usr/share/cnchi/data/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/mirrors.py
+cp ${script_path}/Cnchi/mirrors.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/post_install.py
+cp ${script_path}/Cnchi/post_install.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/install.py
+cp ${script_path}/Cnchi/install.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/select_packages.py
+cp ${script_path}/Cnchi/select_packages.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/download/metalink.py
+cp ${script_path}/Cnchi/metalink.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/download/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/logging_utils.py
+cp ${script_path}/Cnchi/logging_utils.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/slides.py
+cp ${script_path}/Cnchi/slides.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pacman/pac.py
+cp ${script_path}/Cnchi/pac.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pacman/
+# Use neofetch to show in terminal RebornOS logo and info
+rm ${work_dir}/${arch}/airootfs/home/rebornos/.bashrc
+cp ${script_path}/Cnchi/bashrc ${work_dir}/${arch}/airootfs/home/rebornos/.bashrc
+# Correct GeoIP timeout
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/geoip.py
+cp ${script_path}/Cnchi/geoip.py ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/
+# cnchi-start.sh is modified in its original location
+# rm ${work_dir}/${arch}/airootfs/usr/bin/cnchi-start.sh
+# cp ${script_path}/Cnchi/cnchi-start.sh ${work_dir}/${arch}/airootfs/usr/bin/
+# Replace access to the RebornOS installer with another one with "sudo"
+rm ${work_dir}/${arch}/airootfs/usr/share/applications/antergos-install.desktop
+cp ${script_path}/Cnchi/antergos-install.desktop ${work_dir}/${arch}/airootfs/usr/share/applications/
+# Add new cnchi icon for RebornOS Installer
+rm ${work_dir}/${arch}/airootfs/usr/share/pixmaps/cnchi.png
+cp ${script_path}/Cnchi/cnchi.png ${work_dir}/${arch}/airootfs/usr/share/pixmaps/
+cp ${script_path}/Cnchi/reborn-icon-new.png ${work_dir}/${arch}/airootfs/usr/share/pixmaps/
+cp ${script_path}/Cnchi/cnchi.png ${work_dir}/${arch}/airootfs/usr/share/applications/
+# Add new wallpaper for RebornOS Live
+rm ${work_dir}/${arch}/airootfs/usr/share/backgrounds/gnome/adwaita-day.jpg
+cp ${script_path}/Cnchi/adwaita-day.jpg ${work_dir}/${arch}/airootfs/usr/share/backgrounds/gnome/
+rm ${work_dir}/${arch}/airootfs/usr/share/backgrounds/gnome/adwaita-morning.jpg
+cp ${script_path}/Cnchi/adwaita-day.jpg ${work_dir}/${arch}/airootfs/usr/share/backgrounds/gnome/adwaita-morning.jpg
+rm ${work_dir}/${arch}/airootfs/usr/share/backgrounds/gnome/adwaita-night.jpg
+cp ${script_path}/Cnchi/adwaita-day.jpg ${work_dir}/${arch}/airootfs/usr/share/backgrounds/gnome/adwaita-night.jpg
+# Delete refresh.desktop in ~/.config/autostart and add new refresh-keys.desktop
+# to refresh keys and run cnchi-start.sh when Live start
+rm ${work_dir}/${arch}/airootfs/home/rebornos/.config/autostart/refresh.desktop
+cp ${script_path}/Cnchi/refresh-keys.desktop ${work_dir}/${arch}/airootfs/home/rebornos/.config/autostart/
+# Add refresh-keys.sh to /usr/bin (called by install.desktop)
+# In the end, call cnchi-start.sh
+cp ${script_path}/Cnchi/refresh-keys.sh ${work_dir}/${arch}/airootfs/usr/bin/
+rm ${work_dir}/${arch}/airootfs/usr/share/cnchi/scripts/10_antergos
+cp ${script_path}/Cnchi/10_antergos ${work_dir}/${arch}/airootfs/usr/share/cnchi/scripts/
+# Uncomplicated Firewall Workaround
+cp ${script_path}/Cnchi/gufw.png ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/Cnchi/gufw.svg ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+cp ${script_path}/Cnchi/gufw.desktop ${work_dir}/${arch}/airootfs/usr/share/cnchi/
+echo "DONE"
+echo
+echo "Replacing Antergos mentions with RebornOS"
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/advanced.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/alongside.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/ask.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/automatic.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/check.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/gtkbasebox.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/keymap.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/language.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/location.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/installation/process.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/slides.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/summary.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/timezone.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/user_info.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/wireless.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/zfs.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/desktop.py
+sed -i "s/gnome/deepin/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/pages/desktop.py
+#sed -i "s/Antergos/Reborn/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/encfs.py
+sed -i "s/Antergos/RebornOS/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/main_window.py
+echo "DONE"
+echo
+echo "Removing Mycroft-RebornOS's startup file from the ISO..."
+rm -f ${work_dir}/${arch}/airootfs/etc/xdg/autostart/mycroft1.desktop
+echo "DONE"
+}
+# Prepare kernel/initramfs ${install_dir}/boot/
+make_boot() {
+    mkdir -p ${work_dir}/iso/${install_dir}/boot/${arch}
+    echo "Executed 'mkdir -p' Command Successfully"
+    cp ${work_dir}/${arch}/airootfs/boot/archiso.img ${work_dir}/iso/${install_dir}/boot/${arch}/archiso.img
+    echo "Built 'archiso.img' Successfully"
+    cp ${work_dir}/${arch}/airootfs/boot/vmlinuz-linux ${work_dir}/iso/${install_dir}/boot/${arch}/vmlinuz
+    echo "Built 'vmlinuz-linux' Successfully"
+    echo "DONE"
+    echo
+}
+# Add other aditional/extra files to ${install_dir}/boot/
+make_boot_extra() {
+    cp ${work_dir}/${arch}/airootfs/boot/memtest86+/memtest.bin ${work_dir}/iso/${install_dir}/boot/memtest
+    cp ${work_dir}/${arch}/airootfs/usr/share/licenses/common/GPL2/license.txt ${work_dir}/iso/${install_dir}/boot/memtest.COPYING
+    cp ${work_dir}/${arch}/airootfs/boot/intel-ucode.img ${work_dir}/iso/${install_dir}/boot/intel_ucode.img
+    cp ${work_dir}/${arch}/airootfs/usr/share/licenses/intel-ucode/LICENSE ${work_dir}/iso/${install_dir}/boot/intel_ucode.LICENSE
+}
+# Prepare /${install_dir}/boot/syslinux
+make_syslinux() {
+    mkdir -p ${work_dir}/iso/${install_dir}/boot/syslinux
+    for _cfg in ${script_path}/syslinux/*.cfg; do
+        sed "s|%ARCHISO_LABEL%|${iso_label}|g;
+             s|%INSTALL_DIR%|${install_dir}|g" ${_cfg} > ${work_dir}/iso/${install_dir}/boot/syslinux/${_cfg##*/}
+    done
+    cp ${script_path}/syslinux/splash.png ${work_dir}/iso/${install_dir}/boot/syslinux
+    cp ${work_dir}/${arch}/airootfs/usr/lib/syslinux/bios/*.c32 ${work_dir}/iso/${install_dir}/boot/syslinux
+    cp ${work_dir}/${arch}/airootfs/usr/lib/syslinux/bios/lpxelinux.0 ${work_dir}/iso/${install_dir}/boot/syslinux
+    cp ${work_dir}/${arch}/airootfs/usr/lib/syslinux/bios/memdisk ${work_dir}/iso/${install_dir}/boot/syslinux
+    mkdir -p ${work_dir}/iso/${install_dir}/boot/syslinux/hdt
+    gzip -c -9 ${work_dir}/${arch}/airootfs/usr/share/hwdata/pci.ids > ${work_dir}/iso/${install_dir}/boot/syslinux/hdt/pciids.gz
+#    gzip -c -9 ${work_dir}/${arch}/airootfs/usr/lib/modules/*-ARCH/modules.alias > ${work_dir}/iso/${install_dir}/boot/syslinux/hdt/modalias.gz
+}
+# Prepare /isolinux
+make_isolinux() {
+    mkdir -p ${work_dir}/iso/isolinux
+    sed "s|%INSTALL_DIR%|${install_dir}|g" ${script_path}/isolinux/isolinux.cfg > ${work_dir}/iso/isolinux/isolinux.cfg
+    cp ${work_dir}/${arch}/airootfs/usr/lib/syslinux/bios/isolinux.bin ${work_dir}/iso/isolinux/
+    cp ${work_dir}/${arch}/airootfs/usr/lib/syslinux/bios/isohdpfx.bin ${work_dir}/iso/isolinux/
+    cp ${work_dir}/${arch}/airootfs/usr/lib/syslinux/bios/ldlinux.c32 ${work_dir}/iso/isolinux/
+}
+# Prepare /EFI
+make_efi() {
+    mkdir -p ${work_dir}/iso/EFI/boot
+    cp ${work_dir}/x86_64/airootfs/usr/share/efitools/efi/PreLoader.efi ${work_dir}/iso/EFI/boot/bootx64.efi
+    cp ${work_dir}/x86_64/airootfs/usr/share/efitools/efi/HashTool.efi ${work_dir}/iso/EFI/boot/
+    cp ${work_dir}/x86_64/airootfs/usr/lib/systemd/boot/efi/systemd-bootx64.efi ${work_dir}/iso/EFI/boot/loader.efi
+    mkdir -p ${work_dir}/iso/loader/entries
+    cp ${script_path}/efiboot/loader/loader.conf ${work_dir}/iso/loader/
+    cp ${script_path}/efiboot/loader/entries/uefi-shell-v2-x86_64.conf ${work_dir}/iso/loader/entries/
+    cp ${script_path}/efiboot/loader/entries/uefi-shell-v1-x86_64.conf ${work_dir}/iso/loader/entries/
+    sed "s|%ARCHISO_LABEL%|${iso_label}|g;
+         s|%INSTALL_DIR%|${install_dir}|g" \
+        ${script_path}/efiboot/loader/entries/archiso-x86_64-usb.conf > ${work_dir}/iso/loader/entries/archiso-x86_64.conf
+    # EFI Shell 2.0 for UEFI 2.3+
+    curl -o ${work_dir}/iso/EFI/shellx64_v2.efi https://raw.githubusercontent.com/tianocore/edk2/master/ShellBinPkg/UefiShell/X64/Shell.efi
+    # EFI Shell 1.0 for non UEFI 2.3+
+    curl -o ${work_dir}/iso/EFI/shellx64_v1.efi https://raw.githubusercontent.com/tianocore/edk2/master/EdkShellBinPkg/FullShell/X64/Shell_Full.efi
+}
+# Prepare efiboot.img::/EFI for "El Torito" EFI boot mode
+make_efiboot() {
+    mkdir -p ${work_dir}/iso/EFI/archiso
+    truncate -s 40M ${work_dir}/iso/EFI/archiso/efiboot.img
+    mkfs.vfat -n ARCHISO_EFI ${work_dir}/iso/EFI/archiso/efiboot.img
+    mkdir -p ${work_dir}/efiboot
+    mount ${work_dir}/iso/EFI/archiso/efiboot.img ${work_dir}/efiboot
+    mkdir -p ${work_dir}/efiboot/EFI/archiso
+    cp ${work_dir}/iso/${install_dir}/boot/x86_64/vmlinuz ${work_dir}/efiboot/EFI/archiso/vmlinuz.efi
+    cp ${work_dir}/iso/${install_dir}/boot/x86_64/archiso.img ${work_dir}/efiboot/EFI/archiso/archiso.img
+    cp ${work_dir}/iso/${install_dir}/boot/intel_ucode.img ${work_dir}/efiboot/EFI/archiso/intel_ucode.img
+    mkdir -p ${work_dir}/efiboot/EFI/boot
+    cp ${work_dir}/x86_64/airootfs/usr/share/efitools/efi/PreLoader.efi ${work_dir}/efiboot/EFI/boot/bootx64.efi
+    cp ${work_dir}/x86_64/airootfs/usr/share/efitools/efi/HashTool.efi ${work_dir}/efiboot/EFI/boot/
+    cp ${work_dir}/x86_64/airootfs/usr/lib/systemd/boot/efi/systemd-bootx64.efi ${work_dir}/efiboot/EFI/boot/loader.efi
+    mkdir -p ${work_dir}/efiboot/loader/entries
+    cp ${script_path}/efiboot/loader/loader.conf ${work_dir}/efiboot/loader/
+    cp ${script_path}/efiboot/loader/entries/uefi-shell-v2-x86_64.conf ${work_dir}/efiboot/loader/entries/
+    cp ${script_path}/efiboot/loader/entries/uefi-shell-v1-x86_64.conf ${work_dir}/efiboot/loader/entries/
+    sed "s|%ARCHISO_LABEL%|${iso_label}|g;
+         s|%INSTALL_DIR%|${install_dir}|g" \
+        ${script_path}/efiboot/loader/entries/archiso-x86_64-cd.conf > ${work_dir}/efiboot/loader/entries/archiso-x86_64.conf
+    cp ${work_dir}/iso/EFI/shellx64_v2.efi ${work_dir}/efiboot/EFI/
+    cp ${work_dir}/iso/EFI/shellx64_v1.efi ${work_dir}/efiboot/EFI/
+    umount -d ${work_dir}/efiboot
+}
+
+# Build airootfs filesystem image
+make_prepare() {
+    cp -a -l -f ${work_dir}/${arch}/airootfs ${work_dir}
+    setarch ${arch} mkarchiso ${verbose} -w "${work_dir}" -D "${install_dir}" pkglist
+    setarch ${arch} mkarchiso ${verbose} -w "${work_dir}" -D "${install_dir}" ${gpg_key:+-g ${gpg_key}} prepare
+    rm -rf ${work_dir}/airootfs
+    # rm -rf ${work_dir}/${arch}/airootfs (if low space, this helps)
+}
+# Build ISO
+make_iso() {
+    mkarchiso ${verbose} -w "${work_dir}" -D "${install_dir}" -L "${iso_label}" -o "${out_dir}" iso "${iso_name}-${iso_version}-x86_64.iso"
+}
+if [[ ${EUID} -ne 0 ]]; then
+    echo "This script must be run as root."
+    _usage 1
+fi
+if [[ ${arch} != x86_64 ]]; then
+    echo "This script needs to be run on x86_64"
+    _usage 1
+fi
+while getopts 'N:V:L:D:w:o:g:vh' arg; do
+    case "${arg}" in
+        N) iso_name="${OPTARG}" ;;
+        V) iso_version="${OPTARG}" ;;
+        L) iso_label="${OPTARG}" ;;
+        D) install_dir="${OPTARG}" ;;
+        w) work_dir="${OPTARG}" ;;
+        o) out_dir="${OPTARG}" ;;
+        g) gpg_key="${OPTARG}" ;;
+        v) verbose="-v" ;;
+        h) _usage 0 ;;
+        *)
+           echo "Invalid argument '${arg}'"
+           _usage 1
+           ;;
+    esac
+done
+mkdir -p ${work_dir}
+run_once make_pacman_conf
+
+for arch in x86_64; do
+    run_once make_basefs
+    run_once make_packages
+done
+run_once make_packages_efi
+for arch in x86_64; do
+    run_once make_setup_mkinitcpio
+    run_once make_customize_airootfs
+done
+for arch in x86_64; do
+    run_once make_boot
+done
+# Do all stuff for iso
+run_once make_boot_extra
+run_once make_syslinux
+run_once make_isolinux
+run_once make_efi
+run_once make_efiboot
+run_once make_cnchi
+run_once make_fixes
+
+for arch in x86_64; do
+run_once make_prepare
+done
+run_once make_iso
diff --git a/clean.sh b/clean.sh
new file mode 100755 (executable)
index 0000000..ba04b2f
--- /dev/null
+++ b/clean.sh
@@ -0,0 +1,64 @@
+#  Copyright © 2016-2019 RebornOS
+#
+#  This file is part of RebornOS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+#!/bin/bash
+set -e
+#
+##########################################################
+# Author       :       Palanthis (palanthis@gmail.com)
+# Website      :       http://github.com/Palanthis
+# License      :       Distributed under the terms of GNU GPL v3
+# Warning      :       These scripts come with NO WARRANTY!!!!!!
+##########################################################
+
+echo
+echo "Removing work directory!"
+rm -rf work
+echo "Removed!"
+
+echo
+echo "Removing out directory!"
+rm -rf out
+echo "Removed!"
+
+echo
+echo "Cleaning pacman caches before new build!"
+pacman -Scc --noconfirm --quiet
+echo "Cleaned!"
+
+echo
+echo "Ensuring package cache is truly clean!"
+rm -rf /var/cache/pacman/pkg/*
+echo "All gone!"
+
+echo
+echo "Resyncing databases before new build!"
+pacman -Syy --quiet
+echo "Sync'd!"
+
+echo
+echo "All done! You are now ready to build!"
+echo
+
+
+
diff --git a/config b/config
new file mode 100644 (file)
index 0000000..a4f8f19
--- /dev/null
+++ b/config
@@ -0,0 +1,12 @@
+# Install Cnchi from git
+CNCHI_GIT="y"
+CNCHI_GIT_BRANCH="0.16.21"
+# Not used:
+CNCHI_GIT_URL="https://github.com/Antergos/Cnchi/archive/${CNCHI_GIT_BRANCH}.zip"
+# Used:
+CNCHI_REBORNOS_URL="https://repo.rebornos.org/RebornOS/sources/cnchi/0.16.21.zip"
+
+
+# iso-hotfix-utility
+ISO_HOTFIX_UTILITY_VERSION="1.0.17"
+ISO_HOTFIX_UTILITY_URL="https://github.com/Antergos/iso-hotfix-utility/archive/${ISO_HOTFIX_UTILITY_VERSION}.tar.gz"
diff --git a/efiboot/loader/entries/archiso-x86_64-cd.conf b/efiboot/loader/entries/archiso-x86_64-cd.conf
new file mode 100644 (file)
index 0000000..706cf9d
--- /dev/null
@@ -0,0 +1,5 @@
+title   RebornOS x86_64 UEFI CD
+linux   /EFI/archiso/vmlinuz.efi
+initrd  /EFI/archiso/intel_ucode.img
+initrd  /EFI/archiso/archiso.img
+options archisobasedir=%INSTALL_DIR% cow_spacesize=10G archisolabel=%ARCHISO_LABEL%
diff --git a/efiboot/loader/entries/archiso-x86_64-usb.conf b/efiboot/loader/entries/archiso-x86_64-usb.conf
new file mode 100644 (file)
index 0000000..afcd139
--- /dev/null
@@ -0,0 +1,5 @@
+title   RebornOS x86_64 UEFI USB
+linux   /%INSTALL_DIR%/boot/x86_64/vmlinuz
+initrd  /%INSTALL_DIR%/boot/intel_ucode.img
+initrd  /%INSTALL_DIR%/boot/x86_64/archiso.img
+options archisobasedir=%INSTALL_DIR% cow_spacesize=10G archisolabel=%ARCHISO_LABEL% 
diff --git a/efiboot/loader/entries/nvidia-fallback.conf b/efiboot/loader/entries/nvidia-fallback.conf
new file mode 100644 (file)
index 0000000..206240b
--- /dev/null
@@ -0,0 +1,5 @@
+title   NVIDIA RebornOS (NVIDIA LIVE SYSTEM)
+linux   /%INSTALL_DIR%/boot/vmlinuz
+initrd  /%INSTALL_DIR%/boot/intel_ucode.img
+initrd  /%INSTALL_DIR%/boot/archiso.img
+options archisobasedir=%INSTALL_DIR% cow_spacesize=10G archisolabel=%ARCHISO_LABEL% earlymodules=loop modules-load=loop rd.modules-load=loop nohibernate quiet nomodeset nouveau.modeset=0 modprobe.blacklist=nouveau modprobe.driver-nvidia udev.log-priority=crit rd.udev.log-priority=crit
diff --git a/efiboot/loader/entries/uefi-shell-v1-x86_64.conf b/efiboot/loader/entries/uefi-shell-v1-x86_64.conf
new file mode 100644 (file)
index 0000000..206240b
--- /dev/null
@@ -0,0 +1,5 @@
+title   NVIDIA RebornOS (NVIDIA LIVE SYSTEM)
+linux   /%INSTALL_DIR%/boot/vmlinuz
+initrd  /%INSTALL_DIR%/boot/intel_ucode.img
+initrd  /%INSTALL_DIR%/boot/archiso.img
+options archisobasedir=%INSTALL_DIR% cow_spacesize=10G archisolabel=%ARCHISO_LABEL% earlymodules=loop modules-load=loop rd.modules-load=loop nohibernate quiet nomodeset nouveau.modeset=0 modprobe.blacklist=nouveau modprobe.driver-nvidia udev.log-priority=crit rd.udev.log-priority=crit
diff --git a/efiboot/loader/entries/uefi-shell-v2-x86_64.conf b/efiboot/loader/entries/uefi-shell-v2-x86_64.conf
new file mode 100644 (file)
index 0000000..950c424
--- /dev/null
@@ -0,0 +1,2 @@
+title  UEFI Shell (GRUB command line)
+efi    /EFI/shellx64_v1.efi
diff --git a/efiboot/loader/loader.conf b/efiboot/loader/loader.conf
new file mode 100644 (file)
index 0000000..1c6922b
--- /dev/null
@@ -0,0 +1,2 @@
+timeout 5
+default archiso-x86_64.conf
diff --git a/git-v2.sh b/git-v2.sh
new file mode 100755 (executable)
index 0000000..f29f837
--- /dev/null
+++ b/git-v2.sh
@@ -0,0 +1,50 @@
+#  Copyright © 2016-2019 RebornOS
+#
+#  This file is part of RebornOS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+#!/bin/bash
+
+# checking if you have the latest files from github
+echo "Checking for newer files online first"
+git pull
+
+# Below command will backup everything inside the project folder
+git add --all .
+
+# Give a comment to the commit if you want
+echo "####################################"
+echo "Write your commit comment!"
+echo "####################################"
+
+read input
+
+# Committing to the local repository with a message containing the time details and commit text
+
+git commit -m "$input"
+
+# Push the local files to github
+
+git push -u origin master
+
+echo "################################################################"
+echo "###################    Git Push Done      ######################"
+echo "################################################################"
diff --git a/gsettings/101_reborn_budgie.gschema.override b/gsettings/101_reborn_budgie.gschema.override
new file mode 100755 (executable)
index 0000000..4cc7c8d
--- /dev/null
@@ -0,0 +1,59 @@
+[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.screensaver]
+lock-enabled=false
+
+[org.gnome.desktop.session]
+idle-delay=0
+
+[org.gnome.desktop.datetime]
+automatic-timezone=true
+
+[org.gnome.desktop.input-sources]
+sources=[('xkb','us')]
+
+[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.software]
+download-updates = false
+allow-updates = false
+show-ratings = true
+
+[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']
+favorite-apps=['org.gnome.Nautilus.desktop', 'org.gnome.gedit.desktop', 'firefox.desktop', 'org.gnome.Software.desktop', 'gparted.desktop', 'antergos-install.desktop', 'org.gnome.Terminal.desktop', 'pamac.desktop']
+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
+
+[org.gnome.settings-daemon.plugins.power]
+sleep-inactive-ac-type='nothing'
+sleep-inactive-battery-type='nothing'
\ No newline at end of file
diff --git a/gsettings/95_reborn_deepin.gschema.override b/gsettings/95_reborn_deepin.gschema.override
new file mode 100644 (file)
index 0000000..df3a189
--- /dev/null
@@ -0,0 +1,41 @@
+[org.gnome.software]
+download-updates=false
+show-nonfree-software=true
+allow-updates=false
+show-ratings=true
+
+[com.deepin.dde.dock]
+docked-apps=['/S@cnchi', '/S@deepin-toggle-desktop', '/S@deepin-wm-multitaskingview', '/S@dde-file-manager', '/S@deepin-music', '/S@chromium', '/S@org.gnome.Software', '/S@dde-control-center']
+
+[com.deepin.dde.appearance]
+background-uris=['file:///usr/share/wallpapers/deepin/Sunset_of_the_Lake_Nam_by_Wang_Jinyu.jpg', 'file:///usr/share/wallpapers/deepin/Reflection_of_the_Kanas%20Lake_by_Wang_Jinyu.jpg', 'file:///usr/share/wallpapers/deepin/Autumn_in_Kanas_by_Wang_Jinyu.jpg', 'file:///usr/share/wallpapers/deepin/Maple_Leaf_by_Aaron_Burden.jpg']
+
+[com.deepin.wrap.gnome.desktop.background]
+primary-color = '#000000'
+secondary-color = '#023c88'
+picture-uri = 'file:///usr/share/wallpapers/deepin/Sunset_of_the_Lake_Nam_by_Wang_Jinyu.jpg'
+
+[com.deepin.dde.keyboard]
+layout='us;'
+user-layout-list=['us;']
+
+[com.deepin.dde.mouse]
+middle-button-enabled=true
+
+[com.deepin.wrap.gnome.desktop.sound]
+theme-name = 'deepin'
+event-sounds = true
+
+[com.deepin.wrap.gnome.desktop.wm.preferences]
+audible-bell = false
+mouse-button-modifier = ''
+
+[com.deepin.wrap.gnome.desktop.interface]
+cursor-theme = 'deepin'
+gtk-key-theme = 'Default'
+gtk-im-module = 'fcitx'
+
+[com.deepin.dde.zone]
+left-up = 'dbus-send --print-reply --dest=com.deepin.dde.Launcher /com/deepin/dde/Launcher com.deepin.dde.Launcher.Toggle'
+right-up = 'dbus-send --session --dest=com.deepin.wm --print-reply /com/deepin/wm com.deepin.wm.PerformAction int32:1'
+left-down = 'dbus-send --session --dest=com.deepin.wm --print-reply /com/deepin/wm com.deepin.wm.PerformAction int32:6'
diff --git a/images/apricity.png b/images/apricity.png
new file mode 100644 (file)
index 0000000..3585c06
Binary files /dev/null and b/images/apricity.png differ
diff --git a/images/cinnamon.png b/images/cinnamon.png
new file mode 100644 (file)
index 0000000..d209e2e
Binary files /dev/null and b/images/cinnamon.png differ
diff --git a/images/deepin.png b/images/deepin.png
new file mode 100644 (file)
index 0000000..71f016e
Binary files /dev/null and b/images/deepin.png differ
diff --git a/images/desktop-environment-apricity.svg b/images/desktop-environment-apricity.svg
new file mode 100644 (file)
index 0000000..0e8643c
--- /dev/null
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   viewBox="0 0 48 48"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="apricity.svg"
+   inkscape:export-filename="/home/alex/Apricity Website/assets/img/minilogo.png"
+   inkscape:export-xdpi="240"
+   inkscape:export-ydpi="240">
+  <metadata
+     id="metadata93">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="2560"
+     inkscape:window-height="1333"
+     id="namedview91"
+     showgrid="false"
+     inkscape:zoom="4.9166667"
+     inkscape:cx="-76.271187"
+     inkscape:cy="23.186441"
+     inkscape:window-x="0"
+     inkscape:window-y="55"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3764"
+       x1="1"
+       x2="47"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,-1,1,0,-1.5e-6,47.999998)">
+      <stop
+         stop-color="#e4e4e4"
+         stop-opacity="1"
+         id="stop7" />
+      <stop
+         offset="1"
+         stop-color="#eee"
+         stop-opacity="1"
+         id="stop9" />
+    </linearGradient>
+    <clipPath
+       id="clipPath-115814090">
+      <g
+         transform="translate(0,-1004.3622)"
+         id="g12">
+        <path
+           d="m -24 13 c 0 1.105 -0.672 2 -1.5 2 -0.828 0 -1.5 -0.895 -1.5 -2 0 -1.105 0.672 -2 1.5 -2 0.828 0 1.5 0.895 1.5 2 z"
+           transform="matrix(15.333333,0,0,11.5,414.99999,878.8622)"
+           fill="#1890d0"
+           id="path14" />
+      </g>
+    </clipPath>
+    <clipPath
+       id="clipPath-121566083">
+      <g
+         transform="translate(0,-1004.3622)"
+         id="g17">
+        <path
+           d="m -24 13 c 0 1.105 -0.672 2 -1.5 2 -0.828 0 -1.5 -0.895 -1.5 -2 0 -1.105 0.672 -2 1.5 -2 0.828 0 1.5 0.895 1.5 2 z"
+           transform="matrix(15.333333,0,0,11.5,414.99999,878.8622)"
+           fill="#1890d0"
+           id="path19" />
+      </g>
+    </clipPath>
+  </defs>
+  <g
+     id="g21">
+    <path
+       d="m 36.31 5 c 5.859 4.062 9.688 10.831 9.688 18.5 c 0 12.426 -10.07 22.5 -22.5 22.5 c -7.669 0 -14.438 -3.828 -18.5 -9.688 c 1.037 1.822 2.306 3.499 3.781 4.969 c 4.085 3.712 9.514 5.969 15.469 5.969 c 12.703 0 23 -10.298 23 -23 c 0 -5.954 -2.256 -11.384 -5.969 -15.469 c -1.469 -1.475 -3.147 -2.744 -4.969 -3.781 z m 4.969 3.781 c 3.854 4.113 6.219 9.637 6.219 15.719 c 0 12.703 -10.297 23 -23 23 c -6.081 0 -11.606 -2.364 -15.719 -6.219 c 4.16 4.144 9.883 6.719 16.219 6.719 c 12.703 0 23 -10.298 23 -23 c 0 -6.335 -2.575 -12.06 -6.719 -16.219 z"
+       opacity="0.05"
+       id="path23" />
+    <path
+       d="m 41.28 8.781 c 3.712 4.085 5.969 9.514 5.969 15.469 c 0 12.703 -10.297 23 -23 23 c -5.954 0 -11.384 -2.256 -15.469 -5.969 c 4.113 3.854 9.637 6.219 15.719 6.219 c 12.703 0 23 -10.298 23 -23 c 0 -6.081 -2.364 -11.606 -6.219 -15.719 z"
+       opacity="0.1"
+       id="path25" />
+    <path
+       d="m 31.25 2.375 c 8.615 3.154 14.75 11.417 14.75 21.13 c 0 12.426 -10.07 22.5 -22.5 22.5 c -9.708 0 -17.971 -6.135 -21.12 -14.75 a 23 23 0 0 0 44.875 -7 a 23 23 0 0 0 -16 -21.875 z"
+       opacity="0.2"
+       id="path27" />
+  </g>
+  <g
+     id="g29">
+    <path
+       d="m 24 1 c 12.703 0 23 10.297 23 23 c 0 12.703 -10.297 23 -23 23 -12.703 0 -23 -10.297 -23 -23 0 -12.703 10.297 -23 23 -23 z"
+       fill="url(#linearGradient3764)"
+       fill-opacity="1"
+       id="path31" />
+  </g>
+  <g
+     id="g33" />
+  <g
+     id="g87">
+    <path
+       d="m 40.03 7.531 c 3.712 4.084 5.969 9.514 5.969 15.469 0 12.703 -10.297 23 -23 23 c -5.954 0 -11.384 -2.256 -15.469 -5.969 4.178 4.291 10.01 6.969 16.469 6.969 c 12.703 0 23 -10.298 23 -23 0 -6.462 -2.677 -12.291 -6.969 -16.469 z"
+       opacity="0.1"
+       id="path89" />
+  </g>
+  <g
+     id="g4152"
+     transform="matrix(0.85670026,0,0,0.85670026,3.4391934,3.4391938)">
+    <path
+       sodipodi:open="true"
+       d="M 32.351071,32.449646 A 11.810015,11.946828 0 0 1 17.438837,33.935387 11.810015,11.946828 0 0 1 13.089099,19.43011 11.810015,11.946828 0 0 1 26.304149,12.28469 11.810015,11.946828 0 0 1 35.810144,24.001963"
+       sodipodi:end="0"
+       sodipodi:start="0.78539816"
+       sodipodi:ry="11.946828"
+       sodipodi:rx="11.810015"
+       sodipodi:cy="24.001963"
+       sodipodi:cx="24.00013"
+       sodipodi:type="arc"
+       id="path3336-2-2-2-2-2-3-5"
+       style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#313131;stroke-width:1.65701091;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    <path
+       sodipodi:nodetypes="cc"
+       inkscape:connector-curvature="0"
+       id="path4138-0-5-7-2-5-3-5"
+       d="m 35.810127,24.054395 0,11.879225"
+       style="fill:none;fill-rule:evenodd;stroke:#313131;stroke-width:1.65701091;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+  </g>
+</svg>
diff --git a/images/desktop-environment-budgie.svg b/images/desktop-environment-budgie.svg
new file mode 100644 (file)
index 0000000..c5096b3
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">  <image id="image0" width="100" height="100" x="0" y="0"
+    xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
+AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAADAFBMVEVHcEw+Q1E+Q1E+Q1E+
+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+QlE+Q1E+Q1E+Q1E+Q1E+
+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E9Q1E+Q1E+Q1E+Q1E+Q1E+
+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+
+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E+Q1E9
+QlA6QE86QE48QU9DSVZTWGRtcnyGipKVmJ+hpKqoqrCjpayYm6KOkZl8gIheY25MUF4/RFKSlZzE
+xcnl5ef29/f+/v7////8/P3u7+/b3N+2uL2KjZVcYGtARVNjZ3Kvsbbp6uz9/f66vMB3e4RFS1hK
+TlyWmaDFxst0d4FXXWm9v8P7+/vy8/SrrbNRVmNlaXXS09Zna3ZqbXne3+Hh4eRvdH7i4+VobHhb
+X2rX2NrV1tlVW2Y8QVBPVGHHyMxCRlVDSVXz8/Rzd4GQk5v4+PmEiJDJy8/AwcZ2eYOgoqk5P01y
+doD09fXw8fJaXmnZ2t1+goq4ur/Oz9LQ0dVHTFrj5Obn6Onv7/BgZXCqrLL5+vp5fYbr7O3Bw8eV
+obI/TmY+RVU9QU+zz/BOi9FLerJFY4qeoKfE2/VTluRRl+dQkdxJb58/SFrM4PZTleFNgsFEXoCy
+tLn+///2+f3q8fvX4e1Rb5e8vcKcn6VHTVrl7/qoyvB5reloo+d8r+qtzfHt8/zm5+nt7e/C2vVq
+pOdPlONQlONOk+N2q+nU5fhdnOVRlePd6vlupuiItuz3+v6eoaiRu+1amuTIys6gxe/s8vyWvu7q
+6+zG3PW+1/TMzdGEs+tWmOTv9PzZ6PiVvu5Ul+T7/P7R4/fU1dhgnuV5rOnf7Pqdwu9rpedfneV1
+qunz+P21t7ylp62anaSTNx/mAAAAT3RSTlMABBY2W36zx9fp+f7u2sm1XTkBDzdopMzqahBChYgo
+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" />
+</svg>
diff --git a/images/desktop-environment-i3.svg b/images/desktop-environment-i3.svg
new file mode 100644 (file)
index 0000000..3636a6d
--- /dev/null
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="96px" height="92px" viewBox="0 0 96 92" enable-background="new 0 0 96 92" xml:space="preserve">  <image id="image0" width="96" height="92" x="0" y="0"
+    xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABcCAMAAACm/kDcAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
+AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAADAFBMVEUSITgVLEYWMEwWMk8T
+JkARGzAhRnAhSHMhSXQiSnYjTHkjTnsjT30jUX8gRG4bP2MgQmsgQGgePWQeOmAdNlwcNFgbMFIa
+K0wlUoEeRm4lU4IlVIQlVYUlVoYbQGMlV4gmWIonWo0fTXk1V3omWYxAZYiYq77b4+rl6ezi6O29
+ydRbe5gnXI8oXZExWoHr7vHk6Orn6+7I1N4WRnV0jaMoX5Tw8/Xp7O+1w9BMcJEpYZYhUHwcRGkk
+V4bt8fMpY5kdR20mWorm6u0qZJoiU4Hl6ezP2eEqZp2kucvV3eMWNVUqZ54qZZsqaKAmXpAraaEn
+YZYsa6OuvcssbKXl6ewtbqglXIwoZJktb6mdtskucq0eVIJkiKc9apJxkq55m7dLeJ4vdLDn7O8t
+cKkvdrImYZQwY4+FoLc6bJclZJoXOFgxfLkcTHUhXI0vd7Ipap8FHz0IM1kwebVbhqkbVocweLSC
+o70pbKIscaoudK4xere3ydiuwdEiY5YveLTN2eI7cZ1rlLRLeqIyfruLqsPl6u7D0d0we7cygL5g
+jbAzg8Lf5+zY4urr7/Msc6sxfrscWI49daEygsA0hsUGLlGOqb8qbJ7n6u0MUpELTovo7O81icoT
+WJULVJQLUI4MVpcueLEKUo4MWJodXI7K2OMNWp0FKUsKSYQjf8YfecAQbLUPZawMV5MHR3sJR4Eq
+baAsc6oRdMEQcb0Qb7oOZq4LWZgMWJgNXJ8KS4gMXZsPaa4QcbsRdsQReMcPaK4PYKEJSYEOXZ4O
+YKUPabITf9IOY6ROg6vl6+8NXqILUIgSfc4JUYkResrE1eELWJJbjrMOYqgHN14JRX8PcboQa64S
+fMwRd8UMYqMTgdXQ3eeivdIThNkSgNJDga7p7vF9pMEFI0QHOW4IPHEIP3UIQXgJQncIPmgTh90s
+dqwThNYJX56Wtc0JQ3wUi+MSfcwPb7QFVpMIO2QQdsAUa6tUkLuzydoVjugUh9sVku0UjuVpmbwH
+N2oJQGsGMmMJQm3///+IGi9cAAAA/3RSTlPz6OPh7fjc2tjX1dTT0d3a3+Hk5+rt8PTP087NzMvT
+ysrHzNnI0+bw+vPs1sXE0Pr6+evJ28L6+OvSwMnNxPi/ycL4vsb58Lvf89u6vLm8uLy36rb4tL65
+s9evvNDH0c7FrfewrLfB172z2Ka+tKuw48epwrWqy6urqafc4qmm6rXBvqTO9+ajo7mg8Oj1pKC7
+r5yczdml9rK39ZmxsLSun6ysrOGq1ryQlpaeo6i+n5mOkJObnqeouZaPi4uJlZyzo6OZg5ar86Wn
+hZ6H3JqsoMDAhYqBg42A4c19fJz0ut3MycfEvrF5knaFwsJ1d36NuHuEnNBycW1uqdCs1KZb5/3K
+AAAAAWJLR0T/pQfyxQAAAAd0SU1FB+IECw4jN7lTzowAABDsSURBVGjefZcJWFRXtoVLTVRAMr3u
+fiISWp6KqFjAi5aRppCCYhAtMJQgTmgRVIJABJTBANoGJ0CNA6VlG4NDkgYMkTEqg6KIAw4dQBSx
+zPQcUMSgNmp33td773PurVugWfkSo8D6z1p733NLmaxfv/5mGvBS9R/Qr98AyXf1k+o1iWRcr3PJ
++g0cNHjwYAsLC0sLS5IVaEhvWVlZv/GmFX4Zvs/CAn5i0KBBAwe+AXrrrbdB77zzzn+B/gD6I+hP
+oP9GvS7rPwjNRV9rrqHmgj+wedPamlBWCEIIMQYS4y1CCAwJAQCDub1gzi2HSQW/t7V5Ezl9GAOJ
+QYi3X4IAgIXZ4Zm3LWk4ly38fpidzbu2FIVXhgwLgSFJ0YsgAJg9P7nUXJS9zbtAIoCVMBeMYZqG
+eQhOkMn6g7/gTuZgZgey76U/29jY2Q61trS0sbGwZGkZQ2yKIThBQBBA9OfmaNdHIyxt7G2tHaz+
+B2RpaT3UmgZi9RLE29KeEDBEOD6eHu3JcGQvjXCwsR/mMGq04xinseNsrKz5cokp+vZEBJlsgBU/
+PbrzszNHpvHjx9OvzgBwmDBa7uLi4uo2zsYaBz9MWKveCNMkADAE/bk9cydfspYIAf/7ntMYALjI
+R09wGGaH6zWMr65QlLiyQggE8PNDOezwI0RvZ4kmYoLRk9DfRTH5fQdbe3s7u+EQYygfBt8o82H/
+4TXZAGtmb2/PT29mPVHUFPe/eLiPVooAO/h2RAwXm+IIyT4BAQCiPytesDc5g+AXT/epXlCRilfk
+bg/fzRm2pqIsBISwTggQ6uf2gvsUc3n7TJ3oM2G0eswY1zG+4xzs4ZuJIEGIIQaJk0DAMKEewV5w
+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" />
+</svg>
diff --git a/images/desktop-environment-pantheon.svg b/images/desktop-environment-pantheon.svg
new file mode 100644 (file)
index 0000000..fd7b39e
--- /dev/null
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">  <image id="image0" width="48" height="48" x="0" y="0"
+    xlink:href="data:image/png;base64,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" />
+</svg>
diff --git a/images/desktop-environment-windows.svg b/images/desktop-environment-windows.svg
new file mode 100644 (file)
index 0000000..5caf7a0
--- /dev/null
@@ -0,0 +1,854 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="626px" height="626px" viewBox="0 0 626 626" enable-background="new 0 0 626 626" xml:space="preserve">  <image id="image0" width="626" height="626" x="0" y="0"
+    href="data:image/png;base64,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
+LTA3OjAw7QEBRgAAAABJRU5ErkJggg==" />
+</svg>
diff --git a/images/enlightenment.png b/images/enlightenment.png
new file mode 100644 (file)
index 0000000..606d6b5
Binary files /dev/null and b/images/enlightenment.png differ
diff --git a/images/i3.png b/images/i3.png
new file mode 100644 (file)
index 0000000..8cb4e85
Binary files /dev/null and b/images/i3.png differ
diff --git a/images/kde.png b/images/kde.png
new file mode 100644 (file)
index 0000000..63742dd
Binary files /dev/null and b/images/kde.png differ
diff --git a/images/lxqt.png b/images/lxqt.png
new file mode 100644 (file)
index 0000000..24fc3f5
Binary files /dev/null and b/images/lxqt.png differ
diff --git a/images/openbox.png b/images/openbox.png
new file mode 100644 (file)
index 0000000..e3b7ab9
Binary files /dev/null and b/images/openbox.png differ
diff --git a/images/pantheon.png b/images/pantheon.png
new file mode 100644 (file)
index 0000000..4fce01f
Binary files /dev/null and b/images/pantheon.png differ
diff --git a/images/windows.png b/images/windows.png
new file mode 100644 (file)
index 0000000..78e1148
Binary files /dev/null and b/images/windows.png differ
diff --git a/images/xfce.png b/images/xfce.png
new file mode 100644 (file)
index 0000000..ae5d48e
Binary files /dev/null and b/images/xfce.png differ
diff --git a/isolinux/isolinux.cfg b/isolinux/isolinux.cfg
new file mode 100644 (file)
index 0000000..3440ee6
--- /dev/null
@@ -0,0 +1,7 @@
+PATH /%INSTALL_DIR%/boot/syslinux/
+DEFAULT loadconfig
+timeout  3
+
+LABEL loadconfig
+  CONFIG /%INSTALL_DIR%/boot/syslinux/archiso.cfg
+  APPEND /%INSTALL_DIR%/
diff --git a/mkinitcpio.conf b/mkinitcpio.conf
new file mode 100644 (file)
index 0000000..4173419
--- /dev/null
@@ -0,0 +1,4 @@
+MODULES="ext4 squashfs isofs"
+HOOKS="base udev memdisk archiso_shutdown archiso archiso_loop_mnt archiso_kms block filesystems keyboard"
+COMPRESSION="xz"
+COMPRESSION_OPTIONS="-9"
diff --git a/packages.both b/packages.both
new file mode 100755 (executable)
index 0000000..f86b7f4
--- /dev/null
@@ -0,0 +1,228 @@
+# Random Stuff. Bit bloated, contains both core packages and random other ones. Basically, this section is a wreck
+rebornos-keyring
+btrfs-progs
+clonezilla
+darkhttpd
+ddrescue
+dhclient
+dialog
+dmraid
+dnsutils
+elinks
+ethtool
+exfat-utils
+f2fs-tools
+fping
+fsarchiver
+gnu-netcat
+gpm
+gptfdisk
+grml-zsh-config
+grub
+gtk3
+hdparm
+irssi
+iptables
+iputils
+iproute2
+lftp
+linux-atm
+lsscsi
+mc
+mtools
+ndisc6
+nfs-utils
+nilfs-utils
+nmap
+ntp
+openconnect
+partclone
+parted
+partimage
+ppp
+pptpclient
+refind-efi
+rp-pppoe
+sdparm
+sg3_utils
+smartmontools
+tcpdump
+testdisk
+vim-minimal
+vpnc
+wvdial
+xl2tpd
+yay
+
+# Yet another wreck!
+base
+base-devel
+
+# Comsic packages
+common-cosmic-reborn
+full-base-cosmic-reborn
+graphic-cosmic-reborn
+
+
+# GNOME install start here
+gnome
+gnome-cosmic-reborn
+gdm
+network-manager-applet
+gnome-shell-extension-appindicator
+gnome-shell-extension-dash-to-panel
+gvfs-google
+gvfs-mtp
+gvfs-smb
+hicolor-icon-theme
+libgnome-keyring
+yaru-gnome-shell-theme
+yaru-gtk-theme
+yaru-icon-theme
+yaru-sound-theme
+
+# Scripts to aid in installing Arch Linux
+arch-install-scripts
+
+
+polari
+python-pysmbc
+lightdm-webkit-theme-aether
+sddm-config-editor-git
+sddm-theme-gracilis-git
+xdg-user-dirs-gtk
+xdg-desktop-portal
+xdg-desktop-portal-gtk
+xscreensaver
+
+
+reborn-updates
+bubblewrap
+
+
+# Firefox web browser
+firefox
+firefox-adblock-plus
+firefox-extension-privacybadger
+firefox-extension-https-everywhere
+
+colord
+dconf-editor
+elfutils
+fwupd
+gcab
+gtkspell
+gvfs-mtp
+libgusb
+libsmbios
+libwnck3
+libyaml
+ostree
+# pepper-flash
+rsync
+
+# Firewall
+ufw
+gufw
+
+
+# Fonts
+opendesktop-fonts
+ttf-arphic-uming
+ttf-baekmuk
+ttf-droid
+gnu-free-fonts
+ttf-opensans
+ttf-ubuntu-font-family
+noto-fonts-cjk
+
+
+#Linux-y things
+btrfs-progs
+linux-api-headers
+mtools
+squashfs-tools
+
+
+#Things related to booting
+dmraid
+efitools
+fakeroot
+gparted
+gptfdisk
+grub
+hwdetect
+hwinfo
+ntp
+os-prober
+parted
+
+#Random group I sort of didn't ever pay attention to until writing this comment
+expect
+git
+
+
+#Network packages
+b43-firmware
+broadcom-wl-dkms
+dnsutils
+netctl
+nss-mdns
+
+#Virtualbox and xf86 packages? Whose bright idea was it to combine these? Oh wait... probably mine at one point
+# virtualbox-guest-modules-arch --> It is no longer in the repository. Now present in the kernel.
+# virtualbox-guest-utils
+xf86-input-libinput
+#xf86-input-mouse
+xf86-video-amdgpu
+xf86-video-ati
+xf86-video-intel
+xf86-video-dummy
+xorg-apps
+xorg-fonts
+
+# Now why does this guy get his own group?
+xf86-video-nouveau
+
+# NVIDIA
+nvidia
+nvidia-settings
+
+#Necessary for Cnchi. Enter Cnchi bloat!
+encfs
+pam_encfs
+libinput
+clutter
+clutter-gtk
+clutter-gst
+gstreamer
+geoip2-database
+python-cairo
+python
+pyalpm
+python-gobject
+python-geoip2
+python-gobject
+python-libnacl
+python-dbus
+python-defusedxml
+python-feedparser
+python-cairo
+python-requests
+parted
+python-parted
+# Important module for cnchi and not existing
+# in the Arch Linux repository: python-pyparted,
+# for prevent module not found when "import parted"
+python-pyparted
+webkit2gtk
+python-mako
+iso-codes
+gptfdisk
+python-whither
+python-bugsnag
+# reborn-welcome --> Removed to allow cnchi-start.sh to be the only thing to start automatically
+python-lxml
+python-mock
+python-requests
+upower
\ No newline at end of file
diff --git a/packages2.both b/packages2.both
new file mode 100755 (executable)
index 0000000..f6ba9cf
--- /dev/null
@@ -0,0 +1,211 @@
+b43-fwcutter
+btrfs-progs
+clonezilla
+darkhttpd
+ddrescue
+dhclient
+dialog
+dmraid
+dnsmasq
+dnsutils
+elinks
+ethtool
+exfat-utils
+f2fs-tools
+fsarchiver
+gnu-netcat
+gpm
+gptfdisk
+grml-zsh-config
+grub
+hdparm
+ipw2100-fw
+ipw2200-fw
+irssi
+iptables
+iputils
+iproute2
+lftp
+linux-atm
+lsscsi
+mc
+mtools
+ndisc6
+nfs-utils
+nilfs-utils
+nmap
+ntp
+openconnect
+partclone
+parted
+partimage
+ppp
+pptpclient
+refind-efi
+rp-pppoe
+sdparm
+sg3_utils
+smartmontools
+speedtouch
+sudo
+tcpdump
+testdisk
+usb_modeswitch
+vim-minimal
+vpnc
+wireless_tools
+wpa_actiond
+wvdial
+xl2tpd
+zd1211-firmware
+
+
+base
+base-devel
+deepin
+deepin-extra
+common-cosmic-reborn
+full-base-cosmic-reborn
+graphic-cosmic-reborn
+deepin-cosmic-reborn
+bubblewrap
+chromium-widevine
+colord
+dconf-editor
+elfutils
+fwupdate
+gcab
+gksu
+gnome-logs
+gtkspell
+gvfs-mtp
+libgusb
+libsmbios
+libwnck3
+libyaml
+network-manager-applet
+# networkmanager-openvpn
+ostree
+pepper-flash
+polari
+reflector
+rsync
+grsync
+transmission-gtk
+xdg-desktop-portal
+xdg-desktop-portal-gtk
+xdg-user-dirs-gtk
+screenfetch
+sddm-config-editor-git
+sddm-theme-gracilis-git
+
+
+
+flatplat-blue-theme
+
+
+
+divehi-fonts
+opendesktop-fonts
+ttf-aboriginal-sans
+ttf-arphic-uming
+ttf-baekmuk
+ttf-droid
+# ttf-freefont
+gnu-free-fonts
+ttf-opensans
+ttf-ubuntu-font-family
+
+
+
+btrfs-progs
+dosfstools
+f2fs-tools
+linux
+linux-headers
+linux-api-headers
+lvm2
+mdadm
+mtools
+ntfs-3g
+squashfs-tools
+xfsprogs
+
+
+
+arch-install-scripts
+crda
+dmraid
+efibootmgr
+efitools
+fakeroot
+gparted
+gptfdisk
+grub
+hwdetect
+hwinfo
+ntp
+os-prober
+parted
+
+
+
+expect
+git
+gksu
+powerpill
+wget
+
+
+
+iso-hotfix-utility
+b43-firmware
+broadcom-wl-dkms
+dnsutils
+netctl
+nss-mdns
+
+
+
+virtualbox-guest-modules-arch
+virtualbox-guest-utils
+xf86-input-libinput
+xf86-video-amdgpu
+xf86-video-ati
+xf86-video-intel
+xf86-video-dummy
+xorg-apps
+xorg-fonts
+
+
+
+xf86-video-nouveau
+
+
+
+nvidia
+nvidia-libgl
+nvidia-settings
+
+
+
+
+python
+pyalpm
+python-gobject
+python-dbus
+python-cairo
+python-requests
+python-parted
+webkit2gtk
+upower
+python-mako
+iso-codes
+gptfdisk
+python-whither
+python-bugsnag
+python-lxml
+python-mock
+python-requests
+upower
+cnchi
diff --git a/pacman-init.service b/pacman-init.service
new file mode 100644 (file)
index 0000000..0c6603e
--- /dev/null
@@ -0,0 +1,14 @@
+[Unit]
+Description=Initializes Pacman keyring
+Wants=haveged.service
+After=haveged.service etc-pacman.d-gnupg.mount network.target 
+Requires=etc-pacman.d-gnupg.mount network.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/bin/pacman-key --init
+ExecStart=/usr/bin/pacman-key --populate archlinux rebornos
+
+[Install]
+WantedBy=multi-user.target
diff --git a/pacman.conf b/pacman.conf
new file mode 100644 (file)
index 0000000..abbac17
--- /dev/null
@@ -0,0 +1,98 @@
+#
+# /etc/pacman.conf
+#
+# See the pacman.conf(5) manpage for option and repository directives
+
+#
+# GENERAL OPTIONS
+#
+[options]
+# The following paths are commented out with their default values listed.
+# If you wish to use different paths, uncomment and update the paths.
+#RootDir     = /
+#DBPath      = /var/lib/pacman/
+#CacheDir    = /var/cache/pacman/pkg/
+#LogFile     = /var/log/pacman.log
+#GPGDir      = /etc/pacman.d/gnupg/
+HoldPkg     = pacman glibc
+#XferCommand = /usr/bin/curl -C - -f %u > %o
+#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
+#CleanMethod = KeepInstalled
+#UseDelta    = 0.7
+Architecture = auto
+
+# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
+#IgnorePkg   =
+#IgnoreGroup =
+
+#NoUpgrade   =
+#NoExtract   =
+
+# Misc options
+#UseSyslog
+#Color
+#TotalDownload
+# We cannot check disk space from within a chroot environment
+#CheckSpace
+#VerbosePkgLists
+
+# By default, pacman accepts packages signed by keys that its local keyring
+# trusts (see pacman-key and its man page), as well as unsigned packages.
+SigLevel    = Required DatabaseOptional
+LocalFileSigLevel = Optional
+#RemoteFileSigLevel = Required
+
+# NOTE: You must run `pacman-key --init` before first using pacman; the local
+# keyring can then be populated with the keys of all official Arch Linux
+# packagers with `pacman-key --populate archlinux`.
+
+#
+# REPOSITORIES
+#   - can be defined here or included from another file
+#   - pacman will search repositories in the order defined here
+#   - local/custom mirrors can be added here or in separate files
+#   - repositories listed first will take precedence when packages
+#     have identical names, regardless of version number
+#   - URLs will have $repo replaced by the name of the current repo
+#   - URLs will have $arch replaced by the name of the architecture
+#
+# Repository entries are of the format:
+#       [repo-name]
+#       Server = ServerName
+#       Include = IncludePath
+#
+# The header [repo-name] is crucial - it must be present and
+# uncommented to enable the repo.
+#
+
+# The testing repositories are disabled by default. To enable, uncomment the
+# repo name header and Include lines. You can add preferred servers immediately
+# after the header, and they will be used before the default mirrors.
+
+#[testing]
+#Include = /etc/pacman.d/mirrorlist
+
+[Reborn-OS]
+Include = /etc/pacman.d/reborn-mirrorlist
+
+[core]
+Include = /etc/pacman.d/mirrorlist
+
+[extra]
+Include = /etc/pacman.d/mirrorlist
+
+#[community-testing]
+#Include = /etc/pacman.d/mirrorlist
+
+[community]
+Include = /etc/pacman.d/mirrorlist
+
+[multilib]
+Include = /etc/pacman.d/mirrorlist
+
+# An example of a custom package repository.  See the pacman manpage for
+# tips on creating your own repositories.
+#[custom]
+#SigLevel = Optional TrustAll
+#Server = file:///home/custompkgs
+
diff --git a/rebornos.gpg b/rebornos.gpg
new file mode 100644 (file)
index 0000000..dd5cfe7
--- /dev/null
@@ -0,0 +1,135 @@
+-----Keegan Milsten (Old)-----
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEWo6pUhYJKwYBBAHaRw8BAQdASkeBeGxEN+q1hrjEmQNA7I7UZzKhnN4UH+Mu
+GrqcQLK0LUtlZWdhbiBNaWxzdGVuIDxrZWVnYW5taWxzdGVuQHByb3Rvbm1haWwu
+Y29tPoiQBBMWCAA4FiEEQog2UlorDVO4+Xq92/Vz1p3p9LUFAlqRU5ECGwEFCwkI
+BwIGFQoJCAsCBBYCAwECHgECF4AACgkQ2/Vz1p3p9LXw+wD+LA6g8MIe6kfiK78M
+xFAk3winN1wWeuMNNIhyve1EALcA/j0lySosbVVPRRQrNLLRrJnZc2QGJH1P7S3k
+/d8jOhIGuDMEWpFXLBYJKwYBBAHaRw8BAQdA4zNVDrXWrol1kB+AS1VIVzh6BXz2
+DLiF/WQR23bEMyaIeAQYFggAIBYhBEKINlJaKw1TuPl6vdv1c9ad6fS1BQJakVcs
+AhsgAAoJENv1c9ad6fS1GbIA/Rtf/SFDg2mkbgi5B669ZERH57wde8PAadbk4tZr
+QWiFAPsEwQXCDpdarUafO5DFkrLS1hEQYbyzJkVBrKX98rGpA7gzBFqRWMYWCSsG
+AQQB2kcPAQEHQMcqaQM01ArigfbKLuDuowh0h+Ze/DWnejCR2MZnid9hiO8EGBYI
+ACAWIQRCiDZSWisNU7j5er3b9XPWnen0tQUCWpFYxgIbAgCBCRDb9XPWnen0tXYg
+BBkWCAAdFiEE8LL1diJMLGwkjAQu0H70vUknPakFAlqRWMYACgkQ0H70vUknPakx
+WAEA2I4qzSBrPRDNcg8DmhVL8nSO5RMwv14muIP/eZQuk8sBANSLPxK1ZNJBheSh
+Mt5Rf/7awTWpkY5vEDxRqgBsskEARKUA/1XDCGuJd8Qo0BBTsjFCG/QfczUY6S+Z
+Aq9CJlzEYXNwAQCKWzDJmyyefNkE6EzRtw8JK3P6rMvRgOk9u1Ljen5VCrg4BFqR
+WPMSCisGAQQBl1UBBQEBB0CtYJqR8ujykn5wwqzXrJ5pBxgQKy8PNxEw5B7UTY5W
+WQMBCAeIeAQYFggAIBYhBEKINlJaKw1TuPl6vdv1c9ad6fS1BQJakVjzAhsMAAoJ
+ENv1c9ad6fS1rlEA/j2KbJS+oYGu03P0HGrlIC3SwAUSJ/MlADAmEbjPqY6pAQDA
+jFRiCRdJOXihTTXLdq90sqNdliCsCRpoFzY6/u71Cg==
+=TQLH
+-----END PGP PUBLIC KEY BLOCK-----
+
+-----Keegan Milsten (new)-----
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+=b5OE
+-----END PGP PUBLIC KEY BLOCK-----
+
+-----Rafael Costa Rega-----
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+wSRUZHgACt6eWRsQJK/rfDmHdRM=
+=Axuz
+-----END PGP PUBLIC KEY BLOCK-----
\ No newline at end of file
diff --git a/run.sh b/run.sh
new file mode 100755 (executable)
index 0000000..fb089b6
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,37 @@
+#  Copyright © 2016-2019 RebornOS
+#
+#  This file is part of RebornOS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+#!/usr/bin/bash
+echo "Correcting file permissions"
+chmod +x $PWD/Cnchi/postinstall.sh
+chmod +x $PWD/Cnchi/10_antergos
+chmod +x $PWD/build.sh
+chmod +x $PWD/translations.sh
+chmod +x $PWD/airootfs/etc/skel/.config/autostart/internet.sh
+chmod +x $PWD/airootfs/etc/systemd/scripts/choose-mirror
+chmod +x $PWD/airootfs/root/.automated_script.sh
+chmod +x $PWD/airootfs/root/customize_airootfs.sh
+chmod +x $PWD/airootfs/usr/bin/cnchi-start.sh
+chmod +x $PWD/airootfs/usr/bin/internet.sh
+echo "DONE"
+
diff --git a/scripts/antergos-mirrorlist b/scripts/antergos-mirrorlist
new file mode 100644 (file)
index 0000000..684e553
--- /dev/null
@@ -0,0 +1,139 @@
+## Generated by reflector-antergos (version 0.1.34) at Tue Jul 24 07:55:45 PDT 2018.
+##
+## Options used: --save --timeout=5
+##
+#Server = http://mirrors.itbox.bg/antergos/$repo/$arch
+############# downloadtime:    -1 status: server_issued_error Bulgaria
+#Server = http://mirrors.tuxns.net/antergos/$repo/$arch
+############# downloadtime:    -1 status: network_failure USA
+#Server = http://wynsrv.antergos.info/antergos/$repo/$arch
+############# downloadtime:    -1 status: network_failure Canada
+Server = http://antergos.mirror.constant.com/$repo/$arch
+############# downloadtime:  0,81 status: ok USA
+Server = http://mirror.us.leaseweb.net/antergos/$repo/$arch
+############# downloadtime:  0,91 status: ok USA
+Server = http://cinnarch.polymorf.fr/$repo/$arch
+############# downloadtime:  1,39 status: ok France
+Server = http://eu.mirrors.coltondrg.com/antergos/$repo/$arch
+############# downloadtime:  1,72 status: ok France
+Server = http://ftp.cc.uoc.gr/mirrors/linux/antergos/$repo/$arch
+############# downloadtime:  1,95 status: ok Greece
+Server = http://mirror.alpix.eu/antergos/$repo/$arch
+############# downloadtime:  1,60 status: ok Germany
+Server = http://mirror.de.leaseweb.net/antergos/$repo/$arch
+############# downloadtime:  1,38 status: ok Germany
+Server = http://mirror.host.ag/antergos/$repo/$arch
+############# downloadtime:  1,98 status: ok Bulgaria
+Server = http://mirror.neostrada.nl/antergos/$repo/$arch
+############# downloadtime:  1,62 status: ok Netherlands
+Server = http://mirror.nl.leaseweb.net/antergos/$repo/$arch
+############# downloadtime:  1,42 status: ok Netherlands
+Server = http://mirrors.servercentral.com/antergos/$repo/$arch
+############# downloadtime:  1,19 status: ok USA
+Server = http://mirrors.tuna.tsinghua.edu.cn/antergos/$repo/$arch
+############# downloadtime:  1,87 status: ok China
+Server = http://mirror.umd.edu/antergos/$repo/$arch
+############# downloadtime:  1,85 status: ok USA
+Server = http://quantum-mirror.hu/mirrors/pub/antergos/$repo/$arch
+############# downloadtime:  1,98 status: ok Hungary
+Server = http://ftp1.nluug.nl/os/Linux/distr/antergos/$repo/$arch
+############# downloadtime:  2,21 status: ok Netherlands
+Server = http://ftp2.nluug.nl/os/Linux/distr/antergos/$repo/$arch
+############# downloadtime:  2,03 status: ok Netherlands
+Server = http://ftp.acc.umu.se/mirror/antergos.com/$repo/$arch
+############# downloadtime:  2,62 status: ok Sweden
+Server = http://mirrors.acm.wpi.edu/antergos/$repo/$arch
+############# downloadtime:  2,67 status: ok USA
+Server = http://mirrors.dotsrc.org/antergos/$repo/$arch
+############# downloadtime:  2,03 status: ok Denmark
+#Server = http://mirrors.ustc.edu.cn/antergos/$repo/$arch
+############# downloadtime:  2,55 status: outdated China
+Server = http://mirror.yandex.ru/mirrors/cinnarch/$repo/$arch
+############# downloadtime:  2,03 status: ok Russia
+Server = http://softlibre.unizar.es/cinnarch/$repo/$arch
+############# downloadtime:  2,18 status: ok Spain
+Server = http://glua.ua.pt/pub/antergos/$repo/$arch
+############# downloadtime:  3,14 status: ok Portugal
+Server = http://mirror.antergos.jp/$repo/$arch
+############# downloadtime:  3,10 status: ok Japan
+Server = http://mirrors.nic.cz/antergos/$repo/$arch
+############# downloadtime:  3,46 status: ok Czech Republic
+Server = http://www.mirrorservice.org/sites/repo.antergos.com/$repo/$arch
+############# downloadtime:  4,79 status: ok England
+
+
+#############################################################
+############## Original antergos-mirrorlist: ################
+#############################################################
+
+## Antergos Repository Mirrorlist
+
+# Automated Mirror Selection
+
+#Server = http://mirrors.antergos.com/$repo/$arch
+
+# Manual Mirror Selection
+
+# Bulgaria
+#Server = http://mirror.host.ag/antergos/$repo/$arch
+#Server = https://mirrors.itbox.bg/antergos/$repo/$arch
+
+# Canada
+#Server = http://wynsrv.antergos.info/antergos/$repo/$arch
+
+# China
+#Server = http://mirrors.ustc.edu.cn/antergos/$repo/$arch
+#Server = http://mirrors.tuna.tsinghua.edu.cn/antergos/$repo/$arch
+
+# Czech Republic
+#Server = http://mirrors.nic.cz/antergos/$repo/$arch
+
+# Denmark
+#Server = http://mirrors.dotsrc.org/antergos/$repo/$arch
+
+# France
+#Server = http://cinnarch.polymorf.fr/$repo/$arch
+#Server = http://eu.mirrors.coltondrg.com/antergos/$repo/$arch
+
+# England
+#Server = http://www.mirrorservice.org/sites/repo.antergos.com/$repo/$arch
+
+# Germany
+#Server = http://mirror.de.leaseweb.net/antergos/$repo/$arch
+#Server = http://mirror.alpix.eu/antergos/$repo/$arch
+
+# Greece
+#Server = http://ftp.cc.uoc.gr/mirrors/linux/antergos/$repo/$arch
+
+# Hungary
+#Server = https://quantum-mirror.hu/mirrors/pub/antergos/$repo/$arch
+
+# Japan
+#Server = http://mirror.antergos.jp/$repo/$arch
+
+# Netherlands
+#Server = http://mirror.nl.leaseweb.net/antergos/$repo/$arch
+#Server = http://ftp1.nluug.nl/os/Linux/distr/antergos/$repo/$arch
+#Server = http://ftp2.nluug.nl/os/Linux/distr/antergos/$repo/$arch
+#Server = http://mirror.neostrada.nl/antergos/$repo/$arch
+
+# Portugal
+#Server = http://glua.ua.pt/pub/antergos/$repo/$arch
+
+# Russia
+#Server = http://mirror.yandex.ru/mirrors/cinnarch/$repo/$arch
+
+# Spain
+#Server = http://softlibre.unizar.es/cinnarch/$repo/$arch
+
+# Sweden
+#Server = http://ftp.acc.umu.se/mirror/antergos.com/$repo/$arch
+
+# USA
+#Server = http://mirror.us.leaseweb.net/antergos/$repo/$arch
+#Server = http://mirrors.tuxns.net/antergos/$repo/$arch
+#Server = http://mirror.umd.edu/antergos/$repo/$arch
+#Server = http://mirrors.acm.wpi.edu/antergos/$repo/$arch
+#Server = http://mirrors.servercentral.com/antergos/$repo/$arch
+#Server = http://antergos.mirror.constant.com/$repo/$arch
+#Server = http://repo.antergos.info/$repo/$arch
diff --git a/scripts/conky-start.desktop b/scripts/conky-start.desktop
new file mode 100644 (file)
index 0000000..79cd5be
--- /dev/null
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Application
+Name=Autostarting Conky for Openbox
+Exec=conky
+Name[en_US]=conky-start
diff --git a/scripts/deepin-fix.service b/scripts/deepin-fix.service
new file mode 100755 (executable)
index 0000000..15511b1
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Deepin VM service
+
+[Service]
+ExecStart=/usr/bin/deepin-fix.sh
+
+[Install]
+WantedBy=multi-user.target
diff --git a/scripts/deepin-fix.sh b/scripts/deepin-fix.sh
new file mode 100755 (executable)
index 0000000..b0fbf6b
--- /dev/null
@@ -0,0 +1,29 @@
+#  Copyright © 2016-2019 Reborn OS
+#
+#  This file is part of Reborn OS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+#!/bin/bash
+testvirt=$(systemd-detect-virt)
+if [ "$testvirt" != none ]; then
+pkill dde-wm-chooser
+exec /usr/bin/deepin-fix.sh
+fi
diff --git a/scripts/flatpak.desktop b/scripts/flatpak.desktop
new file mode 100644 (file)
index 0000000..6f2daaf
--- /dev/null
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Comment=Flatpak For Reborn
+Exec=gksu flatpak.sh
+GenericName=Flatpak Installer
+Icon=/usr/share/icons/default/reborn-updates.svg
+Name=Flatpak Installer
+Name[en_US]=Flatpak Installer
+Terminal=False
+Type=Application
+Categories=System;
+NoDisplay=false
diff --git a/scripts/flatpak.sh b/scripts/flatpak.sh
new file mode 100755 (executable)
index 0000000..e0eb16e
--- /dev/null
@@ -0,0 +1,37 @@
+#  Copyright © 2016-2019 Reborn OS
+#
+#  This file is part of Reborn OS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+#!/bin/bash
+# -*- coding: utf-8 -*-
+#
+
+wget --spider www.google.com
+if [ "$?" = 0 ]; then
+
+yad --image="gtk-help" --title "Flatpak" --height=100 --width=300 --center --buttons-layout=center \
+        --text="Click the button below to get started using flatpak packages. These are extra packages that are distro independent, adding more available packages to your system. However, an internet connection is required. Please connect to the internet before proceeding. Thank you." --text-align=center \
+        --button="Add Flatpak Repository":pkcon.sh \
+        --button="Remove From Startup":pkcon2.sh
+
+else exec /usr/bin/flatpak.sh
+fi
diff --git a/scripts/mate-panel.desktop b/scripts/mate-panel.desktop
new file mode 100644 (file)
index 0000000..26610ee
--- /dev/null
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Application
+Name=Setting up Plymouth
+Exec=mate-panel --reset --layout default && yadsu rm -f /etc/xdg/autostart/mate-panel.desktop
+Name[en_US]=mate-panel
diff --git a/scripts/obmenu-gen.desktop b/scripts/obmenu-gen.desktop
new file mode 100644 (file)
index 0000000..e59e7ec
--- /dev/null
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Application
+Name=Generating dynamic menus for Openbox
+Exec=yadsu openbox-config.sh
+Name[en_US]=obmenu-gen
diff --git a/scripts/openbox-config.sh b/scripts/openbox-config.sh
new file mode 100755 (executable)
index 0000000..b7e5575
--- /dev/null
@@ -0,0 +1,61 @@
+#  Copyright © 2016-2019 Reborn OS
+#
+#  This file is part of Reborn OS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+
+pacman -S openbox-theme --force --noconfirm
+wget --spider www.google.com
+if [ "$?" = 0 ]; then
+echo "$(ls /home/)" >/home.txt
+sed -i '/timeshift/d' /home.txt
+sed -i '/root/d' /home.txt
+if [[ $(wc -l /home.txt | awk '{print $1}') -ge 2 ]]; then
+sed -i '/reborn/d' /home.txt
+else
+echo "Continuing on..."
+fi
+mkdir /home/$(cat /home.txt)/.config/gtk-3.0
+cp -f /tmp/settings.ini /home/$(cat /home.txt)/.config/gtk-3.0/
+cp -f /tmp/rc.xml /home/$(cat /home.txt)/.config/openbox/
+mkdir /home/$(cat /home.txt)/.config/conky
+cp -f /tmp/conky.conf /home/$(cat /home.txt)/.config/conky/
+rm -f /tmp/conky.conf
+chmod ugo=rw /home/$(cat /home.txt)/.config/conky/conky.conf
+chmod ugo=rw /home/$(cat /home.txt)/.config/openbox/rc.xml
+chmod ugo=rw /home/$(cat /home.txt)/.config/gtk-3.0/settings.ini
+git clone https://github.com/addy-dclxvi/openbox-theme-collections.git /temp
+rm -rf /temp/.git
+mv -f /temp/* /usr/share/themes/
+rm -rf /temp
+git clone https://github.com/addy-dclxvi/gtk-theme-collections.git /temp2
+rm -rf /temp2/.git
+mv -f /temp2/* /usr/share/themes/
+rm -rf /temp2
+mv -f /tmp/settings.ini /usr/share/gtk-3.0/
+mv -f /tmp/rc.xml /etc/xdg/openbox/
+rm -f /etc/xdg/autostart/obmenu-gen.desktop
+sudo -u $(cat /home.txt) obmenu-generator -p -i
+sudo -u $(cat /home.txt) openbox --reconfigure
+yad --title "Conky" --height=100 --width=300 --center --text="Your Openbox setup has now been fully configured! To experience the improvements fully, please log out and then back in. Thank you, and enjoy!" --text-align=center
+sudo -u root rm -f /home.txt
+else exec /usr/bin/openbox-config.sh
+fi
diff --git a/scripts/pkcon.sh b/scripts/pkcon.sh
new file mode 100755 (executable)
index 0000000..61ffac5
--- /dev/null
@@ -0,0 +1,44 @@
+#  Copyright © 2016-2019 Reborn OS
+#
+#  This file is part of Reborn OS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+wget --spider www.google.com
+if [ "$?" = 0 ]; then
+
+flatpak remote-add flathub https://dl.flathub.org/repo/flathub.flatpakrepo
+
+pkcon refresh
+
+pkexec rm /usr/share/applications/flatpak.desktop
+
+if [ -f /usr/bin/gnome-software ]; then
+gnome-software
+echo "STARTING GNOME-SOFTWARE"
+fi
+
+if [ -f /usr/bin/plasma-discover ]; then
+plasma-discover
+echo "STARTING PLASMA-DISCOVER"
+fi
+
+else exec /usr/bin/pkcon.sh
+fi
diff --git a/scripts/pkcon2.sh b/scripts/pkcon2.sh
new file mode 100755 (executable)
index 0000000..37758ec
--- /dev/null
@@ -0,0 +1,32 @@
+#  Copyright © 2016-2019 Reborn OS
+#
+#  This file is part of Reborn OS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+wget --spider www.google.com
+if [ "$?" = 0 ]; then
+
+pkexec rm /etc/xdg/autostart/update.desktop
+
+killall -9 yad
+
+else exec /usr/bin/pkcon2.sh
+fi
diff --git a/scripts/plymouth-reborn.desktop b/scripts/plymouth-reborn.desktop
new file mode 100644 (file)
index 0000000..918569c
--- /dev/null
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Application
+Name=Setting up Plymouth
+Exec=yadsu /usr/bin/plymouth.sh
+Name[en_US]=plymouth-reborn
diff --git a/scripts/plymouth.sh b/scripts/plymouth.sh
new file mode 100755 (executable)
index 0000000..2f43f34
--- /dev/null
@@ -0,0 +1,47 @@
+#  Copyright © 2016-2019 Reborn OS
+#
+#  This file is part of Reborn OS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+#!/bin/bash
+echo $(lspci -v | grep -A10 VGA | grep driver | awk '{print $5}') >/tmp/plymouth.txt
+DEVICE=$(sed '1q;d' /tmp/plymouth.txt)
+#echo $(grep -n "GRUB_CMDLINE_LINUX_DEFAULT" /etc/default/grub | grep -Eo '^[^:]+')s >/tmp/line.txt
+#LINE1=$(sed '1q;d' /tmp/line.txt)
+#SWAP_UUID=$(sudo blkid /dev/sd* | grep "swap" | awk '{print $2}' | sed 's/\"//g')
+#echo $(cat /etc/mkinitcpio.conf | grep "keyboard keymap") >/tmp/hooks.txt
+#HOOK=sed -n 's/.*base udev //p' /tmp/hooks.txt
+#echo $(grep -n "keyboard keymap" /etc/mkinitcpio.conf | grep -Eo '^[^:]+')s >/tmp/line2.txt
+#LINE2=$(sed '1q;d' /tmp/line2.txt)
+#sed -i "$LINE2|.*|HOOKS=base udev plymouth $HOOK|" /etc/mkinitcpio.conf
+# sed -i "$LINE1|.*|GRUB_CMDLINE_LINUX_DEFAULT=quiet splash resume=$SWAP_UUID|" /etc/default/grub
+echo $(grep -n "MODULES=" /etc/mkinitcpio.conf | grep -Eo '^[^:]+')s | awk '{print $2}' >/tmp/line3.txt
+LINE3=$(sed '1q;d' /tmp/line3.txt)
+sed -i "$LINE3|.*|MODULES=\"$DEVICE\"|" /etc/mkinitcpio.conf
+plymouth-set-default-theme -R arch-charge-big
+mkinitcpio -p linux
+grub-mkconfig -o /boot/grub/grub.cfg
+rm -f /tmp/line.txt
+rm -f /tmp/line2.txt
+rm -f /tmp/plymouth.txt
+rm -f /tmp/modules.txt
+rm -f /tmp/hooks.txt
+rm -f /etc/xdg/autostart/plymouth-reborn.desktop
diff --git a/scripts/reflector-antergos b/scripts/reflector-antergos
new file mode 100755 (executable)
index 0000000..9e3db4a
--- /dev/null
@@ -0,0 +1,589 @@
+#!/bin/bash
+#
+# Generates to stdout a ranked Antergos mirror list that can be directly used as
+# file /etc/pacman.d/antergos-mirrorlist.
+#
+# For optimizing performance
+# - while searching the mirrors with this tool
+# AND
+# - while updating your system using the ranked mirrors
+# you may exclude mirror countries (option --exclude-countries)
+# or exclude groups of countries (--exclude-countrygroups), or individual mirrors (--exclude-mirrors).
+#
+# You can specify which protocols (https, http) are used (option --protocols).
+#
+# For more details, see below.
+#
+# Installation:
+# - Copy this script into a file (e.g. /usr/bin/reflector-antergos or /usr/local/bin/reflector-antergos)
+# and make it executable (e.g. chmod +x /usr/bin/reflector-antergos).
+#
+# NOTE1: for option --exclude-countries, in addition to country names you can
+# give "reserved" word 'Automated'. The Automated excludes all
+# automated mirrors (usually one or two).
+# Single mirrors (URLs) can be excluded with option --exclude-mirrors.
+#
+# NOTE2: it is possible to manually edit and re-order mirrors
+# in /etc/pacman.d/antergos-mirrorlist.
+#
+# Usage example:
+# reflector-antergos --exclude-countrygroup=Asia2,Europe > ~/antergos-mirrorlist
+# sudo mv -f /etc/pacman.d/antergos-mirrorlist /etc/pacman.d/antergos-mirrorlist.bak # optional: backup old file
+# sudo mv -f ~/antergos-mirrorlist /etc/pacman.d/antergos-mirrorlist
+#
+# TODO:
+# - Only http and https protocols are currently supported.
+#
+# Caveats:
+# - This script assumes $INPUTFILE (antergos-mirrorlist, see function GetLatestInputFile below)
+# is written using a certain format.
+# If the format changes, this script must be modified accordingly
+# (which may or may not be trivial).
+#
+
+OUTPUTFILE=/etc/pacman.d/antergos-mirrorlist
+INPUTFILE="" # the latest available antergos-mirrorlist, downloaded!
+
+EXCLUDEDCOUNTRYGROUPS=""
+EXCLUDEDCOUNTRIES=""
+EXCLUDEDMIRRORS=""
+PROTOCOLS="http" # default protocol
+INCLUDEDCOUNTRIES=""
+
+REPO=antergos
+ARCH=$(uname -m)
+
+TMPDIR="$HOME/.rankingstuff.tmp.$$"
+#TMPDIR="/tmp/.rankingstuff.tmp.$$"
+RANKEDMIRRORS="$TMPDIR/rankedmirrors"
+INSTALLER="$TMPDIR/installer.bash"
+
+REFLECTOR_ANTERGOS_CONF=/etc/reflector-antergos.conf # may contain e.g. REFERENCEURL
+if [ -f $REFLECTOR_ANTERGOS_CONF ] ; then
+source $REFLECTOR_ANTERGOS_CONF
+fi
+if [ "$REFERENCEURL" = "" ] ; then
+REFERENCEURL=http://mirrors.antergos.com/$REPO/$ARCH/main.files.tar.gz
+fi
+
+REFERENCEFILE="$TMPDIR/referencefile"
+REFERENCESTAMP=0 # timestamp
+
+FOUND_MIRRORS=0
+SAVE_TO_FILE=false # false=output to standard output, true=to /etc/pacman.d/antergos-mirrorlist
+
+HIDE_ORIGINAL_MIRRORS=yes # Controls how the original mirror list is appended after ranked mirrors.
+# yes=hides original mirrors, no=don't hide original mirrors.
+
+VERSION_STUB="$Id: reflector-antergos,v 1.34 2018/06/20 14:38:01 manuel Exp $"
+VERSION=0.$(echo "$VERSION_STUB" | awk '{print $3}')
+
+VERBOSE=1
+DEBUG=0
+
+TIME_CMD=/usr/bin/time
+WGET_CMD=/usr/bin/wget
+
+WGET_TIMEOUT=30 # seconds to wait for the mirror to respond
+
+echo2() { echo "$@" >&2 ; }
+verbose2() { test $VERBOSE -ne 0 && echo2 "$@" ; }
+verbose2more() { test $VERBOSE -eq 2 && echo2 "$@" ; }
+debug2() { test $DEBUG -eq 1 && echo2 "$@" ; }
+printf2()
+{
+local fmt="$1"
+shift
+printf "$fmt" "$@" >&2
+}
+
+_runcheck()
+{
+"$@"
+if [ $? -ne 0 ] ; then
+echo2 "ERROR: command '$*' failed."
+exit 1
+fi
+}
+
+Usage()
+{
+case $VERBOSE in
+1|2) cat <<EOF
+Overview:
+
+reflector-antergos generates (to stdout) a ranked Antergos mirror list
+that can be directly used as file /etc/pacman.d/antergos-mirrorlist.
+
+Version: $VERSION
+
+Usage: $(basename $0) [options]
+where 'options' are:
+--exclude-countries=X,Y,Z,...
+Exclude all mirrors from given countries.
+Note1: If a country name includes spaces, e.g. Czech Republic,
+convert spaces into underscores, e.g. Czech_Republic.
+Note2: The word 'Automated' excludes all mirrors from section
+'Automated Mirror Selection'.
+
+--exclude-countrygroups=X,Y,Z,...
+Exclude countries using group names: America, Asia, Asia2, Europe, Europe2
+where Asia2 and Europe2 include Russia.
+
+--exclude-mirrors=X,Y,Z,...
+Exclude mirror urls that contain any of strings X, Y or Z as substring.
+
+--include-countries=X,Y,Z,...
+The opposite of --exclude-countries.
+Note that this option cannot be used together with options to
+exclude countries.
+
+--save
+Saves the ranked mirror list to file /etc/pacman.d/antergos-mirrorlist
+and makes a backup of the previous file.
+Note that usage of this option requires root permissions.
+If this option is not used, the ranked mirror list is printed to standard output.
+
+--hide-original-mirrors=X
+As the original mirror list is appended to the ranking result,
+this option controls whether original mirrors are hidden or not.
+X=yes hides original mirrors (default).
+X=no does not hide them.
+Note that the original mirror list may contain some out-of-order mirrors.
+
+--protocols=X,Y,...
+Repo protocols in preference order (currently only https and http
+are supported).
+
+--timeout=seconds
+Max time in seconds to wait for a mirror response. Default is $WGET_TIMEOUT.
+
+--repo=X
+X defaults to 'antergos'
+
+--arch=X
+X defaults to $(uname -m)
+
+--quiet
+Don't show progress info.
+
+--verbose
+Show more details about operation
+
+--help
+-h
+This help.
+
+Example:
+$(basename $0) --exclude-countries=China,Russia,USA,Japan,Automated \\
+--exclude-mirrors=sourceforge,mirrors.tuxns,antergos-mirror-a.alpix \\
+--protocols=https,http
+
+EOF
+;;
+esac
+}
+
+
+wget_error()
+{
+# return as one word!
+case "$1" in
+0) echo "ok" ;;
+1) echo "generic_error" ;;
+2) echo "parse_error" ;;
+3) echo "file_i/o_error" ;;
+4) echo "network_failure" ;;
+5) echo "ssl_verification_failure" ;;
+6) echo "username/password_authentication_failure" ;;
+7) echo "protocol_error" ;;
+8) echo "server_issued_error" ;;
+*) echo "(unknown_error)" ;;
+esac
+}
+
+RankMirrorProtocol()
+{
+local mirrorarg="$1"
+local country="$2"
+local mirror=$(echo "$mirrorarg" | sed -e 's|\$repo|'$REPO'|' -e 's|\$arch|'$ARCH'|')
+local mirrorurl="$mirror/$(basename $REFERENCEURL)"
+local mirrorfile="$TMPDIR/mirrorfile"
+local mirrordata="$TMPDIR/mirrordata"
+local result
+local downloadtime=-1
+local status=ok
+local timestamp
+local xx
+local hide
+
+for xx in $EXCLUDEDMIRRORS
+do
+if [[ "$mirror" =~ "$xx" ]] ; then
+return
+fi
+done
+
+verbose2 " $mirrorarg"
+
+rm -f "$mirrorfile"
+
+$TIME_CMD -f %e $WGET_CMD --timeout=$WGET_TIMEOUT --tries=1 -q -O "$mirrorfile" "$mirrorurl" 2>"$mirrordata" 1>/dev/null
+result=$?
+
+if [ $result -eq 0 ] ; then
+downloadtime=$(cat "$mirrordata" | tr '.' ',')
+timestamp=$(stat -c %Y "$mirrorfile")
+
+#verbose2 "vertailu: $timestamp $REFERENCESTAMP"
+if [[ "$timestamp" < "$REFERENCESTAMP" ]] ; then
+status="outdated"
+result=1
+elif [ "$(cmp "$mirrorfile" "$REFERENCEFILE")" != "" ] ; then
+status="contents_differ"
+result=1
+fi
+else
+status=$(wget_error $result) # file download problem
+fi
+
+if [ "$status" != "ok" ] ; then
+hide="#"
+
+echo2 "status = $status"
+case "$status" in
+outdated) echo2 "timestamp = $timestamp refstamp = $REFERENCESTAMP" ;;
+*) ;;
+esac
+else
+((FOUND_MIRRORS++))
+fi
+printf "%sServer = %s ############# downloadtime: %5s status: %s %s\n" \
+"$hide" "$mirrorarg" "$downloadtime" "$status" "$country" >> "$RANKEDMIRRORS"
+
+return $result
+}
+
+RankMirror()
+{
+local mirror="$1"
+local country="$2"
+local protocol
+local m
+
+for protocol in $PROTOCOLS
+do
+m=$(echo "$mirror" | sed 's|^[a-z]*://|'$protocol'://|')
+RankMirrorProtocol "$m" "$country"
+done
+}
+
+RankCountryMirrors()
+{
+local country="$1"
+local mirror
+local mirrors
+
+if [ "$country" = "Automated" ] ; then
+mirrors=$(grep -A 5 "^# [ ]*Automated Mirror" "$INPUTFILE" | grep "^Server = " | awk '{print $3}')
+else
+local country2=$(echo $country | sed 's|_.*$||') # remove first underscore (and tail) that may have been put there
+country=$(echo $country | sed 's|_| |g') # restore the original country name!
+mirrors=$(sed -n '/# '$country2'/,/^$/ p' $INPUTFILE | sed '1d;$d' | awk '{print $3}')
+fi
+
+verbose2 "$country:"
+for mirror in $mirrors
+do
+RankMirror "$mirror" "$country"
+done
+}
+
+SortMirrors()
+{
+local protocol
+
+mv "$RANKEDMIRRORS" "$RANKEDMIRRORS".tmp
+
+for protocol in $PROTOCOLS
+do
+grep "Server = $protocol://" "$RANKEDMIRRORS".tmp | sort -g -k 6 >> "$RANKEDMIRRORS"
+done
+rm -f "$RANKEDMIRRORS".tmp
+}
+
+RemoveCommentsOnServerLines()
+{
+mv "$RANKEDMIRRORS" "$RANKEDMIRRORS".tmp
+cat "$RANKEDMIRRORS".tmp | sed 's|/\$arch[ ]*#|/\$arch\n#|' > "$RANKEDMIRRORS"
+rm -f "$RANKEDMIRRORS".tmp
+}
+
+AddHeaderToFile()
+{
+local program=$(basename $0)
+
+mv "$RANKEDMIRRORS" "$RANKEDMIRRORS".tmp
+echo "## Generated by $program (version $VERSION) at $(date)." >> "$RANKEDMIRRORS"
+echo "##" >> "$RANKEDMIRRORS"
+echo "## Options used: $COMMAND_LINE" >> "$RANKEDMIRRORS"
+echo "##" >> "$RANKEDMIRRORS"
+cat "$RANKEDMIRRORS".tmp >> "$RANKEDMIRRORS"
+if [ -f "$INPUTFILE" ] ; then
+echo "" >> "$RANKEDMIRRORS"
+echo "" >> "$RANKEDMIRRORS"
+echo "#############################################################" >> "$RANKEDMIRRORS"
+echo "############## Original antergos-mirrorlist: ################" >> "$RANKEDMIRRORS"
+echo "#############################################################" >> "$RANKEDMIRRORS"
+echo "" >> "$RANKEDMIRRORS"
+case "$HIDE_ORIGINAL_MIRRORS" in
+yes|y|Y|true|on|1)
+cat "$INPUTFILE" | sed -e 's|^#Server = |##Server = |' -e 's|^Server = |#Server = |' >> "$RANKEDMIRRORS" # original mirrors commented out
+;;
+*)
+cat "$INPUTFILE" >> "$RANKEDMIRRORS"
+;;
+esac
+fi
+rm -f "$RANKEDMIRRORS".tmp
+}
+
+ShowMirrorlist()
+{
+case "$SAVE_TO_FILE" in
+true)
+sudo mv -f "$OUTPUTFILE" "$OUTPUTFILE".bak
+sudo cp -f "$RANKEDMIRRORS" "$OUTPUTFILE"
+;;
+false)
+cat "$RANKEDMIRRORS"
+;;
+esac
+}
+
+RankAllMirrors() # this mainly controls the operation
+{
+# If a country name for allcountries includes spaces, they are turned to underscores.
+# Note that this implementation handles max 3 words in a country name.
+# If more words can exist on a country name, change the awk command on the next line.
+local allcountries=$(grep "^# [A-Z]" "$INPUTFILE" | awk '{print $2"_"$3"_"$4}' | sed 's|[_]*$||')
+local automatedmirrors=$(grep -A 5 "^# [ ]*Automated Mirror" "$INPUTFILE" | grep "^Server = " | awk '{print $3}')
+local country mirror
+
+rm -f "$RANKEDMIRRORS"
+
+if [ -n "$INCLUDEDCOUNTRIES" ] ; then
+# only the included countries are ranked
+for country in $INCLUDEDCOUNTRIES
+do
+RankCountryMirrors "$country"
+done
+else
+if [[ ! "$EXCLUDEDCOUNTRIES" =~ "Automated" ]] ; then
+allcountries+=" Automated"
+else
+printf2 "Automated Mirrors:\n <excluded>\n"
+fi
+
+for country in $allcountries
+do
+if [[ ! "$EXCLUDEDCOUNTRIES" =~ "$country" ]] ; then
+RankCountryMirrors "$country"
+else
+printf2 "$(echo $country | sed 's|_| |g'):\n <excluded>\n"
+fi
+done
+fi
+
+if [ $FOUND_MIRRORS -eq 0 ] ; then
+echo2 ""
+echo2 -n "Fail: no good mirrors currently found! "
+if [ "$EXCLUDEDCOUNTRIES" = "" ] ; then
+echo2 "Please try again later."
+else
+echo2 "Please try again later, or reduce your list of excluded countries."
+fi
+return 1
+else
+echo2 "$FOUND_MIRRORS mirrors found."
+fi
+
+SortMirrors
+AddHeaderToFile
+RemoveCommentsOnServerLines
+ShowMirrorlist
+}
+
+GetLatestInputFile()
+{
+# Download the latest available antergos-mirrorlist as a reference input file.
+
+echo2 -n "Downloading latest mirror info ... "
+_runcheck pushd "$TMPDIR" >/dev/null
+
+local filelist=fileinfo-$$.html
+_runcheck wget -q -O "$filelist" https://repo.antergos.info/antergos/x86_64/
+
+local inputfile=$(cat "$filelist" | sed 's|</a>|</a>\n|g' | grep "antergos-mirrorlist" | grep -v "xz\.sig" | sed 's|^.*>\(antergos\-mirrorlist\-[0-9\-]*\-any\.pkg\.tar\.xz\)<.*$|\1|')
+rm -f "$filelist"
+
+_runcheck wget -q https://repo.antergos.info/antergos/x86_64/"$inputfile"
+_runcheck tar xf "$inputfile"
+
+if [ -f etc/pacman.d/antergos-mirrorlist ] ; then
+mv etc/pacman.d/antergos-mirrorlist .
+rm -rf etc
+rm -f "$inputfile" .BUILDINFO .MTREE .PKGINFO
+else
+echo2 "Error: file antergos-mirrorlist not found in downloaded package '$PWD/$inputfile'!"
+exit 1
+fi
+
+_runcheck popd >/dev/null
+
+echo2 "done."
+
+INPUTFILE="$TMPDIR/antergos-mirrorlist"
+
+if [ "$(cat "$INPUTFILE")" = "" ] ; then
+echo2 "Error: your '$INPUTFILE' is empty??"
+exit 1
+fi
+}
+
+GetReferenceFiles()
+{
+# make sure we have a reference file to read!
+GetLatestInputFile
+
+$WGET_CMD -q -O "$REFERENCEFILE" $REFERENCEURL
+REFERENCESTAMP=$(stat -c %Y "$REFERENCEFILE")
+}
+
+CleanAll()
+{
+rm -rf ${TMPDIR%.*}.*
+}
+
+AddToExcludedCountries()
+{
+local america="Canada USA"
+local asia="China Japan"
+local europe="Bulgaria Czech Denmark France England Germany Greece Netherlands Portugal Spain Sweden"
+
+local xx
+for xx in $EXCLUDEDCOUNTRYGROUPS
+do
+case "$xx" in
+America) EXCLUDEDCOUNTRIES+=" $america" ;;
+Asia) EXCLUDEDCOUNTRIES+=" $asia" ;;
+Asia2) EXCLUDEDCOUNTRIES+=" $asia Russia" ;;
+Europe) EXCLUDEDCOUNTRIES+=" $europe" ;;
+Europe2) EXCLUDEDCOUNTRIES+=" $europe Russia" ;;
+*)
+echo2 "Warning: word '$xx' is not recognized as a country group."
+;;
+esac
+done
+}
+
+HandleOptions()
+{
+local arg
+local doexit=0
+
+for arg in "$@"
+do
+case "$arg" in
+--help|-h)
+CleanAll ; Usage ; doexit=1
+;;
+--quiet)
+VERBOSE=0
+;;
+--verbose)
+VERBOSE=2
+;;
+--debug)
+DEBUG=1
+;;
+--protocols=*)
+PROTOCOLS="$(echo ${arg:12} | tr ',' ' ')"
+;;
+--exclude-countries=*)
+EXCLUDEDCOUNTRIES+=" $(echo ${arg:20} | tr ',' ' ')"
+;;
+--exclude-countrygroups=*)
+EXCLUDEDCOUNTRYGROUPS="$(echo ${arg:24} | tr ',' ' ')"
+AddToExcludedCountries
+;;
+--include-countries=*)
+INCLUDEDCOUNTRIES+=" $(echo ${arg:20} | tr ',' ' ')"
+;;
+--save)
+SAVE_TO_FILE=true
+;;
+--exclude-mirrors=*)
+EXCLUDEDMIRRORS="$(echo ${arg:18} | tr ',' ' ')"
+;;
+--hide-original-mirrors=*)
+HIDE_ORIGINAL_MIRRORS="${arg:24}"
+;;
+--timeout=*)
+WGET_TIMEOUT="${arg:10}"
+;;
+--repo=*)
+REPO=$(echo ${arg:7})
+;;
+--arch=*)
+ARCH=$(echo ${arg:7})
+;;
+esac
+done
+if [ -n "$INCLUDEDCOUNTRIES" ] && [ -n "$EXCLUDEDCOUNTRIES" ] ; then
+echo2 "Sorry, options to both include and exclude countries cannot be used together."
+echo2 "Note however that it is allowed to include countries but exclude mirrors."
+doexit=1
+fi
+
+debug2 "Command line: $0 $@"
+
+verbose2more "EXCLUDEDCOUNTRYGROUPS = $EXCLUDEDCOUNTRYGROUPS"
+verbose2more "EXCLUDEDCOUNTRIES = $EXCLUDEDCOUNTRIES"
+verbose2more "EXCLUDEDMIRRORS = $EXCLUDEDMIRRORS"
+verbose2more "INCLUDEDCOUNTRIES = $INCLUDEDCOUNTRIES"
+verbose2more "PROTOCOLS = $PROTOCOLS"
+verbose2more "WGET_TIMEOUT = $WGET_TIMEOUT"
+
+test $doexit -eq 1 && exit 0
+}
+
+CheckRequirements()
+{
+# check required programs
+if [ ! -x $TIME_CMD ] ; then
+echo2 "Sorry, you need to install package 'time' first!"
+echo2 "pacman -S --needed time"
+exit 1
+fi
+if [ ! -x $WGET_CMD ] ; then
+echo2 "Sorry, you need to install package 'wget' first!"
+echo2 "pacman -S --needed wget"
+exit 1
+fi
+
+mkdir -p "$TMPDIR"
+}
+
+StartHere()
+{
+COMMAND_LINE="$*"
+
+CheckRequirements
+HandleOptions "$@"
+GetReferenceFiles
+RankAllMirrors
+CleanAll
+}
+
+StartHere "$@"
diff --git a/scripts/tint2-start.desktop b/scripts/tint2-start.desktop
new file mode 100644 (file)
index 0000000..d44986a
--- /dev/null
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Application
+Name=Autostarting tint2 for Openbox
+Exec=tint2
+Name[en_US]=tint2-start
diff --git a/scripts/update.desktop b/scripts/update.desktop
new file mode 100644 (file)
index 0000000..e4ffa92
--- /dev/null
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Application
+Name=Flatpak Support
+Exec=flatpak.sh
+Icon=/usr/share/icons/default/reborn-updates.svg
+NoDisplay=true
diff --git a/set_password b/set_password
new file mode 100644 (file)
index 0000000..8ddb3bd
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+export password=reborn
+
+{ autopasswd reborn reborn && exit 0; } || exit 1
diff --git a/syslinux/archiso.cfg b/syslinux/archiso.cfg
new file mode 100644 (file)
index 0000000..40d8b34
--- /dev/null
@@ -0,0 +1,11 @@
+DEFAULT select
+
+LABEL select
+COM32 boot/syslinux/whichsys.c32
+APPEND -pxe- pxe -sys- sys -iso- sys
+
+LABEL pxe
+CONFIG boot/syslinux/archiso_pxe.cfg
+
+LABEL sys
+CONFIG boot/syslinux/archiso_sys.cfg
diff --git a/syslinux/archiso_head.cfg b/syslinux/archiso_head.cfg
new file mode 100644 (file)
index 0000000..c381aa3
--- /dev/null
@@ -0,0 +1,25 @@
+SERIAL 0 38400
+UI boot/syslinux/vesamenu.c32
+MENU TITLE RebornOS
+MENU BACKGROUND boot/syslinux/splash.png
+
+MENU WIDTH 78
+MENU MARGIN 4
+MENU ROWS 7
+MENU VSHIFT 10
+MENU TABMSGROW 14
+MENU CMDLINEROW 14
+MENU HELPMSGROW 16
+MENU HELPMSGENDROW 29
+
+# Refer to http://syslinux.zytor.com/wiki/index.php/Doc/menu
+
+MENU COLOR border       30;44   #40ffffff #a0000000 std
+MENU COLOR title        1;36;44 #9033ccff #a0000000 std
+MENU COLOR sel          7;37;40 #e0ffffff #20ffffff all
+MENU COLOR unsel        37;44   #50ffffff #a0000000 std
+MENU COLOR help         37;40   #c0ffffff #a0000000 std
+MENU COLOR timeout_msg  37;40   #80ffffff #00000000 std
+MENU COLOR timeout      1;37;40 #c0ffffff #00000000 std
+MENU COLOR msg07        37;40   #90ffffff #a0000000 std
+MENU COLOR tabmsg       31;40   #30ffffff #00000000 std
diff --git a/syslinux/archiso_pxe.cfg b/syslinux/archiso_pxe.cfg
new file mode 100644 (file)
index 0000000..3a90fbd
--- /dev/null
@@ -0,0 +1,36 @@
+INCLUDE boot/syslinux/archiso_head.cfg
+
+LABEL arch64_nbd
+TEXT HELP
+Boot the RebornOS Live to install (Using NBD).
+It allows you to install Arch Linux or perform system maintenance.
+ENDTEXT
+MENU LABEL Boot RebornOS (after install) (NBD)
+LINUX boot/x86_64/vmlinuz
+INITRD boot/intel_ucode.img,boot/x86_64/archiso.img
+APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% archiso_nbd_srv=${pxeserver}
+SYSAPPEND 3
+
+LABEL arch64_nfs
+TEXT HELP
+Boot the Arch Linux (x86_64) live medium (Using NFS).
+It allows you to install Arch Linux or perform system maintenance.
+ENDTEXT
+MENU LABEL Boot RebornOS Live to install (NFS)
+LINUX boot/x86_64/vmlinuz
+INITRD boot/intel_ucode.img,boot/x86_64/archiso.img
+APPEND archisobasedir=%INSTALL_DIR% archiso_nfs_srv=${pxeserver}:/run/archiso/bootmnt
+SYSAPPEND 3
+
+LABEL arch64_http
+TEXT HELP
+Boot the Arch Linux (x86_64) live medium (Using HTTP).
+It allows you to install Arch Linux or perform system maintenance.
+ENDTEXT
+MENU LABEL Boot RebornOS Live to install (HTTP)
+LINUX boot/x86_64/vmlinuz
+INITRD boot/intel_ucode.img,boot/x86_64/archiso.img
+APPEND archisobasedir=%INSTALL_DIR% archiso_http_srv=http://${pxeserver}/
+SYSAPPEND 3
+
+INCLUDE boot/syslinux/archiso_tail.cfg
diff --git a/syslinux/archiso_sys.cfg b/syslinux/archiso_sys.cfg
new file mode 100644 (file)
index 0000000..4b1a348
--- /dev/null
@@ -0,0 +1,13 @@
+INCLUDE boot/syslinux/archiso_head.cfg
+
+LABEL arch64
+TEXT HELP
+Boot RebornOS Live to install it.
+It allows you to install RebornOS or perform system maintenance.
+ENDTEXT
+MENU LABEL Boot RebornOS (x86_64)
+LINUX boot/x86_64/vmlinuz
+INITRD boot/intel_ucode.img,boot/x86_64/archiso.img
+APPEND archisobasedir=%INSTALL_DIR% cow_spacesize=10G archisolabel=%ARCHISO_LABEL%
+
+INCLUDE boot/syslinux/archiso_tail.cfg
diff --git a/syslinux/archiso_tail.cfg b/syslinux/archiso_tail.cfg
new file mode 100644 (file)
index 0000000..bc0717b
--- /dev/null
@@ -0,0 +1,37 @@
+LABEL existing
+TEXT HELP
+Boot an existing operating system.
+Press TAB to edit the disk and partition number to boot.
+ENDTEXT
+MENU LABEL Boot RebornOS (after install)
+COM32 boot/syslinux/chain.c32
+APPEND hd0 0
+
+LABEL Nvidia
+TEXT HELP
+Boot RebornOS with NVIDIA support.
+ENDTEXT
+MENU LABEL NVIDIA Reborn
+LINUX boot/x86_64/vmlinuz
+INITRD boot/intel_ucode.img,boot/x86_64/archiso.img
+APPEND archisobasedir=%INSTALL_DIR% cow_spacesize=10G earlymodules=loop modules-load=loop rd.modules-load=loop nohibernate quiet nomodeset nouveau.modeset=0 modprobe.blacklist=nouveau modprobe.driver-nvidia udev.log-priority=crit rd.udev.log-priority=crit      archisolabel=%ARCHISO_LABEL%
+
+
+# http://www.memtest.org/
+LABEL memtest
+MENU LABEL Run Memtest86+ (RAM test)
+LINUX boot/memtest
+
+# http://hdt-project.org/
+LABEL hdt
+MENU LABEL Hardware Information (HDT)
+COM32 boot/syslinux/hdt.c32
+APPEND modules_alias=boot/syslinux/hdt/modalias.gz pciids=boot/syslinux/hdt/pciids.gz
+
+LABEL reboot
+MENU LABEL Reboot
+COM32 boot/syslinux/reboot.c32
+
+LABEL poweroff
+MENU LABEL Power Off
+COM32 boot/syslinux/poweroff.c32
diff --git a/syslinux/splash(copy).png b/syslinux/splash(copy).png
new file mode 100644 (file)
index 0000000..c08185e
Binary files /dev/null and b/syslinux/splash(copy).png differ
diff --git a/syslinux/splash.png b/syslinux/splash.png
new file mode 100644 (file)
index 0000000..f811e6b
Binary files /dev/null and b/syslinux/splash.png differ
diff --git a/syslinux/syslinux.cfg b/syslinux/syslinux.cfg
new file mode 100644 (file)
index 0000000..3ee98de
--- /dev/null
@@ -0,0 +1,5 @@
+DEFAULT loadconfig
+
+LABEL loadconfig
+  CONFIG archiso.cfg
+  APPEND ../../
diff --git a/test-cnchi.sh b/test-cnchi.sh
new file mode 100755 (executable)
index 0000000..2e43094
--- /dev/null
@@ -0,0 +1,441 @@
+#  Copyright © 2016-2019 RebornOS
+#
+#  This file is part of RebornOS.
+#
+#  Reborn OS 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.
+#
+#  Reborn OS 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 Reborn OS; If not, see <http://www.gnu.org/licenses/>.
+
+#!/bin/bash
+##############################
+# Script to download and install Cnchi #
+##############################
+
+
+# Defining Variables
+export CNCHI_GIT_BRANCH="0.16.21"
+export CNCHI_GIT_URL="https://github.com/Antergos/Cnchi/archive/${CNCHI_GIT_BRANCH}.zip"
+export script_path="/usr/share"
+export REBORN="/usr/share/cnchi/reborn"
+
+QUESTION(){
+echo
+echo "Please select your preferred course of action:"
+echo
+options=("Remove all instances of Cnchi from my system" "Build Cnchi" "Quit")
+select opt in "${options[@]}"
+do
+    case $opt in
+        "Remove all instances of Cnchi from my system")
+            REMOVE;break;;
+        "Build Cnchi")
+            RUN;break;;
+        "Quit")
+            break;;
+        *) echo "ERROR!!! ERROR!!!! SOUND THE ALARM!!!"
+            echo "Sadly, option $REPLY is not possible! Please select either option 1, 2, or 3 instead. Thank you!";;
+    esac
+done
+}
+
+# Removing Cnchi files if they exist
+REMOVE(){
+echo
+echo "REMOVING ALL INSTALLED INSTANCES OF CNCHI..."
+if [ -f /usr/share/cnchi/bin/cnchi ]; then
+rm -rf /usr/share/cnchi
+rm -rf /var/log/cnchi
+rm -f /usr/bin/cnchi
+rm -f /usr/bin/cnchi-start.sh
+rm -f /usr/share/applications/antergos-install.desktop
+rm -f /usr/share/applications/cnchi.desktop
+rm -f /usr/share/pixmaps/cnchi.png
+fi
+echo "DONE"
+}
+# Downloading and installing Cnchi
+INSTALL(){
+echo
+echo "#########################################################"
+echo "########## DOWNLOADING & INSTALLING CNCHI... ############"
+echo "#########################################################"
+echo
+    wget "${CNCHI_GIT_URL}" -O ${script_path}/cnchi-git.zip
+    unzip ${script_path}/cnchi-git.zip -d ${script_path}
+    rm -f ${script_path}/cnchi-git.zip
+    CNCHI_SRC="${script_path}/Cnchi-${CNCHI_GIT_BRANCH}"
+        install -d ${script_path}/{cnchi,locale}
+       install -Dm755 "${CNCHI_SRC}/bin/cnchi" "/usr/bin/cnchi"
+         echo
+         echo "COPIED STARTUP FILE OVER"
+         echo
+       install -Dm755 "${CNCHI_SRC}/cnchi.desktop" "/usr/share/applications/cnchi.desktop"
+         echo
+         echo "COPIED DESKTOP FILE OVER"
+         echo
+       install -Dm644 "${CNCHI_SRC}/data/images/antergos/antergos-icon.png" "/usr/share/pixmaps/cnchi.png"
+         echo
+         echo "COPIED CNCHI ICON OVER"
+         echo
+    # TODO: This should be included in Cnchi's src code as a separate file
+    # (as both files are needed to run cnchi)
+    sed -r -i 's|\/usr.+ -v|pkexec /usr/share/cnchi/bin/cnchi -s bugsnag|g' "/usr/bin/cnchi"
+    echo
+    echo "MODIFIED STARTUP COMMAND FOR CNCHI"
+    echo
+    for i in ${CNCHI_SRC}/src ${CNCHI_SRC}/bin ${CNCHI_SRC}/data ${CNCHI_SRC}/scripts ${CNCHI_SRC}/ui; do
+        cp -R ${i} "${script_path}/cnchi/"
+        echo
+        echo "COPIED MAIN CNCHI'S SUBDIRECTORIES OVER TO BUILD FOLDER"
+        echo
+    done
+    for files in ${CNCHI_SRC}/po/*; do
+        if [ -f "$files" ] && [ "$files" != 'po/cnchi.pot' ]; then
+            STRING_PO=`echo ${files#*/}`
+            STRING=`echo ${STRING_PO%.po}`
+            mkdir -p /usr/share/locale/${STRING}/LC_MESSAGES
+            msgfmt $files -o /usr/share/locale/${STRING}/LC_MESSAGES/cnchi.mo
+            echo "${STRING} installed..."
+            echo "CNCHI IS NOW BUILT"
+        fi
+    done
+rm -rf ${script_path}/Cnchi-${CNCHI_GIT_BRANCH}
+}
+
+# Ask whether or not to use experimental files for Cnchi
+ASK(){
+echo
+echo
+echo "Please select your preferred course of action:"
+echo
+options=("Use our official Cnchi files" "Do not use modified Cnchi files" "Use your own files instead" "Setup System for Reborn installation" "Quit")
+select opt in "${options[@]}"
+do
+    case $opt in
+        "Use our official Cnchi files")
+            DOWNLOAD_SIMPLE;break;;
+        "Do not use modified Cnchi files")
+            ANTERGOS;break;;
+        "Use your own files instead")
+            FILES;break;;
+        "Setup System for Reborn installation")
+            DOWNLOAD_ADVANCED;break;;
+        "Quit")
+            break
+            ;;
+        *) echo "ERROR!!! ERROR!!!! SOUND THE ALARM!!!"
+            echo "Sadly, option $REPLY is not possible! Please select either option 1, 2, 3, 4, or 5 instead. Thank you!";;
+    esac
+done
+}
+
+# Use plain old Antergos files (useful for testing sometimes)
+
+ANTERGOS(){
+echo
+echo
+echo "Nice choice! I know I use this one sometimes to test things myself as well, when I need more control with files"
+echo "Just remember - have fun and continue on! Your work truly means a lot to us at Reborn OS."
+echo
+echo "DOWNLOADING A FEW FILES TO HELP CNCHI START PROPERLY FOR YOU..."
+echo
+echo
+cd /
+cd /usr/share/cnchi/
+mkdir $REBORN
+cd $REBORN/
+git clone https://gitlab.com/RebornOS/RebornOS.git --recursive
+cd /
+cd /usr/bin/
+if [ -f /usr/bin/cnchi-start.sh ]; then
+rm -f /usr/bin/cnchi-start.sh
+fi
+wget https://gitlab.com/RebornOS/RebornOS/raw/master/airootfs/usr/bin/cnchi-start.sh
+chmod +x /usr/bin/cnchi-start.sh
+cd /
+cd /usr/share/applications/
+if [ -f /usr/share/applications/cnchi.desktop ]; then
+rm -f /usr/share/applications/cnchi.desktop
+fi
+if [ -f /usr/share/applications/antergos-install.desktop ]; then
+rm -f /usr/share/applications/antergos-install.desktop
+fi
+cp $REBORN/RebornOS/airootfs/usr/share/applications/antergos-install.desktop /usr/share/applications/
+echo
+echo "DONE WITH EVERYTHING!"
+echo "ENJOY ANTERGOS' CNCHI"
+}
+
+# Use your own files for Cnchi
+FILES(){
+echo
+echo
+echo "Fabulous! I see you're quite adventurous' :)"
+echo "Well, have fun and happy hacking."
+yad --center --width=350 --height=100 --form --separator='' --title="Select" --text="Select Disired Directory" --save --field="":CDIR > /tmp/reborn-directory.txt
+SAVING=$(sed '1q;d' /tmp/reborn-directory.txt)
+cd $SAVING
+echo
+echo "DOWNLOADING SPECIAL IMAGE FILES FOR CNCHI. THEN INSTALLING YOUR FILES AS WELL..."
+echo
+echo
+cd /
+cd /usr/share/cnchi/
+mkdir $REBORN
+cd $REBORN/
+git clone https://gitlab.com/RebornOS/RebornOS.git --recursive
+cp -r $SAVING $REBORN/Cnchi
+mv $REBORN/RebornOS/images $REBORN/images
+cd /
+cd /usr/bin/
+if [ -f /usr/bin/cnchi-start.sh ]; then
+rm -f /usr/bin/cnchi-start.sh
+fi
+wget https://gitlab.com/RebornOS/RebornOS/raw/master/airootfs/usr/bin/cnchi-start.sh
+chmod +x /usr/bin/cnchi-start.sh
+cd /
+cd /usr/share/applications/
+if [ -f /usr/share/applications/cnchi.desktop ]; then
+rm -f /usr/share/applications/cnchi.desktop
+fi
+if [ -f /usr/share/applications/antergos-install.desktop ]; then
+rm -f /usr/share/applications/antergos-install.desktop
+fi
+cp $REBORN/RebornOS/airootfs/usr/share/applications/antergos-install.desktop /usr/share/applications/
+echo "DONE"
+rm -f /tmp/reborn-directory.txt
+CUSTOMIZE
+}
+
+# Download Reborn's normal Cnchi files
+DOWNLOAD_SIMPLE(){
+echo
+echo
+echo "Fabulous! Checking out Reborn I see :)"
+echo "Well, have fun and happy hacking."
+echo
+echo "DOWNLOADING SPECIAL REBORN FILES FOR CNCHI..."
+echo
+echo
+cd /
+cd /usr/share/cnchi/
+mkdir $REBORN
+cd $REBORN/
+git clone https://gitlab.com/RebornOS/RebornOS.git --recursive
+mv $REBORN/RebornOS/Cnchi $REBORN/Cnchi
+mv $REBORN/RebornOS/images $REBORN/images
+cd /
+cd /usr/bin/
+if [ -f /usr/bin/cnchi-start.sh ]; then
+rm -f /usr/bin/cnchi-start.sh
+fi
+wget https://gitlab.com/RebornOS/RebornOS/raw/master/airootfs/usr/bin/cnchi-start.sh
+chmod +x /usr/bin/cnchi-start.sh
+cd /
+cd /usr/share/applications/
+if [ -f /usr/share/applications/cnchi.desktop ]; then
+rm -f /usr/share/applications/cnchi.desktop
+fi
+if [ -f /usr/share/applications/antergos-install.desktop ]; then
+rm -f /usr/share/applications/antergos-install.desktop
+fi
+cp $REBORN/RebornOS/airootfs/usr/share/applications/antergos-install.desktop /usr/share/applications/
+echo "DONE"
+CUSTOMIZE
+}
+
+# Install REBORN
+DONWLOAD_ADVANCED() {
+  echo
+  echo
+  echo "Fabulous! Welcome to Reborn :)"
+  echo "Well, have fun and happy hacking."
+  echo
+  echo "DOWNLOADING SPECIAL REBORN FILES FOR CNCHI..."
+  echo
+  echo
+  cd /
+  cd /usr/share/cnchi/
+  mkdir $REBORN
+  cd $REBORN/
+  git clone https://gitlab.com/RebornOS/RebornOS.git --recursive
+  mv $REBORN/RebornOS/Cnchi $REBORN/Cnchi
+  mv $REBORN/RebornOS/images $REBORN/images
+  cd /
+  cd /usr/bin/
+  if [ -f /usr/bin/cnchi-start.sh ]; then
+  rm -f /usr/bin/cnchi-start.sh
+  fi
+  wget https://gitlab.com/RebornOS/RebornOS/raw/master/airootfs/usr/bin/cnchi-start.sh
+  chmod +x /usr/bin/cnchi-start.sh
+  cd /
+  cd /usr/share/applications/
+  if [ -f /usr/share/applications/cnchi.desktop ]; then
+  rm -f /usr/share/applications/cnchi.desktop
+  fi
+  if [ -f /usr/share/applications/antergos-install.desktop ]; then
+  rm -f /usr/share/applications/antergos-install.desktop
+  fi
+  cp $REBORN/RebornOS/airootfs/usr/share/applications/antergos-install.desktop /usr/share/applications/
+  #Use lightdm.conf from local direcectory instead of default one
+  echo "Removing unnecessary lightdm.conf"
+  rm /etc/lightdm/lightdm.conf
+  echo "Copying correct lightdm.conf file over"
+  cp $REBORN/airootfs/etc/lightdm/lightdm.conf /etc/lightdm/
+  echo "DONE"
+  echo
+  #Use sddm.conf from local direcectory instead of default one
+  echo "Removing unnecessary sddm.conf"
+  rm /etc/sddm.conf
+  echo "Copying correct sddm.conf file over"
+  cp $REBORN/etc/sddm.conf /etc/
+  echo "DONE"
+  echo
+  #Copy Antergos Mirrorlist
+  echo "Setting up Antergos and Reborn Mirrorlist"
+  mkdir -p /etc/pacman.d
+  cp $REBORN/airootfs/etc/antergos-mirrorlist /etc/pacman.d/
+  cp $REBORN/airootfs/etc/reborn-mirrorlist /etc/pacman.d/
+  echo "DONE"
+  echo
+  #Copy pacman-init.service over
+  echo "Copying pacman-init.service"
+  cp $REBORN/pacman-init.service /etc/systemd/system/
+  echo "DONE"
+  echo
+  #Replace pacman.conf with Reborn's
+  echo "Replacing pacman.conf with Reborn's"
+  rm /etc/pacman.conf
+  cp $REBORN/Cnchi/pacman.conf /airootfs/etc/
+  cp $REBORN/Cnchi/pacman.conf /usr/share/cnchi/
+  cp $REBORN/Cnchi/pacman2.conf /
+  mv /pacman2.conf /tmp/pacman.conf
+  echo "DONE"
+  echo
+  CUSTOMIZE
+}
+
+# Customize Cnchi for Reborn OS
+CUSTOMIZE(){
+echo
+echo
+echo "MOVING DOWNLOADED FILES OVER..."
+echo
+echo
+
+#Editting Cnchi
+echo "Moving Cnchi files over..."
+
+cp $REBORN/Cnchi/packages.xml /usr/share/cnchi/data/
+cp $REBORN/Cnchi/pacman.tmpl /usr/share/cnchi/data/
+cp $REBORN/Cnchi/features_info.py /usr/share/cnchi/src/
+cp $REBORN/Cnchi/features.py /usr/share/cnchi/src/pages/
+cp $REBORN/Cnchi/desktop_info.py /usr/share/cnchi/src/
+cp $REBORN/Cnchi/grub2.py /usr/share/cnchi/src/installation/boot/
+cp $REBORN/Cnchi/10_antergos /usr/share/cnchi/scripts/
+cp $REBORN/Cnchi/systemd_boot.py /usr/share/cnchi/src/installation/boot/
+cp $REBORN/Cnchi/postinstall.sh /usr/share/cnchi/scripts/
+cp $REBORN/Cnchi/info.py /usr/share/cnchi/src/
+cp $REBORN/Cnchi/show_message.py /usr/share/cnchi/src/
+cp $REBORN/Cnchi/slides.py /usr/share/cnchi/src/pages/
+cp $REBORN/Cnchi/check.py /usr/share/cnchi/src/pages/
+cp $REBORN/Cnchi/welcome.py /usr/share/cnchi/src/pages/
+cp $REBORN/Cnchi/antergos-logo-mini2.png /usr/share/cnchi/data/images/antergos/
+cp $REBORN/Cnchi/20-intel.conf /usr/share/cnchi/
+cp $REBORN/Cnchi/lightdm-webkit2-greeter.conf /usr/share/cnchi/
+cp $REBORN/Cnchi/1.png /usr/share/cnchi/data/images/slides/
+cp $REBORN/Cnchi/2.png /usr/share/cnchi/data/images/slides/
+cp $REBORN/Cnchi/3.png /usr/share/cnchi/data/images/slides/
+cp $REBORN/Cnchi/sddm.conf /usr/share/cnchi/
+cp $REBORN/airootfs/usr/share/applications/cnchi.png /usr/share/pixmaps/
+cp $REBORN/Cnchi/antergos-icon.png /usr/share/cnchi/data/images/antergos/antergos-icon.png
+cp $REBORN/scripts/tint2-start.desktop /usr/share/cnchi/
+cp $REBORN/scripts/conky-start.desktop /usr/share/cnchi/
+cp $REBORN/scripts/obmenu-gen.desktop /usr/share/cnchi/
+cp $REBORN/scripts/plymouth-reborn.desktop /usr/share/cnchi/
+cp $REBORN/scripts/mate-panel.desktop /usr/share/cnchi/
+cp $REBORN/scripts/plymouth.sh /usr/share/cnchi/
+cp $REBORN/scripts/flatpak.sh /usr/share/cnchi/
+cp $REBORN/scripts/pkcon.sh /usr/share/cnchi/
+cp $REBORN/scripts/pkcon2.sh /usr/share/cnchi/
+cp $REBORN/scripts/flatpak.desktop /usr/share/cnchi/
+cp $REBORN/scripts/update.desktop /usr/share/cnchi/
+cp $REBORN/scripts/openbox-config.sh /usr/share/cnchi/
+cp $REBORN/scripts/deepin-fix.sh /usr/share/cnchi/
+cp $REBORN/scripts/deepin-fix.service /usr/share/cnchi/
+cp $REBORN/images/pantheon.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/apricity.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/deepin.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/cinnamon.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/windows.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/kde.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/i3.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/lxqt.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/openbox.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/enlightenment.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/xfce.png /usr/share/cnchi/data/images/desktops/
+cp $REBORN/images/desktop-environment-apricity.svg /usr/share/cnchi/data/icons/scalable/
+cp $REBORN/images/desktop-environment-pantheon.svg /usr/share/cnchi/data/icons/scalable/
+cp $REBORN/images/desktop-environment-windows.svg /usr/share/cnchi/data/icons/scalable/
+cp $REBORN/images/desktop-environment-budgie.svg /usr/share/cnchi/data/icons/scalable/
+cp $REBORN/images/desktop-environment-i3.svg /usr/share/cnchi/data/icons/scalable/
+cp $REBORN/Cnchi/reborn-mirrorlist /etc/pacman.d/
+echo "DONE"
+echo
+echo "Replacing Antergos mentions with Reborn"
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/advanced.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/alongside.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/ask.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/automatic.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/check.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/gtkbasebox.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/keymap.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/language.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/location.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/installation/process.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/slides.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/summary.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/timezone.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/user_info.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/wireless.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/zfs.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/pages/desktop.py
+sed -i "s/gnome/deepin/g" /usr/share/cnchi/src/pages/desktop.py
+#sed -i "s/Antergos/Reborn/g" ${work_dir}/${arch}/airootfs/usr/share/cnchi/src/encfs.py
+sed -i "s/Antergos/Reborn/g" /usr/share/cnchi/src/main_window.py
+echo "DONE"
+}
+
+RUN(){
+echo
+echo
+echo "YAY! Thank you for your help in maintaining Reborn."
+echo "We surely need it! So have fun, and feel free to"
+echo "message me (Keegan) anytime you want. Got questions?"
+echo "Just ask! Thanks, and good luck."
+echo
+echo
+echo
+REMOVE
+INSTALL
+ASK
+}
+
+export -f QUESTION DOWNLOAD_SIMPLE DOWNLOAD_ADVANCED ASK CUSTOMIZE INSTALL REMOVE RUN ANTERGOS
+
+QUESTION
diff --git a/translations.sh b/translations.sh
new file mode 100755 (executable)
index 0000000..2684ecf
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+shopt -s nullglob
+
+_OUT_DIR="$1"
+_WORK_DIR="$2"
+_SCRIPT_DIR="$3"
+
+for mo_file in ${_OUT_DIR}/trans/cnchi_updater/*.mo
+do
+       fullname="$(basename ${mo_file})"
+       #echo "${fullname}"
+       fname="${fullname%.*}"
+       #echo "${fname}"
+       dest="${_SCRIPT_DIR}/root-image/usr/share/locale/${fname}/LC_MESSAGES"
+       echo "${dest}"
+       if ! [[ -d "${dest}" ]]; then
+               mkdir -p "${dest}";
+       fi
+       mv "${mo_file}" "${dest}/CNCHI_UPDATER.mo"
+done
+
+shopt -u nullglob
+
+exit 0;