diff --git a/actions/backports b/actions/backports
deleted file mode 100755
index 353b65313..000000000
--- a/actions/backports
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/python3
-# -*- mode: python -*-
-#
-# This file is part of FreedomBox.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-"""
-Script to check for availability of buster-backports and create an
-apt sources list for backports if available.
-"""
-
-import argparse
-import os
-import requests
-
-from plinth import action_utils
-
-BUSTER_BACKPORTS_RELEASE_FILE_URL = "http://cdn-fastly.deb.debian.org/debian/dists/buster-backports/Release"
-
-
-def parse_arguments():
- """Return parsed command line arguments as dictionary."""
- parser = argparse.ArgumentParser()
- subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
-
- subparsers.add_parser('enable', help='Enable buster backports check')
- subparsers.add_parser('disable', help='Disable buster backports check')
- subparsers.add_parser('check-backports',
- help='Check whether buster backports are available')
-
- subparsers.required = True
- return parser.parse_args()
-
-
-def add_buster_backports_sources():
- backports_list = '/etc/apt/sources.list.d/freedombox-backports.list'
-
- conf = \
-"""deb http://deb.debian.org/debian buster-backports main
-deb-src http://deb.debian.org/debian buster-backports main
-"""
-
- components = {"main"}
-
- # Use tor+http if download over tor is enabled in sources.list
- with open('/etc/apt/sources.list', 'r') as sources_list:
- sources = sources_list.readlines()
- for line in sources:
- if not line.startswith("#") and "tor+http" in line:
- conf = conf.replace("http", "tor+http")
- # Check for contrib and nonfree components
- if "contrib" in line:
- components.add("contrib")
- if "nonfree" in line:
- components.add("nonfree")
- break
-
- conf = conf.replace("main", " ".join(components))
-
- if not os.path.exists(backports_list):
- try:
- with open(backports_list, 'w') as file_handle:
- file_handle.write(conf)
- except PermissionError:
- print(("Failed adding sources list for buster-backports."
- "Try running as a superuser."))
-
-
-def subcommand_check_backports(_):
- """Check whether buster backports is available.
- Add a sources file if it's available.
- """
- response = requests.get(BUSTER_BACKPORTS_RELEASE_FILE_URL)
-
- if response.status_code == 404:
- print("Release file for Buster backports is not available yet.")
- else:
- print("Buster backports is now available. Adding to sources...")
- add_buster_backports_sources()
-
-
-def subcommand_enable(_):
- """Enable systemd service for the daily job buster-backports-check."""
- action_utils.service_enable('buster-backports-check.timer')
- action_utils.service_start('buster-backports-check.timer')
-
-
-def subcommand_disable(_):
- """Disable systemd service for the daily job buster-backports-check."""
- action_utils.service_stop('buster-backports-check.timer')
- action_utils.service_disable('buster-backports-check.timer')
-
-
-def main():
- """Parse arguments and perform all duties."""
- arguments = parse_arguments()
-
- subcommand = arguments.subcommand.replace('-', '_')
- subcommand_method = globals()['subcommand_' + subcommand]
- subcommand_method(arguments)
-
-
-if __name__ == '__main__':
- main()
diff --git a/actions/upgrades b/actions/upgrades
index 69a5f3875..e2666e9c1 100755
--- a/actions/upgrades
+++ b/actions/upgrades
@@ -20,12 +20,16 @@ Configures or runs unattended-upgrades
"""
import argparse
+import os
import re
import subprocess
import sys
+from plinth import action_utils
+
AUTO_CONF_FILE = '/etc/apt/apt.conf.d/20auto-upgrades'
LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades.log'
+BUSTER_BACKPORTS_RELEASE_FILE_URL = 'https://deb.debian.org/debian/dists/buster-backports/Release'
def parse_arguments():
@@ -40,6 +44,9 @@ def parse_arguments():
subparsers.add_parser('disable-auto', help='Disable automatic upgrades.')
subparsers.add_parser('get-log', help='Print the automatic upgrades log')
+ subparsers.add_parser('setup-repositories',
+ help='Setup software repositories for FreedomBox')
+
subparsers.required = True
return parser.parse_args()
@@ -102,6 +109,91 @@ def subcommand_get_log(_):
pass
+def _get_protocol():
+ """Return the protocol to use for newly added repository sources."""
+ try:
+ from plinth.modules.tor import utils
+ if utils.is_apt_transport_tor_enabled():
+ return 'tor+http'
+ except Exception:
+ pass
+
+ return 'http'
+
+
+def _is_release_file_available(protocol):
+ """Return whether the release for backports is available."""
+ wrapper = None
+ if protocol == 'tor+http':
+ wrapper = 'torsocks'
+
+ result = action_utils.check_url(BUSTER_BACKPORTS_RELEASE_FILE_URL,
+ wrapper=wrapper)
+ return result == 'passed'
+
+
+def _add_buster_backports_sources(sources_list, protocol):
+ """Add buster backports sources to freedombox repositories list."""
+ sources = '''# This file is managed by FreedomBox, do not edit.
+# Allow carefully selected updates to 'freedombox' from backports.
+
+deb {protocol}://deb.debian.org/debian buster-backports main
+deb-src {protocol}://deb.debian.org/debian buster-backports main
+'''
+ sources = sources.format(protocol=protocol)
+ with open(sources_list, 'w') as file_handle:
+ file_handle.write(sources)
+
+
+def _check_and_backports_sources():
+ """Add buster backports sources after checking if it is available."""
+ sources_list = '/etc/apt/sources.list.d/freedombox.list'
+ if os.path.exists(sources_list):
+ print('Repositories list up-to-date. Skipping update.')
+ return
+
+ protocol = _get_protocol()
+ if protocol == 'tor+http':
+ print('Package download over Tor is enabled.')
+
+ if not _is_release_file_available(protocol):
+ print('Release file for Buster backports is not available yet.')
+ return
+
+ print('Buster backports is now available. Adding to sources.')
+ _add_buster_backports_sources(sources_list, protocol)
+
+
+def _add_apt_preferences():
+ """Setup APT preferences to upgrade selected packages from backports."""
+ preferences_file = '/etc/apt/preferences.d/50freedombox.pref'
+ if os.path.exists(preferences_file):
+ print('Preferences up-to-date. Skipping update')
+ return
+
+ preferences = '''Explanation: This file is managed by FreedomBox, do not edit.
+Explanation: Allow carefully selected updates to 'freedombox' from backports.
+Package: freedombox
+Pin: release a=buster-backports
+Pin-Priority: 800
+'''
+
+ print('Updating APT preferences.')
+ with open(preferences_file, 'w') as file_handle:
+ file_handle.write(preferences)
+
+
+def subcommand_setup_repositories(_):
+ """Setup software repositories needed for FreedomBox.
+
+ Repositories list for now only contains the backports. If the file exists,
+ assume that it contains backports.
+
+ """
+ _check_and_backports_sources()
+ _add_apt_preferences()
+
+
def main():
"""Parse arguments and perform all duties"""
arguments = parse_arguments()
diff --git a/data/etc/apt/apt.conf.d/60unattended-upgrades b/data/etc/apt/apt.conf.d/60unattended-upgrades
index 68290c1bc..eaa4f7233 100644
--- a/data/etc/apt/apt.conf.d/60unattended-upgrades
+++ b/data/etc/apt/apt.conf.d/60unattended-upgrades
@@ -5,3 +5,12 @@ Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
// Do automatic removal of new unused dependencies after the upgrade
// (equivalent to apt-get autoremove)
Unattended-Upgrade::Remove-Unused-Dependencies "true";
+
+// Allow upgrading from backports repository. This origin pattern is added to
+// other configured patterns. This only works if the current distribution is a
+// stable release. Also all backuports packages have a priority of 100 which is
+// ignored. Only packages that have higher priority set explicitly will get
+// upgraded. Only selected FreedomBox packages have high priority set on them.
+Unattended-Upgrade::Origins-Pattern {
+ "o=Debian Backports,a=${distro_codename}-backports,l=Debian Backports";
+};
diff --git a/data/etc/apt/preferences.d/freedombox.pref b/data/etc/apt/preferences.d/freedombox.pref
deleted file mode 100644
index f298825a3..000000000
--- a/data/etc/apt/preferences.d/freedombox.pref
+++ /dev/null
@@ -1,3 +0,0 @@
-Package: freedombox
-Pin: release a=buster-backports
-Pin-Priority: 800
diff --git a/data/etc/plinth/modules-enabled/backports b/data/etc/plinth/modules-enabled/backports
deleted file mode 100644
index 0b329653d..000000000
--- a/data/etc/plinth/modules-enabled/backports
+++ /dev/null
@@ -1,2 +0,0 @@
-plinth.modules.backports
-
diff --git a/data/lib/systemd/system/buster-backports-check.service b/data/lib/systemd/system/buster-backports-check.service
deleted file mode 100644
index 31cb98928..000000000
--- a/data/lib/systemd/system/buster-backports-check.service
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# This file is part of FreedomBox.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-
-[Unit]
-Description=Service to check for buster backports and enable them
-ConditionPathExists=!/etc/apt/sources.list.d/freedombox-backports.list
-
-[Service]
-ExecStart=/usr/share/plinth/actions/backports check-backports
-
diff --git a/plinth/modules/backports/urls.py b/data/lib/systemd/system/freedombox-setup-repositories.service
similarity index 82%
rename from plinth/modules/backports/urls.py
rename to data/lib/systemd/system/freedombox-setup-repositories.service
index 21fc6a4ac..dfb20a3a7 100644
--- a/plinth/modules/backports/urls.py
+++ b/data/lib/systemd/system/freedombox-setup-repositories.service
@@ -14,8 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
-"""
-URLs for the Backports module.
-"""
-urlpatterns = []
+[Unit]
+Description=FreedomBox: Setup software repositories
+
+[Service]
+ExecStart=/usr/share/plinth/actions/upgrades setup-repositories
+Type=oneshot
diff --git a/data/lib/systemd/system/buster-backports-check.timer b/data/lib/systemd/system/freedombox-setup-repositories.timer
similarity index 91%
rename from data/lib/systemd/system/buster-backports-check.timer
rename to data/lib/systemd/system/freedombox-setup-repositories.timer
index e06b71748..7f9896f21 100644
--- a/data/lib/systemd/system/buster-backports-check.timer
+++ b/data/lib/systemd/system/freedombox-setup-repositories.timer
@@ -16,7 +16,7 @@
#
[Unit]
-Description=Daily check of whether buster backports are available yet
+Description=FreedomBox: Daily check for setting up software repositories
[Timer]
OnCalendar=daily
diff --git a/debian/postrm b/debian/postrm
index fb7db5b7b..b5573a366 100755
--- a/debian/postrm
+++ b/debian/postrm
@@ -4,16 +4,20 @@ set -e
case "$1" in
purge)
- deluser --system --quiet plinth || true
- rm -rf /var/lib/plinth
+ deluser --system --quiet plinth || true
+ rm -rf /var/lib/plinth
- # Remove legacy directory too
- rm -rf /var/log/plinth
+ # Remove legacy directory too
+ rm -rf /var/log/plinth
- if [ -e '/etc/apt/sources.list.d/freedombox-backports.list' ]; then
- rm /etc/apt/sources.list.d/freedombox-backports.list
- fi
- ;;
+ if [ -e '/etc/apt/sources.list.d/freedombox.list' ]; then
+ rm -f /etc/apt/sources.list.d/freedombox.list
+ fi
+
+ if [ -e '/etc/apt/preferences.d/50freedombox.pref' ]; then
+ rm -f /etc/apt/preferences.d/50freedombox.pref
+ fi
+ ;;
esac
#DEBHELPER#
diff --git a/plinth/modules/backports/__init__.py b/plinth/modules/backports/__init__.py
deleted file mode 100644
index 06357928e..000000000
--- a/plinth/modules/backports/__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# This file is part of FreedomBox.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-"""
-FreedomBox app to manage Debian backports.
-"""
-
-from plinth import actions
-
-is_essential = True
-
-version = 1
-
-
-def setup(helper, old_version=None):
- """Configure the module."""
- helper.call('post', actions.superuser_run, 'backports', ['enable'])
diff --git a/setup.py b/setup.py
index 920c0913c..b7bab5163 100755
--- a/setup.py
+++ b/setup.py
@@ -238,8 +238,6 @@ setuptools.setup(
glob.glob('data/etc/apache2/includes/*.conf')),
('/etc/apt/apt.conf.d',
glob.glob('data/etc/apt/apt.conf.d/60unattended-upgrades')),
- ('/etc/apt/preferences.d',
- glob.glob('data/etc/apt/preferences.d/freedombox.pref')),
('/etc/avahi/services/',
glob.glob('data/etc/avahi/services/*.service')),
('/etc/ikiwiki', glob.glob('data/etc/ikiwiki/*.setup')),