copyparty 1.19.16__py3-none-any.whl → 1.19.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- copyparty/__init__.py +25 -1
- copyparty/__main__.py +32 -9
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +78 -31
- copyparty/bos/bos.py +5 -1
- copyparty/cfg.py +20 -0
- copyparty/ftpd.py +6 -4
- copyparty/httpcli.py +166 -45
- copyparty/httpsrv.py +2 -2
- copyparty/mtag.py +2 -2
- copyparty/smbd.py +1 -1
- copyparty/svchub.py +3 -0
- copyparty/tftpd.py +1 -1
- copyparty/up2k.py +39 -15
- copyparty/util.py +15 -5
- copyparty/web/a/partyfuse.py.gz +0 -0
- copyparty/web/a/u2c.py.gz +0 -0
- copyparty/web/a/webdav-cfg.txt.gz +0 -0
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.html +3 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/splash.html +3 -0
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +1 -1
- copyparty/web/tl/chi.js.gz +0 -0
- copyparty/web/tl/cze.js.gz +0 -0
- copyparty/web/tl/deu.js.gz +0 -0
- copyparty/web/tl/epo.js.gz +0 -0
- copyparty/web/tl/fin.js.gz +0 -0
- copyparty/web/tl/fra.js.gz +0 -0
- copyparty/web/tl/grc.js.gz +0 -0
- copyparty/web/tl/ita.js.gz +0 -0
- copyparty/web/tl/kor.js.gz +0 -0
- copyparty/web/tl/nld.js.gz +0 -0
- copyparty/web/tl/nno.js.gz +0 -0
- copyparty/web/tl/nor.js.gz +0 -0
- copyparty/web/tl/pol.js.gz +0 -0
- copyparty/web/tl/por.js.gz +0 -0
- copyparty/web/tl/rus.js.gz +0 -0
- copyparty/web/tl/spa.js.gz +0 -0
- copyparty/web/tl/swe.js.gz +0 -0
- copyparty/web/tl/tur.js.gz +0 -0
- copyparty/web/tl/ukr.js.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/METADATA +26 -4
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/RECORD +52 -33
- copyparty/web/a/partyfuse.py +0 -947
- copyparty/web/a/u2c.py +0 -1718
- copyparty/web/a/webdav-cfg.bat +0 -45
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/WHEEL +0 -0
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/entry_points.txt +0 -0
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/licenses/LICENSE +0 -0
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/top_level.txt +0 -0
copyparty/__init__.py
CHANGED
|
@@ -52,7 +52,7 @@ except:
|
|
|
52
52
|
zs = """
|
|
53
53
|
web/a/partyfuse.py
|
|
54
54
|
web/a/u2c.py
|
|
55
|
-
web/a/webdav-cfg.
|
|
55
|
+
web/a/webdav-cfg.txt
|
|
56
56
|
web/baguettebox.js
|
|
57
57
|
web/browser.css
|
|
58
58
|
web/browser.html
|
|
@@ -97,12 +97,36 @@ web/splash.html
|
|
|
97
97
|
web/splash.js
|
|
98
98
|
web/svcs.html
|
|
99
99
|
web/svcs.js
|
|
100
|
+
web/tl/chi.js
|
|
101
|
+
web/tl/cze.js
|
|
102
|
+
web/tl/deu.js
|
|
103
|
+
web/tl/epo.js
|
|
104
|
+
web/tl/fin.js
|
|
105
|
+
web/tl/fra.js
|
|
106
|
+
web/tl/grc.js
|
|
107
|
+
web/tl/ita.js
|
|
108
|
+
web/tl/kor.js
|
|
109
|
+
web/tl/nld.js
|
|
110
|
+
web/tl/nno.js
|
|
111
|
+
web/tl/nor.js
|
|
112
|
+
web/tl/pol.js
|
|
113
|
+
web/tl/por.js
|
|
114
|
+
web/tl/rus.js
|
|
115
|
+
web/tl/spa.js
|
|
116
|
+
web/tl/swe.js
|
|
117
|
+
web/tl/tur.js
|
|
118
|
+
web/tl/ukr.js
|
|
100
119
|
web/ui.css
|
|
101
120
|
web/up2k.js
|
|
102
121
|
web/util.js
|
|
103
122
|
web/w.hash.js
|
|
104
123
|
"""
|
|
105
124
|
RES = set(zs.strip().split("\n"))
|
|
125
|
+
RESM = {
|
|
126
|
+
"web/a/partyfuse.txt": "web/a/partyfuse.py",
|
|
127
|
+
"web/a/u2c.txt": "web/a/u2c.py",
|
|
128
|
+
"web/a/webdav-cfg.bat": "web/a/webdav-cfg.txt",
|
|
129
|
+
}
|
|
106
130
|
|
|
107
131
|
|
|
108
132
|
class EnvParams(object):
|
copyparty/__main__.py
CHANGED
|
@@ -641,8 +641,11 @@ def get_sects():
|
|
|
641
641
|
if no accounts or volumes are configured,
|
|
642
642
|
current folder will be read/write for everyone
|
|
643
643
|
|
|
644
|
-
the group @acct will always have every user with an account
|
|
645
|
-
(the name of that group can be changed with --grp-all)
|
|
644
|
+
the group \033[33m@acct\033[0m will always have every user with an account
|
|
645
|
+
(the name of that group can be changed with \033[32m--grp-all\033[0m)
|
|
646
|
+
|
|
647
|
+
to hide a volume from authenticated users, specify \033[33m*,-@acct\033[0m
|
|
648
|
+
to subtract \033[33m@acct\033[0m from \033[33m*\033[0m (can subtract users from groups too)
|
|
646
649
|
|
|
647
650
|
consider the config file for more flexible account/volume management,
|
|
648
651
|
including dynamic reload at runtime (and being more readable w)
|
|
@@ -664,12 +667,12 @@ def get_sects():
|
|
|
664
667
|
|
|
665
668
|
send the password in the '\033[36mPW\033[0m' http-header:
|
|
666
669
|
\033[36mPW: \033[35mhunter2\033[0m
|
|
667
|
-
or if you have \033[33m--
|
|
670
|
+
or if you have \033[33m--usernames\033[0m enabled,
|
|
668
671
|
\033[36mPW: \033[35med:hunter2\033[0m
|
|
669
672
|
|
|
670
673
|
send the password in the URL itself:
|
|
671
674
|
\033[36mhttp://127.0.0.1:3923/\033[35m?pw=hunter2\033[0m
|
|
672
|
-
or if you have \033[33m--
|
|
675
|
+
or if you have \033[33m--usernames\033[0m enabled,
|
|
673
676
|
\033[36mhttp://127.0.0.1:3923/\033[35m?pw=ed:hunter2\033[0m
|
|
674
677
|
|
|
675
678
|
use basic-authentication:
|
|
@@ -1166,6 +1169,7 @@ def add_general(ap, nc, srvname):
|
|
|
1166
1169
|
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)")
|
|
1167
1170
|
ap2.add_argument("--license", action="store_true", help="show licenses and exit")
|
|
1168
1171
|
ap2.add_argument("--version", action="store_true", help="show versions and exit")
|
|
1172
|
+
ap2.add_argument("--versionb", action="store_true", help="show version and exit")
|
|
1169
1173
|
|
|
1170
1174
|
|
|
1171
1175
|
def add_qr(ap, tty):
|
|
@@ -1233,6 +1237,7 @@ def add_upload(ap):
|
|
|
1233
1237
|
ap2.add_argument("--hardlink-only", action="store_true", help="do not fallback to symlinks when a hardlink cannot be made (volflag=hardlinkonly)")
|
|
1234
1238
|
ap2.add_argument("--reflink", action="store_true", help="enable reflink-based dedup; will fallback on full copies when that is impossible (non-CoW filesystem) (volflag=reflink)")
|
|
1235
1239
|
ap2.add_argument("--no-dupe", action="store_true", help="reject duplicate files during upload; only matches within the same volume (volflag=nodupe)")
|
|
1240
|
+
ap2.add_argument("--no-dupe-m", action="store_true", help="also reject dupes when moving a file into another volume (volflag=nodupem)")
|
|
1236
1241
|
ap2.add_argument("--no-clone", action="store_true", help="do not use existing data on disk to satisfy dupe uploads; reduces server HDD reads in exchange for much more network load (volflag=noclone)")
|
|
1237
1242
|
ap2.add_argument("--no-snap", action="store_true", help="disable snapshots -- forget unfinished uploads on shutdown; don't create .hist/up2k.snap files -- abandoned/interrupted uploads must be cleaned up manually")
|
|
1238
1243
|
ap2.add_argument("--snap-wri", metavar="SEC", type=int, default=300, help="write upload state to ./hist/up2k.snap every \033[33mSEC\033[0m seconds; allows resuming incomplete uploads after a server crash")
|
|
@@ -1520,6 +1525,7 @@ def add_optouts(ap):
|
|
|
1520
1525
|
ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)")
|
|
1521
1526
|
ap2.add_argument("--no-tail", action="store_true", help="disable streaming a growing files with ?tail (volflag=notail)")
|
|
1522
1527
|
ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader-IP into the database; will also disable unpost, you may want \033[32m--forget-ip\033[0m instead (volflag=no_db_ip)")
|
|
1528
|
+
ap2.add_argument("--no-zls", action="store_true", help="disable browsing the contents of zip/cbz files, does not affect thumbnails")
|
|
1523
1529
|
|
|
1524
1530
|
|
|
1525
1531
|
def add_safety(ap):
|
|
@@ -1538,6 +1544,7 @@ def add_safety(ap):
|
|
|
1538
1544
|
ap2.add_argument("--force-js", action="store_true", help="don't send folder listings as HTML, force clients to use the embedded json instead -- slight protection against misbehaving search engines which ignore \033[33m--no-robots\033[0m")
|
|
1539
1545
|
ap2.add_argument("--no-robots", action="store_true", help="adds http and html headers asking search engines to not index anything (volflag=norobots)")
|
|
1540
1546
|
ap2.add_argument("--logout", metavar="H", type=float, default=8086.0, help="logout clients after \033[33mH\033[0m hours of inactivity; [\033[32m0.0028\033[0m]=10sec, [\033[32m0.1\033[0m]=6min, [\033[32m24\033[0m]=day, [\033[32m168\033[0m]=week, [\033[32m720\033[0m]=month, [\033[32m8760\033[0m]=year)")
|
|
1547
|
+
ap2.add_argument("--dont-ban", metavar="TXT", type=u, default="no", help="anyone at this accesslevel or above will not get banned: [\033[32mav\033[0m]=admin-in-volume, [\033[32maa\033[0m]=has-admin-anywhere, [\033[32mrw\033[0m]=read-write, [\033[32mauth\033[0m]=authenticated, [\033[32many\033[0m]=disable-all-bans, [\033[32mno\033[0m]=anyone-can-get-banned")
|
|
1541
1548
|
ap2.add_argument("--ban-pw", metavar="N,W,B", type=u, default="9,60,1440", help="more than \033[33mN\033[0m wrong passwords in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
|
|
1542
1549
|
ap2.add_argument("--ban-pwc", metavar="N,W,B", type=u, default="5,60,1440", help="more than \033[33mN\033[0m password-changes in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
|
|
1543
1550
|
ap2.add_argument("--ban-404", metavar="N,W,B", type=u, default="50,60,1440", help="hitting more than \033[33mN\033[0m 404's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; only affects users who cannot see directory listings because their access is either g/G/h")
|
|
@@ -1602,12 +1609,14 @@ def add_admin(ap):
|
|
|
1602
1609
|
ap2 = ap.add_argument_group("admin panel options")
|
|
1603
1610
|
ap2.add_argument("--no-reload", action="store_true", help="disable ?reload=cfg (reload users/volumes/volflags from config file)")
|
|
1604
1611
|
ap2.add_argument("--no-rescan", action="store_true", help="disable ?scan (volume reindexing)")
|
|
1605
|
-
ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks)")
|
|
1612
|
+
ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks); same as --stack-who=no")
|
|
1606
1613
|
ap2.add_argument("--no-ups-page", action="store_true", help="disable ?ru (list of recent uploads)")
|
|
1607
1614
|
ap2.add_argument("--no-up-list", action="store_true", help="don't show list of incoming files in controlpanel")
|
|
1608
1615
|
ap2.add_argument("--dl-list", metavar="LVL", type=int, default=2, help="who can see active downloads in the controlpanel? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone")
|
|
1609
1616
|
ap2.add_argument("--ups-who", metavar="LVL", type=int, default=2, help="who can see recent uploads on the ?ru page? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone (volflag=ups_who)")
|
|
1610
1617
|
ap2.add_argument("--ups-when", action="store_true", help="let everyone see upload timestamps on the ?ru page, not just admins")
|
|
1618
|
+
ap2.add_argument("--stack-who", metavar="LVL", type=u, default="a", help="who can see the ?stack page (list of threads)? [\033[32mno\033[0m]=nobody, [\033[32ma\033[0m]=admins, [\033[32mrw\033[0m]=read+write, [\033[32mall\033[0m]=everyone")
|
|
1619
|
+
ap2.add_argument("--stack-v", action="store_true", help="verbose ?stack")
|
|
1611
1620
|
|
|
1612
1621
|
|
|
1613
1622
|
def add_thumbnail(ap):
|
|
@@ -1779,6 +1788,7 @@ def add_ui(ap, retry ):
|
|
|
1779
1788
|
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)")
|
|
1780
1789
|
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)")
|
|
1781
1790
|
ap2.add_argument("--mpmc", type=u, default="", help=argparse.SUPPRESS)
|
|
1791
|
+
ap2.add_argument("--notooltips", action="store_true", help="tooltips disabled as default")
|
|
1782
1792
|
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]")
|
|
1783
1793
|
ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include in the filebrowser html")
|
|
1784
1794
|
ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html")
|
|
@@ -1805,6 +1815,15 @@ def add_ui(ap, retry ):
|
|
|
1805
1815
|
ap2.add_argument("--lg-sba", metavar="TXT", type=u, default="", help="the value of the iframe 'allow' attribute for prologue/epilogue docs (volflag=lg_sba); see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy#iframes")
|
|
1806
1816
|
ap2.add_argument("--no-sb-md", action="store_true", help="don't sandbox README/PREADME.md documents (volflags: no_sb_md | sb_md)")
|
|
1807
1817
|
ap2.add_argument("--no-sb-lg", action="store_true", help="don't sandbox prologue/epilogue docs (volflags: no_sb_lg | sb_lg); enables non-js support")
|
|
1818
|
+
ap2.add_argument("--ui-nombar", action="store_true", help="hide top-menu in the UI (volflag=ui_nombar)")
|
|
1819
|
+
ap2.add_argument("--ui-noacci", action="store_true", help="hide account-info in the UI (volflag=ui_noacci)")
|
|
1820
|
+
ap2.add_argument("--ui-nosrvi", action="store_true", help="hide server-info in the UI (volflag=ui_nosrvi)")
|
|
1821
|
+
ap2.add_argument("--ui-nonav", action="store_true", help="hide navpane+breadcrumbs (volflag=ui_nonav)")
|
|
1822
|
+
ap2.add_argument("--ui-notree", action="store_true", help="hide navpane in the UI (volflag=ui_nonav)")
|
|
1823
|
+
ap2.add_argument("--ui-nocpla", action="store_true", help="hide cpanel-link in the UI (volflag=ui_nocpla)")
|
|
1824
|
+
ap2.add_argument("--ui-nolbar", action="store_true", help="hide link-bar in the UI (volflag=ui_nolbar)")
|
|
1825
|
+
ap2.add_argument("--ui-noctxb", action="store_true", help="hide context-buttons in the UI (volflag=ui_noctxb)")
|
|
1826
|
+
ap2.add_argument("--ui-norepl", action="store_true", help="hide repl-button in the UI (volflag=ui_norepl)")
|
|
1808
1827
|
ap2.add_argument("--have-unlistc", action="store_true", help=argparse.SUPPRESS)
|
|
1809
1828
|
|
|
1810
1829
|
|
|
@@ -1932,15 +1951,19 @@ def run_argparse(
|
|
|
1932
1951
|
|
|
1933
1952
|
|
|
1934
1953
|
def main(argv = None) :
|
|
1954
|
+
if argv is None:
|
|
1955
|
+
argv = sys.argv
|
|
1956
|
+
|
|
1957
|
+
if "--versionb" in argv:
|
|
1958
|
+
print(S_VERSION)
|
|
1959
|
+
sys.exit(0)
|
|
1960
|
+
|
|
1935
1961
|
time.strptime("19970815", "%Y%m%d") # python#7980
|
|
1936
1962
|
if WINDOWS:
|
|
1937
1963
|
os.system("rem") # enables colors
|
|
1938
1964
|
|
|
1939
1965
|
init_E(E)
|
|
1940
1966
|
|
|
1941
|
-
if argv is None:
|
|
1942
|
-
argv = sys.argv
|
|
1943
|
-
|
|
1944
1967
|
f = '\033[36mcopyparty v{} "\033[35m{}\033[36m" ({})\n{}\033[0;36m\n sqlite {} | jinja {} | pyftpd {} | tftp {}\n\033[0m'
|
|
1945
1968
|
f = f.format(
|
|
1946
1969
|
S_VERSION,
|
|
@@ -2067,7 +2090,7 @@ def main(argv = None) :
|
|
|
2067
2090
|
|
|
2068
2091
|
# propagate implications
|
|
2069
2092
|
for k1, k2 in IMPLICATIONS:
|
|
2070
|
-
if getattr(al, k1):
|
|
2093
|
+
if getattr(al, k1, None):
|
|
2071
2094
|
setattr(al, k2, True)
|
|
2072
2095
|
|
|
2073
2096
|
# propagate unplications
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
|
@@ -391,6 +391,9 @@ class VFS(object):
|
|
|
391
391
|
self.vpath = vpath # absolute path in the virtual filesystem
|
|
392
392
|
self.vpath0 = vpath0 # original vpath (before idp expansion)
|
|
393
393
|
self.axs = axs
|
|
394
|
+
self.uaxs = {}
|
|
395
|
+
|
|
396
|
+
|
|
394
397
|
self.flags = flags # config options
|
|
395
398
|
self.root = self
|
|
396
399
|
self.dev = 0 # st_dev
|
|
@@ -548,29 +551,19 @@ class VFS(object):
|
|
|
548
551
|
|
|
549
552
|
def can_access(
|
|
550
553
|
self, vpath , uname
|
|
551
|
-
)
|
|
552
|
-
"""can Read,Write,Move,Delete,Get,Upget,Admin,Dot"""
|
|
554
|
+
) :
|
|
555
|
+
"""can Read,Write,Move,Delete,Get,Upget,Html,Admin,Dot"""
|
|
556
|
+
# NOTE: only used by get_perms, which is only used by hooks; the lowest of fruits
|
|
553
557
|
if vpath:
|
|
554
558
|
vn, _ = self._find(undot(vpath))
|
|
555
559
|
else:
|
|
556
560
|
vn = self
|
|
557
561
|
|
|
558
|
-
|
|
559
|
-
return (
|
|
560
|
-
uname in c.uread,
|
|
561
|
-
uname in c.uwrite,
|
|
562
|
-
uname in c.umove,
|
|
563
|
-
uname in c.udel,
|
|
564
|
-
uname in c.uget,
|
|
565
|
-
uname in c.upget,
|
|
566
|
-
uname in c.uadmin,
|
|
567
|
-
uname in c.udot,
|
|
568
|
-
)
|
|
569
|
-
# skip uhtml because it's rarely needed
|
|
562
|
+
return vn.uaxs[uname]
|
|
570
563
|
|
|
571
564
|
def get_perms(self, vpath , uname ) :
|
|
572
565
|
zbl = self.can_access(vpath, uname)
|
|
573
|
-
ret = "".join(ch for ch, ok in zip("
|
|
566
|
+
ret = "".join(ch for ch, ok in zip("rwmdgGha.", zbl) if ok)
|
|
574
567
|
if "rwmd" in ret and "a." in ret:
|
|
575
568
|
ret += "A"
|
|
576
569
|
return ret
|
|
@@ -763,20 +756,17 @@ class VFS(object):
|
|
|
763
756
|
virt_vis[name] = vn2
|
|
764
757
|
continue
|
|
765
758
|
|
|
766
|
-
|
|
767
|
-
zx = vn2.axs
|
|
768
|
-
axs = [zx.uread, zx.uwrite, zx.umove, zx.udel, zx.uget]
|
|
759
|
+
u_has = vn2.uaxs.get(uname) or [False] * 9
|
|
769
760
|
for pset in permsets:
|
|
770
761
|
ok = True
|
|
771
|
-
for req,
|
|
772
|
-
if req and
|
|
762
|
+
for req, zb in zip(pset, u_has):
|
|
763
|
+
if req and not zb:
|
|
773
764
|
ok = False
|
|
765
|
+
break
|
|
774
766
|
if ok:
|
|
767
|
+
virt_vis[name] = vn2
|
|
775
768
|
break
|
|
776
769
|
|
|
777
|
-
if ok:
|
|
778
|
-
virt_vis[name] = vn2
|
|
779
|
-
|
|
780
770
|
if ".hist" in abspath:
|
|
781
771
|
p = abspath.replace("\\", "/") if WINDOWS else abspath
|
|
782
772
|
if p.endswith("/.hist"):
|
|
@@ -1961,9 +1951,18 @@ class AuthSrv(object):
|
|
|
1961
1951
|
axs_key = "u" + perm
|
|
1962
1952
|
for vp, vol in vfs.all_vols.items():
|
|
1963
1953
|
zx = getattr(vol.axs, axs_key)
|
|
1964
|
-
if "*" in zx:
|
|
1954
|
+
if "*" in zx and "-@acct" not in zx:
|
|
1965
1955
|
for usr in unames:
|
|
1966
1956
|
zx.add(usr)
|
|
1957
|
+
for zs in list(zx):
|
|
1958
|
+
if zs.startswith("-"):
|
|
1959
|
+
zx.discard(zs)
|
|
1960
|
+
zs = zs[1:]
|
|
1961
|
+
zx.discard(zs)
|
|
1962
|
+
if zs.startswith("@"):
|
|
1963
|
+
zs = zs[1:]
|
|
1964
|
+
for zs in grps.get(zs) or []:
|
|
1965
|
+
zx.discard(zs)
|
|
1967
1966
|
|
|
1968
1967
|
# aread,... = dict[uname, list[volnames] or []]
|
|
1969
1968
|
umap = {x: [] for x in unames}
|
|
@@ -1975,6 +1974,23 @@ class AuthSrv(object):
|
|
|
1975
1974
|
umap[usr].sort()
|
|
1976
1975
|
setattr(vfs, "a" + perm, umap)
|
|
1977
1976
|
|
|
1977
|
+
for vol in vfs.all_nodes.values():
|
|
1978
|
+
za = vol.axs
|
|
1979
|
+
vol.uaxs = {
|
|
1980
|
+
un: (
|
|
1981
|
+
un in za.uread,
|
|
1982
|
+
un in za.uwrite,
|
|
1983
|
+
un in za.umove,
|
|
1984
|
+
un in za.udel,
|
|
1985
|
+
un in za.uget,
|
|
1986
|
+
un in za.upget,
|
|
1987
|
+
un in za.uhtml,
|
|
1988
|
+
un in za.uadmin,
|
|
1989
|
+
un in za.udot,
|
|
1990
|
+
)
|
|
1991
|
+
for un in unames
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1978
1994
|
all_users = {}
|
|
1979
1995
|
missing_users = {}
|
|
1980
1996
|
associated_users = {}
|
|
@@ -2308,6 +2324,10 @@ class AuthSrv(object):
|
|
|
2308
2324
|
free_umask = False
|
|
2309
2325
|
have_reflink = False
|
|
2310
2326
|
for vol in vfs.all_nodes.values():
|
|
2327
|
+
if os.path.isfile(vol.realpath):
|
|
2328
|
+
vol.flags["is_file"] = True
|
|
2329
|
+
vol.flags["d2d"] = True
|
|
2330
|
+
|
|
2311
2331
|
if (self.args.e2ds and vol.axs.uwrite) or self.args.e2dsa:
|
|
2312
2332
|
vol.flags["e2ds"] = True
|
|
2313
2333
|
|
|
@@ -2577,6 +2597,15 @@ class AuthSrv(object):
|
|
|
2577
2597
|
for x in drop:
|
|
2578
2598
|
vol.flags.pop(x)
|
|
2579
2599
|
|
|
2600
|
+
zi = vol.flags.get("lifetime") or 0
|
|
2601
|
+
zi2 = time.time() // (86400 * 365)
|
|
2602
|
+
zi3 = zi2 * 86400 * 365
|
|
2603
|
+
if zi < 0 or zi > zi3:
|
|
2604
|
+
t = "the lifetime of volume [/%s] (%d) exceeds max value (%d years; %d)"
|
|
2605
|
+
t = t % (vol.vpath, zi, zi2, zi3)
|
|
2606
|
+
self.log(t, 1)
|
|
2607
|
+
raise Exception(t)
|
|
2608
|
+
|
|
2580
2609
|
# verify tags mentioned by -mt[mp] are used by -mte
|
|
2581
2610
|
local_mtp = {}
|
|
2582
2611
|
local_only_mtp = {}
|
|
@@ -2613,7 +2642,14 @@ class AuthSrv(object):
|
|
|
2613
2642
|
errors = True
|
|
2614
2643
|
|
|
2615
2644
|
for vol in vfs.all_nodes.values():
|
|
2616
|
-
if not vol.
|
|
2645
|
+
if not vol.flags.get("is_file"):
|
|
2646
|
+
continue
|
|
2647
|
+
zs = "og opds xlink"
|
|
2648
|
+
for zs in zs.split():
|
|
2649
|
+
vol.flags.pop(zs, None)
|
|
2650
|
+
|
|
2651
|
+
for vol in vfs.all_nodes.values():
|
|
2652
|
+
if not vol.realpath or vol.flags.get("is_file"):
|
|
2617
2653
|
continue
|
|
2618
2654
|
ccs = vol.flags["casechk"][:1].lower()
|
|
2619
2655
|
if ccs in ("y", "n"):
|
|
@@ -2743,9 +2779,13 @@ class AuthSrv(object):
|
|
|
2743
2779
|
["uadmin", "uadmin"],
|
|
2744
2780
|
]:
|
|
2745
2781
|
u = list(sorted(getattr(zv.axs, attr)))
|
|
2746
|
-
u
|
|
2747
|
-
|
|
2748
|
-
|
|
2782
|
+
if u == ["*"] and acct:
|
|
2783
|
+
u = ["\033[35monly-anonymous\033[0m"]
|
|
2784
|
+
elif "*" in u:
|
|
2785
|
+
u = ["\033[35meverybody\033[0m"]
|
|
2786
|
+
if not u:
|
|
2787
|
+
u = ["\033[36m--none--\033[0m"]
|
|
2788
|
+
u = ", ".join(u)
|
|
2749
2789
|
t += "\n| {}: {}".format(txt, u)
|
|
2750
2790
|
|
|
2751
2791
|
if "e2d" in zv.flags:
|
|
@@ -2857,8 +2897,6 @@ class AuthSrv(object):
|
|
|
2857
2897
|
|
|
2858
2898
|
if have_reflink:
|
|
2859
2899
|
t = "WARNING: Reflink-based dedup was requested, but %s. This will not work; files will be full copies instead."
|
|
2860
|
-
if sys.version_info < (3, 14):
|
|
2861
|
-
self.log(t % "your python version is not new enough", 1)
|
|
2862
2900
|
if not sys.platform.startswith("linux"):
|
|
2863
2901
|
self.log(t % "your OS is not Linux", 1)
|
|
2864
2902
|
|
|
@@ -3016,6 +3054,7 @@ class AuthSrv(object):
|
|
|
3016
3054
|
"have_shr": self.args.shr,
|
|
3017
3055
|
"shr_who": vf["shr_who"],
|
|
3018
3056
|
"have_zip": not self.args.no_zip,
|
|
3057
|
+
"have_zls": not self.args.no_zls,
|
|
3019
3058
|
"have_mv": not self.args.no_mv,
|
|
3020
3059
|
"have_del": not self.args.no_del,
|
|
3021
3060
|
"have_unpost": int(self.args.unpost),
|
|
@@ -3052,6 +3091,14 @@ class AuthSrv(object):
|
|
|
3052
3091
|
"lifetime": vn.js_ls["lifetime"],
|
|
3053
3092
|
"u2sort": self.args.u2sort,
|
|
3054
3093
|
}
|
|
3094
|
+
zs = "ui_noacci ui_nocpla ui_noctxb ui_nolbar ui_nombar ui_nonav ui_notree ui_norepl ui_nosrvi"
|
|
3095
|
+
for zs in zs.split():
|
|
3096
|
+
if vf.get(zs):
|
|
3097
|
+
js_htm[zs] = 1
|
|
3098
|
+
zs = "notooltips"
|
|
3099
|
+
for zs in zs.split():
|
|
3100
|
+
if getattr(self.args, zs, False):
|
|
3101
|
+
js_htm[zs] = 1
|
|
3055
3102
|
vn.js_htm = json_hesc(json.dumps(js_htm))
|
|
3056
3103
|
|
|
3057
3104
|
vols = list(vfs.all_nodes.values())
|
|
@@ -3396,7 +3443,7 @@ class AuthSrv(object):
|
|
|
3396
3443
|
raise Exception("volume not found: " + zs)
|
|
3397
3444
|
|
|
3398
3445
|
self.log(str({"users": users, "vols": vols, "flags": flags}))
|
|
3399
|
-
t = "/{}: read({}) write({}) move({}) del({}) dots({}) get({}) upGet({}) uadmin({})"
|
|
3446
|
+
t = "/{}: read({}) write({}) move({}) del({}) dots({}) get({}) upGet({}) html({}) uadmin({})"
|
|
3400
3447
|
for k, zv in self.vfs.all_vols.items():
|
|
3401
3448
|
vc = zv.axs
|
|
3402
3449
|
vs = [
|
copyparty/bos/bos.py
CHANGED
copyparty/cfg.py
CHANGED
|
@@ -19,6 +19,7 @@ def vf_bmap() :
|
|
|
19
19
|
"no_clone": "noclone",
|
|
20
20
|
"no_dirsz": "nodirsz",
|
|
21
21
|
"no_dupe": "nodupe",
|
|
22
|
+
"no_dupe_m": "nodupem",
|
|
22
23
|
"no_forget": "noforget",
|
|
23
24
|
"no_pipe": "nopipe",
|
|
24
25
|
"no_robots": "norobots",
|
|
@@ -58,6 +59,15 @@ def vf_bmap() :
|
|
|
58
59
|
"rm_partial",
|
|
59
60
|
"rmagic",
|
|
60
61
|
"rss",
|
|
62
|
+
"ui_noacci",
|
|
63
|
+
"ui_nocpla",
|
|
64
|
+
"ui_nolbar",
|
|
65
|
+
"ui_nombar",
|
|
66
|
+
"ui_nonav",
|
|
67
|
+
"ui_notree",
|
|
68
|
+
"ui_norepl",
|
|
69
|
+
"ui_nosrvi",
|
|
70
|
+
"ui_noctxb",
|
|
61
71
|
"wo_up_readme",
|
|
62
72
|
"wram",
|
|
63
73
|
"xdev",
|
|
@@ -189,6 +199,7 @@ flagcats = {
|
|
|
189
199
|
"safededup": "verify on-disk data before using it for dedup",
|
|
190
200
|
"noclone": "take dupe data from clients, even if available on HDD",
|
|
191
201
|
"nodupe": "rejects existing files (instead of linking/cloning them)",
|
|
202
|
+
"nodupem": "rejects existing files during moves as well",
|
|
192
203
|
"chmod_d=755": "unix-permission for new dirs/folders",
|
|
193
204
|
"chmod_f=644": "unix-permission for new files",
|
|
194
205
|
"uid=573": "change owner of new files/folders to unix-user 573",
|
|
@@ -325,6 +336,15 @@ flagcats = {
|
|
|
325
336
|
"md_sba": "value of iframe allow-prop for markdown-sandbox",
|
|
326
337
|
"lg_sba": "value of iframe allow-prop for *logue-sandbox",
|
|
327
338
|
"nohtml": "return html and markdown as text/html",
|
|
339
|
+
"ui_noacci": "hide account-info in the UI",
|
|
340
|
+
"ui_nocpla": "hide cpanel-link in the UI",
|
|
341
|
+
"ui_nolbar": "hide link-bar in the UI",
|
|
342
|
+
"ui_nombar": "hide top-menu in the UI",
|
|
343
|
+
"ui_nonav": "hide navpane+breadcrumbs in the UI",
|
|
344
|
+
"ui_notree": "hide navpane in the UI",
|
|
345
|
+
"ui_norepl": "hide repl-button in the UI",
|
|
346
|
+
"ui_nosrvi": "hide server-info in the UI",
|
|
347
|
+
"ui_noctxb": "hide context-buttons in the UI",
|
|
328
348
|
},
|
|
329
349
|
"opengraph (discord embeds)": {
|
|
330
350
|
"og": "enable OG (disables hotlinking)",
|
copyparty/ftpd.py
CHANGED
|
@@ -194,7 +194,7 @@ class FtpFs(AbstractedFS):
|
|
|
194
194
|
if not avfs:
|
|
195
195
|
raise FSE(t.format(vpath), 1)
|
|
196
196
|
|
|
197
|
-
cr, cw, cm, cd, _, _, _, _ = avfs.
|
|
197
|
+
cr, cw, cm, cd, _, _, _, _, _ = avfs.uaxs[self.h.uname]
|
|
198
198
|
if r and not cr or w and not cw or m and not cm or d and not cd:
|
|
199
199
|
raise FSE(t.format(vpath), 1)
|
|
200
200
|
|
|
@@ -247,7 +247,7 @@ class FtpFs(AbstractedFS):
|
|
|
247
247
|
|
|
248
248
|
if w and need_unlink:
|
|
249
249
|
if td >= -1 and td <= self.args.ftp_wt:
|
|
250
|
-
# within permitted timeframe;
|
|
250
|
+
# within permitted timeframe; allow overwrite or resume
|
|
251
251
|
do_it = True
|
|
252
252
|
elif self.args.no_del or self.args.ftp_no_ow:
|
|
253
253
|
# file too old, or overwrite not allowed; reject
|
|
@@ -264,7 +264,9 @@ class FtpFs(AbstractedFS):
|
|
|
264
264
|
if not do_it:
|
|
265
265
|
raise FSE("File already exists")
|
|
266
266
|
|
|
267
|
-
|
|
267
|
+
# Don't unlink file for append mode
|
|
268
|
+
elif "a" not in mode:
|
|
269
|
+
wunlink(self.log, ap, VF_CAREFUL)
|
|
268
270
|
|
|
269
271
|
ret = open(fsenc(ap), mode, self.args.iobuf)
|
|
270
272
|
if w and "fperms" in vfs.flags:
|
|
@@ -505,7 +507,7 @@ class FtpHandler(FTPHandler):
|
|
|
505
507
|
0,
|
|
506
508
|
self.cli_ip,
|
|
507
509
|
time.time(),
|
|
508
|
-
|
|
510
|
+
None,
|
|
509
511
|
)
|
|
510
512
|
t = hr.get("rejectmsg") or ""
|
|
511
513
|
if t or not hr:
|