Compare commits

...

44 commits

Author SHA1 Message Date
ed fb9f0441c9 fix possible deadlock on shutdown;
if a ?tar=w is hammering the thumbnailer queue on shutdown, give up
issuing sentinels (the workers will see !!stopping and abort anyways)
2025-11-23 21:45:03 +09:00
ed a359b89edd support thumbnail pregen for all output formats 2025-11-23 21:43:55 +09:00
Habetdin 77f74ddb2f
fix ongoing-xfer links in controlpanel (#977)
when viewing the controlpanel from a path other than the webroot,
the links to ongoing transfers were invalid
2025-11-19 16:52:31 +01:00
ed f7e7b03f6f reduce redirect delay 2025-11-20 00:48:34 +09:00
ed 7a291403ca contextual login caption; closes #1012 2025-11-20 00:46:19 +09:00
ed b427d7802a metrics: allow list of users 2025-11-20 00:37:36 +09:00
ed c424a55d6f more hints for invalid config 2025-11-20 00:26:44 +09:00
ed 7d62335c13 repurpose new-md to new-textfile 2025-11-20 00:24:28 +09:00
ed 9385daeae8 fix pypi packaging; closes #1003 2025-11-19 23:49:23 +09:00
ed 79e1078671 panic if unknown users in ipu; closes #959 2025-11-19 23:28:39 +09:00
ed cad15fbf60 warn against changing -j 2025-11-19 23:26:19 +09:00
Robert Ismo 9f08efcabd
inverse unix philosophy (#994)
Signed-off-by: Robert Ismo <robertismo@protonmail.com>
Signed-off-by: ed <s@ocv.me>
Co-authored-by: ed <s@ocv.me>
2025-11-19 15:24:52 +01:00
ed ac085b8149 mtag: fix geotag usage 2025-11-02 14:56:28 +00:00
ed 1c15c0d5d1 mtag: add geotag.py 2025-11-02 13:12:13 +00:00
Nicolas Mémeint 904c984bda
nixos: Only create and bind volumes without variables (#962) 2025-11-02 12:59:48 +00:00
Nicolas Mémeint 3242145e52
nixos: Use volume/global chmod-d for module directory creation (#963) 2025-11-02 12:59:41 +00:00
ed dff6aa2435 update pkgs to 1.19.20 2025-11-02 01:13:32 +00:00
ed 450cd86dc1 v1.19.20 2025-11-02 01:09:40 +00:00
ed db60951d9f apply per-vol (md|lg)_sb during nav 2025-11-02 00:58:13 +00:00
Carson c00314a292
apply per-volume sb_lg during navigation (#967)
Signed-off-by: Carson <57198646+carson-coder@users.noreply.github.com>
2025-11-02 00:29:12 +00:00
ed 2cc53ea151 list files in /?shares; closes #961 2025-10-31 23:09:14 +00:00
ed e9ab040ce8 docker: fix crossbuild from aarch64 2025-10-26 23:30:57 +01:00
ed e005930cd0 readme: pyvips on windows 2025-10-26 23:23:17 +01:00
ed 4fcd2c4193 update pkgs to 1.19.19 2025-10-25 19:21:56 +00:00
ed cec44aa1dd v1.19.19 2025-10-25 19:18:51 +00:00
ed e3524d85bd fix for archlinux pkgbuild 2025-10-25 19:02:06 +00:00
ed 1963ed1795 update pkgs to 1.19.18 2025-10-25 14:36:57 +00:00
ed f0297777eb v1.19.18 2025-10-25 14:34:01 +00:00
ed 36ab77e0bf fix og-specific issue with single-file volumes;
if a textfile was shared with permission r below/inside a more
restrictive volume, then:

* if the parent volume had either permission g or h, then the files in
  the parent folder could be accessed

* if the parent volume had anything stricter than g or h, then filenames
  in the parent volume could be seen, but not accessed
2025-10-25 14:23:55 +00:00
ed db87ea5ce1 send msg when uploads finished; closes #949 2025-10-25 14:00:29 +00:00
ed b1efc0065c disable e2d for single-file volumes;
avoids some warnings in the log
2025-10-25 13:06:04 +00:00
ed a325353b1b option to default-disable tooltips; closes #937 2025-10-25 00:39:37 +00:00
ed 1c17b63b76 fix filesize color on select 2025-10-25 00:15:36 +00:00
ed d3dd34569a u2c: fix delete-url (closes #948);
u2c would produce an URL which triggered a windows-only
sanchk serverside, rejecting the delete-request
2025-10-24 21:48:59 +00:00
ed cdd5e78adf fix unlistc* when parent is jumpvol 2025-10-23 21:54:32 +00:00
ed fff7291dcf show h vols in ls and tree,
and compensate with some optimizations
2025-10-23 21:44:28 +00:00
ed 7f5810f1a7 ENTERPRISE packaging (closes #941);
introduce copyparty-en.pyz, affectionately known as companyparty.pyz

like copyparty-en.py, it is english-only but with a twist;
also remove smb support because the impacket references can look sus
2025-10-22 22:20:57 +00:00
ed b624a38747 ENTERPRISE file extensions (#941 omake);
in case a snakeoil salesman managed to convince your workplace into
purchasing their "internet security solution" which blocks downloads
of certain files according to file extensions -- or, in other words,
smoke and mirrors, with a comforting false sense of security
2025-10-22 22:10:02 +00:00
ed c9e45c12d8 fix permission h with og 2025-10-22 21:00:34 +00:00
/dev/urandom 5c42ad1c78
tl/esperanto: fix capitalization (#936)
Signed-off-by: /dev/urandom <53902042+slashdevslashurandom@users.noreply.github.com>
2025-10-22 19:44:05 +00:00
ed 547a7ab1cc add download mirror https://copyparty.eu/ 2025-10-19 18:19:45 +00:00
ed a04570ff5e packaging: fix $PWD assumption 2025-10-19 17:53:43 +00:00
ed 93eb862c60 docker: replace confusing config example 2025-10-19 17:52:03 +00:00
ed 81881a449b update pkgs to 1.19.17 2025-10-17 23:20:30 +00:00
62 changed files with 555 additions and 165 deletions

View file

@ -21,6 +21,7 @@ made in Norway 🇳🇴
* top
* [quickstart](#quickstart) - just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉
* [mirrors](#mirrors) - other places to download copyparty from
* [at home](#at-home) - make it accessible over the internet
* [on servers](#on-servers) - you may also want these, especially on servers
* [features](#features) - also see [comparison to similar software](./docs/versus.md)
@ -157,7 +158,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
* or install through [pypi](https://pypi.org/project/copyparty/): `python3 -m pip install --user -U copyparty`
* or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
* or install [on arch](#arch-package) [on NixOS](#nixos-module) [through nix](#nix-package)
* or install [on arch](#arch-package) / [homebrew](#homebrew-formulae) [on NixOS](#nixos-module) [through nix](#nix-package)
* or if you are on android, [install copyparty in termux](#install-on-android)
* or maybe an iPhone or iPad? [install in a-Shell on iOS](#install-on-iOS)
* or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
@ -195,6 +196,18 @@ some recommended options:
* see [accounts and volumes](#accounts-and-volumes) (or `--help-accounts`) for the syntax and other permissions
### mirrors
other places to download copyparty from (non-github links):
* https://copyparty.eu/ (hetzner, finland, official mirror):
* https://copyparty.eu/py = https://copyparty.eu/copyparty-sfx.py = the sfx
* https://copyparty.eu/en = https://copyparty.eu/copyparty-en.py = the english-only sfx
* https://copyparty.eu/pyz = https://copyparty.eu/copyparty.pyz = the zipapp
* https://copyparty.eu/enz = https://copyparty.eu/copyparty-en.pyz = the enterprise pyz
* https://copyparty.eu/cli = online cli helptext
### at home
make it accessible over the internet by starting a [cloudflare quicktunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/) like so:
@ -309,7 +322,7 @@ small collection of user feedback
project goals / philosophy
* inverse linux philosophy -- do all the things, and do an *okay* job
* inverse unix philosophy -- do all the things, and do an *okay* job
* quick drop-in service to get a lot of features in a pinch
* some of [the alternatives](./docs/versus.md) might be a better fit for you
* run anywhere, support everything
@ -621,7 +634,7 @@ the main tabs in the ui
* `[🧯]` [unpost](#unpost): undo/delete accidental uploads
* `[🚀]` and `[🎈]` are the [uploaders](#uploading)
* `[📂]` mkdir: create directories
* `[📝]` new-md: create a new markdown document
* `[📝]` new-file: create a new textfile
* `[📟]` send-msg: either to server-log or into textfiles if `--urlform save`
* `[🎺]` audio-player config options
* `[⚙️]` general client config options
@ -2969,7 +2982,9 @@ enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`
enable [smb](#smb-server) support (**not** recommended): `impacket==0.12.0`
`pyvips` gives higher quality thumbnails than `Pillow` and is 320% faster, using 270% more ram: `sudo apt install libvips42 && python3 -m pip install --user -U pyvips`
`pyvips` gives higher quality thumbnails than `Pillow` and is 320% faster, using 270% more ram
* to install `pyvips` on Linux: `sudo apt install libvips42 && python3 -m pip install --user -U pyvips`
* to install `pyvips` on windows: `pip install --user -U "pyvips[binary]"`
to install FFmpeg on Windows, grab [a recent build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) -- you need `ffmpeg.exe` and `ffprobe.exe` from inside the `bin` folder; copy them into `C:\Windows\System32` or any other folder that's in your `%PATH%`
@ -3066,7 +3081,8 @@ another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty
run it by doubleclicking it, or try typing `python copyparty.pyz` in your terminal/console/commandline/telex if that fails
it is a python [zipapp](https://docs.python.org/3/library/zipapp.html) meaning it doesn't have to unpack its own python code anywhere to run, so if the filesystem is busted it has a better chance of getting somewhere
* but note that it currently still needs to extract the web-resources somewhere (they'll land in the default TEMP-folder of your OS)
> there is also [copyparty-en.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty-en.pyz), english-only and without smb support (enterprise-friendly)
# install on android

View file

@ -68,3 +68,8 @@ instead of affecting all volumes, you can set the options for just one volume li
* `:c,mtp=key=f,audio-key.py`
* `:c,mtp=.bpm=f,audio-bpm.py`
* `:c,mtp=ahash,vhash=f,media-hash.py`
# tips & tricks
* to delete tags for all files below `blog*` and rescan that, `sqlite3 .hist/up2k.db "delete from mt where w in (select substr(w,1,16) from up where rd like 'blog%')";`

53
bin/mtag/geotag.py Executable file
View file

@ -0,0 +1,53 @@
import json
import re
import sys
from copyparty.util import fsenc, runcmd
"""
uses exiftool to geotag images based on embedded gps coordinates in exif data
adds four new metadata keys:
.gps_lat = latitute
.gps_lon = longitude
.masl = meters above sea level
city = "city, subregion, region"
usage: -mtp .masl,.gps_lat,.gps_lon,city=ad,t10,bin/mtag/geotag.py
example: https://a.ocv.me/pub/blog/j7/8/?grid=0
"""
def main():
cmd = b"exiftool -api geolocation -n".split(b" ")
rc, so, se = runcmd(cmd + [fsenc(sys.argv[1])])
ptn = re.compile("([^:]*[^ :]) *: (.*)")
city = ["", "", ""]
ret = {}
for ln in so.split("\n"):
m = ptn.match(ln)
if not m:
continue
k, v = m.groups()
if k == "Geolocation City":
city[2] = v
elif k == "Geolocation Subregion":
city[1] = v
elif k == "Geolocation Region":
city[0] = v
elif k == "GPS Latitude":
ret[".gps_lat"] = "%.04f" % (float(v),)
elif k == "GPS Longitude":
ret[".gps_lon"] = "%.04f" % (float(v),)
elif k == "GPS Altitude":
ret[".masl"] = str(int(float(v)))
v = ", ".join(city).strip(", ")
if v:
ret["city"] = v
print(json.dumps(ret))
if __name__ == "__main__":
main()

View file

@ -1,8 +1,8 @@
#!/usr/bin/env python3
from __future__ import print_function, unicode_literals
S_VERSION = "2.13"
S_BUILD_DT = "2025-09-05"
S_VERSION = "2.15"
S_BUILD_DT = "2025-10-25"
"""
u2c.py: upload to copyparty
@ -232,6 +232,7 @@ class HCli(object):
MJ = "application/json"
MO = "application/octet-stream"
MM = "application/x-www-form-urlencoded"
CLEN = "Content-Length"
web = None # type: HCli
@ -979,6 +980,7 @@ class Ctl(object):
self.nfiles, self.nbytes = self.stats
self.filegen = walkdirs([], ar.files, ar.x)
self.recheck = [] # type: list[File]
self.last_file = None
if ar.safe:
self._safe()
@ -1015,6 +1017,11 @@ class Ctl(object):
self._fancy()
file = self.last_file
if self.up_br and file:
zs = quotep(file.name.encode("utf-8", WTF8))
web.req("POST", file.url, {}, b"msg=upload-queue-empty;" + zs, MM)
self.ok = not self.errs
def _safe(self):
@ -1225,9 +1232,7 @@ class Ctl(object):
while req:
print("DELETING ~%s#%s" % (srd, len(req)))
body = json.dumps(req).encode("utf-8")
sc, txt = web.req(
"POST", self.ar.url + "?delete", {}, body, MJ
)
sc, txt = web.req("POST", "/?delete", {}, body, MJ)
if sc == 413 and "json 2big" in txt:
print(" (delete request too big; slicing...)")
req = req[: len(req) // 2]
@ -1455,6 +1460,7 @@ class Ctl(object):
file = fsl.file
cids = fsl.cids
self.last_file = file
with self.mutex:
if not self.uploader_busy:

View file

@ -48,6 +48,8 @@ let
accountsWithPlaceholders = mapAttrs (name: attrs: passwordPlaceholder name);
volumesWithoutVariables = filterAttrs (k: v: !(hasInfix "\${" v.path)) cfg.volumes;
configStr = ''
${mkSection "global" cfg.settings}
${cfg.globalExtraConfig}
@ -325,7 +327,7 @@ in
BindPaths =
(if cfg.settings ? hist then [ cfg.settings.hist ] else [ ])
++ [ externalStateDir ]
++ (mapAttrsToList (k: v: v.path) cfg.volumes);
++ (mapAttrsToList (k: v: v.path) volumesWithoutVariables);
# ProtectSystem = "strict";
# Note that unlike what 'ro' implies,
# this actually makes it impossible to read anything in the root FS,
@ -364,10 +366,20 @@ in
#: in front of things means it wont change it if the directory already exists.
group = ":${cfg.group}";
user = ":${cfg.user}";
mode = ":755";
mode = ":${
# Use volume permissions if set
if (value.flags ? chmod_d) then
value.flags.chmod_d
# Else, use global permission if set
else if (cfg.settings ? chmod-d) then
cfg.settings.chmod-d
# Else, use the default permission
else
"755"
}";
};
}
) cfg.volumes
) volumesWithoutVariables
);
users.groups = lib.mkIf (cfg.group == "copyparty") {

View file

@ -3,7 +3,7 @@
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead.
pkgname=copyparty
pkgver="1.19.16"
pkgver="1.19.20"
pkgrel=1
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
arch=("any")
@ -23,7 +23,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
)
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
backup=("etc/${pkgname}/copyparty.conf" )
sha256sums=("d8cc10d3623eaccc8acaebdd3b0102a1ebf878a03ffe6e737d132c66f799d682")
sha256sums=("050ccc34554e59210aca7a67d87a186e69b3f4dbe013d5ee2f11a22c259a82a6")
build() {
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"

View file

@ -2,7 +2,7 @@
pkgname=copyparty
pkgver=1.19.16
pkgver=1.19.20
pkgrel=1
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
arch=("any")
@ -20,7 +20,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
)
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
backup=("/etc/${pkgname}.d/init" )
sha256sums=("d8cc10d3623eaccc8acaebdd3b0102a1ebf878a03ffe6e737d132c66f799d682")
sha256sums=("050ccc34554e59210aca7a67d87a186e69b3f4dbe013d5ee2f11a22c259a82a6")
build() {
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"

View file

@ -1,5 +1,5 @@
{
"url": "https://github.com/9001/copyparty/releases/download/v1.19.16/copyparty-1.19.16.tar.gz",
"version": "1.19.16",
"hash": "sha256-2MwQ02I+rMyKyuvdOwECoev4eKA//m5zfRMsZveZ1oI="
"url": "https://github.com/9001/copyparty/releases/download/v1.19.20/copyparty-1.19.20.tar.gz",
"version": "1.19.20",
"hash": "sha256-BQzMNFVOWSEKynpn2HoYbmmz9NvgE9XuLxGiLCWagqY="
}

View file

@ -55,7 +55,7 @@ except:
zs = """
web/a/partyfuse.py
web/a/u2c.py
web/a/webdav-cfg.bat
web/a/webdav-cfg.txt
web/baguettebox.js
web/browser.css
web/browser.html
@ -125,6 +125,11 @@ web/util.js
web/w.hash.js
"""
RES = set(zs.strip().split("\n"))
RESM = {
"web/a/partyfuse.txt": "web/a/partyfuse.py",
"web/a/u2c.txt": "web/a/u2c.py",
"web/a/webdav-cfg.bat": "web/a/webdav-cfg.txt",
}
class EnvParams(object):

View file

@ -1160,7 +1160,6 @@ def add_general(ap, nc, srvname):
ap2 = ap.add_argument_group("general options")
ap2.add_argument("-c", metavar="PATH", type=u, default=CFG_DEF, action="append", help="\033[34mREPEATABLE:\033[0m add config file")
ap2.add_argument("-nc", metavar="NUM", type=int, default=nc, help="max num clients")
ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="max num cpu cores, 0=all")
ap2.add_argument("-a", metavar="ACCT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add account, \033[33mUSER\033[0m:\033[33mPASS\033[0m; example [\033[32med:wark\033[0m]")
ap2.add_argument("-v", metavar="VOL", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add volume, \033[33mSRC\033[0m:\033[33mDST\033[0m:\033[33mFLAG\033[0m; examples [\033[32m.::r\033[0m], [\033[32m/mnt/nas/music:/music:r:aed\033[0m], see --help-accounts")
ap2.add_argument("--grp", metavar="G:N,N", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add group, \033[33mNAME\033[0m:\033[33mUSER1\033[0m,\033[33mUSER2\033[0m,\033[33m...\033[0m; example [\033[32madmins:ed,foo,bar\033[0m]")
@ -1175,6 +1174,7 @@ def add_general(ap, nc, srvname):
ap2.add_argument("--mime", metavar="EXT=MIME", type=u, action="append", help="\033[34mREPEATABLE:\033[0m map file \033[33mEXT\033[0mension to \033[33mMIME\033[0mtype, for example [\033[32mjpg=image/jpeg\033[0m]")
ap2.add_argument("--mimes", action="store_true", help="list default mimetype mapping and exit")
ap2.add_argument("--rmagic", action="store_true", help="do expensive analysis to improve accuracy of returned mimetypes; will make file-downloads, rss, and webdav slower (volflag=rmagic)")
ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="num cpu-cores for uploads/downloads (0=all); keeping the default is almost always best")
ap2.add_argument("--license", action="store_true", help="show licenses and exit")
ap2.add_argument("--version", action="store_true", help="show versions and exit")
ap2.add_argument("--versionb", action="store_true", help="show version and exit")
@ -1491,6 +1491,7 @@ def add_hooks(ap):
def add_stats(ap):
ap2 = ap.add_argument_group("grafana/prometheus metrics endpoint")
ap2.add_argument("--stats", action="store_true", help="enable openmetrics at /.cpr/metrics for admin accounts")
ap2.add_argument("--stats-u", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to access /.cpr/metrics even if they aren't admin")
ap2.add_argument("--nos-hdd", action="store_true", help="disable disk-space metrics (used/free space)")
ap2.add_argument("--nos-vol", action="store_true", help="disable volume size metrics (num files, total bytes, vmaxb/vmaxn)")
ap2.add_argument("--nos-vst", action="store_true", help="disable volume state metrics (indexing, analyzing, activity)")
@ -1796,6 +1797,7 @@ def add_ui(ap, retry: int):
ap2.add_argument("--ufavico", metavar="TXT", type=u, default="", help="URL to .ico/png/gif/svg file; \033[33m--favico\033[0m takes precedence unless disabled (volflag=ufavico)")
ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="\033[34mREPEATABLE:\033[0m use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")
ap2.add_argument("--mpmc", type=u, default="", help=argparse.SUPPRESS)
ap2.add_argument("--notooltips", action="store_true", help="tooltips disabled as default")
ap2.add_argument("--spinner", metavar="TXT", type=u, default="🌲", help="\033[33memoji\033[0m or \033[33memoji,css\033[0m Example: [\033[32m🥖,padding:0\033[0m]")
ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include in the filebrowser html")
ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html")

View file

@ -1,8 +1,8 @@
# coding: utf-8
VERSION = (1, 19, 17)
VERSION = (1, 19, 20)
CODENAME = "usernames"
BUILD_DT = (2025, 10, 17)
BUILD_DT = (2025, 11, 2)
S_VERSION = ".".join(map(str, VERSION))
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)

View file

@ -398,6 +398,9 @@ class VFS(object):
self.vpath = vpath # absolute path in the virtual filesystem
self.vpath0 = vpath0 # original vpath (before idp expansion)
self.axs = axs
self.uaxs: dict[
str, tuple[bool, bool, bool, bool, bool, bool, bool, bool, bool]
] = {}
self.flags = flags # config options
self.root = self
self.dev = 0 # st_dev
@ -555,29 +558,19 @@ class VFS(object):
def can_access(
self, vpath: str, uname: str
) -> tuple[bool, bool, bool, bool, bool, bool, bool, bool]:
"""can Read,Write,Move,Delete,Get,Upget,Admin,Dot"""
) -> tuple[bool, bool, bool, bool, bool, bool, bool, bool, bool]:
"""can Read,Write,Move,Delete,Get,Upget,Html,Admin,Dot"""
# NOTE: only used by get_perms, which is only used by hooks; the lowest of fruits
if vpath:
vn, _ = self._find(undot(vpath))
else:
vn = self
c = vn.axs
return (
uname in c.uread,
uname in c.uwrite,
uname in c.umove,
uname in c.udel,
uname in c.uget,
uname in c.upget,
uname in c.uadmin,
uname in c.udot,
)
# skip uhtml because it's rarely needed
return vn.uaxs[uname]
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)
ret = "".join(ch for ch, ok in zip("rwmdgGha.", zbl) if ok)
if "rwmd" in ret and "a." in ret:
ret += "A"
return ret
@ -772,20 +765,17 @@ class VFS(object):
virt_vis[name] = vn2
continue
ok = False
zx = vn2.axs
axs = [zx.uread, zx.uwrite, zx.umove, zx.udel, zx.uget]
u_has = vn2.uaxs.get(uname) or [False] * 9
for pset in permsets:
ok = True
for req, lst in zip(pset, axs):
if req and uname not in lst:
for req, zb in zip(pset, u_has):
if req and not zb:
ok = False
break
if ok:
virt_vis[name] = vn2
break
if ok:
virt_vis[name] = vn2
if ".hist" in abspath:
p = abspath.replace("\\", "/") if WINDOWS else abspath
if p.endswith("/.hist"):
@ -1822,6 +1812,15 @@ class AuthSrv(object):
derive_args(self.args)
self.setup_auth_ord()
if self.args.ipu:
# syntax (CIDR=UNAME) is verified in load_ipu
zsl = [x.split("=", 1)[1] for x in self.args.ipu]
zsl = [x for x in zsl if x not in acct]
if zsl:
t = "ERROR: unknown users in ipu: %s" % (zsl,)
self.log(t, 1)
raise Exception(t)
self.setup_pwhash(acct)
defpw = acct.copy()
self.setup_chpw(acct)
@ -1995,6 +1994,23 @@ class AuthSrv(object):
umap[usr].sort()
setattr(vfs, "a" + perm, umap)
for vol in vfs.all_nodes.values():
za = vol.axs
vol.uaxs = {
un: (
un in za.uread,
un in za.uwrite,
un in za.umove,
un in za.udel,
un in za.uget,
un in za.upget,
un in za.uhtml,
un in za.uadmin,
un in za.udot,
)
for un in unames
}
all_users = {}
missing_users = {}
associated_users = {}
@ -2328,6 +2344,10 @@ class AuthSrv(object):
free_umask = False
have_reflink = False
for vol in vfs.all_nodes.values():
if os.path.isfile(vol.realpath):
vol.flags["is_file"] = True
vol.flags["d2d"] = True
if (self.args.e2ds and vol.axs.uwrite) or self.args.e2dsa:
vol.flags["e2ds"] = True
@ -2642,7 +2662,14 @@ class AuthSrv(object):
errors = True
for vol in vfs.all_nodes.values():
if not vol.realpath or os.path.isfile(vol.realpath):
if not vol.flags.get("is_file"):
continue
zs = "og opds xlink"
for zs in zs.split():
vol.flags.pop(zs, None)
for vol in vfs.all_nodes.values():
if not vol.realpath or vol.flags.get("is_file"):
continue
ccs = vol.flags["casechk"][:1].lower()
if ccs in ("y", "n"):
@ -3033,6 +3060,7 @@ class AuthSrv(object):
"lifetime": vf.get("lifetime") or 0,
"unlist": vf.get("unlist") or "",
"sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
"sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
}
if "ufavico_h" in vf:
vn.js_ls["ufavico"] = vf["ufavico_h"]
@ -3054,7 +3082,8 @@ class AuthSrv(object):
"have_emp": int(self.args.emp),
"md_no_br": int(vf.get("md_no_br") or 0),
"ext_th": vf.get("ext_th_d") or {},
"sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
"sb_lg": vn.js_ls["sb_lg"],
"sb_md": vn.js_ls["sb_md"],
"sba_md": vf.get("md_sba") or "",
"sba_lg": vf.get("lg_sba") or "",
"txt_ext": self.args.textfiles.replace(",", " "),
@ -3088,6 +3117,10 @@ class AuthSrv(object):
for zs in zs.split():
if vf.get(zs):
js_htm[zs] = 1
zs = "notooltips"
for zs in zs.split():
if getattr(self.args, zs, False):
js_htm[zs] = 1
vn.js_htm = json_hesc(json.dumps(js_htm))
vols = list(vfs.all_nodes.values())
@ -3436,7 +3469,7 @@ class AuthSrv(object):
raise Exception("volume not found: " + zs)
self.log(str({"users": users, "vols": vols, "flags": flags}))
t = "/{}: read({}) write({}) move({}) del({}) dots({}) get({}) upGet({}) uadmin({})"
t = "/{}: read({}) write({}) move({}) del({}) dots({}) get({}) upGet({}) html({}) uadmin({})"
for k, zv in self.vfs.all_vols.items():
vc = zv.axs
vs = [

View file

@ -198,7 +198,7 @@ class FtpFs(AbstractedFS):
if not avfs:
raise FSE(t.format(vpath), 1)
cr, cw, cm, cd, _, _, _, _ = avfs.can_access("", self.h.uname)
cr, cw, cm, cd, _, _, _, _, _ = avfs.uaxs[self.h.uname]
if r and not cr or w and not cw or m and not cm or d and not cd:
raise FSE(t.format(vpath), 1)
@ -250,6 +250,7 @@ class FtpFs(AbstractedFS):
td = 0
if w and need_unlink:
assert td # type: ignore # !rm
if td >= -1 and td <= self.args.ftp_wt:
# within permitted timeframe; allow overwrite or resume
do_it = True

View file

@ -30,7 +30,7 @@ try:
except:
pass
from .__init__ import ANYWIN, RES, TYPE_CHECKING, EnvParams, unicode
from .__init__ import ANYWIN, RES, RESM, TYPE_CHECKING, EnvParams, unicode
from .__version__ import S_VERSION
from .authsrv import LEELOO_DALLAS, VFS # typechk
from .bos import bos
@ -124,7 +124,17 @@ from .util import (
if True: # pylint: disable=using-constant-test
import typing
from typing import Any, Generator, Iterable, Match, Optional, Pattern, Type, Union
from typing import (
Any,
Generator,
Iterable,
Match,
Optional,
Pattern,
Sequence,
Type,
Union,
)
if TYPE_CHECKING:
from .httpconn import HttpConn
@ -165,6 +175,24 @@ RE_MDV = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[Mm][Dd])$")
UPARAM_CC_OK = set("doc move tree".split())
PERMS_rwh = [
[True, False],
[False, True],
[False, False, False, False, False, False, True],
]
def _build_zip_xcode() -> Sequence[str]:
ret = "opus mp3 flac wav p".split()
for codec in ("w", "j"):
for suf in ("", "f", "f3", "3"):
ret.append("%s%s" % (codec, suf))
return ret
ZIP_XCODE_L = _build_zip_xcode()
ZIP_XCODE_S = set(ZIP_XCODE_L)
class HttpCli(object):
"""
@ -229,6 +257,7 @@ class HttpCli(object):
self.can_delete = False
self.can_get = False
self.can_upget = False
self.can_html = False
self.can_admin = False
self.can_dot = False
self.out_headerlist: list[tuple[str, str]] = []
@ -737,18 +766,21 @@ class HttpCli(object):
if "bcasechk" in vn.flags and not vn.casechk(rem, True):
return self.tx_404() and False
(
self.can_read,
self.can_write,
self.can_move,
self.can_delete,
self.can_get,
self.can_upget,
self.can_admin,
self.can_dot,
) = (
avn.can_access("", self.uname) if avn else [False] * 8
)
try:
(
self.can_read,
self.can_write,
self.can_move,
self.can_delete,
self.can_get,
self.can_upget,
self.can_html,
self.can_admin,
self.can_dot,
) = avn.uaxs[self.uname]
except:
pass # default is all-false
self.avn = avn
self.vn = vn # note: do not dbv due to walk/zipgen
self.rem = rem
@ -775,7 +807,7 @@ class HttpCli(object):
guess = "modifying" if (origin and host) else "stripping"
t = "cors-reject %s because request-header Origin=%r does not match request-protocol %r and host %r based on request-header Host=%r (note: if this request is not malicious, check if your reverse-proxy is accidentally %s request headers, in particular 'Origin', for example by running copyparty with --ihead='*' to show all request headers)"
self.log(t % (self.mode, origin, proto, self.host, host, guess), 3)
raise Pebkac(403, "rejected by cors-check")
raise Pebkac(403, "rejected by cors-check (see serverlog)")
# getattr(self.mode) is not yet faster than this
if self.mode == "POST":
@ -921,7 +953,7 @@ class HttpCli(object):
return False
self.log("banned for {:.0f} sec".format(rt), 6)
self.terse_reply(b"thank you for playing", 403)
self.terse_reply(b"thank you for playing (see serverlog and readme)", 403)
return True
def permit_caching(self) -> None:
@ -1275,6 +1307,20 @@ class HttpCli(object):
else:
return self.tx_res(res_path)
if res_path in RESM:
ap = self.E.mod_ + RESM[res_path]
if (
"txt" not in self.uparam
and "mime" not in self.uparam
and not self.ouparam.get("dl")
):
# return mimetype matching request extension
self.ouparam["dl"] = res_path.split("/")[-1]
if bos.path.exists(ap) or bos.path.exists(ap + ".gz"):
return self.tx_file(ap)
else:
return self.tx_res(res_path)
self.tx_404()
return False
@ -3284,9 +3330,9 @@ class HttpCli(object):
vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True)
self._assert_safe_rem(rem)
ext = "" if "." not in new_file else new_file.split(".")[-1]
if not ext or len(ext) > 5 or not self.can_delete:
new_file += ".md"
if not self.can_delete and not new_file.lower().endswith(".md"):
t = "you can only create .md files because you don't have the delete-permission"
raise Pebkac(400, t)
sanitized = sanitize_fn(new_file, "")
fdir = vfs.canonical(rem)
@ -3325,7 +3371,6 @@ class HttpCli(object):
raise Pebkac(500, "that file exists already")
with open(fsenc(fn), "wb") as f:
f.write(b"`GRUNNUR`\n")
if "fperms" in vfs.flags:
set_fperms(f, vfs.flags)
@ -4179,8 +4224,11 @@ class HttpCli(object):
# force download
if "dl" in self.ouparam:
cdis = gen_content_disposition(os.path.basename(req_path))
self.out_headers["Content-Disposition"] = cdis
cdis = self.ouparam["dl"] or req_path
zs = gen_content_disposition(os.path.basename(cdis))
self.out_headers["Content-Disposition"] = zs
else:
cdis = req_path
#
# if-modified
@ -4246,7 +4294,7 @@ class HttpCli(object):
elif "mime" in self.uparam:
mime = str(self.uparam.get("mime"))
else:
mime = guess_mime(req_path)
mime = guess_mime(cdis)
logmsg += unicode(status) + logtail
@ -4354,8 +4402,11 @@ class HttpCli(object):
# force download
if "dl" in self.ouparam:
cdis = gen_content_disposition(os.path.basename(req_path))
self.out_headers["Content-Disposition"] = cdis
cdis = self.ouparam["dl"] or req_path
zs = gen_content_disposition(os.path.basename(cdis))
self.out_headers["Content-Disposition"] = zs
else:
cdis = req_path
#
# if-modified
@ -4483,7 +4534,7 @@ class HttpCli(object):
elif "rmagic" in self.vn.flags:
mime = guess_mime(req_path, fs_path)
else:
mime = guess_mime(req_path)
mime = guess_mime(cdis)
if "nohtml" in self.vn.flags and "html" in mime:
mime = "text/plain; charset=utf-8"
@ -4913,9 +4964,12 @@ class HttpCli(object):
# for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]}))
cfmt = ""
if self.thumbcli and not self.args.no_bacode:
for zs in ("opus", "mp3", "flac", "wav", "w", "j", "p"):
if zs in self.ouparam or uarg == zs:
cfmt = zs
if uarg in ZIP_XCODE_S:
cfmt = uarg
else:
for zs in ZIP_XCODE_L:
if zs in self.ouparam:
cfmt = zs
if cfmt:
self.log("transcoding to [{}]".format(cfmt))
@ -5261,7 +5315,7 @@ class HttpCli(object):
dls.append((perc, hsent, spd, eta, idle, usr, erd, rds, fn))
if self.args.have_unlistc:
allvols = self.asrv.vfs.all_vols
allvols = self.asrv.vfs.all_nodes
rvol = [x for x in rvol if "unlistcr" not in allvols[x[1:-1]].flags]
wvol = [x for x in wvol if "unlistcw" not in allvols[x[1:-1]].flags]
@ -5540,7 +5594,7 @@ class HttpCli(object):
rem,
self.uname,
not self.args.no_scandir,
[[True, False], [False, True]],
PERMS_rwh,
)
dots = self.uname in vn.axs.udot
dk_sz = vn.flags.get("dk")
@ -5572,7 +5626,13 @@ class HttpCli(object):
for x in vfs_virt:
if x != excl:
try:
dvn, drem = vfs.get(vjoin(top, x), self.uname, True, False)
dvn, drem = vfs.get(vjoin(top, x), self.uname, False, False)
if (
self.uname not in dvn.axs.uread
and self.uname not in dvn.axs.uwrite
and self.uname not in dvn.axs.uhtml
):
raise Exception()
bos.stat(dvn.canonical(drem, False))
except:
x += "\n"
@ -5978,6 +6038,15 @@ class HttpCli(object):
if self.uname != self.args.shr_adm:
rows = [x for x in rows if x[5] == self.uname]
q = "select vp from sf where k=? limit 99"
for r in rows:
if not r[4]:
r[4] = "---"
else:
zstl = cur.execute(q, (r[0],)).fetchall()
zsl = [html_escape(zst[0]) for zst in zstl]
r[4] = "<br />".join(zsl)
html = self.j2s(
"shares", this=self, shr=self.args.shr, rows=rows, now=int(time.time())
)
@ -6498,8 +6567,7 @@ class HttpCli(object):
return self.tx_svg("upload\nonly")
if not self.can_read and self.can_get and self.avn:
axs = self.avn.axs
if self.uname not in axs.uhtml:
if not self.can_html:
pass
elif is_dir:
for fn in ("index.htm", "index.html"):
@ -6520,6 +6588,7 @@ class HttpCli(object):
fk_pass = True
is_dir = False
add_og = False
rem = vjoin(rem, fn)
vrem = vjoin(vrem, fn)
abspath = ap2
@ -6713,7 +6782,7 @@ class HttpCli(object):
rem,
self.uname,
not self.args.no_scandir,
[[True, False], [False, True]],
PERMS_rwh,
lstat="lt" in self.uparam,
throw=True,
)

View file

@ -383,8 +383,8 @@ class HttpSrv(object):
if nloris < nconn / 2:
continue
t = "slowloris (idle-conn): {} banned for {} min"
self.log(self.name, t.format(ip, self.args.loris, nclose), 1)
t = "slow%s (idle-conn): %s banned for %d min" # slowloris
self.log(self.name, t % ("loris", ip, self.args.loris), 1)
self.bans[ip] = int(time.time() + self.args.loris * 60)
if self.args.log_conn:

View file

@ -17,10 +17,10 @@ class Metrics(object):
self.hsrv = hsrv
def tx(self, cli: "HttpCli") -> bool:
if not cli.avol:
args = cli.args
if not cli.avol and cli.uname.lower() not in args.stats_u_set:
raise Pebkac(403, "'stats' not allowed for user " + cli.uname)
args = cli.args
if not args.stats:
raise Pebkac(403, "the stats feature is not enabled in server config")

View file

@ -1081,7 +1081,7 @@ class SvcHub(object):
vs = os.path.expandvars(os.path.expanduser(vs))
setattr(al, k, vs)
for k in "idp_adm".split(" "):
for k in "idp_adm stats_u".split(" "):
vs = getattr(al, k)
vsa = [x.strip() for x in vs.split(",")]
vsa = [x.lower() for x in vsa if x]

View file

@ -270,6 +270,9 @@ class ThumbSrv(object):
def shutdown(self) -> None:
self.stopping = True
Daemon(self._fire_sentinels, "thumbstopper")
def _fire_sentinels(self):
for _ in range(self.nthr):
self.q.put(None)

View file

@ -2,11 +2,14 @@
# which should help on really slow connections
# but then why are you using copyparty in the first place
pk: $(addsuffix .gz, $(wildcard tl/*.js *.js *.css))
un: $(addsuffix .un, $(wildcard tl/*.gz *.gz))
pk: $(addsuffix .gz, $(wildcard tl/*.js *.js *.css) \
a/webdav-cfg.txt )
un: $(addsuffix .un, $(wildcard tl/*.gz *.gz a/*.gz))
%.gz: %
pigz -11 -J 34 -I 573 $<
pigz -c11 -J 34 -I 573 <$< >$@
touch -r $< $@
rm $<
%.un: %
pigz -d $<

View file

@ -1036,6 +1036,7 @@ html.dz #flogout {
box-shadow: 0 .1em 1.2em var(--g-play-sh);
}
#files tbody tr.sel td,
#files tbody tr.sel span,
#ggrid>a.sel,
#ggrid>a[tt].sel {
color: var(--g-sel-fg);

View file

@ -51,8 +51,9 @@
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
<input type="hidden" name="act" value="new_md" />
📝<input type="text" name="name" class="i" placeholder="weekend-plans">
<input type="submit" value="new markdown doc">
<input type="submit" value="new file">
</form>
<span id="new_mdi"></p>
</div>
<div id="op_msg" class="opview opbox {% if not ls0 %}act{% endif %}">
@ -137,7 +138,6 @@
lang = "{{ lang }}",
dfavico = "{{ favico }}",
have_tags_idx = {{ have_tags_idx }},
sb_lg = "{{ sb_lg }}",
logues = {{ logues|tojson if sb_lg else "[]" }},
ls0 = {{ ls0|tojson }};

View file

@ -119,7 +119,7 @@ if (1)
"ot_unpost": "unpost: delete your recent uploads, or abort unfinished ones",
"ot_bup": "bup: basic uploader, even supports netscape 4.0",
"ot_mkdir": "mkdir: create a new directory",
"ot_md": "new-md: create a new markdown document",
"ot_md": "new-file: create a new textfile",
"ot_msg": "msg: send a message to the server log",
"ot_mp": "media player options",
"ot_cfg": "configuration options",
@ -128,7 +128,7 @@ if (1)
"ot_noie": 'Please use Chrome / Firefox / Edge',
"ab_mkdir": "make directory",
"ab_mkdoc": "new markdown doc",
"ab_mkdoc": "new textfile",
"ab_msg": "send msg to srv log",
"ay_path": "skip to folders",
@ -440,6 +440,8 @@ if (1)
"fcp_both_b": '<a href="#" id="modal-ok">Copy</a><a href="#" id="modal-ng">Upload</a>',
"mk_noname": "type a name into the text field on the left before you do that :p",
"nmd_i1": "also add the file extension you want, for example <code>.md</code>",
"nmd_i2": "you can only create <code>.md</code> files because you don't have the delete-permission",
"tv_load": "Loading text document:\n\n{0}\n\n{1}% ({2} of {3} MiB loaded)",
"tv_xe1": "could not load textfile:\n\nerror ",
@ -7148,6 +7150,8 @@ var treectl = (function () {
if (res.files[a].tags === undefined)
res.files[a].tags = {};
sb_lg = res.sb_lg;
sb_md = res.sb_md;
dnsort = res.dnsort;
read_dsort(res.dsort);
dcrop = res.dcrop;
@ -7741,6 +7745,8 @@ function apply_perms(res) {
if (up2k)
up2k.set_fsearch();
ebi('new_mdi').innerHTML = has(perms, "delete") ? L.nmd_i1 : L.nmd_i2;
widget.setvis();
thegrid.setvis();
if (!have_read && have_write)
@ -8797,7 +8803,7 @@ function show_md(md, name, div, url, depth) {
var els = QSA('#epi a');
for (var a = 0, aa = els.length; a < aa; a++) {
var href = els[a].getAttribute('href');
if (!href.startsWith('#') || href.startsWith('#md-'))
if (!href || !href.startsWith('#') || href.startsWith('#md-'))
continue;
els[a].setAttribute('href', '#md-' + href.slice(1));

View file

@ -43,7 +43,7 @@
<script>
setTimeout(function() {
location.replace("{{ redir }}");
}, 1000);
}, 600);
</script>
{%- endif %}
{%- if js %}

View file

@ -58,6 +58,7 @@ th {
#wrap th {
padding: .3em .6em;
text-align: left;
vertical-align: top;
white-space: nowrap;
}
#wrap td+td+td+td+td+td+td+td {
@ -71,7 +72,10 @@ th {
#wrap td:last-child {
border-radius: 0 .5em .5em 0;
}
#wrap.terse td div {
height: 2.3em;
overflow-y: hidden;
}
html.z {

View file

@ -14,8 +14,9 @@
</head>
<body>
<div id="wrap">
<div id="wrap" class="terse">
<a href="{{ r }}/?shares">refresh</a>
<a id="xpnd" href="#">files</a>
<a href="{{ r }}/?h">control-panel</a>
<span>axs = perms (read,write,move,delet)</span>
@ -46,7 +47,7 @@
<td>{{ "yes" if pw else "--" }}</td>
<td><a href="{{ r }}/{{ vp|e }}">/{{ vp|e }}</a></td>
<td>{{ pr }}</td>
<td>{{ st }}</td>
<td><div>{{ st }}</div></td>
<td>{{ un|e }}</td>
<td>{{ t0 }}</td>
<td>{{ t1 }}</td>

View file

@ -28,6 +28,11 @@ function cb() {
location = '?shares';
}
ebi('xpnd').onclick = function (e) {
ev(e);
clmod(ebi('wrap'), 'terse', 't');
};
function qr(e) {
ev(e);
var href = this.href,

View file

@ -42,7 +42,7 @@
<thead><tr><th>%</th><th>speed</th><th>eta</th><th>idle</th><th>dir</th><th>file</th></tr></thead>
<tbody>
{%- for u in ups %}
<tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td><a href="{{ u[4] }}">{{ u[5]|e }}</a></td><td>{{ u[6]|e }}</td></tr>
<tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td><a href="{{ r }}/{{ u[4] }}">{{ u[5]|e }}</a></td><td>{{ u[6]|e }}</td></tr>
{%- endfor %}
</tbody>
</table>
@ -54,7 +54,7 @@
<thead><tr><th>%</th><th>sent</th><th>speed</th><th>eta</th><th>idle</th><th></th><th>dir</th><th>file</th></tr></thead>
<tbody>
{%- for u in dls %}
<tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td>{{ u[4] }}</td><td>{{ u[5] }}</td><td><a href="{{ u[6] }}">{{ u[7]|e }}</a></td><td>{{ u[8] }}</td></tr>
<tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td>{{ u[4] }}</td><td>{{ u[5] }}</td><td><a href="{{ r }}/{{ u[6] }}">{{ u[7]|e }}</a></td><td>{{ u[8] }}</td></tr>
{%- endfor %}
</tbody>
</table>
@ -120,7 +120,11 @@
</form>
</div>
{%- else %}
{%- if this.uname == '*' %}
<h1 id="l">login for more:</h1>
{%- else %}
<h1 id="l">change account:</h1>
{%- endif %}
<div>
{%- if this.args.idp_login %}
<ul><li>

View file

@ -64,7 +64,7 @@
<li>old version of rclone? replace all <code>=</code> with <code>&nbsp;</code> (space)</li>
</ul>
<p>if you want to use the native WebDAV client in windows instead (slow and buggy), first run <a href="{{ r }}/.cpr/a/webdav-cfg.bat">webdav-cfg.bat</a> to remove the 47 MiB filesize limit (also fixes latency and password login), then connect:</p>
<p>if you want to use the native WebDAV client in windows instead (slow and buggy), first run <a href="{{ r }}/.cpr/a/webdav-cfg.txt?dl=webdav-cfg.bat">webdav-cfg.bat</a> to remove the 47 MiB filesize limit (also fixes latency and password login), then connect:</p>
<pre>
{%- if un %}
net use <b>w:</b> http{{ s }}://{{ ep }}/{{ rvp }}{% if accs %} <b>{{ pw }}</b> /user:{{ b_un }}{% endif %}

View file

@ -116,7 +116,7 @@ Ls.chi = {
"ot_unpost": "取消发布:删除最近上传的内容,或中止未完成的内容",
"ot_bup": "bup基础上传器甚至支持 Netscape 4.0",
"ot_mkdir": "mkdir创建新目录",
"ot_md": "new-md创建新 Markdown 文档",
"ot_md": "new-file创建新的文本文件", //m
"ot_msg": "msg向服务器日志发送消息",
"ot_mp": "媒体播放器选项",
"ot_cfg": "配置选项",
@ -125,7 +125,7 @@ Ls.chi = {
"ot_noie": '请使用 Chrome / Firefox / Edge',
"ab_mkdir": "创建目录",
"ab_mkdoc": "新建 Markdown 文档",
"ab_mkdoc": "新建文本文件", //m
"ab_msg": "发送消息到服务器日志",
"ay_path": "跳转到文件夹",
@ -437,6 +437,8 @@ Ls.chi = {
"fcp_both_b": '<a href="#" id="modal-ok">复制</a><a href="#" id="modal-ng">上传</a>', //m
"mk_noname": "在左侧文本框中输入名称,然后再执行此操作 :p",
"nmd_i1": "还可以添加需要的文件扩展名,例如 <code>.txt</code>", //m
"nmd_i2": "由于没有删除权限,你只能创建 <code>.md</code> 文件", //m
"tv_load": "加载文本文件:\n\n{0}\n\n{1}% ({2} 的 {3} MiB 已加载)",
"tv_xe1": "无法加载文本文件:\n\n错误 ",

View file

@ -120,7 +120,7 @@ Ls.cze = {
"ot_unpost": "unpost: smazat vaše nedávné nahrání nebo zrušit nedokončené",
"ot_bup": "bup: základní nahrávač, podporuje i netscape 4.0",
"ot_mkdir": "mkdir: vytvořit nový adresář",
"ot_md": "new-md: vytvořit nový markdown dokument",
"ot_md": "new-file: vytvořit nový textový soubor", //m
"ot_msg": "msg: poslat zprávu do logu serveru",
"ot_mp": "možnosti přehrávače médií",
"ot_cfg": "možnosti konfigurace",
@ -129,7 +129,7 @@ Ls.cze = {
"ot_noie": 'Prosím použijte Chrome / Firefox / Edge',
"ab_mkdir": "vytvořit adresář",
"ab_mkdoc": "nový markdown dokument",
"ab_mkdoc": "nový textový soubor", //m
"ab_msg": "poslat zprávu do logu serveru",
"ay_path": "přejít na složky",
@ -441,6 +441,8 @@ Ls.cze = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopírovat</a><a href="#" id="modal-ng">Nahrát</a>',
"mk_noname": "napište název do textového pole vlevo předtím než to uděláte :p",
"nmd_i1": "můžeš také přidat příponu souboru, například <code>.txt</code>", //m
"nmd_i2": "můžeš vytvářet pouze <code>.md</code> soubory, protože nemáš oprávnění mazat", //m
"tv_load": "Načítání textového dokumentu:\n\n{0}\n\n{1}% ({2} z {3} MiB načteno)",
"tv_xe1": "nelze načíst textový soubor:\n\nchyba ",

View file

@ -116,7 +116,7 @@ Ls.deu = {
"ot_unpost": "unpost: lösche deine letzten Uploads oder breche unvollständige ab",
"ot_bup": "bup: Basic Uploader, unterstützt sogar Neuheiten wie Netscape 4.0",
"ot_mkdir": "mkdir: Neuen Ordner erstellen",
"ot_md": "new-md: Neues Markdown-Dokument erstellen",
"ot_md": "new-file: Neues Textdokument erstellen", //m
"ot_msg": "msg: Eine Nachricht an das Server-Log schicken",
"ot_mp": "Media Player-Optionen",
"ot_cfg": "Konfigurationsoptionen",
@ -125,7 +125,7 @@ Ls.deu = {
"ot_noie": 'Bitte benutze Chrome / Firefox / Edge',
"ab_mkdir": "Ordner erstellen",
"ab_mkdoc": "Markdown Doc erstellen",
"ab_mkdoc": "Textdatei erstellen", //m
"ab_msg": "Nachricht an Server Log senden",
"ay_path": "zu Ordnern springen",
@ -437,6 +437,8 @@ Ls.deu = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopieren</a><a href="#" id="modal-ng">Hochladen</a>',
"mk_noname": "Tipp' mal vorher lieber einen Namen in das Textfeld links, bevor du das machst :p",
"nmd_i1": "Fügen Sie auch die gewünschte Dateiendung hinzu, z. B. <code>.txt</code>", //m
"nmd_i2": "Sie können nur <code>.md</code>-Dateien erstellen, da Ihnen die Löschberechtigung fehlt", //m
"tv_load": "Textdatei wird geladen:\n\n{0}\n\n{1}% ({2} von {3} MiB geladen)",
"tv_xe1": "Konnte Textdatei nicht laden:\n\nFehler ",

View file

@ -1,5 +1,5 @@
// Linioj finiĝantaj per //m estas nekontrolitaj maŝinaj tradukoj
// Linioj, finiĝantaj per "//m", estas nekontrolitaj maŝinaj tradukoj
Ls.epo = {
"tt": "Esperanto",
@ -116,7 +116,7 @@ Ls.epo = {
"ot_unpost": "unpost: forigi viaj plej lastaj alŝutoj, aŭ ĉesigi nefinigitajn",
"ot_bup": "bup: fundamenta alŝutilo, funkias eĉ kun netscape 4.0",
"ot_mkdir": "mkdir: krei novan dosierujon",
"ot_md": "new-md: krei novan markdown-dosieron",
"ot_md": "new-file: krei novan tekstodosieron", //m
"ot_msg": "msg: sendi mesaĝon al servila protokolo",
"ot_mp": "agordoj de medialudilo",
"ot_cfg": "aliaj agordoj",
@ -125,7 +125,7 @@ Ls.epo = {
"ot_noie": 'Bonvolu uzi retumilojn Chrome / Firefox / Edge',
"ab_mkdir": "krei dosierujon",
"ab_mkdoc": "krei markdown-dosieron",
"ab_mkdoc": "krei tekstodosieron", //m
"ab_msg": "sendi mesaĝon al protokolo",
"ay_path": "iri al dosierujoj",
@ -204,7 +204,7 @@ Ls.epo = {
"u_nav_b": '<a href="#" id="modal-ok">Dosierojn</a><a href="#" id="modal-ng">Unu dosierujo</a>',
"cl_opts": "ŝaltiloj",
"cl_hfsz": "Dosiergrando", //m
"cl_hfsz": "dosiergrando",
"cl_themes": "etoso",
"cl_langs": "lingvo",
"cl_ziptype": "elŝutado de dosieroj",
@ -437,6 +437,8 @@ Ls.epo = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopii</a><a href="#" id="modal-ng">Alŝuti</a>',
"mk_noname": "tajpu nomon en tekstokampo maldekstre antaŭ vi faras ĉi tion :p",
"nmd_i1": "vi povas aldoni la deziratan sufikson, ekzemple <code>.txt</code>", //m
"nmd_i2": "vi povas krei nur <code>.md</code>-dosierojn ĉar vi ne havas forigan permeson", //m
"tv_load": "Ŝargado de teksto-dokumento:\n\n{0}\n\n{1}% ({2} da {3} MiB ŝargita)",
"tv_xe1": "ne povas ŝargi teksto-dosieron:\n\neraro ",

View file

@ -116,7 +116,7 @@ Ls.fin = {
"ot_unpost": "unpost: poista viimeaikaiset tai keskeytä keskeneräiset lataukset",
"ot_bup": "bup: tiedostojen 'perus'lähetysohjelma, tukee jopa netscape 4.0",
"ot_mkdir": "mkdir: luo uusi hakemisto",
"ot_md": "new-md: luo uusi markdown-dokumentti",
"ot_md": "new-file: luo uusi tekstitiedosto", //m
"ot_msg": "msg: lähetä viesti palvelinlokiin",
"ot_mp": "mediasoittimen asetukset",
"ot_cfg": "asetukset",
@ -125,7 +125,7 @@ Ls.fin = {
"ot_noie": 'Suosittelemme käyttämään uudempaa selainta.',
"ab_mkdir": "luo hakemisto",
"ab_mkdoc": "luo markdown-tiedosto",
"ab_mkdoc": "luo tekstitiedosto", //m
"ab_msg": "lähetä viesti palvelinlokiin",
"ay_path": "siirry hakemistoihin",
@ -437,6 +437,8 @@ Ls.fin = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopioi</a><a href="#" id="modal-ng">Lähetä</a>',
"mk_noname": "kirjoita nimi vasemmalla olevaan tekstikenttään ennen kuin teet tuon :p",
"nmd_i1": "voit myös lisätä haluamasi tiedostopäätteen, esimerkiksi <code>.txt</code>", //m
"nmd_i2": "voit luoda vain <code>.md</code>-tiedostoja, koska sinulla ei ole poistolupaa", //m
"tv_load": "Ladataan tekstidokumenttia:\n\n{0}\n\n{1}% ({2} / {3} Mt ladattu)",
"tv_xe1": "tekstitiedoston lataaminen epäonnistui:\n\nvirhe ",

View file

@ -116,7 +116,7 @@ Ls.fra = {
"ot_unpost": "unpost: supprimer vos téléchargements récents, ou annuler ceux en cours",
"ot_bup": "bup: téléverseur de base, prend même en charge netscape 4.0",
"ot_mkdir": "mkdir: créer un nouveau répertoire",
"ot_md": "new-md: créer un nouveau document markdown",
"ot_md": "new-file: créer un nouveau fichier texte", //m
"ot_msg": "msg: envoyer un message au journal du serveur",
"ot_mp": "options du lecteur multimedia",
"ot_cfg": "options de configuration",
@ -125,7 +125,7 @@ Ls.fra = {
"ot_noie": 'Utilisez Chrome / Firefox / Edge',
"ab_mkdir": "créer un nouveau répertoire",
"ab_mkdoc": "faire un nouveau document markdown",
"ab_mkdoc": "nouveau fichier texte", //m
"ab_msg": "envoyer un message au journal du serveur",
"ay_path": "passer aux dossiers",
@ -437,6 +437,8 @@ Ls.fra = {
"fcp_both_b": '<a href="#" id="modal-ok">Copier</a><a href="#" id="modal-ng">Téléverser</a>',
"mk_noname": "entrez un nom dans le champ de texte à gauche avant de faire ça :p",
"nmd_i1": "ajoutez aussi lextension souhaitée, par exemple <code>.txt</code>", //m
"nmd_i2": "vous ne pouvez créer que des fichiers <code>.md</code> car vous navez pas la permission deffacer", //m
"tv_load": "Chargement du document texte:\n\n{0}\n\n{1}% ({2} de {3} MiB chargés)",
"tv_xe1": "impossible de charger le fichier texte:\n\nerreur",

View file

@ -116,7 +116,7 @@ Ls.grc = {
"ot_unpost": "unpost: διαγραφή πρόσφατων μεταφορτώσεων ή ακύρωση ανολοκλήρωτων",
"ot_bup": "bup: βασικός uploader, υποστηρίζει μέχρι και netscape 4.0",
"ot_mkdir": "mkdir: δημιουργία νέου φακέλου",
"ot_md": "new-md: δημιουργία νέου markdown εγγράφου",
"ot_md": "new-file: δημιουργία νέου αρχείου κειμένου", //m
"ot_msg": "msg: αποστολή μηνύματος στο server log",
"ot_mp": "επιλογές media player",
"ot_cfg": "επιλογές ρυθμίσεων",
@ -125,7 +125,7 @@ Ls.grc = {
"ot_noie": 'Χρησιμοποίησε Chrome / Firefox / Edge',
"ab_mkdir": "δημιουργία φακέλου",
"ab_mkdoc": "νέο markdown έγγραφο",
"ab_mkdoc": "νέο αρχείο κειμένου", //m
"ab_msg": "στείλε μήνυμα στο server log",
"ay_path": "πήγαινε σε φακέλους",
@ -437,6 +437,8 @@ Ls.grc = {
"fcp_both_b": '<a href="#" id="modal-ok">Αντιγραφή</a><a href="#" id="modal-ng">Μεταφόρτωση</a>',
"mk_noname": "γράψε ένα όνομα στο πεδίο κειμένου αριστερά πριν το κάνεις :p",
"nmd_i1": "μπορείτε επίσης να προσθέσετε την κατάληξη που θέλετε, όπως <code>.txt</code>", //m
"nmd_i2": "μπορείτε να δημιουργήσετε μόνο αρχεία <code>.md</code> επειδή δεν έχετε δικαίωμα διαγραφής", //m
"tv_load": "Φόρτωση αρχείου κειμένου:\n\n{0}\n\n{1}% ({2} από {3} MiB φορτωμένα)",
"tv_xe1": "αδυναμία φόρτωσης αρχείου κειμένου:\n\nσφάλμα ",

View file

@ -116,7 +116,7 @@ Ls.ita = {
"ot_unpost": "unpost: elimina i tuoi caricamenti recenti, o interrompi quelli non completati",
"ot_bup": "bup: uploader di base, supporta anche netscape 4.0",
"ot_mkdir": "mkdir: crea una nuova directory",
"ot_md": "new-md: crea un nuovo documento markdown",
"ot_md": "new-file: crea un nuovo file di testo", //m
"ot_msg": "msg: invia un messaggio al log del server",
"ot_mp": "opzioni lettore multimediale",
"ot_cfg": "opzioni di configurazione",
@ -125,7 +125,7 @@ Ls.ita = {
"ot_noie": 'Perfavore usa Chrome / Firefox / Edge',
"ab_mkdir": "crea directory",
"ab_mkdoc": "nuovo doc markdown",
"ab_mkdoc": "nuovo file di testo", //m
"ab_msg": "invia msg al log srv",
"ay_path": "salta alle cartelle",
@ -437,6 +437,8 @@ Ls.ita = {
"fcp_both_b": '<a href="#" id="modal-ok">Copia</a><a href="#" id="modal-ng">Carica</a>',
"mk_noname": "scrivi un nome nel campo di testo a sinistra prima di farlo :p",
"nmd_i1": "puoi anche aggiungere lestensione che vuoi, per esempio <code>.txt</code>", //m
"nmd_i2": "puoi creare solo file <code>.md</code> perché non hai il permesso di eliminare", //m
"tv_load": "Caricando documento di testo:\n\n{0}\n\n{1}% ({2} di {3} MiB caricati)",
"tv_xe1": "impossibile caricare file di testo:\n\nerrore ",

View file

@ -116,7 +116,7 @@ Ls.kor = {
"ot_unpost": "주워담기: 최근 업로드한 항목을 삭제하거나 미완료된 업로드를 중단합니다",
"ot_bup": "bup: 기본 업로더. 넷스케이프 4.0도 지원합니다",
"ot_mkdir": "mkdir: 새 디렉터리를 만듭니다",
"ot_md": "new-md: 새 마크다운 문서를 만듭니다",
"ot_md": "new-file: 새 텍스트 파일을 만듭니다", //m
"ot_msg": "msg: 서버 로그에 메시지를 보냅니다",
"ot_mp": "미디어 플레이어 옵션",
"ot_cfg": "구성 옵션",
@ -125,7 +125,7 @@ Ls.kor = {
"ot_noie": 'Chrome / Firefox / Edge를 사용해주세요',
"ab_mkdir": "디렉터리 만들기",
"ab_mkdoc": "새 마크다운 문서",
"ab_mkdoc": "새 텍스트 파일", //m
"ab_msg": "서버 로그에 메시지 보내기",
"ay_path": "폴더로 건너뛰기",
@ -437,6 +437,8 @@ Ls.kor = {
"fcp_both_b": '<a href="#" id="modal-ok">복사</a><a href="#" id="modal-ng">업로드</a>',
"mk_noname": "왼쪽 텍스트 필드에 이름을 먼저 입력해주세요 :p",
"nmd_i1": "원하는 파일 확장자를 추가할 수 있습니다. 예: <code>.txt</code>", //m
"nmd_i2": "삭제 권한이 없어서 <code>.md</code> 파일만 만들 수 있습니다", //m
"tv_load": "텍스트 문서 불러오는 중:\n\n{0}\n\n{1}% ({3} MiB 중 {2} MiB 로드됨)",
"tv_xe1": "텍스트 파일을 불러올 수 없습니다:\n\n오류 ",

View file

@ -116,7 +116,7 @@ Ls.nld = {
"ot_unpost": "unpost: verwijder je recente uploads, of onvoltooide uploads afbreken",
"ot_bup": "bup: Basisuploader, supports zelfs netscape 4.0",
"ot_mkdir": "mkdir: Maak een nieuwe map",
"ot_md": "new-md: Maak een nieuwe markdown bestand",
"ot_md": "new-file: Maak een nieuw tekstbestand", //m
"ot_msg": "msg: Verstuur een bericht naar de server logs",
"ot_mp": "Media speler opties",
"ot_cfg": "Configuratie opties",
@ -125,7 +125,7 @@ Ls.nld = {
"ot_noie": 'Gebruik alstublieft Chrome / Firefox / Edge',
"ab_mkdir": "maak map",
"ab_mkdoc": "nieuw markdown doc",
"ab_mkdoc": "nieuw tekstbestand", //m
"ab_msg": "verstuur msg naar srv log",
"ay_path": "skip naar mappen",
@ -437,6 +437,8 @@ Ls.nld = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopieer</a><a href="#" id="modal-ng">Upload</a>',
"mk_noname": "Voer een naam in het tekstveld aan de linkerkant voordat je verder gaat :p",
"nmd_i1": "Voeg ook de gewenste extensie toe, bijvoorbeeld <code>.txt</code>", //m
"nmd_i2": "Je kunt alleen <code>.md</code>-bestanden maken omdat je geen verwijderrechten hebt", //m
"tv_load": "Tekstdocument laden:\n\n{0}\n\n{1}% ({2} van de {3} MiB geladen)",
"tv_xe1": "Kon tekstbestand niet laden:\n\nfout ",

View file

@ -114,7 +114,7 @@ Ls.nno = {
"ot_unpost": "unpost: slett filer som du nyleg har lastet opp; «angre-knappen»",
"ot_bup": "bup: tradisjonell / primitiv filopplasting,$N$Nfungerar i om lag samtlege nettlesarar",
"ot_mkdir": "mkdir: lag ei ny mappe",
"ot_md": "new-md: lag eit nytt markdown-dokument",
"ot_md": "new-file: lag ein ny tekstfil",
"ot_msg": "msg: send ein beskjed åt serverloggen",
"ot_mp": "musikkspelarinstillinger",
"ot_cfg": "andre innstillinger",
@ -123,7 +123,7 @@ Ls.nno = {
"ot_noie": 'Fungerer mye betre i Chrome / Firefox / Edge',
"ab_mkdir": "lag mappe",
"ab_mkdoc": "nytt dokument",
"ab_mkdoc": "ny tekstfil",
"ab_msg": "send melding",
"ay_path": "gå videre åt mapper",
@ -435,6 +435,8 @@ Ls.nno = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopiér</a><a href="#" id="modal-ng">Last opp</a>',
"mk_noname": "skriv inn eit namn i tekstboksa åt venstre først :p",
"nmd_i1": "leggja også til filendinga du vil, til dømes <code>.txt</code>", //m
"nmd_i2": "du kan berre laga <code>.md</code>-filer fordi du ikkje har delete-tilgang", //m
"tv_load": "Lastar inn tekstfil:\n\n{0}\n\n{1}% ({2} av {3} MiB lasta ned)",
"tv_xe1": "kunne ikkje laste tekstfil:\n\nfeil ",

View file

@ -114,7 +114,7 @@ Ls.nor = {
"ot_unpost": "unpost: slett filer som du nylig har lastet opp; «angre-knappen»",
"ot_bup": "bup: tradisjonell / primitiv filopplastning,$N$Nfungerer i omtrent samtlige nettlesere",
"ot_mkdir": "mkdir: lag en ny mappe",
"ot_md": "new-md: lag et nytt markdown-dokument",
"ot_md": "new-file: lag en ny tekstfil",
"ot_msg": "msg: send en beskjed til serverloggen",
"ot_mp": "musikkspiller-instillinger",
"ot_cfg": "andre innstillinger",
@ -123,7 +123,7 @@ Ls.nor = {
"ot_noie": 'Fungerer mye bedre i Chrome / Firefox / Edge',
"ab_mkdir": "lag mappe",
"ab_mkdoc": "nytt dokument",
"ab_mkdoc": "ny tekstfil",
"ab_msg": "send melding",
"ay_path": "gå videre til mapper",
@ -435,6 +435,8 @@ Ls.nor = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopiér</a><a href="#" id="modal-ng">Last opp</a>',
"mk_noname": "skriv inn et navn i tekstboksen til venstre først :p",
"nmd_i1": "legg også til ønsket filtype, for eksempel <code>.txt</code>", //m
"nmd_i2": "du kan bare lage <code>.md</code>-filer fordi du ikke har delete-tilgang", //m
"tv_load": "Laster inn tekstfil:\n\n{0}\n\n{1}% ({2} av {3} MiB lastet ned)",
"tv_xe1": "kunne ikke laste tekstfil:\n\nfeil ",

View file

@ -119,7 +119,7 @@ Ls.pol = {
"ot_unpost": "unpost: usuń ostatnio przesłane pliki lub przerwij przesyłanie",
"ot_bup": "bup: podstawowe przesyłanie danych, wspiera nawet netscape 4.0",
"ot_mkdir": "mkdir: tworzy nowy folder",
"ot_md": "new-md: tworzy nowy dokument markdown",
"ot_md": "new-file: tworzy nowy plik tekstowy", //m
"ot_msg": "msg: wysyła wiadomość do loga serwera",
"ot_mp": "opcje odtwarzacza multimediów",
"ot_cfg": "opcje konfiguracji",
@ -128,7 +128,7 @@ Ls.pol = {
"ot_noie": 'Użyj przeglądarki Chrome / Firefox / Edge',
"ab_mkdir": "stwórz folder",
"ab_mkdoc": "stwórz dok. markdown",
"ab_mkdoc": "nowy plik tekstowy", //m
"ab_msg": "wyślij wiad. do logów serwera",
"ay_path": "przejdź do folderów",
@ -440,6 +440,8 @@ Ls.pol = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopiuj</a><a href="#" id="modal-ng">Prześlij</a>',
"mk_noname": "wpisz nazwę do pola po lewej zanim to zrobisz :p",
"nmd_i1": "możesz też dodać wybrane rozszerzenie, np. <code>.txt</code>", //m
"nmd_i2": "możesz tworzyć tylko pliki <code>.md</code>, ponieważ nie masz uprawnień do usuwania", //m
"tv_load": "Wczytywanie pliku tekstowego:\n\n{0}\n\n{1}% (wczytano {2} z {3} MiB)",
"tv_xe1": "nie udało się wczytać pliku:\n\nbłąd ",

View file

@ -116,7 +116,7 @@ Ls.por = {
"ot_unpost": "despublicar: excluir seus uploads recentes, ou abortar os que não foram concluídos",
"ot_bup": "bup: uploader básico, até suporta netscape 4.0",
"ot_mkdir": "mkdir: criar um novo diretório",
"ot_md": "new-md: criar um novo documento markdown",
"ot_md": "new-file: criar um novo ficheiro de texto", //m
"ot_msg": "msg: enviar uma mensagem para o log do servidor",
"ot_mp": "opções do reprodutor de mídia",
"ot_cfg": "opções de configuração",
@ -125,7 +125,7 @@ Ls.por = {
"ot_noie": 'Por favor, use Chrome / Firefox / Edge',
"ab_mkdir": "criar diretório",
"ab_mkdoc": "novo documento markdown",
"ab_mkdoc": "novo ficheiro de texto", //m
"ab_msg": "enviar msg para o log do srv",
"ay_path": "pular para pastas",
@ -437,6 +437,8 @@ Ls.por = {
"fcp_both_b": '<a href="#" id="modal-ok">Copiar</a><a href="#" id="modal-ng">Enviar</a>',
"mk_noname": "digite um nome no campo de texto à esquerda antes de fazer isso :p",
"nmd_i1": "também pode adicionar a extensão desejada, por exemplo <code>.txt</code>", //m
"nmd_i2": "só pode criar ficheiros <code>.md</code> porque não tem permissão para apagar", //m
"tv_load": "Carregando documento de texto:\n\n{0}\n\n{1}% ({2} de {3} MiB carregados)",
"tv_xe1": "não foi possível carregar o arquivo de texto:\n\nerro ",

View file

@ -116,7 +116,7 @@ Ls.rus = {
"ot_unpost": "unpost: удалить ваши недавние загрузки и отменить незавершённые",
"ot_bup": "bup: легковесный загрузчик файлов, поддерживает даже netscape 4.0",
"ot_mkdir": "mkdir: создать новую папку",
"ot_md": "new-md: создать новый markdown-документ",
"ot_md": "new-file: создать новый текстовый файл", //m
"ot_msg": "msg: отправить сообщение в лог сервера",
"ot_mp": "настройка медиаплеера",
"ot_cfg": "остальные настройки",
@ -125,7 +125,7 @@ Ls.rus = {
"ot_noie": 'Пожалуйста, используйте Chrome / Firefox / Edge',
"ab_mkdir": "создать папку",
"ab_mkdoc": "создать markdown-документ",
"ab_mkdoc": "создать текстовый файл", //m
"ab_msg": "отправить сообщение в лог сервера",
"ay_path": "перейти к папкам",
@ -437,6 +437,8 @@ Ls.rus = {
"fcp_both_b": '<a href="#" id="modal-ok">Скопировать</a><a href="#" id="modal-ng">Загрузить</a>',
"mk_noname": "введите имя в текстовое поле слева перед тем, как это делать :p",
"nmd_i1": "вы также можете указать нужное расширение, например <code>.txt</code>", //m
"nmd_i2": "вы можете создавать только файлы <code>.md</code>, так как у вас нет разрешения на удаление", //m
"tv_load": "Загружаю текстовый документ:\n\n{0}\n\n{1}% ({2} из {3} МиБ загружено)",
"tv_xe1": "не удалось загрузить текстовый файл:\n\nошибка ",

View file

@ -116,7 +116,7 @@ Ls.spa = {
"ot_unpost": "dessubir: elimina tus subidas recientes, o aborta las inacabadas",
"ot_bup": "bup: uploader básico, soporta hasta netscape 4.0",
"ot_mkdir": "mkdir: crear un nuevo directorio",
"ot_md": "new-md: crear un nuevo documento markdown",
"ot_md": "new-file: crear un nuevo archivo de texto", //m
"ot_msg": "msg: enviar un mensaje al registro del servidor",
"ot_mp": "opciones del reproductor multimedia",
"ot_cfg": "opciones de configuración",
@ -125,7 +125,7 @@ Ls.spa = {
"ot_noie": "Por favor, usa Chrome / Firefox / Edge",
"ab_mkdir": "crear directorio",
"ab_mkdoc": "nuevo documento markdown",
"ab_mkdoc": "nuevo archivo de texto", //m
"ab_msg": "enviar msg al registro del servidor",
"ay_path": "saltar a carpetas",
@ -436,6 +436,8 @@ Ls.spa = {
"fcp_both_b": "<a href=\"#\" id=\"modal-ok\">Copiar</a><a href=\"#\" id=\"modal-ng\">Subir</a>",
"mk_noname": "escribe un nombre en el campo de texto de la izquierda antes de hacer eso :p",
"nmd_i1": "también puedes añadir la extensión que quieras, por ejemplo <code>.txt</code>", //m
"nmd_i2": "solo puedes crear archivos <code>.md</code> porque no tienes permiso para borrar", //m
"tv_load": "Cargando documento de texto:\n\n{0}\n\n{1}% ({2} de {3} MiB cargados)",
"tv_xe1": "no se pudo cargar el archivo de texto:\n\nerror ",

View file

@ -116,7 +116,7 @@ Ls.swe = {
"ot_unpost": "unpost: radera dina senaste uppladdningar, eller avbryt pågående sådana",
"ot_bup": "bup: enkel uppladdare, stödjer t o m netscape 4.0",
"ot_mkdir": "mkdir: skapa en ny mapp",
"ot_md": "new-md: skapa ett nytt markdown-dokument",
"ot_md": "new-file: skapa en ny textfil",
"ot_msg": "msg: skicka ett meddelande till serverloggen",
"ot_mp": "mediaspelarinställningar",
"ot_cfg": "konfigurationsinställningar",
@ -125,7 +125,7 @@ Ls.swe = {
"ot_noie": 'Var vänlig använd Chrome / Firefox / Edge',
"ab_mkdir": "skapa mapp",
"ab_mkdoc": "nytt markdown-dokument",
"ab_mkdoc": "ny textfil",
"ab_msg": "skicka medd. till serverlogg",
"ay_path": "hoppa till mappar",
@ -437,6 +437,8 @@ Ls.swe = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopiera</a><a href="#" id="modal-ng">Ladda upp</a>',
"mk_noname": "skriv ett namn i fältet till vänster först :p",
"nmd_i1": "lägg också till filändelsen du vill ha, till exempel <code>.txt</code>", //m
"nmd_i2": "du kan bara skapa <code>.md</code>-filer eftersom du inte har borttagningsbehörighet", //m
"tv_load": "Laddar textfil:\n\n{0}\n\n{1}% ({2} av {3} MiB laddat)",
"tv_xe1": "kunde ej ladda textfil:\n\nfel ",

View file

@ -116,7 +116,7 @@ Ls.tur = {
"ot_unpost": "unpost: son yüklemelerinizi silin veya tamamlanmamış olanları iptal edin",
"ot_bup": "bup: temel yükleyici, hatta netscape 4.0'ı destekler",
"ot_mkdir": "mkdir: yeni bir dizin oluştur",
"ot_md": "new-md: yeni bir markdown belgesi oluştur",
"ot_md": "new-file: yeni bir metin dosyası oluştur", //m
"ot_msg": "msg: sunucu günlüğüne bir mesaj gönder",
"ot_mp": "medya oynatıcı seçenekleri",
"ot_cfg": "konfigürasyon seçenekleri",
@ -125,7 +125,7 @@ Ls.tur = {
"ot_noie": 'Lütfen Chrome / Firefox / Edge kullanın',
"ab_mkdir": "dizin oluştur",
"ab_mkdoc": "yeni markdown belgesi",
"ab_mkdoc": "yeni metin dosyası", //m
"ab_msg": "sunucu günlüğüne mesaj gönder",
"ay_path": "klasörlere atla",
@ -437,6 +437,8 @@ Ls.tur = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopyala</a><a href="#" id="modal-ng">Yükle</a>',
"mk_noname": "bunu yapmadan önce soldaki boşluğa bir şeyler yazsana :p",
"nmd_i1": "ayrıca istediğin dosya uzantısını ekleyebilirsin, örneğin <code>.txt</code>", //m
"nmd_i2": "silme iznin olmadığı için yalnızca <code>.md</code> dosyaları oluşturabilirsin", //m
"tv_load": "Metin belgesi yükleniyor:\n\n{0}\n\n{1}% ({2} of {3} MiB yüklendi)",
"tv_xe1": "metin dosyası yüklenemedi:\n\nhata ",

View file

@ -116,7 +116,7 @@ Ls.ukr = {
"ot_unpost": "скасувати: видалити недавні завантаження або перервати незавершені",
"ot_bup": "bup: основний завантажувач, підтримує навіть netscape 4.0",
"ot_mkdir": "mkdir: створити нову папку",
"ot_md": "new-md: створити новий markdown документ",
"ot_md": "new-file: створити новий текстовий файл", //m
"ot_msg": "msg: надіслати повідомлення в лог сервера",
"ot_mp": "налаштування медіаплеєра",
"ot_cfg": "параметри конфігурації",
@ -125,7 +125,7 @@ Ls.ukr = {
"ot_noie": 'Будь ласка, використовуйте Chrome / Firefox / Edge',
"ab_mkdir": "створити папку",
"ab_mkdoc": "новий markdown документ",
"ab_mkdoc": "новий текстовий файл", //m
"ab_msg": "надіслати повідомлення в лог сервера",
"ay_path": "перейти до папок",
@ -437,6 +437,8 @@ Ls.ukr = {
"fcp_both_b": '<a href="#" id="modal-ok">Скопіювати</a><a href="#" id="modal-ng">Завантажити</a>',
"mk_noname": "введіть ім'я в текстове поле зліва перед тим, як робити це :p",
"nmd_i1": "ви також можете додати потрібне розширення, наприклад <code>.txt</code>", //m
"nmd_i2": "ви можете створювати тільки файли <code>.md</code>, оскільки не маєте дозволу на видалення", //m
"tv_load": "Завантаження текстового документа:\n\n{0}\n\n{1}% ({2} з {3} MiB завантажено)",
"tv_xe1": "не вдалося завантажити текстовий файл:\n\nпомилка ",

View file

@ -1999,6 +1999,18 @@ function up2k_init(subtle) {
if (pvis.act == 'bz')
pvis.changecard('bz');
var n = st.files.length - 1,
f = n >= 0 && st.files[n];
if (f && !f.srch) {
var xhr = new XHR(),
ct = 'application/x-www-form-urlencoded;charset=UTF-8';
xhr.open('POST', f.purl, true);
xhr.setRequestHeader('Content-Type', ct);
if (xhr.overrideMimeType)
xhr.overrideMimeType('Content-Type', ct);
xhr.send('msg=upload-queue-empty;' + uricom_enc(f.name));
}
}
function chill(t) {
@ -2671,8 +2683,6 @@ function up2k_init(subtle) {
f2f(spd1, 2), !isNum(spd2) ? '--' : f2f(spd2, 2)));
pvis.move(t.n, 'ok');
if (!pvis.ctr.bz && !pvis.ctr.q)
uptoast();
}
else {
if (t.t_uploaded)

View file

@ -1487,7 +1487,7 @@ var tt = (function () {
var r = {
"tt": mknod("div", 'tt'),
"th": mknod("div", 'tth'),
"en": true,
"en": !window.notooltips,
"el": null,
"skip": false,
"lvis": 0

View file

@ -1,3 +1,82 @@
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-1025-1918 `v1.19.19` copyparty.eu マークII
## 🩹 bugfixes
* fix building the archlinux package e3524d85
* otherwise identical to [v1.19.18](https://github.com/9001/copyparty/releases/tag/v1.19.18)
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-1025-1434 `v1.19.18` copyparty.eu
## 🧪 new features
* #949 when all uploads have finished, the client (both the browser and u2c) sends a message to the server saying it's done db87ea5c
* #941 [copyparty-en.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty-en.pyz), yet another copyparty variant, with enterprise-friendly tweaks:
* does not include the smb-server, so antivirus doesn't think it's malware 7f5810f1
* english-only, because antivirus apparently hates certain translations too 7f5810f1
* renamed the webdav-config `.bat` to `.txt` because clearly only one of those are "dangerous" b624a387
* show volumes with permssion `h` in the navpane fff7291d
* #937 global-option `--notooltips` to default-disable tooltips a325353b
## 🩹 bugfixes
* #948 fix the u2c `--dr` option when the server is running on windows d3dd3456
* fix crash on startup when using volflags `unlistc*` and the parent folder is not a volume cdd5e78a
* `og` / opengraph / discord-embed fixes:
* using the `h` permission could result in unexpected 404 c9e45c12
* a single-file volume could make filenames in its parent volume unintentionally visible 36ab77e0
* this would only happen when combined with `--og`
* fix some harmless warnings from single-file volumes b1efc006
* fix filesize-colors in selected rows 1c17b63b
## 🔧 other changes
* releases can now also be downloaded from https://copyparty.eu/ 547a7ab1
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-1017-2313 `v1.19.17` read:cbz + re:ftp
## 🧪 new features
* #916 view cbz manga/comics in the browser (thx @Scotsguy!) 8ef6dda7
* #845 users/groups can be subtracted from a broader access grant b4fda5f1
* for example `*,-@acct` hides a volume from everyone who's logged in
* [reflink dedup](https://github.com/9001/copyparty/#file-deduplication) is now available in most python versions, not just 3.14 and newer f2caab61
* much better and safer than symlink/hardlink-based dedup, but only works with a few filesystems
* #905 option to magnify images/videos to fill the screen 66dc8b5c
* #921 #685 `xm` hooks can see the selected files (thx @carson-coder!) 6c024dbf 33644488
* #927 textfiles can now be viewed with the `?doc=` suffix with just the `g` permission dbb78705
* #742 new volflag `nodupem` to prevent dupes from being moved into a volume; the stronger alternative to `nodupe` which only prevents uploads f55d8341
* audioplayer: show embedded coverart as fallback for cover.jpg in OS widgets 9746b4e2
* #928 option to [hide certain ui-elements](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#hide-ui-elements), either with volflags or url-params 98da5cc5
* #911 users can now avoid autoban according to permissions 6f02812a
* verbosity and permssion options for `?stack` 677fd8ee
* default is now admin-only; previously it was "admin or read+write"
## 🩹 bugfixes
* #914 ftp-server: resuming interrupted uploads (thx @Audionut!) 33b0cd5a
* race-the-beam didn't work in non-toplevel shares d9cd7ec3
## 🔧 other changes
* #904 new example hook [wget-i.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/hooks/wget-i.py); import-safe fork of [wget.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/hooks/wget.py) dbd8f837
* hide the search-ui while viewing a share because searching in shares is not possible cca1f9b2
* config-parser now prevents invalid values for the lifetime volflag 5d96862c
* translations are now [separate files](https://github.com/9001/copyparty/tree/hovudstraum/copyparty/web/tl) instead of all chilling inside browser.js d099e5e8 d6433b78 a7840beb a7cdc5de 98086948 a85ad201 c2e03bf6 b9d7ede3 5a29df6b 52446bb5 bb166c98 0fa862e1 6de6aa4b 748aaa95 07ace416 b61b910e 28b93238 14bd4cf5 50109f76 3b009d97 f5425a88 5232ce6a 02ba9ea7 ff01723c d099e5e8
## 🌠 fun facts
* looks like i'll be in Japan november 726 and then at CCC for newyears!
* wait, I never made stickers... orz
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-1005-2259 `v1.19.16` FULLBURST

View file

@ -96,7 +96,8 @@ copyparty = [
"web/*.xml",
"web/tl/*.js",
"web/tl/*.gz",
"web/a/*.bat",
"web/a/*.txt",
"web/a/*.gz",
"web/deps/*.gz",
"web/deps/*.woff*",
]

View file

@ -33,10 +33,11 @@ i'm not very familiar with containers, so let me know if this section could be b
the container has the same default config as the sfx and the pypi module, meaning it will listen on port 3923 and share the "current folder" (`/w` inside the container) as read-write for anyone
the recommended way to configure copyparty inside a container is to mount a folder which has one or more [config files](https://github.com/9001/copyparty/blob/hovudstraum/docs/example.conf) inside; `-v /your/config/folder:/cfg`
the recommended way to configure copyparty inside a container is to mount a folder which has one or more [config files](https://github.com/9001/copyparty/blob/hovudstraum/docs/examples/docker/basic-docker-compose/copyparty.conf) inside; `-v /your/config/folder:/cfg`
* but you can also provide arguments to the docker command if you prefer that
* config files must be named `something.conf` to get picked up
* there are [more extensive config examples](https://github.com/9001/copyparty/blob/hovudstraum/docs/example.conf) but those are not made for docker so the paths are wrong (`/home/ed/Music` should be `/w/something` and so on)
also see [docker-specific recommendations](#docker-specific-recommendations)

View file

@ -85,6 +85,11 @@ filt=
wget https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py -O $fp
}
# enable arm32 crossbuild from aarch64 (macbook or whatever)
[ $(uname -m) = aarch64 ] && [ ! -e /proc/sys/fs/binfmt_misc/qemu-arm ] &&
echo ":qemu-arm:M:0:\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-arm-static:F" |
sudo tee >/dev/null /proc/sys/fs/binfmt_misc/register
# kill abandoned builders
ps aux | awk '/bin\/qemu-[^-]+-static/{print$2}' | xargs -r kill -9

View file

@ -10,6 +10,7 @@ gtar=$(command -v gtar || command -v gnutar) || true
sed() { gsed "$@"; }
find() { gfind "$@"; }
sort() { gsort "$@"; }
nproc() { gnproc; }
command -v grealpath >/dev/null &&
realpath() { grealpath "$@"; }
}

View file

@ -41,8 +41,10 @@ rm -rf pyz
mkdir -p pyz
cd pyz
cp -pR ../sfx/{copyparty,partftpy} .
cp -pR ../sfx/{ftp,j2}/* .
cp -pR ../sfx/copyparty .
cp -pR ../sfx/j2/* .
[ -e ../sfx/partftpy ] && cp -pR ../sfx/partftpy .
[ -e ../sfx/ftp ] && cp -pR ../sfx/ftp/* .
true && {
rm -rf copyparty/web/mde.* copyparty/web/deps/easymde*

View file

@ -445,14 +445,27 @@ iawk '/^class _Base/{s=1}!s' ftp/pyftpdlib/authorizers.py
iawk '/^ {0,4}[a-zA-Z]/{s=0}/^ {4}def (serve_forever|_loop)/{s=1}!s' ftp/pyftpdlib/servers.py
rm -f ftp/pyftpdlib/{__main__,prefork}.py
[ $no_ftp ] &&
unhelp() {
iawk '!/add_argument\("--'$1'/{print;next}
/ent\("--'$1'"/{print gensub(/(help=")[^"]+/,"\\1not available in this build","1");next}
{sub(/help=.*/,"help=argparse.SUPPRESS)")}1' copyparty/__main__.py
}
[ $no_ftp ] && {
unhelp ftp
rm -rf copyparty/ftpd.py ftp
}
[ $no_tfp ] &&
[ $no_tfp ] && {
unhelp tftp
rm -rf copyparty/tftpd.py partftpy
}
[ $no_smb ] &&
[ $no_smb ] && {
unhelp smb
rm -f copyparty/smbd.py
ised 's/^( {8}elif )record\.name.*"impacket".*/\10:/' copyparty/util.py
}
[ $no_zm ] &&
rm -rf copyparty/mdns.py copyparty/stolen/dnslib
@ -571,7 +584,7 @@ gzres() {
$pk "$f" &
done < <(
find -printf '%s %p\n' |
grep -E '\.(js|css)$' |
grep -E '\.(js|css)$|/web/a/.*\.txt$' |
grep -vF /deps/ |
sort -nr
)

View file

@ -4,7 +4,7 @@ set -e
# general housekeeping before a release
self=$(cd -- "$(dirname "$BASH_SOURCE")"; pwd -P)
ver=$(awk '/^VERSION/{gsub(/[^0-9]/," ");printf "%d.%d.%d\n",$1,$2,$3}' copyparty/__version__.py)
ver=$(awk '/^VERSION/{gsub(/[^0-9]/," ");printf "%d.%d.%d\n",$1,$2,$3}' $self/../copyparty/__version__.py)
update_arch_pkgbuild() {
cd "$self/../contrib/package/arch"

View file

@ -49,14 +49,23 @@ while [ "$1" ]; do
done
./make-pyz.sh
mv ../dist/copyparty{,-int}.pyz
./make-sfx.sh re lang eng "$@"
mv ../dist/copyparty-{sfx,en}.py
rm -rf /tmp/pe-copyparty* ../sfx
../dist/copyparty-en.py --version >/dev/null 2>&1
./make-sfx.sh re no-smb "$@"
./make-pyz.sh
mv ../dist/copyparty{,-en}.pyz
mv ../dist/copyparty{-int,}.pyz
mv ../dist/copyparty-{int,sfx}.py
./genhelp.sh
[ $rls ] || exit # ----------------------------------------------------
[ $rls ] || exit 0 # ----------------------------------------------------
./prep.sh
git add ../contrib/package/arch/PKGBUILD ../contrib/package/makedeb-mpr/PKGBUILD ../contrib/package/nix/copyparty/pin.json

View file

@ -66,7 +66,7 @@ copyparty/web/a,
copyparty/web/a/__init__.py,
copyparty/web/a/partyfuse.py,
copyparty/web/a/u2c.py,
copyparty/web/a/webdav-cfg.bat,
copyparty/web/a/webdav-cfg.txt,
copyparty/web/baguettebox.js,
copyparty/web/browser.css,
copyparty/web/browser.html,

View file

@ -145,7 +145,7 @@ Ls.hmn = {
"ot_unpost": "unpost: delete your recent uploads, or abort unfinished ones",
"ot_bup": "bup: basic uploader, even supports netscape 4.0",
"ot_mkdir": "mkdir: create a new directory",
"ot_md": "new-md: create a new markdown document",
"ot_md": "new-file: create a new textfile",
"ot_msg": "msg: send a message to the server log",
"ot_mp": "media player options",
"ot_cfg": "configuration options",
@ -154,7 +154,7 @@ Ls.hmn = {
"ot_noie": 'Please use Chrome / Firefox / Edge',
"ab_mkdir": "make directory",
"ab_mkdoc": "new markdown doc",
"ab_mkdoc": "new textfile",
"ab_msg": "send msg to srv log",
"ay_path": "skip to folders",
@ -466,6 +466,8 @@ Ls.hmn = {
"fcp_both_b": '<a href="#" id="modal-ok">Copy</a><a href="#" id="modal-ng">Upload</a>',
"mk_noname": "type a name into the text field on the left before you do that :p",
"nmd_i1": "also add the file extension you want, for example <code>.md</code>",
"nmd_i2": "you can only create <code>.md</code> files because you don't have the delete-permission",
"tv_load": "Loading text document:\n\n{0}\n\n{1}% ({2} of {3} MiB loaded)",
"tv_xe1": "could not load textfile:\n\nerror ",

View file

@ -187,9 +187,9 @@ class TestVFS(unittest.TestCase):
self.assertEqual(n.realpath, os.path.join(td, "a"))
self.assertAxs(n.axs.uread, ["*", "k"])
self.assertAxs(n.axs.uwrite, [])
perm_na = (False, False, False, False, False, False, False, False)
perm_rw = (True, True, False, False, False, False, False, False)
perm_ro = (True, False, False, False, False, False, False, False)
perm_na = (False, False, False, False, False, False, False, False, False)
perm_rw = (True, True, False, False, False, False, False, False, False)
perm_ro = (True, False, False, False, False, False, False, False, False)
self.assertEqual(vfs.can_access("/", "*"), perm_na)
self.assertEqual(vfs.can_access("/", "k"), perm_rw)
self.assertEqual(vfs.can_access("/a", "*"), perm_ro)