mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
hooks: add permission filtering, argv-prepend;
hooks can be restricted to users with certain permissions, for example `--xm aw,notify-send` will only `notify-send` if user has write-access the user's list of permissions are now also included in the json that is passed to the hook if enabled; `--xm aw,j,notify-send` will now also stop parsing flags when encountering a blank value, allowing to specify any initial arguments to the command: `--xm aw,j,,notify-send,hey` would run `notify-send` with `hey` as its first argument, and the json would be the 2nd argument, similarly `--xm ,notify-send,hey` when no flags specified this is somewhat explained in `--help-hooks`, but additional related features are planned in the near future and will all be better documented when the dust settles
This commit is contained in:
parent
84e8e1ddfb
commit
d749683d48
|
@ -634,12 +634,12 @@ def get_sects():
|
||||||
\033[36mxban\033[35m executes CMD if someone gets banned
|
\033[36mxban\033[35m executes CMD if someone gets banned
|
||||||
\033[0m
|
\033[0m
|
||||||
can be defined as --args or volflags; for example \033[36m
|
can be defined as --args or volflags; for example \033[36m
|
||||||
--xau notify-send
|
--xau foo.py
|
||||||
-v .::r:c,xau=notify-send
|
-v .::r:c,xau=bar.py
|
||||||
\033[0m
|
\033[0m
|
||||||
commands specified as --args are appended to volflags;
|
hooks specified as commandline --args are appended to volflags;
|
||||||
each --arg and volflag can be specified multiple times,
|
each commandline --arg and volflag can be specified multiple times,
|
||||||
each command will execute in order unless one returns non-zero
|
each hook will execute in order unless one returns non-zero
|
||||||
|
|
||||||
optionally prefix the command with comma-sep. flags similar to -mtp:
|
optionally prefix the command with comma-sep. flags similar to -mtp:
|
||||||
|
|
||||||
|
@ -650,6 +650,10 @@ def get_sects():
|
||||||
\033[36mtN\033[35m sets an N sec timeout before the command is abandoned
|
\033[36mtN\033[35m sets an N sec timeout before the command is abandoned
|
||||||
\033[36miN\033[35m xiu only: volume must be idle for N sec (default = 5)
|
\033[36miN\033[35m xiu only: volume must be idle for N sec (default = 5)
|
||||||
|
|
||||||
|
\033[36mar\033[35m only run hook if user has read-access
|
||||||
|
\033[36marw\033[35m only run hook if user has read-write-access
|
||||||
|
\033[36marwmd\033[35m ...and so on... (doesn't work for xiu or xban)
|
||||||
|
|
||||||
\033[36mkt\033[35m kills the entire process tree on timeout (default),
|
\033[36mkt\033[35m kills the entire process tree on timeout (default),
|
||||||
\033[36mkm\033[35m kills just the main process
|
\033[36mkm\033[35m kills just the main process
|
||||||
\033[36mkn\033[35m lets it continue running until copyparty is terminated
|
\033[36mkn\033[35m lets it continue running until copyparty is terminated
|
||||||
|
@ -659,6 +663,21 @@ def get_sects():
|
||||||
\033[36mc2\033[35m show only stdout
|
\033[36mc2\033[35m show only stdout
|
||||||
\033[36mc3\033[35m mute all process otput
|
\033[36mc3\033[35m mute all process otput
|
||||||
\033[0m
|
\033[0m
|
||||||
|
examples:
|
||||||
|
|
||||||
|
\033[36m--xm some.py\033[35m runs \033[33msome.py msgtxt\033[35m on each 📟 message;
|
||||||
|
\033[33mmsgtxt\033[35m is the message that was written into the web-ui
|
||||||
|
|
||||||
|
\033[36m--xm j,some.py\033[35m runs \033[33msome.py jsontext\033[35m on each 📟 message;
|
||||||
|
\033[33mjsontext\033[35m is the message info (ip, user, ..., msg-text)
|
||||||
|
|
||||||
|
\033[36m--xm aw,j,some.py\033[35m requires user to have write-access
|
||||||
|
|
||||||
|
\033[36m--xm aw,,notify-send,hey,--\033[35m shows an OS alert on linux;
|
||||||
|
the \033[33m,,\033[35m stops copyparty from reading the rest as flags and
|
||||||
|
the \033[33m--\033[35m stops notify-send from reading the message as args
|
||||||
|
and the alert will be "hey" followed by the messagetext
|
||||||
|
\033[0m
|
||||||
each hook is executed once for each event, except for \033[36mxiu\033[0m
|
each hook is executed once for each event, except for \033[36mxiu\033[0m
|
||||||
which builds up a backlog of uploads, running the hook just once
|
which builds up a backlog of uploads, running the hook just once
|
||||||
as soon as the volume has been idle for iN seconds (5 by default)
|
as soon as the volume has been idle for iN seconds (5 by default)
|
||||||
|
@ -685,7 +704,10 @@ def get_sects():
|
||||||
\033[36mstash\033[35m dumps the data to file and returns length + checksum
|
\033[36mstash\033[35m dumps the data to file and returns length + checksum
|
||||||
\033[36msave,get\033[35m dumps to file and returns the page like a GET
|
\033[36msave,get\033[35m dumps to file and returns the page like a GET
|
||||||
\033[36mprint,get\033[35m prints the data in the log and returns GET
|
\033[36mprint,get\033[35m prints the data in the log and returns GET
|
||||||
(leave out the ",get" to return an error instead)
|
(leave out the ",get" to return an error instead)\033[0m
|
||||||
|
|
||||||
|
note that the \033[35m--xm\033[0m hook will only run if \033[35m--urlform\033[0m
|
||||||
|
is either \033[36mprint\033[0m or the default \033[36mprint,get\033[0m
|
||||||
"""
|
"""
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -477,6 +477,13 @@ class VFS(object):
|
||||||
)
|
)
|
||||||
# skip uhtml because it's rarely needed
|
# skip uhtml because it's rarely needed
|
||||||
|
|
||||||
|
def get_perms(self, vpath: str, uname: str) -> str:
|
||||||
|
zbl = self.can_access(vpath, uname)
|
||||||
|
ret = "".join(ch for ch, ok in zip("rwmdgGa.", zbl) if ok)
|
||||||
|
if "rwmd" in ret and "a." in ret:
|
||||||
|
ret += "A"
|
||||||
|
return ret
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
self,
|
self,
|
||||||
vpath: str,
|
vpath: str,
|
||||||
|
|
|
@ -470,9 +470,10 @@ class FtpHandler(FTPHandler):
|
||||||
None,
|
None,
|
||||||
xbu,
|
xbu,
|
||||||
ap,
|
ap,
|
||||||
vfs.canonical(rem),
|
vp,
|
||||||
"",
|
"",
|
||||||
self.uname,
|
self.uname,
|
||||||
|
self.hub.asrv.vfs.get_perms(vp, self.uname),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
self.cli_ip,
|
self.cli_ip,
|
||||||
|
|
|
@ -699,6 +699,7 @@ class HttpCli(object):
|
||||||
self.vpath,
|
self.vpath,
|
||||||
self.host,
|
self.host,
|
||||||
self.uname,
|
self.uname,
|
||||||
|
"",
|
||||||
time.time(),
|
time.time(),
|
||||||
0,
|
0,
|
||||||
self.ip,
|
self.ip,
|
||||||
|
@ -1635,6 +1636,7 @@ class HttpCli(object):
|
||||||
self.vpath,
|
self.vpath,
|
||||||
self.host,
|
self.host,
|
||||||
self.uname,
|
self.uname,
|
||||||
|
self.asrv.vfs.get_perms(self.vpath, self.uname),
|
||||||
time.time(),
|
time.time(),
|
||||||
len(buf),
|
len(buf),
|
||||||
self.ip,
|
self.ip,
|
||||||
|
@ -1784,6 +1786,7 @@ class HttpCli(object):
|
||||||
self.vpath,
|
self.vpath,
|
||||||
self.host,
|
self.host,
|
||||||
self.uname,
|
self.uname,
|
||||||
|
self.asrv.vfs.get_perms(self.vpath, self.uname),
|
||||||
at,
|
at,
|
||||||
remains,
|
remains,
|
||||||
self.ip,
|
self.ip,
|
||||||
|
@ -1874,6 +1877,7 @@ class HttpCli(object):
|
||||||
self.vpath,
|
self.vpath,
|
||||||
self.host,
|
self.host,
|
||||||
self.uname,
|
self.uname,
|
||||||
|
self.asrv.vfs.get_perms(self.vpath, self.uname),
|
||||||
mt,
|
mt,
|
||||||
post_sz,
|
post_sz,
|
||||||
self.ip,
|
self.ip,
|
||||||
|
@ -2556,6 +2560,7 @@ class HttpCli(object):
|
||||||
self.vpath,
|
self.vpath,
|
||||||
self.host,
|
self.host,
|
||||||
self.uname,
|
self.uname,
|
||||||
|
self.asrv.vfs.get_perms(self.vpath, self.uname),
|
||||||
at,
|
at,
|
||||||
0,
|
0,
|
||||||
self.ip,
|
self.ip,
|
||||||
|
@ -2619,6 +2624,7 @@ class HttpCli(object):
|
||||||
self.vpath,
|
self.vpath,
|
||||||
self.host,
|
self.host,
|
||||||
self.uname,
|
self.uname,
|
||||||
|
self.asrv.vfs.get_perms(self.vpath, self.uname),
|
||||||
at,
|
at,
|
||||||
sz,
|
sz,
|
||||||
self.ip,
|
self.ip,
|
||||||
|
@ -2863,6 +2869,7 @@ class HttpCli(object):
|
||||||
self.vpath,
|
self.vpath,
|
||||||
self.host,
|
self.host,
|
||||||
self.uname,
|
self.uname,
|
||||||
|
self.asrv.vfs.get_perms(self.vpath, self.uname),
|
||||||
time.time(),
|
time.time(),
|
||||||
0,
|
0,
|
||||||
self.ip,
|
self.ip,
|
||||||
|
@ -2901,6 +2908,7 @@ class HttpCli(object):
|
||||||
self.vpath,
|
self.vpath,
|
||||||
self.host,
|
self.host,
|
||||||
self.uname,
|
self.uname,
|
||||||
|
self.asrv.vfs.get_perms(self.vpath, self.uname),
|
||||||
new_lastmod,
|
new_lastmod,
|
||||||
sz,
|
sz,
|
||||||
self.ip,
|
self.ip,
|
||||||
|
|
|
@ -240,7 +240,7 @@ class SMB(object):
|
||||||
|
|
||||||
xbu = vfs.flags.get("xbu")
|
xbu = vfs.flags.get("xbu")
|
||||||
if xbu and not runhook(
|
if xbu and not runhook(
|
||||||
self.nlog, xbu, ap, vpath, "", "", 0, 0, "1.7.6.2", 0, ""
|
self.nlog, xbu, ap, vpath, "", "", "", 0, 0, "1.7.6.2", 0, ""
|
||||||
):
|
):
|
||||||
yeet("blocked by xbu server config: " + vpath)
|
yeet("blocked by xbu server config: " + vpath)
|
||||||
|
|
||||||
|
|
|
@ -328,7 +328,7 @@ class Tftpd(object):
|
||||||
|
|
||||||
xbu = vfs.flags.get("xbu")
|
xbu = vfs.flags.get("xbu")
|
||||||
if xbu and not runhook(
|
if xbu and not runhook(
|
||||||
self.nlog, xbu, ap, vpath, "", "", 0, 0, "8.3.8.7", 0, ""
|
self.nlog, xbu, ap, vpath, "", "", "", 0, 0, "8.3.8.7", 0, ""
|
||||||
):
|
):
|
||||||
yeet("blocked by xbu server config: " + vpath)
|
yeet("blocked by xbu server config: " + vpath)
|
||||||
|
|
||||||
|
|
|
@ -2770,6 +2770,7 @@ class Up2k(object):
|
||||||
job["vtop"],
|
job["vtop"],
|
||||||
job["host"],
|
job["host"],
|
||||||
job["user"],
|
job["user"],
|
||||||
|
self.asrv.vfs.get_perms(job["vtop"], job["user"]),
|
||||||
job["lmod"],
|
job["lmod"],
|
||||||
job["size"],
|
job["size"],
|
||||||
job["addr"],
|
job["addr"],
|
||||||
|
@ -3297,6 +3298,7 @@ class Up2k(object):
|
||||||
djoin(vtop, rd, fn),
|
djoin(vtop, rd, fn),
|
||||||
host,
|
host,
|
||||||
usr,
|
usr,
|
||||||
|
self.asrv.vfs.get_perms(djoin(vtop, rd, fn), usr),
|
||||||
int(ts),
|
int(ts),
|
||||||
sz,
|
sz,
|
||||||
ip,
|
ip,
|
||||||
|
@ -3496,6 +3498,7 @@ class Up2k(object):
|
||||||
vpath,
|
vpath,
|
||||||
"",
|
"",
|
||||||
uname,
|
uname,
|
||||||
|
self.asrv.vfs.get_perms(vpath, uname),
|
||||||
stl.st_mtime,
|
stl.st_mtime,
|
||||||
st.st_size,
|
st.st_size,
|
||||||
ip,
|
ip,
|
||||||
|
@ -3529,6 +3532,7 @@ class Up2k(object):
|
||||||
vpath,
|
vpath,
|
||||||
"",
|
"",
|
||||||
uname,
|
uname,
|
||||||
|
self.asrv.vfs.get_perms(vpath, uname),
|
||||||
stl.st_mtime,
|
stl.st_mtime,
|
||||||
st.st_size,
|
st.st_size,
|
||||||
ip,
|
ip,
|
||||||
|
@ -3661,7 +3665,18 @@ class Up2k(object):
|
||||||
xar = dvn.flags.get("xar")
|
xar = dvn.flags.get("xar")
|
||||||
if xbr:
|
if xbr:
|
||||||
if not runhook(
|
if not runhook(
|
||||||
self.log, xbr, sabs, svp, "", uname, stl.st_mtime, st.st_size, "", 0, ""
|
self.log,
|
||||||
|
xbr,
|
||||||
|
sabs,
|
||||||
|
svp,
|
||||||
|
"",
|
||||||
|
uname,
|
||||||
|
self.asrv.vfs.get_perms(svp, uname),
|
||||||
|
stl.st_mtime,
|
||||||
|
st.st_size,
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
"",
|
||||||
):
|
):
|
||||||
t = "move blocked by xbr server config: {}".format(svp)
|
t = "move blocked by xbr server config: {}".format(svp)
|
||||||
self.log(t, 1)
|
self.log(t, 1)
|
||||||
|
@ -3686,7 +3701,20 @@ class Up2k(object):
|
||||||
self.rescan_cond.notify_all()
|
self.rescan_cond.notify_all()
|
||||||
|
|
||||||
if xar:
|
if xar:
|
||||||
runhook(self.log, xar, dabs, dvp, "", uname, 0, 0, "", 0, "")
|
runhook(
|
||||||
|
self.log,
|
||||||
|
xar,
|
||||||
|
dabs,
|
||||||
|
dvp,
|
||||||
|
"",
|
||||||
|
uname,
|
||||||
|
self.asrv.vfs.get_perms(dvp, uname),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
return "k"
|
return "k"
|
||||||
|
|
||||||
|
@ -3785,7 +3813,20 @@ class Up2k(object):
|
||||||
wunlink(self.log, sabs, svn.flags)
|
wunlink(self.log, sabs, svn.flags)
|
||||||
|
|
||||||
if xar:
|
if xar:
|
||||||
runhook(self.log, xar, dabs, dvp, "", uname, 0, 0, "", 0, "")
|
runhook(
|
||||||
|
self.log,
|
||||||
|
xar,
|
||||||
|
dabs,
|
||||||
|
dvp,
|
||||||
|
"",
|
||||||
|
uname,
|
||||||
|
self.asrv.vfs.get_perms(dvp, uname),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
return "k"
|
return "k"
|
||||||
|
|
||||||
|
@ -4074,6 +4115,7 @@ class Up2k(object):
|
||||||
vp_chk,
|
vp_chk,
|
||||||
job["host"],
|
job["host"],
|
||||||
job["user"],
|
job["user"],
|
||||||
|
self.asrv.vfs.get_perms(vp_chk, job["user"]),
|
||||||
int(job["lmod"]),
|
int(job["lmod"]),
|
||||||
job["size"],
|
job["size"],
|
||||||
job["addr"],
|
job["addr"],
|
||||||
|
|
|
@ -2992,7 +2992,8 @@ def retchk(
|
||||||
|
|
||||||
def _parsehook(
|
def _parsehook(
|
||||||
log: Optional["NamedLogger"], cmd: str
|
log: Optional["NamedLogger"], cmd: str
|
||||||
) -> tuple[bool, bool, bool, float, dict[str, Any], str]:
|
) -> tuple[str, bool, bool, bool, float, dict[str, Any], list[str]]:
|
||||||
|
areq = ""
|
||||||
chk = False
|
chk = False
|
||||||
fork = False
|
fork = False
|
||||||
jtxt = False
|
jtxt = False
|
||||||
|
@ -3017,8 +3018,12 @@ def _parsehook(
|
||||||
cap = int(arg[1:]) # 0=none 1=stdout 2=stderr 3=both
|
cap = int(arg[1:]) # 0=none 1=stdout 2=stderr 3=both
|
||||||
elif arg.startswith("k"):
|
elif arg.startswith("k"):
|
||||||
kill = arg[1:] # [t]ree [m]ain [n]one
|
kill = arg[1:] # [t]ree [m]ain [n]one
|
||||||
|
elif arg.startswith("a"):
|
||||||
|
areq = arg[1:] # required perms
|
||||||
elif arg.startswith("i"):
|
elif arg.startswith("i"):
|
||||||
pass
|
pass
|
||||||
|
elif not arg:
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
t = "hook: invalid flag {} in {}"
|
t = "hook: invalid flag {} in {}"
|
||||||
(log or print)(t.format(arg, ocmd))
|
(log or print)(t.format(arg, ocmd))
|
||||||
|
@ -3045,9 +3050,11 @@ def _parsehook(
|
||||||
"capture": cap,
|
"capture": cap,
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = os.path.expandvars(os.path.expanduser(cmd))
|
argv = cmd.split(",") if "," in cmd else [cmd]
|
||||||
|
|
||||||
return chk, fork, jtxt, wait, sp_ka, cmd
|
argv[0] = os.path.expandvars(os.path.expanduser(argv[0]))
|
||||||
|
|
||||||
|
return areq, chk, fork, jtxt, wait, sp_ka, argv
|
||||||
|
|
||||||
|
|
||||||
def runihook(
|
def runihook(
|
||||||
|
@ -3056,10 +3063,9 @@ def runihook(
|
||||||
vol: "VFS",
|
vol: "VFS",
|
||||||
ups: list[tuple[str, int, int, str, str, str, int]],
|
ups: list[tuple[str, int, int, str, str, str, int]],
|
||||||
) -> bool:
|
) -> bool:
|
||||||
ocmd = cmd
|
_, chk, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
|
||||||
chk, fork, jtxt, wait, sp_ka, cmd = _parsehook(log, cmd)
|
bcmd = [sfsenc(x) for x in acmd]
|
||||||
bcmd = [sfsenc(cmd)]
|
if acmd[0].endswith(".py"):
|
||||||
if cmd.endswith(".py"):
|
|
||||||
bcmd = [sfsenc(pybin)] + bcmd
|
bcmd = [sfsenc(pybin)] + bcmd
|
||||||
|
|
||||||
vps = [vjoin(*list(s3dec(x[3], x[4]))) for x in ups]
|
vps = [vjoin(*list(s3dec(x[3], x[4]))) for x in ups]
|
||||||
|
@ -3084,7 +3090,7 @@ def runihook(
|
||||||
|
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
if fork:
|
if fork:
|
||||||
Daemon(runcmd, ocmd, [bcmd], ka=sp_ka)
|
Daemon(runcmd, cmd, bcmd, ka=sp_ka)
|
||||||
else:
|
else:
|
||||||
rc, v, err = runcmd(bcmd, **sp_ka) # type: ignore
|
rc, v, err = runcmd(bcmd, **sp_ka) # type: ignore
|
||||||
if chk and rc:
|
if chk and rc:
|
||||||
|
@ -3105,14 +3111,20 @@ def _runhook(
|
||||||
vp: str,
|
vp: str,
|
||||||
host: str,
|
host: str,
|
||||||
uname: str,
|
uname: str,
|
||||||
|
perms: str,
|
||||||
mt: float,
|
mt: float,
|
||||||
sz: int,
|
sz: int,
|
||||||
ip: str,
|
ip: str,
|
||||||
at: float,
|
at: float,
|
||||||
txt: str,
|
txt: str,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
ocmd = cmd
|
areq, chk, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
|
||||||
chk, fork, jtxt, wait, sp_ka, cmd = _parsehook(log, cmd)
|
if areq:
|
||||||
|
for ch in areq:
|
||||||
|
if ch not in perms:
|
||||||
|
t = "user %s not allowed to run hook %s; need perms %s, have %s"
|
||||||
|
log(t % (uname, cmd, areq, perms))
|
||||||
|
return True # fallthrough to next hook
|
||||||
if jtxt:
|
if jtxt:
|
||||||
ja = {
|
ja = {
|
||||||
"ap": ap,
|
"ap": ap,
|
||||||
|
@ -3123,21 +3135,22 @@ def _runhook(
|
||||||
"at": at or time.time(),
|
"at": at or time.time(),
|
||||||
"host": host,
|
"host": host,
|
||||||
"user": uname,
|
"user": uname,
|
||||||
|
"perms": perms,
|
||||||
"txt": txt,
|
"txt": txt,
|
||||||
}
|
}
|
||||||
arg = json.dumps(ja)
|
arg = json.dumps(ja)
|
||||||
else:
|
else:
|
||||||
arg = txt or ap
|
arg = txt or ap
|
||||||
|
|
||||||
acmd = [cmd, arg]
|
acmd += [arg]
|
||||||
if cmd.endswith(".py"):
|
if acmd[0].endswith(".py"):
|
||||||
acmd = [pybin] + acmd
|
acmd = [pybin] + acmd
|
||||||
|
|
||||||
bcmd = [fsenc(x) if x == ap else sfsenc(x) for x in acmd]
|
bcmd = [fsenc(x) if x == ap else sfsenc(x) for x in acmd]
|
||||||
|
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
if fork:
|
if fork:
|
||||||
Daemon(runcmd, ocmd, [bcmd], ka=sp_ka)
|
Daemon(runcmd, cmd, [bcmd], ka=sp_ka)
|
||||||
else:
|
else:
|
||||||
rc, v, err = runcmd(bcmd, **sp_ka) # type: ignore
|
rc, v, err = runcmd(bcmd, **sp_ka) # type: ignore
|
||||||
if chk and rc:
|
if chk and rc:
|
||||||
|
@ -3158,6 +3171,7 @@ def runhook(
|
||||||
vp: str,
|
vp: str,
|
||||||
host: str,
|
host: str,
|
||||||
uname: str,
|
uname: str,
|
||||||
|
perms: str,
|
||||||
mt: float,
|
mt: float,
|
||||||
sz: int,
|
sz: int,
|
||||||
ip: str,
|
ip: str,
|
||||||
|
@ -3167,7 +3181,7 @@ def runhook(
|
||||||
vp = vp.replace("\\", "/")
|
vp = vp.replace("\\", "/")
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
try:
|
try:
|
||||||
if not _runhook(log, cmd, ap, vp, host, uname, mt, sz, ip, at, txt):
|
if not _runhook(log, cmd, ap, vp, host, uname, perms, mt, sz, ip, at, txt):
|
||||||
return False
|
return False
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
(log or print)("hook: {}".format(ex))
|
(log or print)("hook: {}".format(ex))
|
||||||
|
|
|
@ -1064,7 +1064,7 @@ ebi('ops').innerHTML = (
|
||||||
'<a href="#" data-perm="write" data-dest="bup" tt="' + L.ot_bup + '">🎈</a>' +
|
'<a href="#" data-perm="write" data-dest="bup" tt="' + L.ot_bup + '">🎈</a>' +
|
||||||
'<a href="#" data-perm="write" data-dest="mkdir" tt="' + L.ot_mkdir + '">📂</a>' +
|
'<a href="#" data-perm="write" data-dest="mkdir" tt="' + L.ot_mkdir + '">📂</a>' +
|
||||||
'<a href="#" data-perm="read write" data-dest="new_md" tt="' + L.ot_md + '">📝</a>' +
|
'<a href="#" data-perm="read write" data-dest="new_md" tt="' + L.ot_md + '">📝</a>' +
|
||||||
'<a href="#" data-perm="write" data-dest="msg" tt="' + L.ot_msg + '">📟</a>' +
|
'<a href="#" data-dest="msg" tt="' + L.ot_msg + '">📟</a>' +
|
||||||
'<a href="#" data-dest="player" tt="' + L.ot_mp + '">🎺</a>' +
|
'<a href="#" data-dest="player" tt="' + L.ot_mp + '">🎺</a>' +
|
||||||
'<a href="#" data-dest="cfg" tt="' + L.ot_cfg + '">⚙️</a>' +
|
'<a href="#" data-dest="cfg" tt="' + L.ot_cfg + '">⚙️</a>' +
|
||||||
(IE ? '<span id="noie">' + L.ot_noie + '</span>' : '') +
|
(IE ? '<span id="noie">' + L.ot_noie + '</span>' : '') +
|
||||||
|
|
Loading…
Reference in a new issue