copyparty 1.16.7__py3-none-any.whl → 1.16.9__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/__main__.py +15 -8
 - copyparty/__version__.py +2 -2
 - copyparty/authsrv.py +8 -1
 - copyparty/cfg.py +5 -0
 - copyparty/dxml.py +48 -3
 - copyparty/httpcli.py +62 -30
 - copyparty/svchub.py +13 -0
 - copyparty/up2k.py +32 -12
 - copyparty/util.py +154 -8
 - copyparty/web/a/u2c.py +3 -3
 - copyparty/web/browser.js.gz +0 -0
 - copyparty/web/splash.css.gz +0 -0
 - copyparty/web/svcs.html +64 -3
 - copyparty/web/svcs.js.gz +0 -0
 - copyparty/web/ui.css.gz +0 -0
 - copyparty/web/up2k.js.gz +0 -0
 - copyparty/web/util.js.gz +0 -0
 - {copyparty-1.16.7.dist-info → copyparty-1.16.9.dist-info}/METADATA +84 -16
 - {copyparty-1.16.7.dist-info → copyparty-1.16.9.dist-info}/RECORD +23 -23
 - {copyparty-1.16.7.dist-info → copyparty-1.16.9.dist-info}/WHEEL +1 -1
 - {copyparty-1.16.7.dist-info → copyparty-1.16.9.dist-info}/LICENSE +0 -0
 - {copyparty-1.16.7.dist-info → copyparty-1.16.9.dist-info}/entry_points.txt +0 -0
 - {copyparty-1.16.7.dist-info → copyparty-1.16.9.dist-info}/top_level.txt +0 -0
 
    
        copyparty/__main__.py
    CHANGED
    
    | 
         @@ -54,6 +54,8 @@ from .util import ( 
     | 
|
| 
       54 
54 
     | 
    
         
             
                RAM_TOTAL,
         
     | 
| 
       55 
55 
     | 
    
         
             
                SQLITE_VER,
         
     | 
| 
       56 
56 
     | 
    
         
             
                UNPLICATIONS,
         
     | 
| 
      
 57 
     | 
    
         
            +
                URL_BUG,
         
     | 
| 
      
 58 
     | 
    
         
            +
                URL_PRJ,
         
     | 
| 
       57 
59 
     | 
    
         
             
                Daemon,
         
     | 
| 
       58 
60 
     | 
    
         
             
                align_tab,
         
     | 
| 
       59 
61 
     | 
    
         
             
                ansi_re,
         
     | 
| 
         @@ -326,17 +328,16 @@ def ensure_webdeps()  : 
     | 
|
| 
       326 
328 
     | 
    
         
             
                if has_resource(E, "web/deps/mini-fa.woff"):
         
     | 
| 
       327 
329 
     | 
    
         
             
                    return
         
     | 
| 
       328 
330 
     | 
    
         | 
| 
       329 
     | 
    
         
            -
                 
     | 
| 
       330 
     | 
    
         
            -
                    """could not find webdeps;
         
     | 
| 
      
 331 
     | 
    
         
            +
                t = """could not find webdeps;
         
     | 
| 
       331 
332 
     | 
    
         
             
              if you are running the sfx, or exe, or pypi package, or docker image,
         
     | 
| 
       332 
333 
     | 
    
         
             
              then this is a bug! Please let me know so I can fix it, thanks :-)
         
     | 
| 
       333 
     | 
    
         
            -
               
     | 
| 
      
 334 
     | 
    
         
            +
              %s
         
     | 
| 
       334 
335 
     | 
    
         | 
| 
       335 
336 
     | 
    
         
             
              however, if you are a dev, or running copyparty from source, and you want
         
     | 
| 
       336 
337 
     | 
    
         
             
              full client functionality, you will need to build or obtain the webdeps:
         
     | 
| 
       337 
     | 
    
         
            -
               
     | 
| 
      
 338 
     | 
    
         
            +
              %s/blob/hovudstraum/docs/devnotes.md#building
         
     | 
| 
       338 
339 
     | 
    
         
             
                """
         
     | 
| 
       339 
     | 
    
         
            -
                )
         
     | 
| 
      
 340 
     | 
    
         
            +
                warn(t % (URL_BUG, URL_PRJ))
         
     | 
| 
       340 
341 
     | 
    
         | 
| 
       341 
342 
     | 
    
         | 
| 
       342 
343 
     | 
    
         
             
            def configure_ssl_ver(al )  :
         
     | 
| 
         @@ -731,6 +732,10 @@ def get_sects(): 
     | 
|
| 
       731 
732 
     | 
    
         
             
                          the \033[33m,,\033[35m stops copyparty from reading the rest as flags and
         
     | 
| 
       732 
733 
     | 
    
         
             
                          the \033[33m--\033[35m stops notify-send from reading the message as args
         
     | 
| 
       733 
734 
     | 
    
         
             
                          and the alert will be "hey" followed by the messagetext
         
     | 
| 
      
 735 
     | 
    
         
            +
             
     | 
| 
      
 736 
     | 
    
         
            +
                         \033[36m--xau zmq:pub:tcp://*:5556\033[35m announces uploads on zeromq;
         
     | 
| 
      
 737 
     | 
    
         
            +
                         \033[36m--xau t3,zmq:push:tcp://*:5557\033[35m also works, and you can
         
     | 
| 
      
 738 
     | 
    
         
            +
                         \033[36m--xau t3,j,zmq:req:tcp://localhost:5555\033[35m too for example
         
     | 
| 
       734 
739 
     | 
    
         
             
                        \033[0m
         
     | 
| 
       735 
740 
     | 
    
         
             
                        each hook is executed once for each event, except for \033[36mxiu\033[0m
         
     | 
| 
       736 
741 
     | 
    
         
             
                        which builds up a backlog of uploads, running the hook just once
         
     | 
| 
         @@ -1468,12 +1473,14 @@ def add_ui(ap, retry): 
     | 
|
| 
       1468 
1473 
     | 
    
         
             
                ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
         
     | 
| 
       1469 
1474 
     | 
    
         
             
                ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty @ --name", help="title / service-name to show in html documents")
         
     | 
| 
       1470 
1475 
     | 
    
         
             
                ap2.add_argument("--bname", metavar="TXT", type=u, default="--name", help="server name (displayed in filebrowser document title)")
         
     | 
| 
       1471 
     | 
    
         
            -
                ap2.add_argument("--pb-url", metavar="URL", type=u, default= 
     | 
| 
      
 1476 
     | 
    
         
            +
                ap2.add_argument("--pb-url", metavar="URL", type=u, default=URL_PRJ, help="powered-by link; disable with \033[33m-np\033[0m")
         
     | 
| 
       1472 
1477 
     | 
    
         
             
                ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m)")
         
     | 
| 
       1473 
1478 
     | 
    
         
             
                ap2.add_argument("--k304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable k304 on the controlpanel (workaround for buggy reverse-proxies); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
         
     | 
| 
       1474 
1479 
     | 
    
         
             
                ap2.add_argument("--no304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable no304 on the controlpanel (workaround for buggy caching in browsers); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
         
     | 
| 
       1475 
     | 
    
         
            -
                ap2.add_argument("--md-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to  
     | 
| 
       1476 
     | 
    
         
            -
                ap2.add_argument("--lg-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to  
     | 
| 
      
 1480 
     | 
    
         
            +
                ap2.add_argument("--md-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to allow in the iframe 'sandbox' attribute for README.md docs (volflag=md_sbf); see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox")
         
     | 
| 
      
 1481 
     | 
    
         
            +
                ap2.add_argument("--lg-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to allow in the iframe 'sandbox' attribute for prologue/epilogue docs (volflag=lg_sbf)")
         
     | 
| 
      
 1482 
     | 
    
         
            +
                ap2.add_argument("--md-sba", metavar="TXT", type=u, default="", help="the value of the iframe 'allow' attribute for README.md docs, for example [\033[32mfullscreen\033[0m] (volflag=md_sba)")
         
     | 
| 
      
 1483 
     | 
    
         
            +
                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")
         
     | 
| 
       1477 
1484 
     | 
    
         
             
                ap2.add_argument("--no-sb-md", action="store_true", help="don't sandbox README/PREADME.md documents (volflags: no_sb_md | sb_md)")
         
     | 
| 
       1478 
1485 
     | 
    
         
             
                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")
         
     | 
| 
       1479 
1486 
     | 
    
         | 
    
        copyparty/__version__.py
    CHANGED
    
    
    
        copyparty/authsrv.py
    CHANGED
    
    | 
         @@ -1825,7 +1825,11 @@ class AuthSrv(object): 
     | 
|
| 
       1825 
1825 
     | 
    
         
             
                        if fka and not fk:
         
     | 
| 
       1826 
1826 
     | 
    
         
             
                            fk = fka
         
     | 
| 
       1827 
1827 
     | 
    
         
             
                        if fk:
         
     | 
| 
       1828 
     | 
    
         
            -
                             
     | 
| 
      
 1828 
     | 
    
         
            +
                            fk = 8 if fk is True else int(fk)
         
     | 
| 
      
 1829 
     | 
    
         
            +
                            if fk > 72:
         
     | 
| 
      
 1830 
     | 
    
         
            +
                                t = "max filekey-length is 72; volume /%s specified %d (anything higher than 16 is pointless btw)"
         
     | 
| 
      
 1831 
     | 
    
         
            +
                                raise Exception(t % (vol.vpath, fk))
         
     | 
| 
      
 1832 
     | 
    
         
            +
                            vol.flags["fk"] = fk
         
     | 
| 
       1829 
1833 
     | 
    
         
             
                            have_fk = True
         
     | 
| 
       1830 
1834 
     | 
    
         | 
| 
       1831 
1835 
     | 
    
         
             
                        dk = vol.flags.get("dk")
         
     | 
| 
         @@ -2332,6 +2336,7 @@ class AuthSrv(object): 
     | 
|
| 
       2332 
2336 
     | 
    
         
             
                            "frand": bool(vf.get("rand")),
         
     | 
| 
       2333 
2337 
     | 
    
         
             
                            "lifetime": vf.get("lifetime") or 0,
         
     | 
| 
       2334 
2338 
     | 
    
         
             
                            "unlist": vf.get("unlist") or "",
         
     | 
| 
      
 2339 
     | 
    
         
            +
                            "sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
         
     | 
| 
       2335 
2340 
     | 
    
         
             
                        }
         
     | 
| 
       2336 
2341 
     | 
    
         
             
                        js_htm = {
         
     | 
| 
       2337 
2342 
     | 
    
         
             
                            "s_name": self.args.bname,
         
     | 
| 
         @@ -2344,6 +2349,8 @@ class AuthSrv(object): 
     | 
|
| 
       2344 
2349 
     | 
    
         
             
                            "have_unpost": int(self.args.unpost),
         
     | 
| 
       2345 
2350 
     | 
    
         
             
                            "have_emp": self.args.emp,
         
     | 
| 
       2346 
2351 
     | 
    
         
             
                            "sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
         
     | 
| 
      
 2352 
     | 
    
         
            +
                            "sba_md": vf.get("md_sba") or "",
         
     | 
| 
      
 2353 
     | 
    
         
            +
                            "sba_lg": vf.get("lg_sba") or "",
         
     | 
| 
       2347 
2354 
     | 
    
         
             
                            "txt_ext": self.args.textfiles.replace(",", " "),
         
     | 
| 
       2348 
2355 
     | 
    
         
             
                            "def_hcols": list(vf.get("mth") or []),
         
     | 
| 
       2349 
2356 
     | 
    
         
             
                            "unlist0": vf.get("unlist") or "",
         
     | 
    
        copyparty/cfg.py
    CHANGED
    
    | 
         @@ -74,6 +74,8 @@ def vf_vmap()   : 
     | 
|
| 
       74 
74 
     | 
    
         
             
                    "html_head",
         
     | 
| 
       75 
75 
     | 
    
         
             
                    "lg_sbf",
         
     | 
| 
       76 
76 
     | 
    
         
             
                    "md_sbf",
         
     | 
| 
      
 77 
     | 
    
         
            +
                    "lg_sba",
         
     | 
| 
      
 78 
     | 
    
         
            +
                    "md_sba",
         
     | 
| 
       77 
79 
     | 
    
         
             
                    "nrand",
         
     | 
| 
       78 
80 
     | 
    
         
             
                    "og_desc",
         
     | 
| 
       79 
81 
     | 
    
         
             
                    "og_site",
         
     | 
| 
         @@ -144,6 +146,7 @@ flagcats = { 
     | 
|
| 
       144 
146 
     | 
    
         
             
                    "noclone": "take dupe data from clients, even if available on HDD",
         
     | 
| 
       145 
147 
     | 
    
         
             
                    "nodupe": "rejects existing files (instead of linking/cloning them)",
         
     | 
| 
       146 
148 
     | 
    
         
             
                    "sparse": "force use of sparse files, mainly for s3-backed storage",
         
     | 
| 
      
 149 
     | 
    
         
            +
                    "nosparse": "deny use of sparse files, mainly for slow storage",
         
     | 
| 
       147 
150 
     | 
    
         
             
                    "daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
         
     | 
| 
       148 
151 
     | 
    
         
             
                    "nosub": "forces all uploads into the top folder of the vfs",
         
     | 
| 
       149 
152 
     | 
    
         
             
                    "magic": "enables filetype detection for nameless uploads",
         
     | 
| 
         @@ -240,6 +243,8 @@ flagcats = { 
     | 
|
| 
       240 
243 
     | 
    
         
             
                    "sb_lg": "enable js sandbox for prologue/epilogue (default)",
         
     | 
| 
       241 
244 
     | 
    
         
             
                    "md_sbf": "list of markdown-sandbox safeguards to disable",
         
     | 
| 
       242 
245 
     | 
    
         
             
                    "lg_sbf": "list of *logue-sandbox safeguards to disable",
         
     | 
| 
      
 246 
     | 
    
         
            +
                    "md_sba": "value of iframe allow-prop for markdown-sandbox",
         
     | 
| 
      
 247 
     | 
    
         
            +
                    "lg_sba": "value of iframe allow-prop for *logue-sandbox",
         
     | 
| 
       243 
248 
     | 
    
         
             
                    "nohtml": "return html and markdown as text/html",
         
     | 
| 
       244 
249 
     | 
    
         
             
                },
         
     | 
| 
       245 
250 
     | 
    
         
             
                "others": {
         
     | 
    
        copyparty/dxml.py
    CHANGED
    
    | 
         @@ -1,9 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # coding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            from __future__ import print_function, unicode_literals
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
       1 
4 
     | 
    
         
             
            import importlib
         
     | 
| 
       2 
5 
     | 
    
         
             
            import sys
         
     | 
| 
       3 
6 
     | 
    
         
             
            import xml.etree.ElementTree as ET
         
     | 
| 
       4 
7 
     | 
    
         | 
| 
       5 
8 
     | 
    
         
             
            from .__init__ import PY2
         
     | 
| 
       6 
9 
     | 
    
         | 
| 
      
 10 
     | 
    
         
            +
            class BadXML(Exception):
         
     | 
| 
      
 11 
     | 
    
         
            +
                pass
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
       7 
14 
     | 
    
         
             
            def get_ET()  :
         
     | 
| 
       8 
15 
     | 
    
         
             
                pn = "xml.etree.ElementTree"
         
     | 
| 
       9 
16 
     | 
    
         
             
                cn = "_elementtree"
         
     | 
| 
         @@ -30,7 +37,7 @@ def get_ET()  : 
     | 
|
| 
       30 
37 
     | 
    
         
             
            XMLParser  = get_ET()
         
     | 
| 
       31 
38 
     | 
    
         | 
| 
       32 
39 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
            class  
     | 
| 
      
 40 
     | 
    
         
            +
            class _DXMLParser(XMLParser):  # type: ignore
         
     | 
| 
       34 
41 
     | 
    
         
             
                def __init__(self)  :
         
     | 
| 
       35 
42 
     | 
    
         
             
                    tb = ET.TreeBuilder()
         
     | 
| 
       36 
43 
     | 
    
         
             
                    super(DXMLParser, self).__init__(target=tb)
         
     | 
| 
         @@ -45,8 +52,12 @@ class DXMLParser(XMLParser):  # type: ignore 
     | 
|
| 
       45 
52 
     | 
    
         
             
                    raise BadXML("{}, {}".format(a, ka))
         
     | 
| 
       46 
53 
     | 
    
         | 
| 
       47 
54 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
            class  
     | 
| 
       49 
     | 
    
         
            -
                 
     | 
| 
      
 55 
     | 
    
         
            +
            class _NG(XMLParser):  # type: ignore
         
     | 
| 
      
 56 
     | 
    
         
            +
                def __int__(self)  :
         
     | 
| 
      
 57 
     | 
    
         
            +
                    raise BadXML("dxml selftest failed")
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            DXMLParser = _DXMLParser
         
     | 
| 
       50 
61 
     | 
    
         | 
| 
       51 
62 
     | 
    
         | 
| 
       52 
63 
     | 
    
         
             
            def parse_xml(txt )  :
         
     | 
| 
         @@ -55,6 +66,40 @@ def parse_xml(txt )  : 
     | 
|
| 
       55 
66 
     | 
    
         
             
                return parser.close()  # type: ignore
         
     | 
| 
       56 
67 
     | 
    
         | 
| 
       57 
68 
     | 
    
         | 
| 
      
 69 
     | 
    
         
            +
            def selftest()  :
         
     | 
| 
      
 70 
     | 
    
         
            +
                qbe = r"""<!DOCTYPE d [
         
     | 
| 
      
 71 
     | 
    
         
            +
            <!ENTITY a "nice_bakuretsu">
         
     | 
| 
      
 72 
     | 
    
         
            +
            ]>
         
     | 
| 
      
 73 
     | 
    
         
            +
            <root>&a;&a;&a;</root>"""
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                emb = r"""<!DOCTYPE d [
         
     | 
| 
      
 76 
     | 
    
         
            +
            <!ENTITY a SYSTEM "file:///etc/hostname">
         
     | 
| 
      
 77 
     | 
    
         
            +
            ]>
         
     | 
| 
      
 78 
     | 
    
         
            +
            <root>&a;</root>"""
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                # future-proofing; there's never been any known vulns
         
     | 
| 
      
 81 
     | 
    
         
            +
                # regarding DTDs and ET.XMLParser, but might as well
         
     | 
| 
      
 82 
     | 
    
         
            +
                # block them since webdav-clients don't use them
         
     | 
| 
      
 83 
     | 
    
         
            +
                dtd = r"""<!DOCTYPE d SYSTEM "a.dtd">
         
     | 
| 
      
 84 
     | 
    
         
            +
            <root>a</root>"""
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                for txt in (qbe, emb, dtd):
         
     | 
| 
      
 87 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 88 
     | 
    
         
            +
                        parse_xml(txt)
         
     | 
| 
      
 89 
     | 
    
         
            +
                        t = "WARNING: dxml selftest failed:\n%s\n"
         
     | 
| 
      
 90 
     | 
    
         
            +
                        print(t % (txt,), file=sys.stderr)
         
     | 
| 
      
 91 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 92 
     | 
    
         
            +
                    except BadXML:
         
     | 
| 
      
 93 
     | 
    
         
            +
                        pass
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                return True
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
            DXML_OK = selftest()
         
     | 
| 
      
 99 
     | 
    
         
            +
            if not DXML_OK:
         
     | 
| 
      
 100 
     | 
    
         
            +
                DXMLParser = _NG
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
       58 
103 
     | 
    
         
             
            def mktnod(name , text )  :
         
     | 
| 
       59 
104 
     | 
    
         
             
                el = ET.Element(name)
         
     | 
| 
       60 
105 
     | 
    
         
             
                el.text = text
         
     | 
    
        copyparty/httpcli.py
    CHANGED
    
    | 
         @@ -128,6 +128,8 @@ NO_CACHE = {"Cache-Control": "no-cache"} 
     | 
|
| 
       128 
128 
     | 
    
         | 
| 
       129 
129 
     | 
    
         
             
            ALL_COOKIES = "k304 no304 js idxh dots cppwd cppws".split()
         
     | 
| 
       130 
130 
     | 
    
         | 
| 
      
 131 
     | 
    
         
            +
            BADXFF = " due to dangerous misconfiguration (the http-header specified by --xff-hdr was received from an untrusted reverse-proxy)"
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
       131 
133 
     | 
    
         
             
            H_CONN_KEEPALIVE = "Connection: Keep-Alive"
         
     | 
| 
       132 
134 
     | 
    
         
             
            H_CONN_CLOSE = "Connection: Close"
         
     | 
| 
       133 
135 
     | 
    
         | 
| 
         @@ -157,6 +159,8 @@ class HttpCli(object): 
     | 
|
| 
       157 
159 
     | 
    
         | 
| 
       158 
160 
     | 
    
         
             
                def __init__(self, conn )  :
         
     | 
| 
       159 
161 
     | 
    
         | 
| 
      
 162 
     | 
    
         
            +
                    empty_stringlist  = []
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
       160 
164 
     | 
    
         
             
                    self.t0 = time.time()
         
     | 
| 
       161 
165 
     | 
    
         
             
                    self.conn = conn
         
     | 
| 
       162 
166 
     | 
    
         
             
                    self.u2mutex = conn.u2mutex  # mypy404
         
     | 
| 
         @@ -202,9 +206,7 @@ class HttpCli(object): 
     | 
|
| 
       202 
206 
     | 
    
         
             
                    self.trailing_slash = True
         
     | 
| 
       203 
207 
     | 
    
         
             
                    self.uname = " "
         
     | 
| 
       204 
208 
     | 
    
         
             
                    self.pw = " "
         
     | 
| 
       205 
     | 
    
         
            -
                    self.rvol =  
     | 
| 
       206 
     | 
    
         
            -
                    self.wvol = [" "]
         
     | 
| 
       207 
     | 
    
         
            -
                    self.avol = [" "]
         
     | 
| 
      
 209 
     | 
    
         
            +
                    self.rvol = self.wvol = self.avol = empty_stringlist
         
     | 
| 
       208 
210 
     | 
    
         
             
                    self.do_log = True
         
     | 
| 
       209 
211 
     | 
    
         
             
                    self.can_read = False
         
     | 
| 
       210 
212 
     | 
    
         
             
                    self.can_write = False
         
     | 
| 
         @@ -385,6 +387,7 @@ class HttpCli(object): 
     | 
|
| 
       385 
387 
     | 
    
         
             
                                ) + "0.0/16"
         
     | 
| 
       386 
388 
     | 
    
         
             
                                zs2 = ' or "--xff-src=lan"' if self.conn.xff_lan.map(pip) else ""
         
     | 
| 
       387 
389 
     | 
    
         
             
                                self.log(t % (self.args.xff_hdr, pip, cli_ip, zso, zs, zs2), 3)
         
     | 
| 
      
 390 
     | 
    
         
            +
                                self.bad_xff = True
         
     | 
| 
       388 
391 
     | 
    
         
             
                            else:
         
     | 
| 
       389 
392 
     | 
    
         
             
                                self.ip = cli_ip
         
     | 
| 
       390 
393 
     | 
    
         
             
                                self.is_vproxied = bool(self.args.R)
         
     | 
| 
         @@ -505,7 +508,7 @@ class HttpCli(object): 
     | 
|
| 
       505 
508 
     | 
    
         
             
                            return False
         
     | 
| 
       506 
509 
     | 
    
         | 
| 
       507 
510 
     | 
    
         
             
                        if "k" in uparam:
         
     | 
| 
       508 
     | 
    
         
            -
                            m =  
     | 
| 
      
 511 
     | 
    
         
            +
                            m = re_k.search(uparam["k"])
         
     | 
| 
       509 
512 
     | 
    
         
             
                            if m:
         
     | 
| 
       510 
513 
     | 
    
         
             
                                zs = uparam["k"]
         
     | 
| 
       511 
514 
     | 
    
         
             
                                t = "malicious user; illegal filekey; req(%r) k(%r) => %r"
         
     | 
| 
         @@ -1889,7 +1892,7 @@ class HttpCli(object): 
     | 
|
| 
       1889 
1892 
     | 
    
         
             
                            return self.handle_stash(False)
         
     | 
| 
       1890 
1893 
     | 
    
         | 
| 
       1891 
1894 
     | 
    
         
             
                        if "save" in opt:
         
     | 
| 
       1892 
     | 
    
         
            -
                            post_sz, _, _, _, path, _ = self.dump_to_file(False)
         
     | 
| 
      
 1895 
     | 
    
         
            +
                            post_sz, _, _, _, _, path, _ = self.dump_to_file(False)
         
     | 
| 
       1893 
1896 
     | 
    
         
             
                            self.log("urlform: %d bytes, %r" % (post_sz, path))
         
     | 
| 
       1894 
1897 
     | 
    
         
             
                        elif "print" in opt:
         
     | 
| 
       1895 
1898 
     | 
    
         
             
                            reader, _ = self.get_body_reader()
         
     | 
| 
         @@ -1970,11 +1973,11 @@ class HttpCli(object): 
     | 
|
| 
       1970 
1973 
     | 
    
         
             
                    else:
         
     | 
| 
       1971 
1974 
     | 
    
         
             
                        return read_socket(self.sr, bufsz, remains), remains
         
     | 
| 
       1972 
1975 
     | 
    
         | 
| 
       1973 
     | 
    
         
            -
                def dump_to_file(self, is_put ) 
     | 
| 
       1974 
     | 
    
         
            -
                    # post_sz, sha_hex, sha_b64, remains, path, url
         
     | 
| 
      
 1976 
     | 
    
         
            +
                def dump_to_file(self, is_put )        :
         
     | 
| 
      
 1977 
     | 
    
         
            +
                    # post_sz, halg, sha_hex, sha_b64, remains, path, url
         
     | 
| 
       1975 
1978 
     | 
    
         
             
                    reader, remains = self.get_body_reader()
         
     | 
| 
       1976 
1979 
     | 
    
         
             
                    vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True)
         
     | 
| 
       1977 
     | 
    
         
            -
                    rnd,  
     | 
| 
      
 1980 
     | 
    
         
            +
                    rnd, lifetime, xbu, xau = self.upload_flags(vfs)
         
     | 
| 
       1978 
1981 
     | 
    
         
             
                    lim = vfs.get_dbv(rem)[0].lim
         
     | 
| 
       1979 
1982 
     | 
    
         
             
                    fdir = vfs.canonical(rem)
         
     | 
| 
       1980 
1983 
     | 
    
         
             
                    if lim:
         
     | 
| 
         @@ -2122,12 +2125,14 @@ class HttpCli(object): 
     | 
|
| 
       2122 
2125 
     | 
    
         
             
                            # small toctou, but better than clobbering a hardlink
         
     | 
| 
       2123 
2126 
     | 
    
         
             
                            wunlink(self.log, path, vfs.flags)
         
     | 
| 
       2124 
2127 
     | 
    
         | 
| 
      
 2128 
     | 
    
         
            +
                    halg = "sha512"
         
     | 
| 
       2125 
2129 
     | 
    
         
             
                    hasher = None
         
     | 
| 
       2126 
2130 
     | 
    
         
             
                    copier = hashcopy
         
     | 
| 
       2127 
2131 
     | 
    
         
             
                    if "ck" in self.ouparam or "ck" in self.headers:
         
     | 
| 
       2128 
     | 
    
         
            -
                        zs = self.ouparam.get("ck") or self.headers.get("ck") or ""
         
     | 
| 
      
 2132 
     | 
    
         
            +
                        halg = zs = self.ouparam.get("ck") or self.headers.get("ck") or ""
         
     | 
| 
       2129 
2133 
     | 
    
         
             
                        if not zs or zs == "no":
         
     | 
| 
       2130 
2134 
     | 
    
         
             
                            copier = justcopy
         
     | 
| 
      
 2135 
     | 
    
         
            +
                            halg = ""
         
     | 
| 
       2131 
2136 
     | 
    
         
             
                        elif zs == "md5":
         
     | 
| 
       2132 
2137 
     | 
    
         
             
                            hasher = hashlib.md5(**USED4SEC)
         
     | 
| 
       2133 
2138 
     | 
    
         
             
                        elif zs == "sha1":
         
     | 
| 
         @@ -2161,7 +2166,7 @@ class HttpCli(object): 
     | 
|
| 
       2161 
2166 
     | 
    
         
             
                            raise
         
     | 
| 
       2162 
2167 
     | 
    
         | 
| 
       2163 
2168 
     | 
    
         
             
                    if self.args.nw:
         
     | 
| 
       2164 
     | 
    
         
            -
                        return post_sz, sha_hex, sha_b64, remains, path, ""
         
     | 
| 
      
 2169 
     | 
    
         
            +
                        return post_sz, halg, sha_hex, sha_b64, remains, path, ""
         
     | 
| 
       2165 
2170 
     | 
    
         | 
| 
       2166 
2171 
     | 
    
         
             
                    at = mt = time.time() - lifetime
         
     | 
| 
       2167 
2172 
     | 
    
         
             
                    cli_mt = self.headers.get("x-oc-mtime")
         
     | 
| 
         @@ -2272,19 +2277,30 @@ class HttpCli(object): 
     | 
|
| 
       2272 
2277 
     | 
    
         
             
                        self.args.RS + vpath + vsuf,
         
     | 
| 
       2273 
2278 
     | 
    
         
             
                    )
         
     | 
| 
       2274 
2279 
     | 
    
         | 
| 
       2275 
     | 
    
         
            -
                    return post_sz, sha_hex, sha_b64, remains, path, url
         
     | 
| 
      
 2280 
     | 
    
         
            +
                    return post_sz, halg, sha_hex, sha_b64, remains, path, url
         
     | 
| 
       2276 
2281 
     | 
    
         | 
| 
       2277 
2282 
     | 
    
         
             
                def handle_stash(self, is_put )  :
         
     | 
| 
       2278 
     | 
    
         
            -
                    post_sz, sha_hex, sha_b64, remains, path, url = self.dump_to_file(is_put)
         
     | 
| 
      
 2283 
     | 
    
         
            +
                    post_sz, halg, sha_hex, sha_b64, remains, path, url = self.dump_to_file(is_put)
         
     | 
| 
       2279 
2284 
     | 
    
         
             
                    spd = self._spd(post_sz)
         
     | 
| 
       2280 
2285 
     | 
    
         
             
                    t = "%s wrote %d/%d bytes to %r  # %s"
         
     | 
| 
       2281 
2286 
     | 
    
         
             
                    self.log(t % (spd, post_sz, remains, path, sha_b64[:28]))  # 21
         
     | 
| 
       2282 
2287 
     | 
    
         | 
| 
       2283 
     | 
    
         
            -
                     
     | 
| 
       2284 
     | 
    
         
            -
             
     | 
| 
       2285 
     | 
    
         
            -
                     
     | 
| 
      
 2288 
     | 
    
         
            +
                    mime = "text/plain; charset=utf-8"
         
     | 
| 
      
 2289 
     | 
    
         
            +
                    ac = self.uparam.get("want") or self.headers.get("accept") or ""
         
     | 
| 
      
 2290 
     | 
    
         
            +
                    if ac:
         
     | 
| 
      
 2291 
     | 
    
         
            +
                        ac = ac.split(";", 1)[0].lower()
         
     | 
| 
      
 2292 
     | 
    
         
            +
                        if ac == "application/json":
         
     | 
| 
      
 2293 
     | 
    
         
            +
                            ac = "json"
         
     | 
| 
       2286 
2294 
     | 
    
         
             
                    if ac == "url":
         
     | 
| 
       2287 
2295 
     | 
    
         
             
                        t = url
         
     | 
| 
      
 2296 
     | 
    
         
            +
                    elif ac == "json" or "j" in self.uparam:
         
     | 
| 
      
 2297 
     | 
    
         
            +
                        jmsg = {"fileurl": url, "filesz": post_sz}
         
     | 
| 
      
 2298 
     | 
    
         
            +
                        if halg:
         
     | 
| 
      
 2299 
     | 
    
         
            +
                            jmsg[halg] = sha_hex[:56]
         
     | 
| 
      
 2300 
     | 
    
         
            +
                            jmsg["sha_b64"] = sha_b64
         
     | 
| 
      
 2301 
     | 
    
         
            +
             
     | 
| 
      
 2302 
     | 
    
         
            +
                        mime = "application/json"
         
     | 
| 
      
 2303 
     | 
    
         
            +
                        t = json.dumps(jmsg, indent=2, sort_keys=True)
         
     | 
| 
       2288 
2304 
     | 
    
         
             
                    else:
         
     | 
| 
       2289 
2305 
     | 
    
         
             
                        t = "{}\n{}\n{}\n{}\n".format(post_sz, sha_b64, sha_hex[:56], url)
         
     | 
| 
       2290 
2306 
     | 
    
         | 
| 
         @@ -2294,7 +2310,7 @@ class HttpCli(object): 
     | 
|
| 
       2294 
2310 
     | 
    
         
             
                        h["X-OC-MTime"] = "accepted"
         
     | 
| 
       2295 
2311 
     | 
    
         
             
                        t = ""  # some webdav clients expect/prefer this
         
     | 
| 
       2296 
2312 
     | 
    
         | 
| 
       2297 
     | 
    
         
            -
                    self.reply(t.encode("utf-8"), 201, headers=h)
         
     | 
| 
      
 2313 
     | 
    
         
            +
                    self.reply(t.encode("utf-8", "replace"), 201, mime=mime, headers=h)
         
     | 
| 
       2298 
2314 
     | 
    
         
             
                    return True
         
     | 
| 
       2299 
2315 
     | 
    
         | 
| 
       2300 
2316 
     | 
    
         
             
                def bakflip(
         
     | 
| 
         @@ -2967,7 +2983,7 @@ class HttpCli(object): 
     | 
|
| 
       2967 
2983 
     | 
    
         
             
                    self.redirect(vpath, "?edit")
         
     | 
| 
       2968 
2984 
     | 
    
         
             
                    return True
         
     | 
| 
       2969 
2985 
     | 
    
         | 
| 
       2970 
     | 
    
         
            -
                def upload_flags(self, vfs ) 
     | 
| 
      
 2986 
     | 
    
         
            +
                def upload_flags(self, vfs )     :
         
     | 
| 
       2971 
2987 
     | 
    
         
             
                    if self.args.nw:
         
     | 
| 
       2972 
2988 
     | 
    
         
             
                        rnd = 0
         
     | 
| 
       2973 
2989 
     | 
    
         
             
                    else:
         
     | 
| 
         @@ -2975,10 +2991,6 @@ class HttpCli(object): 
     | 
|
| 
       2975 
2991 
     | 
    
         
             
                        if vfs.flags.get("rand"):  # force-enable
         
     | 
| 
       2976 
2992 
     | 
    
         
             
                            rnd = max(rnd, vfs.flags["nrand"])
         
     | 
| 
       2977 
2993 
     | 
    
         | 
| 
       2978 
     | 
    
         
            -
                    ac = self.uparam.get(
         
     | 
| 
       2979 
     | 
    
         
            -
                        "want", self.headers.get("accept", "").lower().split(";")[-1]
         
     | 
| 
       2980 
     | 
    
         
            -
                    )
         
     | 
| 
       2981 
     | 
    
         
            -
                    want_url = ac == "url"
         
     | 
| 
       2982 
2994 
     | 
    
         
             
                    zs = self.uparam.get("life", self.headers.get("life", ""))
         
     | 
| 
       2983 
2995 
     | 
    
         
             
                    if zs:
         
     | 
| 
       2984 
2996 
     | 
    
         
             
                        vlife = vfs.flags.get("lifetime") or 0
         
     | 
| 
         @@ -2988,7 +3000,6 @@ class HttpCli(object): 
     | 
|
| 
       2988 
3000 
     | 
    
         | 
| 
       2989 
3001 
     | 
    
         
             
                    return (
         
     | 
| 
       2990 
3002 
     | 
    
         
             
                        rnd,
         
     | 
| 
       2991 
     | 
    
         
            -
                        want_url,
         
     | 
| 
       2992 
3003 
     | 
    
         
             
                        lifetime,
         
     | 
| 
       2993 
3004 
     | 
    
         
             
                        vfs.flags.get("xbu") or [],
         
     | 
| 
       2994 
3005 
     | 
    
         
             
                        vfs.flags.get("xau") or [],
         
     | 
| 
         @@ -3041,7 +3052,14 @@ class HttpCli(object): 
     | 
|
| 
       3041 
3052 
     | 
    
         
             
                        if not nullwrite:
         
     | 
| 
       3042 
3053 
     | 
    
         
             
                            bos.makedirs(fdir_base)
         
     | 
| 
       3043 
3054 
     | 
    
         | 
| 
       3044 
     | 
    
         
            -
                    rnd,  
     | 
| 
      
 3055 
     | 
    
         
            +
                    rnd, lifetime, xbu, xau = self.upload_flags(vfs)
         
     | 
| 
      
 3056 
     | 
    
         
            +
                    zs = self.uparam.get("want") or self.headers.get("accept") or ""
         
     | 
| 
      
 3057 
     | 
    
         
            +
                    if zs:
         
     | 
| 
      
 3058 
     | 
    
         
            +
                        zs = zs.split(";", 1)[0].lower()
         
     | 
| 
      
 3059 
     | 
    
         
            +
                        if zs == "application/json":
         
     | 
| 
      
 3060 
     | 
    
         
            +
                            zs = "json"
         
     | 
| 
      
 3061 
     | 
    
         
            +
                    want_url = zs == "url"
         
     | 
| 
      
 3062 
     | 
    
         
            +
                    want_json = zs == "json" or "j" in self.uparam
         
     | 
| 
       3045 
3063 
     | 
    
         | 
| 
       3046 
3064 
     | 
    
         
             
                    files       = []
         
     | 
| 
       3047 
3065 
     | 
    
         
             
                    # sz, sha_hex, sha_b64, p_file, fname, abspath
         
     | 
| 
         @@ -3363,7 +3381,9 @@ class HttpCli(object): 
     | 
|
| 
       3363 
3381 
     | 
    
         
             
                            msg += "\n" + errmsg
         
     | 
| 
       3364 
3382 
     | 
    
         | 
| 
       3365 
3383 
     | 
    
         
             
                        self.reply(msg.encode("utf-8", "replace"), status=sc)
         
     | 
| 
       3366 
     | 
    
         
            -
                    elif  
     | 
| 
      
 3384 
     | 
    
         
            +
                    elif want_json:
         
     | 
| 
      
 3385 
     | 
    
         
            +
                        if len(jmsg["files"]) == 1:
         
     | 
| 
      
 3386 
     | 
    
         
            +
                            jmsg["fileurl"] = jmsg["files"][0]["url"]
         
     | 
| 
       3367 
3387 
     | 
    
         
             
                        jtxt = json.dumps(jmsg, indent=2, sort_keys=True).encode("utf-8", "replace")
         
     | 
| 
       3368 
3388 
     | 
    
         
             
                        self.reply(jtxt, mime="application/json", status=sc)
         
     | 
| 
       3369 
3389 
     | 
    
         
             
                    else:
         
     | 
| 
         @@ -4317,7 +4337,7 @@ class HttpCli(object): 
     | 
|
| 
       4317 
4337 
     | 
    
         
             
                        self.log,
         
     | 
| 
       4318 
4338 
     | 
    
         
             
                        self.asrv,
         
     | 
| 
       4319 
4339 
     | 
    
         
             
                        fgen,
         
     | 
| 
       4320 
     | 
    
         
            -
                        utf8="utf" in uarg,
         
     | 
| 
      
 4340 
     | 
    
         
            +
                        utf8="utf" in uarg or not uarg,
         
     | 
| 
       4321 
4341 
     | 
    
         
             
                        pre_crc="crc" in uarg,
         
     | 
| 
       4322 
4342 
     | 
    
         
             
                        cmp=uarg if cancmp or uarg == "pax" else "",
         
     | 
| 
       4323 
4343 
     | 
    
         
             
                    )
         
     | 
| 
         @@ -4531,12 +4551,12 @@ class HttpCli(object): 
     | 
|
| 
       4531 
4551 
     | 
    
         
             
                        else self.conn.hsrv.nm.map(self.ip) or host
         
     | 
| 
       4532 
4552 
     | 
    
         
             
                    )
         
     | 
| 
       4533 
4553 
     | 
    
         
             
                    # safer than html_escape/quotep since this avoids both XSS and shell-stuff
         
     | 
| 
       4534 
     | 
    
         
            -
                    pw = re.sub(r"[<>&$?`\"']", "_", self.pw or " 
     | 
| 
      
 4554 
     | 
    
         
            +
                    pw = re.sub(r"[<>&$?`\"']", "_", self.pw or "hunter2")
         
     | 
| 
       4535 
4555 
     | 
    
         
             
                    vp = re.sub(r"[<>&$?`\"']", "_", self.uparam["hc"] or "").lstrip("/")
         
     | 
| 
       4536 
4556 
     | 
    
         
             
                    pw = pw.replace(" ", "%20")
         
     | 
| 
       4537 
4557 
     | 
    
         
             
                    vp = vp.replace(" ", "%20")
         
     | 
| 
       4538 
4558 
     | 
    
         
             
                    if pw in self.asrv.sesa:
         
     | 
| 
       4539 
     | 
    
         
            -
                        pw = " 
     | 
| 
      
 4559 
     | 
    
         
            +
                        pw = "hunter2"
         
     | 
| 
       4540 
4560 
     | 
    
         | 
| 
       4541 
4561 
     | 
    
         
             
                    html = self.j2s(
         
     | 
| 
       4542 
4562 
     | 
    
         
             
                        "svcs",
         
     | 
| 
         @@ -4965,8 +4985,16 @@ class HttpCli(object): 
     | 
|
| 
       4965 
4985 
     | 
    
         
             
                        and (self.uname in vol.axs.uread or self.uname in vol.axs.upget)
         
     | 
| 
       4966 
4986 
     | 
    
         
             
                    }
         
     | 
| 
       4967 
4987 
     | 
    
         | 
| 
      
 4988 
     | 
    
         
            +
                    bad_xff = hasattr(self, "bad_xff")
         
     | 
| 
      
 4989 
     | 
    
         
            +
                    if bad_xff:
         
     | 
| 
      
 4990 
     | 
    
         
            +
                        allvols = []
         
     | 
| 
      
 4991 
     | 
    
         
            +
                        t = "will not return list of recent uploads" + BADXFF
         
     | 
| 
      
 4992 
     | 
    
         
            +
                        self.log(t, 1)
         
     | 
| 
      
 4993 
     | 
    
         
            +
                        if self.avol:
         
     | 
| 
      
 4994 
     | 
    
         
            +
                            raise Pebkac(500, t)
         
     | 
| 
      
 4995 
     | 
    
         
            +
             
     | 
| 
       4968 
4996 
     | 
    
         
             
                    x = self.conn.hsrv.broker.ask(
         
     | 
| 
       4969 
     | 
    
         
            -
                        "up2k.get_unfinished_by_user", self.uname, self.ip
         
     | 
| 
      
 4997 
     | 
    
         
            +
                        "up2k.get_unfinished_by_user", self.uname, "" if bad_xff else self.ip
         
     | 
| 
       4970 
4998 
     | 
    
         
             
                    )
         
     | 
| 
       4971 
4999 
     | 
    
         
             
                    uret = x.get()
         
     | 
| 
       4972 
5000 
     | 
    
         | 
| 
         @@ -5360,12 +5388,16 @@ class HttpCli(object): 
     | 
|
| 
       5360 
5388 
     | 
    
         
             
                    if self.args.no_del:
         
     | 
| 
       5361 
5389 
     | 
    
         
             
                        raise Pebkac(403, "the delete feature is disabled in server config")
         
     | 
| 
       5362 
5390 
     | 
    
         | 
| 
      
 5391 
     | 
    
         
            +
                    unpost = "unpost" in self.uparam
         
     | 
| 
      
 5392 
     | 
    
         
            +
                    if unpost and hasattr(self, "bad_xff"):
         
     | 
| 
      
 5393 
     | 
    
         
            +
                        self.log("unpost was denied" + BADXFF, 1)
         
     | 
| 
      
 5394 
     | 
    
         
            +
                        raise Pebkac(403, "the delete feature is disabled in server config")
         
     | 
| 
      
 5395 
     | 
    
         
            +
             
     | 
| 
       5363 
5396 
     | 
    
         
             
                    if not req:
         
     | 
| 
       5364 
5397 
     | 
    
         
             
                        req = [self.vpath]
         
     | 
| 
       5365 
5398 
     | 
    
         
             
                    elif self.is_vproxied:
         
     | 
| 
       5366 
5399 
     | 
    
         
             
                        req = [x[len(self.args.SR) :] for x in req]
         
     | 
| 
       5367 
5400 
     | 
    
         | 
| 
       5368 
     | 
    
         
            -
                    unpost = "unpost" in self.uparam
         
     | 
| 
       5369 
5401 
     | 
    
         
             
                    nlim = int(self.uparam.get("lim") or 0)
         
     | 
| 
       5370 
5402 
     | 
    
         
             
                    lim = [nlim, nlim] if nlim else []
         
     | 
| 
       5371 
5403 
     | 
    
         | 
| 
         @@ -5765,7 +5797,7 @@ class HttpCli(object): 
     | 
|
| 
       5765 
5797 
     | 
    
         
             
                        "taglist": [],
         
     | 
| 
       5766 
5798 
     | 
    
         
             
                        "have_tags_idx": int(e2t),
         
     | 
| 
       5767 
5799 
     | 
    
         
             
                        "have_b_u": (self.can_write and self.uparam.get("b") == "u"),
         
     | 
| 
       5768 
     | 
    
         
            -
                        "sb_lg":  
     | 
| 
      
 5800 
     | 
    
         
            +
                        "sb_lg": vn.js_ls["sb_lg"],
         
     | 
| 
       5769 
5801 
     | 
    
         
             
                        "url_suf": url_suf,
         
     | 
| 
       5770 
5802 
     | 
    
         
             
                        "title": html_escape("%s %s" % (self.args.bname, self.vpath), crlf=True),
         
     | 
| 
       5771 
5803 
     | 
    
         
             
                        "srv_info": srv_infot,
         
     | 
    
        copyparty/svchub.py
    CHANGED
    
    | 
         @@ -44,6 +44,8 @@ from .util import ( 
     | 
|
| 
       44 
44 
     | 
    
         
             
                FFMPEG_URL,
         
     | 
| 
       45 
45 
     | 
    
         
             
                HAVE_PSUTIL,
         
     | 
| 
       46 
46 
     | 
    
         
             
                HAVE_SQLITE3,
         
     | 
| 
      
 47 
     | 
    
         
            +
                HAVE_ZMQ,
         
     | 
| 
      
 48 
     | 
    
         
            +
                URL_BUG,
         
     | 
| 
       47 
49 
     | 
    
         
             
                UTC,
         
     | 
| 
       48 
50 
     | 
    
         
             
                VERSIONS,
         
     | 
| 
       49 
51 
     | 
    
         
             
                Daemon,
         
     | 
| 
         @@ -54,6 +56,7 @@ from .util import ( 
     | 
|
| 
       54 
56 
     | 
    
         
             
                alltrace,
         
     | 
| 
       55 
57 
     | 
    
         
             
                ansi_re,
         
     | 
| 
       56 
58 
     | 
    
         
             
                build_netmap,
         
     | 
| 
      
 59 
     | 
    
         
            +
                expat_ver,
         
     | 
| 
       57 
60 
     | 
    
         
             
                load_ipu,
         
     | 
| 
       58 
61 
     | 
    
         
             
                min_ex,
         
     | 
| 
       59 
62 
     | 
    
         
             
                mp,
         
     | 
| 
         @@ -629,6 +632,7 @@ class SvcHub(object): 
     | 
|
| 
       629 
632 
     | 
    
         
             
                        (HAVE_FFPROBE, "ffprobe", t_ff + ", read audio/media tags"),
         
     | 
| 
       630 
633 
     | 
    
         
             
                        (HAVE_MUTAGEN, "mutagen", "read audio tags (ffprobe is better but slower)"),
         
     | 
| 
       631 
634 
     | 
    
         
             
                        (HAVE_ARGON2, "argon2", "secure password hashing (advanced users only)"),
         
     | 
| 
      
 635 
     | 
    
         
            +
                        (HAVE_ZMQ, "pyzmq", "send zeromq messages from event-hooks"),
         
     | 
| 
       632 
636 
     | 
    
         
             
                        (HAVE_HEIF, "pillow-heif", "read .heif images with pillow (rarely useful)"),
         
     | 
| 
       633 
637 
     | 
    
         
             
                        (HAVE_AVIF, "pillow-avif", "read .avif images with pillow (rarely useful)"),
         
     | 
| 
       634 
638 
     | 
    
         
             
                    ]
         
     | 
| 
         @@ -685,6 +689,15 @@ class SvcHub(object): 
     | 
|
| 
       685 
689 
     | 
    
         
             
                        if self.args.bauth_last:
         
     | 
| 
       686 
690 
     | 
    
         
             
                            self.log("root", "WARNING: ignoring --bauth-last due to --no-bauth", 3)
         
     | 
| 
       687 
691 
     | 
    
         | 
| 
      
 692 
     | 
    
         
            +
                    if not self.args.no_dav:
         
     | 
| 
      
 693 
     | 
    
         
            +
                        from .dxml import DXML_OK
         
     | 
| 
      
 694 
     | 
    
         
            +
             
     | 
| 
      
 695 
     | 
    
         
            +
                        if not DXML_OK:
         
     | 
| 
      
 696 
     | 
    
         
            +
                            if not self.args.no_dav:
         
     | 
| 
      
 697 
     | 
    
         
            +
                                self.args.no_dav = True
         
     | 
| 
      
 698 
     | 
    
         
            +
                                t = "WARNING:\nDisabling WebDAV support because dxml selftest failed. Please report this bug;\n%s\n...and include the following information in the bug-report:\n%s | expat %s\n"
         
     | 
| 
      
 699 
     | 
    
         
            +
                                self.log("root", t % (URL_BUG, VERSIONS, expat_ver()), 1)
         
     | 
| 
      
 700 
     | 
    
         
            +
             
     | 
| 
       688 
701 
     | 
    
         
             
                def _process_config(self)  :
         
     | 
| 
       689 
702 
     | 
    
         
             
                    al = self.args
         
     | 
| 
       690 
703 
     | 
    
         | 
    
        copyparty/up2k.py
    CHANGED
    
    | 
         @@ -790,7 +790,7 @@ class Up2k(object): 
     | 
|
| 
       790 
790 
     | 
    
         
             
                            continue
         
     | 
| 
       791 
791 
     | 
    
         | 
| 
       792 
792 
     | 
    
         
             
                        self.log("xiu: %d# %r" % (len(wrfs), cmd))
         
     | 
| 
       793 
     | 
    
         
            -
                        runihook(self.log, cmd, vol, ups)
         
     | 
| 
      
 793 
     | 
    
         
            +
                        runihook(self.log, self.args.hook_v, cmd, vol, ups)
         
     | 
| 
       794 
794 
     | 
    
         | 
| 
       795 
795 
     | 
    
         
             
                def _vis_job_progress(self, job  )  :
         
     | 
| 
       796 
796 
     | 
    
         
             
                    perc = 100 - (len(job["need"]) * 100.0 / (len(job["hash"]) or 1))
         
     | 
| 
         @@ -851,7 +851,7 @@ class Up2k(object): 
     | 
|
| 
       851 
851 
     | 
    
         
             
                    self.iacct = self.asrv.iacct
         
     | 
| 
       852 
852 
     | 
    
         
             
                    self.grps = self.asrv.grps
         
     | 
| 
       853 
853 
     | 
    
         | 
| 
       854 
     | 
    
         
            -
                    have_e2d = self.args.idp_h_usr
         
     | 
| 
      
 854 
     | 
    
         
            +
                    have_e2d = self.args.idp_h_usr or self.args.chpw or self.args.shr
         
     | 
| 
       855 
855 
     | 
    
         
             
                    vols = list(all_vols.values())
         
     | 
| 
       856 
856 
     | 
    
         
             
                    t0 = time.time()
         
     | 
| 
       857 
857 
     | 
    
         | 
| 
         @@ -1112,6 +1112,7 @@ class Up2k(object): 
     | 
|
| 
       1112 
1112 
     | 
    
         
             
                    reg = {}
         
     | 
| 
       1113 
1113 
     | 
    
         
             
                    drp = None
         
     | 
| 
       1114 
1114 
     | 
    
         
             
                    emptylist = []
         
     | 
| 
      
 1115 
     | 
    
         
            +
                    dotpart = "." if self.args.dotpart else ""
         
     | 
| 
       1115 
1116 
     | 
    
         
             
                    snap = os.path.join(histpath, "up2k.snap")
         
     | 
| 
       1116 
1117 
     | 
    
         
             
                    if bos.path.exists(snap):
         
     | 
| 
       1117 
1118 
     | 
    
         
             
                        with gzip.GzipFile(snap, "rb") as f:
         
     | 
| 
         @@ -1124,6 +1125,8 @@ class Up2k(object): 
     | 
|
| 
       1124 
1125 
     | 
    
         
             
                        except:
         
     | 
| 
       1125 
1126 
     | 
    
         
             
                            pass
         
     | 
| 
       1126 
1127 
     | 
    
         | 
| 
      
 1128 
     | 
    
         
            +
                        reg = reg2  # diff-golf
         
     | 
| 
      
 1129 
     | 
    
         
            +
             
     | 
| 
       1127 
1130 
     | 
    
         
             
                        if reg2 and "dwrk" not in reg2[next(iter(reg2))]:
         
     | 
| 
       1128 
1131 
     | 
    
         
             
                            for job in reg2.values():
         
     | 
| 
       1129 
1132 
     | 
    
         
             
                                job["dwrk"] = job["wark"]
         
     | 
| 
         @@ -1131,7 +1134,8 @@ class Up2k(object): 
     | 
|
| 
       1131 
1134 
     | 
    
         
             
                        rm = []
         
     | 
| 
       1132 
1135 
     | 
    
         
             
                        for k, job in reg2.items():
         
     | 
| 
       1133 
1136 
     | 
    
         
             
                            job["ptop"] = ptop
         
     | 
| 
       1134 
     | 
    
         
            -
                             
     | 
| 
      
 1137 
     | 
    
         
            +
                            is_done = "done" in job
         
     | 
| 
      
 1138 
     | 
    
         
            +
                            if is_done:
         
     | 
| 
       1135 
1139 
     | 
    
         
             
                                job["need"] = job["hash"] = emptylist
         
     | 
| 
       1136 
1140 
     | 
    
         
             
                            else:
         
     | 
| 
       1137 
1141 
     | 
    
         
             
                                if "need" not in job:
         
     | 
| 
         @@ -1139,10 +1143,13 @@ class Up2k(object): 
     | 
|
| 
       1139 
1143 
     | 
    
         
             
                                if "hash" not in job:
         
     | 
| 
       1140 
1144 
     | 
    
         
             
                                    job["hash"] = []
         
     | 
| 
       1141 
1145 
     | 
    
         | 
| 
       1142 
     | 
    
         
            -
                             
     | 
| 
      
 1146 
     | 
    
         
            +
                            if is_done:
         
     | 
| 
      
 1147 
     | 
    
         
            +
                                fp = djoin(ptop, job["prel"], job["name"])
         
     | 
| 
      
 1148 
     | 
    
         
            +
                            else:
         
     | 
| 
      
 1149 
     | 
    
         
            +
                                fp = djoin(ptop, job["prel"], dotpart + job["name"] + ".PARTIAL")
         
     | 
| 
      
 1150 
     | 
    
         
            +
             
     | 
| 
       1143 
1151 
     | 
    
         
             
                            if bos.path.exists(fp):
         
     | 
| 
       1144 
     | 
    
         
            -
                                 
     | 
| 
       1145 
     | 
    
         
            -
                                if "done" in job:
         
     | 
| 
      
 1152 
     | 
    
         
            +
                                if is_done:
         
     | 
| 
       1146 
1153 
     | 
    
         
             
                                    continue
         
     | 
| 
       1147 
1154 
     | 
    
         
             
                                job["poke"] = time.time()
         
     | 
| 
       1148 
1155 
     | 
    
         
             
                                job["busy"] = {}
         
     | 
| 
         @@ -1150,11 +1157,18 @@ class Up2k(object): 
     | 
|
| 
       1150 
1157 
     | 
    
         
             
                                self.log("ign deleted file in snap: %r" % (fp,))
         
     | 
| 
       1151 
1158 
     | 
    
         
             
                                if not n4g:
         
     | 
| 
       1152 
1159 
     | 
    
         
             
                                    rm.append(k)
         
     | 
| 
       1153 
     | 
    
         
            -
                                    continue
         
     | 
| 
       1154 
1160 
     | 
    
         | 
| 
       1155 
1161 
     | 
    
         
             
                        for x in rm:
         
     | 
| 
       1156 
1162 
     | 
    
         
             
                            del reg2[x]
         
     | 
| 
       1157 
1163 
     | 
    
         | 
| 
      
 1164 
     | 
    
         
            +
                        # optimize pre-1.15.4 entries
         
     | 
| 
      
 1165 
     | 
    
         
            +
                        if next((x for x in reg.values() if "done" in x and "poke" in x), None):
         
     | 
| 
      
 1166 
     | 
    
         
            +
                            zsl = "host tnam busy sprs poke t0c".split()
         
     | 
| 
      
 1167 
     | 
    
         
            +
                            for job in reg.values():
         
     | 
| 
      
 1168 
     | 
    
         
            +
                                if "done" in job:
         
     | 
| 
      
 1169 
     | 
    
         
            +
                                    for k in zsl:
         
     | 
| 
      
 1170 
     | 
    
         
            +
                                        job.pop(k, None)
         
     | 
| 
      
 1171 
     | 
    
         
            +
             
     | 
| 
       1158 
1172 
     | 
    
         
             
                        if drp is None:
         
     | 
| 
       1159 
1173 
     | 
    
         
             
                            drp = [k for k, v in reg.items() if not v["need"]]
         
     | 
| 
       1160 
1174 
     | 
    
         
             
                        else:
         
     | 
| 
         @@ -2989,7 +3003,7 @@ class Up2k(object): 
     | 
|
| 
       2989 
3003 
     | 
    
         
             
                            if wark in reg:
         
     | 
| 
       2990 
3004 
     | 
    
         
             
                                del reg[wark]
         
     | 
| 
       2991 
3005 
     | 
    
         
             
                            job["hash"] = job["need"] = []
         
     | 
| 
       2992 
     | 
    
         
            -
                            job["done"] =  
     | 
| 
      
 3006 
     | 
    
         
            +
                            job["done"] = 1
         
     | 
| 
       2993 
3007 
     | 
    
         
             
                            job["busy"] = {}
         
     | 
| 
       2994 
3008 
     | 
    
         | 
| 
       2995 
3009 
     | 
    
         
             
                        if lost:
         
     | 
| 
         @@ -4867,7 +4881,8 @@ class Up2k(object): 
     | 
|
| 
       4867 
4881 
     | 
    
         
             
                        except:
         
     | 
| 
       4868 
4882 
     | 
    
         
             
                            pass
         
     | 
| 
       4869 
4883 
     | 
    
         | 
| 
       4870 
     | 
    
         
            -
                     
     | 
| 
      
 4884 
     | 
    
         
            +
                    vf = self.flags[job["ptop"]]
         
     | 
| 
      
 4885 
     | 
    
         
            +
                    xbu = vf.get("xbu")
         
     | 
| 
       4871 
4886 
     | 
    
         
             
                    ap_chk = djoin(pdir, job["name"])
         
     | 
| 
       4872 
4887 
     | 
    
         
             
                    vp_chk = djoin(job["vtop"], job["prel"], job["name"])
         
     | 
| 
       4873 
4888 
     | 
    
         
             
                    if xbu:
         
     | 
| 
         @@ -4897,7 +4912,7 @@ class Up2k(object): 
     | 
|
| 
       4897 
4912 
     | 
    
         
             
                            if x:
         
     | 
| 
       4898 
4913 
     | 
    
         
             
                                zvfs = vfs
         
     | 
| 
       4899 
4914 
     | 
    
         
             
                                pdir, _, job["name"], (vfs, rem) = x
         
     | 
| 
       4900 
     | 
    
         
            -
                                job["vcfg"] = vfs.flags
         
     | 
| 
      
 4915 
     | 
    
         
            +
                                job["vcfg"] = vf = vfs.flags
         
     | 
| 
       4901 
4916 
     | 
    
         
             
                                job["ptop"] = vfs.realpath
         
     | 
| 
       4902 
4917 
     | 
    
         
             
                                job["vtop"] = vfs.vpath
         
     | 
| 
       4903 
4918 
     | 
    
         
             
                                job["prel"] = rem
         
     | 
| 
         @@ -4947,8 +4962,13 @@ class Up2k(object): 
     | 
|
| 
       4947 
4962 
     | 
    
         
             
                            fs = self.fstab.get(pdir)
         
     | 
| 
       4948 
4963 
     | 
    
         
             
                            if fs == "ok":
         
     | 
| 
       4949 
4964 
     | 
    
         
             
                                pass
         
     | 
| 
       4950 
     | 
    
         
            -
                            elif " 
     | 
| 
       4951 
     | 
    
         
            -
                                t = "volflag ' 
     | 
| 
      
 4965 
     | 
    
         
            +
                            elif "nosparse" in vf:
         
     | 
| 
      
 4966 
     | 
    
         
            +
                                t = "volflag 'nosparse' is preventing creation of sparse files for uploads to [%s]"
         
     | 
| 
      
 4967 
     | 
    
         
            +
                                self.log(t % (job["ptop"],))
         
     | 
| 
      
 4968 
     | 
    
         
            +
                                relabel = True
         
     | 
| 
      
 4969 
     | 
    
         
            +
                                sprs = False
         
     | 
| 
      
 4970 
     | 
    
         
            +
                            elif "sparse" in vf:
         
     | 
| 
      
 4971 
     | 
    
         
            +
                                t = "volflag 'sparse' is forcing creation of sparse files for uploads to [%s]"
         
     | 
| 
       4952 
4972 
     | 
    
         
             
                                self.log(t % (job["ptop"],))
         
     | 
| 
       4953 
4973 
     | 
    
         
             
                                relabel = True
         
     | 
| 
       4954 
4974 
     | 
    
         
             
                            else:
         
     |