copyparty 1.18.3__tar.gz → 1.18.5__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.
Files changed (125) hide show
  1. {copyparty-1.18.3 → copyparty-1.18.5}/PKG-INFO +6 -2
  2. {copyparty-1.18.3 → copyparty-1.18.5}/README.md +5 -1
  3. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/__main__.py +2 -0
  4. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/__version__.py +2 -2
  5. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/authsrv.py +55 -5
  6. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/cfg.py +1 -0
  7. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/httpcli.py +30 -14
  8. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/up2k.py +4 -0
  9. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/util.py +21 -4
  10. copyparty-1.18.5/copyparty/web/browser.js.gz +0 -0
  11. copyparty-1.18.5/copyparty/web/util.js.gz +0 -0
  12. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty.egg-info/PKG-INFO +6 -2
  13. copyparty-1.18.3/copyparty/web/browser.js.gz +0 -0
  14. copyparty-1.18.3/copyparty/web/util.js.gz +0 -0
  15. {copyparty-1.18.3 → copyparty-1.18.5}/LICENSE +0 -0
  16. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/__init__.py +0 -0
  17. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/bos/__init__.py +0 -0
  18. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/bos/bos.py +0 -0
  19. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/bos/path.py +0 -0
  20. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/broker_mp.py +0 -0
  21. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/broker_mpw.py +0 -0
  22. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/broker_thr.py +0 -0
  23. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/broker_util.py +0 -0
  24. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/cert.py +0 -0
  25. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/dxml.py +0 -0
  26. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/fsutil.py +0 -0
  27. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/ftpd.py +0 -0
  28. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/httpconn.py +0 -0
  29. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/httpsrv.py +0 -0
  30. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/ico.py +0 -0
  31. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/mdns.py +0 -0
  32. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/metrics.py +0 -0
  33. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/mtag.py +0 -0
  34. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/multicast.py +0 -0
  35. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/pwhash.py +0 -0
  36. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/res/COPYING.txt +0 -0
  37. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/res/__init__.py +0 -0
  38. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/res/insecure.pem +0 -0
  39. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/smbd.py +0 -0
  40. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/ssdp.py +0 -0
  41. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/star.py +0 -0
  42. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/__init__.py +0 -0
  43. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/dnslib/__init__.py +0 -0
  44. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/dnslib/bimap.py +0 -0
  45. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/dnslib/bit.py +0 -0
  46. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/dnslib/buffer.py +0 -0
  47. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/dnslib/dns.py +0 -0
  48. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/dnslib/label.py +0 -0
  49. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/dnslib/lex.py +0 -0
  50. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/dnslib/ranges.py +0 -0
  51. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/ifaddr/__init__.py +0 -0
  52. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/ifaddr/_posix.py +0 -0
  53. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/ifaddr/_shared.py +0 -0
  54. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/ifaddr/_win32.py +0 -0
  55. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/qrcodegen.py +0 -0
  56. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/stolen/surrogateescape.py +0 -0
  57. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/sutil.py +0 -0
  58. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/svchub.py +0 -0
  59. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/szip.py +0 -0
  60. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/tcpsrv.py +0 -0
  61. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/tftpd.py +0 -0
  62. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/th_cli.py +0 -0
  63. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/th_srv.py +0 -0
  64. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/u2idx.py +0 -0
  65. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/a/__init__.py +0 -0
  66. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/a/partyfuse.py +0 -0
  67. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/a/u2c.py +0 -0
  68. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/a/webdav-cfg.bat +0 -0
  69. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/baguettebox.js.gz +0 -0
  70. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/browser.css.gz +0 -0
  71. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/browser.html +0 -0
  72. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/browser2.html +0 -0
  73. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/cf.html +0 -0
  74. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/dbg-audio.js.gz +0 -0
  75. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/dd/2.png +0 -0
  76. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/dd/3.png +0 -0
  77. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/dd/4.png +0 -0
  78. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/dd/5.png +0 -0
  79. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/dd/__init__.py +0 -0
  80. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/__init__.py +0 -0
  81. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/busy.mp3.gz +0 -0
  82. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/easymde.css.gz +0 -0
  83. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/easymde.js.gz +0 -0
  84. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/fuse.py +0 -0
  85. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/marked.js.gz +0 -0
  86. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/mini-fa.css.gz +0 -0
  87. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/mini-fa.woff +0 -0
  88. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/prism.css.gz +0 -0
  89. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/prism.js.gz +0 -0
  90. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/prismd.css.gz +0 -0
  91. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/scp.woff2 +0 -0
  92. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  93. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  94. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/idp.html +0 -0
  95. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/md.css.gz +0 -0
  96. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/md.html +0 -0
  97. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/md.js.gz +0 -0
  98. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/md2.css.gz +0 -0
  99. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/md2.js.gz +0 -0
  100. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/mde.css.gz +0 -0
  101. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/mde.html +0 -0
  102. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/mde.js.gz +0 -0
  103. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/msg.css.gz +0 -0
  104. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/msg.html +0 -0
  105. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/rups.css.gz +0 -0
  106. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/rups.html +0 -0
  107. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/rups.js.gz +0 -0
  108. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/shares.css.gz +0 -0
  109. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/shares.html +0 -0
  110. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/shares.js.gz +0 -0
  111. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/splash.css.gz +0 -0
  112. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/splash.html +0 -0
  113. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/splash.js.gz +0 -0
  114. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/svcs.html +0 -0
  115. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/svcs.js.gz +0 -0
  116. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/ui.css.gz +0 -0
  117. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/up2k.js.gz +0 -0
  118. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty/web/w.hash.js.gz +0 -0
  119. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty.egg-info/SOURCES.txt +0 -0
  120. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty.egg-info/dependency_links.txt +0 -0
  121. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty.egg-info/entry_points.txt +0 -0
  122. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty.egg-info/requires.txt +0 -0
  123. {copyparty-1.18.3 → copyparty-1.18.5}/copyparty.egg-info/top_level.txt +0 -0
  124. {copyparty-1.18.3 → copyparty-1.18.5}/pyproject.toml +0 -0
  125. {copyparty-1.18.3 → copyparty-1.18.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copyparty
3
- Version: 1.18.3
3
+ Version: 1.18.5
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: # filesystem indexing is required for many of these:
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
 
@@ -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: # filesystem indexing is required for many of these:
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
 
@@ -1280,6 +1280,7 @@ def add_stats(ap):
1280
1280
  def add_yolo(ap):
1281
1281
  ap2 = ap.add_argument_group('yolo options')
1282
1282
  ap2.add_argument("--allow-csrf", action="store_true", help="disable csrf protections; let other domains/sites impersonate you through cross-site requests")
1283
+ ap2.add_argument("--cookie-lax", action="store_true", help="allow cookies from other domains (if you follow a link from another website into your server, you will arrive logged-in); this reduces protection against CSRF")
1283
1284
  ap2.add_argument("--getmod", action="store_true", help="permit ?move=[...] and ?delete as GET")
1284
1285
  ap2.add_argument("--wo-up-readme", action="store_true", help="allow users with write-only access to upload logues and readmes without adding the _wo_ filename prefix (volflag=wo_up_readme)")
1285
1286
 
@@ -1542,6 +1543,7 @@ def add_ui(ap, retry):
1542
1543
  ap2.add_argument("--nsort", action="store_true", help="default-enable natural sort of filenames with leading numbers (volflag=nsort)")
1543
1544
  ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)")
1544
1545
  ap2.add_argument("--see-dots", action="store_true", help="default-enable seeing dotfiles; only takes effect if user has the necessary permissions")
1546
+ ap2.add_argument("--qdel", metavar="LVL", type=int, default=2, help="number of confirmations to show when deleting files (2/1/0)")
1545
1547
  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)")
1546
1548
  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")
1547
1549
  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)")
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 18, 3)
3
+ VERSION = (1, 18, 5)
4
4
  CODENAME = "logtail"
5
- BUILD_DT = (2025, 7, 21)
5
+ BUILD_DT = (2025, 7, 28)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -379,20 +379,20 @@ class VFS(object):
379
379
  self.adot = {}
380
380
  self.js_ls = {}
381
381
  self.js_htm = ""
382
+ self.all_vols = {} # flattened recursive
383
+ self.all_nodes = {} # also jumpvols/shares
382
384
 
383
385
  if realpath:
384
386
  rp = realpath + ("" if realpath.endswith(os.sep) else os.sep)
385
387
  vp = vpath + ("/" if vpath else "")
386
388
  self.histpath = os.path.join(realpath, ".hist") # db / thumbcache
387
389
  self.dbpath = self.histpath
388
- self.all_vols = {vpath: self} # flattened recursive
389
- self.all_nodes = {vpath: self} # also jumpvols/shares
390
+ self.all_vols[vpath] = self
391
+ self.all_nodes[vpath] = self
390
392
  self.all_aps = [(rp, [self])]
391
393
  self.all_vps = [(vp, self)]
392
394
  else:
393
395
  self.histpath = self.dbpath = ""
394
- self.all_vols = {}
395
- self.all_nodes = {}
396
396
  self.all_aps = []
397
397
  self.all_vps = []
398
398
 
@@ -861,6 +861,53 @@ class VFS(object):
861
861
 
862
862
  return self
863
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
+
864
911
 
865
912
  if WINDOWS:
866
913
  re_vol = re.compile(r"^([a-zA-Z]:[\\/][^:]*|[^:]*):([^:]*):(.*)$")
@@ -1493,7 +1540,7 @@ class AuthSrv(object):
1493
1540
  flags[name] = True
1494
1541
  return
1495
1542
 
1496
- 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"
1497
1544
  if name not in zs.split():
1498
1545
  if value is True:
1499
1546
  t = "└─add volflag [{}] = {} ({})"
@@ -2228,6 +2275,8 @@ class AuthSrv(object):
2228
2275
  t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s"
2229
2276
  self.log(t % (vol.vpath, etv), 3)
2230
2277
 
2278
+ vol.check_landmarks()
2279
+
2231
2280
  # d2d drops all database features for a volume
2232
2281
  for grp, rm in [["d2d", "e2d"], ["d2t", "e2t"], ["d2d", "e2v"]]:
2233
2282
  if not vol.flags.get(grp, False):
@@ -2661,6 +2710,7 @@ class AuthSrv(object):
2661
2710
  "def_hcols": list(vf.get("mth") or []),
2662
2711
  "unlist0": vf.get("unlist") or "",
2663
2712
  "see_dots": self.args.see_dots,
2713
+ "dqdel": self.args.qdel,
2664
2714
  "dgrid": "grid" in vf,
2665
2715
  "dgsel": "gsel" in vf,
2666
2716
  "dnsort": "nsort" in vf,
@@ -222,6 +222,7 @@ flagcats = {
222
222
  "d2d": "disables all database stuff, overrides -e2*",
223
223
  "hist=/tmp/cdb": "puts thumbnails and indexes at that location",
224
224
  "dbpath=/tmp/cdb": "puts indexes at that location",
225
+ "landmark=foo": "disable db if file foo doesn't exist",
225
226
  "scan=60": "scan for new files every 60sec, same as --re-maxage",
226
227
  "nohash=\\.iso$": "skips hashing file contents if path matches *.iso",
227
228
  "noidx=\\.iso$": "fully ignores the contents at paths matching *.iso",
@@ -2981,12 +2981,20 @@ class HttpCli(object):
2981
2981
  # reset both plaintext and tls
2982
2982
  # (only affects active tls cookies when tls)
2983
2983
  for k in ("cppwd", "cppws") if self.is_https else ("cppwd",):
2984
- ck = gencookie(k, pwd, self.args.R, False)
2984
+ ck = gencookie(k, pwd, self.args.R, self.args.cookie_lax, False)
2985
2985
  self.out_headerlist.append(("Set-Cookie", ck))
2986
2986
  self.out_headers.pop("Set-Cookie", None) # drop keepalive
2987
2987
  else:
2988
2988
  k = "cppws" if self.is_https else "cppwd"
2989
- ck = gencookie(k, pwd, self.args.R, self.is_https, dur, "; HttpOnly")
2989
+ ck = gencookie(
2990
+ k,
2991
+ pwd,
2992
+ self.args.R,
2993
+ self.args.cookie_lax,
2994
+ self.is_https,
2995
+ dur,
2996
+ "; HttpOnly",
2997
+ )
2990
2998
  self.out_headers["Set-Cookie"] = ck
2991
2999
 
2992
3000
  return dur > 0, msg
@@ -4843,13 +4851,21 @@ class HttpCli(object):
4843
4851
  def tx_svcs(self) :
4844
4852
  aname = re.sub("[^0-9a-zA-Z]+", "", self.args.vname) or "a"
4845
4853
  ep = self.host
4846
- host = ep.split(":")[0]
4847
- hport = ep[ep.find(":") :] if ":" in ep else ""
4848
- rip = (
4849
- host
4850
- if self.args.rclone_mdns or not self.args.zm
4851
- else self.conn.hsrv.nm.map(self.ip) or host
4852
- )
4854
+ sep = "]:" if "]" in ep else ":"
4855
+ if sep in ep:
4856
+ host, hport = ep.rsplit(":", 1)
4857
+ hport = ":" + hport
4858
+ else:
4859
+ host = ep
4860
+ hport = ""
4861
+
4862
+ if host.endswith(".local") and self.args.zm and not self.args.rclone_mdns:
4863
+ rip = self.conn.hsrv.nm.map(self.ip) or host
4864
+ if ":" in rip and "[" not in rip:
4865
+ rip = "[%s]" % (rip,)
4866
+ else:
4867
+ rip = host
4868
+
4853
4869
  # safer than html_escape/quotep since this avoids both XSS and shell-stuff
4854
4870
  pw = re.sub(r"[<>&$?`\"']", "_", self.pw or "hunter2")
4855
4871
  vp = re.sub(r"[<>&$?`\"']", "_", self.uparam["hc"] or "").lstrip("/")
@@ -5018,7 +5034,7 @@ class HttpCli(object):
5018
5034
  def setck(self) :
5019
5035
  k, v = self.uparam["setck"].split("=", 1)
5020
5036
  t = 0 if v in ("", "x") else 86400 * 299
5021
- ck = gencookie(k, v, self.args.R, False, t)
5037
+ ck = gencookie(k, v, self.args.R, self.args.cookie_lax, False, t)
5022
5038
  self.out_headerlist.append(("Set-Cookie", ck))
5023
5039
  if "cc" in self.ouparam:
5024
5040
  self.redirect("", "?h#cc")
@@ -5030,7 +5046,7 @@ class HttpCli(object):
5030
5046
  for k in ALL_COOKIES:
5031
5047
  if k not in self.cookies:
5032
5048
  continue
5033
- cookie = gencookie(k, "x", self.args.R, False)
5049
+ cookie = gencookie(k, "x", self.args.R, self.args.cookie_lax, False)
5034
5050
  self.out_headerlist.append(("Set-Cookie", cookie))
5035
5051
 
5036
5052
  self.redirect("", "?h#cc")
@@ -5549,7 +5565,7 @@ class HttpCli(object):
5549
5565
  db.commit()
5550
5566
  db.close()
5551
5567
 
5552
- self.conn.hsrv.broker.ask("reload", False, False).get()
5568
+ self.conn.hsrv.broker.ask("reload", False, True).get()
5553
5569
 
5554
5570
  self.redirect("", "?idp")
5555
5571
  return True
@@ -5633,7 +5649,7 @@ class HttpCli(object):
5633
5649
 
5634
5650
  cur.connection.commit()
5635
5651
  if reload:
5636
- self.conn.hsrv.broker.ask("reload", False, False).get()
5652
+ self.conn.hsrv.broker.ask("reload", False, True).get()
5637
5653
  self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
5638
5654
 
5639
5655
  self.redirect("", "?shares")
@@ -5725,7 +5741,7 @@ class HttpCli(object):
5725
5741
  cur.execute(q, (skey, fn))
5726
5742
 
5727
5743
  cur.connection.commit()
5728
- self.conn.hsrv.broker.ask("reload", False, False).get()
5744
+ self.conn.hsrv.broker.ask("reload", False, True).get()
5729
5745
  self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
5730
5746
 
5731
5747
  fn = quotep(fns[0]) if len(fns) == 1 else ""
@@ -1371,6 +1371,10 @@ class Up2k(object):
1371
1371
  t = "volume /%s at [%s] is empty; will not be indexed as this could be due to an offline filesystem"
1372
1372
  self.log(t % (vol.vpath, rtop), 6)
1373
1373
  return True, False
1374
+ if not vol.check_landmarks():
1375
+ t = "volume /%s at [%s] will not be indexed due to bad landmarks"
1376
+ self.log(t % (vol.vpath, rtop), 6)
1377
+ return True, False
1374
1378
 
1375
1379
  n_add, _, _ = self._build_dir(
1376
1380
  db,
@@ -1889,7 +1889,7 @@ def rand_name(fdir , fn , rnd ) :
1889
1889
  return fn
1890
1890
 
1891
1891
 
1892
- def gen_filekey(alg , salt , fspath , fsize , inode ) :
1892
+ def _gen_filekey(alg , salt , fspath , fsize , inode ) :
1893
1893
  if alg == 1:
1894
1894
  zs = "%s %s %s %s" % (salt, fspath, fsize, inode)
1895
1895
  else:
@@ -1899,6 +1899,13 @@ def gen_filekey(alg , salt , fspath , fsize , inode ) :
1899
1899
  return ub64enc(hashlib.sha512(zb).digest()).decode("ascii")
1900
1900
 
1901
1901
 
1902
+ def _gen_filekey_w(alg , salt , fspath , fsize , inode ) :
1903
+ return _gen_filekey(alg, salt, fspath.replace("/", "\\"), fsize, inode)
1904
+
1905
+
1906
+ gen_filekey = _gen_filekey_w if ANYWIN else _gen_filekey
1907
+
1908
+
1902
1909
  def gen_filekey_dbg(
1903
1910
  alg ,
1904
1911
  salt ,
@@ -1944,15 +1951,25 @@ def formatdate(ts = None) :
1944
1951
  return RFC2822 % (WKDAYS[wd], d, MONTHS[mo - 1], y, h, mi, s)
1945
1952
 
1946
1953
 
1947
- def gencookie(k , v , r , tls , dur = 0, txt = "") :
1954
+ def gencookie(
1955
+ k , v , r , lax , tls , dur = 0, txt = ""
1956
+ ) :
1948
1957
  v = v.replace("%", "%25").replace(";", "%3B")
1949
1958
  if dur:
1950
1959
  exp = formatdate(time.time() + dur)
1951
1960
  else:
1952
1961
  exp = "Fri, 15 Aug 1997 01:00:00 GMT"
1953
1962
 
1954
- t = "%s=%s; Path=/%s; Expires=%s%s%s; SameSite=Lax"
1955
- return t % (k, v, r, exp, "; Secure" if tls else "", txt)
1963
+ t = "%s=%s; Path=/%s; Expires=%s%s%s; SameSite=%s"
1964
+ return t % (
1965
+ k,
1966
+ v,
1967
+ r,
1968
+ exp,
1969
+ "; Secure" if tls else "",
1970
+ txt,
1971
+ "Lax" if lax else "Strict",
1972
+ )
1956
1973
 
1957
1974
 
1958
1975
  def humansize(sz , terse = False) :
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copyparty
3
- Version: 1.18.3
3
+ Version: 1.18.5
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: # filesystem indexing is required for many of these:
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
 
Binary file
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes