Use rooted paths for served local files

This commit is contained in:
Hassan Raza
2026-05-23 13:31:15 +05:00
parent e1cdb70dc2
commit c2da43a91a
3 changed files with 37 additions and 32 deletions
+19 -14
View File
@@ -44,7 +44,7 @@ from calibre.gui2.viewer import link_prefix_for_location_links, performance_moni
from calibre.gui2.viewer.config import get_session_pref, load_viewer_profiles, save_viewer_profile, viewer_config_dir, vprefs
from calibre.gui2.viewer.tts import TTS
from calibre.srv.code import get_translations_data
from calibre.utils.filenames import make_long_path_useable
from calibre.utils.filenames import make_long_path_useable, path_from_root
from calibre.utils.localization import _, localize_user_manual_link
from calibre.utils.resources import get_path as P
from calibre.utils.serialize import json_loads
@@ -74,9 +74,10 @@ def get_path_for_name(name):
bdir = getattr(set_book_path, 'path', None)
if bdir is None:
return
path = os.path.abspath(os.path.join(bdir, name))
if path.startswith(bdir):
return path
try:
return path_from_root(bdir, name)
except ValueError:
pass
def get_data(name):
@@ -103,12 +104,12 @@ def background_image(encoded_fname=''):
return mt, data
except FileNotFoundError:
return 'image/jpeg', b''
fname = bytes.fromhex(encoded_fname).decode()
base = os.path.abspath(os.path.join(viewer_config_dir, 'background-images')) + os.sep
img_path = os.path.abspath(os.path.join(base, fname))
try:
fname = bytes.fromhex(encoded_fname).decode('utf-8')
img_path = path_from_root(os.path.abspath(os.path.join(viewer_config_dir, 'background-images')), fname, reject_colon=True)
except (ValueError, UnicodeDecodeError):
return 'image/jpeg', b''
mt = guess_type(fname)[0] or 'image/jpeg'
if not img_path.startswith(base):
return mt, b''
try:
with open(make_long_path_useable(img_path), 'rb') as f:
return mt, f.read()
@@ -123,8 +124,12 @@ def get_mathjax_dir():
def handle_mathjax_request(rq, name):
mathjax_dir = get_mathjax_dir()
path = os.path.abspath(os.path.join(mathjax_dir, '..', name))
if path.startswith(mathjax_dir):
mathjax_name = name.partition('/')[2]
try:
path = path_from_root(mathjax_dir, mathjax_name)
except ValueError:
pass
else:
mt = guess_type(name)
try:
with open(path, 'rb') as f:
@@ -136,9 +141,9 @@ def handle_mathjax_request(rq, name):
if name.endswith('/startup.js'):
raw = P('pdf-mathjax-loader.js', data=True, allow_user_override=False) + raw
send_reply(rq, mt, raw)
else:
prints(f'Failed to get mathjax file: {name} outside mathjax directory', file=sys.stderr)
rq.fail(QWebEngineUrlRequestJob.Error.RequestFailed)
return
prints(f'Failed to get mathjax file: {name} outside mathjax directory', file=sys.stderr)
rq.fail(QWebEngineUrlRequestJob.Error.RequestFailed)
class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
+5 -4
View File
@@ -20,7 +20,7 @@ from calibre.srv.metadata import book_as_json
from calibre.srv.render_book import RENDER_VERSION
from calibre.srv.routes import endpoint, json
from calibre.srv.utils import get_db, get_library_data
from calibre.utils.filenames import rmtree
from calibre.utils.filenames import path_from_root, rmtree
from calibre.utils.localization import _
from calibre.utils.resources import get_path as P
from calibre.utils.serialize import json_dumps
@@ -187,9 +187,10 @@ def book_file(ctx, rd, book_id, fmt, size, mtime, name):
if not ctx.has_id(rd, db, book_id):
raise BookNotFound(book_id, db)
bhash = book_hash(db.library_id, book_id, fmt, size, mtime)
base = abspath(os.path.join(books_cache_dir(), 'f'))
mpath = abspath(os.path.join(base, bhash, name))
if not mpath.startswith(base):
base = abspath(os.path.join(books_cache_dir(), 'f', bhash))
try:
mpath = path_from_root(base, name)
except ValueError:
raise HTTPNotFound(f'No book file with hash: {bhash} and name: {name}')
try:
return rd.filesystem_file_with_custom_etag(open(mpath, 'rb'), bhash, name)
+13 -14
View File
@@ -31,7 +31,7 @@ from calibre.srv.routes import endpoint, json
from calibre.srv.utils import get_db, get_use_roman, http_date
from calibre.utils.config_base import tweaks
from calibre.utils.date import timestampfromdt
from calibre.utils.filenames import ascii_filename, atomic_rename, make_long_path_useable
from calibre.utils.filenames import ascii_filename, atomic_rename, make_long_path_useable, path_from_root
from calibre.utils.img import image_from_data, scale_image
from calibre.utils.localization import _
from calibre.utils.resources import get_image_path as I
@@ -236,11 +236,10 @@ def static(ctx, rd, what):
if not what:
raise HTTPNotFound()
base = P('content-server', allow_user_override=False)
path = os.path.abspath(os.path.join(base, *what.split('/')))
if not path.startswith(base) or ':' in what:
try:
path = path_from_root(base, what, reject_colon=True)
except ValueError:
raise HTTPNotFound('Naughty, naughty!')
path = os.path.relpath(path, base).replace(os.sep, '/')
path = P('content-server/' + path)
try:
return share_open(path, 'rb')
except OSError:
@@ -269,16 +268,16 @@ def icon(ctx, rd, which):
raise HTTPNotFound()
if which.startswith('_'):
base = os.path.join(config_dir, 'tb_icons')
path = os.path.abspath(os.path.join(base, *which[1:].split('/')))
if not path.startswith(base) or ':' in which:
try:
path = path_from_root(base, which[1:], reject_colon=True)
except ValueError:
raise HTTPNotFound('Naughty, naughty!')
else:
base = P('images', allow_user_override=False)
path = os.path.abspath(os.path.join(base, *which.split('/')))
if not path.startswith(base) or ':' in which:
try:
path = path_from_root(base, which, reject_colon=True)
except ValueError:
raise HTTPNotFound('Naughty, naughty!')
path = os.path.relpath(path, base).replace(os.sep, '/')
path = P('images/' + path)
if sz == 'full':
try:
return share_open(path, 'rb')
@@ -309,9 +308,9 @@ def icon(ctx, rd, which):
@endpoint('/reader-background/{encoded_fname}', android_workaround=True)
def reader_background(ctx, rd, encoded_fname):
base = os.path.abspath(os.path.normpath(os.path.join(config_dir, 'viewer', 'background-images')))
fname = bytes.fromhex(encoded_fname)
q = os.path.abspath(os.path.normpath(os.path.join(base, fname)))
if not q.startswith(base):
try:
q = path_from_root(base, bytes.fromhex(encoded_fname).decode('utf-8'), reject_colon=True)
except (ValueError, UnicodeDecodeError):
raise HTTPNotFound(f'Reader background {encoded_fname} not found')
try:
return share_open(make_long_path_useable(q), 'rb')