copyparty 1.18.2__tar.gz → 1.18.4__tar.gz
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-1.18.2 → copyparty-1.18.4}/PKG-INFO +8 -2
- {copyparty-1.18.2 → copyparty-1.18.4}/README.md +7 -1
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/__main__.py +41 -1
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/__version__.py +2 -2
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/authsrv.py +125 -17
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/bos/bos.py +18 -6
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/cfg.py +5 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/ftpd.py +9 -4
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/httpcli.py +34 -19
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/smbd.py +2 -2
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/svchub.py +5 -1
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/tcpsrv.py +1 -1
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/tftpd.py +6 -2
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/th_srv.py +3 -2
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/up2k.py +40 -11
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/util.py +17 -3
- copyparty-1.18.4/copyparty/web/baguettebox.js.gz +0 -0
- copyparty-1.18.4/copyparty/web/browser.css.gz +0 -0
- copyparty-1.18.4/copyparty/web/browser.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty.egg-info/PKG-INFO +8 -2
- copyparty-1.18.2/copyparty/web/baguettebox.js.gz +0 -0
- copyparty-1.18.2/copyparty/web/browser.css.gz +0 -0
- copyparty-1.18.2/copyparty/web/browser.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/LICENSE +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/__init__.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/bos/__init__.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/bos/path.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/broker_mp.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/broker_mpw.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/broker_thr.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/broker_util.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/cert.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/dxml.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/fsutil.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/httpconn.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/httpsrv.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/ico.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/mdns.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/metrics.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/mtag.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/multicast.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/pwhash.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/res/COPYING.txt +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/res/__init__.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/res/insecure.pem +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/ssdp.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/star.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/__init__.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/dnslib/__init__.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/dnslib/bimap.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/dnslib/bit.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/dnslib/buffer.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/dnslib/dns.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/dnslib/label.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/dnslib/lex.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/dnslib/ranges.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/ifaddr/__init__.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/ifaddr/_posix.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/ifaddr/_shared.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/ifaddr/_win32.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/qrcodegen.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/stolen/surrogateescape.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/sutil.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/szip.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/th_cli.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/u2idx.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/a/__init__.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/a/partyfuse.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/a/u2c.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/a/webdav-cfg.bat +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/browser.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/browser2.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/cf.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/dbg-audio.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/dd/2.png +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/dd/3.png +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/dd/4.png +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/dd/5.png +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/dd/__init__.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/__init__.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/busy.mp3.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/easymde.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/easymde.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/fuse.py +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/marked.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/mini-fa.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/mini-fa.woff +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/prism.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/prism.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/prismd.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/scp.woff2 +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/sha512.ac.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/deps/sha512.hw.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/idp.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/md.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/md.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/md.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/md2.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/md2.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/mde.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/mde.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/mde.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/msg.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/msg.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/rups.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/rups.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/rups.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/shares.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/shares.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/shares.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/splash.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/splash.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/splash.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/svcs.html +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/svcs.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/ui.css.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/up2k.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/util.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty/web/w.hash.js.gz +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty.egg-info/SOURCES.txt +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty.egg-info/dependency_links.txt +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty.egg-info/entry_points.txt +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty.egg-info/requires.txt +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/copyparty.egg-info/top_level.txt +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/pyproject.toml +0 -0
- {copyparty-1.18.2 → copyparty-1.18.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.18.
|
3
|
+
Version: 1.18.4
|
4
4
|
Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
|
5
5
|
Author-email: ed <copyparty@ocv.me>
|
6
6
|
License: MIT
|
@@ -1663,7 +1663,7 @@ config file example:
|
|
1663
1663
|
w: * # anyone can upload here
|
1664
1664
|
rw: ed # only user "ed" can read-write
|
1665
1665
|
flags:
|
1666
|
-
e2ds
|
1666
|
+
e2ds # filesystem indexing is required for many of these:
|
1667
1667
|
sz: 1k-3m # accept upload only if filesize in this range
|
1668
1668
|
df: 4g # free disk space cannot go lower than this
|
1669
1669
|
vmaxb: 1g # volume can never exceed 1 GiB
|
@@ -1720,6 +1720,8 @@ this can instead be kept in a single place using the `--hist` argument, or the `
|
|
1720
1720
|
|
1721
1721
|
by default, the per-volume `up2k.db` sqlite3-database for `-e2d` and `-e2t` is stored next to the thumbnails according to the `--hist` option, but the global-option `--dbpath` and/or volflag `dbpath` can be used to put the database somewhere else
|
1722
1722
|
|
1723
|
+
if your storage backend is unreliable (NFS or bad HDDs), you can specify one or more "landmarks" to look for before doing anything database-related. A landmark is a file which is always expected to exist inside the volume. This avoids spurious filesystem rescans in the event of an outage. One line per landmark (see example below)
|
1724
|
+
|
1723
1725
|
note:
|
1724
1726
|
* putting the hist-folders on an SSD is strongly recommended for performance
|
1725
1727
|
* markdown edits are always stored in a local `.hist` subdirectory
|
@@ -1737,6 +1739,8 @@ config file example:
|
|
1737
1739
|
flags:
|
1738
1740
|
hist: - # restore the default (/mnt/nas/pics/.hist/)
|
1739
1741
|
hist: /mnt/nas/cache/pics/ # can be absolute path
|
1742
|
+
landmark: me.jpg # /mnt/nas/pics/me.jpg must be readable to enable db
|
1743
|
+
landmark: info/a.txt^=ok # and this textfile must start with "ok"
|
1740
1744
|
```
|
1741
1745
|
|
1742
1746
|
|
@@ -2422,8 +2426,10 @@ TLDR: yes
|
|
2422
2426
|
| send message | yep | yep | yep | yep | yep | yep | yep | yep |
|
2423
2427
|
| set sort order | - | yep | yep | yep | yep | yep | yep | yep |
|
2424
2428
|
| zip selection | - | yep | yep | yep | yep | yep | yep | yep |
|
2429
|
+
| file search | - | yep | yep | yep | yep | yep | yep | yep |
|
2425
2430
|
| file rename | - | yep | yep | yep | yep | yep | yep | yep |
|
2426
2431
|
| file cut/paste | - | yep | yep | yep | yep | yep | yep | yep |
|
2432
|
+
| unpost uploads | - | - | yep | yep | yep | yep | yep | yep |
|
2427
2433
|
| navpane | - | yep | yep | yep | yep | yep | yep | yep |
|
2428
2434
|
| image viewer | - | yep | yep | yep | yep | yep | yep | yep |
|
2429
2435
|
| video player | - | yep | yep | yep | yep | yep | yep | yep |
|
@@ -1605,7 +1605,7 @@ config file example:
|
|
1605
1605
|
w: * # anyone can upload here
|
1606
1606
|
rw: ed # only user "ed" can read-write
|
1607
1607
|
flags:
|
1608
|
-
e2ds
|
1608
|
+
e2ds # filesystem indexing is required for many of these:
|
1609
1609
|
sz: 1k-3m # accept upload only if filesize in this range
|
1610
1610
|
df: 4g # free disk space cannot go lower than this
|
1611
1611
|
vmaxb: 1g # volume can never exceed 1 GiB
|
@@ -1662,6 +1662,8 @@ this can instead be kept in a single place using the `--hist` argument, or the `
|
|
1662
1662
|
|
1663
1663
|
by default, the per-volume `up2k.db` sqlite3-database for `-e2d` and `-e2t` is stored next to the thumbnails according to the `--hist` option, but the global-option `--dbpath` and/or volflag `dbpath` can be used to put the database somewhere else
|
1664
1664
|
|
1665
|
+
if your storage backend is unreliable (NFS or bad HDDs), you can specify one or more "landmarks" to look for before doing anything database-related. A landmark is a file which is always expected to exist inside the volume. This avoids spurious filesystem rescans in the event of an outage. One line per landmark (see example below)
|
1666
|
+
|
1665
1667
|
note:
|
1666
1668
|
* putting the hist-folders on an SSD is strongly recommended for performance
|
1667
1669
|
* markdown edits are always stored in a local `.hist` subdirectory
|
@@ -1679,6 +1681,8 @@ config file example:
|
|
1679
1681
|
flags:
|
1680
1682
|
hist: - # restore the default (/mnt/nas/pics/.hist/)
|
1681
1683
|
hist: /mnt/nas/cache/pics/ # can be absolute path
|
1684
|
+
landmark: me.jpg # /mnt/nas/pics/me.jpg must be readable to enable db
|
1685
|
+
landmark: info/a.txt^=ok # and this textfile must start with "ok"
|
1682
1686
|
```
|
1683
1687
|
|
1684
1688
|
|
@@ -2364,8 +2368,10 @@ TLDR: yes
|
|
2364
2368
|
| send message | yep | yep | yep | yep | yep | yep | yep | yep |
|
2365
2369
|
| set sort order | - | yep | yep | yep | yep | yep | yep | yep |
|
2366
2370
|
| zip selection | - | yep | yep | yep | yep | yep | yep | yep |
|
2371
|
+
| file search | - | yep | yep | yep | yep | yep | yep | yep |
|
2367
2372
|
| file rename | - | yep | yep | yep | yep | yep | yep | yep |
|
2368
2373
|
| file cut/paste | - | yep | yep | yep | yep | yep | yep | yep |
|
2374
|
+
| unpost uploads | - | - | yep | yep | yep | yep | yep | yep |
|
2369
2375
|
| navpane | - | yep | yep | yep | yep | yep | yep | yep |
|
2370
2376
|
| image viewer | - | yep | yep | yep | yep | yep | yep | yep |
|
2371
2377
|
| video player | - | yep | yep | yep | yep | yep | yep | yep |
|
@@ -855,6 +855,43 @@ def get_sects():
|
|
855
855
|
"""
|
856
856
|
),
|
857
857
|
],
|
858
|
+
[
|
859
|
+
"chmod",
|
860
|
+
"file/folder permissions",
|
861
|
+
dedent(
|
862
|
+
"""
|
863
|
+
global-option \033[33m--chmod-f\033[0m and volflag \033[33mchmod_f\033[0m specifies the unix-permission to use when creating a new file
|
864
|
+
|
865
|
+
similarly, \033[33m--chmod-d\033[0m and \033[33mchmod_d\033[0m sets the directory/folder perm
|
866
|
+
|
867
|
+
the value is a three-digit octal number such as 755, 750, 644, etc.
|
868
|
+
|
869
|
+
first digit = "User"; permission for the unix-user
|
870
|
+
second digit = "Group"; permission for the unix-group
|
871
|
+
third digit = "Other"; permission for all other users/groups
|
872
|
+
|
873
|
+
for files:
|
874
|
+
0 = --- = no access
|
875
|
+
1 = --x = can execute the file as a program
|
876
|
+
2 = -w- = can write
|
877
|
+
3 = -wx = can write and execute
|
878
|
+
4 = r-- = can read
|
879
|
+
5 = r-x = can read and execute
|
880
|
+
6 = rw- = can read and write
|
881
|
+
7 = rwx = can read, write, execute
|
882
|
+
|
883
|
+
for directories/folders:
|
884
|
+
0 = --- = no access
|
885
|
+
1 = --x = can read files in folder but not list contents
|
886
|
+
2 = -w- = n/a
|
887
|
+
3 = -wx = can create files but not list
|
888
|
+
4 = r-- = can list, but not read/write
|
889
|
+
5 = r-x = can list and read files
|
890
|
+
6 = rw- = n/a
|
891
|
+
7 = rwx = can read, write, list
|
892
|
+
"""
|
893
|
+
),
|
894
|
+
],
|
858
895
|
[
|
859
896
|
"pwhash",
|
860
897
|
"password hashing",
|
@@ -1005,6 +1042,8 @@ def add_upload(ap):
|
|
1005
1042
|
ap2.add_argument("--reg-cap", metavar="N", type=int, default=38400, help="max number of uploads to keep in memory when running without \033[33m-e2d\033[0m; roughly 1 MiB RAM per 600")
|
1006
1043
|
ap2.add_argument("--no-fpool", action="store_true", help="disable file-handle pooling -- instead, repeatedly close and reopen files during upload (bad idea to enable this on windows and/or cow filesystems)")
|
1007
1044
|
ap2.add_argument("--use-fpool", action="store_true", help="force file-handle pooling, even when it might be dangerous (multiprocessing, filesystems lacking sparse-files support, ...)")
|
1045
|
+
ap2.add_argument("--chmod-f", metavar="UGO", type=u, default="", help="unix file permissions to use when creating files; default is probably 644 (OS-decided), see --help-chmod. Examples: [\033[32m644\033[0m] = owner-RW + all-R, [\033[32m755\033[0m] = owner-RWX + all-RX, [\033[32m777\033[0m] = full-yolo (volflag=chmod_f)")
|
1046
|
+
ap2.add_argument("--chmod-d", metavar="UGO", type=u, default="755", help="unix file permissions to use when creating directories; see --help-chmod. Examples: [\033[32m755\033[0m] = owner-RW + all-R, [\033[32m777\033[0m] = full-yolo (volflag=chmod_d)")
|
1008
1047
|
ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)")
|
1009
1048
|
ap2.add_argument("--safe-dedup", metavar="N", type=int, default=50, help="how careful to be when deduplicating files; [\033[32m1\033[0m] = just verify the filesize, [\033[32m50\033[0m] = verify file contents have not been altered (volflag=safededup)")
|
1010
1049
|
ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")
|
@@ -1041,7 +1080,7 @@ def add_network(ap):
|
|
1041
1080
|
ap2.add_argument("--rp-loc", metavar="PATH", type=u, default="", help="if reverse-proxying on a location instead of a dedicated domain/subdomain, provide the base location here; example: [\033[32m/foo/bar\033[0m]")
|
1042
1081
|
if ANYWIN:
|
1043
1082
|
ap2.add_argument("--reuseaddr", action="store_true", help="set reuseaddr on listening sockets on windows; allows rapid restart of copyparty at the expense of being able to accidentally start multiple instances")
|
1044
|
-
|
1083
|
+
elif not MACOS:
|
1045
1084
|
ap2.add_argument("--freebind", action="store_true", help="allow listening on IPs which do not yet exist, for example if the network interfaces haven't finished going up. Only makes sense for IPs other than '0.0.0.0', '127.0.0.1', '::', and '::1'. May require running as root (unless net.ipv6.ip_nonlocal_bind)")
|
1046
1085
|
ap2.add_argument("--wr-h-eps", metavar="PATH", type=u, default="", help="write list of listening-on ip:port to textfile at \033[33mPATH\033[0m when http-servers have started")
|
1047
1086
|
ap2.add_argument("--wr-h-aon", metavar="PATH", type=u, default="", help="write list of accessible-on ip:port to textfile at \033[33mPATH\033[0m when http-servers have started")
|
@@ -1503,6 +1542,7 @@ def add_ui(ap, retry):
|
|
1503
1542
|
ap2.add_argument("--nsort", action="store_true", help="default-enable natural sort of filenames with leading numbers (volflag=nsort)")
|
1504
1543
|
ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)")
|
1505
1544
|
ap2.add_argument("--see-dots", action="store_true", help="default-enable seeing dotfiles; only takes effect if user has the necessary permissions")
|
1545
|
+
ap2.add_argument("--qdel", metavar="LVL", type=int, default=2, help="number of confirmations to show when deleting files (2/1/0)")
|
1506
1546
|
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
|
1507
1547
|
ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
|
1508
1548
|
ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="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)")
|
@@ -113,6 +113,8 @@ class Lim(object):
|
|
113
113
|
|
114
114
|
self.reg = None # up2k registry
|
115
115
|
|
116
|
+
self.chmod_d = 0o755
|
117
|
+
|
116
118
|
self.nups = {} # num tracker
|
117
119
|
self.bups = {} # byte tracker list
|
118
120
|
self.bupc = {} # byte tracker cache
|
@@ -273,7 +275,7 @@ class Lim(object):
|
|
273
275
|
if not dirs:
|
274
276
|
# no branches yet; make one
|
275
277
|
sub = os.path.join(path, "0")
|
276
|
-
bos.mkdir(sub)
|
278
|
+
bos.mkdir(sub, self.chmod_d)
|
277
279
|
else:
|
278
280
|
# try newest branch only
|
279
281
|
sub = os.path.join(path, str(dirs[-1]))
|
@@ -288,7 +290,7 @@ class Lim(object):
|
|
288
290
|
|
289
291
|
# make a branch
|
290
292
|
sub = os.path.join(path, str(dirs[-1] + 1))
|
291
|
-
bos.mkdir(sub)
|
293
|
+
bos.mkdir(sub, self.chmod_d)
|
292
294
|
ret = self.dive(sub, lvs - 1)
|
293
295
|
if ret is None:
|
294
296
|
raise Pebkac(500, "rotation bug")
|
@@ -365,6 +367,7 @@ class VFS(object):
|
|
365
367
|
self.shr_src = None # source vfs+rem of a share
|
366
368
|
self.shr_files = set() # filenames to include from shr_src
|
367
369
|
self.shr_owner = "" # uname
|
370
|
+
self.shr_all_aps = []
|
368
371
|
self.aread = {}
|
369
372
|
self.awrite = {}
|
370
373
|
self.amove = {}
|
@@ -376,20 +379,20 @@ class VFS(object):
|
|
376
379
|
self.adot = {}
|
377
380
|
self.js_ls = {}
|
378
381
|
self.js_htm = ""
|
382
|
+
self.all_vols = {} # flattened recursive
|
383
|
+
self.all_nodes = {} # also jumpvols/shares
|
379
384
|
|
380
385
|
if realpath:
|
381
386
|
rp = realpath + ("" if realpath.endswith(os.sep) else os.sep)
|
382
387
|
vp = vpath + ("/" if vpath else "")
|
383
388
|
self.histpath = os.path.join(realpath, ".hist") # db / thumbcache
|
384
389
|
self.dbpath = self.histpath
|
385
|
-
self.all_vols =
|
386
|
-
self.all_nodes =
|
387
|
-
self.all_aps = [(rp, self)]
|
390
|
+
self.all_vols[vpath] = self
|
391
|
+
self.all_nodes[vpath] = self
|
392
|
+
self.all_aps = [(rp, [self])]
|
388
393
|
self.all_vps = [(vp, self)]
|
389
394
|
else:
|
390
395
|
self.histpath = self.dbpath = ""
|
391
|
-
self.all_vols = {}
|
392
|
-
self.all_nodes = {}
|
393
396
|
self.all_aps = []
|
394
397
|
self.all_vps = []
|
395
398
|
|
@@ -417,7 +420,11 @@ class VFS(object):
|
|
417
420
|
rp = self.realpath
|
418
421
|
rp += "" if rp.endswith(os.sep) else os.sep
|
419
422
|
vp = self.vpath + ("/" if self.vpath else "")
|
420
|
-
|
423
|
+
hit = next((x[1] for x in aps if x[0] == rp), None)
|
424
|
+
if hit:
|
425
|
+
hit.append(self)
|
426
|
+
else:
|
427
|
+
aps.append((rp, [self]))
|
421
428
|
vps.append((vp, self))
|
422
429
|
|
423
430
|
for v in self.nodes.values():
|
@@ -841,9 +848,11 @@ class VFS(object):
|
|
841
848
|
return None
|
842
849
|
|
843
850
|
if "xvol" in self.flags:
|
844
|
-
|
851
|
+
all_aps = self.shr_all_aps or self.root.all_aps
|
852
|
+
|
853
|
+
for vap, vns in all_aps:
|
845
854
|
if aps.startswith(vap):
|
846
|
-
return
|
855
|
+
return self if self in vns else vns[0]
|
847
856
|
|
848
857
|
if self.log:
|
849
858
|
self.log("vfs", "xvol: %r" % (ap,), 3)
|
@@ -852,6 +861,53 @@ class VFS(object):
|
|
852
861
|
|
853
862
|
return self
|
854
863
|
|
864
|
+
def check_landmarks(self) :
|
865
|
+
if self.dbv:
|
866
|
+
return True
|
867
|
+
|
868
|
+
vps = self.flags.get("landmark") or []
|
869
|
+
if not vps:
|
870
|
+
return True
|
871
|
+
|
872
|
+
failed = ""
|
873
|
+
for vp in vps:
|
874
|
+
if "^=" in vp:
|
875
|
+
vp, zs = vp.split("^=", 1)
|
876
|
+
expect = zs.encode("utf-8")
|
877
|
+
else:
|
878
|
+
expect = b""
|
879
|
+
|
880
|
+
if self.log:
|
881
|
+
t = "checking [/%s] landmark [%s]"
|
882
|
+
self.log("vfs", t % (self.vpath, vp), 6)
|
883
|
+
|
884
|
+
ap = "?"
|
885
|
+
try:
|
886
|
+
ap = self.canonical(vp)
|
887
|
+
with open(ap, "rb") as f:
|
888
|
+
buf = f.read(4096)
|
889
|
+
if not buf.startswith(expect):
|
890
|
+
t = "file [%s] does not start with the expected bytes %s"
|
891
|
+
failed = t % (ap, expect)
|
892
|
+
break
|
893
|
+
except Exception as ex:
|
894
|
+
t = "%r while trying to read [%s] => [%s]"
|
895
|
+
failed = t % (ex, vp, ap)
|
896
|
+
break
|
897
|
+
|
898
|
+
if not failed:
|
899
|
+
return True
|
900
|
+
|
901
|
+
if self.log:
|
902
|
+
t = "WARNING: landmark verification failed; %s; will now disable up2k database for volume [/%s]"
|
903
|
+
self.log("vfs", t % (failed, self.vpath), 3)
|
904
|
+
|
905
|
+
for rm in "e2d e2t e2v".split():
|
906
|
+
self.flags = {k: v for k, v in self.flags.items() if not k.startswith(rm)}
|
907
|
+
self.flags["d2d"] = True
|
908
|
+
self.flags["d2t"] = True
|
909
|
+
return False
|
910
|
+
|
855
911
|
|
856
912
|
if WINDOWS:
|
857
913
|
re_vol = re.compile(r"^([a-zA-Z]:[\\/][^:]*|[^:]*):([^:]*):(.*)$")
|
@@ -915,6 +971,9 @@ class AuthSrv(object):
|
|
915
971
|
|
916
972
|
yield prev, True
|
917
973
|
|
974
|
+
def vf0(self):
|
975
|
+
return {"d2d": True, "tcolor": self.args.tcolor}
|
976
|
+
|
918
977
|
def idp_checkin(
|
919
978
|
self, broker , uname , gname
|
920
979
|
) :
|
@@ -1481,7 +1540,7 @@ class AuthSrv(object):
|
|
1481
1540
|
flags[name] = True
|
1482
1541
|
return
|
1483
1542
|
|
1484
|
-
zs = "ext_th mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban"
|
1543
|
+
zs = "ext_th landmark mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban"
|
1485
1544
|
if name not in zs.split():
|
1486
1545
|
if value is True:
|
1487
1546
|
t = "└─add volflag [{}] = {} ({})"
|
@@ -1620,13 +1679,12 @@ class AuthSrv(object):
|
|
1620
1679
|
t = "Read-access has been disabled due to failsafe: No volumes were defined by the config-file. This failsafe is to prevent unintended access if this is due to accidental loss of config. You can override this safeguard and allow read/write to the working-directory by adding the following arguments: -v .::rw"
|
1621
1680
|
self.log(t, 1)
|
1622
1681
|
axs = AXS()
|
1623
|
-
vfs = VFS(self.log_func, absreal("."), "", "", axs,
|
1682
|
+
vfs = VFS(self.log_func, absreal("."), "", "", axs, self.vf0())
|
1624
1683
|
if not axs.uread:
|
1625
1684
|
self.badcfg1 = True
|
1626
1685
|
elif "" not in mount:
|
1627
1686
|
# there's volumes but no root; make root inaccessible
|
1628
|
-
|
1629
|
-
vfs = VFS(self.log_func, "", "", "", AXS(), zsd)
|
1687
|
+
vfs = VFS(self.log_func, "", "", "", AXS(), self.vf0())
|
1630
1688
|
|
1631
1689
|
maxdepth = 0
|
1632
1690
|
for dst in sorted(mount.keys(), key=lambda x: (x.count("/"), len(x))):
|
@@ -1674,8 +1732,7 @@ class AuthSrv(object):
|
|
1674
1732
|
shrs = enshare[1:]
|
1675
1733
|
if enshare:
|
1676
1734
|
|
1677
|
-
|
1678
|
-
shv = VFS(self.log_func, "", shr, shr, AXS(), zsd)
|
1735
|
+
shv = VFS(self.log_func, "", shr, shr, AXS(), self.vf0())
|
1679
1736
|
|
1680
1737
|
db_path = self.args.shr_db
|
1681
1738
|
db = sqlite3.connect(db_path)
|
@@ -2057,6 +2114,7 @@ class AuthSrv(object):
|
|
2057
2114
|
|
2058
2115
|
all_mte = {}
|
2059
2116
|
errors = False
|
2117
|
+
free_umask = False
|
2060
2118
|
for vol in vfs.all_nodes.values():
|
2061
2119
|
if (self.args.e2ds and vol.axs.uwrite) or self.args.e2dsa:
|
2062
2120
|
vol.flags["e2ds"] = True
|
@@ -2113,6 +2171,27 @@ class AuthSrv(object):
|
|
2113
2171
|
t = 'volume "/%s" has invalid %stry [%s]'
|
2114
2172
|
raise Exception(t % (vol.vpath, k, vol.flags.get(k + "try")))
|
2115
2173
|
|
2174
|
+
for k in ("chmod_d", "chmod_f"):
|
2175
|
+
is_d = k == "chmod_d"
|
2176
|
+
zs = vol.flags.get(k, "")
|
2177
|
+
if not zs and is_d:
|
2178
|
+
zs = "755"
|
2179
|
+
if not zs:
|
2180
|
+
vol.flags.pop(k, None)
|
2181
|
+
continue
|
2182
|
+
if not re.match("^[0-7]{3}$", zs):
|
2183
|
+
t = "config-option '%s' must be a three-digit octal value such as [755] or [644] but the value was [%s]"
|
2184
|
+
t = t % (k, zs)
|
2185
|
+
self.log(t, 1)
|
2186
|
+
raise Exception(t)
|
2187
|
+
zi = int(zs, 8)
|
2188
|
+
vol.flags[k] = zi
|
2189
|
+
if (is_d and zi != 0o755) or not is_d:
|
2190
|
+
free_umask = True
|
2191
|
+
|
2192
|
+
if vol.lim:
|
2193
|
+
vol.lim.chmod_d = vol.flags["chmod_d"]
|
2194
|
+
|
2116
2195
|
if vol.flags.get("og"):
|
2117
2196
|
self.args.uqe = True
|
2118
2197
|
|
@@ -2196,6 +2275,8 @@ class AuthSrv(object):
|
|
2196
2275
|
t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s"
|
2197
2276
|
self.log(t % (vol.vpath, etv), 3)
|
2198
2277
|
|
2278
|
+
vol.check_landmarks()
|
2279
|
+
|
2199
2280
|
# d2d drops all database features for a volume
|
2200
2281
|
for grp, rm in [["d2d", "e2d"], ["d2t", "e2t"], ["d2d", "e2v"]]:
|
2201
2282
|
if not vol.flags.get(grp, False):
|
@@ -2342,6 +2423,10 @@ class AuthSrv(object):
|
|
2342
2423
|
if errors:
|
2343
2424
|
sys.exit(1)
|
2344
2425
|
|
2426
|
+
setattr(self.args, "free_umask", free_umask)
|
2427
|
+
if free_umask:
|
2428
|
+
os.umask(0)
|
2429
|
+
|
2345
2430
|
vfs.bubble_flags()
|
2346
2431
|
|
2347
2432
|
have_e2d = False
|
@@ -2544,6 +2629,28 @@ class AuthSrv(object):
|
|
2544
2629
|
shn.shr_src = (s_vfs, s_rem)
|
2545
2630
|
shn.realpath = s_vfs.canonical(s_rem)
|
2546
2631
|
|
2632
|
+
# root.all_aps doesn't include any shares, so make a copy where the
|
2633
|
+
# share appears in all abspaths it can provide (for example for chk_ap)
|
2634
|
+
ap = shn.realpath
|
2635
|
+
if not ap.endswith(os.sep):
|
2636
|
+
ap += os.sep
|
2637
|
+
shn.shr_all_aps = [(x, y[:]) for x, y in vfs.all_aps]
|
2638
|
+
exact = False
|
2639
|
+
for ap2, vns in shn.shr_all_aps:
|
2640
|
+
if ap == ap2:
|
2641
|
+
exact = True
|
2642
|
+
if ap2.startswith(ap):
|
2643
|
+
try:
|
2644
|
+
vp2 = vjoin(s_rem, ap2[len(ap) :])
|
2645
|
+
vn2, _ = s_vfs.get(vp2, "*", False, False)
|
2646
|
+
if vn2 == s_vfs or vn2.dbv == s_vfs:
|
2647
|
+
vns.append(shn)
|
2648
|
+
except:
|
2649
|
+
pass
|
2650
|
+
if not exact:
|
2651
|
+
shn.shr_all_aps.append((ap, [shn]))
|
2652
|
+
shn.shr_all_aps.sort(key=lambda x: len(x[0]), reverse=True)
|
2653
|
+
|
2547
2654
|
if self.args.shr_v:
|
2548
2655
|
t = "mapped %s share [%s] by [%s] => [%s] => [%s]"
|
2549
2656
|
self.log(t % (s_pr, s_k, s_un, s_vp, shn.realpath))
|
@@ -2558,7 +2665,7 @@ class AuthSrv(object):
|
|
2558
2665
|
continue # also fine
|
2559
2666
|
for zs in svn.nodes.keys():
|
2560
2667
|
# hide subvolume
|
2561
|
-
vn.nodes[zs] = VFS(self.log_func, "", "", "", AXS(),
|
2668
|
+
vn.nodes[zs] = VFS(self.log_func, "", "", "", AXS(), self.vf0())
|
2562
2669
|
|
2563
2670
|
cur2.close()
|
2564
2671
|
cur.close()
|
@@ -2603,6 +2710,7 @@ class AuthSrv(object):
|
|
2603
2710
|
"def_hcols": list(vf.get("mth") or []),
|
2604
2711
|
"unlist0": vf.get("unlist") or "",
|
2605
2712
|
"see_dots": self.args.see_dots,
|
2713
|
+
"dqdel": self.args.qdel,
|
2606
2714
|
"dgrid": "grid" in vf,
|
2607
2715
|
"dgsel": "gsel" in vf,
|
2608
2716
|
"dnsort": "nsort" in vf,
|
@@ -22,14 +22,26 @@ def listdir(p = ".") :
|
|
22
22
|
|
23
23
|
|
24
24
|
def makedirs(name , mode = 0o755, exist_ok = True) :
|
25
|
+
# os.makedirs does 777 for all but leaf; this does mode on all
|
26
|
+
todo = []
|
25
27
|
bname = fsenc(name)
|
26
|
-
|
27
|
-
os.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
while bname:
|
29
|
+
if os.path.isdir(bname):
|
30
|
+
break
|
31
|
+
todo.append(bname)
|
32
|
+
bname = os.path.dirname(bname)
|
33
|
+
if not todo:
|
34
|
+
if not exist_ok:
|
35
|
+
os.mkdir(bname) # to throw
|
32
36
|
return False
|
37
|
+
for zb in todo[::-1]:
|
38
|
+
try:
|
39
|
+
os.mkdir(zb, mode)
|
40
|
+
except:
|
41
|
+
if os.path.isdir(zb):
|
42
|
+
continue
|
43
|
+
raise
|
44
|
+
return True
|
33
45
|
|
34
46
|
|
35
47
|
def mkdir(p , mode = 0o755) :
|
@@ -78,6 +78,8 @@ def vf_vmap() :
|
|
78
78
|
}
|
79
79
|
for k in (
|
80
80
|
"bup_ck",
|
81
|
+
"chmod_d",
|
82
|
+
"chmod_f",
|
81
83
|
"dbd",
|
82
84
|
"forget_ip",
|
83
85
|
"hsortn",
|
@@ -169,6 +171,8 @@ flagcats = {
|
|
169
171
|
"safededup": "verify on-disk data before using it for dedup",
|
170
172
|
"noclone": "take dupe data from clients, even if available on HDD",
|
171
173
|
"nodupe": "rejects existing files (instead of linking/cloning them)",
|
174
|
+
"chmod_d=755": "unix-permission for new dirs/folders",
|
175
|
+
"chmod_f=644": "unix-permission for new files",
|
172
176
|
"sparse": "force use of sparse files, mainly for s3-backed storage",
|
173
177
|
"nosparse": "deny use of sparse files, mainly for slow storage",
|
174
178
|
"daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
|
@@ -218,6 +222,7 @@ flagcats = {
|
|
218
222
|
"d2d": "disables all database stuff, overrides -e2*",
|
219
223
|
"hist=/tmp/cdb": "puts thumbnails and indexes at that location",
|
220
224
|
"dbpath=/tmp/cdb": "puts indexes at that location",
|
225
|
+
"landmark=foo": "disable db if file foo doesn't exist",
|
221
226
|
"scan=60": "scan for new files every 60sec, same as --re-maxage",
|
222
227
|
"nohash=\\.iso$": "skips hashing file contents if path matches *.iso",
|
223
228
|
"noidx=\\.iso$": "fully ignores the contents at paths matching *.iso",
|
@@ -225,7 +225,7 @@ class FtpFs(AbstractedFS):
|
|
225
225
|
r = "r" in mode
|
226
226
|
w = "w" in mode or "a" in mode or "+" in mode
|
227
227
|
|
228
|
-
ap = self.rv2a(filename, r, w)
|
228
|
+
ap, vfs, _ = self.rv2a(filename, r, w)
|
229
229
|
self.validpath(ap)
|
230
230
|
if w:
|
231
231
|
try:
|
@@ -257,7 +257,11 @@ class FtpFs(AbstractedFS):
|
|
257
257
|
|
258
258
|
wunlink(self.log, ap, VF_CAREFUL)
|
259
259
|
|
260
|
-
|
260
|
+
ret = open(fsenc(ap), mode, self.args.iobuf)
|
261
|
+
if w and "chmod_f" in vfs.flags:
|
262
|
+
os.fchmod(ret.fileno(), vfs.flags["chmod_f"])
|
263
|
+
|
264
|
+
return ret
|
261
265
|
|
262
266
|
def chdir(self, path ) :
|
263
267
|
nwd = join(self.cwd, path)
|
@@ -288,8 +292,9 @@ class FtpFs(AbstractedFS):
|
|
288
292
|
) = avfs.can_access("", self.h.uname)
|
289
293
|
|
290
294
|
def mkdir(self, path ) :
|
291
|
-
ap = self.rv2a(path, w=True)
|
292
|
-
|
295
|
+
ap, vfs, _ = self.rv2a(path, w=True)
|
296
|
+
chmod = vfs.flags["chmod_d"]
|
297
|
+
bos.makedirs(ap, chmod) # filezilla expects this
|
293
298
|
|
294
299
|
def listdir(self, path ) :
|
295
300
|
vpath = join(self.cwd, path)
|