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 re
import socket import socket
import stat import stat
import string
import sys import sys
import threading # typechk import threading # typechk
import time import time
@ -31,7 +30,7 @@ try:
except: except:
pass 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 .__version__ import S_VERSION
from .authsrv import LEELOO_DALLAS, VFS # typechk from .authsrv import LEELOO_DALLAS, VFS # typechk
from .bos import bos from .bos import bos
@ -66,6 +65,7 @@ from .util import (
exclude_dotfiles, exclude_dotfiles,
formatdate, formatdate,
fsenc, fsenc,
gen_content_disposition,
gen_filekey, gen_filekey,
gen_filekey_dbg, gen_filekey_dbg,
gencookie, gencookie,
@ -4013,6 +4013,13 @@ class HttpCli(object):
if not editions: if not editions:
return self.tx_404() 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 # if-modified
@ -4181,6 +4188,13 @@ class HttpCli(object):
if not editions: if not editions:
return self.tx_404() 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 # if-modified
@ -4729,24 +4743,7 @@ class HttpCli(object):
if maxn < nf: if maxn < nf:
raise Pebkac(400, t) raise Pebkac(400, t)
safe = (string.ascii_letters + string.digits).replace("%", "") cdis = gen_content_disposition("%s.%s" % (fn, ext))
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)
self.log(repr(cdis)) self.log(repr(cdis))
self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis}) self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis})

View file

@ -52,6 +52,7 @@ from .__init__ import (
VT100, VT100,
WINDOWS, WINDOWS,
EnvParams, EnvParams,
unicode,
) )
from .__version__ import S_BUILD_DT, S_VERSION from .__version__ import S_BUILD_DT, S_VERSION
from .stolen import surrogateescape from .stolen import surrogateescape
@ -115,6 +116,10 @@ IP6ALL = "0:0:0:0:0:0:0:0"
IP6_LL = ("fe8", "fe9", "fea", "feb") IP6_LL = ("fe8", "fe9", "fea", "feb")
IP64_LL = ("fe8", "fe9", "fea", "feb", "169.254") 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: try:
import fcntl 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: def humansize(sz: float, terse: bool = False) -> str:
for unit in HUMANSIZE_UNITS: for unit in HUMANSIZE_UNITS:
if sz < 1024: if sz < 1024:

View file

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