mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
Allow add-on authors to set a whitelist of webview-accessible files
Extends the AddonManager API with two new methods, setWebExports and getWebExports. setWebExports expects an add-on module name and a valid RegEx pattern to match subpaths in the add-on folder against. Any matching subpaths will be accessible in Anki's web views by referencing them with /_addons/{addon_id}/{subpath}. For instance, to allow access to .css and .png files in your add-on's user_files directory you would call: > mw.addonManager.setWebExports(__name__, r"user_files/.+(\.png|\.css)") You could then reference these files in web views as such: > <img src="/_addons/{addon_id}/user_files/test.png"> Please note that this bypasses the default security policies used in Anki webviews. You should take care to construct your RegEx patterns specific enough so that they do not match any sensitive files of our add-on.
This commit is contained in:
parent
1fb6123f5a
commit
5e90758f39
3 changed files with 47 additions and 15 deletions
|
@ -409,6 +409,19 @@ Are you sure you want to continue?"""
|
|||
return
|
||||
os.rename(bp, p)
|
||||
|
||||
# Web Exports
|
||||
######################################################################
|
||||
|
||||
_webExports = {}
|
||||
|
||||
def setWebExports(self, module, pattern):
|
||||
addon = self.addonFromModule(module)
|
||||
self._webExports[addon] = pattern
|
||||
|
||||
def getWebExports(self, addon):
|
||||
return self._webExports.get(addon)
|
||||
|
||||
|
||||
# Add-ons Dialog
|
||||
######################################################################
|
||||
|
||||
|
|
|
@ -1350,8 +1350,7 @@ Please ensure a profile is open and Anki is not busy, then try again."""),
|
|||
##########################################################################
|
||||
|
||||
def setupMediaServer(self):
|
||||
self.mediaServer = aqt.mediasrv.MediaServer(
|
||||
addonFolder=self.pm.addonFolder())
|
||||
self.mediaServer = aqt.mediasrv.MediaServer(self)
|
||||
self.mediaServer.start()
|
||||
|
||||
def baseHTML(self):
|
||||
|
|
|
@ -9,6 +9,7 @@ import socketserver
|
|||
import socket
|
||||
from anki.utils import devMode
|
||||
import threading
|
||||
import re
|
||||
|
||||
# locate web folder in source/binary distribution
|
||||
def _getExportFolder():
|
||||
|
@ -47,12 +48,12 @@ class MediaServer(threading.Thread):
|
|||
_port = None
|
||||
_ready = threading.Event()
|
||||
|
||||
def __init__(self, addonFolder=None, *args, **kwargs):
|
||||
def __init__(self, mw, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._addonFolder = addonFolder
|
||||
self.mw = mw
|
||||
|
||||
def run(self):
|
||||
RequestHandler = createRequestHandler(self._addonFolder)
|
||||
RequestHandler.mw = self.mw
|
||||
self.server = ThreadedHTTPServer(("127.0.0.1", 0), RequestHandler)
|
||||
self._ready.set()
|
||||
self.server.serve_forever()
|
||||
|
@ -64,15 +65,10 @@ class MediaServer(threading.Thread):
|
|||
def shutdown(self):
|
||||
self.server.shutdown()
|
||||
|
||||
def createRequestHandler(addonFolder):
|
||||
"""RequestHandler factory"""
|
||||
return type("RequestHandler", (RequestHandler, ),
|
||||
{"_addonFolder": addonFolder})
|
||||
|
||||
class RequestHandler(http.server.SimpleHTTPRequestHandler):
|
||||
|
||||
timeout = 1
|
||||
_addonFolder = None
|
||||
mw = None
|
||||
|
||||
def do_GET(self):
|
||||
f = self.send_head()
|
||||
|
@ -131,17 +127,41 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler):
|
|||
self.log_date_time_string(),
|
||||
format%args))
|
||||
|
||||
# catch /_anki references and rewrite them to web export folder
|
||||
# catch /_addons references and rewrite them to addons folder
|
||||
|
||||
def _redirectWebExports(self, path):
|
||||
# catch /_anki references and rewrite them to web export folder
|
||||
targetPath = os.path.join(os.getcwd(), "_anki", "")
|
||||
if path.startswith(targetPath):
|
||||
newPath = os.path.join(_exportFolder, path[len(targetPath):])
|
||||
return newPath
|
||||
|
||||
# catch /_addons references and rewrite them to addons folder
|
||||
targetPath = os.path.join(os.getcwd(), "_addons", "")
|
||||
if self._addonFolder and path.startswith(targetPath):
|
||||
newPath = os.path.join(self._addonFolder, path[len(targetPath):])
|
||||
if path.startswith(targetPath):
|
||||
try:
|
||||
addMgr = self.mw.addonManager
|
||||
except AttributeError:
|
||||
return path
|
||||
|
||||
addonPath = path[len(targetPath):]
|
||||
|
||||
try:
|
||||
addon, subPath = addonPath.split(os.path.sep, 1)
|
||||
except ValueError:
|
||||
return path
|
||||
if not addon:
|
||||
return path
|
||||
|
||||
pattern = addMgr.getWebExports(addon)
|
||||
if not pattern:
|
||||
return path
|
||||
|
||||
if not re.match("^{}$".format(pattern), subPath):
|
||||
return path
|
||||
|
||||
newPath = os.path.join(addMgr.addonsFolder(), addonPath)
|
||||
return newPath
|
||||
|
||||
return path
|
||||
|
||||
# work around Windows machines with incorrect mime type
|
||||
|
|
Loading…
Reference in a new issue