summaryrefslogtreecommitdiff
path: root/searx/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'searx/plugins')
-rw-r--r--searx/plugins/__init__.py104
-rw-r--r--searx/plugins/https_rewrite.py5
-rw-r--r--searx/plugins/oa_doi_rewrite.py2
-rw-r--r--searx/plugins/open_results_on_new_tab.py25
-rw-r--r--searx/plugins/self_info.py12
-rw-r--r--searx/plugins/tracker_url_remover.py2
6 files changed, 103 insertions, 47 deletions
diff --git a/searx/plugins/__init__.py b/searx/plugins/__init__.py
index 4dbcbbd28..51f6981a2 100644
--- a/searx/plugins/__init__.py
+++ b/searx/plugins/__init__.py
@@ -14,25 +14,29 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2015 by Adam Tauber, <asciimoo@gmail.com>
'''
-from sys import exit, version_info
-from searx import logger
-if version_info[0] == 3:
- unicode = str
+from hashlib import sha256
+from importlib import import_module
+from os import listdir, makedirs, remove, stat, utime
+from os.path import abspath, basename, dirname, exists, join
+from shutil import copyfile
+from traceback import print_exc
+
+from searx import logger, settings, static_path
+
logger = logger.getChild('plugins')
from searx.plugins import (oa_doi_rewrite,
https_rewrite,
infinite_scroll,
- open_results_on_new_tab,
self_info,
search_on_category_select,
tracker_url_remover,
vim_hotkeys)
-required_attrs = (('name', (str, unicode)),
- ('description', (str, unicode)),
+required_attrs = (('name', str),
+ ('description', str),
('default_on', bool))
optional_attrs = (('js_dependencies', tuple),
@@ -54,7 +58,9 @@ class PluginStore():
for plugin in self.plugins:
yield plugin
- def register(self, *plugins):
+ def register(self, *plugins, external=False):
+ if external:
+ plugins = load_external_plugins(plugins)
for plugin in plugins:
for plugin_attr, plugin_attr_type in required_attrs:
if not hasattr(plugin, plugin_attr) or not isinstance(getattr(plugin, plugin_attr), plugin_attr_type):
@@ -77,12 +83,92 @@ class PluginStore():
return ret
+def load_external_plugins(plugin_names):
+ plugins = []
+ for name in plugin_names:
+ logger.debug('loading plugin: {0}'.format(name))
+ try:
+ pkg = import_module(name)
+ except Exception as e:
+ logger.critical('failed to load plugin module {0}: {1}'.format(name, e))
+ exit(3)
+
+ pkg.__base_path = dirname(abspath(pkg.__file__))
+
+ prepare_package_resources(pkg, name)
+
+ plugins.append(pkg)
+ logger.debug('plugin "{0}" loaded'.format(name))
+ return plugins
+
+
+def sync_resource(base_path, resource_path, name, target_dir, plugin_dir):
+ dep_path = join(base_path, resource_path)
+ file_name = basename(dep_path)
+ resource_path = join(target_dir, file_name)
+ if not exists(resource_path) or sha_sum(dep_path) != sha_sum(resource_path):
+ try:
+ copyfile(dep_path, resource_path)
+ # copy atime_ns and mtime_ns, so the weak ETags (generated by
+ # the HTTP server) do not change
+ dep_stat = stat(dep_path)
+ utime(resource_path, ns=(dep_stat.st_atime_ns, dep_stat.st_mtime_ns))
+ except:
+ logger.critical('failed to copy plugin resource {0} for plugin {1}'.format(file_name, name))
+ exit(3)
+
+ # returning with the web path of the resource
+ return join('plugins/external_plugins', plugin_dir, file_name)
+
+
+def prepare_package_resources(pkg, name):
+ plugin_dir = 'plugin_' + name
+ target_dir = join(static_path, 'plugins/external_plugins', plugin_dir)
+ try:
+ makedirs(target_dir, exist_ok=True)
+ except:
+ logger.critical('failed to create resource directory {0} for plugin {1}'.format(target_dir, name))
+ exit(3)
+
+ resources = []
+
+ if hasattr(pkg, 'js_dependencies'):
+ resources.extend(map(basename, pkg.js_dependencies))
+ pkg.js_dependencies = tuple([
+ sync_resource(pkg.__base_path, x, name, target_dir, plugin_dir)
+ for x in pkg.js_dependencies
+ ])
+ if hasattr(pkg, 'css_dependencies'):
+ resources.extend(map(basename, pkg.css_dependencies))
+ pkg.css_dependencies = tuple([
+ sync_resource(pkg.__base_path, x, name, target_dir, plugin_dir)
+ for x in pkg.css_dependencies
+ ])
+
+ for f in listdir(target_dir):
+ if basename(f) not in resources:
+ resource_path = join(target_dir, basename(f))
+ try:
+ remove(resource_path)
+ except:
+ logger.critical('failed to remove unused resource file {0} for plugin {1}'.format(resource_path, name))
+ exit(3)
+
+
+def sha_sum(filename):
+ with open(filename, "rb") as f:
+ bytes = f.read()
+ return sha256(bytes).hexdigest()
+
+
plugins = PluginStore()
plugins.register(oa_doi_rewrite)
plugins.register(https_rewrite)
plugins.register(infinite_scroll)
-plugins.register(open_results_on_new_tab)
plugins.register(self_info)
plugins.register(search_on_category_select)
plugins.register(tracker_url_remover)
plugins.register(vim_hotkeys)
+# load external plugins
+if 'plugins' in settings:
+ plugins.register(*settings['plugins'], external=True)
diff --git a/searx/plugins/https_rewrite.py b/searx/plugins/https_rewrite.py
index 82556017e..aeb42495e 100644
--- a/searx/plugins/https_rewrite.py
+++ b/searx/plugins/https_rewrite.py
@@ -16,17 +16,14 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
'''
import re
-import sys
+from urllib.parse import urlparse
from lxml import etree
from os import listdir, environ
from os.path import isfile, isdir, join
from searx.plugins import logger
from flask_babel import gettext
from searx import searx_dir
-from searx.url_utils import urlparse
-if sys.version_info[0] == 3:
- unicode = str
name = "HTTPS rewrite"
description = gettext('Rewrite HTTP links to HTTPS if possible')
diff --git a/searx/plugins/oa_doi_rewrite.py b/searx/plugins/oa_doi_rewrite.py
index be80beb26..eef29f103 100644
--- a/searx/plugins/oa_doi_rewrite.py
+++ b/searx/plugins/oa_doi_rewrite.py
@@ -1,6 +1,6 @@
+from urllib.parse import urlparse, parse_qsl
from flask_babel import gettext
import re
-from searx.url_utils import urlparse, parse_qsl
from searx import settings
diff --git a/searx/plugins/open_results_on_new_tab.py b/searx/plugins/open_results_on_new_tab.py
deleted file mode 100644
index 0f06f5a56..000000000
--- a/searx/plugins/open_results_on_new_tab.py
+++ /dev/null
@@ -1,25 +0,0 @@
-'''
-searx 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.
-
-searx 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 searx. If not, see < http://www.gnu.org/licenses/ >.
-
-(C) 2016 by Adam Tauber, <asciimoo@gmail.com>
-'''
-from flask_babel import gettext
-name = gettext('Open result links on new browser tabs')
-description = gettext('Results are opened in the same window by default. '
- 'This plugin overwrites the default behaviour to open links on new tabs/windows. '
- '(JavaScript required)')
-default_on = False
-preference_section = 'ui'
-
-js_dependencies = ('plugins/js/open_results_on_new_tab.js',)
diff --git a/searx/plugins/self_info.py b/searx/plugins/self_info.py
index 8d6c661ad..4fdfb4288 100644
--- a/searx/plugins/self_info.py
+++ b/searx/plugins/self_info.py
@@ -16,13 +16,13 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
'''
from flask_babel import gettext
import re
-name = "Self Informations"
+name = gettext('Self Informations')
description = gettext('Displays your IP if the query is "ip" and your user agent if the query contains "user agent".')
default_on = True
# Self User Agent regex
-p = re.compile(b'.*user[ -]agent.*', re.IGNORECASE)
+p = re.compile('.*user[ -]agent.*', re.IGNORECASE)
# attach callback to the post search hook
@@ -31,16 +31,14 @@ p = re.compile(b'.*user[ -]agent.*', re.IGNORECASE)
def post_search(request, search):
if search.search_query.pageno > 1:
return True
- if search.search_query.query == b'ip':
+ if search.search_query.query == 'ip':
x_forwarded_for = request.headers.getlist("X-Forwarded-For")
if x_forwarded_for:
ip = x_forwarded_for[0]
else:
ip = request.remote_addr
- search.result_container.answers.clear()
- search.result_container.answers.add(ip)
+ search.result_container.answers['ip'] = {'answer': ip}
elif p.match(search.search_query.query):
ua = request.user_agent
- search.result_container.answers.clear()
- search.result_container.answers.add(ua)
+ search.result_container.answers['user-agent'] = {'answer': ua}
return True
diff --git a/searx/plugins/tracker_url_remover.py b/searx/plugins/tracker_url_remover.py
index 33dd621e1..742f39013 100644
--- a/searx/plugins/tracker_url_remover.py
+++ b/searx/plugins/tracker_url_remover.py
@@ -17,7 +17,7 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
from flask_babel import gettext
import re
-from searx.url_utils import urlunparse, parse_qsl, urlencode
+from urllib.parse import urlunparse, parse_qsl, urlencode
regexes = {re.compile(r'utm_[^&]+'),
re.compile(r'(wkey|wemail)[^&]*'),