mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-02-11 08:23:49 +00:00
Merge remote-tracking branch 'upstream/master' into dynamicdns
This commit is contained in:
commit
6c214c915c
2
INSTALL
2
INSTALL
@ -7,7 +7,7 @@
|
||||
$ sudo apt-get install libjs-jquery libjs-modernizr \
|
||||
libjs-bootstrap make pandoc python3 python3-cherrypy3 \
|
||||
python3-coverage python3-django python3-bootstrapform \
|
||||
python3-setuptools
|
||||
python3-gi python3-setuptools gir1.2-packagekitglib-1.0
|
||||
|
||||
2. Install Plinth:
|
||||
|
||||
|
||||
@ -9,19 +9,19 @@
|
||||
#
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Action to backup ejabberd database before changing hostname.
|
||||
domainname="$1"
|
||||
hostname=$(hostname)
|
||||
|
||||
BACKUP=/tmp/ejabberd.dump
|
||||
ejabberdctl dump $BACKUP
|
||||
ejabberdctl stop
|
||||
|
||||
# Make sure there aren't files in the Mnesia spool dir
|
||||
mkdir -p /var/lib/ejabberd/oldfiles
|
||||
mv /var/lib/ejabberd/*.* /var/lib/ejabberd/oldfiles/
|
||||
if grep -q 127.0.1.1 /etc/hosts ; then
|
||||
sed -i "s/127.0.1.1.*/127.0.1.1 $hostname.$domainname $hostname/" /etc/hosts
|
||||
else
|
||||
sed -i "/127.0.0.1.*/a \
|
||||
127.0.1.1 $hostname.$domainname $hostname" /etc/hosts
|
||||
fi
|
||||
@ -30,10 +30,6 @@ def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
||||
|
||||
# Get installed status
|
||||
subparsers.add_parser('get-installed',
|
||||
help='Get whether firewalld is installed')
|
||||
|
||||
# Get status
|
||||
subparsers.add_parser('get-status',
|
||||
help='Get whether firewalld is running')
|
||||
@ -64,14 +60,6 @@ def parse_arguments():
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def subcommand_get_installed(_):
|
||||
"""Print whether firewalld is installed"""
|
||||
with open('/dev/null', 'w') as file_handle:
|
||||
status = subprocess.call(['which', 'firewalld'], stdout=file_handle)
|
||||
|
||||
print('installed' if not status else 'not installed')
|
||||
|
||||
|
||||
def subcommand_get_status(_):
|
||||
"""Print status of the firewalld service"""
|
||||
subprocess.call(['firewall-cmd', '--state'])
|
||||
|
||||
@ -18,11 +18,15 @@
|
||||
|
||||
hostname="$1"
|
||||
|
||||
echo "$hostname" > /etc/hostname
|
||||
if [ -x /etc/init.d/hostname.sh ] ; then
|
||||
service hostname.sh start
|
||||
if [ -d /run/systemd/system ] ; then
|
||||
hostnamectl set-hostname --transient --static "$hostname"
|
||||
else
|
||||
service hostname start
|
||||
echo "$hostname" > /etc/hostname
|
||||
if [ -x /etc/init.d/hostname.sh ] ; then
|
||||
invoke-rc.d hostname.sh start
|
||||
else
|
||||
service hostname start
|
||||
fi
|
||||
fi
|
||||
|
||||
service avahi-daemon restart
|
||||
|
||||
@ -58,14 +58,6 @@ done
|
||||
|
||||
if [ "$owncloud_enable" != "$owncloud_enable_cur" ] ; then
|
||||
if $owncloud_enable ; then
|
||||
# Select postgresql as the backend database for OwnCloud, and
|
||||
# make sure its php support is enabled when owncloud is
|
||||
# installed.
|
||||
DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends \
|
||||
install -y postgresql php5-pgsql 2>&1 | logger -t owncloud-setup
|
||||
DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends \
|
||||
install -y owncloud 2>&1 | logger -t owncloud-setup
|
||||
|
||||
# Keep existing configuration if it exist
|
||||
if [ ! -e /etc/owncloud/config.php ] ; then
|
||||
# Set up postgresql database and user
|
||||
|
||||
@ -48,10 +48,6 @@ def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
||||
|
||||
# Get installed status
|
||||
subparsers.add_parser('get-installed',
|
||||
help='Get whether PakeKite is installed')
|
||||
|
||||
# Start PageKite
|
||||
subparsers.add_parser('start', help='Start PageKite service')
|
||||
|
||||
@ -91,14 +87,6 @@ def parse_arguments():
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def subcommand_get_installed(_):
|
||||
"""Print whether PageKite is installed"""
|
||||
with open('/dev/null', 'w') as file_handle:
|
||||
status = subprocess.call(['which', 'pagekite'], stdout=file_handle)
|
||||
|
||||
print('installed' if not status else 'not installed')
|
||||
|
||||
|
||||
def subcommand_start(_):
|
||||
"""Start PageKite service"""
|
||||
status = subprocess.call(['service', 'pagekite', 'start'])
|
||||
|
||||
27
actions/tor
27
actions/tor
@ -34,10 +34,6 @@ def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
||||
|
||||
# Get whether Tor is installed
|
||||
subparsers.add_parser('get-installed',
|
||||
help='Get whether Tor is installed')
|
||||
|
||||
# Get whether Tor is running
|
||||
subparsers.add_parser('is-running', help='Get whether Tor is running')
|
||||
|
||||
@ -59,11 +55,6 @@ def parse_arguments():
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def subcommand_get_installed(_):
|
||||
"""Get whether Tor is installed"""
|
||||
print('installed' if get_installed() else 'not installed')
|
||||
|
||||
|
||||
def subcommand_is_running(_):
|
||||
"""Get whether Tor is running"""
|
||||
try:
|
||||
@ -101,7 +92,7 @@ def subcommand_get_hs(_):
|
||||
|
||||
def subcommand_enable_hs(_):
|
||||
"""Enable Tor hidden service"""
|
||||
if not get_installed() or get_hidden_service():
|
||||
if get_hidden_service():
|
||||
return
|
||||
|
||||
with open(TOR_CONFIG, 'r') as conffile:
|
||||
@ -121,7 +112,7 @@ def subcommand_enable_hs(_):
|
||||
|
||||
def subcommand_disable_hs(_):
|
||||
"""Disable Tor hidden service"""
|
||||
if not get_installed() or not get_hidden_service():
|
||||
if not get_hidden_service():
|
||||
return
|
||||
|
||||
with open(TOR_CONFIG, 'r') as conffile:
|
||||
@ -154,19 +145,8 @@ def subcommand_disable_hs(_):
|
||||
subprocess.call(['service', 'tor', 'restart'])
|
||||
|
||||
|
||||
def get_installed():
|
||||
"""Get whether Tor is installed"""
|
||||
with open('/dev/null', 'w') as file_handle:
|
||||
status = subprocess.call(['which', 'tor'], stdout=file_handle)
|
||||
|
||||
return not status
|
||||
|
||||
|
||||
def set_tor_service(enable):
|
||||
"""Enable/disable Tor service; enable: boolean"""
|
||||
if not get_installed():
|
||||
return
|
||||
|
||||
newline = 'RUN_DAEMON="yes"\n' if enable else 'RUN_DAEMON="no"\n'
|
||||
|
||||
with open(SERVICE_CONFIG, 'r') as file:
|
||||
@ -182,9 +162,6 @@ def set_tor_service(enable):
|
||||
|
||||
def get_hidden_service():
|
||||
"""Return a string with configured Tor hidden service information"""
|
||||
if not get_installed():
|
||||
return ''
|
||||
|
||||
hs_dir = None
|
||||
hs_ports = []
|
||||
|
||||
|
||||
127
actions/xmpp
127
actions/xmpp
@ -23,6 +23,14 @@ Configuration helper for the ejabberd service
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import os
|
||||
import socket
|
||||
import re
|
||||
|
||||
JWCHAT_CONFIG = '/etc/jwchat/config.js'
|
||||
EJABBERD_CONFIG = '/etc/ejabberd/ejabberd.yml'
|
||||
EJABBERD_BACKUP = '/var/log/ejabberd/ejabberd.dump'
|
||||
EJABBERD_BACKUP_NEW = '/var/log/ejabberd/ejabberd_new.dump'
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
@ -30,9 +38,29 @@ def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
||||
|
||||
# Get whether ejabberd is installed
|
||||
subparsers.add_parser('get-installed',
|
||||
help='Get whether ejabberd is installed')
|
||||
# Prepare ejabberd for hostname change
|
||||
pre_hostname_change = subparsers.add_parser(
|
||||
'pre-change-hostname',
|
||||
help='Prepare ejabberd for hostname change')
|
||||
pre_hostname_change.add_argument('--old-hostname',
|
||||
help='Previous hostname')
|
||||
pre_hostname_change.add_argument('--new-hostname',
|
||||
help='New hostname')
|
||||
|
||||
# Update ejabberd and jwchat with new hostname
|
||||
hostname_change = subparsers.add_parser(
|
||||
'change-hostname',
|
||||
help='Update ejabberd and jwchat with new hostname')
|
||||
hostname_change.add_argument('--old-hostname',
|
||||
help='Previous hostname')
|
||||
hostname_change.add_argument('--new-hostname',
|
||||
help='New hostname')
|
||||
|
||||
# Update ejabberd and jwchat with new domainname
|
||||
domainname_change = subparsers.add_parser(
|
||||
'change-domainname',
|
||||
help='Update ejabberd and jwchat with new domainname')
|
||||
domainname_change.add_argument('--domainname', help='New domainname')
|
||||
|
||||
# Register a new user account
|
||||
register = subparsers.add_parser('register',
|
||||
@ -45,37 +73,96 @@ def parse_arguments():
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def subcommand_get_installed(_):
|
||||
"""Get whether ejabberd is installed"""
|
||||
print('installed' if get_installed() else 'not installed')
|
||||
def subcommand_pre_change_hostname(arguments):
|
||||
"""Prepare ejabberd for hostname change"""
|
||||
old_hostname = arguments.old_hostname
|
||||
new_hostname = arguments.new_hostname
|
||||
|
||||
subprocess.call(['ejabberdctl', 'backup', EJABBERD_BACKUP])
|
||||
try:
|
||||
subprocess.check_output(['ejabberdctl', 'mnesia-change-nodename',
|
||||
'ejabberd@' + old_hostname,
|
||||
'ejabberd@' + new_hostname,
|
||||
EJABBERD_BACKUP, EJABBERD_BACKUP_NEW])
|
||||
os.remove(EJABBERD_BACKUP)
|
||||
except subprocess.CalledProcessError as err:
|
||||
print('Failed to change hostname in ejabberd backup database: %s', err)
|
||||
|
||||
|
||||
def subcommand_change_hostname(arguments):
|
||||
"""Update ejabberd and jwchat with new hostname"""
|
||||
subprocess.call(['service', 'ejabberd', 'stop'])
|
||||
subprocess.call(['pkill', '-u', 'ejabberd'])
|
||||
|
||||
# Make sure there aren't files in the Mnesia spool dir
|
||||
os.makedirs('/var/lib/ejabberd/oldfiles', exist_ok=True)
|
||||
subprocess.call('mv /var/lib/ejabberd/*.* /var/lib/ejabberd/oldfiles/',
|
||||
shell=True)
|
||||
|
||||
subprocess.call(['service', 'ejabberd', 'start'])
|
||||
|
||||
# restore backup database
|
||||
if os.path.exists(EJABBERD_BACKUP_NEW):
|
||||
try:
|
||||
subprocess.check_output(['ejabberdctl',
|
||||
'restore',
|
||||
EJABBERD_BACKUP_NEW])
|
||||
os.remove(EJABBERD_BACKUP_NEW)
|
||||
except subprocess.CalledProcessError as err:
|
||||
print('Failed to restore ejabberd backup database: %s', err)
|
||||
else:
|
||||
print('Could not load ejabberd backup database: %s not found'
|
||||
% EJABBERD_BACKUP_NEW)
|
||||
|
||||
|
||||
def subcommand_change_domainname(arguments):
|
||||
"""Update ejabberd and jwchat with new domainname"""
|
||||
domainname = arguments.domainname
|
||||
fqdn = socket.gethostname() + '.' + domainname
|
||||
|
||||
# update jwchat's sitename, if it's installed
|
||||
if os.path.exists(JWCHAT_CONFIG):
|
||||
with open(JWCHAT_CONFIG, 'r') as conffile:
|
||||
lines = conffile.readlines()
|
||||
with open(JWCHAT_CONFIG, 'w') as conffile:
|
||||
for line in lines:
|
||||
if re.match(r'\s*var\s+SITENAME', line):
|
||||
conffile.write('var SITENAME = "' + fqdn + '";\n')
|
||||
else:
|
||||
conffile.write(line)
|
||||
else:
|
||||
print('Skipping configuring jwchat sitename: %s not found',
|
||||
JWCHAT_CONFIG)
|
||||
|
||||
subprocess.call(['service', 'ejabberd', 'stop'])
|
||||
subprocess.call(['pkill', '-u', 'ejabberd'])
|
||||
|
||||
# add updated FQDN to top of ejabberd hosts list
|
||||
with open(EJABBERD_CONFIG, 'r') as conffile:
|
||||
lines = conffile.readlines()
|
||||
with open(EJABBERD_CONFIG, 'w') as conffile:
|
||||
for line in lines:
|
||||
conffile.write(line)
|
||||
if re.match(r'\s*hosts:', line):
|
||||
conffile.write(' - "' + fqdn + '"\n')
|
||||
|
||||
subprocess.call(['service', 'ejabberd', 'start'])
|
||||
|
||||
|
||||
def subcommand_register(arguments):
|
||||
"""Register a new user account"""
|
||||
if not get_installed():
|
||||
print('Failed to register XMPP account: ejabberd is not installed.')
|
||||
return
|
||||
|
||||
username = arguments.username
|
||||
password = arguments.password
|
||||
hostname = subprocess.check_output(['hostname'])
|
||||
fqdn = socket.getfqdn()
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(['ejabberdctl', 'register',
|
||||
username, hostname, password])
|
||||
username, fqdn, password])
|
||||
print(output.decode())
|
||||
except subprocess.CalledProcessError as e:
|
||||
print('Failed to register XMPP account:', e.output.decode())
|
||||
|
||||
|
||||
def get_installed():
|
||||
"""Check if ejabberd is installed"""
|
||||
with open('/dev/null', 'w') as file_handle:
|
||||
status = subprocess.call(['which', 'ejabberdctl'], stdout=file_handle)
|
||||
|
||||
return not status
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse arguments and perform all duties"""
|
||||
arguments = parse_arguments()
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Action to set up new hostname for ejabberd and jwchat.
|
||||
|
||||
hostname="$1"
|
||||
old_hostname=`debconf-show ejabberd | awk '/hostname/ { print $3 }'`
|
||||
|
||||
# Based on http://www.process-one.net/docs/ejabberd/guide_en.html#htoc77
|
||||
|
||||
BACKUP=/tmp/ejabberd.dump
|
||||
|
||||
# Note: dpkg-reconfigure will fail if there have been manual changes made to the
|
||||
# configuration file for a package. Since this is the case for ejabberd,
|
||||
# manually update the hostname in the configuration file.
|
||||
echo "ejabberd ejabberd/hostname string $hostname" | debconf-set-selections
|
||||
echo "jwchat jwchat/ApacheServerName string $hostname" | debconf-set-selections
|
||||
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure jwchat
|
||||
|
||||
sed -i "s/$old_hostname/$hostname/g" /etc/ejabberd/ejabberd.yml
|
||||
sed -i "s/$old_hostname/$hostname/g" $BACKUP
|
||||
|
||||
service ejabberd restart
|
||||
|
||||
# Load backup database
|
||||
sleep 10
|
||||
ejabberdctl load $BACKUP
|
||||
rm $BACKUP
|
||||
@ -19,4 +19,4 @@
|
||||
Plinth package init file
|
||||
"""
|
||||
|
||||
__version__ = '0.4.1'
|
||||
__version__ = '0.4.2'
|
||||
|
||||
@ -31,6 +31,8 @@ import socket
|
||||
|
||||
from plinth import actions
|
||||
from plinth import cfg
|
||||
from plinth.signals import pre_hostname_change, post_hostname_change
|
||||
from plinth.signals import domainname_change
|
||||
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
@ -41,6 +43,12 @@ def get_hostname():
|
||||
return socket.gethostname()
|
||||
|
||||
|
||||
def get_domainname():
|
||||
"""Return the domainname"""
|
||||
fqdn = socket.getfqdn()
|
||||
return '.'.join(fqdn.split('.')[1:])
|
||||
|
||||
|
||||
class TrimmedCharField(forms.CharField):
|
||||
"""Trim the contents of a CharField"""
|
||||
def clean(self, value):
|
||||
@ -68,6 +76,15 @@ and must not be greater than 63 characters in length.'),
|
||||
validators.RegexValidator(r'^[a-zA-Z][a-zA-Z0-9]{,62}$',
|
||||
_('Invalid hostname'))])
|
||||
|
||||
domainname = TrimmedCharField(
|
||||
label=_('Domain Name'),
|
||||
help_text=_('Your domain name is the global name by which other \
|
||||
machines on the Internet can reach you. It must consist of alphanumeric words \
|
||||
separated by dots.'),
|
||||
validators=[
|
||||
validators.RegexValidator(r'^[a-zA-Z][a-zA-Z0-9.]*$',
|
||||
_('Invalid domain name'))])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# pylint: disable-msg=E1101, W0233
|
||||
forms.Form.__init__(self, *args, **kwargs)
|
||||
@ -124,6 +141,7 @@ def index(request):
|
||||
def get_status():
|
||||
"""Return the current status"""
|
||||
return {'hostname': get_hostname(),
|
||||
'domainname': get_domainname(),
|
||||
'time_zone': open('/etc/timezone').read().rstrip()}
|
||||
|
||||
|
||||
@ -140,6 +158,17 @@ def _apply_changes(request, old_status, new_status):
|
||||
else:
|
||||
messages.info(request, _('Hostname is unchanged'))
|
||||
|
||||
if old_status['domainname'] != new_status['domainname']:
|
||||
try:
|
||||
set_domainname(new_status['domainname'])
|
||||
except Exception as exception:
|
||||
messages.error(request, _('Error setting domain name: %s') %
|
||||
exception)
|
||||
else:
|
||||
messages.success(request, _('Domain name set'))
|
||||
else:
|
||||
messages.info(request, _('Domain name is unchanged'))
|
||||
|
||||
if old_status['time_zone'] != new_status['time_zone']:
|
||||
try:
|
||||
actions.superuser_run('timezone-change', [new_status['time_zone']])
|
||||
@ -154,11 +183,34 @@ def _apply_changes(request, old_status, new_status):
|
||||
|
||||
def set_hostname(hostname):
|
||||
"""Sets machine hostname to hostname"""
|
||||
old_hostname = get_hostname()
|
||||
|
||||
# Hostname should be ASCII. If it's unicode but passed our
|
||||
# valid_hostname check, convert to ASCII.
|
||||
hostname = str(hostname)
|
||||
|
||||
pre_hostname_change.send_robust(sender='config',
|
||||
old_hostname=old_hostname,
|
||||
new_hostname=hostname)
|
||||
|
||||
LOGGER.info('Changing hostname to - %s', hostname)
|
||||
actions.superuser_run('xmpp-pre-hostname-change')
|
||||
actions.superuser_run('hostname-change', hostname)
|
||||
actions.superuser_run('xmpp-hostname-change', hostname, async=True)
|
||||
|
||||
post_hostname_change.send_robust(sender='config',
|
||||
old_hostname=old_hostname,
|
||||
new_hostname=hostname)
|
||||
|
||||
|
||||
def set_domainname(domainname):
|
||||
"""Sets machine domain name to domainname"""
|
||||
old_domainname = get_domainname()
|
||||
|
||||
# Domain name should be ASCII. If it's unicode, convert to ASCII.
|
||||
domainname = str(domainname)
|
||||
|
||||
LOGGER.info('Changing domain name to - %s', domainname)
|
||||
actions.superuser_run('domainname-change', domainname)
|
||||
|
||||
domainname_change.send_robust(sender='config',
|
||||
old_domainname=old_domainname,
|
||||
new_domainname=domainname)
|
||||
|
||||
@ -26,6 +26,7 @@ import logging
|
||||
|
||||
from plinth import actions
|
||||
from plinth import cfg
|
||||
from plinth import package
|
||||
from plinth.signals import service_enabled
|
||||
import plinth.service as service_module
|
||||
|
||||
@ -42,13 +43,9 @@ def init():
|
||||
|
||||
|
||||
@login_required
|
||||
@package.required('firewalld')
|
||||
def index(request):
|
||||
"""Serve introcution page"""
|
||||
if not get_installed_status():
|
||||
return TemplateResponse(request, 'firewall.html',
|
||||
{'title': _('Firewall'),
|
||||
'firewall_status': 'not_installed'})
|
||||
|
||||
if not get_enabled_status():
|
||||
return TemplateResponse(request, 'firewall.html',
|
||||
{'title': _('Firewall'),
|
||||
@ -65,14 +62,8 @@ def index(request):
|
||||
'external_enabled_services': external_enabled_services})
|
||||
|
||||
|
||||
def get_installed_status():
|
||||
"""Return whether firewall is installed"""
|
||||
output = _run(['get-installed'], superuser=True)
|
||||
return output.split()[0] == 'installed'
|
||||
|
||||
|
||||
def get_enabled_status():
|
||||
"""Return whether firewall is installed"""
|
||||
"""Return whether firewall is enabled"""
|
||||
output = _run(['get-status'], superuser=True)
|
||||
return output.split()[0] == 'running'
|
||||
|
||||
|
||||
@ -29,13 +29,7 @@ threat from the Internet.</p>
|
||||
|
||||
<p>The following is the current status:</p>
|
||||
|
||||
{% if firewall_status = 'not_installed' %}
|
||||
<p>Firewall is not installed. Please install it. Firewall comes
|
||||
pre-installed with {{ cfg.box_name }}. On any Debian based system (such
|
||||
as {{ cfg.box_name }}) you may install it using the
|
||||
command <code>aptitude install firewalld</code></p>
|
||||
|
||||
{% elif firewall_status = 'not_running' %}
|
||||
{% if firewall_status = 'not_running' %}
|
||||
|
||||
<p>Firewall daemon is not running. Please run it. Firewall comes
|
||||
enabled by default on {{ cfg.box_name }}. On any Debian based system
|
||||
|
||||
@ -23,6 +23,7 @@ from gettext import gettext as _
|
||||
|
||||
from plinth import actions
|
||||
from plinth import cfg
|
||||
from plinth import package
|
||||
from plinth import service
|
||||
|
||||
|
||||
@ -47,6 +48,7 @@ def init():
|
||||
|
||||
|
||||
@login_required
|
||||
@package.required('postgresql', 'php5-pgsql', 'owncloud')
|
||||
def index(request):
|
||||
"""Serve the ownCloud configuration page"""
|
||||
status = get_status()
|
||||
|
||||
@ -27,14 +27,10 @@
|
||||
|
||||
<h2>ownCloud</h2>
|
||||
|
||||
<p>When enabled, the owncloud installation will be available
|
||||
<p>When enabled, the ownCloud installation will be available
|
||||
from <a href="/owncloud">/owncloud</a> path on the web server.
|
||||
Visit this URL to set up the initial administration account for
|
||||
owncloud.</p>
|
||||
|
||||
<p><strong>Note: Setting up owncloud for the first time might take
|
||||
5 minutes or more, depending on download bandwidth from the
|
||||
Debian APT sources.</strong></p>
|
||||
ownCloud.</p>
|
||||
|
||||
{{ form|bootstrap }}
|
||||
<input type="submit" class="btn btn-primary btn-md" value="Update setup"/>
|
||||
|
||||
@ -30,6 +30,7 @@ import logging
|
||||
|
||||
from plinth import actions
|
||||
from plinth import cfg
|
||||
from plinth import package
|
||||
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
@ -101,6 +102,7 @@ https://pagekite.net/wiki/Howto/SshOverPageKite/">instructions</a>'))
|
||||
|
||||
|
||||
@login_required
|
||||
@package.required('pagekite')
|
||||
def configure(request):
|
||||
"""Serve the configuration form"""
|
||||
status = get_status()
|
||||
@ -131,11 +133,6 @@ def get_status():
|
||||
"""
|
||||
status = {}
|
||||
|
||||
# Check if PageKite is installed
|
||||
output = _run(['get-installed'])
|
||||
if output.split()[0] != 'installed':
|
||||
return None
|
||||
|
||||
# PageKite service enabled/disabled
|
||||
output = _run(['get-status'])
|
||||
status['enabled'] = (output.split()[0] == 'enabled')
|
||||
|
||||
@ -22,15 +22,6 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if not status %}
|
||||
|
||||
<p>PageKite is not installed, please install it. PageKite comes
|
||||
pre-installed with {{ cfg.box_name }}. On any Debian based system
|
||||
(such as {{ cfg.box_name }}) you may install it using the command
|
||||
<code>aptitude install pagekite<code></p>
|
||||
|
||||
{% else %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
@ -52,8 +43,6 @@
|
||||
|
||||
</form>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block page_js %}
|
||||
|
||||
@ -24,8 +24,6 @@
|
||||
|
||||
<h2>Tor</h2>
|
||||
|
||||
{% if is_installed %}
|
||||
|
||||
<h3>Status</h3>
|
||||
|
||||
<p>
|
||||
@ -80,15 +78,6 @@ port-forwarded, if necessary:</p>
|
||||
<p>A Tor SOCKS port is available on your {{ cfg.box_name }} on TCP port
|
||||
9050.</p>
|
||||
|
||||
{% else %}
|
||||
|
||||
<p>Tor is not installed, please install it. Tor comes pre-installed
|
||||
with {{ cfg.box_name }}. On any Debian-based system (such as
|
||||
{{ cfg.box_name }}) you may install it using the command
|
||||
<code>aptitude install tor</code>.</p>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
|
||||
@ -27,6 +27,7 @@ from gettext import gettext as _
|
||||
|
||||
from plinth import actions
|
||||
from plinth import cfg
|
||||
from plinth import package
|
||||
|
||||
|
||||
class TorForm(forms.Form): # pylint: disable=W0232
|
||||
@ -43,6 +44,7 @@ def init():
|
||||
|
||||
|
||||
@login_required
|
||||
@package.required('tor')
|
||||
def index(request):
|
||||
"""Service the index page"""
|
||||
status = get_status()
|
||||
@ -61,7 +63,6 @@ def index(request):
|
||||
|
||||
return TemplateResponse(request, 'tor.html',
|
||||
{'title': _('Tor Control Panel'),
|
||||
'is_installed': status['is_installed'],
|
||||
'is_running': status['is_running'],
|
||||
'tor_ports': status['ports'],
|
||||
'tor_hs_enabled': status['hs_enabled'],
|
||||
@ -72,9 +73,6 @@ def index(request):
|
||||
|
||||
def get_status():
|
||||
"""Return the current status"""
|
||||
is_installed = actions.superuser_run(
|
||||
'tor',
|
||||
['get-installed']).strip() == 'installed'
|
||||
is_running = actions.superuser_run('tor', ['is-running']).strip() == 'yes'
|
||||
|
||||
output = actions.superuser_run('tor-get-ports')
|
||||
@ -103,8 +101,7 @@ def get_status():
|
||||
hs_hostname = hs_info[0]
|
||||
hs_ports = hs_info[1]
|
||||
|
||||
return {'is_installed': is_installed,
|
||||
'is_running': is_running,
|
||||
return {'is_running': is_running,
|
||||
'ports': ports,
|
||||
'hs_enabled': hs_enabled,
|
||||
'hs_hostname': hs_hostname,
|
||||
|
||||
@ -22,8 +22,6 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if is_installed %}
|
||||
|
||||
<p>XMPP is an open and standardized communication protocol. Here you
|
||||
can run and configure your XMPP server, called ejabberd. To actually
|
||||
communicate, you can use the <a href='/jwchat'>web client</a> or any
|
||||
@ -33,16 +31,4 @@
|
||||
<p><a href='/jwchat' target='_blank'class='btn btn-primary'> Launch web
|
||||
client</a></p>
|
||||
|
||||
{% else %}
|
||||
|
||||
<h2>XMPP Server</h2>
|
||||
|
||||
<p>The XMPP server <i>ejabberd</i> is not installed.</p>
|
||||
|
||||
<p>ejabberd comes pre-installed with {{ cfg.box_name }}. On any Debian-based
|
||||
system (such as {{ cfg.box_name }}) you may install it using the command
|
||||
<code>aptitude install ejabberd</code>.</p>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -25,7 +25,10 @@ import logging
|
||||
|
||||
from plinth import actions
|
||||
from plinth import cfg
|
||||
from plinth import package
|
||||
from plinth import service
|
||||
from plinth.signals import pre_hostname_change, post_hostname_change
|
||||
from plinth.signals import domainname_change
|
||||
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
@ -53,23 +56,18 @@ def init():
|
||||
'xmpp-bosh', _('Chat Server - web interface'), is_external=True,
|
||||
enabled=True)
|
||||
|
||||
pre_hostname_change.connect(on_pre_hostname_change)
|
||||
post_hostname_change.connect(on_post_hostname_change)
|
||||
domainname_change.connect(on_domainname_change)
|
||||
|
||||
|
||||
@login_required
|
||||
@package.required('jwchat', 'ejabberd')
|
||||
def index(request):
|
||||
"""Serve XMPP page"""
|
||||
is_installed = actions.superuser_run(
|
||||
'xmpp',
|
||||
['get-installed']).strip() == 'installed'
|
||||
|
||||
if is_installed:
|
||||
index_subsubmenu = subsubmenu
|
||||
else:
|
||||
index_subsubmenu = None
|
||||
|
||||
return TemplateResponse(request, 'xmpp.html',
|
||||
{'title': _('XMPP Server'),
|
||||
'is_installed': is_installed,
|
||||
'subsubmenu': index_subsubmenu})
|
||||
'subsubmenu': subsubmenu})
|
||||
|
||||
|
||||
class ConfigureForm(forms.Form): # pylint: disable-msg=W0232
|
||||
@ -178,3 +176,44 @@ def _register_user(request, data):
|
||||
messages.error(request,
|
||||
_('Failed to register account for %s: %s') %
|
||||
(data['username'], output))
|
||||
|
||||
|
||||
def on_pre_hostname_change(sender, old_hostname, new_hostname, **kwargs):
|
||||
"""
|
||||
Backup ejabberd database before hostname is changed.
|
||||
"""
|
||||
del sender # Unused
|
||||
del kwargs # Unused
|
||||
|
||||
actions.superuser_run('xmpp',
|
||||
['pre-change-hostname',
|
||||
'--old-hostname', old_hostname,
|
||||
'--new-hostname', new_hostname])
|
||||
|
||||
|
||||
def on_post_hostname_change(sender, old_hostname, new_hostname, **kwargs):
|
||||
"""
|
||||
Update ejabberd and jwchat config after hostname is changed.
|
||||
"""
|
||||
del sender # Unused
|
||||
del kwargs # Unused
|
||||
|
||||
actions.superuser_run('xmpp',
|
||||
['change-hostname',
|
||||
'--old-hostname', old_hostname,
|
||||
'--new-hostname', new_hostname],
|
||||
async=True)
|
||||
|
||||
|
||||
def on_domainname_change(sender, old_domainname, new_domainname, **kwargs):
|
||||
"""
|
||||
Update ejabberd and jwchat config after domain name is changed.
|
||||
"""
|
||||
del sender # Unused
|
||||
del old_domainname # Unused
|
||||
del kwargs # Unused
|
||||
|
||||
actions.superuser_run('xmpp',
|
||||
['change-domainname',
|
||||
'--domainname', new_domainname],
|
||||
async=True)
|
||||
|
||||
184
plinth/package.py
Normal file
184
plinth/package.py
Normal file
@ -0,0 +1,184 @@
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Framework for installing and updating distribution packages
|
||||
"""
|
||||
|
||||
import functools
|
||||
from gi.repository import PackageKitGlib as packagekit
|
||||
import logging
|
||||
import threading
|
||||
|
||||
import plinth
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
transactions = {}
|
||||
packages_resolved = {}
|
||||
|
||||
|
||||
class Transaction(object):
|
||||
"""Information about an ongoing transaction."""
|
||||
|
||||
def __init__(self, package_names):
|
||||
"""Initialize transaction object.
|
||||
|
||||
Set most values to None until they are sent as progress update.
|
||||
"""
|
||||
self.package_names = package_names
|
||||
|
||||
# Progress
|
||||
self.allow_cancel = None
|
||||
self.percentage = None
|
||||
self.status = None
|
||||
self.status_string = None
|
||||
self.flags = None
|
||||
self.package = None
|
||||
self.package_id = None
|
||||
self.item_progress = None
|
||||
self.role = None
|
||||
self.caller_active = None
|
||||
self.download_size_remaining = None
|
||||
|
||||
def get_id(self):
|
||||
"""Return a identifier to use as a key in a map of transactions."""
|
||||
return frozenset(self.package_names)
|
||||
|
||||
def __str__(self):
|
||||
"""Return the string representation of the object"""
|
||||
return ('Transaction(packages={0}, allow_cancel={1}, status={2}, '
|
||||
' percentage={3}, package={4}, item_progress={5})').format(
|
||||
self.package_names, self.allow_cancel, self.status_string,
|
||||
self.percentage, self.package, self.item_progress)
|
||||
|
||||
def start_install(self):
|
||||
"""Start a PackageKit transaction to install given list of packages.
|
||||
|
||||
This operation is non-blocking at it spawns a new thread.
|
||||
"""
|
||||
thread = threading.Thread(target=self._install)
|
||||
thread.start()
|
||||
|
||||
def _install(self):
|
||||
"""Run a PackageKit transaction to install given packages."""
|
||||
package_ids = [packages_resolved[package_name].get_id()
|
||||
for package_name in self.package_names]
|
||||
client = packagekit.Client()
|
||||
client.set_interactive(False)
|
||||
client.install_packages(packagekit.TransactionFlagEnum.ONLY_TRUSTED,
|
||||
package_ids + [None], None,
|
||||
self.progress_callback, self)
|
||||
|
||||
def progress_callback(self, progress, progress_type, user_data):
|
||||
"""Process progress updates on package resolve operation"""
|
||||
if progress_type == packagekit.ProgressType.PERCENTAGE:
|
||||
self.percentage = progress.props.percentage
|
||||
elif progress_type == packagekit.ProgressType.PACKAGE:
|
||||
self.package = progress.props.package
|
||||
elif progress_type == packagekit.ProgressType.ALLOW_CANCEL:
|
||||
self.allow_cancel = progress.props.allow_cancel
|
||||
elif progress_type == packagekit.ProgressType.PACKAGE_ID:
|
||||
self.package_id = progress.props.package_id
|
||||
elif progress_type == packagekit.ProgressType.ITEM_PROGRESS:
|
||||
self.item_progress = progress.props.item_progress
|
||||
elif progress_type == packagekit.ProgressType.STATUS:
|
||||
self.status = progress.props.status
|
||||
self.status_string = \
|
||||
packagekit.StatusEnum.to_string(progress.props.status)
|
||||
if self.status == packagekit.StatusEnum.FINISHED:
|
||||
self.finish()
|
||||
elif progress_type == packagekit.ProgressType.TRANSACTION_FLAGS:
|
||||
self.flags = progress.props.transaction_flags
|
||||
elif progress_type == packagekit.ProgressType.ROLE:
|
||||
self.role = progress.props.role
|
||||
elif progress_type == packagekit.ProgressType.CALLER_ACTIVE:
|
||||
self.caller_active = progress.props.caller_active
|
||||
elif progress_type == packagekit.ProgressType.DOWNLOAD_SIZE_REMAINING:
|
||||
self.download_size_remaining = \
|
||||
progress.props.download_size_remaining
|
||||
else:
|
||||
logger.info('Unhandle packagekit progress callback - %s, %s',
|
||||
progress, progress_type)
|
||||
|
||||
def finish(self):
|
||||
"""Perform clean up operations on the transaction.
|
||||
|
||||
Remove self from global transactions list.
|
||||
"""
|
||||
del transactions[self.get_id()]
|
||||
|
||||
|
||||
def required(*package_names):
|
||||
"""Decorate a view to check and install required packages."""
|
||||
|
||||
def wrapper2(func):
|
||||
"""Return a function to check and install packages."""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(request, *args, **kwargs):
|
||||
"""Check and install packages required by a view."""
|
||||
if not is_installing(package_names) and \
|
||||
check_installed(package_names):
|
||||
return func(request, *args, **kwargs)
|
||||
|
||||
view = plinth.views.PackageInstallView.as_view()
|
||||
return view(request, package_names=package_names, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return wrapper2
|
||||
|
||||
|
||||
def check_installed(package_names):
|
||||
"""Return a boolean installed status of package.
|
||||
|
||||
This operation is blocking and waits until the check is finished.
|
||||
"""
|
||||
def _callback(progress, progress_type, user_data):
|
||||
"""Process progress updates on package resolve operation."""
|
||||
pass
|
||||
|
||||
client = packagekit.Client()
|
||||
response = client.resolve(packagekit.FilterEnum.INSTALLED,
|
||||
package_names + (None, ), None,
|
||||
_callback, None)
|
||||
|
||||
installed_package_names = []
|
||||
for package in response.get_package_array():
|
||||
if package.get_info() == packagekit.InfoEnum.INSTALLED:
|
||||
installed_package_names.append(package.get_name())
|
||||
|
||||
packages_resolved[package.get_name()] = package
|
||||
|
||||
return set(installed_package_names) == set(package_names)
|
||||
|
||||
|
||||
def is_installing(package_names):
|
||||
"""Return whether a set of packages are currently being installed."""
|
||||
return frozenset(package_names) in transactions
|
||||
|
||||
|
||||
def start_install(package_names):
|
||||
"""Start a PackageKit transaction to install given list of packages.
|
||||
|
||||
This operation is non-blocking at it spawns a new thread.
|
||||
"""
|
||||
transaction = Transaction(package_names)
|
||||
transactions[frozenset(package_names)] = transaction
|
||||
|
||||
transaction.start_install()
|
||||
@ -25,3 +25,6 @@ from django.dispatch import Signal
|
||||
service_enabled = Signal(providing_args=['service_id', 'enabled'])
|
||||
pre_module_loading = Signal()
|
||||
post_module_loading = Signal()
|
||||
pre_hostname_change = Signal(providing_args=['old_hostname', 'new_hostname'])
|
||||
post_hostname_change = Signal(providing_args=['old_hostname', 'new_hostname'])
|
||||
domainname_change = Signal(providing_args=['old_domainname', 'new_domainname'])
|
||||
|
||||
79
plinth/templates/package_install.html
Normal file
79
plinth/templates/package_install.html
Normal file
@ -0,0 +1,79 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
|
||||
|
||||
{% block page_head %}
|
||||
|
||||
{% if is_installing %}
|
||||
<meta http-equiv="refresh" content="3" />
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>Installation</h2>
|
||||
|
||||
{% if not is_installing %}
|
||||
|
||||
<p>This feature requires addtional packages to be installed. Do you
|
||||
wish to install them?</p>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th>Package</th><th>Summary</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for package in packages %}
|
||||
<tr>
|
||||
<td>{{ package.get_name }}</td>
|
||||
<td>{{ package.get_summary }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="btn btn-md btn-primary" value="Install" />
|
||||
</form>
|
||||
|
||||
{% else %}
|
||||
|
||||
{% for key, transaction in transactions.items %}
|
||||
<div>Installing {{ transaction.package_names|join:", " }}:
|
||||
{{ transaction.status_string }}
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-striped active"
|
||||
role="progressbar" aria-valuemin="0" aria-valuemax="100"
|
||||
aria-valuenow="{{ transaction.percentage }}"
|
||||
style="width: {{ transaction.percentage }}%">
|
||||
<span class="sr-only">{{ transaction.percentage }}% complete</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
@ -21,11 +21,42 @@ Main Plinth views
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http.response import HttpResponseRedirect
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from plinth import package as package_module
|
||||
|
||||
|
||||
def index(request):
|
||||
"""Serve the main index page"""
|
||||
"""Serve the main index page."""
|
||||
if request.user.is_authenticated():
|
||||
return HttpResponseRedirect(reverse('apps:index'))
|
||||
|
||||
return HttpResponseRedirect(reverse('help:about'))
|
||||
|
||||
|
||||
class PackageInstallView(TemplateView):
|
||||
"""View to prompt and install packages."""
|
||||
template_name = 'package_install.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Return the context data rendering the template."""
|
||||
context = super(PackageInstallView, self).get_context_data(**kwargs)
|
||||
|
||||
if 'packages_names' not in context:
|
||||
context['package_names'] = self.kwargs.get('package_names', [])
|
||||
context['packages'] = [package_module.packages_resolved[package_name]
|
||||
for package_name in context['package_names']]
|
||||
context['is_installing'] = \
|
||||
package_module.is_installing(context['package_names'])
|
||||
context['transactions'] = package_module.transactions
|
||||
|
||||
return context
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
"""Handle installing packages
|
||||
|
||||
Start the package installation, and refresh the page every x seconds to
|
||||
keep displaying PackageInstallView.get() with the installation status.
|
||||
"""
|
||||
package_module.start_install(self.kwargs['package_names'])
|
||||
return self.render_to_response(self.get_context_data())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user