url-param "dl" downloads file

This commit is contained in:
ed 2025-08-24 20:54:28 +00:00
parent 68503444c7
commit 48d6224ec8
3 changed files with 46 additions and 20 deletions

View file

@ -12,7 +12,6 @@ import random
import re
import socket
import stat
import string
import sys
import threading # typechk
import time
@ -31,7 +30,7 @@ try:
except:
pass
from .__init__ import ANYWIN, PY2, RES, TYPE_CHECKING, EnvParams, unicode
from .__init__ import ANYWIN, RES, TYPE_CHECKING, EnvParams, unicode
from .__version__ import S_VERSION
from .authsrv import LEELOO_DALLAS, VFS # typechk
from .bos import bos
@ -66,6 +65,7 @@ from .util import (
exclude_dotfiles,
formatdate,
fsenc,
gen_content_disposition,
gen_filekey,
gen_filekey_dbg,
gencookie,
@ -4013,6 +4013,13 @@ class HttpCli(object):
if not editions:
return self.tx_404()
#
# force download
if "dl" in self.ouparam:
cdis = gen_content_disposition(os.path.basename(req_path))
self.out_headers["Content-Disposition"] = cdis
#
# if-modified
@ -4181,6 +4188,13 @@ class HttpCli(object):
if not editions:
return self.tx_404()
#
# force download
if "dl" in self.ouparam:
cdis = gen_content_disposition(os.path.basename(req_path))
self.out_headers["Content-Disposition"] = cdis
#
# if-modified
@ -4729,24 +4743,7 @@ class HttpCli(object):
if maxn < nf:
raise Pebkac(400, t)
safe = (string.ascii_letters + string.digits).replace("%", "")
afn = "".join([x if x in safe.replace('"', "") else "_" for x in fn])
bascii = unicode(safe).encode("utf-8")
zb = fn.encode("utf-8", "xmlcharrefreplace")
if not PY2:
zbl = [
chr(x).encode("utf-8")
if x in bascii
else "%{:02x}".format(x).encode("ascii")
for x in zb
]
else:
zbl = [unicode(x) if x in bascii else "%{:02x}".format(ord(x)) for x in zb]
ufn = b"".join(zbl).decode("ascii")
cdis = "attachment; filename=\"{}.{}\"; filename*=UTF-8''{}.{}"
cdis = cdis.format(afn, ext, ufn, ext)
cdis = gen_content_disposition("%s.%s" % (fn, ext))
self.log(repr(cdis))
self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis})

View file

@ -52,6 +52,7 @@ from .__init__ import (
VT100,
WINDOWS,
EnvParams,
unicode,
)
from .__version__ import S_BUILD_DT, S_VERSION
from .stolen import surrogateescape
@ -115,6 +116,10 @@ IP6ALL = "0:0:0:0:0:0:0:0"
IP6_LL = ("fe8", "fe9", "fea", "feb")
IP64_LL = ("fe8", "fe9", "fea", "feb", "169.254")
UC_CDISP = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._"
BC_CDISP = UC_CDISP.encode("ascii")
UC_CDISP_SET = set(UC_CDISP)
BC_CDISP_SET = set(BC_CDISP)
try:
import fcntl
@ -2073,6 +2078,29 @@ def gencookie(
)
def gen_content_disposition(fn: str) -> str:
safe = UC_CDISP_SET
bsafe = BC_CDISP_SET
fn = fn.replace("/", "_").replace("\\", "_")
zb = fn.encode("utf-8", "xmlcharrefreplace")
if not PY2:
zbl = [
chr(x).encode("utf-8")
if x in bsafe
else "%{:02X}".format(x).encode("ascii")
for x in zb
]
else:
zbl = [unicode(x) if x in bsafe else "%{:02X}".format(ord(x)) for x in zb]
ufn = b"".join(zbl).decode("ascii")
afn = "".join([x if x in safe else "_" for x in fn]).lstrip(".")
while ".." in afn:
afn = afn.replace("..", ".")
return "attachment; filename=\"%s\"; filename*=UTF-8''%s" % (afn, ufn)
def humansize(sz: float, terse: bool = False) -> str:
for unit in HUMANSIZE_UNITS:
if sz < 1024:

View file

@ -160,6 +160,7 @@ authenticate using header `Cookie: cppwd=foo` or url param `&pw=foo`
| method | params | result |
|--|--|--|
| GET | `?dl` | download file (don't show in-browser) |
| GET | `?ls` | list files/folders at URL as JSON |
| GET | `?ls&dots` | list files/folders at URL as JSON, including dotfiles |
| GET | `?ls=t` | list files/folders at URL as plaintext |