From 25d3f7643489315ed88116f6acbcb63bf7f6391f Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sun, 24 Feb 2019 22:52:42 +0100 Subject: [PATCH 01/27] i2p: Add new application - installs i2p from apt - accessible under /i2p/ - needed to modify diagnostics url to include numbers TODO: - fix CSS at /i2p/ : firefox NS_ERROR_INVALID_CONTENT_ENCODING ??? - all green diagnostics - functional tests - autoconfiguration in setup form - configuration of SOCKS5 proxy for network interfaces Reviewed-by: Sunil Mohan Adapa --- actions/i2p | 93 ++++++++++++ .../apache2/conf-available/i2p-plinth.conf | 22 +++ data/etc/plinth/modules-enabled/i2p | 1 + plinth/modules/diagnostics/urls.py | 2 +- plinth/modules/i2p/__init__.py | 132 ++++++++++++++++++ plinth/modules/i2p/manifest.py | 55 ++++++++ plinth/modules/i2p/urls.py | 34 +++++ static/themes/default/icons/i2p.png | Bin 0 -> 4868 bytes 8 files changed, 338 insertions(+), 1 deletion(-) create mode 100755 actions/i2p create mode 100644 data/etc/apache2/conf-available/i2p-plinth.conf create mode 100644 data/etc/plinth/modules-enabled/i2p create mode 100644 plinth/modules/i2p/__init__.py create mode 100644 plinth/modules/i2p/manifest.py create mode 100644 plinth/modules/i2p/urls.py create mode 100644 static/themes/default/icons/i2p.png diff --git a/actions/i2p b/actions/i2p new file mode 100755 index 000000000..f56d553aa --- /dev/null +++ b/actions/i2p @@ -0,0 +1,93 @@ +#!/usr/bin/python3 +# +# 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 . +# + +""" +Wrapper to list and handle system services +""" + +import argparse +import os + +from plinth import action_utils, cfg + +cfg.read() +module_config_path = os.path.join(cfg.config_dir, 'modules-enabled') + + +def add_service_action(subparsers, action, help): + parser = subparsers.add_parser(action, help=help) + + +def parse_arguments(): + """Return parsed command line arguments as dictionary.""" + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') + + add_service_action(subparsers, 'start', 'start i2p service') + add_service_action(subparsers, 'stop', 'stop i2p service') + add_service_action(subparsers, 'enable', 'enable i2p service') + add_service_action(subparsers, 'disable', 'disable i2p service') + add_service_action(subparsers, 'restart', 'restart i2p service') + add_service_action(subparsers, 'is-running', 'status of a service') + add_service_action(subparsers, 'is-enabled', 'status a service') + + subparsers.required = True + return parser.parse_args() + + +def subcommand_start(): + action_utils.service_start("i2p") + + +def subcommand_stop(): + action_utils.service_stop("i2p") + + +def subcommand_enable(): + action_utils.service_enable("i2p") + action_utils.webserver_enable("i2p-plinth") + + +def subcommand_disable(): + action_utils.service_disable("i2p") + action_utils.webserver_disable("i2p-plinth") + + +def subcommand_restart(): + action_utils.service_restart("i2p") + + +def subcommand_is_enabled(): + print(action_utils.service_is_enabled("i2p")) + + +def subcommand_is_running(): + print(action_utils.service_is_running("i2p")) + + +def main(): + """Parse arguments and perform all duties.""" + arguments = parse_arguments() + + subcommand = arguments.subcommand.replace('-', '_') + subcommand_method = globals()['subcommand_' + subcommand] + subcommand_method() + + +if __name__ == '__main__': + main() diff --git a/data/etc/apache2/conf-available/i2p-plinth.conf b/data/etc/apache2/conf-available/i2p-plinth.conf new file mode 100644 index 000000000..3aa37a579 --- /dev/null +++ b/data/etc/apache2/conf-available/i2p-plinth.conf @@ -0,0 +1,22 @@ +## +## On all sites, provide Transmission on a default path: /i2p +## +## Requires the following Apache modules to be enabled: +## mod_headers +## mod_proxy +## mod_proxy_http +## mod_proxy_html +## + + ProxyPass http://localhost:7657 + ProxyPassReverse http://localhost:7657 + + # Rewrite absolute urls from i2p to pass through apache + ProxyHTMLEnable On + ProxyHTMLURLMap / /i2p/ + + Include includes/freedombox-single-sign-on.conf + + TKTAuthToken "admin" "i2p" + + diff --git a/data/etc/plinth/modules-enabled/i2p b/data/etc/plinth/modules-enabled/i2p new file mode 100644 index 000000000..77c98ea19 --- /dev/null +++ b/data/etc/plinth/modules-enabled/i2p @@ -0,0 +1 @@ +plinth.modules.i2p diff --git a/plinth/modules/diagnostics/urls.py b/plinth/modules/diagnostics/urls.py index 7e31b8a22..56ff7ef0b 100644 --- a/plinth/modules/diagnostics/urls.py +++ b/plinth/modules/diagnostics/urls.py @@ -26,6 +26,6 @@ from . import diagnostics as views urlpatterns = [ url(r'^sys/diagnostics/$', views.index, name='index'), - url(r'^sys/diagnostics/(?P[a-z\-]+)/$', views.module, + url(r'^sys/diagnostics/(?P[1-9a-z\-]+)/$', views.module, name='module'), ] diff --git a/plinth/modules/i2p/__init__.py b/plinth/modules/i2p/__init__.py new file mode 100644 index 000000000..65eb3e170 --- /dev/null +++ b/plinth/modules/i2p/__init__.py @@ -0,0 +1,132 @@ +# +# 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 configure I2P. +""" + +from django.utils.translation import ugettext_lazy as _ + +from plinth import action_utils, actions, frontpage +from plinth import service as service_module +from plinth.menu import main_menu +from plinth.modules.users import register_group +from .manifest import backup, clients + +version = 1 + +servicename = 'i2p' + +managed_services = [servicename] + +managed_packages = ['i2p'] + +name = _('I2P') + +short_description = _('Anonymity Network') + +description = [ + _('I2P is an anonymous overlay network - a network within a network. ' + 'It is intended to protect communication from dragnet surveillance ' + 'and monitoring by third parties such as ISPs. '), + _('When enabled, I2P\'s web interface will be available from ' + '/i2p.'), +] + +clients = clients + +group = ('i2p', _('Manage I2P application')) + +service = None + +manual_page = 'I2P' + + +def init(): + """Intialize the module.""" + menu = main_menu.get('apps') + menu.add_urlname(name, 'i2p', 'i2p:index', + short_description) + register_group(group) + + global service + setup_helper = globals()['setup_helper'] + if setup_helper.get_state() != 'needs-setup': + service = service_module.Service(managed_services[0], name, ports=[ + 'http', 'https' + ], is_external=True, is_enabled=is_enabled, enable=enable, + disable=disable, + is_running=is_running) + + if is_enabled(): + add_shortcut() + + +def setup(helper, old_version=None): + """Install and configure the module.""" + + helper.install(managed_packages) + helper.call('post', action_utils.webserver_enable, "proxy_html", kind="module") + global service + if service is None: + service = service_module.Service(managed_services[0], name, ports=[ + 'http', 'https' + ], is_external=True, is_enabled=is_enabled, enable=enable, + disable=disable, + is_running=is_running) + helper.call('post', service.notify_enabled, None, True) + helper.call('post', add_shortcut) + + +def add_shortcut(): + """Helper method to add a shortcut to the frontpage.""" + frontpage.add_shortcut('i2p', name, + short_description=short_description, + url='/i2p/', login_required=True, + allowed_groups=[group[0]]) + + +def is_running(): + """Return whether the service is running.""" + return action_utils.service_is_running("i2p") + + +def is_enabled(): + """Return whether the module is enabled.""" + return action_utils.service_is_enabled("i2p") and action_utils.webserver_is_enabled("i2p-plinth") + + +def enable(): + """Enable the module.""" + actions.superuser_run("i2p", ["enable"]) + add_shortcut() + + +def disable(): + """Enable the module.""" + actions.superuser_run("i2p", ["disable"]) + frontpage.remove_shortcut('i2p') + + +def diagnose(): + """Run diagnostics and return the results.""" + results = [] + + results.extend( + action_utils.diagnose_url_on_all('https://{host}/i2p/', + check_certificate=False)) + + return results diff --git a/plinth/modules/i2p/manifest.py b/plinth/modules/i2p/manifest.py new file mode 100644 index 000000000..33de80dc2 --- /dev/null +++ b/plinth/modules/i2p/manifest.py @@ -0,0 +1,55 @@ +# +# 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 . +# + +from django.utils.translation import ugettext_lazy as _ + +from plinth.clients import validate +from plinth.modules.backups.api import validate as validate_backup + +_package_id = 'net.geti2p.i2p' +_download_url = 'https://geti2p.net/download' + +clients = validate([{ + 'name': + _('I2P'), + 'platforms': [ + { + 'type': 'package', + 'format': 'deb', + 'name': 'i2p', + }, { + 'type': 'download', + 'os': 'gnu-linux', + 'url': _download_url, + }, { + 'type': 'download', + 'os': 'macos', + 'url': _download_url, + }, { + 'type': 'download', + 'os': 'windows', + 'url': _download_url, + } + ] +}]) + +backup = validate_backup({ + 'secrets': { + 'directories': ['/var/lib/i2p/.config'] + }, + 'services': ['i2p'] +}) diff --git a/plinth/modules/i2p/urls.py b/plinth/modules/i2p/urls.py new file mode 100644 index 000000000..f2646e328 --- /dev/null +++ b/plinth/modules/i2p/urls.py @@ -0,0 +1,34 @@ +# +# 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 . +# +""" +URLs for the I2P module. +""" + +from django.conf.urls import url + +from plinth.modules import i2p +from plinth.views import ServiceView + +urlpatterns = [ + url(r'^apps/i2p/$', + ServiceView.as_view( + service_id=i2p.managed_services[0], + diagnostics_module_name='i2p', + description=i2p.description, clients=i2p.clients, + manual_page=i2p.manual_page, show_status_block=True), + name='index'), +] diff --git a/static/themes/default/icons/i2p.png b/static/themes/default/icons/i2p.png new file mode 100644 index 0000000000000000000000000000000000000000..2e5d69b1aa470c5a0c9ea753216215610ba8a21e GIT binary patch literal 4868 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 literal 0 HcmV?d00001 From f0f32b014bdd999e084e9eb9ca0c1dbf567c093e Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Wed, 27 Feb 2019 15:01:48 +0100 Subject: [PATCH 02/27] i2p: Disable compression on /i2p/ As soon as a response has to be chunked, something goes wrong and the response cannot be decoded. freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- data/etc/apache2/conf-available/i2p-plinth.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/etc/apache2/conf-available/i2p-plinth.conf b/data/etc/apache2/conf-available/i2p-plinth.conf index 3aa37a579..3262c2076 100644 --- a/data/etc/apache2/conf-available/i2p-plinth.conf +++ b/data/etc/apache2/conf-available/i2p-plinth.conf @@ -8,6 +8,10 @@ ## mod_proxy_html ## + # Disable compression + # As soon as it has to be chunked, it doesn't work + RequestHeader unset Accept-Encoding + ProxyPass http://localhost:7657 ProxyPassReverse http://localhost:7657 From f2936f0eed05b7c3c40c673e6c3e6f975df58309 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Thu, 28 Feb 2019 23:20:02 +0100 Subject: [PATCH 03/27] i2p: apache: Catch more I2P locations They used to fall through and hit a 404 freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- data/etc/apache2/conf-available/i2p-plinth.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/etc/apache2/conf-available/i2p-plinth.conf b/data/etc/apache2/conf-available/i2p-plinth.conf index 3262c2076..3848a1750 100644 --- a/data/etc/apache2/conf-available/i2p-plinth.conf +++ b/data/etc/apache2/conf-available/i2p-plinth.conf @@ -24,3 +24,7 @@ TKTAuthToken "admin" "i2p" + +# Catch some other root i2p addresses +# These are most likely generated by javascript +RedirectMatch "^/(i2p[^/]+.*)" "/i2p/$1" From 5e5e0119d21bd27e61110741d7f3f174cb0c9474 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sat, 2 Mar 2019 00:45:23 +0100 Subject: [PATCH 04/27] i2p: django: Add shortcuts to /i2p/... URLs This should help the user reach pages of the configuration more quickly freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- plinth/modules/i2p/templates/i2p_frame.html | 13 ++++ plinth/modules/i2p/urls.py | 18 ++--- plinth/modules/i2p/views.py | 75 +++++++++++++++++++++ 3 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 plinth/modules/i2p/templates/i2p_frame.html create mode 100644 plinth/modules/i2p/views.py diff --git a/plinth/modules/i2p/templates/i2p_frame.html b/plinth/modules/i2p/templates/i2p_frame.html new file mode 100644 index 000000000..4c08b6be7 --- /dev/null +++ b/plinth/modules/i2p/templates/i2p_frame.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} +{% block page_head %} + +{% endblock %} +{% block content %} + +{% endblock %} diff --git a/plinth/modules/i2p/urls.py b/plinth/modules/i2p/urls.py index f2646e328..0fcd68b48 100644 --- a/plinth/modules/i2p/urls.py +++ b/plinth/modules/i2p/urls.py @@ -20,15 +20,15 @@ URLs for the I2P module. from django.conf.urls import url -from plinth.modules import i2p -from plinth.views import ServiceView +from plinth.modules.i2p import views urlpatterns = [ - url(r'^apps/i2p/$', - ServiceView.as_view( - service_id=i2p.managed_services[0], - diagnostics_module_name='i2p', - description=i2p.description, clients=i2p.clients, - manual_page=i2p.manual_page, show_status_block=True), - name='index'), + url(r'^apps/i2p/$', views.I2PServiceView.as_view(), name='index'), + url(r'^apps/i2p/frame/tunnels/?$', views.create_i2p_frame_view( + "I2P Proxies and Tunnels", "i2ptunnel" + ), name='frame_tunnels'), + url(r'^apps/i2p/frame/torrent/?$', views.create_i2p_frame_view( + "Anonymous torrents", "i2psnark" + ), name='frame_torrent'), + ] diff --git a/plinth/modules/i2p/views.py b/plinth/modules/i2p/views.py new file mode 100644 index 000000000..9bc7fd84f --- /dev/null +++ b/plinth/modules/i2p/views.py @@ -0,0 +1,75 @@ +# +# 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 . +# +from django.template.response import TemplateResponse +from django.urls import reverse_lazy +from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy + +import plinth.modules.i2p as i2p +from plinth.views import ServiceView + +subsubmenu = [{ + 'url': reverse_lazy('i2p:index'), + 'text': ugettext_lazy('Configure') +}, { + 'url': reverse_lazy('i2p:frame_tunnels'), + 'text': ugettext_lazy('Proxies') +}, { + 'url': reverse_lazy('i2p:frame_torrent'), + 'text': ugettext_lazy('Anonymous torrents') +}] + + +class I2PServiceView(ServiceView): + """Serve configuration page.""" + service_id = i2p.servicename + description = i2p.description + diagnostics_module_name = i2p.servicename + show_status_block = False + + def get_context_data(self, **kwargs): + """Return the context data for rendering the template view.""" + context = super().get_context_data(**kwargs) + context['subsubmenu'] = subsubmenu + context['clients'] = i2p.clients + return context + + +def create_i2p_frame_view(title, rel_path): + """ + Creates a view with an iframe to the given path + + This is primarily used as a shortcut to pages under /i2p/ + + :param title: the page title that will have to be i18n + :type title: basestring + :param rel_path: the URL path after /i2p/ + :type rel_path: basestring + :return: a django view + :rtype: callable + """ + path = "/i2p/" + rel_path + + def i2p_frame_view(request): + return TemplateResponse( + request, 'i2p_frame.html', { + 'title': _(title), + 'subsubmenu': subsubmenu, + 'path': path + }) + + return i2p_frame_view From ad9712b54f50e60a93187b75f58b690b7d82a0ce Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sat, 2 Mar 2019 00:48:59 +0100 Subject: [PATCH 05/27] i2p: django: Additional information about /i2p location The first visit will start a configuration process freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- plinth/modules/i2p/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plinth/modules/i2p/__init__.py b/plinth/modules/i2p/__init__.py index 65eb3e170..6b1d2a8e0 100644 --- a/plinth/modules/i2p/__init__.py +++ b/plinth/modules/i2p/__init__.py @@ -44,6 +44,7 @@ description = [ 'and monitoring by third parties such as ISPs. '), _('When enabled, I2P\'s web interface will be available from ' '/i2p.'), + _('The first visit will initiate the configuration process, which can also be skippped') ] clients = clients From 6db2bd6e04605dcff7a4f7b6278ac55cd11bfe53 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sat, 2 Mar 2019 01:17:15 +0100 Subject: [PATCH 06/27] i2p: todo: Add TODOs for I2P freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- TODO.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 000000000..63f129291 --- /dev/null +++ b/TODO.md @@ -0,0 +1,10 @@ +# I2P + + - [ ] TODO functional tests + - [ ] TODO improve i2p_frames to add a description above the frame + - [ ] TODO configure HTTP/S proxy from freedombox interface + + disabled + + local interfaces + + 0.0.0.0 (with warning) + - [ ] TODO add TorBrowser with I2P addon in clients + - [ ] TODO add wiki in description From 2c97f76b59514338a9f0c4d191e5dfc28310cf67 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sat, 2 Mar 2019 01:29:16 +0100 Subject: [PATCH 07/27] i2p: todo: add more TODOs for I2P More pages to the favorites freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- TODO.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/TODO.md b/TODO.md index 63f129291..5354c6ea4 100644 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,14 @@ # I2P - - [ ] TODO functional tests - - [ ] TODO improve i2p_frames to add a description above the frame - - [ ] TODO configure HTTP/S proxy from freedombox interface + - [ ] functional tests + - [ ] improve i2p_frames to add a description above the frame + - [ ] configure HTTP/S proxy from freedombox interface + disabled + local interfaces + 0.0.0.0 (with warning) - - [ ] TODO add TorBrowser with I2P addon in clients - - [ ] TODO add wiki in description + - [ ] add TorBrowser with I2P addon in clients + - [ ] add wiki in description + - [ ] add more pages to the favorites at setup to router.config + - Searx instance: http://ransack.i2p + - YaCY instance: legwork.i2p + - another YaCY instance: seeker.i2p From 1b5825602328064ad5ea75df2ee60a78daa392d2 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sat, 2 Mar 2019 01:31:40 +0100 Subject: [PATCH 08/27] i2p: idea: Browse eepsites directly from freedombox freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 5354c6ea4..0bbbafe81 100644 --- a/TODO.md +++ b/TODO.md @@ -12,3 +12,5 @@ - Searx instance: http://ransack.i2p - YaCY instance: legwork.i2p - another YaCY instance: seeker.i2p + - [ ] (IDEA) Add a browsing iframe to browser eepsites + directly from freedombox through apache proxy From c199fd50cb7300a10f50665c92fa9c83fd372efc Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sat, 2 Mar 2019 01:39:26 +0100 Subject: [PATCH 09/27] i2p: todo: Add torrent tracker to list of favorites freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index 0bbbafe81..dbdb665a7 100644 --- a/TODO.md +++ b/TODO.md @@ -9,6 +9,7 @@ - [ ] add TorBrowser with I2P addon in clients - [ ] add wiki in description - [ ] add more pages to the favorites at setup to router.config + - Torrent tracker: http://tracker2.postman.i2p - Searx instance: http://ransack.i2p - YaCY instance: legwork.i2p - another YaCY instance: seeker.i2p From 23b4d33b3b15d3fe210944532e93f2b2fff6f3e9 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sat, 2 Mar 2019 16:35:10 +0100 Subject: [PATCH 10/27] i2p: django: Add description for the configuration shortcuts Tunnels have a better description now. I2P snark will need a better description and introduction freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- TODO.md | 2 +- plinth/modules/i2p/templates/i2p_frame.html | 22 ++++++++++++++++---- plinth/modules/i2p/urls.py | 8 ++----- plinth/modules/i2p/views.py | 23 +++++++++++++++++++-- 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/TODO.md b/TODO.md index dbdb665a7..f814521e0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,7 @@ # I2P - [ ] functional tests - - [ ] improve i2p_frames to add a description above the frame + - [x] improve i2p_frames to add a description above the frame - [ ] configure HTTP/S proxy from freedombox interface + disabled + local interfaces diff --git a/plinth/modules/i2p/templates/i2p_frame.html b/plinth/modules/i2p/templates/i2p_frame.html index 4c08b6be7..8c3c4a6d0 100644 --- a/plinth/modules/i2p/templates/i2p_frame.html +++ b/plinth/modules/i2p/templates/i2p_frame.html @@ -1,13 +1,27 @@ {% extends "base.html" %} {% block page_head %} {% endblock %} {% block content %} - +
+
+ {% for line in description %} +

{{ line|safe }}

+ {% endfor %} + +
+ + +
{% endblock %} diff --git a/plinth/modules/i2p/urls.py b/plinth/modules/i2p/urls.py index 0fcd68b48..fa5f0e8e1 100644 --- a/plinth/modules/i2p/urls.py +++ b/plinth/modules/i2p/urls.py @@ -24,11 +24,7 @@ from plinth.modules.i2p import views urlpatterns = [ url(r'^apps/i2p/$', views.I2PServiceView.as_view(), name='index'), - url(r'^apps/i2p/frame/tunnels/?$', views.create_i2p_frame_view( - "I2P Proxies and Tunnels", "i2ptunnel" - ), name='frame_tunnels'), - url(r'^apps/i2p/frame/torrent/?$', views.create_i2p_frame_view( - "Anonymous torrents", "i2psnark" - ), name='frame_torrent'), + url(r'^apps/i2p/frame/tunnels/?$', views.i2p_frame_tunnels, name='frame_tunnels'), + url(r'^apps/i2p/frame/torrent/?$', views.i2p_frame_torrent, name='frame_torrent'), ] diff --git a/plinth/modules/i2p/views.py b/plinth/modules/i2p/views.py index 9bc7fd84f..b818c6970 100644 --- a/plinth/modules/i2p/views.py +++ b/plinth/modules/i2p/views.py @@ -49,7 +49,7 @@ class I2PServiceView(ServiceView): return context -def create_i2p_frame_view(title, rel_path): +def _create_i2p_frame_view(title, rel_path, description): """ Creates a view with an iframe to the given path @@ -69,7 +69,26 @@ def create_i2p_frame_view(title, rel_path): request, 'i2p_frame.html', { 'title': _(title), 'subsubmenu': subsubmenu, - 'path': path + 'path': path, + 'description': description }) return i2p_frame_view + + +i2p_frame_tunnels = _create_i2p_frame_view( + "I2P Proxies and Tunnels", "i2ptunnel", [ + _('I2P has the concept of tunnels. These enter an exit the network and are configured and (de)activated here.'), + _('HTTP/S SOCKS5 proxies are entry tunnels, so they are configured here.'), + _('In order to allow usage by other members of your network, ' + 'select the proxy then the interface you want your proxies to be bound to and save the settings.' + 'The interface is in the "Reachable by" dropdown list.'), + _('You can find the IP addresses of your interfaces/connections here'), + ] +) +i2p_frame_torrent = _create_i2p_frame_view( + "Anonymous torrents", "i2psnark", [ + _('Track the progress of your anonymous torrent downlaods here.'), + _('You can find torrents on the Postman Tracker'), + ] +) From 4e7fc70192abe7b68526ab0be259b1bdc87d60d7 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sat, 2 Mar 2019 16:55:47 +0100 Subject: [PATCH 11/27] i2p: django: Add i2p homepage to description freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- TODO.md | 2 +- plinth/modules/i2p/__init__.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index f814521e0..cf4b97590 100644 --- a/TODO.md +++ b/TODO.md @@ -7,7 +7,7 @@ + local interfaces + 0.0.0.0 (with warning) - [ ] add TorBrowser with I2P addon in clients - - [ ] add wiki in description + - [x] add homepage in description - [ ] add more pages to the favorites at setup to router.config - Torrent tracker: http://tracker2.postman.i2p - Searx instance: http://ransack.i2p diff --git a/plinth/modules/i2p/__init__.py b/plinth/modules/i2p/__init__.py index 6b1d2a8e0..77e11b464 100644 --- a/plinth/modules/i2p/__init__.py +++ b/plinth/modules/i2p/__init__.py @@ -41,10 +41,12 @@ short_description = _('Anonymity Network') description = [ _('I2P is an anonymous overlay network - a network within a network. ' 'It is intended to protect communication from dragnet surveillance ' - 'and monitoring by third parties such as ISPs. '), + 'and monitoring by third parties such as ISPs.'), _('When enabled, I2P\'s web interface will be available from ' '/i2p.'), - _('The first visit will initiate the configuration process, which can also be skippped') + _('The first visit will initiate the configuration process, which can also be skippped'), + _('You can find more information about I2P one can peruse their ' + 'homepage.') ] clients = clients From 0b2922749b19db9b8854fbe14845ba4f810f3944 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sun, 3 Mar 2019 00:45:39 +0100 Subject: [PATCH 12/27] i2p: setup: Enrich I2P favorites 3 search engines and a torrent tracker added to the favorites freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- TODO.md | 2 +- actions/i2p | 89 +++++++++++++++++++++++++++------- plinth/modules/i2p/__init__.py | 15 ++++++ 3 files changed, 87 insertions(+), 19 deletions(-) diff --git a/TODO.md b/TODO.md index cf4b97590..f66959ea1 100644 --- a/TODO.md +++ b/TODO.md @@ -8,7 +8,7 @@ + 0.0.0.0 (with warning) - [ ] add TorBrowser with I2P addon in clients - [x] add homepage in description - - [ ] add more pages to the favorites at setup to router.config + - [x] add more pages to the favorites at setup to router.config - Torrent tracker: http://tracker2.postman.i2p - Searx instance: http://ransack.i2p - YaCY instance: legwork.i2p diff --git a/actions/i2p b/actions/i2p index f56d553aa..de6e5f70c 100755 --- a/actions/i2p +++ b/actions/i2p @@ -28,9 +28,7 @@ from plinth import action_utils, cfg cfg.read() module_config_path = os.path.join(cfg.config_dir, 'modules-enabled') - -def add_service_action(subparsers, action, help): - parser = subparsers.add_parser(action, help=help) +I2P_CONF_DIR = "/var/lib/i2p/i2p-config" def parse_arguments(): @@ -38,55 +36,110 @@ def parse_arguments(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - add_service_action(subparsers, 'start', 'start i2p service') - add_service_action(subparsers, 'stop', 'stop i2p service') - add_service_action(subparsers, 'enable', 'enable i2p service') - add_service_action(subparsers, 'disable', 'disable i2p service') - add_service_action(subparsers, 'restart', 'restart i2p service') - add_service_action(subparsers, 'is-running', 'status of a service') - add_service_action(subparsers, 'is-enabled', 'status a service') + subparsers.add_parser('start', help='start i2p service') + subparsers.add_parser('stop', help='stop i2p service') + subparsers.add_parser('enable', help='enable i2p service') + subparsers.add_parser('disable', help='disable i2p service') + subparsers.add_parser('restart', help='restart i2p service') + subparsers.add_parser('is-running', help='status of a service') + subparsers.add_parser('is-enabled', help='status a service') + + subparser = subparsers.add_parser('add-favorite', help='Add an eepsite to the list of favorites') + subparser.add_argument("-n", "--name", help="Name of the entry", required=True) + subparser.add_argument("-u", "--url", help="URL of the entry", required=True) subparsers.required = True return parser.parse_args() -def subcommand_start(): +def subcommand_start(arguments): action_utils.service_start("i2p") -def subcommand_stop(): +def subcommand_stop(arguments): action_utils.service_stop("i2p") -def subcommand_enable(): +def subcommand_enable(arguments): action_utils.service_enable("i2p") action_utils.webserver_enable("i2p-plinth") -def subcommand_disable(): +def subcommand_disable(arguments): action_utils.service_disable("i2p") action_utils.webserver_disable("i2p-plinth") -def subcommand_restart(): +def subcommand_restart(arguments): action_utils.service_restart("i2p") -def subcommand_is_enabled(): +def subcommand_is_enabled(arguments): print(action_utils.service_is_enabled("i2p")) -def subcommand_is_running(): +def subcommand_is_running(arguments): print(action_utils.service_is_running("i2p")) +def subcommand_add_favorite(arguments): + """ + Adds a favorite to the router.config + + :param arguments: + :type arguments: + """ + router_config_path = os.path.join(I2P_CONF_DIR, "router.config") + # Read config + with open(router_config_path) as config_file: + config_lines = config_file.readlines() + + found_favorites = False + treated = False + url = arguments.url + for i in range(len(config_lines)): + line = config_lines[i] + + # Find favorites line + if line.startswith("routerconsole.favorites"): + found_favorites = True + if url in line: + print("URL already in favorites") + exit(0) + + # Append favorite + line = "%(line)s%(name)s,%(description)s,%(url)s,%(icon)s,\n" % { + 'line': line.strip(), + 'name': arguments.name, + 'description': "", + 'url': arguments.url, + 'icon': "/themes/console/images/eepsite.png" + } + config_lines[i] = line + treated = True + break + + if not found_favorites: + print("No favorites line found") + exit(1) + if not treated: + print("Nothing to do") + exit(0) + + # Update config + with open(router_config_path, mode='w') as config_file: + config_file.writelines(config_lines) + + print("Added '%s' to favorites" % url) + + def main(): """Parse arguments and perform all duties.""" arguments = parse_arguments() subcommand = arguments.subcommand.replace('-', '_') subcommand_method = globals()['subcommand_' + subcommand] - subcommand_method() + subcommand_method(arguments) if __name__ == '__main__': diff --git a/plinth/modules/i2p/__init__.py b/plinth/modules/i2p/__init__.py index 77e11b464..36b302a74 100644 --- a/plinth/modules/i2p/__init__.py +++ b/plinth/modules/i2p/__init__.py @@ -57,6 +57,13 @@ service = None manual_page = 'I2P' +additional_favorites = [ + ("Searx instance", "http://ransack.i2p"), + ("Torrent tracker", "http://tracker2.postman.i2p"), + ("YaCy Legwork", "http://legwork.i2p"), + ("YaCy Seeker", "http://seeker.i2p"), +] + def init(): """Intialize the module.""" @@ -82,6 +89,14 @@ def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) + + # Add favorites to the configuration + for fav_name, fav_url in additional_favorites: + helper.call('post', actions.superuser_run, + "i2p", ["add-favorite", + "--name='%s'" % fav_name, + "--url='%s'" % fav_url, + ]) helper.call('post', action_utils.webserver_enable, "proxy_html", kind="module") global service if service is None: From 4d4d79228262240a59e9820a93d2ba5f423836cb Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sun, 3 Mar 2019 11:01:00 +0100 Subject: [PATCH 13/27] i2p: todo: Tick off a TODO and reword one There's no **documented** JSON-RPC for configuring a tunnel, so scratch that off the list There also doesn't seem to be a working browser bundle with I2P support. [AbscondBrowser](https://github.com/PrivacySolutions/Abscond-Browser) doesn't seem to be very active freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- TODO.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index f66959ea1..2cd4ca23e 100644 --- a/TODO.md +++ b/TODO.md @@ -2,11 +2,12 @@ - [ ] functional tests - [x] improve i2p_frames to add a description above the frame - - [ ] configure HTTP/S proxy from freedombox interface + - [x] **UNDONE: [JSON-RPC API](https://geti2p.net/en/docs/api/i2pcontrol) unknown** + configure HTTP/S proxy from freedombox interface + disabled + local interfaces + 0.0.0.0 (with warning) - - [ ] add TorBrowser with I2P addon in clients + - [ ] write tutorial on using TorBrowser with I2P SOCKS - [x] add homepage in description - [x] add more pages to the favorites at setup to router.config - Torrent tracker: http://tracker2.postman.i2p From 748787d24282bf107f9a0bbd821727d581adf616 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Mon, 4 Mar 2019 10:19:58 +0100 Subject: [PATCH 14/27] i2p: todo: Remove IDEA for browsing to .i2p sites in iframe It's too big for this PR, not important and too complicated to be secure. A guide on using the TOR Browser with extensions and/or a proxy would be better freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- TODO.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO.md b/TODO.md index 2cd4ca23e..9b86a65bb 100644 --- a/TODO.md +++ b/TODO.md @@ -14,5 +14,3 @@ - Searx instance: http://ransack.i2p - YaCY instance: legwork.i2p - another YaCY instance: seeker.i2p - - [ ] (IDEA) Add a browsing iframe to browser eepsites - directly from freedombox through apache proxy From 3d211d26eb06428bc1cd11d82592af81ac387eed Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Mon, 4 Mar 2019 10:50:11 +0100 Subject: [PATCH 15/27] i2p: torrents: Link to the list of trackers There are already a few listed, so no need to rewrite the list freedombox-team/plinth#1428 Request: I2P support Reviewed-by: Sunil Mohan Adapa --- plinth/modules/i2p/templates/i2p_frame.html | 2 +- plinth/modules/i2p/views.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plinth/modules/i2p/templates/i2p_frame.html b/plinth/modules/i2p/templates/i2p_frame.html index 8c3c4a6d0..77a556e8b 100644 --- a/plinth/modules/i2p/templates/i2p_frame.html +++ b/plinth/modules/i2p/templates/i2p_frame.html @@ -21,7 +21,7 @@ {% endfor %} - + {% endblock %} diff --git a/plinth/modules/i2p/views.py b/plinth/modules/i2p/views.py index b818c6970..0eb5ec316 100644 --- a/plinth/modules/i2p/views.py +++ b/plinth/modules/i2p/views.py @@ -88,7 +88,8 @@ i2p_frame_tunnels = _create_i2p_frame_view( ) i2p_frame_torrent = _create_i2p_frame_view( "Anonymous torrents", "i2psnark", [ - _('Track the progress of your anonymous torrent downlaods here.'), - _('You can find torrents on the Postman Tracker'), + _('Track the progress of your anonymous torrent downloads here.'), + _('You can find a list of trackers on the ' + 'Configuration page'), ] ) From d551d525376ba0f4cad6aeac37f491534a6cb5b1 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Fri, 22 Mar 2019 19:10:27 +0100 Subject: [PATCH 16/27] i2p: Add functional tests Reviewed-by: Sunil Mohan Adapa --- functional_tests/features/i2p.feature | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 functional_tests/features/i2p.feature diff --git a/functional_tests/features/i2p.feature b/functional_tests/features/i2p.feature new file mode 100644 index 000000000..4b27ad189 --- /dev/null +++ b/functional_tests/features/i2p.feature @@ -0,0 +1,34 @@ +# +# 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 . +# + +@apps @i2p-app +Feature: I2P Anonymity Network + Manage I2P configuration. + +Background: + Given I'm a logged in user + Given the i2p application is installed + +Scenario: Enable i2p application + Given the i2p application is disabled + When I enable the i2p application + Then the i2p service should be running + +Scenario: Disable i2p application + Given the i2p application is enabled + When I disable the i2p application + Then the i2p service should not be running From eb35629a8ce059682cad4a110f3cb0f6efe1eebe Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 16:07:49 -0700 Subject: [PATCH 17/27] i2p: Move data files into the app's data folder Signed-off-by: Sunil Mohan Adapa --- .../modules/i2p/data}/etc/apache2/conf-available/i2p-plinth.conf | 0 {data => plinth/modules/i2p/data}/etc/plinth/modules-enabled/i2p | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {data => plinth/modules/i2p/data}/etc/apache2/conf-available/i2p-plinth.conf (100%) rename {data => plinth/modules/i2p/data}/etc/plinth/modules-enabled/i2p (100%) diff --git a/data/etc/apache2/conf-available/i2p-plinth.conf b/plinth/modules/i2p/data/etc/apache2/conf-available/i2p-plinth.conf similarity index 100% rename from data/etc/apache2/conf-available/i2p-plinth.conf rename to plinth/modules/i2p/data/etc/apache2/conf-available/i2p-plinth.conf diff --git a/data/etc/plinth/modules-enabled/i2p b/plinth/modules/i2p/data/etc/plinth/modules-enabled/i2p similarity index 100% rename from data/etc/plinth/modules-enabled/i2p rename to plinth/modules/i2p/data/etc/plinth/modules-enabled/i2p From 4fbf7760ed96cfa2913161ffdf382a5b66d0cc58 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 16:11:50 -0700 Subject: [PATCH 18/27] i2p: Use project logo instead of mascot Source from https://commons.wikimedia.org/wiki/File:I2P_logo.svg under public domain. Modified and licensed under AGPLv3 for each of license management. Add SVG logo also for future use. Signed-off-by: Sunil Mohan Adapa --- static/themes/default/icons/i2p.png | Bin 4868 -> 26374 bytes static/themes/default/icons/i2p.svg | 196 ++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 static/themes/default/icons/i2p.svg diff --git a/static/themes/default/icons/i2p.png b/static/themes/default/icons/i2p.png index 2e5d69b1aa470c5a0c9ea753216215610ba8a21e..63e9aacfb3fdb26c85ebc0409a8b0069893f0369 100644 GIT binary patch literal 26374 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 literal 4868 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 diff --git a/static/themes/default/icons/i2p.svg b/static/themes/default/icons/i2p.svg new file mode 100644 index 000000000..1d057dbb3 --- /dev/null +++ b/static/themes/default/icons/i2p.svg @@ -0,0 +1,196 @@ + + + + + I2P + + + + + + image/svg+xml + + I2P + + + + Sunil Mohan Adapa <sunil@medhas.org> + + + 2019-04-01 + + https://commons.wikimedia.org/wiki/File:I2P_logo.svg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From de4d8ac841193de0a97c0666441ebb6d12378079 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 16:16:21 -0700 Subject: [PATCH 19/27] i2p: Remove TODO in favor of issue tracker Signed-off-by: Sunil Mohan Adapa --- TODO.md | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 TODO.md diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 9b86a65bb..000000000 --- a/TODO.md +++ /dev/null @@ -1,16 +0,0 @@ -# I2P - - - [ ] functional tests - - [x] improve i2p_frames to add a description above the frame - - [x] **UNDONE: [JSON-RPC API](https://geti2p.net/en/docs/api/i2pcontrol) unknown** - configure HTTP/S proxy from freedombox interface - + disabled - + local interfaces - + 0.0.0.0 (with warning) - - [ ] write tutorial on using TorBrowser with I2P SOCKS - - [x] add homepage in description - - [x] add more pages to the favorites at setup to router.config - - Torrent tracker: http://tracker2.postman.i2p - - Searx instance: http://ransack.i2p - - YaCY instance: legwork.i2p - - another YaCY instance: seeker.i2p From 70a9f4a6aea52eb95a36ef63433dda3114773e39 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 16:16:57 -0700 Subject: [PATCH 20/27] apache: Add proxy_html module needed by i2p app Enable by default so that app installation (algo during backup restore) does not cause apache to restart and show error pages. Signed-off-by: Sunil Mohan Adapa --- actions/apache | 1 + plinth/modules/apache/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/actions/apache b/actions/apache index 01f5e8e93..a703e1588 100755 --- a/actions/apache +++ b/actions/apache @@ -120,6 +120,7 @@ def subcommand_setup(arguments): webserver.enable('proxy', kind='module') webserver.enable('proxy_http', kind='module') webserver.enable('proxy_fcgi', kind='module') + webserver.enable('proxy_html', kind='module') webserver.enable('rewrite', kind='module') webserver.enable('macro', kind='module') diff --git a/plinth/modules/apache/__init__.py b/plinth/modules/apache/__init__.py index 581d36979..9d2a96fae 100644 --- a/plinth/modules/apache/__init__.py +++ b/plinth/modules/apache/__init__.py @@ -20,7 +20,7 @@ FreedomBox app for Apache server. from plinth import actions -version = 6 +version = 7 is_essential = True From 14b581c96db661e70e99a5b62943b6559eeabdc7 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 16:27:16 -0700 Subject: [PATCH 21/27] i2p: Backup/restore the correct state folder Signed-off-by: Sunil Mohan Adapa --- plinth/modules/i2p/manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plinth/modules/i2p/manifest.py b/plinth/modules/i2p/manifest.py index 33de80dc2..91421ee10 100644 --- a/plinth/modules/i2p/manifest.py +++ b/plinth/modules/i2p/manifest.py @@ -49,7 +49,7 @@ clients = validate([{ backup = validate_backup({ 'secrets': { - 'directories': ['/var/lib/i2p/.config'] + 'directories': ['/var/lib/i2p/i2p-config'] }, 'services': ['i2p'] }) From ba4d73ae609e40b6796981572185ee6b48bc5026 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 16:35:09 -0700 Subject: [PATCH 22/27] i2p: Minor styling changes Signed-off-by: Sunil Mohan Adapa --- actions/i2p | 11 +++++------ plinth/modules/i2p/__init__.py | 23 +++++++++++------------ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/actions/i2p b/actions/i2p index de6e5f70c..343c6e4d8 100755 --- a/actions/i2p +++ b/actions/i2p @@ -15,7 +15,6 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ Wrapper to list and handle system services """ @@ -28,7 +27,7 @@ from plinth import action_utils, cfg cfg.read() module_config_path = os.path.join(cfg.config_dir, 'modules-enabled') -I2P_CONF_DIR = "/var/lib/i2p/i2p-config" +I2P_CONF_DIR = '/var/lib/i2p/i2p-config' def parse_arguments(): @@ -89,7 +88,7 @@ def subcommand_add_favorite(arguments): :param arguments: :type arguments: """ - router_config_path = os.path.join(I2P_CONF_DIR, "router.config") + router_config_path = os.path.join(I2P_CONF_DIR, 'router.config') # Read config with open(router_config_path) as config_file: config_lines = config_file.readlines() @@ -101,10 +100,10 @@ def subcommand_add_favorite(arguments): line = config_lines[i] # Find favorites line - if line.startswith("routerconsole.favorites"): + if line.startswith('routerconsole.favorites'): found_favorites = True if url in line: - print("URL already in favorites") + print('URL already in favorites') exit(0) # Append favorite @@ -130,7 +129,7 @@ def subcommand_add_favorite(arguments): with open(router_config_path, mode='w') as config_file: config_file.writelines(config_lines) - print("Added '%s' to favorites" % url) + print('Added {} to favorites'.format(url)) def main(): diff --git a/plinth/modules/i2p/__init__.py b/plinth/modules/i2p/__init__.py index 36b302a74..485d53dfc 100644 --- a/plinth/modules/i2p/__init__.py +++ b/plinth/modules/i2p/__init__.py @@ -24,6 +24,7 @@ from plinth import action_utils, actions, frontpage from plinth import service as service_module from plinth.menu import main_menu from plinth.modules.users import register_group + from .manifest import backup, clients version = 1 @@ -58,18 +59,17 @@ service = None manual_page = 'I2P' additional_favorites = [ - ("Searx instance", "http://ransack.i2p"), - ("Torrent tracker", "http://tracker2.postman.i2p"), - ("YaCy Legwork", "http://legwork.i2p"), - ("YaCy Seeker", "http://seeker.i2p"), + ('Searx instance', 'http://ransack.i2p'), + ('Torrent tracker', 'http://tracker2.postman.i2p'), + ('YaCy Legwork', 'http://legwork.i2p'), + ('YaCy Seeker', 'http://seeker.i2p'), ] def init(): """Intialize the module.""" menu = main_menu.get('apps') - menu.add_urlname(name, 'i2p', 'i2p:index', - short_description) + menu.add_urlname(name, 'i2p', 'i2p:index', short_description) register_group(group) global service @@ -87,7 +87,6 @@ def init(): def setup(helper, old_version=None): """Install and configure the module.""" - helper.install(managed_packages) # Add favorites to the configuration @@ -105,21 +104,21 @@ def setup(helper, old_version=None): ], is_external=True, is_enabled=is_enabled, enable=enable, disable=disable, is_running=is_running) + helper.call('post', service.notify_enabled, None, True) helper.call('post', add_shortcut) def add_shortcut(): """Helper method to add a shortcut to the frontpage.""" - frontpage.add_shortcut('i2p', name, - short_description=short_description, + frontpage.add_shortcut('i2p', name, short_description=short_description, url='/i2p/', login_required=True, allowed_groups=[group[0]]) def is_running(): """Return whether the service is running.""" - return action_utils.service_is_running("i2p") + return action_utils.service_is_running('i2p') def is_enabled(): @@ -129,13 +128,13 @@ def is_enabled(): def enable(): """Enable the module.""" - actions.superuser_run("i2p", ["enable"]) + actions.superuser_run('i2p', ['enable']) add_shortcut() def disable(): """Enable the module.""" - actions.superuser_run("i2p", ["disable"]) + actions.superuser_run('i2p', ['disable']) frontpage.remove_shortcut('i2p') From c98f44f5ae453573c5916ce9a74228695e1bdc03 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 16:35:43 -0700 Subject: [PATCH 23/27] i2p: Add diagnostic test for web interface port Signed-off-by: Sunil Mohan Adapa --- plinth/modules/i2p/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plinth/modules/i2p/__init__.py b/plinth/modules/i2p/__init__.py index 485d53dfc..fe81b17df 100644 --- a/plinth/modules/i2p/__init__.py +++ b/plinth/modules/i2p/__init__.py @@ -142,6 +142,7 @@ def diagnose(): """Run diagnostics and return the results.""" results = [] + results.append(action_utils.diagnose_port_listening(7657, 'tcp6')) results.extend( action_utils.diagnose_url_on_all('https://{host}/i2p/', check_certificate=False)) From 9ceec9c8d84ade40a994731e44c02005209e82c2 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 16:36:40 -0700 Subject: [PATCH 24/27] i2p: Add main web interface to list of clients Signed-off-by: Sunil Mohan Adapa --- plinth/modules/i2p/manifest.py | 39 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/plinth/modules/i2p/manifest.py b/plinth/modules/i2p/manifest.py index 91421ee10..c65bdebe7 100644 --- a/plinth/modules/i2p/manifest.py +++ b/plinth/modules/i2p/manifest.py @@ -26,25 +26,26 @@ _download_url = 'https://geti2p.net/download' clients = validate([{ 'name': _('I2P'), - 'platforms': [ - { - 'type': 'package', - 'format': 'deb', - 'name': 'i2p', - }, { - 'type': 'download', - 'os': 'gnu-linux', - 'url': _download_url, - }, { - 'type': 'download', - 'os': 'macos', - 'url': _download_url, - }, { - 'type': 'download', - 'os': 'windows', - 'url': _download_url, - } - ] + 'platforms': [{ + 'type': 'web', + 'url': '/i2p/' + }, { + 'type': 'package', + 'format': 'deb', + 'name': 'i2p', + }, { + 'type': 'download', + 'os': 'gnu-linux', + 'url': _download_url, + }, { + 'type': 'download', + 'os': 'macos', + 'url': _download_url, + }, { + 'type': 'download', + 'os': 'windows', + 'url': _download_url, + }] }]) backup = validate_backup({ From 6e5a45a9b381967f248b0ce3645f1498d35f7a98 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 16:37:57 -0700 Subject: [PATCH 25/27] i2p: Review and cleanup action script - Remove unneeded actions: start, stop, restart, is-running and is-enabled. - Remove short form parameter passing for add-favorite action. Mostly for consistency and avoid confusion. Actions are not expected to be used by regular users. - Rename Apache configuration from: i2p-plinth.conf to i2p-freedombox.conf - Fix issue with adding favorites when none already present. This eliminates failure during first time installation for I2P. - Fix issue with incorrect new lines while editing favorites. - Minor fixes in Apache configuration. Signed-off-by: Sunil Mohan Adapa --- actions/i2p | 66 +++++-------------- plinth/modules/i2p/__init__.py | 3 +- .../{i2p-plinth.conf => i2p-freedombox.conf} | 4 +- 3 files changed, 21 insertions(+), 52 deletions(-) rename plinth/modules/i2p/data/etc/apache2/conf-available/{i2p-plinth.conf => i2p-freedombox.conf} (87%) diff --git a/actions/i2p b/actions/i2p index 343c6e4d8..9bdaacefe 100755 --- a/actions/i2p +++ b/actions/i2p @@ -35,50 +35,28 @@ def parse_arguments(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - subparsers.add_parser('start', help='start i2p service') - subparsers.add_parser('stop', help='stop i2p service') subparsers.add_parser('enable', help='enable i2p service') subparsers.add_parser('disable', help='disable i2p service') - subparsers.add_parser('restart', help='restart i2p service') - subparsers.add_parser('is-running', help='status of a service') - subparsers.add_parser('is-enabled', help='status a service') - subparser = subparsers.add_parser('add-favorite', help='Add an eepsite to the list of favorites') - subparser.add_argument("-n", "--name", help="Name of the entry", required=True) - subparser.add_argument("-u", "--url", help="URL of the entry", required=True) + subparser = subparsers.add_parser( + 'add-favorite', help='Add an eepsite to the list of favorites') + subparser.add_argument('--name', help='Name of the entry', required=True) + subparser.add_argument('--url', help='URL of the entry', required=True) subparsers.required = True return parser.parse_args() -def subcommand_start(arguments): - action_utils.service_start("i2p") +def subcommand_enable(_): + """Enable I2P service.""" + action_utils.service_enable('i2p') + action_utils.webserver_enable('i2p-freedombox') -def subcommand_stop(arguments): - action_utils.service_stop("i2p") - - -def subcommand_enable(arguments): - action_utils.service_enable("i2p") - action_utils.webserver_enable("i2p-plinth") - - -def subcommand_disable(arguments): - action_utils.service_disable("i2p") - action_utils.webserver_disable("i2p-plinth") - - -def subcommand_restart(arguments): - action_utils.service_restart("i2p") - - -def subcommand_is_enabled(arguments): - print(action_utils.service_is_enabled("i2p")) - - -def subcommand_is_running(arguments): - print(action_utils.service_is_running("i2p")) +def subcommand_disable(_): + """Disable I2P service.""" + action_utils.service_disable('i2p') + action_utils.webserver_disable('i2p-freedombox') def subcommand_add_favorite(arguments): @@ -94,8 +72,10 @@ def subcommand_add_favorite(arguments): config_lines = config_file.readlines() found_favorites = False - treated = False url = arguments.url + new_favorite = '{name},{description},{url},{icon},'.format( + name=arguments.name, description='', url=arguments.url, + icon='/themes/console/images/eepsite.png') for i in range(len(config_lines)): line = config_lines[i] @@ -107,23 +87,11 @@ def subcommand_add_favorite(arguments): exit(0) # Append favorite - line = "%(line)s%(name)s,%(description)s,%(url)s,%(icon)s,\n" % { - 'line': line.strip(), - 'name': arguments.name, - 'description': "", - 'url': arguments.url, - 'icon': "/themes/console/images/eepsite.png" - } - config_lines[i] = line - treated = True + config_lines[i] = line.strip() + new_favorite + '\n' break if not found_favorites: - print("No favorites line found") - exit(1) - if not treated: - print("Nothing to do") - exit(0) + config_lines.append('routerconsole.favorites=' + new_favorite + '\n') # Update config with open(router_config_path, mode='w') as config_file: diff --git a/plinth/modules/i2p/__init__.py b/plinth/modules/i2p/__init__.py index fe81b17df..ce8a13fab 100644 --- a/plinth/modules/i2p/__init__.py +++ b/plinth/modules/i2p/__init__.py @@ -123,7 +123,8 @@ def is_running(): def is_enabled(): """Return whether the module is enabled.""" - return action_utils.service_is_enabled("i2p") and action_utils.webserver_is_enabled("i2p-plinth") + return action_utils.service_is_enabled('i2p') and \ + action_utils.webserver_is_enabled('i2p-freedombox') def enable(): diff --git a/plinth/modules/i2p/data/etc/apache2/conf-available/i2p-plinth.conf b/plinth/modules/i2p/data/etc/apache2/conf-available/i2p-freedombox.conf similarity index 87% rename from plinth/modules/i2p/data/etc/apache2/conf-available/i2p-plinth.conf rename to plinth/modules/i2p/data/etc/apache2/conf-available/i2p-freedombox.conf index 3848a1750..54f3c45f2 100644 --- a/plinth/modules/i2p/data/etc/apache2/conf-available/i2p-plinth.conf +++ b/plinth/modules/i2p/data/etc/apache2/conf-available/i2p-freedombox.conf @@ -1,5 +1,5 @@ ## -## On all sites, provide Transmission on a default path: /i2p +## On all sites, provide I2P on a default path: /i2p ## ## Requires the following Apache modules to be enabled: ## mod_headers @@ -12,7 +12,7 @@ # As soon as it has to be chunked, it doesn't work RequestHeader unset Accept-Encoding - ProxyPass http://localhost:7657 + ProxyPass http://localhost:7657 ProxyPassReverse http://localhost:7657 # Rewrite absolute urls from i2p to pass through apache From fe03b4f34f378425fa7ed7fa67d0b60cebb26853 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 16:42:51 -0700 Subject: [PATCH 26/27] i2p: Review and update views - Update the description of the app. Remove link to web interface at it is now in clients list. - Add showing running status of the service. - Use the new style for showing subsubmenus under the description of the application. - Don't use frames to show the interface. Let users launch that interface in a new window instead. - Use class based views for I2P service views. - Update description of the I2P service views. - Minor styling updates. Signed-off-by: Sunil Mohan Adapa --- plinth/modules/i2p/__init__.py | 34 +++--- plinth/modules/i2p/templates/i2p.html | 63 ++++++++++ plinth/modules/i2p/templates/i2p_frame.html | 27 ----- plinth/modules/i2p/templates/i2p_service.html | 16 +++ plinth/modules/i2p/urls.py | 5 +- plinth/modules/i2p/views.py | 109 ++++++++++-------- 6 files changed, 159 insertions(+), 95 deletions(-) create mode 100644 plinth/modules/i2p/templates/i2p.html delete mode 100644 plinth/modules/i2p/templates/i2p_frame.html create mode 100644 plinth/modules/i2p/templates/i2p_service.html diff --git a/plinth/modules/i2p/__init__.py b/plinth/modules/i2p/__init__.py index ce8a13fab..33ca296bf 100644 --- a/plinth/modules/i2p/__init__.py +++ b/plinth/modules/i2p/__init__.py @@ -29,9 +29,9 @@ from .manifest import backup, clients version = 1 -servicename = 'i2p' +service_name = 'i2p' -managed_services = [servicename] +managed_services = [service_name] managed_packages = ['i2p'] @@ -40,14 +40,14 @@ name = _('I2P') short_description = _('Anonymity Network') description = [ - _('I2P is an anonymous overlay network - a network within a network. ' - 'It is intended to protect communication from dragnet surveillance ' - 'and monitoring by third parties such as ISPs.'), - _('When enabled, I2P\'s web interface will be available from ' - '/i2p.'), - _('The first visit will initiate the configuration process, which can also be skippped'), - _('You can find more information about I2P one can peruse their ' - 'homepage.') + _('The Invisible Internet Project is an anonymous network layer intended ' + 'to protect communication from censorship and surveillance. I2P ' + 'provides anonymity by sending encrypted traffic through a ' + 'volunteer-run network distributed around the world.'), + _('Find more information about I2P on their project ' + 'homepage.'), + _('The first visit to the provided web interface will initiate the ' + 'configuration process.') ] clients = clients @@ -91,12 +91,14 @@ def setup(helper, old_version=None): # Add favorites to the configuration for fav_name, fav_url in additional_favorites: - helper.call('post', actions.superuser_run, - "i2p", ["add-favorite", - "--name='%s'" % fav_name, - "--url='%s'" % fav_url, - ]) - helper.call('post', action_utils.webserver_enable, "proxy_html", kind="module") + helper.call('post', actions.superuser_run, 'i2p', [ + 'add-favorite', + '--name', + fav_name, + '--url', + fav_url, + ]) + helper.call('post', enable) global service if service is None: service = service_module.Service(managed_services[0], name, ports=[ diff --git a/plinth/modules/i2p/templates/i2p.html b/plinth/modules/i2p/templates/i2p.html new file mode 100644 index 000000000..84a154ca3 --- /dev/null +++ b/plinth/modules/i2p/templates/i2p.html @@ -0,0 +1,63 @@ +{% extends "service-subsubmenu.html" %} +{% comment %} +# +# 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 . +# +{% endcomment %} + +{% load bootstrap %} +{% load i18n %} + +{% block configuration %} + {% block status %} + {% if show_status_block %} +

{% trans "Status" %}

+

+ {% with service_name=service.name %} + {% if service.is_running %} + + {% blocktrans trimmed %} + Service {{ service_name }} is running. + {% endblocktrans %} + {% else %} + + {% blocktrans trimmed %} + Service {{ service_name }} is not running. + {% endblocktrans %} + {% endif %} + {% endwith %} +

+ {% endif %} + {% endblock %} + + {% block diagnostics %} + {% if diagnostics_module_name %} + {% include "diagnostics_button.html" with module=diagnostics_module_name enabled=service.is_enabled %} + {% endif %} + {% endblock %} + +

{% trans "Configuration" %}

+ +
+ {% csrf_token %} + + {{ form|bootstrap }} + + +
+ +{% endblock %} diff --git a/plinth/modules/i2p/templates/i2p_frame.html b/plinth/modules/i2p/templates/i2p_frame.html deleted file mode 100644 index 77a556e8b..000000000 --- a/plinth/modules/i2p/templates/i2p_frame.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "base.html" %} -{% block page_head %} - -{% endblock %} -{% block content %} -
-
- {% for line in description %} -

{{ line|safe }}

- {% endfor %} - -
- - -
-{% endblock %} diff --git a/plinth/modules/i2p/templates/i2p_service.html b/plinth/modules/i2p/templates/i2p_service.html new file mode 100644 index 000000000..364ba0f38 --- /dev/null +++ b/plinth/modules/i2p/templates/i2p_service.html @@ -0,0 +1,16 @@ +{% extends "service-subsubmenu.html" %} + +{% load i18n %} + +{% block configuration %} + {% for line in service_description %} +

{{ line|safe }}

+ {% endfor %} + +

+ + {% trans "Launch" %} + +

+{% endblock %} diff --git a/plinth/modules/i2p/urls.py b/plinth/modules/i2p/urls.py index fa5f0e8e1..8a62a9f76 100644 --- a/plinth/modules/i2p/urls.py +++ b/plinth/modules/i2p/urls.py @@ -24,7 +24,6 @@ from plinth.modules.i2p import views urlpatterns = [ url(r'^apps/i2p/$', views.I2PServiceView.as_view(), name='index'), - url(r'^apps/i2p/frame/tunnels/?$', views.i2p_frame_tunnels, name='frame_tunnels'), - url(r'^apps/i2p/frame/torrent/?$', views.i2p_frame_torrent, name='frame_torrent'), - + url(r'^apps/i2p/tunnels/?$', views.TunnelsView.as_view(), name='tunnels'), + url(r'^apps/i2p/torrents/?$', views.TorrentsView.as_view(), name='torrents'), ] diff --git a/plinth/modules/i2p/views.py b/plinth/modules/i2p/views.py index 0eb5ec316..ed279a739 100644 --- a/plinth/modules/i2p/views.py +++ b/plinth/modules/i2p/views.py @@ -14,10 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # -from django.template.response import TemplateResponse +""" +Views for I2P application. +""" + from django.urls import reverse_lazy from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy +from django.views.generic import TemplateView import plinth.modules.i2p as i2p from plinth.views import ServiceView @@ -26,70 +30,77 @@ subsubmenu = [{ 'url': reverse_lazy('i2p:index'), 'text': ugettext_lazy('Configure') }, { - 'url': reverse_lazy('i2p:frame_tunnels'), + 'url': reverse_lazy('i2p:tunnels'), 'text': ugettext_lazy('Proxies') -}, { - 'url': reverse_lazy('i2p:frame_torrent'), - 'text': ugettext_lazy('Anonymous torrents') -}] +}, + { + 'url': reverse_lazy('i2p:torrents'), + 'text': ugettext_lazy('Anonymous torrents') + }] class I2PServiceView(ServiceView): """Serve configuration page.""" - service_id = i2p.servicename + service_id = i2p.service_name + clients = i2p.clients description = i2p.description - diagnostics_module_name = i2p.servicename - show_status_block = False + diagnostics_module_name = i2p.service_name + show_status_block = True + template_name = 'i2p.html' def get_context_data(self, **kwargs): """Return the context data for rendering the template view.""" context = super().get_context_data(**kwargs) - context['subsubmenu'] = subsubmenu + context['title'] = i2p.name + context['description'] = i2p.description context['clients'] = i2p.clients + context['manual_page'] = i2p.manual_page + context['subsubmenu'] = subsubmenu return context -def _create_i2p_frame_view(title, rel_path, description): - """ - Creates a view with an iframe to the given path +class ServiceBaseView(TemplateView): + """View to describe and launch a service.""" + service_description = None + service_title = None + service_path = None - This is primarily used as a shortcut to pages under /i2p/ - - :param title: the page title that will have to be i18n - :type title: basestring - :param rel_path: the URL path after /i2p/ - :type rel_path: basestring - :return: a django view - :rtype: callable - """ - path = "/i2p/" + rel_path - - def i2p_frame_view(request): - return TemplateResponse( - request, 'i2p_frame.html', { - 'title': _(title), - 'subsubmenu': subsubmenu, - 'path': path, - 'description': description - }) - - return i2p_frame_view + def get_context_data(self, **kwargs): + """Add context data for template.""" + context = super().get_context_data(**kwargs) + context['title'] = i2p.name + context['description'] = i2p.description + context['clients'] = i2p.clients + context['manual_page'] = i2p.manual_page + context['subsubmenu'] = subsubmenu + context['service_title'] = self.service_title + context['service_path'] = self.service_path + context['service_description'] = self.service_description + return context -i2p_frame_tunnels = _create_i2p_frame_view( - "I2P Proxies and Tunnels", "i2ptunnel", [ - _('I2P has the concept of tunnels. These enter an exit the network and are configured and (de)activated here.'), - _('HTTP/S SOCKS5 proxies are entry tunnels, so they are configured here.'), - _('In order to allow usage by other members of your network, ' - 'select the proxy then the interface you want your proxies to be bound to and save the settings.' - 'The interface is in the "Reachable by" dropdown list.'), - _('You can find the IP addresses of your interfaces/connections here'), +class TunnelsView(ServiceBaseView): + """View to describe and launch tunnel configuration.""" + template_name = 'i2p_service.html' + service_title = _('I2P Proxies and Tunnels') + service_path = '/i2p/i2ptunnel/' + service_description = [ + _('I2P lets you browse the Internet and hidden services (eepsites) ' + 'anonymously. For this, your browser, preferably a Tor Browser, ' + 'needs to be configured for a proxy.'), + _('By default HTTP, HTTPS and SOCKS5 proxies are available. Additional ' + 'proxies and tunnels may be configured using the tunnel ' + 'configuration interface.'), ] -) -i2p_frame_torrent = _create_i2p_frame_view( - "Anonymous torrents", "i2psnark", [ - _('Track the progress of your anonymous torrent downloads here.'), - _('You can find a list of trackers on the ' - 'Configuration page'), + + +class TorrentsView(ServiceBaseView): + """View to describe and launch I2P torrents application.""" + template_name = 'i2p_service.html' + service_title = _('Anonymous Torrents') + service_path = '/i2p/i2psnark/' + service_description = [ + _('I2P provides an application to download files anonymously in a ' + 'peer-to-peer network. Download files by adding torrents or create a ' + 'new torrent to share a file.'), ] -) From 7a89474cb618331a6f755ee151c47e760def8b80 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 1 Apr 2019 17:31:36 -0700 Subject: [PATCH 27/27] i2p: Disable app until further fixes are done Signed-off-by: Sunil Mohan Adapa --- plinth/modules/i2p/data/etc/plinth/modules-enabled/i2p | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plinth/modules/i2p/data/etc/plinth/modules-enabled/i2p b/plinth/modules/i2p/data/etc/plinth/modules-enabled/i2p index 77c98ea19..a9be526d5 100644 --- a/plinth/modules/i2p/data/etc/plinth/modules-enabled/i2p +++ b/plinth/modules/i2p/data/etc/plinth/modules-enabled/i2p @@ -1 +1 @@ -plinth.modules.i2p +#plinth.modules.i2p