diff --git a/actions/matrixsynapse b/actions/matrixsynapse
new file mode 100755
index 000000000..8df75b3fb
--- /dev/null
+++ b/actions/matrixsynapse
@@ -0,0 +1,81 @@
+#!/usr/bin/python3
+# -*- mode: python -*-
+#
+# 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 .
+#
+
+"""
+Configuration helper for Matrix-Synapse server.
+"""
+import argparse
+import subprocess
+
+from plinth import action_utils
+
+
+def parse_arguments():
+ """Return parsed command line arguments as dictionary"""
+ parser = argparse.ArgumentParser()
+ subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
+
+ # Preseed debconf values before packages are installed.
+ pre_install = subparsers.add_parser(
+ 'pre-install',
+ help='Preseed debconf values before packages are installed.')
+
+ subparsers.add_parser('enable', help='Enable matrix-synapse service')
+ subparsers.add_parser('disable', help='Disable matrix-synapse service')
+ setup = subparsers.add_parser('setup', help='Set Domain name for Matrix')
+ setup.add_argument(
+ '--domain-name',
+ help='The domain name that will be used by Matrix Synapse')
+
+ return parser.parse_args()
+
+
+def subcommand_pre_install(_):
+ subprocess.check_output(
+ ['debconf-set-selections'],
+ input=b'matrix-synapse matrix-synapse/report-stats boolean ' +
+ b'false')
+
+
+def subcommand_setup(arguments):
+ domain_name = arguments.domain_name
+ action_utils.dpkg_reconfigure('matrix-synapse',
+ {'server-name': domain_name})
+
+
+def subcommand_enable(_):
+ """Enable service"""
+ action_utils.service_enable('matrix-synapse')
+
+
+def subcommand_disable(_):
+ """Disable service"""
+ action_utils.service_disable('matrix-synapse')
+
+
+def main():
+ arguments = parse_arguments()
+
+ sub_command = arguments.subcommand.replace('-', '_')
+ sub_command_method = globals()['subcommand_' + sub_command]
+ sub_command_method(arguments)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/plinth/modules/matrixsynapse/__init__.py b/plinth/modules/matrixsynapse/__init__.py
new file mode 100644
index 000000000..71682b3c2
--- /dev/null
+++ b/plinth/modules/matrixsynapse/__init__.py
@@ -0,0 +1,149 @@
+#
+# 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 .
+#
+
+"""
+Plinth module to configure matrix-synapse server
+"""
+
+import logging
+import os
+
+from django.urls import reverse_lazy
+from django.utils.translation import ugettext_lazy as _
+from ruamel.yaml.util import load_yaml_guess_indent
+
+from plinth import action_utils
+from plinth import actions
+from plinth import cfg
+from plinth import frontpage
+from plinth import service as service_module
+from plinth.modules import names
+
+version = 1
+
+depends = ['apps']
+
+managed_services = ['matrix-synapse']
+
+managed_packages = ['matrix-synapse']
+
+title = _('Federated IM, VoIP and Video server \n (matrix-synapse)')
+
+description = [
+ _('Matrix is an new ecosystem for open federated Instant Messaging '
+ 'and VoIP. Synapse is a reference Matrix server implementation.'),
+
+ _('To communicate, you can use the '
+ 'Riot client.'),
+
+ _('Changing the FreedomBox domain name needs a reinstall of '
+ 'Matrix-Synapse and you WILL LOSE DATA.')
+]
+
+service = None
+
+logger = logging.getLogger(__name__)
+
+SERVER_NAME_PATH = "/etc/matrix-synapse/conf.d/server_name.yaml"
+
+
+def init():
+ """Initialize the matrix-synapse module"""
+ menu = cfg.main_menu.get('apps:index')
+ menu.add_urlname(title, 'glyphicon-comment', 'matrixsynapse:index')
+
+ global service
+ setup_helper = globals()['setup_helper']
+ if setup_helper.get_state() != 'needs-setup':
+ service = service_module.Service(
+ 'matrix-synapse', title,
+ ports=['matrix-synapse'],
+ is_external=True, is_enabled=is_enabled, enable=enable,
+ disable=disable)
+ if is_enabled():
+ add_shortcut()
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.call('pre', actions.superuser_run, 'matrixsynapse',
+ ['pre-install'])
+ helper.install(managed_packages)
+ global service
+ if service is None:
+ service = service_module.Service(
+ 'matrix-synapse', title,
+ ports=['matrix-synapse'],
+ is_external=True, is_enabled=is_enabled, enable=enable,
+ disable=disable)
+ helper.call('post', service.notify_enabled, None, True)
+ helper.call('post', add_shortcut)
+
+
+def add_shortcut():
+ frontpage.add_shortcut('matrix-synapse', title, details=description,
+ configure_url=reverse_lazy('matrix-synapse:index'),
+ login_required=True)
+
+
+def is_setup():
+ return os.path.exists(SERVER_NAME_PATH)
+
+
+def is_enabled():
+ """Return whether the module is enabled."""
+ return action_utils.service_is_enabled('matrix-synapse')
+
+
+def get_domain_names():
+ """Return the domain name(s)"""
+ results = []
+
+ for domain_type, domains in names.domains.items():
+ if domain_type == 'hiddenservice':
+ continue
+ for domain in domains:
+ results.append((domain, domain))
+
+ return results
+
+
+def get_configured_domain_name():
+ if not is_setup():
+ return ""
+
+ with open(SERVER_NAME_PATH) as config_file:
+ config, _, _ = load_yaml_guess_indent(config_file)
+
+ return config["server_name"]
+
+
+def enable():
+ """Enable the module."""
+ actions.superuser_run('matrixsynapse', ['enable'])
+ add_shortcut()
+
+
+def disable():
+ """Enable the module."""
+ actions.superuser_run('matrixsynapse', ['disable'])
+ frontpage.remove_shortcut('matrixsynapse')
+
+
+def diagnose():
+ """Run diagnostics and return the results."""
+ return [action_utils.diagnose_port_listening(8008, 'tcp4')]
diff --git a/plinth/modules/matrixsynapse/forms.py b/plinth/modules/matrixsynapse/forms.py
new file mode 100644
index 000000000..6bb382fd3
--- /dev/null
+++ b/plinth/modules/matrixsynapse/forms.py
@@ -0,0 +1,37 @@
+#
+# 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 .
+#
+
+"""
+Forms for configuring matrix-synapse
+"""
+
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+
+from plinth.modules import matrixsynapse
+
+
+class MatrixSynapseForm(forms.Form):
+ """Form to do initial configuration of matrix-synapse"""
+ domain_name = forms.ChoiceField(
+ label=_('Select the domain name to be used for Matrix'),
+ choices=[]
+ )
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.fields['domain_name'].choices = matrixsynapse.get_domain_names()
diff --git a/plinth/modules/matrixsynapse/templates/matrix-post-setup.html b/plinth/modules/matrixsynapse/templates/matrix-post-setup.html
new file mode 100644
index 000000000..a7cf46d44
--- /dev/null
+++ b/plinth/modules/matrixsynapse/templates/matrix-post-setup.html
@@ -0,0 +1,39 @@
+{% extends "service.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
+.
+#
+{% endcomment %}
+
+{% load i18n %}
+
+{% block description %}
+
+{% for paragraph in description %}
+
{{ paragraph|safe }}
+{% endfor %}
+
+
+ {% url 'config:index' as index_url %}
+ {% blocktrans trimmed with domain_name=domain_name %}
+ The Matrix server domain is set to {{ domain_name }}. User
+ IDs will look like @username:{{ domain_name }}
+ Changing the FreedomBox domain name needs a reinstall of
+ Matrix-Synapse and you WILL LOSE DATA.
+ {% endblocktrans %}
+
+{% endblock %}
diff --git a/plinth/modules/matrixsynapse/templates/matrix-pre-setup.html b/plinth/modules/matrixsynapse/templates/matrix-pre-setup.html
new file mode 100644
index 000000000..a7037a1dc
--- /dev/null
+++ b/plinth/modules/matrixsynapse/templates/matrix-pre-setup.html
@@ -0,0 +1,62 @@
+{% 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 .
+#
+{% endcomment %}
+
+{% load bootstrap %}
+{% load i18n %}
+
+{% block content %}
+ {% block pagetitle %}
+ {{ title }}
+ {% endblock %}
+
+ {% block description %}
+ {% for paragraph in description %}
+ {{ paragraph|safe }}
+ {% endfor %}
+ {% endblock %}
+
+
+ {% url 'config:index' as index_url %}
+ {% if domain_names|length == 0 %}
+ No domain(s) are set. You can setup your domain on the system at
+ Configure page.
+ {% endif %}
+
+
+ {% block status %}
+ {% endblock %}
+
+ {% block diagnostics %}
+ {% endblock %}
+
+ {% block configuration %}
+ {% if domain_names|length > 0 %}
+ Configuration
+
+ {% endif %}
+ {% endblock %}
+{% endblock %}
diff --git a/plinth/modules/matrixsynapse/tests/__init__.py b/plinth/modules/matrixsynapse/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/plinth/modules/matrixsynapse/urls.py b/plinth/modules/matrixsynapse/urls.py
new file mode 100644
index 000000000..19e41a110
--- /dev/null
+++ b/plinth/modules/matrixsynapse/urls.py
@@ -0,0 +1,31 @@
+#
+# 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 .
+#
+
+"""
+URL for the matrix-synapse module
+"""
+
+from django.conf.urls import url
+
+from .views import MatrixSynapseSetupView, MatrixSynapseServiceView
+
+urlpatterns = [
+ url(r'^apps/matrixsynapse/setup', MatrixSynapseSetupView.as_view(),
+ name='setup'),
+ url(r'^apps/matrixsynapse/$', MatrixSynapseServiceView.as_view(),
+ name='index')
+]
diff --git a/plinth/modules/matrixsynapse/views.py b/plinth/modules/matrixsynapse/views.py
new file mode 100644
index 000000000..8100d352f
--- /dev/null
+++ b/plinth/modules/matrixsynapse/views.py
@@ -0,0 +1,71 @@
+#
+# 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 .
+#
+
+"""
+Views for the Matrix Synapse module
+"""
+from django.shortcuts import redirect
+from django.urls import reverse_lazy
+from django.views.generic import FormView
+
+from plinth import actions
+from plinth.modules import matrixsynapse
+from plinth.modules.matrixsynapse.forms import MatrixSynapseForm
+from plinth.views import ServiceView
+
+
+class MatrixSynapseSetupView(FormView):
+ """Show matrix-synapse setup page."""
+ template_name = 'matrix-pre-setup.html'
+ form_class = MatrixSynapseForm
+ description = matrixsynapse.description
+ title = matrixsynapse.title
+ success_url = reverse_lazy('matrixsynapse:index')
+
+ def form_valid(self, form):
+ domain_name = form.cleaned_data['domain_name']
+ actions.superuser_run('matrixsynapse',
+ ['setup', '--domain-name', domain_name])
+
+ return super().form_valid(form)
+
+ def get_context_data(self, *args, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['description'] = self.description
+ context['title'] = self.title
+ context['domain_names'] = matrixsynapse.get_domain_names()
+
+ return context
+
+
+class MatrixSynapseServiceView(ServiceView):
+ """Show matrix-synapse service page."""
+ service_id = matrixsynapse.managed_services[0]
+ template_name = 'matrix-post-setup.html'
+ diagnostics_module_name = 'matrixsynapse'
+
+ def dispatch(self, request, *args, **kwargs):
+ if not matrixsynapse.is_setup():
+ return redirect('matrixsynapse:setup')
+
+ return super().dispatch(request, *args, **kwargs)
+
+ def get_context_data(self, *args, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['domain_name'] = matrixsynapse.get_configured_domain_name()
+
+ return context