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:
         
     |